diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml new file mode 100644 index 000000000..6898a80fd --- /dev/null +++ b/.builds/freebsd.yml @@ -0,0 +1,31 @@ +# 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: + CC: /usr/bin/clang +tasks: +- setup: | + cd Nim + git clone --depth 1 -q https://github.com/nim-lang/csources.git + gmake -C csources -j $(sysctl -n hw.ncpu) + bin/nim c --skipUserCfg --skipParentCfg koch + echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv +- test: | + cd Nim + if ! ./koch runCI; then + nim c -r tools/ci_testresults.nim + exit 1 + fi +triggers: +- action: email + condition: failure + to: Andreas Rumpf diff --git a/.builds/openbsd_0.yml b/.builds/openbsd_0.yml new file mode 100644 index 000000000..278cb3de9 --- /dev/null +++ b/.builds/openbsd_0.yml @@ -0,0 +1,34 @@ +## do not 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_3" + CC: /usr/bin/clang +tasks: +- setup: | + cd Nim + git clone --depth 1 -q https://github.com/nim-lang/csources.git + gmake -C csources -j $(sysctl -n hw.ncpuonline) + bin/nim c koch + echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv +- test: | + cd Nim + if ! ./koch runCI; then + nim c -r tools/ci_testresults.nim + exit 1 + fi +triggers: +- action: email + condition: failure + to: Andreas Rumpf diff --git a/.builds/openbsd_1.yml b/.builds/openbsd_1.yml new file mode 100644 index 000000000..d3f0c8795 --- /dev/null +++ b/.builds/openbsd_1.yml @@ -0,0 +1,34 @@ +## do not 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_3" + CC: /usr/bin/clang +tasks: +- setup: | + cd Nim + git clone --depth 1 -q https://github.com/nim-lang/csources.git + gmake -C csources -j $(sysctl -n hw.ncpuonline) + bin/nim c koch + echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv +- test: | + cd Nim + if ! ./koch runCI; then + nim c -r tools/ci_testresults.nim + exit 1 + fi +triggers: +- action: email + condition: failure + to: Andreas Rumpf diff --git a/.builds/openbsd_2.yml b/.builds/openbsd_2.yml new file mode 100644 index 000000000..4b63e914d --- /dev/null +++ b/.builds/openbsd_2.yml @@ -0,0 +1,34 @@ +## do not 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: "2_3" + CC: /usr/bin/clang +tasks: +- setup: | + cd Nim + git clone --depth 1 -q https://github.com/nim-lang/csources.git + gmake -C csources -j $(sysctl -n hw.ncpuonline) + bin/nim c koch + echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv +- test: | + cd Nim + if ! ./koch runCI; then + nim c -r tools/ci_testresults.nim + exit 1 + fi +triggers: +- action: email + condition: failure + to: Andreas Rumpf diff --git a/.gitattributes b/.gitattributes index 2c9ff103c..674da7939 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,6 +4,3 @@ # duplicated, which is easily identifiable and fixable. /changelog.md merge=union -# bug https://github.com/dom96/choosenim/issues/256 for WSL CRLF -*.sh text eol=lf -/config/build_config.txt text eol=lf diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..9571896d8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,51 @@ +--- +name: Bug report +about: Have you found an unexpected behavior? Use this template. +title: Think about the title, twice +labels: '' +assignees: '' + +--- + +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 deleted file mode 100644 index 8406f607f..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,75 +0,0 @@ -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: description - attributes: - label: Description - 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 reproducible code and detailed information will be fixed faster. - validations: - required: true - -- type: textarea - id: nim-version - attributes: - label: Nim Version - description: Copy and paste the output of `nim -v` on the command line. For development versions, make sure to include the commit hash. - validations: - required: true - -- type: textarea - id: current-logs - attributes: - label: Current Output - 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 reproducible code and detailed information will be fixed faster. - render: text - -- type: textarea - id: expected-logs - attributes: - label: Expected Output - 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 reproducible code and detailed information will be fixed faster. - render: text - -- 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/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 0187064b5..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,10 +0,0 @@ -contact_links: - - name: Forum - url: https://forum.nim-lang.org - about: Please ask and answer questions here. - - name: Discord - url: https://discord.gg/nim - about: Please ask and answer questions here. - - name: Stack Overflow - url: https://stackoverflow.com/questions/tagged/nim-lang - about: Please ask and answer questions here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..b9f7caad5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,35 @@ +--- +name: Feature request +about: Do you want to suggest a new feature? Use this template. +title: '' +labels: '' +assignees: '' + +--- + + + + +### Summary + + + +### Description + + + +### Alternatives + + + +### Additional Information + diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index 5cb458626..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,72 +0,0 @@ -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: Examples - description: Provide examples for your feature request here. - placeholder: Example code here. - -- 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.yml.disabled b/.github/workflows/ci.yml.disabled new file mode 100644 index 000000000..183576456 --- /dev/null +++ b/.github/workflows/ci.yml.disabled @@ -0,0 +1,122 @@ +name: Continous Integration +on: [push, pull_request] + +jobs: + build: + strategy: + matrix: + os: [ubuntu-18.04, macos-10.15, windows-2019] + cpu: [amd64, i386] + cpp: ['false', 'true'] + pkg: ['false', 'true'] + exclude: + - os: ubuntu-18.04 + cpp: 'true' + - os: ubuntu-18.04 + pkg: 'true' + - os: macos-10.15 + cpu: i386 + - os: macos-10.15 + pkg: 'true' + - os: windows-2019 + cpu: i386 + - os: windows-2019 + cpp: 'true' + name: '${{ matrix.os }} (${{ matrix.cpu }}, cpp: ${{ matrix.cpp }}, pkg: ${{ matrix.pkg }})' + runs-on: ${{ matrix.os }} + env: + NIM_COMPILE_TO_CPP: ${{ matrix.cpp }} + NIM_TEST_PACKAGES: ${{ matrix.pkg }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + - name: 'Checkout csources' + uses: actions/checkout@v2 + with: + repository: nim-lang/csources + path: csources + + - name: 'Install node.js 8.x' + uses: actions/setup-node@v1 + with: + node-version: '8.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 + - name: 'Install dependencies (Linux i386)' + if: runner.os == 'Linux' && matrix.cpu == 'i386' + run: | + sudo dpkg --add-architecture i386 + + sudo apt-fast update -qq + DEBIAN_FRONTEND='noninteractive' \ + sudo apt-fast install --no-install-recommends --allow-downgrades -yq \ + g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \ + libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 \ + libffi-dev:i386 + + cat << EOF > bin/gcc + #!/bin/bash + + exec $(which gcc) -m32 "\$@" + EOF + cat << EOF > bin/g++ + #!/bin/bash + + exec $(which g++) -m32 "\$@" + EOF + + chmod 755 bin/gcc + chmod 755 bin/g++ + - name: 'Install dependencies (macOS)' + if: runner.os == 'macOS' + run: brew install boehmgc make sfml + - name: 'Install dependencies (Windows)' + if: runner.os == 'Windows' + shell: bash + run: | + mkdir dist + curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z + curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip + 7z x dist/mingw64.7z -odist + 7z x dist/dlls.zip -obin + echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" + + - name: 'Add build binaries to PATH' + shell: bash + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" + + - name: 'Build csources' + shell: bash + run: | + ncpu= + case '${{ runner.os }}' in + 'Linux') + ncpu=$(nproc) + ;; + 'macOS') + ncpu=$(sysctl -n hw.ncpu) + ;; + 'Windows') + ncpu=$NUMBER_OF_PROCESSORS + ;; + esac + [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 + + make -C csources -j $ncpu CC=gcc ucpu='${{ matrix.cpu }}' + - name: 'Build koch' + shell: bash + run: nim c koch + - name: 'Run CI' + shell: bash + run: ./koch runCI + + - name: 'Show failed tests' + if: failure() + shell: bash + run: nim c -r tools/ci_testresults.nim diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index cde30e5fa..74ca804f3 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -6,7 +6,6 @@ on: - 'compiler/renderverbatim.nim' - 'config/nimdoc.cfg' - 'doc/**.rst' - - 'doc/**.md' - 'doc/nimdoc.css' - 'lib/**.nim' - 'nimdoc/testproject/expected/testproject.html' @@ -14,7 +13,6 @@ on: - 'tools/kochdocs.nim' - '.github/workflows/ci_docs.yml' - 'koch.nim' - pull_request: # Run only on changes on these files. paths: @@ -22,42 +20,36 @@ on: - 'compiler/renderverbatim.nim' - 'config/nimdoc.cfg' - 'doc/**.rst' - - 'doc/**.md' - 'doc/nimdoc.css' - 'lib/**.nim' - 'nimdoc/testproject/expected/testproject.html' - 'tools/dochack/dochack.nim' - 'tools/kochdocs.nim' - '.github/workflows/ci_docs.yml' - - 'koch.nim' -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true jobs: build: + if: | + !contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: target: [linux, windows, osx] include: - target: linux - os: ubuntu-20.04 + os: ubuntu-18.04 - target: windows os: windows-2019 - target: osx - os: macos-11 + os: macos-10.15 name: ${{ matrix.target }} runs-on: ${{ matrix.os }} - timeout-minutes: 60 # refs bug #18178 steps: - name: 'Checkout' - uses: actions/checkout@v3 - with: - fetch-depth: 2 + uses: actions/checkout@v2 - name: 'Install build dependencies (macOS)' if: runner.os == 'macOS' @@ -67,33 +59,64 @@ jobs: if: runner.os == 'Windows' shell: bash run: | - set -e - . ci/funs.sh - nimInternalInstallDepsWindows + mkdir dist + curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z + curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip + 7z x dist/mingw64.7z -odist + 7z x dist/dlls.zip -obin 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' + - name: 'Get current csources version' + id: csources-version shell: bash - run: . ci/funs.sh && nimCiSystemInfo + run: | + sha=$(git ls-remote https://github.com/nim-lang/csources master | cut -f 1) + echo "::set-output name=sha::$sha" - - name: 'Build csourcesAny (posix)' - # this would work on windows and other CI use this on windows, - # but we ensure here that `ci/build_autogen.bat` keeps working on windows. - if: runner.os != 'Windows' + - name: 'Get prebuilt csources from cache' + id: csources-cache + uses: actions/cache@v1 + with: + path: bin + key: '${{ matrix.os }}-${{ steps.csources-version.outputs.sha }}' + + - name: 'Checkout csources' + if: steps.csources-cache.outputs.cache-hit != 'true' + uses: actions/checkout@v2 + with: + repository: nim-lang/csources + path: csources + + - name: 'Build 1-stage compiler from csources' shell: bash - run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc - # was previously using caching via `actions/cache@v1` but this wasn't - # used in other CI pipelines and it's unclear the added complexity - # was worth the saving; can be revisited if needed. + run: | + ext= + [[ '${{ runner.os }}' == 'Windows' ]] && ext=.exe + if [[ ! -x bin/nim-csources$ext ]]; then + ncpu= + case '${{ runner.os }}' in + 'Linux') + ncpu=$(nproc) + ;; + 'macOS') + ncpu=$(sysctl -n hw.ncpu) + ;; + 'Windows') + ncpu=$NUMBER_OF_PROCESSORS + ;; + esac + [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 - - name: 'Build csourcesAny (windows)' - if: runner.os == 'Windows' - shell: cmd - run: ci/build_autogen.bat + make -C csources -j $ncpu CC=gcc + cp bin/nim{,-csources}$ext + else + echo 'Cache hit, using prebuilt csources' + cp bin/nim{-csources,}$ext + fi - name: 'Build koch' shell: bash @@ -103,6 +126,10 @@ jobs: shell: bash run: ./koch boot -d:release + - name: 'Clone fusion' + shell: bash + run: ./koch fusion + - name: 'Build documentation' shell: bash run: ./koch doc --git.commit:devel @@ -111,7 +138,7 @@ jobs: if: | github.event_name == 'push' && github.ref == 'refs/heads/devel' && matrix.target == 'linux' - uses: crazy-max/ghaction-github-pages@v3 + uses: crazy-max/ghaction-github-pages@v1 with: build_dir: doc/html env: diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 0bec4cc21..59e918f58 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -1,42 +1,33 @@ name: Packages CI -on: - pull_request: - push: - branches: - - 'devel' - - 'version-2-0' - - 'version-1-6' - - 'version-1-2' - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true +on: [push, pull_request] jobs: build: + if: | + !contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: - os: [ubuntu-20.04, macos-11] + os: [ubuntu-18.04, macos-10.15] cpu: [amd64] - batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num` - name: '${{ matrix.os }} (batch: ${{ matrix.batch }})' + pkg: [1, 2] + name: '${{ matrix.os }} (pkg: ${{ matrix.pkg }})' runs-on: ${{ matrix.os }} - timeout-minutes: 60 # refs bug #18178 env: - NIM_TEST_PACKAGES: "1" - NIM_TESTAMENT_BATCH: ${{ matrix.batch }} + NIM_TEST_PACKAGES: ${{ matrix.pkg }} steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v2 + - name: 'Checkout csources' + uses: actions/checkout@v2 with: - fetch-depth: 2 + repository: nim-lang/csources + path: csources - - name: 'Install node.js 16.x' - uses: actions/setup-node@v3 + - name: 'Install node.js 12.x' + uses: actions/setup-node@v1 with: - node-version: '16.x' - + node-version: '12.x' - name: 'Install dependencies (Linux amd64)' if: runner.os == 'Linux' && matrix.cpu == 'amd64' run: | @@ -52,23 +43,43 @@ jobs: if: runner.os == 'Windows' shell: bash run: | - set -e - . ci/funs.sh - nimInternalInstallDepsWindows - echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" + mkdir dist + curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z + curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip + 7z x dist/mingw64.7z -odist + 7z x dist/dlls.zip -obin + 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' + - name: 'Build csources' shell: bash - run: . ci/funs.sh && nimCiSystemInfo + run: | + ncpu= + case '${{ runner.os }}' in + 'Linux') + ncpu=$(nproc) + ;; + 'macOS') + ncpu=$(sysctl -n hw.ncpu) + ;; + 'Windows') + ncpu=$NUMBER_OF_PROCESSORS + ;; + esac + [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 - - name: 'Build csourcesAny' + make -C csources -j $ncpu CC=gcc ucpu='${{ matrix.cpu }}' + - name: 'Build koch' shell: bash - run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}' + run: nim c koch + - name: 'Run CI' + shell: bash + run: ./koch runCI - - name: 'koch, Run CI' + - name: 'Show failed tests' + if: failure() shell: bash - run: . ci/funs.sh && nimInternalBuildKochAndRunCI + run: nim c -r tools/ci_testresults.nim diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml deleted file mode 100644 index d6b628d8c..000000000 --- a/.github/workflows/ci_publish.yml +++ /dev/null @@ -1,90 +0,0 @@ -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@v3 - with: - fetch-depth: 2 - - - name: 'Install node.js 16.x' - uses: actions/setup-node@v3 - 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/.github/workflows/ci_ssl.yml b/.github/workflows/ci_ssl.yml new file mode 100644 index 000000000..0ec7aeb3b --- /dev/null +++ b/.github/workflows/ci_ssl.yml @@ -0,0 +1,93 @@ +name: Nim SSL CI +on: + pull_request: + # Run only on changes on related files + paths: + - 'lib/pure/httpclient.nim' + - 'lib/pure/net.nim' + - 'lib/pure/ssl_certs.nim' + - 'lib/wrappers/openssl.nim' + - 'tests/stdlib/thttpclient_ssl*' + - 'tests/untestable/thttpclient_ssl*' + +jobs: + build: + if: | + !contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04, macos-10.15, windows-2019] + cpu: [amd64] + name: '${{ matrix.os }} (${{ matrix.cpu }})' + runs-on: ${{ matrix.os }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + + - name: 'Checkout csources' + uses: actions/checkout@v2 + with: + repository: nim-lang/csources + path: csources + + - 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 -y libssl1.1 + - name: 'Install dependencies (macOS)' + if: runner.os == 'macOS' + run: brew install make + - name: 'Install dependencies (Windows)' + if: runner.os == 'Windows' + shell: bash + run: | + mkdir dist + curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z + curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip + 7z x dist/mingw64.7z -odist + 7z x dist/dlls.zip -obin + echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" + + - name: 'Add build binaries to PATH' + shell: bash + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" + + - name: 'Build 1-stage compiler from csources' + shell: bash + run: | + ncpu= + case '${{ runner.os }}' in + 'Linux') + ncpu=$(nproc) + ;; + 'macOS') + ncpu=$(sysctl -n hw.ncpu) + ;; + 'Windows') + ncpu=$NUMBER_OF_PROCESSORS + ;; + esac + [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 + + make -C csources -j $ncpu CC=gcc ucpu='${{ matrix.cpu }}' + + - name: 'Build koch' + shell: bash + run: nim c koch + + - name: 'Build the real compiler' + shell: bash + run: ./koch boot + + - name: 'Run SSL nimDisableCertificateValidation integration tests' + shell: bash + run: nim c -d:nimDisableCertificateValidation -d:ssl -r -p:. tests/untestable/thttpclient_ssl_disabled.nim + + - name: 'Run SSL certificate check integration tests' + # Not supported on Windows due to old openssl version + if: runner.os != 'Windows' + shell: bash + run: nim c -d:ssl -p:. --threads:on -r tests/untestable/thttpclient_ssl.nim diff --git a/.gitignore b/.gitignore index d0c4558df..e55f2c5f0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,9 @@ !*.* # Cache -nimcache*/ -rnimcache*/ -dnimcache*/ +nimcache/ +rnimcache/ +dnimcache/ *.o !/icons/*.o @@ -30,7 +30,7 @@ deinstall.sh doc/html/ doc/*.html -doc/pdf +doc/*.pdf doc/*.idx /web/upload /build/* @@ -64,30 +64,22 @@ lib/**/*.html testament.db /tests/**/*.json /tests/**/*.js - /csources -/csources_v1 -/csources_v2 - /dist/ -# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it +/lib/fusion # Private directories and files (IDEs) .*/ ~* -# testament cruft; TODO: generate these in a gitignore'd dir (./build) in the first place. +# testament cruft; TODO: generate these in a gitignore'd dir in the first place. testresults/ test.txt /test.ini tweeter.db tweeter_test.db - -/tests/megatest.nim -/tests/ic/*_temp.nim -/tests/navigator/*_temp.nim - +megatest.nim /outputExpected.txt /outputGotten.txt @@ -108,5 +100,3 @@ htmldocs ## these are not needed anymore unless checkout old older versions nimdoc.out.css -# except here: -!/nimdoc/testproject/expected/* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 31908d223..9f536e6c9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,4 @@ -# xxx unused, out of date - -image: ubuntu:18.04 +image: ubuntu:16.04 stages: - pre-build @@ -56,7 +54,7 @@ test-windows: script: - call ci\deps.bat - nim c testament\tester - - testament\tester.exe all + - testament\tester.exe --pedantic all tags: - windows - fast diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..4c4c35334 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,52 @@ +sudo: false +language: c + +dist: xenial + +matrix: + include: + - os: linux + env: + - NIM_COMPILE_TO_CPP=false + - CPU=amd64 + +addons: + apt: + # update the list above if more deps are introduced + packages: + - libcurl4-openssl-dev + - libsdl1.2-dev + - libgc-dev + - libsfml-dev + - libc6-dbg + - valgrind + +before_script: + - git clone --depth 1 https://github.com/nim-lang/csources.git + - export PATH="$PWD/bin${PATH:+:$PATH}" + - make -C csources -j 2 LD=$CC ucpu=$CPU + +script: + - echo "travis_fold:start:nim_c_koch" + - nim c koch + - echo "travis_fold:end:nim_c_koch" + - echo "travis_fold:start:koch_boot" + - ./koch boot + - echo "travis_fold:end:koch_boot" + - echo "travis_fold:start:koch_doc" + - ./koch doc + - echo "travis_fold:end:koch_doc" + +before_deploy: + # Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html + - cp -f ./doc/html/overview.html ./doc/html/index.html + +deploy: # https://nim-lang.github.io/Nim + provider: pages + # local-dir *has* to be a relative path from the repo root. + local-dir: "doc/html" + skip-cleanup: true + github-token: "$GITHUB_OAUTH_TOKEN" + keep-history: false + on: + branch: devel diff --git a/appveyor.yml.disabled b/appveyor.yml.disabled new file mode 100644 index 000000000..5468ac88a --- /dev/null +++ b/appveyor.yml.disabled @@ -0,0 +1,37 @@ +version: '{build}' + +environment: + DLLS_URL: https://nim-lang.org/download/dlls.zip + DLLS_ARCHIVE: dlls.zip + MINGW_DIR: mingw64 + MINGW_URL: https://nim-lang.org/download/mingw64.7z + MINGW_ARCHIVE: mingw64.7z + + matrix: + - NIM_TEST_PACKAGES: false + - NIM_TEST_PACKAGES: true + +cache: + - '%MINGW_ARCHIVE%' + - '%DLLS_ARCHIVE%' + + +install: + - ps: Install-Product node 8 # node 8 or later is required to test js async stuff + - IF not exist "%DLLS_ARCHIVE%" appveyor DownloadFile "%DLLS_URL%" -FileName "%DLLS_ARCHIVE%" + - 7z x -y "%DLLS_ARCHIVE%" -o"%CD%\BIN"> nul + - IF not exist "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%" + - 7z x -y "%MINGW_ARCHIVE%" -o"%CD%\DIST"> nul + - SET PATH=%CD%\DIST\%MINGW_DIR%\BIN;%CD%\BIN;%PATH% + - git clone --depth 1 https://github.com/nim-lang/csources + - cd csources + - build64.bat + - cd .. + +build_script: + - openssl version + - openssl version -d + - bin\nim c koch + - koch runCI + +deploy: off diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d034e3b9b..109efc6e1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,17 +1,12 @@ trigger: branches: include: - - 'devel' - - 'version-*' + - '*' pr: branches: include: - '*' -variables: -- name: skipci - value: false - jobs: - job: packages @@ -20,19 +15,16 @@ jobs: strategy: matrix: Linux_amd64: - vmImage: 'ubuntu-20.04' + vmImage: 'ubuntu-16.04' CPU: amd64 - # regularly breaks, refs bug #17325 Linux_i386: - # on 'ubuntu-16.04' (not supported anymore anyways) it errored with: - # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed - vmImage: 'ubuntu-18.04' + vmImage: 'ubuntu-16.04' CPU: i386 OSX_amd64: - vmImage: 'macOS-11' + vmImage: 'macOS-10.15' CPU: amd64 OSX_amd64_cpp: - vmImage: 'macOS-11' + vmImage: 'macOS-10.15' CPU: amd64 NIM_COMPILE_TO_CPP: true Windows_amd64_batch0_3: @@ -58,54 +50,43 @@ jobs: steps: - bash: git config --global core.autocrlf false displayName: 'Disable auto conversion to CRLF by git (Windows-only)' - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + condition: eq(variables['Agent.OS'], 'Windows_NT') - checkout: self - fetchDepth: 2 # see D20210329T004830 + fetchDepth: 1 - - bash: | - set -e - . ci/funs.sh - if nimIsCiSkip; then - echo '##vso[task.setvariable variable=skipci]true' - fi - displayName: 'Check whether to skip CI' + - bash: git clone --depth 1 https://github.com/nim-lang/csources + displayName: 'Checkout Nim csources' - task: NodeTool@0 inputs: - versionSpec: '16.x' - displayName: 'Install node.js 16.x' - condition: and(succeeded(), eq(variables['skipci'], 'false')) + versionSpec: '12.x' + displayName: 'Install node.js 12.x' - bash: | - set -e - . ci/funs.sh - echo_run sudo apt-fast update -qq + sudo apt-fast update -qq DEBIAN_FRONTEND='noninteractive' \ - echo_run sudo apt-fast install --no-install-recommends -yq \ + sudo apt-fast install --no-install-recommends -yq \ libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg displayName: 'Install dependencies (amd64 Linux)' - condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64')) + condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64')) - bash: | - set -e - . ci/funs.sh - echo_run sudo dpkg --add-architecture i386 + sudo dpkg --add-architecture i386 + # Downgrade llvm: # - llvm has to be downgraded to have 32bit version installed for sfml. - cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel Package: libllvm6.0 Pin: origin "azure.archive.ubuntu.com" Pin-Priority: 1001 EOF - # echo_run sudo apt-fast update -qq - echo_run sudo apt-fast update -qq || echo "failed, see bug #17343" + sudo apt-fast update -qq # `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get: # `could not load: libffi.so` during dynamic loading. DEBIAN_FRONTEND='noninteractive' \ - echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \ + sudo apt-fast install --no-install-recommends --allow-downgrades -yq \ g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \ libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386 @@ -114,55 +95,86 @@ jobs: exec $(which gcc) -m32 "\$@" EOF - cat << EOF > bin/g++ #!/bin/bash exec $(which g++) -m32 "\$@" EOF - echo_run chmod 755 bin/gcc - echo_run chmod 755 bin/g++ + chmod 755 bin/gcc + chmod 755 bin/g++ displayName: 'Install dependencies (i386 Linux)' - condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386')) + condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386')) - bash: brew install boehmgc make sfml displayName: 'Install dependencies (OSX)' - condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin')) + condition: eq(variables['Agent.OS'], 'Darwin') - bash: | - set -e - . ci/funs.sh - nimInternalInstallDepsWindows - echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin' + mkdir dist + curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z + curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip + 7z x dist/mingw64.7z -odist + 7z x dist/dlls.zip -obin + echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin' displayName: 'Install dependencies (Windows)' - condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Windows_NT')) + condition: eq(variables['Agent.OS'], 'Windows_NT') - bash: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/bin' - condition: and(succeeded(), eq(variables['skipci'], 'false')) displayName: 'Add build binaries to PATH' - - bash: . ci/funs.sh && nimCiSystemInfo - condition: and(succeeded(), eq(variables['skipci'], 'false')) + - bash: | + echo 'PATH:' "$PATH" + echo '##[section]gcc version' + gcc -v + echo '##[section]nodejs version' + node -v + echo '##[section]make version' + make -v displayName: 'System information' - - bash: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu=$(CPU) - condition: and(succeeded(), eq(variables['skipci'], 'false')) - displayName: 'Build csourcesAny' + - bash: echo '##vso[task.setvariable variable=csources_version]'"$(git -C csources rev-parse HEAD)" + displayName: 'Get csources version' - # this could be revived if performance justifies it (needs a few updates) - # - task: Cache@2 - # inputs: - # key: 'csourcesAny | "$(Agent.OS)" | $(CPU) | $(csources_version)' - # path: $(nim_csources) - # condition: and(succeeded(), eq(variables['skipci'], 'false')) - # displayName: 'Restore built csourcesAny' + - task: Cache@2 + inputs: + key: 'csources | "$(Agent.OS)" | $(CPU) | $(csources_version)' + path: csources/bin + displayName: 'Restore built csources' - - bash: . ci/funs.sh && nimInternalBuildKochAndRunCI - # would could also show on failure: echo '##vso[task.complete result=Failed]' - condition: and(succeeded(), eq(variables['skipci'], 'false')) - displayName: 'koch, Run CI' + - bash: | + ncpu= + ext= + case '$(Agent.OS)' in + 'Linux') + ncpu=$(nproc) + ;; + 'Darwin') + ncpu=$(sysctl -n hw.ncpu) + ;; + 'Windows_NT') + ncpu=$NUMBER_OF_PROCESSORS + ext=.exe + ;; + esac + [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 + + if [[ -x csources/bin/nim$ext ]]; then + echo "Found cached compiler, skipping build" + else + make -C csources -j $ncpu CC=gcc ucpu=$(CPU) koch=no + fi + + cp csources/bin/nim$ext bin + displayName: 'Build 1-stage compiler from csources' + + - bash: nim c koch + displayName: 'Build koch' + + # set result to omit the "bash exited with error code '1'" message + - bash: ./koch runCI || echo '##vso[task.complete result=Failed]' + displayName: 'Run CI' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/bin/nim-gdb b/bin/nim-gdb index d4d60e432..f7f9a79c6 100755 --- a/bin/nim-gdb +++ b/bin/nim-gdb @@ -7,14 +7,14 @@ which nim > /dev/null || (echo "nim not in PATH"; exit 1) which gdb > /dev/null || (echo "gdb not in PATH"; exit 1) which readlink > /dev/null || (echo "readlink not in PATH."; exit 1) -if [[ $(uname -s) == Darwin || $(uname -s) == *BSD ]]; then +if [[ "$(uname -s)" == "Darwin" || "(uname -s)" == *"BSD" ]]; then NIM_SYSROOT=$(dirname $(dirname $(readlink -f $(which nim)))) else NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim)))) fi # Find out where the pretty printer Python module is -GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/debug/nim-gdb.py" +GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py" # Run GDB with the additional arguments that load the pretty printers # Set the environment variable `NIM_GDB` to overwrite the call to a diff --git a/bin/nim-gdb.bash b/bin/nim-gdb.bash new file mode 120000 index 000000000..f6eba4b0c --- /dev/null +++ b/bin/nim-gdb.bash @@ -0,0 +1 @@ +nim-gdb \ No newline at end of file diff --git a/bin/nim-gdb.bat b/bin/nim-gdb.bat index b5537dd9d..e98a2063c 100644 --- a/bin/nim-gdb.bat +++ b/bin/nim-gdb.bat @@ -3,7 +3,7 @@ for %%i in (nim.exe) do (set NIM_BIN=%%~dp$PATH:i) for %%i in ("%NIM_BIN%\..\") do (set NIM_ROOT=%%~fi) -set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\debug\nim-gdb.py +set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\nim-gdb.py set @NIM_GDB=gdb.exe @echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt diff --git a/build_all.bat b/build_all.bat index ef4a5f6a4..7967e2584 100644 --- a/build_all.bat +++ b/build_all.bat @@ -1,29 +1,17 @@ @echo off -rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` -rem Build development version of the compiler; can be rerun safely -rem bare bones version of ci/funs.sh adapted for windows. - -rem Read in some common shared variables (shared with other tools), -rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file -for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H -SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe -echo "building from csources: %nim_csources%" - -if not exist %nim_csourcesDir% ( - git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir% +rem build development version of the compiler; can be rerun safely +if not exist csources ( + git clone --depth 1 https://github.com/nim-lang/csources.git ) - -if not exist %nim_csources% ( - cd %nim_csourcesDir% - git checkout %nim_csourcesHash% - echo "%PROCESSOR_ARCHITECTURE%" - if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( +if not exist bin\nim.exe ( + cd csources + if PROCESSOR_ARCHITECTURE == AMD64 ( SET ARCH=64 ) CALL build.bat cd .. - copy /y bin\nim.exe %nim_csources% ) - bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch - koch boot -d:release --skipUserCfg --skipParentCfg --hints:off - koch tools --skipUserCfg --skipParentCfg --hints:off +bin\nim.exe c --skipUserCfg --skipParentCfg koch +koch.exe boot -d:release --skipUserCfg --skipParentCfg +koch.exe tools --skipUserCfg --skipParentCfg + diff --git a/build_all.sh b/build_all.sh index 83848f41a..e66980e56 100755 --- a/build_all.sh +++ b/build_all.sh @@ -1,17 +1,52 @@ -#!/bin/sh -# DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` +#! /bin/sh # build development version of the compiler; can be rerun safely. -# arguments can be passed, e.g.: -# CC=gcc ucpu=amd64 uos=darwin +# arguments can be passed, e.g. `--os freebsd` set -u # error on undefined variables set -e # exit on first error -. ci/funs.sh -nimBuildCsourcesIfNeeded "$@" +echo_run(){ + echo "$*" + "$@" +} -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 +[ -d csources ] || echo_run git clone -q --depth 1 https://github.com/nim-lang/csources.git + +nim_csources=bin/nim_csources + +build_nim_csources_via_script(){ + echo_run cd csources + echo_run sh build.sh "$@" +} + +build_nim_csources(){ + # avoid changing dir in case of failure + ( + if [ $# -ne 0 ]; then + # some args were passed (e.g.: `--cpu i386`), need to call build.sh + build_nim_csources_via_script "$@" + else + # no args, use multiple Make jobs (5X faster on 16 cores: 10s instead of 50s) + makeX=make + unamestr=$(uname) + if [ "$unamestr" = 'FreeBSD' ]; then + makeX=gmake + fi + nCPU=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1) + which $makeX && echo_run $makeX -C csources -j $((nCPU + 2)) -l $nCPU || build_nim_csources_via_script + fi + ) + # keep $nim_csources in case needed to investigate bootstrap issues + # without having to rebuild from csources + echo_run cp bin/nim $nim_csources +} + +[ -f $nim_csources ] || echo_run build_nim_csources $@ + +# Note: if fails, may need to `cd csources && git pull` +echo_run bin/nim c --skipUserCfg --skipParentCfg koch + +echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg +echo_run ./koch tools --skipUserCfg --skipParentCfg # Compile Nimble and other tools. diff --git a/changelog.md b/changelog.md index 2db577d0b..306cf54d3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,31 +1,125 @@ -# v2.2.0 - yyyy-mm-dd +# v1.6.x - yyyy-mm-dd -## Changes affecting backward compatibility - ## Standard library additions and changes -[//]: # "Changes:" +- Make `{.requiresInit.}` pragma to work for `distinct` types. + +- Added a macros `enumLen` for returning the number of items in an enum to the + `typetraits.nim` module. + +- `prelude` now works with the JavaScript target. + +- Added `almostEqual` in `math` for comparing two float values using a machine epsilon. + +- The JSON module can now handle integer literals and floating point literals of + arbitrary length and precision. + Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are + kept as string literals and one can use external BigNum libraries to handle these. + The `parseFloat` family of functions also has now optional `rawIntegers` and + `rawFloats` parameters that can be used to enforce that all integer or float + literals remain in the "raw" string form so that client code can easily treat + small and large numbers uniformly. + +- Added an overload for the `collect` macro that inferes the container type based + on the syntax of the last expression. Works with std seqs, tables and sets. + +- Added `randState` template that exposes the default random number generator. + Useful for library authors. + +- Added std/enumutils module containing `genEnumCaseStmt` macro that generates + case statement to parse string to enum. + +- Removed deprecated `iup` module from stdlib, it has already moved to + [nimble](https://github.com/nim-lang/iup). + +- `nodejs` backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`. + +- Added `cmpMem` to `system`. + +- `doAssertRaises` now correctly handles foreign exceptions. + +- Added `asyncdispatch.activeDescriptors` that returns the number of currently + active async event handles/file descriptors. + +- ``--gc:orc`` is now 10% faster than previously for common workloads. If + you have trouble with its changed behavior, compile with ``-d:nimOldOrc``. -[//]: # "Additions:" -- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes. +- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`, + determining preferred I/O block size for this file object. -[//]: # "Deprecations:" +- `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, + see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. +- Added `**` to jsffi. -[//]: # "Removals:" +- `writeStackTrace` is available in JS backend now. + +- Added `decodeQuery` to `std/uri`. +- `strscans.scanf` now supports parsing single characters. +- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`. + +- Added `setutils.toSet` that can take any iterable and convert it to a built-in set, + if the iterable yields a built-in settable type. + +- Added `math.isNaN`. + +- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior + silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior. + +- Added `math.copySign`. +- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList` + and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements + shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes + its argument, `addMoved`, is also supplied. + +- Added `sequtils` import to `prelude`. + +- Added `euclDiv` and `euclMod` to `math`. +- Added `httpcore.is1xx` and missing HTTP codes. +- Added `jsconsole.jsAssert` for JavaScript target. + +- Added `mimetypes.mimesExtMaxLen` thats equal to the length of the longest "ext" from `mimes`. +- Added `mimetypes.mimesMaxLen` thats equal to the length of the longest "mime" from `mimes`. + +- Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs. + https://www.freedesktop.org/software/systemd/man/os-release.html + +- Added `BackwardsIndex` overload for `JsonNode`. + +- `math.round` now is rounded "away from zero" in JS backend which is consistent +with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior. ## Language changes +- `nimscript` now handles `except Exception as e`. + +- The `cstring` doesn't support `[]=` operator in JS backend. + +- nil dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time. ## Compiler changes +- Added `--declaredlocs` to show symbol declaration location in messages. +- Source+Edit links now appear on top of every docgen'd page when + `nim doc --git.url:url ...` is given. + +- Added `nim --eval:cmd` to evaluate a command directly, see `nim --help`. + +- VM now supports `addr(mystring[ind])` (index + index assignment) +- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous + behavior. ## Tool changes +- The rst parser now supports markdown table syntax. + Known limitations: + - cell alignment is not supported, i.e. alignment annotations in a delimiter + row (`:---`, `:--:`, `---:`) are ignored, + - every table row must start with `|`, e.g. `| cell 1 | cell 2 |`. diff --git a/changelogs/changelog_0_19_0.md b/changelogs/changelog_0_19_0.md index 45cefe933..18d3ca2b3 100644 --- a/changelogs/changelog_0_19_0.md +++ b/changelogs/changelog_0_19_0.md @@ -15,7 +15,7 @@ at runtime. Use ``ref object`` with inheritance rather than ``object`` with inheritance to prevent this issue. - The ``not nil`` type annotation now has to be enabled explicitly - via ``{.experimental: "notnil".}`` as we are still not pleased with how this + via ``{.experimental: "notnil"}`` as we are still not pleased with how this feature works with Nim's containers. - The parser now warns about inconsistent spacing around binary operators as these can easily be confused with unary operators. This warning will likely diff --git a/changelogs/changelog_1_4_0.md b/changelogs/changelog_1_4_0.md index 77f66ffde..091048ad1 100644 --- a/changelogs/changelog_1_4_0.md +++ b/changelogs/changelog_1_4_0.md @@ -282,7 +282,7 @@ - Removed the deprecated `asyncdispatch.newAsyncNativeSocket`. - Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`. -- Removed `sharedlist.initSharedList`, was deprecated and produces undefined behaviour. +- Removed `sharedlists.initSharedList`, was deprecated and produces undefined behaviour. - There is a new experimental feature called "strictFuncs" which makes the definition of `.noSideEffect` stricter. [See here](manual_experimental.html#stricts-funcs) diff --git a/changelogs/changelog_1_6_0.md b/changelogs/changelog_1_6_0.md deleted file mode 100644 index 4a1047d20..000000000 --- a/changelogs/changelog_1_6_0.md +++ /dev/null @@ -1,957 +0,0 @@ -# v1.6.0 - 2021-10-14 - - - -# Major new features - -With so many new features, pinpointing the most salient ones is a subjective exercise, -but here are a select few: - - -## `iterable[T]` - -The `iterable[T]` type class was added to match called iterators, -which solves a number of long-standing issues related to iterators. -Example: - -```nim -iterator iota(n: int): int = - for i in 0.. response.json()) - .then((json: JsObject) => console.log(json)) - .catch((err: Error) => console.log("Request Failed", err)) -discard fn() -``` - - -## New `std/tasks` module - -Provides basic primitives for creating parallel programs. -Example: -```nim -import std/tasks -var num = 0 -proc hello(a: int) = num+=a - -let b = toTask hello(13) # arguments must be isolated, see `std/isolation` -b.invoke() -assert num == 13 -b.invoke() # can be called again -assert num == 26 -``` - - -## New module: `std/genasts` - -Provides an API `genAst` that avoids the problems inherent with `quote do` and can -be used as a replacement. -Example showing how this could be used for writing a simplified version of `unittest.check`: -```nim -import std/[genasts, macros, strutils] -macro check2(cond: bool): untyped = - assert cond.kind == nnkInfix, "$# not implemented" % $cond.kind - result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]): - # each local symbol we access must be explicitly captured - if not cond: - doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs] -let a = 3 -check2 a*2 == a+3 -if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4' -``` - -See PR [#17426](https://github.com/nim-lang/Nim/pull/17426) for details. - - -## New module: `std/setutils` - -- Added `setutils.toSet` that can take any iterable and convert it to a built-in `set`, - if the iterable yields a built-in settable type. -- Added `setutils.fullSet` which returns a full built-in `set` for a valid type. -- Added `setutils.complement` which returns the complement of a built-in `set`. -- Added `setutils.[]=`. - - -## New module: `std/enumutils` - -- Added `genEnumCaseStmt` macro that generates - case statement to parse string to enum. -- Added `items` for enums with holes. -- Added `symbolName` to return the `enum` symbol name ignoring the human-readable name. -- Added `symbolRank` to return the index in which an `enum` member is listed in an enum. - - -## `system` - -- Added `system.prepareMutation` for better support of low - level `moveMem`, `copyMem` operations for `gc:orc`'s copy-on-write string - implementation. -- `system.addEscapedChar` now renders `\r` as `\r` instead of `\c`, to be compatible - with most other languages. -- Added `cmpMem` to `system`. -- `doAssertRaises` now correctly handles foreign exceptions. -- `addInt` now supports unsigned integers. - -Compatibility notes: -- `system.delete` had surprising behavior when the index passed to it was out of - bounds (it would delete the last entry then). Compile with `-d:nimStrictDelete` so - that an index error is produced instead. Be aware however that your code might depend on - this quirky behavior so a review process is required on your part before you can - use `-d:nimStrictDelete`. To make this review easier, use the `-d:nimAuditDelete` - switch, which pretends that `system.delete` is deprecated so that it is easier - to see where it was used in your code. - `-d:nimStrictDelete` will become the default in upcoming versions. -- `cuchar` is now deprecated as it aliased `char` where arguably it should have aliased `uint8`. - Please use `char` or `uint8` instead. -- `repr` now doesn't insert trailing newlines; the previous behavior was very inconsistent, - see [#16034](https://github.com/nim-lang/Nim/pull/16034). - Use `-d:nimLegacyReprWithNewline` for the previous behavior. - `repr` now also renders ASTs correctly for user defined literals, setters, `do`, etc. -- Deprecated `any`. See RFC [#281](https://github.com/nim-lang/RFCs/issues/281). -- The unary slice `..b` was deprecated, use `0..b` instead. - - -## `std/math` - -- Added `almostEqual` for comparing two float values using a machine epsilon. -- Added `clamp` which allows using a `Slice` to clamp to a value. -- Added `ceilDiv` for integer division that rounds up. -- Added `isNaN`. -- Added `copySign`. -- Added `euclDiv` and `euclMod`. -- Added `signbit`. -- Added `frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead. - -Compatibility notes: -- `math.round` now rounds "away from zero" in the JS backend, which is consistent - with other backends. See [#9125](https://github.com/nim-lang/Nim/pull/9125). - Use `-d:nimLegacyJsRound` for the previous behavior. - - -## Random number generators: `std/random`, `std/sysrand`, `std/oids` - -- Added `std/sysrand` module (see details above). -- Added `randState` template that exposes the default random number generator. - Useful for library authors. -- Added `initRand()` overload with no argument which uses the current time as a seed. -- `initRand(seed)` now allows `seed == 0`. -- Fixed overflow bugs. -- Fix `initRand` to avoid random number sequences overlapping, refs - [#18744](https://github.com/nim-lang/Nim/pull/18744). -- `std/oids` now uses `std/random`. - -Compatibility notes: -- Deprecated `std/mersenne`. -- `random.initRand(seed)` now produces non-skewed values for the first call to - `rand()` after initialization with a small (< 30000) seed. - Use `-d:nimLegacyRandomInitRand` to restore previous behavior for a transition - time, see PR [#17467](https://github.com/nim-lang/Nim/pull/17467). - - -## `std/json`, `std/jsonutils` - -- With `-d:nimPreviewJsonutilsHoleyEnum`, `jsonutils` now can serialize/deserialize - holey enums as regular enums (via `ord`) instead of as strings. - It is expected that this behavior becomes the new default in upcoming versions. - `toJson` now serializes `JsonNode` as is via reference (without a deep copy) - instead of treating `JsonNode` as a regular ref object, - this can be customized via `jsonNodeMode`. -- `std/json` and `std/jsonutils` now serialize `NaN`, `Inf`, `-Inf` as strings, - so that `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` - which was invalid JSON. -- `std/json` can now handle integer literals and floating point literals of - arbitrary length and precision. - Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are - kept as string literals and one can use external BigNum libraries to handle these. - The `parseFloat` family of functions also has now optional `rawIntegers` and - `rawFloats` parameters that can be used to enforce that all integer or float - literals remain in the "raw" string form so that client code can easily treat - small and large numbers uniformly. -- Added `BackwardsIndex` overload for `JsonNode`. -- `json.%`,`json.to`, `jsonutils.fromJson`,`jsonutils.toJson` now work with `uint|uint64` - instead of raising (as in 1.4) or giving wrong results (as in 1.2). -- `std/jsonutils` now handles `cstring` (including as Table key), and `set`. -- Added `jsonutils.jsonTo` overload with `opt = Joptions()` param. -- `jsonutils.toJson` now supports customization via `ToJsonOptions`. -- `std/json`, `std/jsonutils` now support round-trip serialization when - `-d:nimPreviewFloatRoundtrip` is used. - - -## `std/typetraits`, `std/compilesettings` - -- `distinctBase` now is identity instead of error for non distinct types. -- `distinctBase` now allows controlling whether to be recursive or not. -- Added `enumLen` to return the number of elements in an enum. -- Added `HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes. -- Added `hasClosure`. -- Added `pointerBase` to return `T` for `ref T | ptr T`. -- Added `compilesettings.SingleValueSetting.libPath`. - - -## networking: `std/net`, `std/asyncnet`, `std/htmlgen`, `std/httpclient`, `std/asyncdispatch`, `std/asynchttpserver`, `std/httpcore` - -- Fixed buffer overflow bugs in `std/net`. -- Exported `sslHandle` from `std/net` and `std/asyncnet`. -- Added `hasDataBuffered` to `std/asyncnet`. -- Various functions in `std/httpclient` now accept `url` of type `Uri`. - Moreover, the `request` function's `httpMethod` argument of type `string` was - deprecated in favor of `HttpMethod` `enum` type; see - [#15919](https://github.com/nim-lang/Nim/pull/15919). -- Added `asyncdispatch.activeDescriptors` that returns the number of currently - active async event handles/file descriptors. -- Added `getPort` to `std/asynchttpserver` to resolve OS-assigned `Port(0)`; - this is usually recommended instead of hardcoding ports which can lead to - "Address already in use" errors. -- Fixed premature garbage collection in `std/asyncdispatch`, when a stacktrace - override is in place. -- Added `httpcore.is1xx` and missing HTTP codes. -- Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals). - -Compatibility notes: -- On Windows, the SSL library now checks for valid certificates. - For this purpose it uses the `cacert.pem` file, which was extracted - from `https://curl.se/ca/cacert.pem`. Besides - the OpenSSL DLLs (e.g. `libssl-1_1-x64.dll`, `libcrypto-1_1-x64.dll`) you - now also need to ship `cacert.pem` with your `.exe` file. - - -## `std/hashes` - -- `hashes.hash` can now support `object` and `ref` (can be overloaded in user code), - if `-d:nimPreviewHashRef` is used. It is expected that this behavior - becomes the new default in upcoming versions. -- `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`. - `hashes.hash(closure)` has also been improved. - - -## OS: `std/os`, `std/io`, `std/socketstream`, `std/linenoise`, `std/tempfiles` - -- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`, - determining preferred I/O block size for this file object. -- Added `os.getCacheDir()` to return platform specific cache directory. -- Improved `os.getTempDir()`, see PR [#16914](https://github.com/nim-lang/Nim/pull/16914). -- Added `os.isAdmin` to tell whether the caller's process is a member of the - Administrators local group (on Windows) or a root (on POSIX). -- Added optional `options` argument to `copyFile`, `copyFileToDir`, and - `copyFileWithPermissions`. By default, on non-Windows OSes, symlinks are - followed (copy files symlinks point to); on Windows, `options` argument is - ignored and symlinks are skipped. -- On non-Windows OSes, `copyDir` and `copyDirWithPermissions` copy symlinks as - symlinks (instead of skipping them as it was before); on Windows symlinks are - skipped. -- On non-Windows OSes, `moveFile` and `moveDir` move symlinks as symlinks - (instead of skipping them sometimes as it was before). -- Added optional `followSymlinks` argument to `setFilePermissions`. -- Added a simpler to use `io.readChars` overload. -- Added `socketstream` module that wraps sockets in the stream interface. -- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C). - - -## Environment variable handling - -- Empty environment variable values are now supported across OS's and backends. -- Environment variable APIs now work in multithreaded scenarios, by delegating to - direct OS calls instead of trying to keep track of the environment. -- `putEnv`, `delEnv` now work at compile time. -- NodeJS backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`. - -Compatibility notes: -- `std/os`: `putEnv` now raises if the first argument contains a `=`. - - -## POSIX - -- On POSIX systems, the default signal handlers used for Nim programs (it's - used for printing the stacktrace on fatal signals) will now re-raise the - signal for the OS default handlers to handle. - This lets the OS perform its default actions, which might include core - dumping (on select signals) and notifying the parent process about the cause - of termination. -- On POSIX systems, we now ignore `SIGPIPE` signals, use `-d:nimLegacySigpipeHandler` - for previous behavior. -- Added `posix_utils.osReleaseFile` to get system identification from `os-release` - file on Linux and the BSDs. - ([link](https://www.freedesktop.org/software/systemd/man/os-release.html)) -- Removed undefined behavior for `posix.open`. - - -## `std/prelude` - -- `std/strformat` is now part of `include std/prelude`. -- Added `std/sequtils` import to `std/prelude`. -- `std/prelude` now works with the JS target. -- `std/prelude` can now be used via `include std/prelude`, but `include prelude` still works. - - -## String manipulation: `std/strformat`, `std/strbasics` - -- Added support for parenthesized expressions. -- Added support for const strings instead of just string literals. -- Added `std/strbasics` for high-performance string operations. -- Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`. - - -## `std/wrapnils` - -- `std/wrapnils` doesn't use `experimental:dotOperators` anymore, avoiding - issues like bug [#13063](https://github.com/nim-lang/Nim/issues/13063) - (which affected error messages) for modules importing `std/wrapnils`. -- Added `??.` macro which returns an `Option`. -- `std/wrapnils` can now be used to protect against `FieldDefect` errors in - case objects, generates optimal code (no overhead compared to manual - if-else branches), and preserves lvalue semantics which allows modifying - an expression. - - -## Containers: `std/algorithm`, `std/lists`, `std/sequtils`, `std/options`, `std/packedsets` - -- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix` - iterators (it never worked reliably). -- Added `algorithm.merge`. -- In `std/lists`: renamed `append` to `add` and retained `append` as an alias; - added `prepend` and `prependMoved` analogously to `add` and `addMoved`; - added `remove` for `SinglyLinkedList`s. -- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList` - and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements - shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes - its argument, `addMoved`, is also supplied. - See PRs [#16362](https://github.com/nim-lang/Nim/pull/16362), - [#16536](https://github.com/nim-lang/Nim/pull/16536). -- New module: `std/packedsets`. - Generalizes `std/intsets`, see PR [#15564](https://github.com/nim-lang/Nim/pull/15564). - -Compatibility notes: -- Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises - a defect if the slice is out of bounds, likewise with `strutils.delete`. -- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` - in `std/algorithm`. -- `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"` - and `$none(int)` to `"none(int)"` instead of `"None[int]"`. - - -## `std/times` - -- Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time - zone offsets without colons, e.g. `UTC+7 -> +0700`. -- Added `dateTime` and deprecated `initDateTime`. - - -## `std/macros` and AST - -- New module `std/genasts`, see description above. -- The required name of case statement macros for the experimental - `caseStmtMacros` feature has changed from `match` to `` `case` ``. -- Tuple expressions are now parsed consistently as - `nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`. -- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes - in a collapsed way. - Use `-d:nimLegacyMacrosCollapseSymChoice` to get the previous behavior. -- Made custom op in `macros.quote` work for all statements. - - -## `std/sugar` - -- Added `sugar.dumpToString` which improves on `sugar.dump`. -- Added an overload for the `collect` macro that infers the container type based - on the syntax of the last expression. Works with std seqs, tables and sets. - -Compatibility notes: -- Removed support for named procs in `sugar.=>`. - - -## Parsing: `std/parsecfg`, `std/strscans`, `std/uri` - -- Added `sections` iterator in `parsecfg`. -- `strscans.scanf` now supports parsing single characters. -- Added `strscans.scanTuple` which uses `strscans.scanf` internally, - returning a tuple which can be unpacked for easier usage of `scanf`. -- Added `decodeQuery` to `std/uri`. -- `parseopt.initOptParser` has been made available and `parseopt` has been - added back to `std/prelude` for all backends. Previously `initOptParser` was - unavailable if the `std/os` module did not have `paramCount` or `paramStr`, - but the use of these in `initOptParser` were conditionally to the runtime - arguments passed to it, so `initOptParser` has been changed to raise - `ValueError` when the real command line is not available. `parseopt` was - previously excluded from `std/prelude` for JS, as it could not be imported. - -Compatibility notes: -- Changed the behavior of `uri.decodeQuery` when there are unencoded `=` - characters in the decoded values. Prior versions would raise an error. This is - no longer the case to comply with the HTML spec and other languages' - implementations. Old behavior can be obtained with - `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same - underlying code is also updated the same way. - - -## JS stdlib changes - -- Added `std/jsbigints` module, which provides arbitrary precision integers for the JS target. -- Added `setCurrentException` for the JS backend. -- `writeStackTrace` is available in the JS backend now. -- Added `then`, `catch` to `std/asyncjs` for promise pipelining, for now hidden - behind `-d:nimExperimentalAsyncjsThen`. -- Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API) - wrapper for the JS target. -- Added `std/jsheaders` module [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) - wrapper for the JS target. -- Added `std/jsformdata` module [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) - wrapper for the JS target. -- Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger). -- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`. -- Added dollar `$` and `len` for `jsre.RegExp`. -- Added `jsconsole.jsAssert` for the JS target. -- Added `**` to `std/jsffi`. -- Added `copyWithin` [for `seq` and `array` for JS targets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin). -- In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. - Definitions of `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` were updated. -- Added `dom.scrollIntoView` proc with options. -- Added `dom.setInterval`, `dom.clearInterval` overloads. -- Merged `std/dom_extensions` into the `std/dom` module, - as it was a module with a single line, see RFC [#413](https://github.com/nim-lang/RFCs/issues/413). -- `$` now gives more correct results on the JS backend. - - -## JS compiler changes - -- `cstring` doesn't support the `[]=` operator anymore in the JS backend. -- Array literals now use JS typed arrays when the corresponding JS typed array exists, - for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`. - - -## VM and nimscript backend - -- VM now supports `addr(mystring[ind])` (index + index assignment). -- `nimscript` now handles `except Exception as e`. -- `nil` dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time. -- `static[T]` now works better, refs [#17590](https://github.com/nim-lang/Nim/pull/17590), - [#15853](https://github.com/nim-lang/Nim/pull/15853). -- `distinct T` conversions now work in VM. -- `items(cstring)` now works in VM. -- Fix `addr`, `len`, `high` in VM ([#16002](https://github.com/nim-lang/Nim/pull/16002), - [#16610](https://github.com/nim-lang/Nim/pull/16610)). -- `std/cstrutils` now works in VM. - - -## OS/platform-specific notes - -- Support for Apple silicon/M1. -- Support for 32-bit RISC-V, refs [#16231](https://github.com/nim-lang/Nim/pull/16231). -- Support for armv8l, refs [#18901](https://github.com/nim-lang/Nim/pull/18901). -- Support for CROSSOS, refs [#18889](https://github.com/nim-lang/Nim/pull/18889). -- The allocator for Nintendo Switch, which was nonfunctional because - of breaking changes in libnx, was removed, in favor of the new `-d:nimAllocPagesViaMalloc` option. -- Allow reading parameters when compiling for Nintendo Switch. -- `--nimcache` now correctly works in a cross-compilation setting. -- Cross compilation targeting Windows was improved. -- This now works from macOS/Linux: `nim r -d:mingw main` - - - -## Performance / memory optimizations - -- The comment field in PNode AST was moved to a side channel, reducing overall - memory usage during compilation by a factor 1.25x -- `std/jsonutils` deserialization is now up to 20x faster. -- `os.copyFile` is now 2.5x faster on macOS, by using `copyfile` from `copyfile.h`; - use `-d:nimLegacyCopyFile` for macOS < 10.5. -- Float to string conversion is now 10x faster thanks to the Dragonbox algorithm, - with `-d:nimPreviewFloatRoundtrip`. -- `newSeqWith` is 3x faster. -- CI now supports batching (making Windows CI 2.3X faster). -- Sets now uses the optimized `countSetBits` proc, see PR - [#17334](https://github.com/nim-lang/Nim/pull/17334). - - -## Debugging - -- You can now enable/disable VM tracing in user code via `vmutils.vmTrace`. -- `koch tools` now builds `bin/nim_dbg` which allows easy access to a debug version - of Nim without recompiling. -- Added new module `compiler/debugutils` to help with debugging Nim compiler. -- Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints` and - used it in more contexts; this flag which works in tandem with `--stackTraceMsgs` - to show user code context in compiler stacktraces. - - -## Type system - -- `typeof(voidStmt)` now works and returns `void`. -- `enum` values can now be overloaded. This needs to be enabled - via `{.experimental: "overloadableEnums".}`. We hope that this feature allows - for the development of more fluent (less ugly) APIs. - See RFC [#373](https://github.com/nim-lang/RFCs/issues/373) for more details. -- A type conversion from one `enum` type to another now produces an `[EnumConv]` warning. - You should use `ord` (or `cast`, but the compiler won't help, if you misuse it) instead. - ```nim - type A = enum a1, a2 - type B = enum b1, b2 - echo a1.B # produces a warning - echo a1.ord.B # produces no warning - ``` -- A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning. - This warning will become an error in future versions! Use an explicit conversion - like `cstring(x)` in order to silence the warning. -- There is a new warning for *any* type conversion to `enum` that can be enabled via - `.warning[AnyEnumConv]:on` or `--warning:AnyEnumConv:on`. -- Reusing a type name in a different scope now works, refs [#17710](https://github.com/nim-lang/Nim/pull/17710). -- Fixed implicit and explicit generics in procedures, refs [#18808](https://github.com/nim-lang/Nim/pull/18808). - - -## New-style concepts - -Example: -```nim -type - Comparable = concept # no T, an atom - proc cmp(a, b: Self): int -``` -The new design does not rely on `system.compiles` and may compile faster. -See PR [#15251](https://github.com/nim-lang/Nim/pull/15251) -and RFC [#168](https://github.com/nim-lang/RFCs/issues/168) for details. - - -## Lexical / syntactic - -- Nim now supports a small subset of Unicode operators as operator symbols. - The supported symbols are: "∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔". - To enable this feature, use `--experimental:unicodeOperators`. Note that due - to parser limitations you **cannot** enable this feature via a - pragma `{.experimental: "unicodeOperators".}` reliably, you need to enable - it via the command line or in a configuration file. -- `var a {.foo.} = expr` now works inside templates (except when `foo` is overloaded). - - -## Compiler messages, error messages, hints, warnings - -- Significant improvement to error messages involving effect mismatches, - see PRs [#18384](https://github.com/nim-lang/Nim/pull/18384), - [#18418](https://github.com/nim-lang/Nim/pull/18418). -- Added `--declaredLocs` to show symbol declaration location in error messages. -- Added `--spellSuggest` to show spelling suggestions on typos. -- Added `--processing:dots|filenames|off` which customizes `hintProcessing`; - `--processing:filenames` shows which include/import modules are being compiled as an import stack. -- `FieldDefect` messages now shows discriminant value + lineinfo, in all backends (C, JS, VM) -- Added `--hintAsError` with similar semantics as `--warningAsError`. -- Added `--unitsep:on|off` to control whether to add ASCII unit separator `\31` - before a newline for every generated message (potentially multiline), - so tooling can tell when messages start and end. -- Added `--filenames:abs|canonical|legacyRelProj` which replaces `--listFullPaths:on|off` -- `--hint:all:on|off` is now supported to select or deselect all hints; it - differs from `--hints:on|off` which acts as a (reversible) gate. - Likewise with `--warning:all:on|off`. -- The style checking of the compiler now supports a `--styleCheck:usages` switch. - This switch enforces that every symbol is written as it was declared, not enforcing - the official Nim style guide. To be enabled, this has to be combined either - with `--styleCheck:error` or `--styleCheck:hint`. -- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for - previous behavior. -- `typedesc[Foo]` now renders as such instead of `type Foo` in compiler messages. -- `runnableExamples` now show originating location in stacktraces on failure. -- `SuccessX` message now shows more useful information. -- New `DuplicateModuleImport` warning; improved `UnusedImport` and - `XDeclaredButNotUsed` accuracy. - -Compatibility notes: -- `--hint:CC` now prints to stderr (like all other hints) instead of stdout. - - -## Building and running Nim programs, configuration system - -- JSON build instructions are now generated in `$nimcache/outFileBasename.json` - instead of `$nimcache/projectName.json`. This allows avoiding recompiling a - given project compiled with different options if the output file differs. -- `--usenimcache` (implied by `nim r main`) now generates an output file that includes - a hash of some of the compilation options, which allows caching generated binaries: - ```bash - nim r main # recompiles - nim r -d:foo main # recompiles - nim r main # uses cached binary - nim r main arg1 arg2 # likewise (runtime arguments are irrelevant) - ``` -- `nim r` now supports cross compilation from unix to windows when specifying - `-d:mingw` by using Wine, e.g.: - `nim r --eval:'import os; echo "a" / "b"'` prints `a\b`. -- `nim` can compile version 1.4.0 as follows: - `nim c --lib:lib --stylecheck:off -d:nimVersion140 compiler/nim`. - `-d:nimVersion140` is not needed for bootstrapping, only for building 1.4.0 from devel. -- `nim e` now accepts arbitrary file extensions for the nimscript file, - although `.nims` is still the preferred extension in general. -- The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected. - The downside is that these defines now have custom logic that doesn't apply for - other defines. - - -## Multithreading - -- TLS: macOS now uses native TLS (`--tlsEmulation:off`). TLS now works with - `importcpp` non-POD types; such types must use `.cppNonPod` and - `--tlsEmulation:off`should be used. -- Added `unsafeIsolate` and `extract` to `std/isolation`. -- Added `std/tasks`, see description above. - - -## Memory management - -- `--gc:arc` now bootstraps (PR [#17342](https://github.com/nim-lang/Nim/pull/17342)). -- Lots of improvements to `gc:arc`, `gc:orc`, see PR - [#15697](https://github.com/nim-lang/Nim/pull/15697), - [#16849](https://github.com/nim-lang/Nim/pull/16849), - [#17993](https://github.com/nim-lang/Nim/pull/17993). -- `--gc:orc` is now 10% faster than previously for common workloads. - If you have trouble with its changed behavior, compile with `-d:nimOldOrc`. -- The `--gc:orc` algorithm was refined so that custom container types can participate in the - cycle collection process. See the documentation of `=trace` for more details. -- On embedded devices `malloc` can now be used instead of `mmap` via `-d:nimAllocPagesViaMalloc`. - This is only supported for `--gc:orc` or `--gc:arc`. - -Compatibility notes: -- `--newruntime` and `--refchecks` are deprecated, - use `--gc:arc`, `--gc:orc`, or `--gc:none` as appropriate instead. - - -## Docgen - -- docgen: RST files can now use single backticks instead of double backticks and - correctly render in both `nim rst2html` (as before) as well as common tools rendering - RST directly (e.g. GitHub). - This is done by adding the `default-role:: code` directive inside the RST file - (which is now handled by `nim rst2html`). -- Source+Edit links now appear on top of every docgen'd page when - `nim doc --git.url:url ...` is given. -- Latex doc generation is revised: output `.tex` files should be compiled - by `xelatex` (not by `pdflatex` as before). Now default Latex settings - provide support for Unicode and better avoid margin overflows. - The minimum required version is TeXLive 2018 (or an equivalent MikTeX version). -- The RST parser now supports footnotes, citations, admonitions, and short style - references with symbols. -- The RST parser now supports Markdown table syntax. - Known limitations: - - cell alignment is not supported, i.e. alignment annotations in a delimiter - row (`:---`, `:--:`, `---:`) are ignored - - every table row must start with `|`, e.g. `| cell 1 | cell 2 |`. -- Implemented `doc2tex` compiler command which converts documentation in - `.nim` files to Latex. -- docgen now supports syntax highlighting for inline code. -- docgen now supports same-line doc comments: - ```nim - func fn*(a: int): int = 42 ## Doc comment - ``` -- docgen now renders `deprecated` and other pragmas. -- `runnableExamples` now works with templates and nested templates. -- `runnableExamples: "-r:off"` now works for examples that should compile but not run. -- `runnableExamples` now renders code verbatim, and produces correct code in all cases. -- docgen now shows correct, canonical import paths in docs. -- docgen now shows all routines in sidebar, and the proc signature is now shown in sidebar. - - -## Effects and checks - -- Significant improvement to error messages involving effect mismatches -- There is a new `cast` section `{.cast(uncheckedAssign).}: body` that disables some - compiler checks regarding `case objects`. This allows serialization libraries - to avoid ugly, non-portable solutions. See RFC - [#407](https://github.com/nim-lang/RFCs/issues/407) for more details. - -Compatibility notes: -- Fixed effect tracking for borrowed procs (see [#18882](https://github.com/nim-lang/Nim/pull/18882)). - One consequence is that, under some circumstances, Nim could previously permit a procedure with side effects to be written with `func` - you may need to change some occurrences of `func` to `proc`. - To illustrate, Nim versions before 1.6.0 compile the below without error - ```nim - proc print(s: string) = - echo s - - type - MyString = distinct string - - proc print(s: MyString) {.borrow.} - - func foo(s: MyString) = - print(s) - ``` - but Nim 1.6.0 produces the error - ``` - Error: 'foo' can have side effects - ``` - similar to how we expect that - ```nim - func print(s: string) = - echo s - ``` - produces - ``` - Error: 'print' can have side effects - ``` - - -## Tools - -- Major improvements to `nimgrep`, see PR [#15612 -](https://github.com/nim-lang/Nim/pull/15612). -- `fusion` is now un-bundled from Nim, `./koch fusion` will - install it via Nimble at a fixed hash. -- `testament`: added `nimoutFull: bool` spec to compare full output of compiler - instead of a subset; many bugfixes to testament. - - -## Misc/cleanups - -- Deprecated `TaintedString` and `--taintmode`. -- Deprecated `--nilseqs` which is now a noop. -- Added `-d:nimStrictMode` in CI in several places to ensure code doesn't have - certain hints/warnings. -- Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`. -- `[skip ci]` now works in azure and CI pipelines, see detail in PR - [#17561](https://github.com/nim-lang/Nim/pull/17561). diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md deleted file mode 100644 index cbe755478..000000000 --- a/changelogs/changelog_2_0_0.md +++ /dev/null @@ -1,367 +0,0 @@ -# v2.0.0 - yyyy-mm-dd - - -## Changes affecting backward compatibility -- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `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` - - `std/widestrs` - - `std/typedthreads` - - `std/sysatomics` - - 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.github.io/Nim/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` - -- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in - in the `system` module. - -- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore. - -- 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`. - -- Automatic dereferencing (experimental feature) is removed. - -- 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` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and -`sink` for optimization purposes. - -- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises. - -- 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`. - -- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent - with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`. - This decision was made with the assumption that the old syntax was used rarely; - if your code used the old syntax, please be aware of this change. - -- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators - are no longer experimental. - -- Removed the `nimIncrSeqV3` define. - -- `macros.getImpl` for `const` symbols now returns the full definition node - (as `nnkConstDef`) rather than the AST of the constant value. - -- Lock levels are deprecated, now a noop. - -- ORC is now the default memory management strategy. Use - `--mm:refc` for a transition period. - -- `strictEffects` are no longer experimental. - Use `legacy:laxEffects` to keep backward compatibility. - -- The `gorge`/`staticExec` calls will now return a descriptive message in the output - if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`. - -- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning. - This warning will become an error in future versions! Use a `cast` operation - like `cast[cstring](x)` instead. - -- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`. - -- Redefining templates with the same signature was previously - allowed to support certain macro code. To do this explicitly, the - `{.redefine.}` pragma has been added. Note that this is only for templates. - Implicit redefinition of templates is now deprecated and will give an error in the future. - -- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. - -- Several Standard libraries are moved to nimble packages, use `nimble` to install them: - - `std/punycode` => `punycode` - - `std/asyncftpclient` => `asyncftpclient` - - `std/smtp` => `smtp` - - `std/db_common` => `db_connector/db_common` - - `std/db_sqlite` => `db_connector/db_sqlite` - - `std/db_mysql` => `db_connector/db_mysql` - - `std/db_postgres` => `db_connector/db_postgres` - - `std/db_odbc` => `db_connector/db_odbc` - -- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of - `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`. - This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead. - -- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. - -- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. - -- Underscores (`_`) as routine parameters are now ignored and cannot be used in the routine body. - The following code now does not compile: - - ```nim - proc foo(_: int): int = _ + 1 - echo foo(1) - ``` - - Instead, the following code now compiles: - - ```nim - proc foo(_, _: int): int = 123 - echo foo(1, 2) - ``` -- Underscores (`_`) as generic parameters are not supported and cannot be used. - Generics that use `_` as parameters will no longer compile requires you to replace `_` with something else: - - ```nim - proc foo[_](t: typedesc[_]): string = "BAR" # Can not compile - proc foo[T](t: typedesc[T]): string = "BAR" # Can compile - ``` - -- - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages. - -## Standard library additions and changes - -[//]: # "Changes:" -- OpenSSL 3 is now supported. -- `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)). -- The `md5` module now works at compile time and in JavaScript. -- 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. -- Undeprecated `os.isvalidfilename`. -- `std/oids` now uses `int64` to store time internally (before it was int32). -- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`. -- `std/uri.Uri.isIpv6` is now exported. -- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2. - - -- `std/net.IpAddress` dollar `$` improved, uses a fixed capacity for the `string` result based from the `IpAddressFamily`. -- `std/jsfetch.newFetchOptions` now has default values for all parameters -- `std/jsformdata` now accepts `Blob` data type. - -- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433). - -- New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall. - - This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. - - On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall. - - When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be: - ```sh - $ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details - $ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8 - $ sh build.sh # per unix install instructions - $ bin/nim c koch # per unix install instructions - $ ./koch boot -d:release # per unix install instructions - $ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall - ``` - - This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular). - -[//]: # "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 new modules which were part of `std/os`: - - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. - - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`. - - Added `std/cmdline` for reading command line parameters. -- 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 -- Added `openArray[char]` overloads for `std/parseutils` allowing more code reuse. -- Added `openArray[char]` overloads for `std/unicode` allowing more code reuse. -- Added `safe` parameter to `base64.encodeMime`. - -[//]: # "Deprecations:" -- Deprecated `selfExe` for Nimscript. -- Deprecated `std/sums`. -- Deprecated `std/base64.encode` for collections of arbitrary integer element type. - Now only `byte` and `char` are supported. - -[//]: # "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`. -- 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/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. -- Removed deprecated `osproc.poDemon`, symbol with typo. -- Removed deprecated `tables.rightSize`. - - -- Removed deprecated `posix.CLONE_STOPPED`. - - -## Language changes - -- [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". - -- Compile-time define changes: - - `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. - - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) - now support a string argument for qualified define names. - - ```nim - # -d:package.FooBar=42 - const FooBar {.intdefine: "package.FooBar".}: int = 5 - echo FooBar # 42 - ``` - - This was added to help disambiguate similar define names for different packages. - In older versions, this could only be achieved with something like the following: - - ```nim - const FooBar = block: - const `package.FooBar` {.intdefine.}: int = 5 - `package.FooBar` - ``` - - A generic `define` pragma for constants has been added that interprets - the value of the define based on the type of the constant value. - See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-define-pragma) - for a list of supported types. - -- [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 - ``` - -- 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. - -- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters). -- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`. - -- IBM Z architecture and macOS m1 arm64 architecture are supported. - -- `=wasMoved` can be overridden by users. - -## 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. - -- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package. - -- 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. - -- When compiling for Release the flag `-fno-math-errno` is used for GCC. - - -## Tool changes - -- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`). diff --git a/changelogs/changelog_X_XX_X.md b/changelogs/changelog_X_XX_X.md index 524599b11..48520fd44 100644 --- a/changelogs/changelog_X_XX_X.md +++ b/changelogs/changelog_X_XX_X.md @@ -3,16 +3,13 @@ This is an example file. The changes should go to changelog.md! -## Changes affecting backward compatibility - -- `foo` now behaves differently, use `-d:nimLegacyFoo` for previous behavior. ## Standard library additions and changes - Added `example.exampleProc`. - - Changed `example.foo` to take additional `bar` parameter. + ## Language changes diff --git a/ci/action.nim b/ci/action.nim deleted file mode 100644 index 5d3a50fda..000000000 --- a/ci/action.nim +++ /dev/null @@ -1,26 +0,0 @@ -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/ci/build.bat b/ci/build.bat new file mode 100644 index 000000000..cdce8d3d2 --- /dev/null +++ b/ci/build.bat @@ -0,0 +1,14 @@ +REM Some debug info +echo "Running on %CI_RUNNER_ID% (%CI_RUNNER_DESCRIPTION%) with tags %CI_RUNNER_TAGS%." +gcc -v + +git clone --depth 1 https://github.com/nim-lang/csources.git +cd csources +call build64.bat +cd .. +set PATH=%CD%\bin;%PATH% +nim -v +nim c koch +koch.exe boot +copy bin/nim bin/nimd +koch.exe boot -d:release diff --git a/ci/build.sh b/ci/build.sh new file mode 100644 index 000000000..6321fffba --- /dev/null +++ b/ci/build.sh @@ -0,0 +1,15 @@ +sh ci/deps.sh + +# Build from C sources. +git clone --depth 1 https://github.com/nim-lang/csources.git +cd csources +sh build.sh +cd .. +# Add Nim to the PATH +export PATH=$(pwd)/bin${PATH:+:$PATH} +# Bootstrap. +nim -v +nim c koch +./koch boot +cp bin/nim bin/nimd +./koch boot -d:release diff --git a/ci/build_autogen.bat b/ci/build_autogen.bat deleted file mode 100644 index 0b6ca566e..000000000 --- a/ci/build_autogen.bat +++ /dev/null @@ -1,26 +0,0 @@ -@echo off -rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` -rem Build development version of the compiler; can be rerun safely -rem bare bones version of ci/funs.sh adapted for windows. - -rem Read in some common shared variables (shared with other tools), -rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file -for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H -SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe -echo "building from csources: %nim_csources%" - -if not exist %nim_csourcesDir% ( - git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir% -) - -if not exist %nim_csources% ( - cd %nim_csourcesDir% - git checkout %nim_csourcesHash% - echo "%PROCESSOR_ARCHITECTURE%" - if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( - SET ARCH=64 - ) - CALL build.bat - cd .. - copy /y bin\nim.exe %nim_csources% -) diff --git a/ci/deps.bat b/ci/deps.bat new file mode 100644 index 000000000..bda1fe14f --- /dev/null +++ b/ci/deps.bat @@ -0,0 +1,4 @@ +nim e install_nimble.nims +nim e tests/test_nimscript.nims +nimble update +nimble install -y zip opengl sdl1 jester@#head niminst diff --git a/ci/deps.sh b/ci/deps.sh new file mode 100644 index 000000000..f0f831a2a --- /dev/null +++ b/ci/deps.sh @@ -0,0 +1,16 @@ +# Some debug info +echo "Running on $CI_RUNNER_ID ($CI_RUNNER_DESCRIPTION) with tags $CI_RUNNER_TAGS." + +# Packages +apt-get update -qq +apt-get install -y -qq build-essential git libcurl4-openssl-dev libsdl1.2-dev libgc-dev nodejs + +gcc -v + +export PATH=$(pwd)/bin${PATH:+:$PATH} + +# Nimble deps +nim e install_nimble.nims +nim e tests/test_nimscript.nims +nimble update +nimble install zip opengl sdl1 jester@#head niminst diff --git a/ci/funs.sh b/ci/funs.sh deleted file mode 100644 index 8e7f8faf7..000000000 --- a/ci/funs.sh +++ /dev/null @@ -1,143 +0,0 @@ -# Utilities used in CI pipelines and tooling to avoid duplication. -# Avoid top-level statements. -# Prefer nim scripts whenever possible. -# functions starting with `_` are considered internal, less stable. - -echo_run () { - # echo's a command before running it, which helps understanding logs - echo "" - echo "cmd: $@" # in azure we could also use this: echo '##[section]"$@"' - "$@" -} - -nimGetLastCommit() { - git log --no-merges -1 --pretty=format:"%s" -} - -nimIsCiSkip(){ - # D20210329T004830:here refs https://github.com/microsoft/azure-pipelines-agent/issues/2944 - # `--no-merges` is needed to avoid merge commits which occur for PR's. - # $(Build.SourceVersionMessage) is not helpful - # nor is `github.event.head_commit.message` for github actions. - # Note: `[skip ci]` is now handled automatically for github actions, see https://github.blog/changelog/2021-02-08-github-actions-skip-pull-request-and-push-workflows-with-skip-ci/ - commitMsg=$(nimGetLastCommit) - echo commitMsg: "$commitMsg" - if [[ $commitMsg == *"[skip ci]"* ]]; then - echo "skipci: true" - return 0 - else - echo "skipci: false" - return 1 - fi -} - -nimInternalInstallDepsWindows(){ - echo_run mkdir dist - echo_run curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z - echo_run curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip - echo_run 7z x dist/mingw64.7z -odist - echo_run 7z x dist/dlls.zip -obin -} - -nimInternalBuildKochAndRunCI(){ - echo_run nim c koch - if ! echo_run ./koch runCI; then - echo_run echo "runCI failed" - echo_run nim r tools/ci_testresults.nim - return 1 - fi -} - -nimDefineVars(){ - . config/build_config.txt - nim_csources=bin/nim_csources_$nim_csourcesHash -} - -_nimNumCpu(){ - # linux: $(nproc) - # FreeBSD | macOS: $(sysctl -n hw.ncpu) - # OpenBSD: $(sysctl -n hw.ncpuonline) - # windows: $NUMBER_OF_PROCESSORS ? - echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1) -} - -_nimBuildCsourcesIfNeeded(){ - # if some systems cannot use make or gmake, we could add support for calling `build.sh` - # but this is slower (not parallel jobs) and would require making build.sh - # understand the arguments passed to the makefile (e.g. `CC=gcc ucpu=amd64 uos=darwin`), - # instead of `--cpu amd64 --os darwin`. - unamestr=$(uname) - # uname values: https://en.wikipedia.org/wiki/Uname - if [ "$unamestr" = 'FreeBSD' ]; then - makeX=gmake - elif [ "$unamestr" = 'OpenBSD' ]; then - makeX=gmake - elif [ "$unamestr" = 'NetBSD' ]; then - makeX=gmake - elif [ "$unamestr" = 'CROSSOS' ]; then - makeX=gmake - elif [ "$unamestr" = 'SunOS' ]; then - makeX=gmake - else - makeX=make - fi - nCPU=$(_nimNumCpu) - echo_run which $makeX - # parallel jobs (5X faster on 16 cores: 10s instead of 50s) - echo_run $makeX -C $nim_csourcesDir -j $((nCPU + 2)) -l $nCPU "$@" - # keep $nim_csources in case needed to investigate bootstrap issues - # without having to rebuild - echo_run cp bin/nim $nim_csources -} - -nimCiSystemInfo(){ - nimDefineVars - echo_run eval echo '$'nim_csources - echo_run pwd - echo_run date - echo_run uname -a - echo_run git log --no-merges -1 --pretty=oneline - echo_run eval echo '$'PATH - echo_run gcc -v - echo_run node -v - echo_run make -v -} - -nimCsourcesHash(){ - nimDefineVars - echo $nim_csourcesHash -} - -nimBuildCsourcesIfNeeded(){ - # goal: allow cachine each tagged version independently - # to avoid rebuilding, so that tools like `git bisect` - # can grab a cached past version without rebuilding. - nimDefineVars - ( - set -e - # avoid polluting caller scope with internal variable definitions. - if test -f "$nim_csources"; then - echo "$nim_csources exists." - else - if test -d "$nim_csourcesDir"; then - echo "$nim_csourcesDir exists." - else - # Note: using git tags would allow fetching just what's needed, unlike git hashes, e.g. - # via `git clone -q --depth 1 --branch $tag $nim_csourcesUrl`. - echo_run git clone -q --depth 1 -b $nim_csourcesBranch \ - $nim_csourcesUrl "$nim_csourcesDir" - # old `git` versions don't support -C option, using `cd` explicitly: - echo_run cd "$nim_csourcesDir" - echo_run git checkout $nim_csourcesHash - echo_run cd "$OLDPWD" - # if needed we could also add: `git reset --hard $nim_csourcesHash` - fi - _nimBuildCsourcesIfNeeded "$@" - fi - - echo_run rm -f bin/nim - # fixes bug #17913, but it's unclear why it's needed, maybe specific to MacOS Big Sur 11.3 on M1 arch? - echo_run cp $nim_csources bin/nim - echo_run $nim_csources -v - ) -} diff --git a/ci/nsis_build.bat b/ci/nsis_build.bat new file mode 100644 index 000000000..4806810d7 --- /dev/null +++ b/ci/nsis_build.bat @@ -0,0 +1,59 @@ +REM - Run the full testsuite; testament\tester all + +REM - Uncomment the list of changes in news.txt +REM - write a news ticker entry +REM - Update the version + +REM - Generate the full docs; koch web0 +REM - Generate the installers; +REM - Update the version in system.nim +REM - Test the installers +REM - Tag the release +REM - Merge devel into master +REM - Update csources + +set NIMVER=%1 + +Rem Build -docs file: +koch web0 +cd web\upload +7z a -tzip docs-%NIMVER%.zip *.html +move /y docs-%NIMVER%.zip download +cd ..\.. + +Rem Build csources +koch csources -d:release || exit /b + +rem Grab C sources and nimsuggest +git clone --depth 1 https://github.com/nim-lang/csources.git + +set PATH=%CD%\bin;%PATH% + +ReM Build Win32 version: + +set PATH=C:\Users\araq\projects\mingw32\bin;%PATH% +cd csources +call build.bat +cd .. +ReM Rebuilding koch is necessary because it uses its pointer size to determine +ReM which mingw link to put in the NSIS installer. +nim c --out:koch_temp koch || exit /b +koch_temp boot -d:release || exit /b +koch_temp nsis -d:release || exit /b +koch_temp zip -d:release || exit /b +dir build +move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x32.exe || exit /b +move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x32.zip || exit /b + + +ReM Build Win64 version: +set PATH=C:\Users\araq\projects\mingw64\bin;%PATH% +cd csources +call build64.bat +cd .. +nim c --out:koch_temp koch || exit /b +koch_temp boot -d:release || exit /b +koch_temp nsis -d:release || exit /b +koch_temp zip -d:release || exit /b +move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x64.exe || exit /b +move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x64.zip || exit /b diff --git a/compiler.nimble b/compiler.nimble new file mode 100644 index 000000000..f4da45519 --- /dev/null +++ b/compiler.nimble @@ -0,0 +1,9 @@ + +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/aliasanalysis.nim b/compiler/aliasanalysis.nim deleted file mode 100644 index f14b81524..000000000 --- a/compiler/aliasanalysis.nim +++ /dev/null @@ -1,128 +0,0 @@ - -import ast - -import std / assertions - -const - PathKinds0* = {nkDotExpr, nkCheckedFieldExpr, - nkBracketExpr, nkDerefExpr, nkHiddenDeref, - nkAddr, nkHiddenAddr, - nkObjDownConv, nkObjUpConv} - PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv} - -proc skipConvDfa*(n: PNode): PNode = - result = n - while true: - case result.kind - of nkObjDownConv, nkObjUpConv: - result = result[0] - of PathKinds1: - result = result[1] - else: break - -proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = - var n = orig - while true: - case n.kind - of PathKinds0 - {nkHiddenDeref, nkDerefExpr}: - n = n[0] - of PathKinds1: - n = n[1] - of nkHiddenDeref, nkDerefExpr: - # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty - # pointer indirection. - # bug #14159, we cannot reason about sinkParam[].location as it can - # still be shared for tyRef. - n = n[0] - return n.kind == nkSym and n.sym.owner == owner and - (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned}) - else: break - # XXX Allow closure deref operations here if we know - # the owner controlled the closure allocation? - result = n.kind == nkSym and n.sym.owner == owner and - {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and - (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar) - # Note: There is a different move analyzer possible that checks for - # consume(param.key); param.key = newValue for all paths. Then code like - # - # let splited = split(move self.root, x) - # self.root = merge(splited.lower, splited.greater) - # - # could be written without the ``move self.root``. However, this would be - # wrong! Then the write barrier for the ``self.root`` assignment would - # free the old data and all is lost! Lesson: Don't be too smart, trust the - # lower level C++ optimizer to specialize this code. - -type AliasKind* = enum - yes, no, maybe - -proc aliases*(obj, field: PNode): AliasKind = - # obj -> field: - # x -> x: true - # x -> x.f: true - # x.f -> x: false - # x.f -> x.f: true - # x.f -> x.v: false - # x -> x[]: true - # x[] -> x: false - # x -> x[0]: true - # x[0] -> x: false - # x[0] -> x[0]: true - # x[0] -> x[1]: false - # x -> x[i]: true - # x[i] -> x: false - # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant - # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant - template collectImportantNodes(result, n) = - var result: seq[PNode] - var n = n - while true: - case n.kind - of PathKinds0 - {nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref}: - n = n[0] - of PathKinds1: - n = n[1] - of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref: - result.add n - n = n[0] - of nkSym: - result.add n - break - else: return no - - collectImportantNodes(objImportantNodes, obj) - collectImportantNodes(fieldImportantNodes, field) - - # If field is less nested than obj, then it cannot be part of/aliased by obj - if fieldImportantNodes.len < objImportantNodes.len: return no - - result = yes - for i in 1..objImportantNodes.len: - # We compare the nodes leading to the location of obj and field - # with each other. - # We continue until they diverge, in which case we return no, or - # until we reach the location of obj, in which case we do not need - # to look further, since field must be part of/aliased by obj now. - # If we encounter an element access using an index which is a runtime value, - # we simply return maybe instead of yes; should further nodes not diverge. - let currFieldPath = fieldImportantNodes[^i] - let currObjPath = objImportantNodes[^i] - - if currFieldPath.kind != currObjPath.kind: - return no - - case currFieldPath.kind - of nkSym: - if currFieldPath.sym != currObjPath.sym: return no - of nkDotExpr: - if currFieldPath[1].sym != currObjPath[1].sym: return no - of nkDerefExpr, nkHiddenDeref: - discard - of nkBracketExpr: - if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals: - if currFieldPath[1].intVal != currObjPath[1].intVal: - return no - else: - result = maybe - else: assert false # unreachable - diff --git a/compiler/aliases.nim b/compiler/aliases.nim index 4b50fdb28..0006c9fe6 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -12,9 +12,6 @@ import ast, astalgo, types, trees, intsets -when defined(nimPreviewSlimSystem): - import std/assertions - type TAnalysisResult* = enum arNo, arMaybe, arYes @@ -77,29 +74,24 @@ 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 f93c8d910..ccf4fe497 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -10,12 +10,9 @@ # abstract syntax tree + symbol table import - lineinfos, hashes, options, ropes, idents, int128, tables + lineinfos, hashes, options, ropes, idents, int128 from strutils import toLowerAscii -when defined(nimPreviewSlimSystem): - import std/assertions - export int128 type @@ -207,11 +204,12 @@ type nkPtrTy, # ``ptr T`` nkVarTy, # ``var T`` nkConstTy, # ``const T`` - nkOutTy, # ``out T`` + nkMutableTy, # ``mutable T`` nkDistinctTy, # distinct type nkProcTy, # proc type nkIteratorTy, # iterator type - nkSinkAsgn, # '=sink(x, y)' + nkSharedTy, # 'shared T' + # we use 'nkPostFix' for the 'not nil' addition nkEnumTy, # enum body nkEnumFieldDef, # `ident = expr` in an enumeration nkArgList, # argument list @@ -223,15 +221,11 @@ type nkBreakState, # special break statement for easier code generation nkFuncDef, # a func nkTupleConstr # a tuple constructor - nkError # erroneous AST node - nkModuleRef # for .rod file support: A (moduleId, itemId) pair - nkReplayAction # for .rod file support: A replay action - nkNilRodNode # for .rod file support: a 'nil' PNode TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 49 flags! + TSymFlag* = enum # 46 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 @@ -259,8 +253,7 @@ 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 - sfEscapes # param escapes - # currently unimplemented + sfProcvar, # proc can be passed to a proc var sfDiscriminant, # field is a discriminant in a record/object sfRequiresInit, # field must be initialized during construction sfDeprecated, # symbol is deprecated @@ -269,10 +262,6 @@ type sfShadowed, # a symbol that was shadowed in some inner scope sfThread, # proc will run as a thread # variable is a thread variable - sfCppNonPod, # tells compiler to treat such types as non-pod's, so that - # `thread_local` is used instead of `__thread` for - # {.threadvar.} + `--threads`. Only makes sense for importcpp types. - # This has a performance impact so isn't set by default. sfCompileTime, # proc can be evaluated at compile time sfConstructor, # proc is a C++ constructor sfDispatcher, # copied method symbol is the dispatcher @@ -299,17 +288,9 @@ type sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation sfNeverRaises # proc can never raise an exception, not even OverflowDefect # or out-of-memory - sfSystemRaisesDefect # proc in the system can raise defects sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally' 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] @@ -338,10 +319,10 @@ const sfExperimental* = sfOverriden # module uses the .experimental switch sfGoto* = sfOverriden # var is used for 'goto' code generation sfWrittenTo* = sfBorrow # param is assigned to - # currently unimplemented + sfEscapes* = sfProcvar # param escapes 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 @@ -354,8 +335,7 @@ 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 - forbiddenEffects* = 5 # list of illegal effects - effectListLen* = 6 # list of effects list + effectListLen* = 5 # list of effects list nkLastBlockStmts* = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} # these must be last statements in a block @@ -391,7 +371,7 @@ type tySequence, tyProc, tyPointer, tyOpenArray, - tyString, tyCstring, tyForward, + tyString, tyCString, tyForward, tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers tyFloat, tyFloat32, tyFloat64, tyFloat128, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, @@ -447,17 +427,16 @@ type # instantiation and prior to this it has the potential to # be any type. - tyConcept - # new style concept. + tyOptDeprecated + # 'out' parameter. Comparable to a 'var' parameter but every + # path must assign a value to it before it can be read from. tyVoid # now different from tyEmpty, hurray! - tyIterable static: # remind us when TTypeKind stops to fit in a single 64-bit word - # assert TTypeKind.high.ord <= 63 - discard + assert TTypeKind.high.ord <= 63 const tyPureObject* = tyTuple @@ -476,8 +455,6 @@ const # consider renaming as `tyAbstractVarRange` abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tyInferred, tySink, tyOwned} - abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, - tyInferred, tySink, tyOwned} # xxx what about tyStatic? type TTypeKinds* = set[TTypeKind] @@ -507,13 +484,9 @@ type nfDefaultRefsParam # a default param value references another parameter # the flag is applied to proc default values and to calls nfExecuteOnReload # A top-level statement that will be executed during reloads - nfLastRead # this node is a last read - nfFirstWrite # this node is a first write - nfHasComment # node has a comment - nfUseDefaultField # node has a default value (object constructor) TNodeFlags* = set[TNodeFlag] - TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 46) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~40) tfVarargs, # procedure has C styled varargs # tyArray type represeting a varargs list tfNoSideEffect, # procedure type does not allow side effects @@ -580,9 +553,6 @@ type # (for importc types); type is fully specified, allowing to compute # sizeof, alignof, offsetof at CT tfExplicitCallConv - tfIsConstructor - tfEffectSystemWorkaround - tfIsOutParam TTypeFlags* = set[TTypeFlag] @@ -624,8 +594,6 @@ type const routineKinds* = {skProc, skFunc, skMethod, skIterator, skConverter, skMacro, skTemplate} - ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub, skAlias} + routineKinds - tfUnion* = tfNoSideEffect tfGcSafe* = tfThread tfObjHasKids* = tfEnumHasHoles @@ -633,7 +601,7 @@ const skError* = skUnknown var - eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam} + eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect} ## type flags that are essential for type equality. ## This is now a variable because for emulation of version:1.0 we ## might exclude {tfGcSafe, tfNoSideEffect}. @@ -668,9 +636,7 @@ type mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, - mCharToStr, mBoolToStr, - mIntToStr, mInt64ToStr, mFloatToStr, # for compiling nimStdlibVersion < 1.5.1 (not bootstrapping) - mCStrToStr, + mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, mImplies, mIff, mExists, mForall, mOld, @@ -683,13 +649,13 @@ type mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mIsPartOf, mAstToStr, mParallel, - mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, + mSwap, mIsNil, mArrToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, - mMove, mWasMoved, mDestroy, mTrace, - mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset, + mMove, mWasMoved, mDestroy, + mDefault, mUnown, mIsolate, mAccessEnv, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, - mOrdinal, mIterableType, + mOrdinal, mInt, mInt8, mInt16, mInt32, mInt64, mUInt, mUInt8, mUInt16, mUInt32, mUInt64, mFloat, mFloat32, mFloat64, mFloat128, @@ -706,19 +672,19 @@ type mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext, mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, - mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetStrVal, mNLineInfo, + mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf, mNBindSym, mNCallSite, mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym, mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2, - mNimvm, mIntDefine, mStrDefine, mBoolDefine, mGenericDefine, mRunnableExamples, + mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples, mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, - mSymIsInstantiationOf, mNodeId, mPrivateAccess, mZeroDefault + mSymIsInstantiationOf, mNodeId +# 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, @@ -738,38 +704,20 @@ const mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, - mCharToStr, mBoolToStr, - mIntToStr, mInt64ToStr, mFloatToStr, - mCStrToStr, + mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem, - mInSet, mRepr, mOpenArrayToSeq} - - generatedMagics* = {mNone, mIsolate, mFinished, mOpenArrayToSeq} - ## magics that are generated as normal procs in the backend + mInSet, mRepr} 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 - -proc hash*(x: ItemId): Hash = - var h: Hash = hash(x.module) - h = h !& hash(x.item) - result = !$h - - -type - TIdObj* {.acyclic.} = object of RootObj + TIdObj* = object of RootObj itemId*: ItemId PIdObj* = ref TIdObj @@ -796,8 +744,7 @@ type ident*: PIdent else: sons*: TNodeSeq - when defined(nimsuggest): - endInfo*: TLineInfo + comment*: string TStrTable* = object # a table[PIdent] of PSym counter*: int @@ -869,11 +816,10 @@ type PInstantiation* = ref TInstantiation - TScope* {.acyclic.} = object + TScope* = object depthLevel*: int symbols*: TStrTable parent*: PScope - allowPrivateAccess*: seq[PSym] # # enable access to private fields PScope* = ref TScope @@ -881,10 +827,24 @@ type TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym # proc and type instantiations are cached in the generic symbol case kind*: TSymKind + of skType, skGenericParam: + typeInstCache*: seq[PType] of routineKinds: - #procInstCache*: seq[PInstantiation] - gcUnsafetyReason*: PSym # for better error messages regarding gcsafe + procInstCache*: seq[PInstantiation] + gcUnsafetyReason*: PSym # for better error messages wrt gcsafe transformedBody*: PNode # cached body after transf pass + of skModule, skPackage: + # modules keep track of the generic symbols they use from other modules. + # this is because in incremental compilation, when a module is about to + # be replaced with a newer version, we must decrement the usage count + # of all previously used generics. + # For 'import as' we copy the module symbol but shallowCopy the 'tab' + # and set the 'usedGenerics' to ... XXX gah! Better set module.name + # instead? But this doesn't work either. --> We need an skModuleAlias? + # No need, just leave it as skModule but set the owner accordingly and + # check for the owner when touching 'usedGenerics'. + usedGenerics*: seq[PInstantiation] + tab*: TStrTable # interface table for modules of skLet, skVar, skField, skForVar: guard*: PSym bitsize*: int @@ -894,8 +854,6 @@ type typ*: PType name*: PIdent info*: TLineInfo - when defined(nimsuggest): - endInfo*: TLineInfo owner*: PSym flags*: TSymFlags ast*: PNode # syntax tree of proc, iterator, etc.: @@ -933,13 +891,14 @@ type allUsages*: seq[TLineInfo] TTypeSeq* = seq[PType] + TLockLevel* = distinct int16 TTypeAttachedOp* = enum ## as usual, order is important here - attachedWasMoved, attachedDestructor, attachedAsgn, attachedSink, attachedTrace, + attachedDispose, attachedDeepCopy TType* {.acyclic.} = object of TIdObj # \ @@ -963,15 +922,18 @@ type owner*: PSym # the 'owner' of the type sym*: PSym # types have the sym associated with them # it is used for converting types to strings + attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc. + methods*: seq[(int,PSym)] # attached methods size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown align*: int16 # the type's alignment requirements paddingAtEnd*: int16 # + lockLevel*: TLockLevel # lock level as required for deadlock checking loc*: TLoc typeInst*: PType # for generic instantiations the tyGenericInst that led to this # type. uniqueId*: ItemId # due to a design mistake, we need to keep the real ID here as it - # is required by the --incremental:on mode. + # required by the --incremental:on mode. TPair* = object key*, val*: RootRef @@ -1015,45 +977,13 @@ type TImplication* = enum impUnknown, impNo, impYes -template nodeId(n: PNode): int = cast[int](n) - -type Gconfig = object - # we put comments in a side channel to avoid increasing `sizeof(TNode)`, which - # reduces memory usage given that `PNode` is the most allocated type by far. - comments: Table[int, string] # nodeId => comment - useIc*: bool - -var gconfig {.threadvar.}: Gconfig - -proc setUseIc*(useIc: bool) = gconfig.useIc = useIc - -proc comment*(n: PNode): string = - if nfHasComment in n.flags and not gconfig.useIc: - # IC doesn't track comments, see `packed_ast`, so this could fail - result = gconfig.comments[n.nodeId] - -proc `comment=`*(n: PNode, a: string) = - let id = n.nodeId - if a.len > 0: - # if needed, we could periodically cleanup gconfig.comments when its size increases, - # to ensure only live nodes (and with nfHasComment) have an entry in gconfig.comments; - # for compiling compiler, the waste is very small: - # num calls to newNodeImpl: 14984160 (num of PNode allocations) - # size of gconfig.comments: 33585 - # num of nodes with comments that were deleted and hence wasted: 3081 - n.flags.incl nfHasComment - gconfig.comments[id] = a - elif nfHasComment in n.flags: - n.flags.excl nfHasComment - gconfig.comments.del(id) - # BUGFIX: a module is overloadable so that a proc can have the # same name as an imported module. This is necessary because of # the poor naming choices in the standard library. const OverloadableSyms* = {skProc, skFunc, skMethod, skIterator, - skConverter, skModule, skTemplate, skMacro, skEnumField} + skConverter, skModule, skTemplate, skMacro} GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody, tyGenericParam} @@ -1067,21 +997,23 @@ const tyBool, tyChar, tyEnum, tyArray, tyObject, tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc, tyPointer, - tyOpenArray, tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128, + tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64} IntegralTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64} # weird name because it contains tyFloat ConstantDataTypes*: TTypeKinds = {tyArray, tySet, tyTuple, tySequence} - NilableTypes*: TTypeKinds = {tyPointer, tyCstring, tyRef, tyPtr, + NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tyProc, tyError} # TODO PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM + ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType, + skIterator, + skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, nfIsRef, nfIsPtr, nfPreventCg, nfLL, nfFromTemplate, nfDefaultRefsParam, - nfExecuteOnReload, nfLastRead, - nfFirstWrite, nfUseDefaultField} + nfExecuteOnReload} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 @@ -1106,7 +1038,6 @@ const declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef} routineDefs* = declarativeDefs + {nkMacroDef, nkTemplateDef} procDefs* = nkLambdaKinds + declarativeDefs - callableDefs* = nkLambdaKinds + routineDefs nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice} nkStrKinds* = {nkStrLit..nkTripleStrLit} @@ -1117,16 +1048,23 @@ const defaultSize = -1 defaultAlignment = -1 - defaultOffset* = -1 + defaultOffset = -1 -proc getPIdent*(a: PNode): PIdent {.inline.} = - ## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`. - # xxx consider whether also returning the 1st ident for {nkOpenSymChoice, nkClosedSymChoice} - # which may simplify code. - case a.kind - of nkSym: a.sym.name - 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 @@ -1140,7 +1078,11 @@ type module*: int32 symId*: int32 typeId*: int32 - sealed*: bool + +proc hash*(x: ItemId): Hash = + var h: Hash = hash(x.module) + h = h !& hash(x.item) + result = !$h const PackageModuleId* = -3'i32 @@ -1150,12 +1092,10 @@ proc idGeneratorFromModule*(m: PSym): IdGenerator = result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0) proc nextSymId*(x: IdGenerator): ItemId {.inline.} = - assert(not x.sealed) inc x.symId result = ItemId(module: x.module, item: x.symId) proc nextTypeId*(x: IdGenerator): ItemId {.inline.} = - assert(not x.sealed) inc x.typeId result = ItemId(module: x.module, item: x.typeId) @@ -1172,7 +1112,13 @@ 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 @@ -1182,7 +1128,11 @@ proc discardSons*(father: PNode) type Indexable = PNode | PType proc len*(n: Indexable): int {.inline.} = - result = n.sons.len + when defined(nimNoNilSeqs): + result = n.sons.len + else: + if isNil(n.sons): result = 0 + else: result = n.sons.len proc safeLen*(n: PNode): int {.inline.} = ## works even for leaves. @@ -1197,9 +1147,8 @@ proc safeArrLen*(n: PNode): int {.inline.} = proc add*(father, son: Indexable) = assert son != nil - father.sons.add(son) - -proc addAllowNil*(father, son: Indexable) {.inline.} = + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] father.sons.add(son) template `[]`*(n: Indexable, i: int): Indexable = n.sons[i] @@ -1208,77 +1157,12 @@ template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int] template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x -proc getDeclPragma*(n: PNode): PNode = - ## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found. - ## Currently only supports routineDefs + {nkTypeDef}. - case n.kind - of routineDefs: - if n[pragmasPos].kind != nkEmpty: result = n[pragmasPos] - of nkTypeDef: - #[ - type F3*{.deprecated: "x3".} = int - - TypeSection - TypeDef - PragmaExpr - Postfix - Ident "*" - Ident "F3" - Pragma - ExprColonExpr - Ident "deprecated" - StrLit "x3" - Empty - Ident "int" - ]# - if n[0].kind == nkPragmaExpr: - result = n[0][1] - else: - # support as needed for `nkIdentDefs` etc. - result = nil - 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 - -proc setInfoRecursive*(n: PNode, info: TLineInfo) = - ## set line info recursively - if n != nil: - for i in 0.. 0: newSeq(result.sons, children) - setIdMaybe() + when defined(useNodeIds): + result.id = gNodeId + if result.id == nodeIdToDebug: + echo "KIND ", result.kind + writeStackTrace() + inc gNodeId proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = - ## new node with line info, type, and no children result = newNode(kind) result.info = info result.typ = typ @@ -1330,29 +1216,15 @@ proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[ template previouslyInferred*(t: PType): PType = if t.sons.len > 1: t.lastSon else: nil -when false: - import tables, strutils - var x: CountTable[string] - - addQuitProc proc () {.noconv.} = - for k, v in pairs(x): - echo k - echo v - proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym, info: TLineInfo; options: TOptions = {}): PSym = # generates a symbol and initializes the hash field too result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id, options: options, owner: owner, offset: defaultOffset) - when false: - if id.module == 48 and id.item == 39: - writeStackTrace() - echo "kind ", symKind, " ", name.s - if owner != nil: echo owner.name.s proc astdef*(s: PSym): PNode = # get only the definition (initializer) portion of the ast - if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}: + if s.ast != nil and s.ast.kind == nkIdentDefs: s.ast[2] else: s.ast @@ -1413,7 +1285,10 @@ proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) = for i in 0..high(src.data): dest.data[i] = src.data[i] proc discardSons*(father: PNode) = - father.sons = @[] + when defined(nimNoNilSeqs): + father.sons = @[] + else: + father.sons = nil proc withInfo*(n: PNode, info: TLineInfo): PNode = n.info = info @@ -1502,8 +1377,16 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, pragmas, exceptions, body] const + UnspecifiedLockLevel* = TLockLevel(-1'i16) + MaxLockLevel* = 1000'i16 + UnknownLockLevel* = TLockLevel(1001'i16) AttachedOpToStr*: array[TTypeAttachedOp, string] = [ - "=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"] + "=destroy", "=copy", "=sink", "=trace", "=dispose", "=deepcopy"] + +proc `$`*(x: TLockLevel): string = + if x.ord == UnspecifiedLockLevel.ord: result = "" + elif x.ord == UnknownLockLevel.ord: result = "" + else: result = $int16(x) proc `$`*(s: PSym): string = if s != nil: @@ -1514,9 +1397,10 @@ proc `$`*(s: PSym): string = proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType = result = PType(kind: kind, owner: owner, size: defaultSize, align: defaultAlignment, itemId: id, + lockLevel: UnspecifiedLockLevel, uniqueId: id) when false: - if result.itemId.module == 55 and result.itemId.item == 2: + if result.id == 76426: echo "KNID ", kind writeStackTrace() @@ -1526,10 +1410,16 @@ 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 == "": a.r = b.r + if a.r == nil: a.r = b.r proc newSons*(father: Indexable, length: int) = - setLen(father.sons, length) + when defined(nimNoNilSeqs): + setLen(father.sons, length) + else: + if isNil(father.sons): + newSeq(father.sons, length) + else: + setLen(father.sons, length) proc assignType*(dest, src: PType) = dest.kind = src.kind @@ -1538,10 +1428,12 @@ proc assignType*(dest, src: PType) = dest.n = src.n dest.size = src.size dest.align = src.align + dest.attachedOps = src.attachedOps + dest.lockLevel = src.lockLevel # this fixes 'type TLock = TSysLock': if src.sym != nil: if dest.sym != nil: - dest.sym.flags.incl src.sym.flags-{sfUsed, sfExported} + dest.sym.flags.incl src.sym.flags-{sfExported} if dest.sym.annex == nil: dest.sym.annex = src.sym.annex mergeLoc(dest.sym.loc, src.sym.loc) else: @@ -1563,6 +1455,8 @@ proc copySym*(s: PSym; id: ItemId): PSym = result.typ = s.typ result.flags = s.flags result.magic = s.magic + if s.kind == skModule: + copyStrTable(result.tab, s.tab) result.options = s.options result.position = s.position result.loc = s.loc @@ -1580,10 +1474,13 @@ proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo; result.ast = s.ast #result.id = s.id # XXX figure out what to do with the ID. result.flags = s.flags + system.shallowCopy(result.tab, s.tab) result.options = s.options result.position = s.position result.loc = s.loc result.annex = s.annex + # XXX once usedGenerics is used, ensure module aliases keep working! + assert s.usedGenerics.len == 0 proc initStrTable*(x: var TStrTable) = x.counter = 0 @@ -1649,7 +1546,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) = if mask != {} and propagateHasAsgn: let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink}) if o2.kind in {tyTuple, tyObject, tyArray, - tySequence, tySet, tyDistinct}: + tySequence, tySet, tyDistinct, tyOpenArray, tyVarargs}: o2.flags.incl mask owner.flags.incl mask @@ -1662,17 +1559,26 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) = owner.flags.incl tfHasGCedMem proc rawAddSon*(father, son: PType; propagateHasAsgn = true) = + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] father.sons.add(son) if not son.isNil: propagateToOwner(father, son, propagateHasAsgn) proc rawAddSonNoPropagationOfTypeFlags*(father, son: PType) = + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] father.sons.add(son) proc addSonNilAllowed*(father, son: PNode) = + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] father.sons.add(son) proc delSon*(father: PNode, idx: int) = - if father.len == 0: return + when defined(nimNoNilSeqs): + if father.len == 0: return + else: + if isNil(father.sons): return for i in idx..' 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: sonsRope = rope("[") @@ -589,9 +585,6 @@ proc value(this: var DebugPrinter; value: PNode) = this.openCurly this.key "kind" this.value value.kind - if value.comment.len > 0: - this.key "comment" - this.value value.comment when defined(useNodeIds): this.key "id" this.value value.id @@ -766,9 +759,9 @@ proc strTableAdd*(t: var TStrTable, n: PSym) = proc strTableInclReportConflict*(t: var TStrTable, n: PSym; onConflictKeepOld = false): PSym = - # if `t` has a conflicting symbol (same identifier as `n`), return it - # otherwise return `nil`. Incl `n` to `t` unless `onConflictKeepOld = true` - # and a conflict was found. + # returns true if n is already in the string table: + # It is essential that `n` is written nevertheless! + # This way the newest redefinition is picked by the semantic analyses! assert n.name != nil var h: Hash = n.name.h and high(t.data) var replaceSlot = -1 @@ -784,10 +777,9 @@ proc strTableInclReportConflict*(t: var TStrTable, n: PSym; replaceSlot = h h = nextTry(h, high(t.data)) if replaceSlot >= 0: - result = t.data[replaceSlot] # found it if not onConflictKeepOld: t.data[replaceSlot] = n # overwrite it with newer definition! - return result # but return the old one + return t.data[replaceSlot] # found it elif mustRehash(t.data.len, t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) @@ -816,21 +808,16 @@ type name*: PIdent proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym = - # hot spots var h = ti.h and high(tab.data) var start = h - var p {.cursor.} = tab.data[h] - while p != nil: - if p.name.id == ti.name.id: break + result = tab.data[h] + while result != nil: + if result.name.id == ti.name.id: break h = nextTry(h, high(tab.data)) if h == start: - p = nil + result = nil break - p = tab.data[h] - if p != nil: - result = p # increase the count - else: - result = nil + result = tab.data[h] ti.h = nextTry(h, high(tab.data)) proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym = diff --git a/compiler/astmsgs.nim b/compiler/astmsgs.nim deleted file mode 100644 index a9027126a..000000000 --- a/compiler/astmsgs.nim +++ /dev/null @@ -1,39 +0,0 @@ -# this module avoids ast depending on msgs or vice versa -import std/strutils -import options, ast, msgs - -proc typSym*(t: PType): PSym = - result = t.sym - if result == nil and t.kind == tyGenericInst: # this might need to be refined - result = t[0].sym - -proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) = - result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)] - -proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; sym: PSym) = - if optDeclaredLocs in conf.globalOptions and sym != nil: - addDeclaredLoc(result, conf, sym) - -proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) = - # xxx figure out how to resolve `tyGenericParam`, e.g. for - # proc fn[T](a: T, b: T) = discard - # fn(1.1, "a") - let typ = typ.skipTypes(abstractInst + {tyStatic, tySequence, tyArray, tySet, tyUserTypeClassInst, tyVar, tyRef, tyPtr} - {tyRange}) - result.add " [$1" % typ.kind.toHumanStr - if typ.sym != nil: - result.add " declared in " & toFileLineCol(conf, typ.sym.info) - result.add "]" - -proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) = - if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ) - -template quoteExpr*(a: string): untyped = - ## can be used for quoting expressions in error msgs. - "'" & a & "'" - -proc genFieldDefect*(conf: ConfigRef, field: string, disc: PSym): string = - let obj = disc.owner.name.s # `types.typeToString` might be better, eg for generics - result = "field '$#' is not accessible for type '$#'" % [field, obj] - if optDeclaredLocs in conf.globalOptions: - result.add " [discriminant declared in $#]" % toFileLineCol(conf, disc.info) - result.add " using '$# = " % disc.name.s diff --git a/compiler/backendpragmas.nim b/compiler/backendpragmas.nim deleted file mode 100644 index b18644810..000000000 --- a/compiler/backendpragmas.nim +++ /dev/null @@ -1,25 +0,0 @@ -import pragmas, options, ast, trees - -proc pushBackendOption(optionsStack: var seq[TOptions], options: var TOptions) = - optionsStack.add options - -proc popBackendOption(optionsStack: var seq[TOptions], options: var TOptions) = - options = optionsStack[^1] - optionsStack.setLen(optionsStack.len-1) - -proc processPushBackendOption*(optionsStack: var seq[TOptions], options: var TOptions, - n: PNode, start: int) = - pushBackendOption(optionsStack, options) - for i in start..= 0) x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or (One shl elem.modElemSize) -proc bitSetExcl*(x: var TBitSet, elem: BiggestInt) = +proc bitSetExcl(x: var TBitSet, elem: BiggestInt) = x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and not(One shl elem.modElemSize) -proc bitSetInit*(b: var TBitSet, length: int) = +proc bitSetInit(b: var TBitSet, length: int) = newSeq(b, length) -proc bitSetUnion*(x: var TBitSet, y: TBitSet) = +proc bitSetUnion(x: var TBitSet, y: TBitSet) = for i in 0..high(x): x[i] = x[i] or y[i] -proc bitSetDiff*(x: var TBitSet, y: TBitSet) = +proc bitSetDiff(x: var TBitSet, y: TBitSet) = for i in 0..high(x): x[i] = x[i] and not y[i] -proc bitSetSymDiff*(x: var TBitSet, y: TBitSet) = +proc bitSetSymDiff(x: var TBitSet, y: TBitSet) = for i in 0..high(x): x[i] = x[i] xor y[i] -proc bitSetIntersect*(x: var TBitSet, y: TBitSet) = +proc bitSetIntersect(x: var TBitSet, y: TBitSet) = for i in 0..high(x): x[i] = x[i] and y[i] -proc bitSetEquals*(x, y: TBitSet): bool = +proc bitSetEquals(x, y: TBitSet): bool = for i in 0..high(x): if x[i] != y[i]: return false result = true -proc bitSetContains*(x, y: TBitSet): bool = +proc bitSetContains(x, y: TBitSet): bool = for i in 0..high(x): if (x[i] and not y[i]) != Zero: return false @@ -86,6 +96,6 @@ const populationCount: array[uint8, uint8] = block: arr -proc bitSetCard*(x: TBitSet): BiggestInt = +proc bitSetCard(x: TBitSet): BiggestInt = for it in x: result.inc int(populationCount[it]) diff --git a/compiler/btrees.nim b/compiler/btrees.nim index c79442249..5f78c07fe 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -10,16 +10,13 @@ ## BTree implementation with few features, but good enough for the ## Nim compiler's needs. -when defined(nimPreviewSlimSystem): - import std/assertions - const M = 512 # max children per B-tree node = M-1 # (must be even and greater than 2) Mhalf = M div 2 type - Node[Key, Val] {.acyclic.} = ref object + Node[Key, Val] = ref object entries: int keys: array[M, Key] case isInternal: bool @@ -68,10 +65,7 @@ proc copyHalf[Key, Val](h, result: Node[Key, Val]) = result.links[j] = h.links[Mhalf + j] else: for j in 0..') + +proc encodeType(w: PRodWriter, t: PType, result: var string) = + if t == nil: + # nil nodes have to be stored too: + result.add("[]") + return + # we need no surrounding [] here because the type is in a line of its own + if t.kind == tyForward: internalError("encodeType: tyForward") + # for the new rodfile viewer we use a preceding [ so that the data section + # can easily be disambiguated: + result.add('[') + encodeVInt(ord(t.kind), result) + result.add('+') + encodeVInt(t.id, result) + if t.n != nil: + encodeNode(w, unknownLineInfo, t.n, result) + if t.flags != {}: + result.add('$') + encodeVInt(cast[int32](t.flags), result) + if t.callConv != low(t.callConv): + result.add('?') + encodeVInt(ord(t.callConv), result) + if t.owner != nil: + result.add('*') + encodeVInt(t.owner.id, result) + pushSym(w, t.owner) + if t.sym != nil: + result.add('&') + encodeVInt(t.sym.id, result) + pushSym(w, t.sym) + if t.size != - 1: + result.add('/') + encodeVBiggestInt(t.size, result) + if t.align != - 1: + result.add('=') + encodeVInt(t.align, result) + encodeLoc(w, t.loc, result) + for i in 0.. 0): message(p.config, le.info, warnObservableStores, $le) - # bug #19613 prevent dangerous aliasing too: - if dest != nil and dest != le: - for i in 1.. 0: @@ -222,19 +210,19 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) = genStmts(p, q[i]) q = q.lastSon let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0]) - result.add x & ", " & y + result = x & ", " & y else: var a: TLoc initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a) - case skipTypes(a.t, abstractVar+{tyStatic}).kind + case skipTypes(a.t, abstractVar).kind of tyOpenArray, tyVarargs: if reifiedOpenArray(n): if a.t.kind in {tyVar, tyLent}: - result.add "$1->Field0, $1->Field1" % [rdLoc(a)] + result = "$1->Field0, $1->Field1" % [rdLoc(a)] else: - result.add "$1.Field0, $1.Field1" % [rdLoc(a)] + result = "$1.Field0, $1.Field1" % [rdLoc(a)] else: - result.add "$1, $1Len_0" % [rdLoc(a)] + result = "$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 @@ -243,24 +231,19 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) = if ntyp.kind in {tyVar} and not compileToCpp(p.module): var t: TLoc t.r = "(*$1)" % [a.rdLoc] - result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" % - [a.rdLoc, lenExpr(p, t), dataField(p), - dataFieldAccessor(p, "*" & a.rdLoc)] + result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] else: - result.add "($4) ? ($1$3) : NIM_NIL, $2" % - [a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)] + result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)] of tyArray: - result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))] + result = "$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.add "($4) ? ((*$1)$3) : NIM_NIL, $2" % - [a.rdLoc, lenExpr(p, t), dataField(p), - dataFieldAccessor(p, "*" & a.rdLoc)] + result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] of tyArray: - result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))] + result = "$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)) @@ -276,28 +259,21 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc = else: result = a -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; result: var Rope; needsTmp: bool) {.inline.} = +proc genArgStringToCString(p: BProc, n: PNode, needsTmp: bool): Rope {.inline.} = var a: TLoc initLocExpr(p, n[0], a) - appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc]) + ropecg(p.module, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc]) -proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) = +proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rope = var a: TLoc if n.kind == nkStringToCString: - genArgStringToCString(p, n, result, needsTmp) + result = genArgStringToCString(p, n, needsTmp) elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: var n = if n.kind != nkHiddenAddr: n else: n[0] - openArrayLoc(p, param.typ, n, result) + result = openArrayLoc(p, param.typ, n) elif ccgIntroducedPtr(p.config, param, call[0].typ[0]): initLocExpr(p, n, a) - if n.kind in {nkCharLit..nkNilLit}: - addAddrLoc(p.config, literalsNeedsTmp(p, a), result) - else: - addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result) + result = addrLoc(p.config, withTmpIfNeeded(p, a, needsTmp)) elif p.module.compileToCpp and param.typ.kind in {tyVar} and n.kind == nkHiddenAddr: initLocExprSingleUse(p, n[0], a) @@ -307,77 +283,64 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need if callee.kind == nkSym and {sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}: - addAddrLoc(p.config, a, result) + result = addrLoc(p.config, a) else: - addRdLoc(a, result) + result = rdLoc(a) else: initLocExprSingleUse(p, n, a) - addRdLoc(withTmpIfNeeded(p, a, needsTmp), result) - #assert result != nil + result = rdLoc(withTmpIfNeeded(p, a, needsTmp)) -proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) = +proc genArgNoParam(p: BProc, n: PNode, needsTmp = false): Rope = var a: TLoc if n.kind == nkStringToCString: - genArgStringToCString(p, n, result, needsTmp) + result = genArgStringToCString(p, n, needsTmp) else: initLocExprSingleUse(p, n, a) - addRdLoc(withTmpIfNeeded(p, a, needsTmp), result) + result = rdLoc(withTmpIfNeeded(p, a, needsTmp)) -import aliasanalysis +from dfa import instrTargets, InstrTargetKind proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool = for p in potentialWrites: - if p.aliases(n) != no or n.aliases(p) != no: + if instrTargets(p, n) != None: return true proc skipTrivialIndirections(n: PNode): PNode = result = n while true: case result.kind - of nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv: + of {nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv}: result = result[0] - of nkHiddenStdConv, nkHiddenSubConv: + of {nkHiddenStdConv, nkHiddenSubConv}: result = result[1] else: break -proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) = +proc getPotentialWrites(n: PNode, mutate = false): seq[PNode] = case n.kind: - of nkLiterals, nkIdent, nkFormalParams: discard + of nkLiterals, nkIdent: discard of nkSym: if mutate: result.add n - of nkAsgn, nkFastAsgn, nkSinkAsgn: - getPotentialWrites(n[0], true, result) - getPotentialWrites(n[1], mutate, result) + of nkAsgn, nkFastAsgn: + result.add getPotentialWrites(n[0], true) + result.add getPotentialWrites(n[1], mutate) of nkAddr, nkHiddenAddr: - getPotentialWrites(n[0], true, result) - of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr: - getPotentialWrites(n[0], mutate, result) - of nkCallKinds: - case n.getMagic: - of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem, - mAddr, mNew, mNewFinalize, mWasMoved, mDestroy, mReset: - getPotentialWrites(n[1], true, result) - for i in 2.. 1: pl.add(", ") + if isInvalidReturnType(p.config, typ[0]): + 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): + if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri): # Great, we can use 'd': if d.k == locNone: getTemp(p, typ[0], d, needsInit=true) @@ -510,30 +463,24 @@ 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; result: var Rope; - argsCounter: var int) = +proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = 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: - discard + result = nil elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr: - if argsCounter > 0: result.add ", " - genArgNoParam(p, ri[i][0], result) - inc argsCounter + result = genArgNoParam(p, ri[i][0]) else: - if argsCounter > 0: result.add ", " - genArgNoParam(p, ri[i], result) #, typ.n[i].sym) - inc argsCounter + result = genArgNoParam(p, ri[i]) #, typ.n[i].sym) else: if tfVarargs notin typ.flags: localError(p.config, ri.info, "wrong argument count") + result = nil else: - if argsCounter > 0: result.add ", " - genArgNoParam(p, ri[i], result) - inc argsCounter + result = genArgNoParam(p, ri[i]) discard """ Dot call syntax in C++ @@ -590,7 +537,7 @@ proc skipAddrDeref(node: PNode): PNode = else: result = node -proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) = +proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): 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. @@ -604,72 +551,75 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) = if t.kind in {tyVar}: let x = if ri.kind == nkHiddenAddr: ri[0] else: ri if x.typ.kind == tyPtr: - genArgNoParam(p, x, result) + result = genArgNoParam(p, x) result.add("->") elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr: - genArgNoParam(p, x[0], result) + result = genArgNoParam(p, x[0]) result.add("->") else: - genArgNoParam(p, x, result) + result = genArgNoParam(p, x) result.add(".") elif t.kind == tyPtr: if ri.kind in {nkAddr, nkHiddenAddr}: - genArgNoParam(p, ri[0], result) + result = genArgNoParam(p, ri[0]) result.add(".") else: - genArgNoParam(p, ri, result) + result = genArgNoParam(p, ri) result.add("->") else: ri = skipAddrDeref(ri) if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0] - genArgNoParam(p, ri, result) #, typ.n[i].sym) + result = genArgNoParam(p, ri) #, typ.n[i].sym) result.add(".") -proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) = +proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope = var i = 0 var j = 1 while i < pat.len: case pat[i] of '@': - var argsCounter = 0 + var first = true for k in j.. 0: + if not first: + result.add(~", ") + first = false + result.add arg 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] == '+': genArgNoParam(p, ri[0], result) - result.add("(") + if pat[i+1] == '+': result.add genArgNoParam(p, ri[0]) + result.add(~"(") if 1 < ri.len: - var argsCounterB = 0 - genOtherArg(p, ri, 1, typ, result, argsCounterB) + result.add genOtherArg(p, ri, 1, typ) for k in j+1.. 0 if pat.contains({'#', '(', '@', '\''}): - var pl = newRopeAppender() - genPatternCall(p, ri, pat, typ, pl) + var pl = genPatternCall(p, ri, pat, typ) # simpler version of 'fixupCall' that works with the pl+params combination: var typ = skipTypes(ri[0].typ, abstractInst) if typ[0] != nil: @@ -710,79 +659,80 @@ 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 = newRopeAppender() - var argsCounter = 0 + var pl: Rope = nil + #var param = typ.n[1].sym if 1 < ri.len: - genThisArg(p, ri, 1, typ, pl) + pl.add(genThisArg(p, ri, 1, typ)) pl.add(op.r) - var params = newRopeAppender() + var params: Rope for i in 2.. 0 var start = 3 if ' ' in pat: start = 1 pl.add(op.r) if ri.len > 1: - pl.add(": ") - genArg(p, ri[1], typ.n[1].sym, ri, pl) + pl.add(~": ") + pl.add(genArg(p, ri[1], typ.n[1].sym, ri)) start = 2 else: if ri.len > 1: - genArg(p, ri[1], typ.n[1].sym, ri, pl) - pl.add(" ") + pl.add(genArg(p, ri[1], typ.n[1].sym, ri)) + pl.add(~" ") pl.add(op.r) if ri.len > 2: - pl.add(": ") - genArg(p, ri[2], typ.n[2].sym, ri, pl) + pl.add(~": ") + pl.add(genArg(p, ri[2], typ.n[2].sym, ri)) 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(": ") - genArg(p, ri[i], param, ri, pl) + pl.add(~": ") + pl.add(genArg(p, ri[i], param, ri)) if typ[0] != nil: - if isInvalidReturnType(p.config, typ): - if ri.len > 1: pl.add(" ") + if isInvalidReturnType(p.config, typ[0]): + 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 @@ -790,7 +740,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.} = @@ -828,5 +778,6 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = genNamedParamCall(p, ri, d) else: genPrefixCall(p, le, ri, d) + postStmtActions(p) proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index a64a42219..b8a8d21b0 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -9,7 +9,7 @@ # included from cgen.nim -when defined(nimCompilerStacktraceHints): +when defined(nimCompilerStackraceHints): import std/stackframes proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, @@ -18,31 +18,29 @@ proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, # -------------------------- constant expressions ------------------------ -proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType; result: var Rope) - -proc int64Literal(i: BiggestInt; result: var Rope) = +proc int64Literal(i: BiggestInt): Rope = if i > low(int64): - result.add "IL64($1)" % [rope(i)] + result = "IL64($1)" % [rope(i)] else: - result.add "(IL64(-9223372036854775807) - IL64(1))" + result = ~"(IL64(-9223372036854775807) - IL64(1))" -proc uint64Literal(i: uint64; result: var Rope) = result.add rope($i & "ULL") +proc uint64Literal(i: uint64): Rope = rope($i & "ULL") -proc intLiteral(i: BiggestInt; result: var Rope) = +proc intLiteral(i: BiggestInt): Rope = if i > low(int32) and i <= high(int32): - result.add rope(i) + result = rope(i) elif i == low(int32): # Nim has the same bug for the same reasons :-) - result.add "(-2147483647 -1)" + result = ~"(-2147483647 -1)" elif i > low(int64): - result.add "IL64($1)" % [rope(i)] + result = "IL64($1)" % [rope(i)] else: - result.add "(IL64(-9223372036854775807) - IL64(1))" + result = ~"(IL64(-9223372036854775807) - IL64(1))" -proc intLiteral(i: Int128; result: var Rope) = - intLiteral(toInt64(i), result) +proc intLiteral(i: Int128): Rope = + intLiteral(toInt64(i)) -proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Rope) = +proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = case n.kind of nkCharLit..nkUInt64Lit: var k: TTypeKind @@ -56,68 +54,63 @@ proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Rope) = else: k = tyNil # don't go into the case variant that uses 'ty' case k of tyChar, tyNil: - intLiteral(n.intVal, result) + result = intLiteral(n.intVal) of tyBool: - 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) + 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.add "((" - result.add getTypeDesc(p.module, ty) - result.add ")" - intLiteral(n.intVal, result) - result.add ")" + result = "(($1) $2)" % [getTypeDesc(p.module, + ty), intLiteral(n.intVal)] 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) - let tmpName = p.module.tmpBase & rope(id) + result = p.module.tmpBase & rope(id) if id == p.module.labels: # not found in cache: inc(p.module.labels) - p.module.s[cfsStrData].addf( + p.module.s[cfsData].addf( "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n", - [getTypeDesc(p.module, ty), tmpName]) - result.add tmpName + [getTypeDesc(p.module, ty), result]) elif k in {tyPointer, tyNil, tyProc}: - result.add rope("NIM_NIL") + result = rope("NIM_NIL") else: - result.add "(($1) NIM_NIL)" % [getTypeDesc(p.module, ty)] + result = "(($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: - genNilStringLiteral(p.module, n.info, result) + result = genNilStringLiteral(p.module, n.info) of tyString: - # with the new semantics for not 'nil' strings, we can map "" to nil and + # with the new semantics for 'nil' strings, we can map "" to nil and # save tons of allocations: - if n.strVal.len == 0 and optSeqDestructors notin p.config.globalOptions: - genNilStringLiteral(p.module, n.info, result) + if n.strVal.len == 0 and optNilSeqs notin p.options and + optSeqDestructors notin p.config.globalOptions: + result = genNilStringLiteral(p.module, n.info) else: - genStringLiteral(p.module, n, result) + result = genStringLiteral(p.module, n) else: - result.add makeCString(n.strVal) + result = makeCString(n.strVal) of nkFloatLit, nkFloat64Lit: - if ty.kind == tyFloat32: - result.add rope(n.floatVal.float32.toStrMaxPrecision) - else: - result.add rope(n.floatVal.toStrMaxPrecision) + result = rope(n.floatVal.toStrMaxPrecision) of nkFloat32Lit: - result.add rope(n.floatVal.float32.toStrMaxPrecision) + result = rope(n.floatVal.toStrMaxPrecision("f")) else: internalError(p.config, n.info, "genLiteral(" & $n.kind & ')') + result = nil -proc genLiteral(p: BProc, n: PNode; result: var Rope) = - genLiteral(p, n, n.typ, result) +proc genLiteral(p: BProc, n: PNode): Rope = + result = genLiteral(p, n, n.typ) 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) - let tmpName = p.module.tmpBase & rope(id) + result = p.module.tmpBase & rope(id) if id == p.module.labels: # not found in cache: inc(p.module.labels) - 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 + p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", + [getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)]) else: - genRawSetData(cs, size, result) + result = genRawSetData(cs, size) proc getStorageLoc(n: PNode): TStorageLoc = ## deadcode @@ -186,7 +176,8 @@ proc canMove(p: BProc, n: PNode; dest: TLoc): bool = if not isDeepConstExpr(n) or n.len == 0: if skipTypes(n.typ, abstractVarRange).kind == tySequence: return true - elif n.kind in nkStrKinds and n.strVal.len == 0: + elif optNilSeqs notin p.options and + n.kind in nkStrKinds and n.strVal.len == 0: # Empty strings are codegen'd as NIM_NIL so it's just a pointer copy return true result = n.kind in nkCallKinds @@ -220,7 +211,7 @@ proc asgnComplexity(n: PNode): int = else: discard proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = - assert field != "" + assert field != nil result.k = locField result.storage = a.storage result.lode = lodeTyp t @@ -254,7 +245,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, case t.kind of nkSym: let field = t.sym - if field.loc.r == "": fillObjectFields(p.module, typ) + if field.loc.r == nil: fillObjectFields(p.module, typ) genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r), optAsgnLoc(src, field.typ, field.loc.r), newflags) of nkRecList: @@ -298,8 +289,8 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) = linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $2Len_0;$n", [rdLoc(d), a.rdLoc]) of tySequence: - linefmt(p, cpsStmts, "$1.Field0 = ($5) ? ($2$3) : NIM_NIL; $1.Field1 = $4;$n", - [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a), dataFieldAccessor(p, a.rdLoc)]) + linefmt(p, cpsStmts, "$1.Field0 = $2$3; $1.Field1 = $4;$n", + [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)]) of tyArray: linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), rdLoc(a), rope(lengthOrd(p.config, a.t))]) @@ -308,8 +299,8 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) = if etyp.kind in {tyVar} and optSeqDestructors in p.config.globalOptions: linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) - linefmt(p, cpsStmts, "$1.Field0 = ($5) ? ($2$3) : NIM_NIL; $1.Field1 = $4;$n", - [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a), dataFieldAccessor(p, a.rdLoc)]) + linefmt(p, cpsStmts, "$1.Field0 = $2$3; $1.Field1 = $4;$n", + [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)]) else: internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind) @@ -410,7 +401,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)]) else: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) - of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCstring, + of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString, tyInt..tyUInt64, tyRange, tyVar, tyLent, tyNil: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) else: internalError(p.config, "genAssignment: " & $ty.kind) @@ -460,7 +451,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) = [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)]) else: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) - of tyPointer, tyChar, tyBool, tyEnum, tyCstring, + of tyPointer, tyChar, tyBool, tyEnum, tyCString, tyInt..tyUInt64, tyRange, tyVar, tyLent: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) else: internalError(p.config, "genDeepCopy: " & $ty.kind) @@ -555,21 +546,12 @@ 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(); ", - [result, cpname, rdCharLoc(a), rdCharLoc(b)]) - raiseInstr(p, p.s(cpsStmts)) - linefmt p, cpsStmts, "};$n", [] - + lineCg(p, cpsStmts, "if (#$2($3, $4, &$1)) { #raiseOverflow(); $5};$n", + [result, cpname, rdCharLoc(a), rdCharLoc(b), raiseInstr(p)]) if size < p.config.target.intSize or t.kind in {tyRange, tyEnum}: - 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", [] - + linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseOverflow(); $4}$n", + [result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t)), + raiseInstr(p)]) result proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = @@ -599,24 +581,13 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: # we handle div by zero here so that we know that the compilerproc's # result is only for overflows. - var needsOverflowCheck = true if m in {mDivI, mModI}: - var canBeZero = true - if e[2].kind in {nkIntLit..nkUInt64Lit}: - canBeZero = e[2].intVal == 0 - if e[2].kind in {nkIntLit..nkInt64Lit}: - needsOverflowCheck = e[2].intVal == -1 - if canBeZero: - 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]) - putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) - else: - let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)] - putIntoDest(p, d, e, res) + linefmt(p, cpsStmts, "if ($1 == 0){ #raiseDivByZero(); $2}$n", + [rdLoc(b), raiseInstr(p)]) + + let res = binaryArithOverflowRaw(p, t, a, b, + if t.kind == tyInt64: prc64[m] else: prc[m]) + putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = var @@ -626,13 +597,8 @@ 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: - 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", [] - + linefmt(p, cpsStmts, "if ($1 == $2){ #raiseOverflow(); $3}$n", + [rdLoc(a), intLiteral(firstOrd(p.config, t)), raiseInstr(p)]) case m of mUnaryMinusI: putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)]) @@ -752,22 +718,7 @@ 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 @@ -871,7 +822,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 != "" + assert r != nil while ty != nil: ty = ty.skipTypes(skipPtrs) assert(ty.kind in {tyTuple, tyObject}) @@ -892,18 +843,15 @@ 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.add ".Field" - r.add rope(f.position) + r.addf(".Field$1", [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 == "" 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 + 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]) putIntoDest(p, d, e, r, a.storage) - r.freeze proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) @@ -920,54 +868,20 @@ 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 = newRopeAppender() - v.r.add obj + v.r = obj v.r.add(".") v.r.add(disc.sym.loc.r) genInExprAux(p, it, u, v, test) - var msg = "" - if optDeclaredLocs in p.config.globalOptions: - # xxx this should be controlled by a separate flag, and - # used for other similar defects so that location information is shown - # even without the expensive `--stacktrace`; binary size could be optimized - # by encoding the file names separately from `file(line:col)`, essentially - # 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) - 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 - 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); " - linefmt(p, cpsStmts, code, [strLit]) - else: - const code = "{ #raiseFieldError2($1, (NI)$2); " - linefmt(p, cpsStmts, code, [strLit, discIndex]) - + let msg = genFieldDefect(field, disc.sym) + let strLit = genStringLiteral(p.module, newStrNode(nkStrLit, msg)) + if op.magic == mNot: + linefmt(p, cpsStmts, + "if ($1){ #raiseFieldError($2); $3}$n", + [rdLoc(test), strLit, raiseInstr(p)]) else: - # complication needed for signed types - let first = p.config.firstOrd(disc.sym.typ) - 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); " - linefmt(p, cpsStmts, code, [strLit]) - else: - 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", [] + linefmt(p, cpsStmts, + "if (!($1)){ #raiseFieldError($2); $3}$n", + [rdLoc(test), strLit, raiseInstr(p)]) proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = assert e[0].kind == nkDotExpr @@ -978,14 +892,12 @@ 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 == "": fillObjectFields(p.module, ty) - if field.loc.r == "": + if field.loc.r == nil: fillObjectFields(p.module, ty) + if field.loc.r == nil: internalError(p.config, e.info, "genCheckedRecordField") # generate the checks: genFieldCheck(p, e, r, field) - r.add(".") - r.add field.loc.r + r.add(ropecg(p.module, ".$1", [field.loc.r])) putIntoDest(p, d, e[0], r, a.storage) - r.freeze else: genRecordField(p, e[0], d) @@ -1002,28 +914,18 @@ 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 = newRopeAppender() - intLiteral(firstOrd(p.config, ty), first) + var first = intLiteral(firstOrd(p.config, ty)) # 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, ty) == 0: if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)): - 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", [] + linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); $3}$n", + [rdCharLoc(b), intLiteral(lastOrd(p.config, ty)), raiseInstr(p)]) else: - 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", [] - + linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3){ #raiseIndexError3($1, $2, $3); $4}$n", + [rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)), raiseInstr(p)]) else: let idx = getOrdValue(y) if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty): @@ -1047,37 +949,24 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = if reifiedOpenArray(arr.lode): linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "($1 < 0 || $1 >= $3.Field1 || $2 < 0 || $2 >= $3.Field1)){ #raiseIndexError4($1, $2, $3.Field1); ", - [rdLoc(a), rdLoc(b), rdLoc(arr)]) + "((NU)($1) >= (NU)($3.Field1) || (NU)($2) >= (NU)($3.Field1))){ #raiseIndexError(); $4}$n", + [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) else: linefmt(p, cpsStmts, - "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", [] - + "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)]) of tyArray: - var first = newRopeAppender() - intLiteral(firstOrd(p.config, ty), first) - var last = newRopeAppender() - intLiteral(lastOrd(p.config, ty), last) + let first = intLiteral(firstOrd(p.config, ty)) linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "($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", [] - + "($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)]) of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "($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", [] - + "((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)){ #raiseIndexError(); $4}$n", + [rdLoc(a), rdLoc(b), lenExpr(p, arr), raiseInstr(p)]) else: discard proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = @@ -1087,21 +976,15 @@ 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 ($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", [] - + linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n", + [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! 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 ($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", [] - + linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2.Field1)){ #raiseIndexError2($1,$2.Field1-1); $3}$n", + [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! inheritLocation(d, a) putIntoDest(p, d, n, ropecg(p.module, "$1.Field0[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) @@ -1114,12 +997,14 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: if optBoundsCheck in p.options: - linefmt(p, cpsStmts, - "if ($1 < 0 || $1 >= $2){ #raiseIndexError2($1,$2-1); ", - [rdCharLoc(b), lenExpr(p, a)]) - raiseInstr(p, p.s(cpsStmts)) - linefmt p, cpsStmts, "}$n", [] - + if ty.kind == tyString and not defined(nimNoZeroTerminator): + linefmt(p, cpsStmts, + "if ((NU)($1) > (NU)$2){ #raiseIndexError2($1,$2); $3}$n", + [rdLoc(b), lenExpr(p, a), raiseInstr(p)]) + else: + linefmt(p, cpsStmts, + "if ((NU)($1) >= (NU)$2){ #raiseIndexError2($1,$2-1); $3}$n", + [rdLoc(b), lenExpr(p, a), raiseInstr(p)]) if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = ropecg(p.module, "(*$1)", [a.r]) @@ -1138,7 +1023,7 @@ proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = of tyArray: genArrayElem(p, n, n[0], n[1], d) of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n[0], n[1], d) of tySequence, tyString: genSeqElem(p, n, n[0], n[1], d) - of tyCstring: genCStringElem(p, n, n[0], n[1], d) + of tyCString: genCStringElem(p, n, n[0], n[1], d) of tyTuple: genTupleElem(p, n, d) else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') discard getTypeDesc(p.module, n.typ) @@ -1223,23 +1108,17 @@ 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 = "" + var args: Rope = nil var a: TLoc - for i, it in n.sons: + for it in n.sons: if it.skipConv.kind == nkNilLit: args.add(", \"\"") - elif n.len != 0: + else: initLocExpr(p, it, a) - if i > 0: - args.add(", ") - case detectStrVersion(p.module) - of 2: - args.add(ropecg(p.module, "Genode::Cstring($1.p->data, $1.len)", [a.rdLoc])) - else: - args.add(ropecg(p.module, "Genode::Cstring($1->data, $1->len)", [a.rdLoc])) + args.add(ropecg(p.module, ", Genode::Cstring($1->data, $1->len)", [rdLoc(a)])) p.module.includeHeader("") p.module.includeHeader("") - linefmt(p, cpsStmts, """Genode::log($1);$n""", [args]) + linefmt(p, cpsStmts, """Genode::log(""$1);$n""", [args]) else: if n.len == 0: linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len]) @@ -1264,7 +1143,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' # # # { @@ -1282,8 +1161,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 = "" - var lens: Rope = "" + var appends: Rope = nil + var lens: Rope = nil 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? # # { @@ -1400,7 +1279,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 == "": + if sizeExpr.isNil: sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)] if optTinyRtti in p.config.globalOptions: @@ -1413,17 +1292,16 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = genAssignment(p, a, b, {}) else: let ti = genTypeInfoV1(p.module, typ, a.lode.info) - let op = getAttachedOp(p.module.g.graph, bt, attachedDestructor) - if op != nil and not isTrivialProc(p.module.g.graph, op): + if bt.destructor != nil and not isTrivialProc(bt.destructor): # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling # convention at least: - if op.typ == nil or op.typ.callConv != ccNimCall: + if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccNimCall: localError(p.module.config, a.lode.info, "the destructor that is turned into a finalizer needs " & "to have the 'nimcall' calling convention") var f: TLoc - initLocExpr(p, newSymNode(op), f) + initLocExpr(p, newSymNode(bt.destructor), f) p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) if a.storage == OnHeap and usesWriteBarrier(p.config): @@ -1456,7 +1334,7 @@ proc genNew(p: BProc, e: PNode) = initLocExpr(p, e[2], se) rawGenNew(p, a, se.rdLoc, needsInit = true) else: - rawGenNew(p, a, "", needsInit = true) + rawGenNew(p, a, nil, needsInit = true) gcUsage(p.config, e) proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = @@ -1497,7 +1375,8 @@ proc genNewSeq(p: BProc, e: PNode) = getTypeDesc(p.module, seqtype.lastSon), getSeqPayloadType(p.module, seqtype)]) else: - let lenIsZero = e[2].kind == nkIntLit and e[2].intVal == 0 + let lenIsZero = optNilSeqs notin p.options and + e[2].kind == nkIntLit and e[2].intVal == 0 genNewSeqAux(p, a, b.rdLoc, lenIsZero) gcUsage(p.config, e) @@ -1526,9 +1405,8 @@ 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 = ", [getTypeDesc(p.module, t), d.r]) - genBracedInit(p, n, isConst = true, t, p.module.s[cfsData]) - p.module.s[cfsData].addf(";$n", []) + p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", + [getTypeDesc(p.module, t), d.r, genBracedInit(p, n, isConst = true, t)]) proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: @@ -1565,7 +1443,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = getTemp(p, t, tmp) r = rdLoc(tmp) if isRef: - rawGenNew(p, tmp, "", needsInit = nfAllFieldsSet notin e.flags) + rawGenNew(p, tmp, nil, needsInit = nfAllFieldsSet notin e.flags) t = t.lastSon.skipTypes(abstractInstOwned) r = "(*$1)" % [r] gcUsage(p.config, e) @@ -1581,8 +1459,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 == "": fillObjectFields(p.module, ty) - if field.loc.r == "": internalError(p.config, e.info, "genObjConstr") + if field.loc.r == nil: fillObjectFields(p.module, ty) + if field.loc.r == nil: internalError(p.config, e.info, "genObjConstr") if it.len == 3 and optFieldCheck in p.options: genFieldCheck(p, it[2], r, field) tmp2.r.add(".") @@ -1615,21 +1493,19 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = elif d.k == locNone: getTemp(p, n.typ, d) - var lit = newRopeAppender() - intLiteral(n.len, lit) + let l = intLiteral(n.len) 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[], lit, getTypeDesc(p.module, seqtype.lastSon), + [rdLoc dest[], l, getTypeDesc(p.module, seqtype.lastSon), getSeqPayloadType(p.module, seqtype)]) else: # generate call to newSeq before adding the elements per hand: - genNewSeqAux(p, dest[], lit, n.len == 0) + genNewSeqAux(p, dest[], l, + optNilSeqs notin p.options and n.len == 0) for i in 0..finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ getTypeDesc(p.module, refType), @@ -1702,30 +1577,34 @@ proc genNewFinalize(p: BProc, e: PNode) = genObjectInit(p, cpsStmts, bt, a, constructRefObj) gcUsage(p.config, e) -proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Rope) = +proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope = if optTinyRtti in p.config.globalOptions: - let token = $genDisplayElem(MD5Digest(hashType(dest, p.config))) - appcg(p.module, result, "#isObjDisplayCheck($#.m_type, $#, $#)", [a, getObjDepth(dest), token]) + result = ropecg(p.module, "#isObj($1.m_type, $2)", + [a, genTypeInfo2Name(p.module, dest)]) else: # unfortunately 'genTypeInfoV1' sets tfObjHasKids as a side effect, so we # have to call it here first: let ti = genTypeInfoV1(p.module, dest, info) if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and tfObjHasKids notin dest.flags): - result.add "$1.m_type == $2" % [a, ti] + result = "$1.m_type == $2" % [a, ti] else: - cgsym(p.module, "TNimType") + discard cgsym(p.module, "TNimType") inc p.module.labels let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope p.module.s[cfsVars].addf("static TNimType* $#[2];$n", [cache]) - appcg(p.module, result, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache]) + result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache]) + when false: + # former version: + result = ropecg(p.module, "#isObj($1.m_type, $2)", + [a, genTypeInfoV1(p.module, dest, info)]) proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = var a: TLoc initLocExpr(p, x, a) var dest = skipTypes(typ, typedescPtrs) var r = rdLoc(a) - var nilCheck: Rope = "" + var nilCheck: Rope = nil var t = skipTypes(a.t, abstractInstOwned) while t.kind in {tyVar, tyLent, tyPtr, tyRef}: if t.kind notin {tyVar, tyLent}: nilCheck = r @@ -1735,27 +1614,16 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = discard getTypeDesc(p.module, t) if not p.module.compileToCpp: while t.kind == tyObject and t[0] != nil: - r.add(".Sup") + r.add(~".Sup") t = skipTypes(t[0], skipPtrs) if isObjLackingTypeField(t): globalError(p.config, x.info, "no 'of' operator available for pure objects") - - var ro = newRopeAppender() - genOfHelper(p, dest, r, x.info, ro) - var ofExpr = newRopeAppender() - ofExpr.add "(" - if nilCheck != "": - ofExpr.add "(" - ofExpr.add nilCheck - ofExpr.add ") && (" - ofExpr.add ro - ofExpr.add "))" + if nilCheck != nil: + r = ropecg(p.module, "(($1) && ($2))", [nilCheck, genOfHelper(p, dest, r, x.info)]) else: - ofExpr.add ro - ofExpr.add ")" - - putIntoDest(p, d, x, ofExpr, a.storage) + r = ropecg(p.module, "($1)", [genOfHelper(p, dest, r, x.info)]) + putIntoDest(p, d, x, r, a.storage) proc genOf(p: BProc, n: PNode, d: var TLoc) = genOf(p, n[1], n[2].typ, d) @@ -1792,9 +1660,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) of tyString, tySequence: putIntoDest(p, b, e, - "($4) ? ($1$3) : NIM_NIL, $2" % - [rdLoc(a), lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)], - a.storage) + "$1$3, $2" % [rdLoc(a), lenExpr(p, a), dataField(p)], a.storage) of tyArray: putIntoDest(p, b, e, "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage) @@ -1802,7 +1668,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), genTypeInfoV1(p.module, elemType(t), e.info)]), a.storage) - of tyCstring, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence: + of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence: putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)", [ rdLoc(a), genTypeInfoV1(p.module, t, e.info)]), a.storage) @@ -1814,15 +1680,14 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = a.storage) gcUsage(p.config, e) -proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope; result: var Rope; enforceV1 = false) = - var derefs = rdLoc(a) +proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope; enforceV1 = false): Rope = + result = rdLoc(a) var t = skipTypes(a.t, abstractInst) while t.kind in {tyVar, tyLent, tyPtr, tyRef}: - if t.kind notin {tyVar, tyLent}: nilCheck = derefs + if t.kind notin {tyVar, tyLent}: nilCheck = result if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: - derefs = "(*$1)" % [derefs] + result = "(*$1)" % [result] t = skipTypes(t.lastSon, abstractInst) - result.add derefs discard getTypeDesc(p.module, t) if not p.module.compileToCpp: while t.kind == tyObject and t[0] != nil: @@ -1833,7 +1698,7 @@ proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope; result: var Rope; enforceV1 result.add "->typeInfoV1" proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = - cgsym(p.module, "TNimType") + discard cgsym(p.module, "TNimType") let t = e[1].typ # ordinary static type information putIntoDest(p, d, e, genTypeInfoV1(p.module, t, e.info)) @@ -1846,20 +1711,9 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = else: var a: TLoc initLocExpr(p, e[1], a) - var nilCheck = "" + var nilCheck = Rope(nil) # use the dynamic type stored at offset 0: - 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 = "" - # use the dynamic type stored at offset 0: - var rt = newRopeAppender() - rdMType(p, a, nilCheck, rt) - putIntoDest(p, d, e, rt) + putIntoDest(p, d, e, rdMType(p, a, nilCheck)) template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc @@ -1879,13 +1733,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # Bug #9279, len(toOpenArray()) has to work: if a.kind in nkCallKinds and a[0].kind == nkSym and a[0].sym.magic == mSlice: # magic: pass slice to openArray: - var m: TLoc var b, c: TLoc - initLocExpr(p, a[1], m) initLocExpr(p, a[2], b) initLocExpr(p, a[3], c) - if optBoundsCheck in p.options: - genBoundsCheck(p, m, b, c) if op == mHigh: putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)", [rdLoc(b), rdLoc(c)])) else: @@ -1897,7 +1747,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: if op == mHigh: unaryExpr(p, e, d, "($1.Field1-1)") else: unaryExpr(p, e, d, "$1.Field1") - of tyCstring: + of tyCString: if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") of tyString: @@ -1991,17 +1841,14 @@ 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; result: var Rope) = +proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType): Rope = # read a location of an set element; it may need a subtraction operation # before the set operation - result.add "(" - result.add rdCharLoc(a) + result = rdCharLoc(a) let setType = typ.skipTypes(abstractPtrs) assert(setType.kind == tySet) if firstOrd(conf, setType) != 0: - result.add " - " - result.add rope(firstOrd(conf, setType)) - result.add ")" + result = "($1- $2)" % [result, rope(firstOrd(conf, setType))] proc fewCmps(conf: ConfigRef; s: PNode): bool = # this function estimates whether it is better to emit code @@ -2015,9 +1862,7 @@ 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) = - var elem = newRopeAppender() - rdSetElemLoc(p.config, b, a.t, elem) - putIntoDest(p, d, e, frmt % [rdLoc(a), elem]) + putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = case int(getSize(p.config, skipTypes(e[1].typ, abstractVar))) @@ -2032,9 +1877,7 @@ 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) - var elem = newRopeAppender() - rdSetElemLoc(p.config, b, a.t, elem) - lineF(p, cpsStmts, frmt, [rdLoc(a), elem]) + lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) proc genInOp(p: BProc, e: PNode, d: var TLoc) = var a, b, x, y: TLoc @@ -2126,7 +1969,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mCard: var a: TLoc initLocExpr(p, e[1], a) - putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [addrLoc(p.config, a), size])) + putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [rdCharLoc(a), size])) of mLtSet, mLeSet: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter initLocExpr(p, e[1], a) @@ -2188,11 +2031,6 @@ 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) @@ -2232,39 +2070,26 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = # emit range check: if n0t.kind in {tyUInt, tyUInt64}: - 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", [] - + 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)]) else: let raiser = case skipTypes(n.typ, abstractVarRange).kind of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU" of tyFloat..tyFloat128: "raiseRangeErrorF" else: "raiseRangeErrorI" - cgsym(p.module, raiser) + discard cgsym(p.module, raiser) let boundaryCast = - if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64}: + if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64} or + (n0t.sym != nil and sfSystemModule in n0t.sym.owner.flags and n0t.sym.name.s == "csize"): "(NI64)" else: "" - 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", [] - + 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]) putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) @@ -2318,15 +2143,9 @@ 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(); ", [rdLoc(d)]) - raiseInstr(p, p.s(cpsStmts)) - linefmt p, cpsStmts, "}$n", [] - + linefmt(p, cpsStmts, "if ($1 != $1){ #raiseFloatInvalidOp(); $2}$n", [rdLoc(d), raiseInstr(p)]) if optInfCheck in p.options: - linefmt(p, cpsStmts, "if ($1 != 0.0 && $1*0.5 == $1) { #raiseFloatOverflow($1); ", [rdLoc(d)]) - raiseInstr(p, p.s(cpsStmts)) - linefmt p, cpsStmts, "}$n", [] - + linefmt(p, cpsStmts, "if ($1 != 0.0 && $1*0.5 == $1) { #raiseFloatOverflow($1); $2}$n", [rdLoc(d), raiseInstr(p)]) else: binaryArith(p, e, d, m) @@ -2385,8 +2204,7 @@ proc genDestroy(p: BProc; n: PNode) = else: discard "nothing to do" else: let t = n[1].typ.skipTypes(abstractVar) - let op = getAttachedOp(p.module.g.graph, t, attachedDestructor) - if op != nil and getBody(p.module.g.graph, op).len != 0: + if t.destructor != nil and t.destructor.ast[bodyPos].len != 0: internalError(p.config, n.info, "destructor turned out to be not trivial") discard "ignore calls to the default destructor" @@ -2408,10 +2226,7 @@ proc genDispose(p: BProc; n: PNode) = lineCg(p, cpsStmts, ["#nimDestroyAndDispose($#)", rdLoc(a)]) proc genSlice(p: BProc; e: PNode; d: var TLoc) = - let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon, - prepareForMutation = e[1].kind == nkHiddenDeref and - e[1].typ.skipTypes(abstractInst).kind == tyString and - p.config.selectedGC in {gcArc, gcOrc}) + let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon) if d.k == locNone: getTemp(p, e.typ, d) linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y]) when false: @@ -2419,9 +2234,16 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) = "'toOpenArray' is only valid within a call expression") proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) = + const ToStringProcSlot = -4 let t = e[1].typ.skipTypes(abstractInst+{tyRange}) - let toStrProc = getToStringProc(p.module.g.graph, t) - # XXX need to modify this logic for IC. + var toStrProc: PSym = nil + for idx, p in items(t.methods): + if idx == ToStringProcSlot: + toStrProc = p + break + if toStrProc == nil: + toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph, p.module.idgen) + t.methods.add((ToStringProcSlot, toStrProc)) var n = copyTree(e) n[0] = newSymNode(toStrProc) expr(p, n, d) @@ -2486,14 +2308,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)") of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)") of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)") - of mFloatToStr: - if e[1].typ.skipTypes(abstractInst).kind == tyFloat32: - genDollar(p, e, d, "#nimFloat32ToStr($1)") - else: - genDollar(p, e, d, "#nimFloatToStr($1)") + of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") - of mStrToStr, mUnown: expr(p, e[1], d) - of generatedMagics: genCall(p, e, d) + of mStrToStr, mUnown, mIsolate: expr(p, e[1], d) of mEnumToStr: if optTinyRtti in p.config.globalOptions: genEnumToStr(p, e, d) @@ -2505,16 +2322,11 @@ 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, "", needsInit = true) + rawGenNew(p, a, nil, needsInit = true) gcUsage(p.config, e) else: genNewFinalize(p, e) - of mNewSeq: - if optSeqDestructors in p.config.globalOptions: - e[1] = makeAddr(e[1], p.module.idgen) - genCall(p, e, d) - else: - genNewSeq(p, e) + of mNewSeq: genNewSeq(p, e) of mNewSeqOfCap: genNewSeqOfCap(p, e, d) of mSizeOf: let t = e[1].typ.skipTypes({tyTypeDesc}) @@ -2555,7 +2367,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # somehow forward-declared from some other usage, but it is *possible* if lfNoDecl notin opr.loc.flags: let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r) - assert prc != nil, $opr.loc.r # HACK: # Explicitly add this proc as declared here so the cgsym call doesn't # add a forward declaration - without this we could end up with the same @@ -2568,13 +2379,13 @@ 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. - cgsym(p.module, $opr.loc.r) + discard 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", [mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)]) genCall(p, e, d) - of mDefault, mZeroDefault: genDefault(p, e, d) + of mDefault: genDefault(p, e, d) of mReset: genReset(p, e) of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) @@ -2582,13 +2393,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e[0].sym.name.s)) of mSpawn: when defined(leanCompiler): - p.config.quitOrRaise "compiler built without support for the 'spawn' statement" + quit "compiler built without support for the 'spawn' statement" else: let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.idgen, p.module.module, e, e.typ, nil, nil) expr(p, n, d) of mParallel: when defined(leanCompiler): - p.config.quitOrRaise "compiler built without support for the 'parallel' statement" + quit "compiler built without support for the 'parallel' statement" else: let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e) expr(p, n, d) @@ -2607,9 +2418,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mMove: genMove(p, e, d) of mDestroy: genDestroy(p, e) of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0") - of mAccessTypeField: genAccessTypeField(p, e, d) of mSlice: genSlice(p, e, d) - of mTrace: discard "no code to generate" else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind @@ -2623,9 +2432,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = var a, b, idx: TLoc if nfAllConst in e.flags: - var elem = newRopeAppender() - genSetNode(p, e, elem) - putIntoDest(p, d, e, elem) + putIntoDest(p, d, e, genSetNode(p, e)) else: if d.k == locNone: getTemp(p, e.typ, d) if getSize(p.config, e.typ) > 8: @@ -2637,19 +2444,13 @@ 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), - aa, bb]) + rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ)]) 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), aa]) + [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)]) else: # small set var ts = "NU" & $(getSize(p.config, e.typ) * 8) @@ -2659,21 +2460,15 @@ 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), aa, bb, rope(ts)]) + rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ), + rdSetElemLoc(p.config, b, e.typ), 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), aa, rope(ts)]) + [rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var rec: TLoc @@ -2699,10 +2494,8 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = if isConstClosure(n): inc(p.module.labels) var tmp = "CNSTCLOSURE" & rope(p.module.labels) - 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 + 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)]) putIntoDest(p, d, n, tmp, OnStatic) else: var tmp, a, b: TLoc @@ -2727,14 +2520,12 @@ 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 != "": + if frameName != nil: p.s(cpsStmts).add deinitFrameNoDebug(p, frameName) proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) = @@ -2774,29 +2565,18 @@ 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 = "" - var r = newRopeAppender() - rdMType(p, a, nilCheck, r) - if optTinyRtti in p.config.globalOptions: - let checkFor = $getObjDepth(dest) - let token = $genDisplayElem(MD5Digest(hashType(dest, p.config))) - if nilCheck != "": - linefmt(p, cpsStmts, "if ($1 && !#isObjDisplayCheck($2, $3, $4)){ #raiseObjectConversionError(); ", - [nilCheck, r, checkFor, token]) - else: - linefmt(p, cpsStmts, "if (!#isObjDisplayCheck($1, $2, $3)){ #raiseObjectConversionError(); ", - [r, checkFor, token]) + var nilCheck = Rope(nil) + let r = rdMType(p, a, nilCheck) + 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)]) else: - let checkFor = genTypeInfoV1(p.module, dest, n.info) - if nilCheck != "": - linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); ", - [nilCheck, r, checkFor]) - else: - linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); ", - [r, checkFor]) - raiseInstr(p, p.s(cpsStmts)) - linefmt p, cpsStmts, "}$n", [] - + linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); $3}$n", + [r, checkFor, raiseInstr(p)]) if n[0].typ.kind != tyObject: if n.isLValue: putIntoDest(p, d, n, @@ -2809,32 +2589,40 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = [getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage) proc downConv(p: BProc, n: PNode, d: var TLoc) = - var arg = n[0] - while arg.kind == nkObjDownConv: arg = arg[0] - - let dest = skipTypes(n.typ, abstractPtrs) - let src = skipTypes(arg.typ, abstractPtrs) - discard getTypeDesc(p.module, src) - let isRef = skipTypes(arg.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent} - if isRef and d.k == locNone and n.typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr} and n.isLValue: - # it can happen that we end up generating '&&x->Sup' here, so we pack - # the '&x->Sup' into a temporary and then those address is taken - # (see bug #837). However sometimes using a temporary is not correct: - # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test - # this by ensuring the destination is also a pointer: - var a: TLoc - initLocExpr(p, arg, a) - putIntoDest(p, d, n, - "(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage) - elif p.module.compileToCpp: - # C++ implicitly downcasts for us - expr(p, arg, d) + if p.module.compileToCpp: + discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs)) + expr(p, n[0], d) # downcast does C++ for us else: + var dest = skipTypes(n.typ, abstractPtrs) + + var arg = n[0] + while arg.kind == nkObjDownConv: arg = arg[0] + + var src = skipTypes(arg.typ, abstractPtrs) + discard getTypeDesc(p.module, src) var a: TLoc initLocExpr(p, arg, a) - var r = rdLoc(a) & (if isRef: "->Sup" else: ".Sup") + var r = rdLoc(a) + let isRef = skipTypes(arg.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent} + if isRef: + r.add("->Sup") + else: + r.add(".Sup") for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup") - putIntoDest(p, d, n, if isRef: "&" & r else: r, a.storage) + if isRef: + # it can happen that we end up generating '&&x->Sup' here, so we pack + # the '&x->Sup' into a temporary and then those address is taken + # (see bug #837). However sometimes using a temporary is not correct: + # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test + # this by ensuring the destination is also a pointer: + if d.k == locNone and skipTypes(n.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent}: + getTemp(p, n.typ, d) + linefmt(p, cpsStmts, "$1 = &$2;$n", [rdLoc(d), r]) + else: + r = "&" & r + putIntoDest(p, d, n, r, a.storage) + else: + putIntoDest(p, d, n, r, a.storage) proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = let t = n.typ @@ -2845,10 +2633,8 @@ 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 = ", - [getTypeDesc(p.module, t, skConst), tmp]) - genBracedInit(p, n, isConst = true, t, p.module.s[cfsData]) - p.module.s[cfsData].addf(";$n", []) + p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", + [getTypeDesc(p.module, t, skConst), tmp, genBracedInit(p, n, isConst = true, t)]) if d.k == locNone: fillLoc(d, locData, n, tmp, OnStatic) @@ -2859,65 +2645,8 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = if t.kind notin {tySequence, tyString}: d.storage = OnStatic -proc genConstSetup(p: BProc; sym: PSym): bool = - let m = p.module - useHeader(m, sym) - if sym.loc.k == locNone: - 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 == "": - if not genConstSetup(p, sym): return - 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( - "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, - getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)]) - else: - let headerDecl = "extern NIM_CONST $1 $2;$n" % - [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r] - m.s[cfsData].add(headerDecl) - if sfExportc in sym.flags and p.module.g.generatedHeader != nil: - p.module.g.generatedHeader.s[cfsData].add(headerDecl) - -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 - 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]) - # register it (but ignore the boolean result of hcrRegisterGlobal) - q.initProc.procSec(cpsLocals).addf( - "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n", - [getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)]) - # always copy over the contents of the actual constant with the _const - # suffix ==> this means that the constant is reloadable & updatable! - q.initProc.procSec(cpsLocals).add(ropecg(q, - "\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n", - [sym.loc.r, actualConstName, rdLoc(sym.loc)])) - -proc genConstStmt(p: BProc, n: PNode) = - # This code is only used in the new DCE implementation. - assert useAliveDataFromDce in p.module.flags - let m = p.module - for it in n: - if it[0].kind == nkSym: - let sym = it[0].sym - if not isSimpleConst(sym.typ) and sym.itemId.item in m.alive and genConstSetup(p, sym): - genConstDefinition(m, p, sym) - proc expr(p: BProc, n: PNode, d: var TLoc) = - when defined(nimCompilerStacktraceHints): + when defined(nimCompilerStackraceHints): setFrameMsg p.config$n.info & " " & $n.kind p.currLineInfo = n.info @@ -2926,7 +2655,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = var sym = n.sym case sym.kind of skMethod: - if useAliveDataFromDce in p.module.flags or {sfDispatcher, sfForward} * sym.flags != {}: + if {sfDispatcher, sfForward} * sym.flags != {}: # we cannot produce code for the dispatcher yet: fillProcLoc(p.module, n) genProcPrototype(p.module, sym) @@ -2939,23 +2668,13 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sfCompileTime in sym.flags: localError(p.config, n.info, "request to generate code for .compileTime proc: " & sym.name.s) - if useAliveDataFromDce in p.module.flags and sym.typ.callConv != ccInline: - fillProcLoc(p.module, n) - genProcPrototype(p.module, sym) - else: - genProc(p.module, sym) - if sym.loc.r == "" or sym.loc.lode == nil: + genProc(p.module, sym) + if sym.loc.r == nil 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): - 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 != "") and (sym.loc.t != nil)) - putLocIntoDest(p, d, sym.loc) + putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic) else: genComplexConst(p, sym, d) of skEnumField: @@ -2969,7 +2688,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 == "" or sym.loc.t == nil: + if sym.loc.r == nil 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 @@ -2984,17 +2703,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of skTemp: when false: # this is more harmful than helpful. - if sym.loc.r == "": + if sym.loc.r == nil: # we now support undeclared 'skTemp' variables for easier # transformations in other parts of the compiler: assignLocalVar(p, n) - if sym.loc.r == "" or sym.loc.t == nil: + if sym.loc.r == nil 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 == "" or sym.loc.t == nil: + if sym.loc.r == nil or sym.loc.t == nil: # echo "FAILED FOR PRCO ", p.prc.name.s # debug p.prc.typ.n # echo renderTree(p.prc.ast, {renderIds}) @@ -3003,17 +2722,12 @@ 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): - var lit = newRopeAppender() - genLiteral(p, n, lit) - putIntoDest(p, d, n, lit) + putIntoDest(p, d, n, genLiteral(p, n)) of nkStrLit..nkTripleStrLit: - 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) + putDataIntoDest(p, d, n, genLiteral(p, n)) + of nkIntLit..nkUInt64Lit, + nkFloatLit..nkFloat128Lit, nkCharLit: + putIntoDest(p, d, n, genLiteral(p, n)) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: genLineDir(p, n) # may be redundant, it is generated in fixupCall as well @@ -3033,9 +2747,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genCall(p, n, d) of nkCurly: if isDeepConstExpr(n) and n.len != 0: - var lit = newRopeAppender() - genSetNode(p, n, lit) - putIntoDest(p, d, n, lit) + putIntoDest(p, d, n, genSetNode(p, n)) else: genSetConstr(p, n, d) of nkBracket: @@ -3055,16 +2767,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkObjConstr: genObjConstr(p, n, d) of nkCast: genCast(p, n, d) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d) - of nkHiddenAddr: - if n[0].kind == nkDerefExpr: - # addr ( deref ( x )) --> x - var x = n[0][0] - if n.typ.skipTypes(abstractVar).kind != tyOpenArray: - x.typ = n.typ - expr(p, x, d) - return - genAddr(p, n, d) - of nkAddr: genAddr(p, n, d) + of nkHiddenAddr, nkAddr: genAddr(p, n, d) of nkBracketExpr: genBracketExpr(p, n, d) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d) of nkDotExpr: genRecordField(p, n, d) @@ -3084,7 +2787,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 == "" or sym.loc.lode == nil: + if sym.loc.r == nil 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) @@ -3092,10 +2795,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkEmpty: discard of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) - of nkConstSection: - if useAliveDataFromDce in p.module.flags: - genConstStmt(p, n) - # else: consts generated lazily on use + of nkConstSection: discard # consts generated lazily on use of nkForStmt: internalError(p.config, n.info, "for statement not eliminated") of nkCaseStmt: genCase(p, n, d) of nkReturnStmt: genReturnStmt(p, n) @@ -3104,7 +2804,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = cow(p, n[1]) if nfPreventCg notin n.flags: genAsgn(p, n, fastAsgn=false) - of nkFastAsgn, nkSinkAsgn: + of nkFastAsgn: cow(p, n[1]) if nfPreventCg notin n.flags: # transf is overly aggressive with 'nkFastAsgn', so we work around here. @@ -3136,35 +2836,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard of nkPragma: genPragma(p, n) - of nkPragmaBlock: - var inUncheckedAssignSection = 0 - let pragmaList = n[0] - for pi in pragmaList: - if whichPragma(pi) == wCast: - case whichPragma(pi[1]) - of wUncheckedAssign: - inUncheckedAssignSection = 1 - else: - discard - - inc p.inUncheckedAssignSection, inUncheckedAssignSection - expr(p, n.lastSon, d) - dec p.inUncheckedAssignSection, inUncheckedAssignSection - + of nkPragmaBlock: expr(p, n.lastSon, d) of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: 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 generatedMagics: - genProc(p.module, prc) - elif prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: + # due to a bug/limitation in the lambda lifting, unused inner procs + # are not transformed correctly. We work around this issue (#411) here + # by ensuring it's no inner proc (owner is a module): + if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (prc.kind == skMethod): - # due to a bug/limitation in the lambda lifting, unused inner procs - # are not transformed correctly. We work around this issue (#411) here - # by ensuring it's no inner proc (owner is a module). # Generate proc even if empty body, bugfix #11651. genProc(p.module, prc) of nkParForStmt: genParForStmt(p, n) @@ -3174,53 +2856,54 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = inc p.splitDecls genGotoState(p, n) of nkBreakState: genBreakState(p, n, d) - of nkMixinStmt, nkBindStmt: discard else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") -proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = +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 = var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc}) case t.kind - 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, + of tyBool: result = rope"NIM_FALSE" + of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0" + of tyFloat..tyFloat128: result = rope"0.0" + of tyCString, tyVar, tyLent, tyPointer, tyPtr, tyUntyped, tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil: - result.add rope"NIM_NIL" + result = rope"NIM_NIL" of tyString, tySequence: if optSeqDestructors in p.config.globalOptions: - result.add "{0, NIM_NIL}" + result = rope"{0, NIM_NIL}" else: - result.add "NIM_NIL" + result = rope"NIM_NIL" of tyProc: if t.callConv != ccClosure: - result.add "NIM_NIL" + result = rope"NIM_NIL" else: - result.add "{NIM_NIL, NIM_NIL}" + result = rope"{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.add "{" - if p.vccAndC and t.len == 0: - result.add "0" + result = rope"{" for i in 0.. 0: result.add ", " - getDefaultValue(p, t[i], info, result) + result.add getDefaultValue(p, t[i], info) result.add "}" of tyArray: - result.add "{" + result = rope"{" for i in 0.. 0: result.add ", " - getDefaultValue(p, t.sons[1], info, result) + result.add getDefaultValue(p, t.sons[1], info) result.add "}" #result = rope"{}" of tyOpenArray, tyVarargs: - result.add "{NIM_NIL, 0}" + result = rope"{NIM_NIL, 0}" of tySet: - if mapSetType(p.config, t) == ctArray: result.add "{}" - else: result.add "0" + if mapSetType(p.config, t) == ctArray: result = rope"{}" + else: result = rope"0" else: globalError(p.config, info, "cannot create null element for: " & $t.kind) @@ -3239,11 +2922,6 @@ proc caseObjDefaultBranch(obj: PNode; branch: Int128): int = return i assert(false, "unreachable") -proc isEmptyCaseObjectBranch(n: PNode): bool = - for it in n: - if it.kind == nkSym and not isEmptyType(it.sym.typ): return false - return true - proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, result: var Rope; count: var int; isConst: bool, info: TLineInfo) = @@ -3272,7 +2950,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, let b = lastSon(obj[selectedBranch]) # designated initilization is the only way to init non first element of unions # branches are allowed to have no members (b.len == 0), in this case they don't need initializer - if b.kind == nkRecList and not isEmptyCaseObjectBranch(b): + if b.kind == nkRecList and b.len > 0: result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {" getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info) result.add "}" @@ -3288,15 +2966,14 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, if constOrNil != nil: for i in 1.. 0: result.add ",\n" - if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, it[0].typ, result) - else: genBracedInit(p, it, isConst, it.typ, result) + if it.kind == nkExprColonExpr: result.add genBracedInit(p, it[1], isConst, it[0].typ) + else: result.add genBracedInit(p, it, isConst, it.typ) result.add("}\n") -proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Rope) = - result.add "{" - if p.vccAndC and n.len == 0: - result.add "0" +proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType): Rope = + result = rope("{") for i in 0.. 0: result.add ",\n" - if it.kind == nkExprColonExpr: genBracedInit(p, it[1], isConst, tup[i], result) - else: genBracedInit(p, it, isConst, tup[i], result) + if it.kind == nkExprColonExpr: result.add genBracedInit(p, it[1], isConst, tup[i]) + else: result.add genBracedInit(p, it, isConst, tup[i]) result.add("}\n") -proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) = +proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool): Rope = var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope] let base = t.skipTypes(abstractInst)[0] if n.len > 0: @@ -3362,44 +3035,44 @@ proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) data.add(", {") for i in 0.. 0: data.addf(",$n", []) - genBracedInit(p, n[i], isConst, base, data) + data.add genBracedInit(p, n[i], isConst, base) data.add("}") data.add("}") - let tmpName = getTempName(p.module) + result = getTempName(p.module) - appcg(p.module, cfsStrData, + appcg(p.module, cfsData, "static $5 struct {$n" & " #TGenericSeq Sup;$n" & " $1 data[$2];$n" & "} $3 = $4;$n", [ - getTypeDesc(p.module, base), n.len, tmpName, data, + getTypeDesc(p.module, base), n.len, result, data, if isConst: "NIM_CONST" else: ""]) - result.add "(($1)&$2)" % [getTypeDesc(p.module, t), tmpName] + result = "(($1)&$2)" % [getTypeDesc(p.module, t), result] -proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) = +proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope = let base = t.skipTypes(abstractInst)[0] var data = rope"{" for i in 0.. 0: data.addf(",$n", []) - genBracedInit(p, n[i], isConst, base, data) + data.add genBracedInit(p, n[i], isConst, base) data.add("}") let payload = getTempName(p.module) - appcg(p.module, cfsStrData, + appcg(p.module, cfsData, "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.add "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload] + result = "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload] -proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope) = +proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope = case n.kind of nkHiddenStdConv, nkHiddenSubConv: - genBracedInit(p, n[1], isConst, n.typ, result) + result = genBracedInit(p, n[1], isConst, n.typ) else: var ty = tyNone var typ: PType = nil @@ -3414,12 +3087,12 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul case ty of tySet: let cs = toBitSet(p.config, n) - genRawSetData(cs, int(getSize(p.config, n.typ)), result) + result = genRawSetData(cs, int(getSize(p.config, n.typ))) of tySequence: if optSeqDestructors in p.config.globalOptions: - genConstSeqV2(p, n, typ, isConst, result) + result = genConstSeqV2(p, n, typ, isConst) else: - genConstSeq(p, n, typ, isConst, result) + result = genConstSeq(p, n, typ, isConst) 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` @@ -3432,45 +3105,44 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul # leading to duplicate code like this: # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}" if n[0].kind == nkNilLit: - result.add "{NIM_NIL,NIM_NIL}" + result = ~"{NIM_NIL,NIM_NIL}" else: var d: TLoc initLocExpr(p, n[0], d) - result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)] + result = "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)] else: var d: TLoc initLocExpr(p, n, d) - result.add rdLoc(d) + result = rdLoc(d) of tyArray, tyVarargs: - genConstSimpleList(p, n, isConst, result) + result = genConstSimpleList(p, n, isConst) of tyTuple: - genConstTuple(p, n, isConst, typ, result) + result = genConstTuple(p, n, isConst, typ) of tyOpenArray: if n.kind != nkBracket: internalError(p.config, n.info, "const openArray expression is not an array construction") - var data = newRopeAppender() - genConstSimpleList(p, n, isConst, data) + let data = genConstSimpleList(p, n, isConst) let payload = getTempName(p.module) let ctype = getTypeDesc(p.module, typ[0]) let arrLen = n.len - appcg(p.module, cfsStrData, + appcg(p.module, cfsData, "static $5 $1 $3[$2] = $4;$n", [ ctype, arrLen, payload, data, if isConst: "const" else: ""]) - result.add "{($1*)&$2, $3}" % [ctype, payload, rope arrLen] + result = "{($1*)&$2, $3}" % [ctype, payload, rope arrLen] of tyObject: - genConstObjConstr(p, n, isConst, result) - of tyString, tyCstring: + result = genConstObjConstr(p, n, isConst) + of tyString, tyCString: if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString: - genStringLiteralV2Const(p.module, n, isConst, result) + result = genStringLiteralV2Const(p.module, n, isConst) else: var d: TLoc initLocExpr(p, n, d) - result.add rdLoc(d) + result = rdLoc(d) else: var d: TLoc initLocExpr(p, n, d) - result.add rdLoc(d) + result = rdLoc(d) diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index cbef6771f..ee56da586 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.astdef)) + m.g.field = toInt(ast.getInt(core.ast)) result = m.g.field proc detectStrVersion(m: BModule): int = @@ -32,87 +32,82 @@ proc detectSeqVersion(m: BModule): int = # ----- Version 1: GC'ed strings and seqs -------------------------------- -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 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 genStringLiteralV1(m: BModule; n: PNode; result: var Rope) = +proc genStringLiteralV1(m: BModule; n: PNode): Rope = if s.isNil: - appcg(m, result, "((#NimStringDesc*) NIM_NIL)", []) + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) else: let id = nodeTableTestOrSet(m.dataCache, n, m.labels) if id == m.labels: # string literal not found in the cache: - appcg(m, result, "((#NimStringDesc*) &", []) - genStringLiteralDataOnlyV1(m, n.strVal, result) - result.add ")" + result = ropecg(m, "((#NimStringDesc*) &$1)", + [genStringLiteralDataOnlyV1(m, n.strVal)]) else: - appcg(m, result, "((#NimStringDesc*) &$1$2)", + result = ropecg(m, "((#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[cfsStrData].addf("static $4 struct {$n" & + m.s[cfsData].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; result: var Rope) = +proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool): Rope = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) if id == m.labels: let pureLit = getTempName(m) genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst) - let tmp = getTempName(m) - result.add tmp - cgsym(m, "NimStrPayload") - cgsym(m, "NimStringV2") + result = getTempName(m) + discard cgsym(m, "NimStrPayload") + discard cgsym(m, "NimStringV2") # string literal not found in the cache: - m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", - [tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")]) + m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", + [result, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")]) else: - 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), + result = getTempName(m) + m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", + [result, rope(n.strVal.len), m.tmpBase & rope(id), rope(if isConst: "const" else: "")]) -proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) = +proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool): Rope = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) var pureLit: Rope if id == m.labels: pureLit = getTempName(m) - cgsym(m, "NimStrPayload") - cgsym(m, "NimStringV2") + discard cgsym(m, "NimStrPayload") + discard cgsym(m, "NimStringV2") # string literal not found in the cache: genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst) else: pureLit = m.tmpBase & rope(id) - result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit] + result = "{$1, (NimStrPayload*)&$2}" % [rope(n.strVal.len), pureLit] # ------ Version selector --------------------------------------------------- proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo; - isConst: bool; result: var Rope) = + isConst: bool): Rope = case detectStrVersion(m) - of 0, 1: genStringLiteralDataOnlyV1(m, s, result) + of 0, 1: result = genStringLiteralDataOnlyV1(m, s) of 2: - let tmp = getTempName(m) - genStringLiteralDataOnlyV2(m, s, tmp, isConst) - result.add tmp + result = getTempName(m) + genStringLiteralDataOnlyV2(m, s, result, isConst) else: localError(m.config, info, "cannot determine how to produce code for string literal") -proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) = - appcg(m, result, "((#NimStringDesc*) NIM_NIL)", []) +proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope = + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) -proc genStringLiteral(m: BModule; n: PNode; result: var Rope) = +proc genStringLiteral(m: BModule; n: PNode): Rope = case detectStrVersion(m) - of 0, 1: genStringLiteralV1(m, n, result) - of 2: genStringLiteralV2(m, n, isConst = true, result) + of 0, 1: result = genStringLiteralV1(m, n) + of 2: result = genStringLiteralV2(m, n, isConst = true) else: localError(m.config, n.info, "cannot determine how to produce code for string literal") diff --git a/compiler/ccgmerge_unused.nim b/compiler/ccgmerge.nim similarity index 96% rename from compiler/ccgmerge_unused.nim rename to compiler/ccgmerge.nim index a1413034f..40833a64e 100644 --- a/compiler/ccgmerge_unused.nim +++ b/compiler/ccgmerge.nim @@ -19,11 +19,13 @@ import const CFileSectionNames: array[TCFileSection, string] = [ + cfsMergeInfo: "", cfsHeaders: "NIM_merge_HEADERS", cfsFrameDefines: "NIM_merge_FRAME_DEFINES", cfsForwardTypes: "NIM_merge_FORWARD_TYPES", cfsTypes: "NIM_merge_TYPES", cfsSeqTypes: "NIM_merge_SEQ_TYPES", + cfsFieldInfo: "NIM_merge_FIELD_INFO", cfsTypeInfo: "NIM_merge_TYPE_INFO", cfsProcHeaders: "NIM_merge_PROC_HEADERS", cfsData: "NIM_merge_DATA", @@ -32,8 +34,11 @@ const cfsInitProc: "NIM_merge_INIT_PROC", cfsDatInitProc: "NIM_merge_DATINIT_PROC", cfsTypeInit1: "NIM_merge_TYPE_INIT1", + cfsTypeInit2: "NIM_merge_TYPE_INIT2", cfsTypeInit3: "NIM_merge_TYPE_INIT3", - cfsDynLibInit: "NIM_merge_DYNLIB_INIT" + cfsDebugInit: "NIM_merge_DEBUG_INIT", + cfsDynLibInit: "NIM_merge_DYNLIB_INIT", + cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT", ] CProcSectionNames: array[TCProcSection, string] = [ cpsLocals: "NIM_merge_PROC_LOCALS", @@ -213,6 +218,9 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) = m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0) else: doAssert(false, "ccgmerge: unknown key: " & k) +when not defined(nimhygiene): + {.pragma: inject.} + template withCFile(cfilename: AbsoluteFile, body: untyped) = var s = llStreamOpen(cfilename, fmRead) if s == nil: return diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index 5e6456704..ef1505f57 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 == "": fillObjectFields(p.module, typ) + if disc.loc.r == nil: 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 == "": fillObjectFields(p.module, typ) + if field.loc.r == nil: 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) @@ -85,22 +85,9 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor]) of tyChar, tyBool, tyEnum, tyInt..tyUInt64: lineCg(p, cpsStmts, "$1 = 0;$n", [accessor]) - of tyCstring, tyPointer, tyPtr, tyVar, tyLent: + of tyCString, tyPointer, tyPtr, tyVar, tyLent: lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor]) - 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}: + else: discard proc specializeReset(p: BProc, a: TLoc) = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 7ade85b42..8c419caac 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -8,44 +8,37 @@ # # included from cgen.nim + const RangeExpandLimit = 256 # do not generate ranges # over 'RangeExpandLimit' elements stringCaseThreshold = 8 # above X strings a hash-switch for strings is generated -proc registerTraverseProc(p: BProc, v: PSym) = - var traverseProc = "" - if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcRefc} and +proc getTraverseProc(p: BProc, v: PSym): Rope = + if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcV2, 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 :-) - traverseProc = genTraverseProcForGlobal(p.module, v, v.info) + result = genTraverseProcForGlobal(p.module, v, v.info) - 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 registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) = + if sfThread in v.flags: + appcg(p.module, p.module.initProc.procSec(cpsInit), + "$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc]) + else: + appcg(p.module, p.module.initProc.procSec(cpsInit), + "$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc]) proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = - if n.kind == nkEmpty: - result = false - elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc: - if isInvalidReturnType(conf, n[0].typ, true): - # var v = f() - # is transformed into: var v; f(addr v) - # where 'f' **does not** initialize the result! - result = false - else: - result = true - elif isInvalidReturnType(conf, n.typ, false): - result = false - else: - result = true + if n.kind == nkEmpty: return false + if isInvalidReturnType(conf, n.typ): + # var v = f() + # is transformed into: var v; f(addr v) + # where 'f' **does not** initialize the result! + return false + result = true proc inExceptBlockLen(p: BProc): int = for x in p.nestedTryStmts: @@ -54,9 +47,7 @@ proc inExceptBlockLen(p: BProc): int = proc startBlockInternal(p: BProc): int {.discardable.} = inc(p.labels) result = p.blocks.len - - p.blocks.add initBlock() - + setLen(p.blocks, result + 1) p.blocks[result].id = p.labels p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16 @@ -80,7 +71,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: "" + let hcrCond = if forHcr: getTempName(p.module) else: nil 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 @@ -99,10 +90,13 @@ 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, "") + assignGlobalVar(p, vn, nil) genObjectInit(p, cpsInit, v.typ, v.loc, constructObj) - registerTraverseProc(p, v) + traverseProc = getTraverseProc(p, v) + if traverseProc != nil and not p.hcrOn: + registerTraverseProc(p, v, traverseProc) else: assignLocalVar(p, vn) initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1])) @@ -114,7 +108,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: "NULL")) + hcrGlobals.add((loc: v.loc, tp: if traverseProc == nil: ~"NULL" else: traverseProc)) if forHcr: # end the block where the tuple gets initialized @@ -144,12 +138,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; result: var Rope) {.inline.} = +proc assignLabel(b: var TBlock): Rope {.inline.} = b.label = "LA" & b.id.rope - result.add b.label + result = b.label -proc blockBody(b: var TBlock; result: var Rope) = - result.add b.sections[cpsLocals] +proc blockBody(b: var TBlock): Rope = + result = b.sections[cpsLocals] if b.frameLen > 0: result.addf("FR_.len+=$1;$n", [b.frameLen.rope]) result.add(b.sections[cpsInit]) @@ -158,7 +152,7 @@ proc blockBody(b: var TBlock; result: var Rope) = proc endBlock(p: BProc, blockEnd: Rope) = let topBlock = p.blocks.len-1 # the block is merged into the parent block - p.blocks[topBlock].blockBody(p.blocks[topBlock-1].sections[cpsStmts]) + p.blocks[topBlock-1].sections[cpsStmts].add(p.blocks[topBlock].blockBody) setLen(p.blocks, topBlock) # this is done after the block is popped so $n is # properly indented when pretty printing is enabled @@ -170,7 +164,7 @@ proc endBlock(p: BProc) = var blockEnd: Rope if frameLen > 0: blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope]) - if p.blocks[topBlock].label.len != 0: + if p.blocks[topBlock].label != nil: blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label]) else: blockEnd.addf("}$n", []) @@ -278,15 +272,17 @@ 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; result: var Rope) +proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope -proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) = +proc potentialValueInit(p: BProc; v: PSym; value: PNode): Rope = if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn: - discard "nothing to do" + result = nil 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 - genBracedInit(p, value, isConst = false, v.typ, result) + result = genBracedInit(p, value, isConst = false, v.typ) + else: + result = nil proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = if sfGoto in v.flags: @@ -294,8 +290,8 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = genGotoVar(p, value) return var targetProc = p - var valueAsRope = "" - potentialValueInit(p, v, value, valueAsRope) + var traverseProc: Rope + let valueAsRope = potentialValueInit(p, v, value) if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and value.kind == nkEmpty and @@ -311,7 +307,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.len == 0: + if valueAsRope == nil: var loc = v.loc # When the native TLS is unavailable, a global thread-local variable needs @@ -325,7 +321,9 @@ 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) - registerTraverseProc(p, v) + traverseProc = getTraverseProc(p, v) + if traverseProc != nil and not p.hcrOn: + registerTraverseProc(p, v, traverseProc) else: let imm = isAssignedImmediately(p.config, value) if imm and p.module.compileToCpp and p.splitDecls == 0 and @@ -338,14 +336,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 = newRopeAppender() - var argsCounter = 0 + var params: Rope 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") - p.blocks[idx].label = "LA" & p.blocks[idx].id.rope + let label = assignLabel(p.blocks[idx]) blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts, p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts) genLineDir(p, t) - lineF(p, cpsStmts, "goto $1;$n", [p.blocks[idx].label]) + lineF(p, cpsStmts, "goto $1;$n", [label]) proc raiseExit(p: BProc) = assert p.config.exc == excGoto @@ -727,19 +722,21 @@ proc finallyActions(p: BProc) = if finallyBlock != nil: genSimpleBlock(p, finallyBlock[0]) -proc raiseInstr(p: BProc; result: var Rope) = +proc raiseInstr(p: BProc): Rope = if p.config.exc == excGoto: let L = p.nestedTryStmts.len if L == 0: p.flags.incl beforeRetNeeded # easy case, simply goto 'ret': - result.add ropecg(p.module, "goto BeforeRet_;$n", []) + result = 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.add ropecg(p.module, "goto LA$1_;$n", + result = 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: @@ -768,10 +765,12 @@ 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", []) - raiseInstr(p, p.s(cpsStmts)) + let gotoInstr = raiseInstr(p) + if gotoInstr != nil: + line(p, cpsStmts, gotoInstr) template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, rangeFormat, eqFormat: FormatStr, labl: TLabel) = @@ -831,27 +830,17 @@ 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: "" + splitPoint, a) else: nil # generate switch part (might be empty): if splitPoint+1 < n.len: @@ -965,7 +939,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 != "": fixLabel(p, lend) + if lend != nil: fixLabel(p, lend) proc genCase(p: BProc, t: PNode, d: var TLoc) = genLineDir(p, t) @@ -973,9 +947,7 @@ 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, tyString, d) - of tyCstring: - genStringCase(p, t, tyCstring, d) + genStringCase(p, t, d) of tyFloat..tyFloat128: genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n", "if ($1 == $2) goto $3;$n") @@ -1066,7 +1038,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) else: - var orExpr = newRopeAppender() + var orExpr = Rope(nil) var exvar = PNode(nil) for j in 0..$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ, p.config)))]) + if orExpr != nil: orExpr.add("||") + let checkFor = if optTinyRtti in p.config.globalOptions: + genTypeInfo2Name(p.module, typeNode.typ) else: - let checkFor = genTypeInfoV1(p.module, typeNode.typ, typeNode.info) - appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) + genTypeInfoV1(p.module, typeNode.typ, typeNode.info) + let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" + appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) - if orExpr.len != 0: + if orExpr != nil: if hasIf: startBlock(p, "else if ($1) {$n", [orExpr]) else: startBlock(p, "if ($1) {$n", [orExpr]) hasIf = true if exvar != nil: - fillLocalName(p, exvar.sym) - fillLoc(exvar.sym.loc, locTemp, exvar, OnStack) + fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), 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: @@ -1135,8 +1105,7 @@ 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:` - fillLocalName(p, exvar.sym) - fillLoc(exvar.sym.loc, locTemp, exvar, OnStack) + fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), 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) @@ -1189,7 +1158,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) - cgsym(p.module, "popCurrentExceptionEx") + discard 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") @@ -1215,8 +1184,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) = for j in 0..$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) + if orExpr != nil: orExpr.add("||") + let checkFor = if optTinyRtti in p.config.globalOptions: + genTypeInfo2Name(p.module, t[i][j].typ) else: - let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) - appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) + genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) + let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" + appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) if i > 1: line(p, cpsStmts, "else ") startBlock(p, "if ($1) {$n", [orExpr]) @@ -1379,7 +1346,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = else: p.flags.incl noSafePoints genLineDir(p, t) - cgsym(p.module, "Exception") + discard cgsym(p.module, "Exception") var safePoint: Rope if not quirkyExceptions: safePoint = getTempName(p.module) @@ -1389,24 +1356,8 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) elif isDefined(p.config, "nimSigSetjmp"): linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint]) - elif isDefined(p.config, "nimBuiltinSetjmp"): - linefmt(p, cpsStmts, "$1.status = __builtin_setjmp($1.context);$n", [safePoint]) elif isDefined(p.config, "nimRawSetjmp"): - if isDefined(p.config, "mswindows"): - if isDefined(p.config, "vcc") or isDefined(p.config, "clangcl"): - # For the vcc compiler, use `setjmp()` with one argument. - # See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setjmp?view=msvc-170 - linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) - 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 - linefmt(p, cpsStmts, "$1.status = _setjmp($1.context, 0);$n", [safePoint]) - else: - linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint]) + linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint]) else: linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint]) @@ -1438,17 +1389,16 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) else: - var orExpr = newRopeAppender() + var orExpr: Rope = nil for j in 0..$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) + if orExpr != nil: orExpr.add("||") + let checkFor = if optTinyRtti in p.config.globalOptions: + genTypeInfo2Name(p.module, t[i][j].typ) else: - let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) - appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) + genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) + let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" + appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) if i > 1: line(p, cpsStmts, "else ") startBlock(p, "if ($1) {$n", [orExpr]) @@ -1474,7 +1424,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = if not quirkyExceptions: linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint]) -proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = +proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = var res = "" for it in t.sons: case it.kind @@ -1490,8 +1440,13 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = res.add($getTypeDesc(p.module, sym.typ)) else: discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs)) - fillBackendName(p.module, sym) - res.add($sym.loc.r) + var r = sym.loc.r + if r == nil: + # if no name has already been given, + # it doesn't matter much: + r = mangleName(p.module, sym) + sym.loc.r = r # but be consequent! + res.add($r) of nkTypeOfExpr: res.add($getTypeDesc(p.module, it.typ)) else: @@ -1516,13 +1471,12 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = result.add("\\n\"\n") else: res.add("\L") - result.add res.rope + result = res.rope proc genAsmStmt(p: BProc, t: PNode) = assert(t.kind == nkAsmStmt) genLineDir(p, t) - var s = newRopeAppender() - genAsmOrEmitStmt(p, t, isAsmStmt=true, s) + var s = genAsmOrEmitStmt(p, t, isAsmStmt=true) # see bug #2362, "top level asm statements" seem to be a mis-feature # but even if we don't do this, the example in #2362 cannot possibly # work: @@ -1530,20 +1484,18 @@ proc genAsmStmt(p: BProc, t: PNode) = # top level asm statement? p.module.s[cfsProcHeaders].add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]) else: - addIndent p, p.s(cpsStmts) - p.s(cpsStmts).add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]) + p.s(cpsStmts).add indentLine(p, runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])) proc determineSection(n: PNode): TCFileSection = result = cfsProcHeaders if n.len >= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}: let sec = n[0].strVal - if sec.startsWith("/*TYPESECTION*/"): result = cfsForwardTypes # TODO WORKAROUND + if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes elif sec.startsWith("/*VARSECTION*/"): result = cfsVars elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders proc genEmit(p: BProc, t: PNode) = - var s = newRopeAppender() - genAsmOrEmitStmt(p, t[1], false, s) + var s = genAsmOrEmitStmt(p, t[1]) if p.prc == nil: # top level emit pragma? let section = determineSection(t[1]) @@ -1554,14 +1506,14 @@ proc genEmit(p: BProc, t: PNode) = line(p, cpsStmts, s) proc genPragma(p: BProc, n: PNode) = - for i in 0.. error. - s.loc.r = res.rope + if m.hcrOn or isKeyword(s.name) or m.g.config.cppDefines.contains(res): + res.add "_0" + result = res.rope + s.loc.r = result writeMangledName(m.ndi, s, m.config) -proc fillLocalName(p: BProc; s: PSym) = +proc mangleLocalName(p: BProc; s: PSym): Rope = assert s.kind in skLocalVars+{skTemp} #assert sfGlobal notin s.flags - if s.loc.r == "": + result = s.loc.r + if result == nil: var key = s.name.s.mangle + shallow(key) let counter = p.sigConflicts.getOrDefault(key) - var result = key.rope + 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) @@ -92,6 +105,7 @@ 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 @@ -99,12 +113,13 @@ const tyDistinct, tyRange, tyStatic, tyAlias, tySink, tyInferred, tyOwned} -proc typeName(typ: PType; result: var Rope) = +proc typeName(typ: PType): Rope = let typ = typ.skipTypes(irrelevantForBackend) - result.add $typ.kind - if typ.sym != nil and typ.kind in {tyObject, tyEnum}: - result.add "_" - result.add typ.sym.name.s.mangle + result = + if typ.sym != nil and typ.kind in {tyObject, tyEnum}: + rope($typ.kind & '_' & typ.sym.name.s.mangle) + else: + rope($typ.kind) proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = var t = typ @@ -117,17 +132,14 @@ 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 == "": - typ.typeName(typ.loc.r) - typ.loc.r.add $sig + if typ.loc.r == nil: + typ.loc.r = typ.typeName & $sig else: when defined(debugSigHashes): # check consistency: - var tn = newRopeAppender() - typ.typeName(tn) - assert($typ.loc.r == $(tn & $sig)) + assert($typ.loc.r == $(typ.typeName & $sig)) result = typ.loc.r - if result == "": internalError(m.config, "getTypeName: " & $typ.kind) + if result == nil: internalError(m.config, "getTypeName: " & $typ.kind) proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind = case int(getSize(conf, typ)) @@ -179,13 +191,13 @@ proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind = of tySequence: result = ctNimSeq of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct of tyString: result = ctNimStr - of tyCstring: result = ctCString + of tyCString: result = ctCString of tyInt..tyUInt64: result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: if typ.n != nil: result = mapType(conf, lastSon typ, kind) - else: doAssert(false, "mapType: " & $typ.kind) - else: doAssert(false, "mapType: " & $typ.kind) + else: doAssert(false, "mapType") + else: doAssert(false, "mapType") proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind = #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr @@ -206,19 +218,12 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and (typ[0] == nil) or isPureObject(typ)) -proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool = +proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = # Arrays and sets cannot be returned by a C procedure, because C is # such a poor programming language. # We exclude records with refs too. This enhances efficiency and # is necessary for proper code generation of assignments. - var rettype = typ - var isAllowedCall = true - if isProc: - rettype = rettype[0] - isAllowedCall = typ.callConv in {ccClosure, ccInline, ccNimCall} - if rettype == nil or (isAllowedCall and - getSize(conf, rettype) > conf.target.floatSize*3): - result = true + if rettype == nil: result = true else: case mapType(conf, rettype, skResult) of ctArray: @@ -253,12 +258,38 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) = m.s[cfsTypeInfo].addf("NIM_STATIC_ASSERT(sizeof($1) == $2, $3);$n", [name, rope(size), msg2.rope]) # see `testCodegenABICheck` for example error message it generates +proc ccgIntroducedPtr(conf: ConfigRef; s: PSym, retType: PType): bool = + var pt = skipTypes(s.typ, typedescInst) + assert skResult != s.kind -proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) = - fillLoc(param.sym.loc, locParam, param, "Result", + if tfByRef in pt.flags: return true + elif tfByCopy in pt.flags: return false + case pt.kind + of tyObject: + if s.typ.sym != nil and sfForward in s.typ.sym.flags: + # forwarded objects are *always* passed by pointers for consistency! + result = true + elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3): + result = true # requested anyway + elif (tfFinal in pt.flags) and (pt[0] == nil): + result = false # no need, because no subtyping possible + else: + result = true # ordinary objects are always passed by reference, + # otherwise casting doesn't work + of tyTuple: + result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) + else: + result = false + # first parameter and return type is 'lent T'? --> use pass by pointer + if s.position == 0 and retType != nil and retType.kind == tyLent: + result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or + pt.kind == tySet and mapSetType(conf, pt) == ctArray) + +proc fillResult(conf: ConfigRef; param: PNode) = + fillLoc(param.sym.loc, locParam, param, ~"Result", OnStack) let t = param.sym.typ - if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, proctype): + if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, t): incl(param.sym.loc.flags, lfIndirect) param.sym.loc.storage = OnUnknown @@ -281,13 +312,13 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyString: case detectStrVersion(m) of 2: - cgsym(m, "NimStrPayload") - cgsym(m, "NimStringV2") + discard cgsym(m, "NimStrPayload") + discard cgsym(m, "NimStringV2") result = typeNameOrLiteral(m, typ, "NimStringV2") else: - cgsym(m, "NimStringDesc") + discard cgsym(m, "NimStringDesc") result = typeNameOrLiteral(m, typ, "NimStringDesc*") - of tyCstring: result = typeNameOrLiteral(m, typ, "NCSTRING") + of tyCString: result = typeNameOrLiteral(m, typ, "NCSTRING") of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL") of tyChar: result = typeNameOrLiteral(m, typ, "NIM_CHAR") of tyNil: result = typeNameOrLiteral(m, typ, "void*") @@ -299,11 +330,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 = "" + else: result = nil - if result != "" and typ.isImportedType(): - let sig = hashType(typ, m.config) - if cacheGetType(m.typeCache, sig) == "": + if result != nil and typ.isImportedType(): + let sig = hashType typ + if cacheGetType(m.typeCache, sig) == nil: m.typeCache[sig] = result proc pushType(m: BModule, typ: PType) = @@ -316,7 +347,7 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope = if typ == nil: result = rope("void") else: result = getSimpleTypeDesc(m, typ) - if result == "": result = cacheGetType(m.typeCache, sig) + if result == nil: result = cacheGetType(m.typeCache, sig) proc structOrUnion(t: PType): Rope = let cachedUnion = rope("union") @@ -337,9 +368,9 @@ proc seqStar(m: BModule): string = proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = result = cacheGetType(m.forwTypeCache, sig) - if result != "": return + if result != nil: return result = getTypePre(m, typ, sig) - if result != "": return + if result != nil: return let concrete = typ.skipTypes(abstractInst) case concrete.kind of tySequence, tyTuple, tyObject: @@ -362,16 +393,16 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R if isImportedCppType(etB) and t.kind == tyGenericInst: result = getTypeDescAux(m, t, check, kind) else: - result = getTypeForward(m, t, hashType(t, m.config)) + result = getTypeForward(m, t, hashType(t)) pushType(m, t) of tySequence: - let sig = hashType(t, m.config) + let sig = hashType(t) if optSeqDestructors in m.config.globalOptions: if skipTypes(etB[0], typedescInst).kind == tyEmpty: internalError(m.config, "cannot map the empty seq type to a C type") result = cacheGetType(m.forwTypeCache, sig) - if result == "": + if result == nil: result = getTypeName(m, t, sig) if not isImportedType(t): m.forwTypeCache[sig] = result @@ -379,7 +410,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) == "": + if cacheGetType(m.typeCache, sig) == nil: m.typeCache[sig] = result #echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s appcg(m, m.s[cfsTypes], @@ -398,9 +429,9 @@ proc getSeqPayloadType(m: BModule; t: PType): Rope = #result = getTypeForward(m, t, hashType(t)) & "_Content" proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) = - let sig = hashType(t, m.config) + let sig = hashType(t) let result = cacheGetType(m.typeCache, sig) - if result == "": + if result == nil: discard getTypeDescAux(m, t, check, skVar) else: # little hack for now to prevent multiple definitions of the same @@ -422,34 +453,33 @@ proc paramStorageLoc(param: PSym): TStorageLoc = proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, check: var IntSet, declareEnvironment=true; weakDep=false) = - params = "(" - if t[0] == nil or isInvalidReturnType(m.config, t): - rettype = "void" + params = nil + if t[0] == nil or isInvalidReturnType(m.config, t[0]): + rettype = ~"void" else: rettype = getTypeDescAux(m, t[0], check, skResult) for i in 1.. 1: result.add(" COMMA ") - addResultType(tt[i]) + addResultType(origTyp[i]) result.add("> ") # always call for sideeffects: assert t.kind != tyTuple @@ -869,13 +892,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 == "": + if result == nil: result = getTypeName(m, origTyp, sig) m.forwTypeCache[sig] = result if not isImportedType(t): @@ -891,9 +914,7 @@ 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 = rope("tySet_") - t.lastSon.typeName(result) - result.add $t.lastSon.hashType(m.config) + result = $t.kind & '_' & t.lastSon.typeName & $t.lastSon.hashType m.typeCache[sig] = result if not isImportedType(t): let s = int(getSize(m.config, t)) @@ -906,7 +927,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 = "" + result = nil # fixes bug #145: excl(check, t.id) @@ -957,12 +978,21 @@ 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; result: var Rope; asPtr: bool = false) = +proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope = + var + rettype, params: Rope # 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() - fillBackendName(m, prc) - fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown) - var rettype, params: Rope + fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown) 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 @@ -973,21 +1003,12 @@ proc genProcHeader(m: BModule, prc: PSym; result: var Rope; asPtr: bool = false) # 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.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params]) + result = runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params]) # ------------------ type info generation ------------------------------------- @@ -1023,10 +1044,10 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; var flags = 0 if not containsGarbageCollectedRef(typ): flags = flags or 1 if not canFormAcycle(typ): flags = flags or 2 - #else echo("can contain a cycle: " & typeToString(typ)) + #else MessageOut("can contain a cycle: " & typeToString(typ)) if flags != 0: m.s[cfsTypeInit3].addf("$1.flags = $2;$n", [nameHcr, rope(flags)]) - cgsym(m, "TNimType") + discard cgsym(m, "TNimType") if isDefined(m.config, "nimTypeNames"): var typename = typeToString(if origType.typeInst != nil: origType.typeInst else: origType, preferName) @@ -1034,16 +1055,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]) - cgsym(m, "nimTypeRoot") + discard cgsym(m, "nimTypeRoot") m.s[cfsTypeInit3].addf("$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n", [nameHcr]) if m.hcrOn: - m.s[cfsStrData].addf("static TNimType* $1;$n", [name]) + m.s[cfsData].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[cfsStrData].addf("N_LIB_PRIVATE TNimType $1;$n", [name]) + m.s[cfsData].addf("N_LIB_PRIVATE TNimType $1;$n", [name]) proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) = @@ -1066,12 +1087,12 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = objtype = objtype[0].skipTypes(abstractPtrs) if objtype.sym == nil: internalError(m.config, d.info, "anonymous obj with discriminator") - result = "NimDT_$1_$2" % [rope($hashType(objtype, m.config)), rope(d.name.s.mangle)] + result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)] proc rope(arg: Int128): Rope = rope($arg) proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope = - cgsym(m, "TNimNode") + discard cgsym(m, "TNimNode") var tmp = discriminatorTableName(m, objtype, d) result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)] @@ -1106,7 +1127,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 == "": fillObjectFields(m, typ) + if field.loc.r == nil: fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 3;$n" & @@ -1144,7 +1165,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 == "": fillObjectFields(m, typ) + if field.loc.r == nil: fillObjectFields(m, typ) if field.loc.t == nil: internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 1;$n" & @@ -1238,7 +1259,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) = @@ -1260,39 +1281,43 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", [result, s.loc.r]) -proc declareNimType(m: BModule, name: string; str: Rope, module: int) = +proc declareNimType(m: BModule, name: string; str: Rope, ownerModule: PSym) = let nr = rope(name) if m.hcrOn: - m.s[cfsStrData].addf("static $2* $1;$n", [str, nr]) + m.s[cfsData].addf("static $2* $1;$n", [str, nr]) m.s[cfsTypeInit1].addf("\t$1 = ($3*)hcrGetGlobal($2, \"$1\");$n", - [str, getModuleDllPath(m, module), nr]) + [str, getModuleDllPath(m, ownerModule), nr]) else: - m.s[cfsStrData].addf("extern $2 $1;$n", [str, nr]) + m.s[cfsData].addf("extern $2 $1;$n", [str, nr]) proc genTypeInfo2Name(m: BModule; t: PType): Rope = + var res = "|" var it = t - it = it.skipTypes(skipPtrs) - 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: - # produce short names for system types: - result = it.sym.name.s + while it != nil: + it = it.skipTypes(skipPtrs) + if it.sym != nil: + var m = it.sym.owner + while m != nil and m.kind != skModule: m = m.owner + if m == nil or sfSystemModule in m.flags: + # produce short names for system types: + res.add it.sym.name.s + else: + var p = m.owner + if p != nil and p.kind == skPackage: + res.add p.name.s & "." + res.add m.name.s & "." + res.add it.sym.name.s else: - var p = m.owner - if p != nil and p.kind == skPackage: - result.add p.name.s & "." - result.add m.name.s & "." - result.add it.sym.name.s - else: - result = $hashType(it, m.config) - result = makeCString(result) + res.add $hashType(it) + res.add "|" + it = it[0] + result = makeCString(res) -proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0 +proc isTrivialProc(s: PSym): bool {.inline.} = s.ast[bodyPos].len == 0 -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): +proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = + let theProc = t.attachedOps[op] + if theProc != nil and not isTrivialProc(theProc): # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling # convention at least: @@ -1301,11 +1326,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: theProc.name.s & " needs to have the 'nimcall' calling convention") genProc(m, theProc) - result.add theProc.loc.r - - when false: - if not canFormAcycle(t) and op == attachedTrace: - echo "ayclic but has this =trace ", t, " ", theProc.ast + result = theProc.loc.r else: when false: if op == attachedTrace and m.config.selectedGC == gcOrc and @@ -1313,136 +1334,45 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: # 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.add rope("NIM_NIL") + result = rope("NIM_NIL") -proc getObjDepth(t: PType): int16 = - var x = t - result = -1 - while x != nil: - x = skipTypes(x, skipPtrs) - x = x[0] - inc(result) +proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) = + var typeName: Rope + if t.kind == tyObject: + if incompleteType(t): + localError(m.config, info, "request for RTTI generation for incomplete object: " & + typeToString(t)) + typeName = genTypeInfo2Name(m, t) + else: + typeName = rope("NIM_NIL") -proc genDisplayElem(d: MD5Digest): uint32 = - result = 0 - for i in 0..3: - result += uint32(d[i]) - result = result shl 8 + 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) + let disposeImpl = genHook(m, t, info, attachedDispose) -proc genDisplay(m: BModule, t: PType, depth: int): Rope = - result = Rope"{" - var x = t - var seqs = newSeq[string](depth+1) - var i = 0 - while x != nil: - x = skipTypes(x, skipPtrs) - seqs[i] = $genDisplayElem(MD5Digest(hashType(x, m.config))) - x = x[0] - inc i - - for i in countdown(depth, 1): - result.add seqs[i] & ", " - result.add seqs[0] - result.add "}" - -proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) = - 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 - - 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) - - let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1 - - if t.kind in {tyObject, tyDistinct} and incompleteType(t): - localError(m.config, info, "request for RTTI generation for incomplete object: " & - typeToString(t)) - - if isDefined(m.config, "nimTypeNames"): - var typeName: Rope - if t.kind in {tyObject, tyDistinct}: - typeName = genTypeInfo2Name(m, t) - else: - typeName = rope("NIM_NIL") - addf(typeEntry, "; $1.name = $2", [name, typeName]) - addf(typeEntry, "; $1.size = sizeof($2); $1.align = (NI16) NIM_ALIGNOF($2); $1.depth = $3; $1.flags = $4;", - [name, getTypeDesc(m, t), rope(objDepth), rope(flags)]) - - if objDepth >= 0: - let objDisplay = genDisplay(m, t, objDepth) - let objDisplayStore = getTempName(m) - m.s[cfsVars].addf("static $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), skVar), objDisplayStore, rope(objDepth+1), objDisplay]) - addf(typeEntry, "$1.display = $2;$n", [name, rope(objDisplayStore)]) - - 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) - -proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) = - 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 - - var typeEntry = newRopeAppender() - addf(typeEntry, "N_LIB_PRIVATE TNimTypeV2 $1 = {", [name]) - add(typeEntry, ".destructor = (void*)") - genHook(m, t, info, attachedDestructor, typeEntry) - - let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1 - - if t.kind in {tyObject, tyDistinct} and incompleteType(t): - localError(m.config, info, "request for RTTI generation for incomplete object: " & - typeToString(t)) - - addf(typeEntry, ", .size = sizeof($1), .align = (NI16) NIM_ALIGNOF($1), .depth = $2", - [getTypeDesc(m, t), rope(objDepth)]) - - if objDepth >= 0: - let objDisplay = genDisplay(m, t, objDepth) - let objDisplayStore = getTempName(m) - m.s[cfsVars].addf("static NIM_CONST $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), skVar), objDisplayStore, rope(objDepth+1), objDisplay]) - addf(typeEntry, ", .display = $1", [rope(objDisplayStore)]) - if isDefined(m.config, "nimTypeNames"): - var typeName: Rope - if t.kind in {tyObject, tyDistinct}: - typeName = genTypeInfo2Name(m, t) - else: - typeName = rope("NIM_NIL") - addf(typeEntry, ", .name = $1", [typeName]) - add(typeEntry, ", .traceImpl = (void*)") - genHook(m, t, info, attachedTrace, typeEntry) - - addf(typeEntry, ", .flags = $1};$n", [rope(flags)]) - m.s[cfsVars].add typeEntry + 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.disposeImpl = (void*)$6;", [ + name, destroyImpl, getTypeDesc(m, t), typeName, + traceImpl, disposeImpl]) if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions: discard genTypeInfoV1(m, t, info) proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = let origType = t - # distinct types can have their own destructors - var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses - {tyDistinct}) + var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) let prefixTI = if m.hcrOn: "(" else: "(&" - let sig = hashType(origType, m.config) + let sig = hashType(origType) result = m.typeInfoMarkerV2.getOrDefault(sig) - if result != "": + if result != nil: return prefixTI.rope & result & ")".rope let marker = m.g.typeInfoMarkerV2.getOrDefault(sig) - if marker.str != "": - cgsym(m, "TNimTypeV2") + if marker.str != nil: + discard cgsym(m, "TNimTypeV2") declareNimType(m, "TNimTypeV2", marker.str, marker.owner) # also store in local type section: m.typeInfoMarkerV2[sig] = marker.str @@ -1451,20 +1381,18 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = result = "NTIv2$1_" % [rope($sig)] m.typeInfoMarkerV2[sig] = result - let owner = t.skipTypes(typedescPtrs).itemId.module - if owner != m.module.position and moduleOpenForCodegen(m.g.graph, FileIndex owner): + let owner = t.skipTypes(typedescPtrs).owner.getModule + if owner != m.module: # make sure the type info is created in the owner module - discard genTypeInfoV2(m.g.modules[owner], origType, info) + assert m.g.modules[owner.position] != nil + discard genTypeInfoV2(m.g.modules[owner.position], origType, info) # reference the type info as extern here - cgsym(m, "TNimTypeV2") + discard cgsym(m, "TNimTypeV2") declareNimType(m, "TNimTypeV2", result, owner) return prefixTI.rope & result & ")".rope m.g.typeInfoMarkerV2[sig] = (str: result, owner: owner) - if m.compileToCpp: - genTypeInfoV2OldImpl(m, t, origType, result, info) - else: - genTypeInfoV2Impl(m, t, origType, result, info) + genTypeInfoV2Impl(m, t, origType, result, info) result = prefixTI.rope & result & ")".rope proc openArrayToTuple(m: BModule; t: PType): PType = @@ -1476,81 +1404,45 @@ proc openArrayToTuple(m: BModule; t: PType): PType = result.add p result.add getSysType(m.g.graph, t.owner.info, tyInt) -proc typeToC(t: PType): string = - ## Just for more readable names, the result doesn't have - ## to be unique. - let s = typeToString(t) - result = newStringOfCap(s.len) - for i in 0.. conf.target.floatSize * 3): - result = true # requested anyway - elif (tfFinal in pt.flags) and (pt[0] == nil): - result = false # no need, because no subtyping possible - else: - result = true # ordinary objects are always passed by reference, - # otherwise casting doesn't work - of tyTuple: - result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) - else: - result = false - # first parameter and return type is 'lent T'? --> use pass by pointer - if s.position == 0 and retType != nil and retType.kind == tyLent: - result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or - pt.kind == tySet and mapSetType(conf, pt) == ctArray) - diff --git a/compiler/cgen.nim b/compiler/cgen.nim index d2095016c..220b8e3c1 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -12,22 +12,19 @@ import ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets, nversion, nimsets, msgs, bitsets, idents, types, - ccgutils, os, ropes, math, wordrecg, treetab, cgmeth, - rodutils, renderer, cgendata, aliases, - lowerings, tables, sets, ndi, lineinfos, pathutils, transf, - injectdestructors, astmsgs, modulepaths, backendpragmas - -import pipelineutils - -when defined(nimPreviewSlimSystem): - import std/assertions + ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, + rodutils, renderer, cgendata, ccgmerge, aliases, + lowerings, tables, sets, ndi, lineinfos, pathutils, transf, enumtostr, + injectdestructors when not defined(leanCompiler): import spawn, semparallel -import strutils except `%`, addf # collides with ropes.`%` +import strutils except `%` # collides with ropes.`%` -from ic / ic import ModuleBackendFlag +from modulegraphs import ModuleGraph, PPassContext +from lineinfos import + warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated, errCannotOpenFile import dynlib when not declared(dynlib.libCandidates): @@ -53,35 +50,23 @@ proc addForwardedProc(m: BModule, prc: PSym) = m.g.forwardedProcs.add(prc) proc findPendingModule(m: BModule, s: PSym): BModule = - # TODO fixme - if m.config.symbolFiles == v2Sf: - let ms = s.itemId.module #getModule(s) - result = m.g.modules[ms] - else: - var ms = getModule(s) - result = m.g.modules[ms.position] + var ms = getModule(s) + result = m.g.modules[ms.position] proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) = result.k = k result.storage = s result.lode = lode - result.r = "" + result.r = nil result.flags = {} -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.} = +proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) = # 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: @@ -105,24 +90,24 @@ proc useHeader(m: BModule, sym: PSym) = let str = getStr(sym.annex.path) m.includeHeader(str) -proc cgsym(m: BModule, name: string) -proc cgsymValue(m: BModule, name: string): Rope +proc cgsym(m: BModule, name: string): Rope proc getCFile(m: BModule): AbsoluteFile proc getModuleDllPath(m: BModule): Rope = let (dir, name, ext) = splitFile(getCFile(m)) let filename = strutils.`%`(platform.OS[m.g.config.target.targetOS].dllFrmt, [name & ext]) - result = makeCString(dir.string & "/" & filename) - -proc getModuleDllPath(m: BModule, module: int): Rope = - result = getModuleDllPath(m.g.modules[module]) + return makeCString(dir.string & "/" & filename) proc getModuleDllPath(m: BModule, s: PSym): Rope = - result = getModuleDllPath(m.g.modules[s.itemId.module]) + return getModuleDllPath(findPendingModule(m, s)) 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 @@ -168,11 +153,6 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope = inc(i) result.add newCall(formatValue, resVar, args[num]) inc(num) - of '^': - flushStrLit() - inc(i) - result.add newCall(formatValue, resVar, args[^1]) - inc(num) of '0'..'9': var j = 0 while true: @@ -203,7 +183,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"cgsymValue", m, ident)) + result.add newCall(formatValue, resVar, newCall(ident"cgsym", m, ident)) elif frmt[i] == '#' and frmt[i+1] == '$': inc(i, 2) var j = 0 @@ -212,11 +192,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"cgsymValue", m, ident)) - elif frmt[i] == '#' and frmt[i+1] == '#': - inc(i, 2) - strLit.add("#") - + result.add newCall(formatValue, resVar, newCall(ident"cgsym", m, ident)) var start = i while i < frmt.len: if frmt[i] != '$' and frmt[i] != '#': inc(i) @@ -227,9 +203,10 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope = flushStrLit() result.add newCall(ident"rope", resVar) -proc addIndent(p: BProc; result: var Rope) = +proc indentLine(p: BProc, r: Rope): Rope = + result = r for i in 0..data" @@ -351,24 +315,16 @@ template mapTypeChooser(n: PNode): TSymKind = template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode) -proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) = - if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray: - result.add "(&" & a.r & ")" - else: - result.add a.r - proc addrLoc(conf: ConfigRef; a: TLoc): Rope = + result = a.r if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray: - result = "(&" & a.r & ")" - else: - result = a.r + result = "(&" & result & ")" proc byRefLoc(p: BProc; a: TLoc): Rope = + result = a.r if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a)) != ctArray and not p.module.compileToCpp: - result = "(&" & a.r & ")" - else: - result = a.r + result = "(&" & result & ")" proc rdCharLoc(a: TLoc): Rope = # read a location that may need a char-cast: @@ -449,13 +405,8 @@ proc resetLoc(p: BProc, loc: var TLoc) = let typ = skipTypes(loc.t, abstractVarRange) if isImportedCppType(typ): return if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}: - assert loc.r != "" - - let atyp = skipTypes(loc.t, abstractInst) - if atyp.kind in {tyVar, tyLent}: - linefmt(p, cpsStmts, "$1->len = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)]) - else: - linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) + assert rdLoc(loc) != nil + linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) elif not isComplexValueType(typ): if containsGcRef: var nilLoc: TLoc @@ -483,27 +434,18 @@ proc resetLoc(p: BProc, loc: var TLoc) = # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, constructObj) -proc isOrHasImportedCppType(typ: PType): bool = - searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType) - proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = let typ = loc.t if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}: linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) elif not isComplexValueType(typ): - if containsGarbageCollectedRef(loc.t): - var nilLoc: TLoc - initLoc(nilLoc, locTemp, loc.lode, OnStack) - nilLoc.r = rope("NIM_NIL") - genRefAssign(p, loc, nilLoc) - else: - linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc), - getTypeDesc(p.module, typ, mapTypeChooser(loc))]) + linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc), + getTypeDesc(p.module, typ, mapTypeChooser(loc))]) else: if not isTemp or containsGarbageCollectedRef(loc.t): # don't use nimZeroMem for temporary values for performance if we can # avoid it: - if not isOrHasImportedCppType(typ): + if not isImportedCppType(typ): linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", [addrLoc(p.config, loc), getTypeDesc(p.module, typ, mapTypeChooser(loc))]) genObjectInit(p, cpsStmts, loc.t, loc, constructObj) @@ -523,10 +465,7 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = inc(p.labels) result.r = "T" & rope(p.labels) & "_" - if p.module.compileToCpp and isOrHasImportedCppType(t): - linefmt(p, cpsLocals, "$1 $2{};$n", [getTypeDesc(p.module, t, skVar), result.r]) - else: - linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, skVar), result.r]) + linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, skVar), result.r]) result.k = locTemp result.lode = lodeTyp t result.storage = OnStack @@ -562,14 +501,10 @@ proc getIntTemp(p: BProc, result: var TLoc) = proc localVarDecl(p: BProc; n: PNode): Rope = let s = n.sym if s.loc.k == locNone: - fillLocalName(p, s) - fillLoc(s.loc, locLocalVar, n, OnStack) + fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: result.addf("NIM_ALIGN($1) ", [rope(s.alignment)]) - - genCLineDir(result, n.info, p.config) - result.add getTypeDesc(p.module, s.typ, skVar) if s.constraint.isNil: if sfRegister in s.flags: result.add(" register") @@ -587,7 +522,7 @@ proc assignLocalVar(p: BProc, n: PNode) = # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! let nl = if optLineDir in p.config.options: "" else: "\L" - let decl = localVarDecl(p, n) & (if p.module.compileToCpp and isOrHasImportedCppType(n.typ): "{};" else: ";") & nl + let decl = localVarDecl(p, n) & ";" & nl line(p, cpsLocals, decl) include ccgthreadvars @@ -603,8 +538,7 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool = proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = let s = n.sym if s.loc.k == locNone: - fillBackendName(p.module, s) - fillLoc(s.loc, locGlobalVar, n, OnHeap) + fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap) if treatGlobalDifferentlyForHCR(p.module, s): incl(s.loc.flags, lfIndirect) if lfDynamicLib in s.loc.flags: @@ -613,7 +547,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = varInDynamicLib(q, s) else: s.loc.r = mangleDynLibProc(s) - if value != "": + if value != nil: internalError(p.config, n.info, ".dynlib variables cannot have a value") return useHeader(p.module, s) @@ -621,10 +555,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 != "": + if value != nil: internalError(p.config, n.info, ".threadvar variables cannot have a value") else: - var decl: Rope = "" + var decl: Rope = nil 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: @@ -633,35 +567,34 @@ 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 != "": decl.add("NIM_CONST ") + if s.kind == skLet and value != nil: 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 != "": + if value != nil: decl.addf(" $1 = $2;$n", [s.loc.r, value]) else: decl.addf(" $1;$n", [s.loc.r]) else: - if value != "": + if value != nil: 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 == "": + if p.withinLoop > 0 and value == nil: # fixes tests/run/tzeroarray: resetLoc(p, s.loc) proc assignParam(p: BProc, s: PSym, retType: PType) = - assert(s.loc.r != "") + assert(s.loc.r != nil) scopeMangledParam(p, s) proc fillProcLoc(m: BModule; n: PNode) = let sym = n.sym if sym.loc.k == locNone: - fillBackendName(m, sym) - fillLoc(sym.loc, locProc, n, OnStack) + fillLoc(sym.loc, locProc, n, mangleName(m, sym), OnStack) proc getLabel(p: BProc): TLabel = inc(p.labels) @@ -676,9 +609,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; 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 intLiteral(i: BiggestInt): Rope +proc genLiteral(p: BProc, n: PNode): Rope +proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope proc raiseExit(p: BProc) proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = @@ -702,25 +635,25 @@ include ccgcalls, "ccgstmts.nim" proc initFrame(p: BProc, procname, filename: Rope): Rope = const frameDefines = """ -$1define nimfr_(proc, file) \ - TFrame FR_; \ - FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_); + $1 define nimfr_(proc, file) \ + TFrame FR_; \ + FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_); - $1define nimfrs_(proc, file, slots, length) \ - struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename;NI len;VarSlot s[slots];} FR_; \ - FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_); + $1 define nimfrs_(proc, file, slots, length) \ + struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR_; \ + FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_); - $1define nimln_(n, file) \ - FR_.line = n; FR_.filename = file; -""" + $1 define nimln_(n, file) \ + FR_.line = n; FR_.filename = file; + """ if p.module.s[cfsFrameDefines].len == 0: appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"]) - cgsym(p.module, "nimFrame") + discard 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 = - cgsym(p.module, "nimFrame") + discard 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", @@ -747,28 +680,24 @@ proc loadDynamicLib(m: BModule, lib: PLib) = if not lib.generated: lib.generated = true var tmp = getTempName(m) - assert(lib.name == "") + assert(lib.name == nil) 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 = "" + var loadlib: Rope = nil 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(", [tmp]) - genStringLiteral(m, n, loadlib) - loadlib.addf "))$n", [] + appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n", + [tmp, genStringLiteral(m, n)]) appcg(m, m.s[cfsDynLibInit], - "if (!($1)) #nimLoadLibraryError(", - [loadlib]) - genStringLiteral(m, lib.path, m.s[cfsDynLibInit]) - m.s[cfsDynLibInit].addf ");$n", [] - + "if (!($1)) #nimLoadLibraryError($2);$n", + [loadlib, genStringLiteral(m, lib.path)]) else: var p = newProc(nil, m) p.options.excl optStackTrace @@ -787,7 +716,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n", [tmp, rdLoc(dest)]) - if lib.name == "": internalError(m.config, "loadDynamicLib") + if lib.name == nil: internalError(m.config, "loadDynamicLib") proc mangleDynLibProc(sym: PSym): Rope = # we have to build this as a single rope in order not to trip the @@ -852,25 +781,18 @@ proc symInDynamicLibPartial(m: BModule, sym: PSym) = sym.loc.r = mangleDynLibProc(sym) sym.typ.sym = nil # generate a new name -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) = +proc cgsym(m: BModule, name: string): Rope = 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: - cgsymImpl m, sym + 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) 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}: @@ -901,12 +823,12 @@ proc generateHeaders(m: BModule) = #undef unix """) -proc openNamespaceNim(namespace: string; result: var Rope) = +proc openNamespaceNim(namespace: string): Rope = result.add("namespace ") result.add(namespace) result.add(" {\L") -proc closeNamespaceNim(result: var Rope) = +proc closeNamespaceNim(): Rope = result.add("}\L") proc closureSetup(p: BProc, prc: PSym) = @@ -927,19 +849,13 @@ proc closureSetup(p: BProc, prc: PSym) = [rdLoc(env.loc), getTypeDesc(p.module, env.typ)]) proc containsResult(n: PNode): bool = - result = false - case n.kind - of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkFormalParams: - discard - of nkSym: - if n.sym.kind == skResult: - result = true + if n.kind == nkSym and n.sym.kind == skResult: + result = true else: - for i in 0.. this means that the constant is reloadable & updatable! + q.initProc.procSec(cpsLocals).add(ropecg(q, + "\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n", + [sym.loc.r, actualConstName, rdLoc(sym.loc)])) + # declare header: + if q != m and not containsOrIncl(m.declaredThings, sym.id): + assert(sym.loc.r != nil) + 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( + "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, + getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)]) + else: + let headerDecl = "extern NIM_CONST $1 $2;$n" % + [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r] + m.s[cfsData].add(headerDecl) + if sfExportc in sym.flags and p.module.g.generatedHeader != nil: + p.module.g.generatedHeader.s[cfsData].add(headerDecl) proc isActivated(prc: PSym): bool = prc.typ != nil @@ -1308,15 +1252,14 @@ proc genVarPrototype(m: BModule, n: PNode) = #assert(sfGlobal in sym.flags) let sym = n.sym useHeader(m, sym) - fillBackendName(m, sym) - fillLoc(sym.loc, locGlobalVar, n, OnHeap) + fillLoc(sym.loc, locGlobalVar, n, mangleName(m, sym), OnHeap) if treatGlobalDifferentlyForHCR(m, sym): incl(sym.loc.flags, lfIndirect) if (lfNoDecl in sym.loc.flags) or contains(m.declaredThings, sym.id): return if sym.owner.id != m.module.id: # else we already have the symbol generated! - assert(sym.loc.r != "") + assert(sym.loc.r != nil) if sfThread in sym.flags: declareThreadVar(m, sym, true) else: @@ -1363,25 +1306,23 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = if conf.hcrOn: result.add("#define NIM_HOT_CODE_RELOADING\L") addNimDefines(result, conf) -proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope = - ## Returns a mangled module name. - result.add mangleModuleName(conf, filename).mangle - -proc getSomeNameForModule(m: BModule): Rope = - ## Returns a mangled module name. - assert m.module.kind == skModule - assert m.module.owner.kind == skPackage - result.add mangleModuleName(m.g.config, m.filename).mangle +proc getSomeNameForModule(m: PSym): Rope = + assert m.kind == skModule + assert m.owner.kind == skPackage + if {sfSystemModule, sfMainModule} * m.flags == {}: + result = m.owner.name.s.mangle.rope + result.add "_" + result.add m.name.s.mangle proc getSomeInitName(m: BModule, suffix: string): Rope = if not m.hcrOn: - result = getSomeNameForModule(m) + result = getSomeNameForModule(m.module) result.add suffix proc getInitName(m: BModule): Rope = if sfMainModule in m.module.flags: # generate constant name for main module, for "easy" debugging. - result = rope(m.config.nimMainPrefix) & rope"NimMainModule" + result = rope"NimMainModule" else: result = getSomeInitName(m, "Init000") @@ -1402,11 +1343,9 @@ proc genMainProc(m: BModule) = assert prc != nil let n = newStrNode(nkStrLit, prc.annex.path.strVal) n.info = prc.annex.path.info - var strLit = newRopeAppender() - genStringLiteral(m, n, strLit) appcg(m, result, "\tif (!($1 = #nimLoadLibrary($2)))$N" & "\t\t#nimLoadLibraryError($2);$N", - [handle, strLit]) + [handle, genStringLiteral(m, n)]) preMainCode.add(loadLib("hcr_handle", "hcrGetProc")) preMainCode.add("\tvoid* rtl_handle;\L") @@ -1416,59 +1355,47 @@ proc genMainProc(m: BModule) = preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L") preMainCode.add("\t(*inner)();\L") else: - preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix]) - - var posixCmdLine: Rope - if optNoMain notin m.config.globalOptions: - posixCmdLine.add "\tN_LIB_PRIVATE int cmdCount;\L" - posixCmdLine.add "\tN_LIB_PRIVATE char** cmdLine;\L" - posixCmdLine.add "\tN_LIB_PRIVATE char** gEnv;\L" + preMainCode.add("\tPreMain();\L") const + # not a big deal if we always compile these 3 global vars... makes the HCR code easier + PosixCmdLine = + "N_LIB_PRIVATE int cmdCount;$N" & + "N_LIB_PRIVATE char** cmdLine;$N" & + "N_LIB_PRIVATE char** gEnv;$N" + # The use of a volatile function pointer to call Pre/NimMainInner # prevents inlining of the NimMainInner function and dependent # functions, which might otherwise merge their stack frames. - PreMainBody = "$N" & - "N_LIB_PRIVATE void $3PreMainInner(void) {$N" & + "N_LIB_PRIVATE void PreMainInner(void) {$N" & "$2" & "}$N$N" & - "$4" & - "N_LIB_PRIVATE void $3PreMain(void) {$N" & - "\t##if $5$N" & # 1 for volatile call, 0 for non-volatile + PosixCmdLine & + "N_LIB_PRIVATE void PreMain(void) {$N" & "\tvoid (*volatile inner)(void);$N" & - "\tinner = $3PreMainInner;$N" & + "\tinner = PreMainInner;$N" & "$1" & "\t(*inner)();$N" & - "\t##else$N" & - "$1" & - "\t$3PreMainInner();$N" & - "\t##endif$N" & "}$N$N" MainProcs = - "\t$^NimMain();$N" + "\tNimMain();$N" MainProcsWithResult = MainProcs & ("\treturn $1nim_program_result;$N") - NimMainInner = "N_LIB_PRIVATE N_CDECL(void, $5NimMainInner)(void) {$N" & + NimMainInner = "N_LIB_PRIVATE N_CDECL(void, NimMainInner)(void) {$N" & "$1" & "}$N$N" NimMainProc = - "N_CDECL(void, $5NimMain)(void) {$N" & - "\t##if $6$N" & # 1 for volatile call, 0 for non-volatile - "\tvoid (*volatile inner)(void);$N" & - "$4" & - "\tinner = $5NimMainInner;$N" & - "$2" & - "\t(*inner)();$N" & - "\t##else$N" & - "$4" & - "$2" & - "\t$5NimMainInner();$N" & - "\t##endif$N" & + "N_CDECL(void, NimMain)(void) {$N" & + "\tvoid (*volatile inner)(void);$N" & + "$4" & + "\tinner = NimMainInner;$N" & + "$2" & + "\t(*inner)();$N" & "}$N$N" NimMainBody = NimMainInner & NimMainProc @@ -1511,7 +1438,7 @@ proc genMainProc(m: BModule) = GenodeNimMain = "extern Genode::Env *nim_runtime_env;$N" & - "extern \"C\" void nim_component_construct(Genode::Env*);$N$N" & + "extern void nim_component_construct(Genode::Env*);$N$N" & NimMainBody ComponentConstruct = @@ -1533,91 +1460,67 @@ proc genMainProc(m: BModule) = m.includeHeader("") let initStackBottomCall = - if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope + if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []) inc(m.labels) - - let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcOrc}: "1" else: "0" - appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine, isVolatile]) + appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit]) if m.config.target.targetOS == osWindows and m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: if optGenGuiApp in m.config.globalOptions: const nimMain = WinNimMain appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) else: const nimMain = WinNimDllMain appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) elif m.config.target.targetOS == osGenode: const nimMain = GenodeNimMain appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) elif optGenDynLib in m.config.globalOptions: const nimMain = PosixNimDllMain appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) + elif m.config.target.targetOS == osStandalone: + const nimMain = NimMainBody + appcg(m, m.s[cfsProcs], nimMain, + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) else: const nimMain = NimMainBody appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) + if optNoMain notin m.config.globalOptions: if m.config.cppCustomNamespace.len > 0: - closeNamespaceNim(m.s[cfsProcs]) - m.s[cfsProcs].add "using namespace " & m.config.cppCustomNamespace & ";\L" + m.s[cfsProcs].add closeNamespaceNim() & "using namespace " & m.config.cppCustomNamespace & ";\L" if m.config.target.targetOS == osWindows and m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: if optGenGuiApp in m.config.globalOptions: const otherMain = WinCMain - appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix]) + appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: ""]) else: const otherMain = WinCDllMain - appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + appcg(m, m.s[cfsProcs], otherMain, []) elif m.config.target.targetOS == osGenode: const otherMain = ComponentConstruct - appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + appcg(m, m.s[cfsProcs], otherMain, []) elif optGenDynLib in m.config.globalOptions: const otherMain = PosixCDllMain - appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + appcg(m, m.s[cfsProcs], otherMain, []) elif m.config.target.targetOS == osStandalone: const otherMain = StandaloneCMain - appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + appcg(m, m.s[cfsProcs], otherMain, []) else: const otherMain = PosixCMain - appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix]) + appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: ""]) + if m.config.cppCustomNamespace.len > 0: - 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(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(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: - g.mainModInit.add(initCall) - elif sfSystemModule in m.flags: - g.mainDatInit.add(initCall) # systemInit must called right after systemDatInit if any - else: - g.otherModsInit.add(initCall) - -proc whichInitProcs*(m: BModule): set[ModuleBackendFlag] = - # called from IC. - result = {} - if m.hcrOn or m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0: - result.incl HasModuleInitProc - for i in cfsTypeInit1..cfsDynLibInit: - if m.s[i].len != 0: - result.incl HasDatInitProc - break + m.s[cfsProcs].add openNamespaceNim(m.config.cppCustomNamespace) proc registerModuleToMain(g: BModuleList; m: BModule) = let @@ -1636,7 +1539,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) = hcrModuleMeta.addf("\t\"\"};$n", []) hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(void**, HcrGetImportedModules)() { return (void**)hcr_module_list; }$n", []) hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(char*, HcrGetSigHash)() { return \"$1\"; }$n$n", - [($sigHash(m.module, m.config)).rope]) + [($sigHash(m.module)).rope]) if sfMainModule in m.module.flags: g.mainModProcs.add(hcrModuleMeta) g.mainModProcs.addf("static void* hcr_handle;$N", []) @@ -1707,13 +1610,14 @@ proc genDatInitCode(m: BModule) = for i in cfsTypeInit1..cfsDynLibInit: if m.s[i].len != 0: moduleDatInitRequired = true + prc.add(genSectionStart(i, m.config)) prc.add(m.s[i]) + prc.add(genSectionEnd(i, m.config)) prc.addf("}$N$N", []) if moduleDatInitRequired: m.s[cfsDatInitProc].add(prc) - #rememberFlag(m.g.graph, m.module, HasDatInitProc) # Very similar to the contents of symInDynamicLib - basically only the # things needed for the hot code reloading runtime procs to be loaded @@ -1766,19 +1670,21 @@ proc genInitCode(m: BModule) = if m.thing.s(section).len > 0: moduleInitRequired = true if addHcrGuards: prc.add("\tif (nim_hcr_do_init_) {\n\n") + prc.add(genSectionStart(section, m.config)) prc.add(m.thing.s(section)) + prc.add(genSectionEnd(section, m.config)) if addHcrGuards: prc.add("\n\t} // nim_hcr_do_init_\n") if m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0: # 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) writeSection(preInitProc, cpsStmts) - prc.addf("}/* preInitProc end */$N", []) + prc.addf("}$N", []) when false: m.initProc.blocks[0].sections[cpsLocals].add m.preInitProc.s(cpsLocals) m.initProc.blocks[0].sections[cpsInit].prepend m.preInitProc.s(cpsInit) @@ -1799,13 +1705,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: @@ -1813,9 +1719,6 @@ proc genInitCode(m: BModule) = if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: prc.add(deinitFrame(m.initProc)) - elif sfMainModule in m.module.flags and m.config.exc == excGoto: - if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil: - m.appcg(prc, "\t#nimTestErrorFlag();$n", []) prc.addf("}$N", []) @@ -1839,7 +1742,7 @@ proc genInitCode(m: BModule) = m.s[cfsInitProc].addf("}$N$N", []) for i, el in pairs(m.extensionLoaders): - if el != "": + if el != nil: let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" % [(i.ord - '0'.ord).rope, el] moduleInitRequired = true @@ -1847,7 +1750,6 @@ proc genInitCode(m: BModule) = if moduleInitRequired or sfMainModule in m.module.flags: m.s[cfsInitProc].add(prc) - #rememberFlag(m.g.graph, m.module, HasModuleInitProc) genDatInitCode(m) @@ -1862,21 +1764,28 @@ proc genModule(m: BModule, cfile: Cfile): Rope = var moduleIsEmpty = true result = getFileHeader(m.config, cfile) + result.add(genMergeInfo(m)) generateThreadLocalStorage(m) generateHeaders(m) + result.add(genSectionStart(cfsHeaders, m.config)) result.add(m.s[cfsHeaders]) if m.config.cppCustomNamespace.len > 0: - openNamespaceNim(m.config.cppCustomNamespace, result) + result.add openNamespaceNim(m.config.cppCustomNamespace) + result.add(genSectionEnd(cfsHeaders, m.config)) + result.add(genSectionStart(cfsFrameDefines, m.config)) if m.s[cfsFrameDefines].len > 0: result.add(m.s[cfsFrameDefines]) else: result.add("#define nimfr_(x, y)\n#define nimln_(x, y)\n") + result.add(genSectionEnd(cfsFrameDefines, m.config)) for i in cfsForwardTypes..cfsProcs: if m.s[i].len > 0: moduleIsEmpty = false + result.add(genSectionStart(i, m.config)) result.add(m.s[i]) + result.add(genSectionEnd(i, m.config)) if m.s[cfsInitProc].len > 0: moduleIsEmpty = false @@ -1886,10 +1795,10 @@ proc genModule(m: BModule, cfile: Cfile): Rope = result.add(m.s[cfsDatInitProc]) if m.config.cppCustomNamespace.len > 0: - closeNamespaceNim(result) + result.add closeNamespaceNim() if moduleIsEmpty: - result = "" + result = nil proc initProcOptions(m: BModule): TOptions = let opts = m.config.options @@ -1910,7 +1819,6 @@ 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 @@ -1931,7 +1839,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex)) -proc newModule*(g: BModuleList; module: PSym; conf: ConfigRef): BModule = +proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = # we should create only one cgen module for each module sym result = rawNewModule(g, module, conf) if module.position >= g.modules.len: @@ -1944,7 +1852,10 @@ template injectG() {.dirty.} = graph.backend = newModuleList(graph) let g = BModuleList(graph.backend) -proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = +when not defined(nimHasSinkInference): + {.pragma: nosinks.} + +proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = injectG() result = newModule(g, module, graph.config) result.idgen = idgen @@ -1964,15 +1875,16 @@ proc writeHeader(m: BModule) = generateThreadLocalStorage(m) for i in cfsHeaders..cfsProcs: + result.add(genSectionStart(i, m.config)) result.add(m.s[i]) - if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: - openNamespaceNim(m.config.cppCustomNamespace, result) + result.add(genSectionEnd(i, m.config)) + if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: result.add openNamespaceNim(m.config.cppCustomNamespace) 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: closeNamespaceNim(result) + result.addf("N_CDECL(void, NimMain)(void);$n", []) + if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim() result.addf("#endif /* $1 */$n", [guard]) if not writeRope(result, m.filename): rawMessage(m.config, errCannotOpenFile, m.filename.string) @@ -1982,7 +1894,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, mangleModuleName(m.config, m.cfilename).AbsoluteFile), ext) + result = changeFileExt(completeCfilePath(m.config, withPackageName(m.config, m.cfilename)), ext) when false: proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = @@ -2010,9 +1922,13 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) = genStmts(p, n) -proc genTopLevelStmt*(m: BModule; n: PNode) = - ## Also called from `ic/cbackend.nim`. - if pipelineutils.skipCodegen(m.config, n): return +proc myProcess(b: PPassContext, n: PNode): PNode = + result = n + if b == nil: return + var m = BModule(b) + if passes.skipCodegen(m.config, n) or + not moduleHasChanged(m.g.graph, m.module): + return m.initProc.options = initProcOptions(m) #softRnl = if optLineDir in m.config.options: noRnl else: rnl # XXX replicate this logic! @@ -2058,26 +1974,46 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = proc writeModule(m: BModule, pending: bool) = template onExit() = close(m.ndi, m.config) let cfile = getCFile(m) - if moduleHasChanged(m.g.graph, m.module): + if true or optForceFullMake in m.config.globalOptions: + if moduleHasChanged(m.g.graph, m.module): + genInitCode(m) + finishTypeDescriptions(m) + if sfMainModule in m.module.flags: + # generate main file: + genMainProc(m) + m.s[cfsProcHeaders].add(m.g.mainModProcs) + generateThreadVarsSize(m) + + 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: + when hasTinyCBackend: + if m.config.cmd == cmdTcc: + tccgen.compileCCode($code, m.config) + onExit() + return + + if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} + addFileToCompile(m.config, cf) + elif pending and mergeRequired(m) and sfMainModule notin m.module.flags: + let cf = Cfile(nimname: m.module.name.s, cname: cfile, + obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) + mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) - if sfMainModule in m.module.flags: - # generate main file: - genMainProc(m) - m.s[cfsProcHeaders].add(m.g.mainModProcs) - generateThreadVarsSize(m) - - 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 != "" or m.config.symbolFiles != disabledSf: - when hasTinyCBackend: - if m.config.cmd == cmdTcc: - tccgen.compileCCode($code, m.config) - onExit() - return - - if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} + var code = genModule(m, cf) + if code != nil: + if not writeRope(code, cfile): + rawMessage(m.config, errCannotOpenFile, cfile.string) + addFileToCompile(m.config, cf) + else: + # Consider: first compilation compiles ``system.nim`` and produces + # ``system.c`` but then compilation fails due to an error. This means + # that ``system.o`` is missing, so we need to call the C compiler for it: + var cf = Cfile(nimname: m.module.name.s, cname: cfile, + obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) + if not fileExists(cf.obj): cf.flags = {CfileFlag.Cached} addFileToCompile(m.config, cf) onExit() @@ -2085,23 +2021,36 @@ proc updateCachedModule(m: BModule) = let cfile = getCFile(m) var cf = Cfile(nimname: m.module.name.s, cname: cfile, obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) - if sfMainModule notin m.module.flags: - genMainProc(m) - cf.flags = {CfileFlag.Cached} - addFileToCompile(m.config, cf) -proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = - ## Also called from IC. + if mergeRequired(m) and sfMainModule notin m.module.flags: + mergeFiles(cfile, m) + genInitCode(m) + finishTypeDescriptions(m) + var code = genModule(m, cf) + if code != nil: + if not writeRope(code, cfile): + rawMessage(m.config, errCannotOpenFile, cfile.string) + addFileToCompile(m.config, cf) + else: + if sfMainModule notin m.module.flags: + genMainProc(m) + cf.flags = {CfileFlag.Cached} + addFileToCompile(m.config, cf) + +proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = + result = n + if b == nil: return + var m = BModule(b) if sfMainModule in m.module.flags: # 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: - cgsym(m, "nimTestErrorFlag") + discard cgsym(m, "nimTestErrorFlag") if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}: for i in countdown(high(graph.globalDestructors), 0): n.add graph.globalDestructors[i] - if pipelineutils.skipCodegen(m.config, n): return + if passes.skipCodegen(m.config, n): return if moduleHasChanged(graph, m.module): # if the module is cached, we don't regenerate the main proc # nor the dispatchers? But if the dispatchers changed? @@ -2112,7 +2061,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) - cgsym(m, "programResult") + discard cgsym(m, "programResult") if m.inHcrInitGuard: endBlock(m.initProc) @@ -2122,17 +2071,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 - cgsym(m, "nimLoadLibrary") - cgsym(m, "nimLoadLibraryError") - cgsym(m, "nimGetProcAddr") - cgsym(m, "procAddrError") - cgsym(m, "rawWrite") + discard cgsym(m, "nimLoadLibrary") + discard cgsym(m, "nimLoadLibraryError") + discard cgsym(m, "nimGetProcAddr") + discard cgsym(m, "procAddrError") + discard cgsym(m, "rawWrite") # raise dependencies on behalf of genMainProc - if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}: - cgsym(m, "initStackBottomWith") + if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone: + discard cgsym(m, "initStackBottomWith") if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: - cgsym(m, "initThreadVarsEmulation") + discard cgsym(m, "initThreadVarsEmulation") if m.g.forwardedProcs.len == 0: incl m.flags, objHasKidsValid @@ -2149,7 +2098,8 @@ proc genForwardedProcs(g: BModuleList) = while g.forwardedProcs.len > 0: let prc = g.forwardedProcs.pop() - m = g.modules[prc.itemId.module] + ms = getModule(prc) + m = g.modules[ms.position] if sfForward in prc.flags: internalError(m.config, prc.info, "still forwarded: " & prc.name.s) @@ -2168,3 +2118,5 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) = m.writeModule(pending=true) writeMapping(config, g.mapping) if g.generatedHeader != nil: writeHeader(g.generatedHeader) + +const cgenPass* = makePass(myOpen, myProcess, myClose) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index e1309e0fd..8e5094336 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -16,6 +16,7 @@ import type TLabel* = Rope # for the C generator a label is just a rope TCFileSection* = enum # the sections a generated C file consists of + cfsMergeInfo, # section containing merge information cfsHeaders, # section for C include file headers cfsFrameDefines # section for nim frame macros cfsForwardTypes, # section for C forward typedefs @@ -23,17 +24,21 @@ type cfsSeqTypes, # section for sequence types only # this is needed for strange type generation # reasons + 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 cfsInitProc, # section for the C init proc cfsDatInitProc, # section for the C datInit proc cfsTypeInit1, # section 1 for declarations of type information + cfsTypeInit2, # section 2 for init of type information cfsTypeInit3, # section 3 for init of type information + cfsDebugInit, # section for init of debug information cfsDynLibInit, # section for init of dynamic library binding + cfsDynLibDeinit # section for deinitialization of dynamic + # libraries TCTypeKind* = enum # describes the type kind of a C type ctVoid, ctChar, ctBool, ctInt, ctInt8, ctInt16, ctInt32, ctInt64, @@ -86,7 +91,6 @@ type options*: TOptions # options that should be used for code # generation; this is the same as prc.options # unless prc == nil - optionsStack*: seq[TOptions] module*: BModule # used to prevent excessive parameter passing withinLoop*: int # > 0 if we are within a loop splitDecls*: int # > 0 if we are in some context for C++ that @@ -95,11 +99,10 @@ type withinTryWithExcept*: int # required for goto based exception handling withinBlockLeaveActions*: int # complex to explain sigConflicts*: CountTable[string] - inUncheckedAssignSection*: int TTypeSeq* = seq[PType] TypeCache* = Table[SigHash, Rope] - TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: int32]] + TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: PSym]] CodegenFlag* = enum preventStackTrace, # true if stack traces need to be prevented @@ -109,8 +112,7 @@ type isHeaderFile, # C source file is the header file includesStringh, # C source file already includes ```` objHasKidsValid # whether we can rely on tfObjHasKids - useAliveDataFromDce # use the `alive: IntSet` field instead of - # computing alive data on our own. + BModuleList* = ref object of RootObj mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope @@ -152,7 +154,6 @@ type forwTypeCache*: TypeCache # cache for forward declarations of types declaredThings*: IntSet # things we have declared in this .c file declaredProtos*: IntSet # prototypes we have declared in this .c file - alive*: IntSet # symbol IDs of alive data as computed by `dce.nim` headerFiles*: seq[string] # needed headers to include typeInfoMarker*: TypeCache # needed for generating type information typeInfoMarkerV2*: TypeCache @@ -167,13 +168,13 @@ type labels*: Natural # for generating unique module-scope names extensionLoaders*: array['0'..'9', Rope] # special procs for the # OpenGL wrapper + injectStmt*: Rope sigConflicts*: CountTable[SigHash] g*: BModuleList ndi*: NdiFile template config*(m: BModule): ConfigRef = m.g.config template config*(p: BProc): ConfigRef = p.module.g.config -template vccAndC*(p: BProc): bool = p.module.config.cCompiler == ccVcc and p.module.config.backend == backendC proc includeHeader*(this: BModule; header: string) = if not this.headerFiles.contains header: @@ -187,24 +188,19 @@ 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 - result.blocks = @[initBlock()] + newSeq(result.blocks, 1) result.nestedTryStmts = @[] result.finallySafePoints = @[] result.sigConflicts = initCountTable[string]() proc newModuleList*(g: ModuleGraph): BModuleList = - BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](), + BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: PSym]](), config: g.config, graph: g, nimtvDeclared: initIntSet()) iterator cgenModules*(g: BModuleList): BModule = diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 4940a9093..a995804c7 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -11,11 +11,7 @@ import intsets, options, ast, msgs, idents, renderer, types, magicsys, - sempass2, modulegraphs, lineinfos - -when defined(nimPreviewSlimSystem): - import std/assertions - + sempass2, strutils, modulegraphs, lineinfos proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode = var dest = skipTypes(d, abstractPtrs) @@ -50,7 +46,6 @@ proc methodCall*(n: PNode; conf: ConfigRef): PNode = # replace ordinary method by dispatcher method: let disp = getDispatcher(result[0].sym) if disp != nil: - result[0].typ = disp.typ result[0].sym = disp # change the arguments to up/downcasts to fit the dispatcher's parameters: for i in 1.. resultPos: disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen)) @@ -148,7 +140,24 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) = disp.ast[resultPos].kind == nkEmpty: disp.ast[resultPos] = copyTree(meth.ast[resultPos]) -proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) = + # The following code works only with lock levels, so we disable + # it when they're not available. + when declared(TLockLevel): + proc `<`(a, b: TLockLevel): bool {.borrow.} + proc `==`(a, b: TLockLevel): bool {.borrow.} + if disp.typ.lockLevel == UnspecifiedLockLevel: + disp.typ.lockLevel = meth.typ.lockLevel + elif meth.typ.lockLevel != UnspecifiedLockLevel and + meth.typ.lockLevel != disp.typ.lockLevel: + message(conf, meth.info, warnLockLevel, + "method has lock level $1, but another method has $2" % + [$meth.typ.lockLevel, $disp.typ.lockLevel]) + # XXX The following code silences a duplicate warning in + # checkMethodeffects() in sempass2.nim for now. + if disp.typ.lockLevel < meth.typ.lockLevel: + disp.typ.lockLevel = meth.typ.lockLevel + +proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym, fromCache: bool) = var witness: PSym for i in 0..= 0: - msgs.setDirtyFile(conf, dirtyOriginalIdx, makeAbsolute(dirty)) - conf.m.trackPos = newLineInfo(dirtyOriginalIdx, ln, col) - proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) = var a = arg.split(',') if a.len != 4: localError(conf, info, "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected") - setTrackingInfo(conf, a[0], a[1], a[2], a[3], info) + var line, column: int + if parseUtils.parseInt(a[2], line) <= 0: + localError(conf, info, errInvalidNumber % a[1]) + if parseUtils.parseInt(a[3], column) <= 0: + localError(conf, info, errInvalidNumber % a[2]) + + let dirtyOriginalIdx = fileInfoIdx(conf, AbsoluteFile a[1]) + if dirtyOriginalIdx.int32 >= 0: + msgs.setDirtyFile(conf, dirtyOriginalIdx, AbsoluteFile a[0]) + + conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column) proc track(conf: ConfigRef; arg: string, info: TLineInfo) = var a = arg.split(',') if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected") - setTrackingInfo(conf, "", a[0], a[1], a[2], info) - -proc trackIde(conf: ConfigRef; cmd: IdeCmd, arg: string, info: TLineInfo) = - ## set the tracking info related to an ide cmd, supports optional dirty file - var a = arg.split(',') - case a.len - of 4: - setTrackingInfo(conf, a[0], a[1], a[2], a[3], info) - of 3: - setTrackingInfo(conf, "", a[0], a[1], a[2], info) - else: - localError(conf, info, "[DIRTY_BUFFER,]ORIGINAL_FILE,LINE,COLUMN expected") - conf.ideCmd = cmd + var line, column: int + if parseUtils.parseInt(a[1], line) <= 0: + localError(conf, info, errInvalidNumber % a[1]) + if parseUtils.parseInt(a[2], column) <= 0: + localError(conf, info, errInvalidNumber % a[2]) + conf.m.trackPos = newLineInfo(conf, AbsoluteFile a[0], line, column) proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if pass in {passCmd2, passPP}: @@ -447,11 +408,8 @@ proc parseCommand*(command: string): Command = of "check": cmdCheck of "e": cmdNimscript of "doc0": cmdDoc0 - of "doc2", "doc": cmdDoc - of "doc2tex": cmdDoc2tex + of "doc2", "doc": cmdDoc2 of "rst2html": cmdRst2html - of "md2tex": cmdMd2tex - of "md2html": cmdMd2html of "rst2tex": cmdRst2tex of "jsondoc0": cmdJsondoc0 of "jsondoc2", "jsondoc": cmdJsondoc @@ -460,7 +418,7 @@ proc parseCommand*(command: string): Command = of "gendepend": cmdGendepend of "dump": cmdDump of "parse": cmdParse - of "rod": cmdRod + of "scan": cmdScan of "secret": cmdInteractive of "nop", "help": cmdNop of "jsonscript": cmdJsonscript @@ -483,108 +441,11 @@ proc setCommandEarly*(conf: ConfigRef, command: string) = # command early customizations # must be handled here to honor subsequent `--hint:x:on|off` case conf.cmd - of cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex: - # xxx see whether to add others: cmdGendepend, etc. + of cmdRst2html, cmdRst2tex: # xxx see whether to add others: cmdGendepend, etc. conf.foreignPackageNotes = {hintSuccessX} else: conf.foreignPackageNotes = foreignPackageNotesDefault -proc specialDefine(conf: ConfigRef, key: string; pass: TCmdLinePass) = - # Keep this syncronized with the default config/nim.cfg! - if cmpIgnoreStyle(key, "nimQuirky") == 0: - conf.exc = excQuirky - elif cmpIgnoreStyle(key, "release") == 0 or cmpIgnoreStyle(key, "danger") == 0: - if pass in {passCmd1, passPP}: - conf.options.excl {optStackTrace, optLineTrace, optLineDir, optOptimizeSize} - conf.globalOptions.excl {optExcessiveStackTrace, optCDebug} - conf.options.incl optOptimizeSpeed - if cmpIgnoreStyle(key, "danger") == 0 or cmpIgnoreStyle(key, "quick") == 0: - if pass in {passCmd1, passPP}: - conf.options.excl {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, - optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir} - conf.globalOptions.excl {optCDebug} - -proc initOrcDefines*(conf: ConfigRef) = - conf.selectedGC = gcOrc - defineSymbol(conf.symbols, "gcorc") - defineSymbol(conf.symbols, "gcdestructors") - incl conf.globalOptions, optSeqDestructors - incl conf.globalOptions, optTinyRtti - defineSymbol(conf.symbols, "nimSeqsV2") - defineSymbol(conf.symbols, "nimV2") - if conf.exc == excNone and conf.backend != backendCpp: - conf.exc = excGoto - -proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) = - if isOrc: - conf.selectedGC = gcOrc - defineSymbol(conf.symbols, "gcorc") - else: - conf.selectedGC = gcArc - defineSymbol(conf.symbols, "gcarc") - - defineSymbol(conf.symbols, "gcdestructors") - incl conf.globalOptions, optSeqDestructors - incl conf.globalOptions, optTinyRtti - if pass in {passCmd2, passPP}: - defineSymbol(conf.symbols, "nimSeqsV2") - defineSymbol(conf.symbols, "nimV2") - if conf.exc == excNone and conf.backend != backendCpp: - conf.exc = excGoto - -proc unregisterArcOrc(conf: ConfigRef) = - undefSymbol(conf.symbols, "gcdestructors") - undefSymbol(conf.symbols, "gcarc") - undefSymbol(conf.symbols, "gcorc") - undefSymbol(conf.symbols, "nimSeqsV2") - undefSymbol(conf.symbols, "nimV2") - excl conf.globalOptions, optSeqDestructors - excl conf.globalOptions, optTinyRtti - -proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass, - info: TLineInfo; conf: ConfigRef) = - if conf.backend == backendJs: return # for: bug #16033 - expectArg(conf, switch, arg, pass, info) - if pass in {passCmd2, passPP}: - case arg.normalize - of "boehm": - unregisterArcOrc(conf) - conf.selectedGC = gcBoehm - defineSymbol(conf.symbols, "boehmgc") - incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS - of "refc": - unregisterArcOrc(conf) - defineSymbol(conf.symbols, "gcrefc") - conf.selectedGC = gcRefc - of "markandsweep": - unregisterArcOrc(conf) - conf.selectedGC = gcMarkAndSweep - defineSymbol(conf.symbols, "gcmarkandsweep") - of "destructors", "arc": - registerArcOrc(pass, conf, false) - of "orc": - registerArcOrc(pass, conf, true) - of "hooks": - conf.selectedGC = gcHooks - defineSymbol(conf.symbols, "gchooks") - incl conf.globalOptions, optSeqDestructors - processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info) - if pass in {passCmd2, passPP}: - defineSymbol(conf.symbols, "nimSeqsV2") - of "go": - unregisterArcOrc(conf) - conf.selectedGC = gcGo - defineSymbol(conf.symbols, "gogc") - of "none": - unregisterArcOrc(conf) - conf.selectedGC = gcNone - defineSymbol(conf.symbols, "nogc") - of "stack", "regions": - unregisterArcOrc(conf) - conf.selectedGC = gcRegions - defineSymbol(conf.symbols, "gcregions") - else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) - proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = var @@ -604,7 +465,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; addPath(conf, if pass == passPP: processCfgPath(conf, path, info) else: processPath(conf, path, info), info) of "nimblepath", "babelpath": - if switch.normalize == "babelpath": deprecatedAlias(switch, "nimblepath") + # keep the old name for compat if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions: expectArg(conf, switch, arg, pass, info) var path = processPath(conf, arg, info, notRelativeToProj=true) @@ -613,7 +474,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; path = nimbleDir / RelativeDir"pkgs" nimblePath(conf, path, info) of "nonimblepath", "nobabelpath": - if switch.normalize == "nobabelpath": deprecatedAlias(switch, "nonimblepath") expectNoArg(conf, switch, arg, pass, info) disableNimblePath(conf) of "clearnimblepath": @@ -626,10 +486,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf.lazyPaths.keepItIf(it != path) of "nimcache": expectArg(conf, switch, arg, pass, info) - var arg = arg - # refs bug #18674, otherwise `--os:windows` messes up with `--nimcache` set - # in config nims files, e.g. via: `import os; switch("nimcache", "/tmp/somedir")` - if conf.target.targetOS == osWindows and DirSep == '/': arg = arg.replace('\\', '/') conf.nimcacheDir = processPath(conf, arg, info, notRelativeToProj=true) of "out", "o": expectArg(conf, switch, arg, pass, info) @@ -649,22 +505,27 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "backend", "b": let backend = parseEnum(arg.normalize, TBackend.default) if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg) - if backend == backendJs: # bug #21209 - conf.globalOptions.excl {optThreadAnalysis, optThreads} conf.backend = backend of "doccmd": conf.docCmd = arg + of "mainmodule", "m": + discard "allow for backwards compatibility, but don't do anything" of "define", "d": expectArg(conf, switch, arg, pass, info) if {':', '='} in arg: splitSwitch(conf, arg, key, val, pass, info) - specialDefine(conf, key, pass) + if cmpIgnoreStyle(key, "nimQuirky") == 0: + conf.exc = excQuirky defineSymbol(conf.symbols, key, val) else: - specialDefine(conf, arg, pass) + if cmpIgnoreStyle(arg, "nimQuirky") == 0: + conf.exc = excQuirky defineSymbol(conf.symbols, arg) of "undef", "u": expectArg(conf, switch, arg, pass, info) undefSymbol(conf.symbols, arg) + of "symbol": + expectArg(conf, switch, arg, pass, info) + # deprecated, do nothing of "compile": expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: processCompile(conf, arg) @@ -687,21 +548,68 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "project": processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info) of "gc": - warningDeprecated(conf, info, "`gc:option` is deprecated; use `mm:option` instead") - processMemoryManagementOption(switch, arg, pass, info, conf) - of "mm": - processMemoryManagementOption(switch, arg, pass, info, conf) + if conf.backend == backendJs: return # for: bug #16033 + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: + case arg.normalize + of "boehm": + conf.selectedGC = gcBoehm + defineSymbol(conf.symbols, "boehmgc") + incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS + of "refc": + conf.selectedGC = gcRefc + of "v2": + message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc") + of "markandsweep": + conf.selectedGC = gcMarkAndSweep + defineSymbol(conf.symbols, "gcmarkandsweep") + of "destructors", "arc": + conf.selectedGC = gcArc + defineSymbol(conf.symbols, "gcdestructors") + defineSymbol(conf.symbols, "gcarc") + incl conf.globalOptions, optSeqDestructors + incl conf.globalOptions, optTinyRtti + if pass in {passCmd2, passPP}: + defineSymbol(conf.symbols, "nimSeqsV2") + defineSymbol(conf.symbols, "nimV2") + if conf.exc == excNone and conf.backend != backendCpp: + conf.exc = excGoto + of "orc": + conf.selectedGC = gcOrc + defineSymbol(conf.symbols, "gcdestructors") + defineSymbol(conf.symbols, "gcorc") + incl conf.globalOptions, optSeqDestructors + incl conf.globalOptions, optTinyRtti + if pass in {passCmd2, passPP}: + defineSymbol(conf.symbols, "nimSeqsV2") + defineSymbol(conf.symbols, "nimV2") + if conf.exc == excNone and conf.backend != backendCpp: + conf.exc = excGoto + of "hooks": + conf.selectedGC = gcHooks + defineSymbol(conf.symbols, "gchooks") + incl conf.globalOptions, optSeqDestructors + processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info) + if pass in {passCmd2, passPP}: + defineSymbol(conf.symbols, "nimSeqsV2") + of "go": + conf.selectedGC = gcGo + defineSymbol(conf.symbols, "gogc") + of "none": + conf.selectedGC = gcNone + defineSymbol(conf.symbols, "nogc") + of "stack", "regions": + conf.selectedGC = gcRegions + defineSymbol(conf.symbols, "gcregions") + else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "warnings", "w": if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf) of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf) of "hint": processSpecificNote(arg, wHint, pass, info, switch, conf) of "warningaserror": processSpecificNote(arg, wWarningAsError, pass, info, switch, conf) - of "hintaserror": processSpecificNote(arg, wHintAsError, pass, info, switch, conf) of "hints": if processOnOffSwitchOrList(conf, {optHints}, arg, pass, info): listHints(conf) - of "threadanalysis": - if conf.backend == backendJs: discard - else: processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info) + of "threadanalysis": processOnOffSwitchG(conf, {optThreadAnalysis}, arg, pass, info) of "stacktrace": processOnOffSwitch(conf, {optStackTrace}, arg, pass, info) of "stacktracemsgs": processOnOffSwitch(conf, {optStackTraceMsgs}, arg, pass, info) of "excessivestacktrace": processOnOffSwitchG(conf, {optExcessiveStackTrace}, arg, pass, info) @@ -740,32 +648,32 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; else: undefSymbol(conf.symbols, "hotcodereloading") undefSymbol(conf.symbols, "useNimRtl") + of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info) of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info) of "floatchecks": processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info) of "infchecks": processOnOffSwitch(conf, {optInfCheck}, arg, pass, info) of "nanchecks": processOnOffSwitch(conf, {optNaNCheck}, arg, pass, info) + of "nilchecks": discard "no such thing anymore" of "objchecks": processOnOffSwitch(conf, {optObjCheck}, arg, pass, info) of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info) of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info) of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info) - of "refchecks": - warningDeprecated(conf, info, "refchecks is deprecated!") - processOnOffSwitch(conf, {optRefCheck}, arg, pass, info) + of "refchecks": processOnOffSwitch(conf, {optRefCheck}, arg, pass, info) of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info) of "staticboundchecks": processOnOffSwitch(conf, {optStaticBoundsCheck}, arg, pass, info) of "stylechecks": processOnOffSwitch(conf, {optStyleCheck}, arg, pass, info) of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info) of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info) + of "deadcodeelim": discard # deprecated, dead code elim always on of "threads": - if conf.backend == backendJs or conf.cmd == cmdNimscript: discard - else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info) + processOnOffSwitchG(conf, {optThreads}, arg, pass, info) #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe) of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) + of "taintmode": processOnOffSwitchG(conf, {optTaintMode}, arg, pass, info) of "implicitstatic": processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info) of "patterns", "trmacros": - if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros") processOnOffSwitch(conf, {optTrMacros}, arg, pass, info) of "opt": expectArg(conf, switch, arg, pass, info) @@ -817,18 +725,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "clib": expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: - conf.cLinkedLibs.add arg + conf.cLinkedLibs.add processPath(conf, arg, info).string of "header": if conf != nil: conf.headerFile = arg incl(conf.globalOptions, optGenIndex) of "index": - case arg.normalize - of "", "on": conf.globalOptions.incl {optGenIndex} - of "only": conf.globalOptions.incl {optGenIndexOnly, optGenIndex} - of "off": conf.globalOptions.excl {optGenIndex, optGenIndexOnly} - else: localError(conf, info, errOnOrOffExpectedButXFound % arg) - of "noimportdoc": - processOnOffSwitchG(conf, {optNoImportdoc}, arg, pass, info) + processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info) of "import": expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: @@ -898,19 +800,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "help", "h": expectNoArg(conf, switch, arg, pass, info) helpOnError(conf, pass) - of "symbolfiles", "incremental", "ic": - if switch.normalize == "symbolfiles": deprecatedAlias(switch, "incremental") - # xxx maybe also ic, since not in help? - if pass in {passCmd2, passPP}: - case arg.normalize - of "on": conf.symbolFiles = v2Sf - of "off": conf.symbolFiles = disabledSf - of "writeonly": conf.symbolFiles = writeOnlySf - of "readonly": conf.symbolFiles = readOnlySf - of "v2": conf.symbolFiles = v2Sf - of "stress": conf.symbolFiles = stressTest - else: localError(conf, info, "invalid option for --incremental: " & arg) - setUseIc(conf.symbolFiles != disabledSf) + of "symbolfiles": discard "ignore for backwards compat" + of "incremental": + case arg.normalize + of "on": conf.symbolFiles = v2Sf + of "off": conf.symbolFiles = disabledSf + of "writeonly": conf.symbolFiles = writeOnlySf + of "readonly": conf.symbolFiles = readOnlySf + of "v2": conf.symbolFiles = v2Sf + else: localError(conf, info, "invalid option for --incremental: " & arg) of "skipcfg": processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info) of "skipprojcfg": @@ -920,11 +818,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "skipparentcfg": processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info) of "genscript", "gendeps": - if switch.normalize == "gendeps": deprecatedAlias(switch, "genscript") processOnOffSwitchG(conf, {optGenScript}, arg, pass, info) processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info) - of "gencdeps": - processOnOffSwitchG(conf, {optGenCDeps}, arg, pass, info) of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info) of "lib": expectArg(conf, switch, arg, pass, info) @@ -934,9 +829,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; splitSwitch(conf, arg, key, val, pass, info) os.putEnv(key, val) of "cc": - if conf.backend != backendJs: # bug #19330 - expectArg(conf, switch, arg, pass, info) - setCC(conf, arg, info) + expectArg(conf, switch, arg, pass, info) + setCC(conf, arg, info) of "track": expectArg(conf, switch, arg, pass, info) track(conf, arg, info) @@ -947,50 +841,27 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectNoArg(conf, switch, arg, pass, info) conf.ideCmd = ideSug of "def": - expectArg(conf, switch, arg, pass, info) - trackIde(conf, ideDef, arg, info) + expectNoArg(conf, switch, arg, pass, info) + conf.ideCmd = ideDef of "context": expectNoArg(conf, switch, arg, pass, info) conf.ideCmd = ideCon of "usages": - expectArg(conf, switch, arg, pass, info) - trackIde(conf, ideUse, arg, info) - of "defusages": - expectArg(conf, switch, arg, pass, info) - trackIde(conf, ideDus, arg, info) + expectNoArg(conf, switch, arg, pass, info) + conf.ideCmd = ideUse of "stdout": processOnOffSwitchG(conf, {optStdout}, arg, pass, info) - of "filenames": - case arg.normalize - of "abs": conf.filenameOption = foAbs - of "canonical": conf.filenameOption = foCanonical - of "legacyrelproj": conf.filenameOption = foLegacyRelProj - else: localError(conf, info, "expected: abs|canonical|legacyRelProj, got: $1" % arg) - of "processing": - incl(conf.notes, hintProcessing) - incl(conf.mainPackageNotes, hintProcessing) - case arg.normalize - of "dots": conf.hintProcessingDots = true - of "filenames": conf.hintProcessingDots = false - of "off": - excl(conf.notes, hintProcessing) - excl(conf.mainPackageNotes, hintProcessing) - else: localError(conf, info, "expected: dots|filenames|off, got: $1" % arg) - of "unitsep": - conf.unitSep = if switchOn(arg): "\31" else: "" of "listfullpaths": - # xxx in future work, use `warningDeprecated` - conf.filenameOption = if switchOn(arg): foAbs else: foCanonical - of "spellsuggest": - if arg.len == 0: conf.spellSuggestMax = spellSuggestSecretSauce - elif arg == "auto": conf.spellSuggestMax = spellSuggestSecretSauce - else: conf.spellSuggestMax = parseInt(arg) + processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info) of "declaredlocs": processOnOffSwitchG(conf, {optDeclaredLocs}, arg, pass, info) of "dynliboverride": dynlibOverride(conf, switch, arg, pass, info) of "dynliboverrideall": processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info) + of "cs": + # only supported for compatibility. Does nothing. + expectArg(conf, switch, arg, pass, info) of "experimental": if arg.len == 0: conf.features.incl oldExperimentalFeatures @@ -1008,9 +879,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectNoArg(conf, switch, arg, pass, info) conf.exc = low(ExceptionSystem) defineSymbol(conf.symbols, "noCppExceptions") - of "shownonexports": - expectNoArg(conf, switch, arg, pass, info) - showNonExportedFields(conf) of "exceptions": case arg.normalize of "cpp": conf.exc = excCpp @@ -1023,7 +891,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if conf != nil: conf.cppDefine(arg) of "newruntime": - warningDeprecated(conf, info, "newruntime is deprecated, use arc/orc instead!") expectNoArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: doAssert(conf != nil) @@ -1045,7 +912,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError} of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError} of "error": conf.globalOptions = conf.globalOptions + {optStyleError} - of "usages": conf.globalOptions.incl optStyleUsages else: localError(conf, info, errOffHintsError % arg) of "showallmismatches": processOnOffSwitchG(conf, {optShowAllMismatches}, arg, pass, info) @@ -1071,6 +937,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "1.0": defineSymbol(conf.symbols, "NimMajor", "1") defineSymbol(conf.symbols, "NimMinor", "0") + # always be compatible with 1.0.100: + defineSymbol(conf.symbols, "NimPatch", "100") # old behaviors go here: defineSymbol(conf.symbols, "nimOldRelativePathBehavior") undefSymbol(conf.symbols, "nimDoesntTrackDefects") @@ -1079,15 +947,11 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "1.2": defineSymbol(conf.symbols, "NimMajor", "1") defineSymbol(conf.symbols, "NimMinor", "2") + # always be compatible with 1.2.100: + defineSymbol(conf.symbols, "NimPatch", "100") conf.globalOptions.incl optNimV12Emulation - of "1.6": - defineSymbol(conf.symbols, "NimMajor", "1") - defineSymbol(conf.symbols, "NimMinor", "6") - conf.globalOptions.incl optNimV16Emulation else: - localError(conf, info, "unknown Nim version; currently supported values are: `1.0`, `1.2`") - # always be compatible with 1.x.100: - defineSymbol(conf.symbols, "NimPatch", "100") + localError(conf, info, "unknown Nim version; currently supported values are: {1.0}") of "benchmarkvm": processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info) of "profilevm": @@ -1101,15 +965,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; processOnOffSwitchG(conf, {optPanics}, arg, pass, info) if optPanics in conf.globalOptions: defineSymbol(conf.symbols, "nimPanics") - of "sourcemap": # xxx document in --fullhelp + of "sourcemap": conf.globalOptions.incl optSourcemap conf.options.incl optLineDir of "deepcopy": processOnOffSwitchG(conf, {optEnableDeepCopy}, arg, pass, info) of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -) handleStdinInput(conf) - of "nilseqs", "nilchecks", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch) - of "nimmainprefix": conf.nimMainPrefix = arg else: if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg) else: invalidCmdLineOption(conf, pass, switch, info) @@ -1119,6 +981,7 @@ proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) = splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo) processSwitch(cmd, arg, pass, gCmdLineInfo, config) + proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) = # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off") # we transform it to (key = hint, val = [X]:off) @@ -1146,8 +1009,6 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser; else: if pass == passCmd1: config.commandArgs.add p.key if argsCount == 1: - if p.key.endsWith(".nims"): - incl(config.globalOptions, optWasNimscript) # support UNIX style filenames everywhere for portable build scripts: if config.projectName.len == 0: config.projectName = unixToNativePath(p.key) diff --git a/compiler/concepts.nim b/compiler/concepts.nim deleted file mode 100644 index bcf2d4d0e..000000000 --- a/compiler/concepts.nim +++ /dev/null @@ -1,344 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2020 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## New styled concepts for Nim. See https://github.com/nim-lang/RFCs/issues/168 -## for details. Note this is a first implementation and only the "Concept matching" -## section has been implemented. - -import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets - -from magicsys import addSonSkipIntLit - -when defined(nimPreviewSlimSystem): - import std/assertions - -const - logBindings = false - -## Code dealing with Concept declarations -## -------------------------------------- - -proc declareSelf(c: PContext; info: TLineInfo) = - ## Adds the magical 'Self' symbols to the current scope. - let ow = getCurrOwner(c) - let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info) - s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow) - s.typ.flags.incl {tfUnresolved, tfPacked} - s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow) - addDecl(c, s, info) - -proc isSelf*(t: PType): bool {.inline.} = - ## Is this the magical 'Self' type? - t.kind == tyTypeDesc and tfPacked in t.flags - -proc makeTypeDesc*(c: PContext, typ: PType): PType = - if typ.kind == tyTypeDesc and not isSelf(typ): - result = typ - else: - result = newTypeS(tyTypeDesc, c) - incl result.flags, tfCheckedForDestructor - result.addSonSkipIntLit(typ, c.idgen) - -proc semConceptDecl(c: PContext; n: PNode): PNode = - ## Recursive helper for semantic checking for the concept declaration. - ## Currently we only support (possibly empty) lists of statements - ## containing 'proc' declarations and the like. - case n.kind - of nkStmtList, nkStmtListExpr: - result = shallowCopy(n) - for i in 0.. 0 and f[0].kind != tyNone: - # also check the generic's constraints: - let oldLen = m.inferred.len - result = matchType(c, f[0], a, m) - m.inferred.setLen oldLen - if result: - when logBindings: echo "A adding ", f, " ", ak - m.inferred.add((f, ak)) - elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}: - when logBindings: echo "B adding ", f, " ", lastSon ak - m.inferred.add((f, lastSon ak)) - result = true - else: - when logBindings: echo "C adding ", f, " ", ak - m.inferred.add((f, ak)) - #echo "binding ", typeToString(ak), " to ", typeToString(f) - result = true - elif not m.marker.containsOrIncl(old.id): - result = matchType(c, old, ak, m) - if m.magic == mArrPut and ak.kind == tyGenericParam: - result = true - #echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation) - - of tyVar, tySink, tyLent, tyOwned: - # modifiers in the concept must be there in the actual implementation - # too but not vice versa. - if a.kind == f.kind: - result = matchType(c, f.sons[0], a.sons[0], m) - elif m.magic == mArrPut: - result = matchType(c, f.sons[0], a, m) - else: - result = false - of tyEnum, tyObject, tyDistinct: - result = sameType(f, a) - of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid: - result = a.skipTypes(ignorableForArgType).kind == f.kind - of tyBool, tyChar, tyInt..tyUInt64: - let ak = a.skipTypes(ignorableForArgType) - result = ak.kind == f.kind or ak.kind == tyOrdinal or - (ak.kind == tyGenericParam and ak.len > 0 and ak[0].kind == tyOrdinal) - of tyConcept: - let oldLen = m.inferred.len - let oldPotentialImplementation = m.potentialImplementation - m.potentialImplementation = a - result = conceptMatchNode(c, f.n.lastSon, m) - m.potentialImplementation = oldPotentialImplementation - if not result: - m.inferred.setLen oldLen - of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr, - tyGenericInst: - let ak = a.skipTypes(ignorableForArgType - {f.kind}) - if ak.kind == f.kind and f.len == ak.len: - for i in 0..= a.len - if not result: - m.inferred.setLen oldLen - else: - for i in 0.. \"$2\";$n", [rope(importing), rope(imported)]) + 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 = +proc addDotDependency(c: PPassContext, n: PNode): PNode = result = n let g = PGen(c) let b = Backend(g.graph.backend) case n.kind of nkImportStmt: for i in 0.. we need to 'join' every pushed 'fork' here + + +This works and then our abstract interpretation needs to deal with 'fork' +differently. It really causes a split in execution. Two threads are +"spawned" and both need to reach the 'join L' instruction. Afterwards +the abstract interpretations are joined and execution resumes single +threaded. + + +Abstract Interpretation +----------------------- + +proc interpret(pc, state, comesFrom): state = + result = state + # we need an explicit 'create' instruction (an explicit heap), in order + # to deal with 'var x = create(); var y = x; var z = y; destroy(z)' + while true: + case pc + of fork: + let a = interpret(pc+1, result, pc) + let b = interpret(forkTarget, result, pc) + result = a ++ b # ++ is a union operation + inc pc + of join: + if joinTarget == comesFrom: return result + else: inc pc + of use X: + if not result.contains(x): + error "variable not initialized " & x + inc pc + of def X: + if not result.contains(x): + result.incl X + else: + error "overwrite of variable causes memory leak " & x + inc pc + of destroy X: + result.excl X + +This is correct but still can lead to false positives: + +proc p(cond: bool) = + if cond: + new(x) + otherThings() + if cond: + destroy x + +Is not a leak. We should find a way to model *data* flow, not just +control flow. One solution is to rewrite the 'if' without a fork +instruction. The unstructured aspect can now be easily dealt with +the 'goto' and 'join' instructions. + +proc p(cond: bool) = + L0: fork Lend + new(x) + # do not 'join' here! + + Lend: + otherThings() + join L0 # SKIP THIS FOR new(x) SOMEHOW + destroy x + join L0 # but here. + + + +But if we follow 'goto Louter' we will never come to the join point. +We restore the bindings after popping pc from the stack then there +"no" problem?! + + +while cond: + prelude() + if not condB: break + postlude() + +---> +var setFlag = true +while cond and not setFlag: + prelude() + if not condB: + setFlag = true # BUT: Dependency + if not setFlag: # HERE + postlude() + +---> +var setFlag = true +while cond and not setFlag: + prelude() + if not condB: + postlude() + setFlag = true + + +------------------------------------------------- + +while cond: + prelude() + if more: + if not condB: break + stuffHere() + postlude() + +--> +var setFlag = true +while cond and not setFlag: + prelude() + if more: + if not condB: + setFlag = false + else: + stuffHere() + postlude() + else: + postlude() + +This is getting complicated. Instead we keep the whole 'join' idea but +duplicate the 'join' instructions on breaks and return exits! + +]# proc genLabel(c: Con): TPosition = TPosition(c.code.len) @@ -101,8 +274,8 @@ template checkedDistance(dist): int = doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2 dist -proc jmpBack(c: var Con, p = TPosition(0)) = - c.code.add Instr(kind: loop, dest: checkedDistance(p.int - c.code.len)) +proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) = + c.code.add Instr(n: n, kind: goto, dest: checkedDistance(p.int - c.code.len)) proc patch(c: var Con, p: TPosition) = # patch with current index @@ -112,12 +285,12 @@ proc gen(c: var Con; n: PNode) proc popBlock(c: var Con; oldLen: int) = var exits: seq[TPosition] - exits.add c.gotoI() + exits.add c.gotoI(newNode(nkEmpty)) for f in c.blocks[oldLen].breakFixups: c.patch(f[0]) for finale in f[1]: c.gen(finale) - exits.add c.gotoI() + exits.add c.gotoI(newNode(nkEmpty)) for e in exits: c.patch e c.blocks.setLen(oldLen) @@ -132,29 +305,86 @@ proc isTrue(n: PNode): bool = n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or n.kind == nkIntLit and n.intVal != 0 -template forkT(body) = - let lab1 = c.forkI() +when true: + proc genWhile(c: var Con; n: PNode) = + # We unroll every loop 3 times. We emulate 0, 1, 2 iterations + # through the loop. We need to prove this is correct for our + # purposes. But Herb Sutter claims it is. (Proof by authority.) + #[ + while cond: + body + + Becomes: + + block: + if cond: + body + if cond: + body + if cond: + body + + We still need to ensure 'break' resolves properly, so an AST to AST + translation is impossible. + + So the code to generate is: + + cond + fork L4 # F1 + body + cond + fork L5 # F2 + body + cond + fork L6 # F3 + body + L6: + join F3 + L5: + join F2 + L4: + join F1 + ]# + if isTrue(n[0]): + # 'while true' is an idiom in Nim and so we produce + # better code for it: + withBlock(nil): + for i in 0..2: + c.gen(n[1]) + else: + withBlock(nil): + var endings: array[3, TPosition] + for i in 0..2: + c.gen(n[0]) + endings[i] = c.forkI(n) + c.gen(n[1]) + for i in countdown(endings.high, 0): + c.patch(endings[i]) + +else: + proc genWhile(c: var Con; n: PNode) = + # lab1: + # cond, tmp + # fork tmp, lab2 + # body + # jmp lab1 + # lab2: + let lab1 = c.genLabel + withBlock(nil): + if isTrue(n[0]): + c.gen(n[1]) + c.jmpBack(n, lab1) + else: + c.gen(n[0]) + forkT(n): + c.gen(n[1]) + c.jmpBack(n, lab1) + +template forkT(n, body) = + let lab1 = c.forkI(n) body c.patch(lab1) -proc genWhile(c: var Con; n: PNode) = - # lab1: - # cond, tmp - # fork tmp, lab2 - # body - # jmp lab1 - # lab2: - let lab1 = c.genLabel - withBlock(nil): - if isTrue(n[0]): - c.gen(n[1]) - c.jmpBack(lab1) - else: - c.gen(n[0]) - forkT: - c.gen(n[1]) - c.jmpBack(lab1) - proc genIf(c: var Con, n: PNode) = #[ @@ -193,22 +423,15 @@ proc genIf(c: var Con, n: PNode) = ]# var endings: seq[TPosition] = @[] - let oldInteresting = c.interestingInstructions - let oldLen = c.code.len - for i in 0.. 0: for i in countdown(c.blocks.high, 0): @@ -327,14 +543,13 @@ proc genRaise(c: var Con; n: PNode) = return assert false #Unreachable else: - genNoReturn(c) + genNoReturn(c, n) proc genImplicitReturn(c: var Con) = if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len: gen(c, c.owner.ast[resultPos]) proc genReturn(c: var Con; n: PNode) = - inc c.interestingInstructions if n[0].kind != nkEmpty: gen(c, n[0]) else: @@ -343,6 +558,89 @@ proc genReturn(c: var Con; n: PNode) = const InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp} + PathKinds0 = {nkDotExpr, nkCheckedFieldExpr, + nkBracketExpr, nkDerefExpr, nkHiddenDeref, + nkAddr, nkHiddenAddr, + nkObjDownConv, nkObjUpConv} + PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv} + +proc skipConvDfa*(n: PNode): PNode = + result = n + while true: + case result.kind + of nkObjDownConv, nkObjUpConv: + result = result[0] + of PathKinds1: + result = result[1] + else: break + +proc aliases*(obj, field: PNode): bool = + var n = field + var obj = obj + while true: + case obj.kind + of {nkObjDownConv, nkObjUpConv, nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref}: + obj = obj[0] + of PathKinds1: + obj = obj[1] + else: break + while true: + if sameTrees(obj, n): return true + case n.kind + of PathKinds0: + n = n[0] + of PathKinds1: + n = n[1] + else: break + +type InstrTargetKind* = enum + None, Full, Partial + +proc instrTargets*(insloc, loc: PNode): InstrTargetKind = + if sameTrees(insloc, loc) or insloc.aliases(loc): + Full # x -> x; x -> x.f + elif loc.aliases(insloc): + Partial # x.f -> x + else: None + +proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = + var n = orig + while true: + case n.kind + of PathKinds0 - {nkBracketExpr, nkHiddenDeref, nkDerefExpr}: + n = n[0] + of PathKinds1: + n = n[1] + of nkBracketExpr: + # in a[i] the 'i' must be known + if n.len > 1 and n[1].kind in {nkCharLit..nkUInt64Lit}: + n = n[0] + else: + return false + of nkHiddenDeref, nkDerefExpr: + # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty + # pointer indirection. + # bug #14159, we cannot reason about sinkParam[].location as it can + # still be shared for tyRef. + n = n[0] + return n.kind == nkSym and n.sym.owner == owner and + (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned}) + else: break + # XXX Allow closure deref operations here if we know + # the owner controlled the closure allocation? + result = n.kind == nkSym and n.sym.owner == owner and + {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and + (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar) + # Note: There is a different move analyzer possible that checks for + # consume(param.key); param.key = newValue for all paths. Then code like + # + # let splited = split(move self.root, x) + # self.root = merge(splited.lower, splited.greater) + # + # could be written without the ``move self.root``. However, this would be + # wrong! Then the write barrier for the ``self.root`` assignment would + # free the old data and all is lost! Lesson: Don't be too smart, trust the + # lower level C++ optimizer to specialize this code. proc skipTrivials(c: var Con, n: PNode): PNode = result = n @@ -360,20 +658,16 @@ proc skipTrivials(c: var Con, n: PNode): PNode = proc genUse(c: var Con; orig: PNode) = let n = c.skipTrivials(orig) - if n.kind == nkSym: - if n.sym.kind in InterestingSyms and n.sym == c.root: - c.code.add Instr(kind: use, n: orig) - inc c.interestingInstructions - else: + if n.kind == nkSym and n.sym.kind in InterestingSyms: + c.code.add Instr(n: orig, kind: use) + elif n.kind in nkCallKinds: gen(c, n) proc genDef(c: var Con; orig: PNode) = let n = c.skipTrivials(orig) if n.kind == nkSym and n.sym.kind in InterestingSyms: - if n.sym == c.root: - c.code.add Instr(kind: def, n: orig) - inc c.interestingInstructions + c.code.add Instr(n: orig, kind: def) proc genCall(c: var Con; n: PNode) = gen(c, n[0]) @@ -381,17 +675,18 @@ proc genCall(c: var Con; n: PNode) = if t != nil: t = t.skipTypes(abstractInst) for i in 1.. 0 and canRaiseConservative(n[0]): + if c.inTryStmt > 0 and canRaiseConservative(n[0]): # we generate the instruction sequence: # fork lab1 # goto exceptionHandler (except or finally) # lab1: # join F1 - forkT: + forkT(n): for i in countdown(c.blocks.high, 0): if c.blocks[i].isTryBlock: genBreakOrRaiseAux(c, i, n) @@ -429,18 +724,12 @@ proc gen(c: var Con; n: PNode) = else: genCall(c, n) if sfNoReturn in n[0].sym.flags: - genNoReturn(c) + genNoReturn(c, n) else: genCall(c, n) of nkCharLit..nkNilLit: discard - of nkAsgn, nkFastAsgn, nkSinkAsgn: + of nkAsgn, nkFastAsgn: gen(c, n[1]) - - if n[0].kind in PathKinds0: - let a = c.skipTrivials(n[0]) - if a.kind in nkCallKinds: - gen(c, a) - # watch out: 'obj[i].f2 = value' sets 'f2' but # "uses" 'i'. But we are only talking about builtin array indexing so # it doesn't matter and 'x = 34' is NOT a usage of 'x'. @@ -470,32 +759,13 @@ proc gen(c: var Con; n: PNode) = of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'" else: discard -when false: - proc optimizeJumps(c: var ControlFlowGraph) = - for i in 0.. pc: - pc = newPc - else: - break - c[i].dest = pc - i - of loop, def, use: discard - -proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph = +proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph = ## constructs a control flow graph for ``body``. - var c = Con(code: @[], blocks: @[], owner: s, root: root) + var c = Con(code: @[], blocks: @[], owner: s) withBlock(s): gen(c, body) - if root.kind == skResult: - genImplicitReturn(c) + genImplicitReturn(c) when defined(gcArc) or defined(gcOrc): result = c.code # will move else: shallowCopy(result, c.code) - when false: - optimizeJumps result diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 8105cc807..d21db7634 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -7,95 +7,45 @@ # distribution, for details about the copyright. # -## This is the Nim documentation generator. Cross-references are generated -## by knowing how the anchors are going to be named. -## -## .. importdoc:: ../docgen.md -## -## For corresponding users' documentation see [Nim DocGen Tools Guide]. +# This is the documentation generator. Cross-references are generated +# by knowing how the anchors are going to be named. import - ast, strutils, strtabs, algorithm, sequtils, options, msgs, os, idents, - wordrecg, syntaxes, renderer, lexer, - packages/docutils/[rst, rstidx, rstgen, dochelpers], + ast, strutils, strtabs, options, msgs, os, ropes, idents, + wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, + packages/docutils/rst, packages/docutils/rstgen, json, xmltree, trees, types, typesrenderer, astalgo, lineinfos, intsets, - pathutils, tables, nimpaths, renderverbatim, osproc, packages -import packages/docutils/rstast except FileIndex, TLineInfo + pathutils, trees, tables, nimpaths, renderverbatim, osproc from uri import encodeUrl -from nodejs import findNodeJs - -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] +from std/private/globs import nativeToUnixPath const exportSection = skField docCmdSkip = "skip" - DocColOffset = "## ".len # assuming that a space was added after ## type - ItemFragment = object ## A fragment from each item will be eventually - ## constructed by converting `rst` fields to strings. - case isRst: bool - of true: - rst: PRstNode - of false: ## contains ready markup e.g. from runnableExamples - str: string - ItemPre = seq[ItemFragment] ## A pre-processed item. - Item = object ## Any item in documentation, e.g. symbol - ## entry. Configuration variable ``doc.item`` - ## is used for its HTML rendering. - descRst: ItemPre ## Description of the item (may contain - ## runnableExamples). - substitutions: seq[string] ## Variable names in `doc.item`... - sortName: string ## The string used for sorting in output - info: rstast.TLineInfo ## place where symbol was defined (for messages) - anchor: string ## e.g. HTML anchor - name: string ## short name of the symbol, not unique - ## (includes backticks ` if present) - detailedName: string ## longer name like `proc search(x: int): int` - ModSection = object ## Section like Procs, Types, etc. - secItems: Table[string, seq[Item]] - ## Map basic name -> pre-processed items. - finalMarkup: string ## The items, after RST pass 2 and rendering. - ModSections = array[TSymKind, ModSection] - TocItem = object ## HTML TOC item - content: string - sortName: string - TocSectionsFinal = array[TSymKind, string] + TSections = array[TSymKind, Rope] ExampleGroup = ref object ## a group of runnableExamples with same rdoccmd rdoccmd: string ## from 1st arg in `runnableExamples(rdoccmd): body` docCmd: string ## from user config, e.g. --doccmd:-d:foo code: string ## contains imports; each import contains `body` index: int ## group index - JsonItem = object # pre-processed item: `rst` should be finalized - json: JsonNode - rst: PRstNode - rstField: string TDocumentor = object of rstgen.RstGenerator - modDescPre: ItemPre # module description, not finalized - modDescFinal: string # module description, after RST pass 2 and rendering + modDesc: Rope # module description module: PSym - modDeprecationMsg: string - section: ModSections # entries of ``.nim`` file (for `proc`s, etc) - tocSimple: array[TSymKind, seq[TocItem]] - # TOC entries for non-overloadable symbols (e.g. types, constants)... - tocTable: array[TSymKind, Table[string, seq[TocItem]]] - # ...otherwise (e.g. procs) - toc2: TocSectionsFinal # TOC `content`, which is probably wrapped - # in `doc.section.toc2` - toc: TocSectionsFinal # final TOC (wrapped in `doc.section.toc`) + modDeprecationMsg: Rope + toc, toc2, section: TSections + tocTable: array[TSymKind, Table[string, Rope]] indexValFilename: string analytics: string # Google Analytics javascript, "" if doesn't exist seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. - jEntriesPre: seq[JsonItem] # pre-processed RST + JSON content - jEntriesFinal: JsonNode # final JSON after RST pass 2 and rendering + jArray: JsonNode types: TStrTable - sharedState: PRstSharedState - standaloneDoc: bool # is markup (.rst/.md) document? + isPureRst: bool conf*: ConfigRef cache*: IdentCache exampleCounter: int @@ -104,73 +54,10 @@ type thisDir*: AbsoluteDir exampleGroups: OrderedTable[string, ExampleGroup] wroteSupportFiles*: bool - nimToRstFid: Table[lineinfos.FileIndex, rstast.FileIndex] - ## map Nim FileIndex -> RST one, it's needed because we keep them separate PDoc* = ref TDocumentor ## Alias to type less. -proc add(dest: var ItemPre, rst: PRstNode) = dest.add ItemFragment(isRst: true, rst: rst) -proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, str: str) - -proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex = - let invalid = rstast.FileIndex(-1) - result = d.nimToRstFid.getOrDefault(info.fileIndex, default = invalid) - if result == invalid: - let fname = toFullPath(d.conf, info) - result = addFilename(d.sharedState, fname) - d.nimToRstFid[info.fileIndex] = result - -proc cmpDecimalsIgnoreCase(a, b: string): int = - ## For sorting with correct handling of cases like 'uint8' and 'uint16'. - ## Also handles leading zeros well (however note that leading zeros are - ## significant when lengths of numbers mismatch, e.g. 'bar08' > 'bar8' !). - runnableExamples: - doAssert cmpDecimalsIgnoreCase("uint8", "uint16") < 0 - doAssert cmpDecimalsIgnoreCase("val00032", "val16suffix") > 0 - doAssert cmpDecimalsIgnoreCase("val16suffix", "val16") > 0 - doAssert cmpDecimalsIgnoreCase("val_08_32", "val_08_8") > 0 - doAssert cmpDecimalsIgnoreCase("val_07_32", "val_08_8") < 0 - doAssert cmpDecimalsIgnoreCase("ab8", "ab08") < 0 - doAssert cmpDecimalsIgnoreCase("ab8de", "ab08c") < 0 # sanity check - let aLen = a.len - let bLen = b.len - var - iA = 0 - iB = 0 - while iA < aLen and iB < bLen: - if isDigit(a[iA]) and isDigit(b[iB]): - var - limitA = iA # index after the last (least significant) digit - limitB = iB - while limitA < aLen and isDigit(a[limitA]): inc limitA - while limitB < bLen and isDigit(b[limitB]): inc limitB - var pos = max(limitA-iA, limitB-iA) - while pos > 0: - if limitA-pos < iA: # digit in `a` is 0 effectively - result = ord('0') - ord(b[limitB-pos]) - elif limitB-pos < iB: # digit in `b` is 0 effectively - result = ord(a[limitA-pos]) - ord('0') - else: - result = ord(a[limitA-pos]) - ord(b[limitB-pos]) - if result != 0: return - dec pos - result = (limitA - iA) - (limitB - iB) # consider 'bar08' > 'bar8' - if result != 0: return - iA = limitA - iB = limitB - else: - result = ord(toLowerAscii(a[iA])) - ord(toLowerAscii(b[iB])) - if result != 0: return - inc iA - inc iB - result = (aLen - iA) - (bLen - iB) - -proc prettyString(a: object): string = - # xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906 - for k, v in fieldPairs(a): - result.add k & ": " & $v & "\n" - -proc presentationPath*(conf: ConfigRef, file: AbsoluteFile): RelativeFile = +proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): RelativeFile = ## returns a relative file that will be appended to outDir let file2 = $file template bail() = @@ -195,7 +82,7 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile): RelativeFile = if result.isEmpty: bail() elif conf.docRoot.len > 0: # we're (currently) requiring `isAbsolute` to avoid confusion when passing - # a relative path (would it be relative with regard to $PWD or to projectfile) + # a relative path (would it be relative wrt $PWD or to projectfile) conf.globalAssert conf.docRoot.isAbsolute, arg=conf.docRoot conf.globalAssert conf.docRoot.dirExists, arg=conf.docRoot # needed because `canonicalizePath` called on `file` @@ -204,7 +91,10 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile): RelativeFile = bail() if isAbsolute(result.string): result = file.string.splitPath()[1].RelativeFile - result = result.string.replace("..", dotdotMangle).RelativeFile + if isTitle: + result = result.string.nativeToUnixPath.RelativeFile + else: + result = result.string.replace("..", dotdotMangle).RelativeFile doAssert not result.isEmpty doAssert not isAbsolute(result.string) @@ -229,7 +119,7 @@ proc attachToType(d: PDoc; p: PSym): PSym = if params.len > 0: check(0) for i in 2.. @@ -345,27 +188,22 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, """ % [conf.configVars.getOrDefault"doc.googleAnalytics"] - elif conf.configVars.hasKey("doc.plausibleAnalytics"): - result.analytics = """ - - """ % [conf.configVars.getOrDefault"doc.plausibleAnalytics"] else: result.analytics = "" result.seenSymbols = newStringTable(modeCaseInsensitive) result.id = 100 - result.jEntriesFinal = newJArray() + result.jArray = newJArray() initStrTable result.types result.onTestSnippet = - proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) {.gcsafe.} = + proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) = if conf.docCmd == docCmdSkip: return inc(gen.id) - var d = (ptr TDocumentor)(addr gen) + var d = TDocumentor(gen) var outp: AbsoluteFile if filename.len == 0: let nameOnly = splitFile(d.filename).name - # "snippets" needed, refs bug #17183 - outp = getNimcacheDir(conf) / "snippets".RelativeDir / RelativeDir(nameOnly) / + outp = getNimcacheDir(conf) / RelativeDir(nameOnly) / RelativeFile(nameOnly & "_snippet_" & $d.id & ".nim") elif isAbsolute(filename): outp = AbsoluteFile(filename) @@ -376,21 +214,17 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, # Make sure the destination directory exists createDir(outp.splitFile.dir) # Include the current file if we're parsing a nim file - let importStmt = if d.standaloneDoc: "" else: "import \"$1\"\n" % [d.filename.replace("\\", "/")] + let importStmt = if d.isPureRst: "" else: "import \"$1\"\n" % [d.filename.replace("\\", "/")] writeFile(outp, importStmt & content) proc interpSnippetCmd(cmd: string): string = # backward compatibility hacks; interpolation commands should explicitly use `$` if cmd.startsWith "nim ": result = "$nim " & cmd[4..^1] else: result = cmd - # factor with D20210224T221756 result = result.replace("$1", "$options") % [ "nim", os.getAppFilename().quoteShell, - "libpath", quoteShell(d.conf.libpath), - "docCmd", d.conf.docCmd, "backend", $d.conf.backend, "options", outp.quoteShell, - # xxx `quoteShell` seems buggy if user passes options = "-d:foo somefile.nim" ] let cmd = cmd.interpSnippetCmd rawMessage(conf, hintExecuting, cmd) @@ -398,12 +232,11 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, if gotten != status: rawMessage(conf, errGenerated, "snippet failed: cmd: '$1' status: $2 expected: $3 output: $4" % [cmd, $gotten, $status, output]) result.emitted = initIntSet() - result.destFile = destFile + result.destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false).string result.thisDir = result.destFile.AbsoluteFile.splitFile.dir -template dispA(conf: ConfigRef; dest: var string, xml, tex: string, - args: openArray[string]) = - if not conf.isLatexCmd: dest.addf(xml, args) +template dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) = + if conf.cmd != cmdRst2tex: dest.addf(xml, args) else: dest.addf(tex, args) proc getVarIdx(varnames: openArray[string], id: string): int = @@ -412,30 +245,91 @@ proc getVarIdx(varnames: openArray[string], id: string): int = return i result = -1 -proc genComment(d: PDoc, n: PNode): PRstNode = - if n.comment.len > 0: - d.sharedState.currFileIdx = addRstFileIndex(d, n.info) - result = parseRst(n.comment, - toLinenumber(n.info), - toColumn(n.info) + DocColOffset, - d.conf, d.sharedState) +proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr, + varnames: openArray[string], + varvalues: openArray[Rope]): Rope = + var i = 0 + result = nil + var num = 0 + while i < frmt.len: + if frmt[i] == '$': + inc(i) # skip '$' + case frmt[i] + of '#': + result.add(varvalues[num]) + inc(num) + inc(i) + of '$': + result.add("$") + inc(i) + of '0'..'9': + var j = 0 + while true: + j = (j * 10) + ord(frmt[i]) - ord('0') + inc(i) + if (i > frmt.len + 0 - 1) or not (frmt[i] in {'0'..'9'}): break + if j > high(varvalues) + 1: + rawMessage(conf, errGenerated, "Invalid format string; too many $s: " & frmt) + num = j + result.add(varvalues[j - 1]) + of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': + var id = "" + while true: + id.add(frmt[i]) + inc(i) + if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break + var idx = getVarIdx(varnames, id) + if idx >= 0: result.add(varvalues[idx]) + else: rawMessage(conf, errGenerated, "unknown substition variable: " & id) + of '{': + var id = "" + inc(i) + while i < frmt.len and frmt[i] != '}': + id.add(frmt[i]) + inc(i) + if i >= frmt.len: + rawMessage(conf, errGenerated, "expected closing '}'") + else: + inc(i) # skip } + # search for the variable: + let idx = getVarIdx(varnames, id) + if idx >= 0: result.add(varvalues[idx]) + else: rawMessage(conf, errGenerated, "unknown substition variable: " & id) + else: + result.add("$") + var start = i + while i < frmt.len: + if frmt[i] != '$': inc(i) + else: break + if i - 1 >= start: result.add(substr(frmt, start, i - 1)) -proc genRecCommentAux(d: PDoc, n: PNode): PRstNode = +proc genComment(d: PDoc, n: PNode): string = + result = "" + if n.comment.len > 0: + let comment = n.comment + when false: + # RFC: to preseve newlines in comments, this would work: + comment = comment.replace("\n", "\n\n") + renderRstToOut(d[], parseRst(comment, toFullPath(d.conf, n.info), toLinenumber(n.info), + toColumn(n.info), (var dummy: bool; dummy), d.options, d.conf), result) + +proc genRecCommentAux(d: PDoc, n: PNode): Rope = if n == nil: return nil - result = genComment(d, n) + result = genComment(d, n).rope if result == nil: if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, - nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkSinkAsgn, nkHiddenStdConv}: + nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkHiddenStdConv}: # notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}: 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) @@ -465,16 +362,15 @@ proc externalDep(d: PDoc; module: PSym): string = else: result = extractFilename toFullPath(d.conf, FileIndex module.position) -proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string; - renderFlags: TRenderFlags = {}; - procLink: string) = +proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRenderFlags = {}; + procLink: Rope) = var r: TSrcGen var literal = "" initTokRender(r, n, renderFlags) var kind = tkEof var tokenPos = 0 var procTokenPos = 0 - template escLit(): untyped = esc(d.target, literal) + template escLit(): untyped = rope(esc(d.target, literal)) while true: getNextTok(r, kind, literal) inc tokenPos @@ -488,11 +384,11 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string; if kind in {tkProc, tkMethod, tkIterator, tkMacro, tkTemplate, tkFunc, tkConverter}: procTokenPos = tokenPos dispA(d.conf, result, "$1", "\\spanKeyword{$1}", - [literal]) + [rope(literal)]) of tkOpr: dispA(d.conf, result, "$1", "\\spanOperator{$1}", [escLit]) - of tkStrLit..tkTripleStrLit, tkCustomLit: + of tkStrLit..tkTripleStrLit: dispA(d.conf, result, "$1", "\\spanStringLit{$1}", [escLit]) of tkCharLit: @@ -507,37 +403,37 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string; of tkSymbol: let s = getTokSym(r) # -2 because of the whitespace in between: - if procTokenPos == tokenPos-2 and procLink != "": + if procTokenPos == tokenPos-2 and procLink != nil: dispA(d.conf, result, "$1", "\\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 - belongsToProjectPackage(d.conf, s.owner) and d.target == outHtml: + belongsToPackage(d.conf, s.owner) and d.target == outHtml: let external = externalDep(d, s.owner) result.addf "$3", - [changeFileExt(external, "html"), literal, + [rope changeFileExt(external, "html"), rope literal, escLit] else: dispA(d.conf, result, "$1", "\\spanIdentifier{$1}", [escLit]) of tkSpaces, tkInvalid: result.add(literal) - of tkHideableStart: + of tkCurlyDotLe: template fun(s) = dispA(d.conf, result, s, "\\spanOther{$1}", [escLit]) if renderRunnableExamples in renderFlags: fun "$1" - else: - # 1st span is required for the JS to work properly - fun """ - -... + else: fun: "" & # This span is required for the JS to work properly + """{...} -""".replace("\n", "") # Must remove newlines because wrapped in a
-    of tkHideableEnd:
+
+$1
+""".replace("\n", "")  # Must remove newlines because wrapped in a 
+    of tkCurlyDotRi:
       template fun(s) = dispA(d.conf, result, s, "\\spanOther{$1}", [escLit])
       if renderRunnableExamples in renderFlags: fun "$1"
-      else: fun ""
-    of tkCurlyDotLe: dispA(d.conf, result, "$1", "\\spanOther{$1}", [escLit])
-    of tkCurlyDotRi: dispA(d.conf, result, "$1", "\\spanOther{$1}", [escLit])
+      else: fun """
+
+$1
+""".replace("\n", "")
     of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
        tkBracketDotLe, tkBracketDotRi, tkParDotLe,
        tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
@@ -549,6 +445,18 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
 
 proc exampleOutputDir(d: PDoc): AbsoluteDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
 
+proc writeExample(d: PDoc; ex: PNode, rdoccmd: string) =
+  if d.conf.errorCounter > 0: return
+  let outputDir = d.exampleOutputDir
+  createDir(outputDir)
+  inc d.exampleCounter
+  let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
+      "_examples" & $d.exampleCounter & ".nim"))
+  #let nimcache = outp.changeFileExt"" & "_nimcache"
+  renderModule(ex, d.filename, outp.string, conf = d.conf)
+  if rdoccmd notin d.exampleGroups: d.exampleGroups[rdoccmd] = ExampleGroup(rdoccmd: rdoccmd, docCmd: d.conf.docCmd, index: d.exampleGroups.len)
+  d.exampleGroups[rdoccmd].code.add "import r\"$1\"\n" % outp.string
+
 proc runAllExamples(d: PDoc) =
   # This used to be: `let backend = if isDefined(d.conf, "js"): "js"` (etc), however
   # using `-d:js` (etc) cannot work properly, e.g. would fail with `importjs`
@@ -561,9 +469,8 @@ proc runAllExamples(d: PDoc) =
     writeFile(outp, group.code)
     # most useful semantics is that `docCmd` comes after `rdoccmd`, so that we can (temporarily) override
     # via command line
-    # D20210224T221756:here
     let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off --path:$path --nimcache:$nimcache $rdoccmd $docCmd $file" % [
-      "nim", quoteShell(os.getAppFilename()),
+      "nim", os.getAppFilename(),
       "backend", $d.conf.backend,
       "path", quoteShell(d.conf.projectPath),
       "libpath", quoteShell(d.conf.libpath),
@@ -572,22 +479,14 @@ proc runAllExamples(d: PDoc) =
       "rdoccmd", group.rdoccmd,
       "docCmd", group.docCmd,
     ]
-    if d.conf.backend == backendJs and findNodeJs() == "":
-      discard "ignore JS runnableExample"
-    elif os.execShellCmd(cmd) != 0:
-      d.conf.quitOrRaise "[runnableExamples] failed: generated file: '$1' group: '$2' cmd: $3" % [outp.string, group[].prettyString, cmd]
+    if os.execShellCmd(cmd) != 0:
+      quit "[runnableExamples] failed: generated file: '$1' group: '$2' cmd: $3" % [outp.string, $group[], cmd]
     else:
       # keep generated source file `outp` to allow inspection.
       rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
       # removeFile(outp.changeFileExt(ExeExt)) # it's in nimcache, no need to remove
 
-proc quoted(a: string): string = result.addQuoted(a)
-
-proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): (string, int, int) =
-  # xxx expose in compiler/lineinfos.nim
-  (conf.toMsgFilename(info), info.line.int, info.col.int + ColOffset)
-
-proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, code: string] =
+proc prepareExample(d: PDoc; n: PNode): tuple[rdoccmd: string, code: string] =
   ## returns `rdoccmd` and source code for this runnableExamples
   var rdoccmd = ""
   if n.len < 2 or n.len > 3: globalError(d.conf, n.info, "runnableExamples invalid")
@@ -597,64 +496,19 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
     if n1.kind notin nkStrKinds: globalError(d.conf, n1.info, "string litteral expected")
     rdoccmd = n1.strVal
 
-  let useRenderModule = false
+  var docComment = newTree(nkCommentStmt)
   let loc = d.conf.toFileLineCol(n.info)
-  let code = extractRunnableExamplesSource(d.conf, n)
-  let codeIndent = extractRunnableExamplesSource(d.conf, n, indent = 2)
 
-  if d.conf.errorCounter > 0:
-    return (rdoccmd, code)
-
-  let comment = "autogenerated by docgen\nloc: $1\nrdoccmd: $2" % [loc, rdoccmd]
-  let outputDir = d.exampleOutputDir
-  createDir(outputDir)
-  inc d.exampleCounter
-  let outp = outputDir / RelativeFile("$#_examples_$#.nim" % [d.filename.extractFilename.changeFileExt"", $d.exampleCounter])
-
-  if useRenderModule:
-    var docComment = newTree(nkCommentStmt)
-    docComment.comment = comment
-    var runnableExamples = newTree(nkStmtList,
-        docComment,
-        newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
-    runnableExamples.info = n.info
-    for a in n.lastSon: runnableExamples.add a
-
-    # buggy, refs bug #17292
-    # still worth fixing as it can affect other code relying on `renderModule`,
-    # so we keep this code path here for now, which could still be useful in some
-    # other situations.
-    renderModule(runnableExamples, outp.string, conf = d.conf)
-
-  else:
-    var code2 = code
-    if code.len > 0 and "codeReordering" notin code:
-      # hacky but simplest solution, until we devise a way to make `{.line.}`
-      # work without introducing a scope
-      code2 = """
-{.line: $#.}:
-$#
-""" % [$toInstantiationInfo(d.conf, n.info), codeIndent]
-    code2 = """
-#[
-$#
-]#
-import $#
-$#
-""" % [comment, d.filename.quoted, code2]
-    writeFile(outp.string, code2)
-
-  if rdoccmd notin d.exampleGroups:
-    d.exampleGroups[rdoccmd] = ExampleGroup(rdoccmd: rdoccmd, docCmd: d.conf.docCmd, index: d.exampleGroups.len)
-  d.exampleGroups[rdoccmd].code.add "import $1\n" % outp.string.quoted
-
-  var codeShown: string
-  if topLevel: # refs https://github.com/nim-lang/RFCs/issues/352
-    let title = canonicalImport(d.conf, AbsoluteFile d.filename)
-    codeShown = "import $#\n$#" % [title, code]
-  else:
-    codeShown = code
-  result = (rdoccmd, codeShown)
+  docComment.comment = "autogenerated by docgen\nloc: $1\nrdoccmd: $2" % [loc, rdoccmd]
+  var runnableExamples = newTree(nkStmtList,
+      docComment,
+      newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
+  runnableExamples.info = n.info
+  let ret = extractRunnableExamplesSource(d.conf, n)
+  for a in n.lastSon: runnableExamples.add a
+  # we could also use `ret` instead here, to keep sources verbatim
+  writeExample(d, runnableExamples, rdoccmd)
+  result = (rdoccmd, ret)
   when false:
     proc extractImports(n: PNode; result: PNode) =
       if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
@@ -674,9 +528,7 @@ type RunnableState = enum
   rsRunnable
   rsDone
 
-proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var ItemPre,
-                                state: RunnableState, topLevel: bool):
-                               RunnableState =
+proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var Rope, state: RunnableState): RunnableState =
   ##[
   Simple state machine to tell whether we render runnableExamples and doc comments.
   This is to ensure that we can interleave runnableExamples and doc comments freely;
@@ -700,25 +552,20 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var ItemPre,
       return rsComment
   of nkCallKinds:
     if isRunnableExamples(n[0]) and
-        n.len >= 2 and n.lastSon.kind == nkStmtList:
-      if state in {rsStart, rsComment, rsRunnable}:
-        let (rdoccmd, code) = prepareExample(d, n, topLevel)
-        var msg = "Example:"
-        if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd
-        var s: string
-        dispA(d.conf, s, "\n

$1

\n", - "\n\n\\textbf{$1}\n", [msg]) - dest.add s - inc d.listingCounter - let id = $d.listingCounter - dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim", ""]) - var dest2 = "" - renderNimCode(dest2, code, d.target) - dest.add dest2 - dest.add(d.config.getOrDefault"doc.listing_end" % id) - return rsRunnable - else: - localError(d.conf, n.info, errUser, "runnableExamples must appear before the first non-comment statement") + n.len >= 2 and n.lastSon.kind == nkStmtList and state in {rsStart, rsComment, rsRunnable}: + let (rdoccmd, code) = prepareExample(d, n) + var msg = "Example:" + if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd + dispA(d.conf, dest, "\n

$1

\n", + "\n\\textbf{$1}\n", [msg.rope]) + inc d.listingCounter + let id = $d.listingCounter + dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim"]) + var dest2 = "" + renderNimCode(dest2, code, isLatex = d.conf.cmd == cmdRst2tex) + dest.add dest2 + dest.add(d.config.getOrDefault"doc.listing_end" % id) + return rsRunnable else: discard return rsDone # change this to `rsStart` if you want to keep generating doc comments @@ -754,22 +601,22 @@ proc getRoutineBody(n: PNode): PNode = doAssert result.len == 2 result = result[1] -proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var ItemPre) = +proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var Rope) = var n = n var state = rsStart - template fn(n2, topLevel) = - state = getAllRunnableExamplesImpl(d, n2, dest, state, topLevel) - dest.add genComment(d, n) + template fn(n2) = + state = getAllRunnableExamplesImpl(d, n2, dest, state) + dest.add genComment(d, n).rope case n.kind of routineDefs: n = n.getRoutineBody case n.kind - of nkCommentStmt, nkCallKinds: fn(n, topLevel = false) + of nkCommentStmt, nkCallKinds: fn(n) else: for i in 0.. 0: - result.setLen(pos - 1) + result.delete(pos, result.len - 1) result.add("…") if pos < maxDocstringChars: return @@ -906,21 +758,23 @@ proc docstringSummary(rstText: string): string = pos = result.find({'.', ',', ':'}) let last = result.len - 1 if pos > 0 and pos < last: - result.setLen(pos - 1) + result.delete(pos, last) result.add("…") -proc genDeprecationMsg(d: PDoc, n: PNode): string = +proc genDeprecationMsg(d: PDoc, n: PNode): Rope = ## Given a nkPragma wDeprecated node output a well-formatted section if n == nil: return case n.safeLen: of 0: # Deprecated w/o any message - result = getConfigVar(d.conf, "doc.deprecationmsg") % [ - "label" , "Deprecated", "message", ""] + result = ropeFormatNamedVars(d.conf, + getConfigVar(d.conf, "doc.deprecationmsg"), ["label", "message"], + [~"Deprecated", nil]) of 2: # Deprecated w/ a message if n[1].kind in {nkStrLit..nkTripleStrLit}: - result = getConfigVar(d.conf, "doc.deprecationmsg") % [ - "label", "Deprecated:", "message", xmltree.escape(n[1].strVal)] + result = ropeFormatNamedVars(d.conf, + getConfigVar(d.conf, "doc.deprecationmsg"), ["label", "message"], + [~"Deprecated:", rope(xmltree.escape(n[1].strVal))]) else: doAssert false @@ -928,7 +782,7 @@ type DocFlags = enum kDefault kForceExport -proc genSeeSrc(d: PDoc, path: string, line: int): string = +proc genSeeSrcRope(d: PDoc, path: string, line: int): Rope = let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc") if docItemSeeSrc.len > 0: let path = relativeTo(AbsoluteFile path, AbsoluteDir getCurrentDir(), '/') @@ -944,80 +798,20 @@ proc genSeeSrc(d: PDoc, path: string, line: int): string = else: "version-$1-$2" % [$NimMajor, $NimMinor] let commit = getConfigVar(d.conf, "git.commit", defaultBranch) let develBranch = getConfigVar(d.conf, "git.devel", "devel") - dispA(d.conf, result, "$1", "", [docItemSeeSrc % [ - "path", path.string, "line", $line, "url", gitUrl, - "commit", commit, "devel", develBranch]]) + dispA(d.conf, result, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc, + ["path", "line", "url", "commit", "devel"], [rope path.string, + rope($line), rope gitUrl, rope commit, rope develBranch])]) -proc symbolPriority(k: TSymKind): int = - result = case k - of skMacro: -3 - of skTemplate: -2 - of skIterator: -1 - else: 0 # including skProc which have higher priority - # documentation itself has even higher priority 1 - -proc getTypeKind(n: PNode): string = - case n[2].kind - of nkEnumTy: "enum" - of nkObjectTy: "object" - of nkTupleTy: "tuple" - else: "" - -proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol = - ## Converts symbol info (names/types/parameters) in `n` into format - ## `LangSymbol` convenient for ``rst.nim``/``dochelpers.nim``. - result.name = baseName.nimIdentNormalize - result.symKind = k.toHumanStr - if k in routineKinds: - var - paramTypes: seq[string] - renderParamTypes(paramTypes, n[paramsPos], toNormalize=true) - let paramNames = renderParamNames(n[paramsPos], toNormalize=true) - # In some rare cases (system.typeof) parameter type is not set for default: - doAssert paramTypes.len <= paramNames.len - for i in 0 ..< paramNames.len: - if i < paramTypes.len: - result.parameters.add (paramNames[i], paramTypes[i]) - else: - result.parameters.add (paramNames[i], "") - result.parametersProvided = true - - result.outType = renderOutType(n[paramsPos], toNormalize=true) - - if k in {skProc, skFunc, skType, skIterator}: - # Obtain `result.generics` - # Use `n[miscPos]` since n[genericParamsPos] does not contain constraints - var genNode: PNode = nil - if k == skType: - genNode = n[1] # FIXME: what is index 1? - else: - if n[miscPos].kind != nkEmpty: - genNode = n[miscPos][1] # FIXME: what is index 1? - if genNode != nil: - var literal = "" - var r: TSrcGen - initTokRender(r, genNode, {renderNoBody, renderNoComments, - renderNoPragmas, renderNoProcDefs, renderExpandUsing}) - var kind = tkEof - while true: - getNextTok(r, kind, literal) - if kind == tkEof: - break - if kind != tkSpaces: - result.generics.add(literal.nimIdentNormalize) - - if k == skType: result.symTypeKind = getTypeKind(n) - -proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonExports: bool = false) = +proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = if (docFlags != kForceExport) and not isVisible(d, nameNode): return let - name = getName(nameNode) - nameEsc = esc(d.target, name) + name = getName(d, nameNode) + nameRope = name.rope var plainDocstring = getPlainDocstring(n) # call here before genRecComment! - var result = "" + var result: Rope = nil var literal, plainName = "" var kind = tkEof - var comm: ItemPre + var comm: Rope = nil if n.kind in routineDefs: getAllRunnableExamples(d, n, comm) else: @@ -1026,123 +820,97 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx var r: TSrcGen # Obtain the plain rendered string for hyperlink titles. initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments, - renderNoPragmas, renderNoProcDefs, renderExpandUsing}) + renderNoPragmas, renderNoProcDefs}) while true: getNextTok(r, kind, literal) if kind == tkEof: break plainName.add(literal) - var pragmaNode = getDeclPragma(n) - if pragmaNode != nil: pragmaNode = findPragma(pragmaNode, wDeprecated) + var pragmaNode: PNode = nil + if n.isCallable and n[pragmasPos].kind != nkEmpty: + pragmaNode = findPragma(n[pragmasPos], wDeprecated) inc(d.id) let - plainNameEsc = esc(d.target, plainName.strip) - typeDescr = - if k == skType and getTypeKind(n) != "": getTypeKind(n) - else: k.toHumanStr - detailedName = typeDescr & " " & ( - if k in routineKinds: plainName else: name) - uniqueName = if k in routineKinds: plainNameEsc else: nameEsc - sortName = if k in routineKinds: plainName.strip else: name + plainNameRope = rope(xmltree.escape(plainName.strip)) cleanPlainSymbol = renderPlainSymbolName(nameNode) complexSymbol = complexName(k, n, cleanPlainSymbol) - plainSymbolEnc = encodeUrl(cleanPlainSymbol, usePlus = false) + plainSymbolRope = rope(cleanPlainSymbol) + plainSymbolEncRope = rope(encodeUrl(cleanPlainSymbol)) + itemIDRope = rope(d.id) symbolOrId = d.newUniquePlainSymbol(complexSymbol) - symbolOrIdEnc = encodeUrl(symbolOrId, usePlus = false) - deprecationMsg = genDeprecationMsg(d, pragmaNode) - rstLangSymbol = toLangSymbol(k, n, cleanPlainSymbol) + symbolOrIdRope = symbolOrId.rope + symbolOrIdEncRope = encodeUrl(symbolOrId).rope + deprecationMsgRope = genDeprecationMsg(d, pragmaNode) - # we generate anchors automatically for subsequent use in doc comments - let lineinfo = rstast.TLineInfo( - line: nameNode.info.line, col: nameNode.info.col, - fileIndex: addRstFileIndex(d, nameNode.info)) - addAnchorNim(d.sharedState, external = false, refn = symbolOrId, - tooltip = detailedName, langSym = rstLangSymbol, - priority = symbolPriority(k), info = lineinfo) + nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments, + renderDocComments, renderSyms}, symbolOrIdEncRope) - let renderFlags = - if nonExports: {renderNoBody, renderNoComments, renderDocComments, renderSyms, - renderExpandUsing, renderNonExportedFields} - else: {renderNoBody, renderNoComments, renderDocComments, renderSyms, renderExpandUsing} - nodeToHighlightedHtml(d, n, result, renderFlags, symbolOrIdEnc) - - let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int) - - d.section[k].secItems.mgetOrPut(cleanPlainSymbol, newSeq[Item]()).add Item( - descRst: comm, - sortName: sortName, - info: lineinfo, - anchor: symbolOrId, - detailedName: detailedName, - name: name, - substitutions: @[ - "uniqueName", uniqueName, - "header", result, "itemID", $d.id, - "header_plain", plainNameEsc, "itemSym", cleanPlainSymbol, - "itemSymEnc", plainSymbolEnc, - "itemSymOrIDEnc", symbolOrIdEnc, "seeSrc", seeSrc, - "deprecationMsg", deprecationMsg]) + let seeSrcRope = genSeeSrcRope(d, toFullPath(d.conf, n.info), n.info.line.int) + d.section[k].add(ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"), + ["name", "header", "desc", "itemID", "header_plain", "itemSym", + "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc", "deprecationMsg"], + [nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope, + symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope, + deprecationMsgRope])) let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').changeFileExt(HtmlExt).string - var attype = "" + var attype: Rope if k in routineKinds and nameNode.kind == nkSym: let att = attachToType(d, nameNode.sym) if att != nil: - attype = esc(d.target, att.name.s) + attype = rope esc(d.target, att.name.s) elif k == skType and nameNode.kind == nkSym and nameNode.sym.typ.kind in {tyEnum, tyBool}: let etyp = nameNode.sym.typ for e in etyp.n: if e.sym.kind != skEnumField: continue let plain = renderPlainSymbolName(e) let symbolOrId = d.newUniquePlainSymbol(plain) - setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId, - term = plain, linkTitle = nameNode.sym.name.s & '.' & plain, - linkDesc = xmltree.escape(getPlainDocstring(e).docstringSummary), - line = n.info.line.int) + setIndexTerm(d[], external, symbolOrId, plain, nameNode.sym.name.s & '.' & plain, + xmltree.escape(getPlainDocstring(e).docstringSummary)) - d.tocSimple[k].add TocItem( - sortName: sortName, - content: getConfigVar(d.conf, "doc.item.toc") % [ - "name", name, "header_plain", plainNameEsc, - "itemSymOrIDEnc", symbolOrIdEnc]) + d.toc[k].add(ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item.toc"), + ["name", "header_plain", "itemSymOrIDEnc"], + [nameRope, plainNameRope, symbolOrIdEncRope])) - d.tocTable[k].mgetOrPut(cleanPlainSymbol, newSeq[TocItem]()).add TocItem( - sortName: sortName, - content: getConfigVar(d.conf, "doc.item.tocTable") % [ - "name", name, "header_plain", plainNameEsc, - "itemSymOrID", symbolOrId.replace(",", ","), - "itemSymOrIDEnc", symbolOrIdEnc]) + d.tocTable[k].mgetOrPut(cleanPlainSymbol, nil).add(ropeFormatNamedVars( + d.conf, getConfigVar(d.conf, "doc.item.tocTable"), + ["name", "header_plain", "itemSymOrID", "itemSymOrIDEnc"], + [nameRope, plainNameRope, rope(symbolOrId.replace(",", ",")), symbolOrIdEncRope])) - setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId, term = name, - linkTitle = detailedName, - linkDesc = xmltree.escape(plainDocstring.docstringSummary), - line = n.info.line.int) + # Ironically for types the complexSymbol is *cleaner* than the plainName + # because it doesn't include object fields or documentation comments. So we + # use the plain one for callable elements, and the complex for the rest. + var linkTitle = changeFileExt(extractFilename(d.filename), "") & ": " + if n.isCallable: linkTitle.add(xmltree.escape(plainName.strip)) + else: linkTitle.add(xmltree.escape(complexSymbol.strip)) + + setIndexTerm(d[], external, symbolOrId, name, linkTitle, + xmltree.escape(plainDocstring.docstringSummary)) if k == skType and nameNode.kind == nkSym: d.types.strTableAdd nameNode.sym -proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem = +proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = if not isVisible(d, nameNode): return var - name = getNameEsc(d, nameNode) - comm = genRecComment(d, n) + name = getName(d, nameNode) + comm = $genRecComment(d, n) r: TSrcGen - initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing}) - result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int, - "col": %n.info.col} - if comm != nil: - result.rst = comm - result.rstField = "description" + initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) + result = %{ "name": %name, "type": %($k), "line": %n.info.line.int, + "col": %n.info.col} + if comm.len > 0: + result["description"] = %comm if r.buf.len > 0: - result.json["code"] = %r.buf + result["code"] = %r.buf if k in routineKinds: - result.json["signature"] = newJObject() + result["signature"] = newJObject() if n[paramsPos][0].kind != nkEmpty: - result.json["signature"]["return"] = %($n[paramsPos][0]) + result["signature"]["return"] = %($n[paramsPos][0]) if n[paramsPos].len > 1: - result.json["signature"]["arguments"] = newJArray() + result["signature"]["arguments"] = newJArray() for paramIdx in 1 ..< n[paramsPos].len: for identIdx in 0 ..< n[paramsPos][paramIdx].len - 2: let @@ -1150,62 +918,22 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem = paramType = $n[paramsPos][paramIdx][^2] if n[paramsPos][paramIdx][^1].kind != nkEmpty: let paramDefault = $n[paramsPos][paramIdx][^1] - result.json["signature"]["arguments"].add %{"name": %paramName, "type": %paramType, "default": %paramDefault} + result["signature"]["arguments"].add %{"name": %paramName, "type": %paramType, "default": %paramDefault} else: - result.json["signature"]["arguments"].add %{"name": %paramName, "type": %paramType} + result["signature"]["arguments"].add %{"name": %paramName, "type": %paramType} if n[pragmasPos].kind != nkEmpty: - result.json["signature"]["pragmas"] = newJArray() + result["signature"]["pragmas"] = newJArray() for pragma in n[pragmasPos]: - result.json["signature"]["pragmas"].add %($pragma) + result["signature"]["pragmas"].add %($pragma) if n[genericParamsPos].kind != nkEmpty: - result.json["signature"]["genericParams"] = newJArray() + result["signature"]["genericParams"] = newJArray() for genericParam in n[genericParamsPos]: var param = %{"name": %($genericParam)} if genericParam.sym.typ.sons.len > 0: param["types"] = newJArray() 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] - ) + result["signature"]["genericParams"].add param proc checkForFalse(n: PNode): bool = result = n.kind == nkIdent and cmpIgnoreStyle(n.ident.s, "false") == 0 @@ -1222,37 +950,37 @@ proc traceDeps(d: PDoc, it: PNode) = for x in it[2]: a[2] = x traceDeps(d, a) - elif it.kind == nkSym and belongsToProjectPackage(d.conf, it.sym): + elif it.kind == nkSym and belongsToPackage(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, + if d.section[k] != nil: d.section[k].add(", ") + dispA(d.conf, d.section[k], "$1", - "$1", [esc(d.target, external.prettyLink), - changeFileExt(external, "html")]) + "$1", [rope esc(d.target, external.prettyLink), + rope changeFileExt(external, "html")]) proc exportSym(d: PDoc; s: PSym) = const k = exportSection - if s.kind == skModule and belongsToProjectPackage(d.conf, s): + if s.kind == skModule and belongsToPackage(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, + if d.section[k] != nil: d.section[k].add(", ") + dispA(d.conf, d.section[k], "$1", - "$1", [esc(d.target, external.prettyLink), - changeFileExt(external, "html")]) + "$1", [rope esc(d.target, external.prettyLink), + rope changeFileExt(external, "html")]) elif s.kind != skModule and s.owner != nil: let module = originatingModule(s) - if belongsToProjectPackage(d.conf, module): + if belongsToPackage(d.conf, module): let complexSymbol = complexName(s.kind, s.ast, s.name.s) - symbolOrId = d.newUniquePlainSymbol(complexSymbol) + symbolOrIdRope = rope(d.newUniquePlainSymbol(complexSymbol)) external = externalDep(d, module) - if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ") + if d.section[k] != nil: d.section[k].add(", ") # XXX proper anchor generation here - dispA(d.conf, d.section[k].finalMarkup, + dispA(d.conf, d.section[k], "$1", - "$1", [esc(d.target, s.name.s), - changeFileExt(external, "html"), - symbolOrId]) + "$1", [rope esc(d.target, s.name.s), + rope changeFileExt(external, "html"), + symbolOrIdRope]) proc documentNewEffect(cache: IdentCache; n: PNode): PNode = let s = n[namePos].sym @@ -1267,11 +995,10 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id let actual = s.typ.n[0] if actual.len != effectListLen: return let real = actual[idx] - if real == nil: return - let realLen = real.len + # warning: hack ahead: - var effects = newNodeI(nkBracket, n.info, realLen) - for i in 0.. 1: - let refn = overloadGroupName(plainName, k) - let tooltip = "$1 ($2 overloads)" % [ - k.toHumanStr & " " & plainName, $overloadChoices.len] - let name = nimIdentBackticksNormalize(plainName) - # save overload group to ``.idx`` - let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/'). - changeFileExt(HtmlExt).string - setIndexTerm(d[], ieNimGroup, htmlFile = external, id = refn, - term = name, linkTitle = k.toHumanStr, - linkDesc = "", line = overloadChoices[0].info.line.int) - if optGenIndexOnly in d.conf.globalOptions: continue - addAnchorNim(d.sharedState, external=false, refn, tooltip, - LangSymbol(symKind: k.toHumanStr, - name: name, - isGroup: true), - priority = symbolPriority(k), - # select index `0` just to have any meaningful warning: - info = overloadChoices[0].info) - - if optGenIndexOnly in d.conf.globalOptions: - return - - # Finalize fragments of ``.nim`` or ``.rst`` file - proc renderItemPre(d: PDoc, fragments: ItemPre, result: var string) = - for f in fragments: - case f.isRst: - of true: - var resolved = resolveSubs(d.sharedState, f.rst) - renderRstToOut(d[], resolved, result) - of false: result &= f.str - proc cmp(x, y: Item): int = cmpDecimalsIgnoreCase(x.sortName, y.sortName) - for k in TSymKind: - # add symbols to section for each `k`, while optionally wrapping - # overloadable items with the same basic name by ``doc.item2`` - let overloadableNames = toSeq(keys(d.section[k].secItems)) - for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase): - var overloadChoices = d.section[k].secItems[plainName] - overloadChoices.sort(cmp) - var nameContent = "" - for item in overloadChoices: - var itemDesc: string - renderItemPre(d, item.descRst, itemDesc) - nameContent.add( - getConfigVar(d.conf, "doc.item") % ( - item.substitutions & @[ - "desc", itemDesc, - "name", item.name, - "itemSymOrID", item.anchor])) - if k in routineKinds: - let plainNameEsc1 = esc(d.target, plainName.strip) - let plainNameEsc2 = esc(d.target, plainName.strip, escMode=emUrl) - d.section[k].finalMarkup.add( - getConfigVar(d.conf, "doc.item2") % ( - @["header_plain", plainNameEsc1, - "overloadGroupName", overloadGroupName(plainNameEsc2, k), - "content", nameContent])) - else: - d.section[k].finalMarkup.add(nameContent) - d.section[k].secItems.clear - renderItemPre(d, d.modDescPre, d.modDescFinal) - d.modDescPre.setLen 0 - - # Finalize fragments of ``.json`` file - for i, entry in d.jEntriesPre: - if entry.rst != nil: - let resolved = resolveSubs(d.sharedState, entry.rst) - var str: string - renderRstToOut(d[], resolved, str) - entry.json[entry.rstField] = %str - d.jEntriesPre[i].rst = nil - - d.jEntriesFinal.add entry.json # generates docs - - setIndexTitle(d, useMetaTitle = d.standaloneDoc) - completePass2(d.sharedState) - -proc add(d: PDoc; j: JsonItem) = - if j.json != nil or j.rst != nil: d.jEntriesPre.add j +proc add(d: PDoc; j: JsonNode) = + if j != nil: d.jArray.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", - json: %Table[string, string]()) + d.add %*{"comment": genComment(d, n)} else: - d.modDescPre.add(genComment(d, n)) + d.modDesc.add(genComment(d, n)) of nkProcDef, nkFuncDef: when useEffectSystem: documentRaises(d.cache, n) d.add genJsonItem(d, n, n[namePos], skProc) @@ -1556,9 +1132,9 @@ proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = else: discard proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string = - result = getNameEsc(d, nameNode) & "\n" + result = getName(d, nameNode) & "\n" -proc generateTags*(d: PDoc, n: PNode, r: var string) = +proc generateTags*(d: PDoc, n: PNode, r: var Rope) = case n.kind of nkCommentStmt: if startsWith(n.comment, "##"): @@ -1603,116 +1179,82 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) = "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs", "Methods", "Iterators", "Converters", "Macros", "Templates", "Exports" ] - if d.section[kind].finalMarkup == "": return - var title = sectionNames[kind] - d.section[kind].finalMarkup = getConfigVar(d.conf, "doc.section") % [ - "sectionid", $ord(kind), "sectionTitle", title, - "sectionTitleID", $(ord(kind) + 50), "content", d.section[kind].finalMarkup] + if d.section[kind] == nil: return + var title = sectionNames[kind].rope + d.section[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section"), [ + "sectionid", "sectionTitle", "sectionTitleID", "content"], [ + ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]]) - proc cmp(x, y: TocItem): int = cmpDecimalsIgnoreCase(x.sortName, y.sortName) + var tocSource = d.toc if groupedToc: - let overloadableNames = toSeq(keys(d.tocTable[kind])) - for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase): - var overloadChoices = d.tocTable[kind][plainName] - overloadChoices.sort(cmp) - var content: string - for item in overloadChoices: - content.add item.content - d.toc2[kind].add getConfigVar(d.conf, "doc.section.toc2") % [ - "sectionid", $ord(kind), "sectionTitle", title, - "sectionTitleID", $(ord(kind) + 50), - "content", content, "plainName", plainName] - else: - for item in d.tocSimple[kind].sorted(cmp): - d.toc2[kind].add item.content + for p in d.tocTable[kind].keys: + d.toc2[kind].add ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section.toc2"), [ + "sectionid", "sectionTitle", "sectionTitleID", "content", "plainName"], [ + ord(kind).rope, title, rope(ord(kind) + 50), d.tocTable[kind][p], p.rope]) + tocSource = d.toc2 - let sectionValues = @[ - "sectionID", $ord(kind), "sectionTitleID", $(ord(kind) + 50), - "sectionTitle", title - ] + d.toc[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section.toc"), [ + "sectionid", "sectionTitle", "sectionTitleID", "content"], [ + ord(kind).rope, title, rope(ord(kind) + 50), tocSource[kind]]) - # 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): Rope = + rope($relativeTo(outDir / linkto, destFile.splitFile().dir, '/')) -proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): string = - $relativeTo(outDir / linkto, destFile.splitFile().dir, '/') - -proc genOutFile(d: PDoc, groupedToc = false): string = +proc genOutFile(d: PDoc, groupedToc = false): Rope = var - code, content: string + code, content: Rope title = "" var j = 0 - var toc = "" - renderTocEntries(d[], j, 1, toc) + var tmp = "" + renderTocEntries(d[], j, 1, tmp) + var toc = tmp.rope for i in TSymKind: - var shouldSort = i in routineKinds and groupedToc + var shouldSort = i in {skProc, skFunc} and groupedToc genSection(d, i, shouldSort) toc.add(d.toc[i]) - if toc != "" or d.target == outLatex: - # for Latex $doc.toc will automatically generate TOC if `d.hasToc` is set - toc = getConfigVar(d.conf, "doc.toc") % ["content", toc] - for i in TSymKind: code.add(d.section[i].finalMarkup) + if toc != nil: + toc = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.toc"), ["content"], [toc]) + for i in TSymKind: code.add(d.section[i]) # Extract the title. Non API modules generate an entry in the index table. if d.meta[metaTitle].len != 0: title = d.meta[metaTitle] + let external = presentationPath(d.conf, AbsoluteFile d.filename).changeFileExt(HtmlExt).string.nativeToUnixPath + setIndexTerm(d[], external, "", title) else: - title = canonicalImport(d.conf, AbsoluteFile d.filename) - title = esc(d.target, title) - var subtitle = "" - if d.meta[metaSubtitle] != "": - dispA(d.conf, subtitle, "

$1

", - "\\\\\\vspace{0.5em}\\large $1", [esc(d.target, d.meta[metaSubtitle])]) + # Modules get an automatic title for the HTML, but no entry in the index. + # better than `extractFilename(changeFileExt(d.filename, ""))` as it disambiguates dups + title = $presentationPath(d.conf, AbsoluteFile d.filename, isTitle = true).changeFileExt("") var groupsection = getConfigVar(d.conf, "doc.body_toc_groupsection") - let bodyname = if d.hasToc and not d.standaloneDoc and not d.conf.isLatexCmd: + let bodyname = if d.hasToc and not d.isPureRst: groupsection.setLen 0 "doc.body_toc_group" elif d.hasToc: "doc.body_toc" else: "doc.body_no_toc" - let seeSrc = genSeeSrc(d, d.filename, 1) - content = getConfigVar(d.conf, bodyname) % [ - "title", title, "subtitle", subtitle, - "tableofcontents", toc, "moduledesc", d.modDescFinal, "date", getDateStr(), - "time", getClockStr(), "content", code, - "deprecationMsg", d.modDeprecationMsg, - "theindexhref", relLink(d.conf.outDir, d.destFile.AbsoluteFile, - theindexFname.RelativeFile), - "body_toc_groupsection", groupsection, "seeSrc", seeSrc] + let seeSrcRope = genSeeSrcRope(d, d.filename, 1) + content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title", + "tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg", "theindexhref", "body_toc_groupsection", "seeSrc"], + [title.rope, toc, d.modDesc, rope(getDateStr()), + rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile.AbsoluteFile, theindexFname.RelativeFile), groupsection.rope, seeSrcRope]) if optCompileOnly notin d.conf.globalOptions: # XXX what is this hack doing here? 'optCompileOnly' means raw output!? - code = getConfigVar(d.conf, "doc.file") % [ - "nimdoccss", relLink(d.conf.outDir, d.destFile.AbsoluteFile, - nimdocOutCss.RelativeFile), - "dochackjs", relLink(d.conf.outDir, d.destFile.AbsoluteFile, - docHackJsFname.RelativeFile), - "title", title, "subtitle", subtitle, "tableofcontents", toc, - "moduledesc", d.modDescFinal, "date", getDateStr(), "time", getClockStr(), - "content", content, "author", d.meta[metaAuthor], - "version", esc(d.target, d.meta[metaVersion]), "analytics", d.analytics, - "deprecationMsg", d.modDeprecationMsg] + code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), [ + "nimdoccss", "dochackjs", "title", "tableofcontents", "moduledesc", "date", "time", + "content", "author", "version", "analytics", "deprecationMsg"], + [relLink(d.conf.outDir, d.destFile.AbsoluteFile, nimdocOutCss.RelativeFile), + relLink(d.conf.outDir, d.destFile.AbsoluteFile, docHackJsFname.RelativeFile), + title.rope, toc, d.modDesc, rope(getDateStr()), rope(getClockStr()), + content, d.meta[metaAuthor].rope, d.meta[metaVersion].rope, d.analytics.rope, d.modDeprecationMsg]) else: code = content result = code -proc indexFile(d: PDoc): AbsoluteFile = - let dir = d.conf.outDir - result = dir / changeFileExt(presentationPath(d.conf, - AbsoluteFile d.filename), - IndexExt) - let (finalDir, _, _) = result.string.splitFile - createDir(finalDir) - proc generateIndex*(d: PDoc) = if optGenIndex in d.conf.globalOptions: - let dest = indexFile(d) + let dir = d.conf.outDir + createDir(dir) + let dest = dir / changeFileExt(presentationPath(d.conf, AbsoluteFile d.filename), IndexExt) writeIndexFile(d[], dest.string) proc updateOutfile(d: PDoc, outfile: AbsoluteFile) = @@ -1723,31 +1265,22 @@ proc updateOutfile(d: PDoc, outfile: AbsoluteFile) = d.conf.outFile = splitPath(d.conf.outFile.string)[1].RelativeFile proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) = - if optGenIndexOnly in d.conf.globalOptions: - d.conf.outFile = indexFile(d).relativeTo(d.conf.outDir) # just for display - return runAllExamples(d) var content = genOutFile(d, groupedToc) if optStdout in d.conf.globalOptions: - write(stdout, content) + writeRope(stdout, content) else: template outfile: untyped = d.destFile.AbsoluteFile #let outfile = getOutFile2(d.conf, shortenDir(d.conf, filename), outExt) let dir = outfile.splitFile.dir createDir(dir) updateOutfile(d, outfile) - try: - writeFile(outfile, content) - except IOError: + if not writeRope(content, outfile): rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, outfile.string) - if not d.wroteSupportFiles: # nimdoc.css + dochack.js + elif not d.wroteSupportFiles: # nimdoc.css + dochack.js let nimr = $d.conf.getPrefixDir() - case d.target - of outHtml: - copyFile(docCss.interp(nimr = nimr), $d.conf.outDir / nimdocOutCss) - of outLatex: - copyFile(docCls.interp(nimr = nimr), $d.conf.outDir / nimdocOutCls) + copyFile(docCss.interp(nimr = nimr), $d.conf.outDir / nimdocOutCss) if optGenIndex in d.conf.globalOptions: let docHackJs2 = getDocHacksJs(nimr, nim = getAppFilename()) copyFile(docHackJs2, $d.conf.outDir / docHackJs2.lastPathPart) @@ -1756,17 +1289,15 @@ proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) = proc writeOutputJson*(d: PDoc, useWarning = false) = runAllExamples(d) var modDesc: string - for desc in d.modDescFinal: + for desc in d.modDesc: modDesc &= desc let content = %*{"orig": d.filename, "nimble": getPackageName(d.conf, d.filename), "moduleDescription": modDesc, - "entries": d.jEntriesFinal} + "entries": d.jArray} 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) @@ -1785,117 +1316,93 @@ proc handleDocOutputOptions*(conf: ConfigRef) = conf.outDir = AbsoluteDir(conf.outDir / conf.outFile) proc commandDoc*(cache: IdentCache, conf: ConfigRef) = - ## implementation of deprecated ``doc0`` command (without semantic checking) handleDocOutputOptions conf var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return - var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true) - generateDoc(d, ast, ast, conf) - finishGenerateDoc(d) + var d = newDocumentor(conf.projectFull, cache, conf) + d.hasToc = true + generateDoc(d, ast, ast) writeOutput(d) generateIndex(d) proc commandRstAux(cache: IdentCache, conf: ConfigRef; - filename: AbsoluteFile, outExt: string, - preferMarkdown: bool) = + filename: AbsoluteFile, outExt: string) = var filen = addFileExt(filename, "txt") - 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) - d.modDescPre = @[ItemFragment(isRst: true, rst: rst)] - finishGenerateDoc(d) + var d = newDocumentor(filen, cache, conf, outExt) + + d.isPureRst = true + var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc, + {roSupportRawDirective, roSupportMarkdown}, conf) + var modDesc = newStringOfCap(30_000) + renderRstToOut(d[], rst, modDesc) + d.modDesc = rope(modDesc) writeOutput(d) generateIndex(d) -proc commandRst2Html*(cache: IdentCache, conf: ConfigRef, - preferMarkdown=false) = - commandRstAux(cache, conf, conf.projectFull, HtmlExt, preferMarkdown) +proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) = + commandRstAux(cache, conf, conf.projectFull, HtmlExt) -proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef, - preferMarkdown=false) = - commandRstAux(cache, conf, conf.projectFull, TexExt, preferMarkdown) +proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) = + commandRstAux(cache, conf, conf.projectFull, TexExt) 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, hasToc = true) + var d = newDocumentor(conf.projectFull, cache, conf) d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; - status: int; content: string) {.gcsafe.} = + 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 - let content = pretty(json) + let json = d.jArray + let content = rope(pretty(json)) if optStdout in d.conf.globalOptions: - write(stdout, content) + writeRope(stdout, content) else: #echo getOutFile(gProjectFull, JsonExt) let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt) - try: - writeFile(filename, content) - except IOError: + if not writeRope(content, filename): rawMessage(conf, errCannotOpenFile, filename.string) proc commandTags*(cache: IdentCache, conf: ConfigRef) = var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return - var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true) + var d = newDocumentor(conf.projectFull, cache, conf) d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; - status: int; content: string) {.gcsafe.} = + 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 = "" + content: Rope generateTags(d, ast, content) if optStdout in d.conf.globalOptions: - write(stdout, content) + writeRope(stdout, content) else: #echo getOutFile(gProjectFull, TagsExt) let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt) - try: - writeFile(filename, content) - except IOError: + if not writeRope(content, filename): rawMessage(conf, errCannotOpenFile, filename.string) proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") = - if optGenIndexOnly in conf.globalOptions: - return - var content = mergeIndexes(dir) + var content = mergeIndexes(dir).rope var outFile = outFile if outFile.isEmpty: outFile = theindexFname.RelativeFile.changeFileExt("") let filename = getOutFile(conf, outFile, HtmlExt) - let code = getConfigVar(conf, "doc.file") % [ - "nimdoccss", relLink(conf.outDir, filename, nimdocOutCss.RelativeFile), - "dochackjs", relLink(conf.outDir, filename, docHackJsFname.RelativeFile), - "title", "Index", - "subtitle", "", "tableofcontents", "", "moduledesc", "", - "date", getDateStr(), "time", getClockStr(), - "content", content, "author", "", "version", "", "analytics", ""] + let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), [ + "nimdoccss", "dochackjs", + "title", "tableofcontents", "moduledesc", "date", "time", + "content", "author", "version", "analytics"], + [relLink(conf.outDir, filename, nimdocOutCss.RelativeFile), + relLink(conf.outDir, filename, docHackJsFname.RelativeFile), + rope"Index", nil, nil, rope(getDateStr()), + rope(getClockStr()), content, nil, nil, nil]) # no analytics because context is not available - try: - writeFile(filename, code) - except IOError: - 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 IOError: + if not writeRope(code, filename): rawMessage(conf, errCannotOpenFile, filename.string) diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 0941be78a..cfbb33156 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -11,7 +11,7 @@ # semantic checking. import - options, ast, msgs, docgen, lineinfos, pathutils, packages + options, ast, msgs, passes, docgen, lineinfos, pathutils 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.doc.conf.belongsToProjectPackage(g.module)) or + (optWholeProject in g.doc.conf.globalOptions and g.module.getnimblePkgId == g.doc.conf.mainPackageId) or sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex template closeImpl(body: untyped) {.dirty.} = @@ -31,28 +31,27 @@ template closeImpl(body: untyped) {.dirty.} = let useWarning = sfMainModule notin g.module.flags let groupedToc = true if shouldProcess(g): - finishGenerateDoc(g.doc) body try: generateIndex(g.doc) except IOError: discard -proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = +proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: writeOutput(g.doc, useWarning, groupedToc) -proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = +proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: writeOutputJson(g.doc, useWarning) -proc processNode*(c: PPassContext, n: PNode): PNode = +proc processNode(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) if shouldProcess(g): - generateDoc(g.doc, n, n, g.config) + generateDoc(g.doc, n, n) -proc processNodeJson*(c: PPassContext, n: PNode): PNode = +proc processNodeJson(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) if shouldProcess(g): @@ -64,15 +63,20 @@ 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, hasToc = true) + graph.cache, graph.config, ext, module) + d.hasToc = true g.doc = d result = g -proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = +proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = myOpenImpl(HtmlExt) -proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = - myOpenImpl(TexExt) - -proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = +proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = myOpenImpl(JsonExt) + +const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close) +const docgen2JsonPass* = makePass(open = myOpenJson, process = processNodeJson, + close = closeJson) + +proc finishDoc2Pass*(project: string) = + discard diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim index f8b3744d5..9bfa7001a 100644 --- a/compiler/enumtostr.nim +++ b/compiler/enumtostr.nim @@ -1,10 +1,6 @@ import ast, idents, lineinfos, modulegraphs, magicsys -when defined(nimPreviewSlimSystem): - import std/assertions - - proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym = result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info) @@ -30,7 +26,7 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener assert(t.n[i].kind == nkSym) var field = t.n[i].sym let val = if field.ast == nil: field.name.s else: field.ast.strVal - caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t), + caseStmt.add newTree(nkOfBranch, newSymNode(field), newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info)))) #newIntTypeNode(nkIntLit, field.position, t) diff --git a/compiler/errorhandling.nim b/compiler/errorhandling.nim deleted file mode 100644 index a8361105e..000000000 --- a/compiler/errorhandling.nim +++ /dev/null @@ -1,82 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2021 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module contains support code for new-styled error -## handling via an `nkError` node kind. - -import ast, renderer, options, strutils, types - -when defined(nimPreviewSlimSystem): - import std/assertions - -type - ErrorKind* = enum ## expand as you need. - RawTypeMismatchError - ExpressionCannotBeCalled - CustomError - WrongNumberOfArguments - AmbiguousCall - -proc errorSubNode*(n: PNode): PNode = - case n.kind - of nkEmpty..nkNilLit: - result = nil - of nkError: - result = n - else: - result = nil - for i in 0.. 1 - let wrongNode = n[0] - case ErrorKind(n[1].intVal) - of RawTypeMismatchError: - result = "type mismatch" - of ExpressionCannotBeCalled: - result = "expression '$1' cannot be called" % wrongNode[0].renderTree - of CustomError: - result = n[2].strVal - of WrongNumberOfArguments: - result = "wrong number of arguments" - of AmbiguousCall: - let a = n[2].sym - let b = n[3].sym - var args = "(" - for i in 1.. 1: args.add(", ") - args.add(typeToString(wrongNode[i].typ)) - args.add(")") - result = "ambiguous call; both $1 and $2 match for: $3" % [ - getProcHeader(config, a), - getProcHeader(config, b), - args] diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 0577619b8..0d471cf98 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -65,19 +65,19 @@ 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.cstring) + theAddr = gExeHandle.symAddr(name) # then try libc: if theAddr.isNil: let dllhandle = getDll(conf, gDllCache, libcDll, sym.info) - theAddr = dllhandle.symAddr(name.cstring) + theAddr = dllhandle.symAddr(name) 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.cstring) + theAddr = dllhandle.symAddr(name) if theAddr.isNil: globalError(conf, sym.info, "cannot import symbol: " & name & " from " & libPathMsg) - result.intVal = cast[int](theAddr) + result.intVal = cast[ByteAddress](theAddr) proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type = if t == nil: return addr libffi.type_void @@ -92,7 +92,7 @@ proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type = else: result = nil of tyFloat, tyFloat64: result = addr libffi.type_double of tyFloat32: result = addr libffi.type_float - of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCstring, tySequence, tyString, tyUntyped, + of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyUntyped, tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil: result = addr libffi.type_pointer of tyDistinct, tyAlias, tySink: @@ -110,10 +110,10 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI else: globalError(conf, info, "cannot map calling convention to FFI") -template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[] -template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v +template rd(T, p: untyped): untyped = (cast[ptr T](p))[] +template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v template `+!`(x, y: untyped): untyped = - cast[pointer](cast[int](x) + y) + cast[pointer](cast[ByteAddress](x) + y) proc packSize(conf: ConfigRef, v: PNode, typ: PType): int = ## computes the size of the blob @@ -177,8 +177,8 @@ const maxPackDepth = 20 var packRecCheck = 0 proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) = - template awr(typ, v: untyped): untyped = - wr(typ, res, v) + template awr(T, v: untyped): untyped = + wr(T, res, v) case typ.kind of tyBool: awr(bool, v.intVal != 0) @@ -205,7 +205,7 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) = of tyFloat32: awr(float32, v.floatVal) of tyFloat64: awr(float64, v.floatVal) - of tyPointer, tyProc, tyCstring, tyString: + of tyPointer, tyProc, tyCString, tyString: if v.kind == nkNilLit: # nothing to do since the memory is 0 initialized anyway discard @@ -369,13 +369,13 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode = # in their unboxed representation so nothing it to be unpacked: result = n else: - awi(nkPtrLit, cast[int](p)) + awi(nkPtrLit, cast[ByteAddress](p)) of tyPtr, tyRef, tyVar, tyLent: let p = rd(pointer, x) if p.isNil: setNil() elif n == nil or n.kind == nkPtrLit: - awi(nkPtrLit, cast[int](p)) + awi(nkPtrLit, cast[ByteAddress](p)) elif n != nil and n.len == 1: internalAssert(conf, n.kind == nkRefTy) n[0] = unpack(conf, p, typ.lastSon, n[0]) @@ -386,7 +386,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode = result = unpackObject(conf, x, typ, n) of tyArray: result = unpackArray(conf, x, typ, n) - of tyCstring, tyString: + of tyCString, tyString: let p = rd(cstring, x) if p.isNil: setNil() @@ -402,7 +402,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode = proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode = if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer, - tyProc, tyCstring, tyString, + tyProc, tyCString, tyString, tySequence}: result = newNodeIT(x.kind, x.info, destTyp) result.intVal = x.intVal diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index d4aa466c2..691d33a2c 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -26,7 +26,7 @@ type proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = result = copyNode(a) - if ctx.instLines: setInfoRecursive(result, b.info) + if ctx.instLines: result.info = b.info proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = template handleParam(param) = @@ -83,14 +83,10 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = not c.isDeclarative: c.isDeclarative = true isDeclarative = true - if (not c.isDeclarative) and templ.kind in nkCallKinds and isRunnableExamples(templ[0]): - # fixes bug #16993, bug #18054 - discard - else: - var res = copyNode(c, templ, actual) - for i in 0.. 0: - flushDot(conf) - stderr.writeLine(cmd) - proc nameToCC*(name: string): TSystemCC = ## Returns the kind of compiler referred to by `name`, or ccNone ## if the name doesn't refer to any known compiler. @@ -328,9 +318,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = platform.OS[conf.target.targetOS].name & '.' & CC[c].name & fullSuffix result = getConfigVar(conf, fullCCname) - if existsConfigVar(conf, fullCCname): - result = getConfigVar(conf, fullCCname) - else: + if result.len == 0: # not overridden for this cross compilation setting? result = getConfigVar(conf, CC[c].name & fullSuffix) else: @@ -374,7 +362,6 @@ 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 = @@ -385,7 +372,7 @@ proc addFileToCompile*(conf: ConfigRef; cf: Cfile) = conf.toCompile.add(cf) proc addLocalCompileOption*(conf: ConfigRef; option: string; nimfile: AbsoluteFile) = - let key = completeCfilePath(conf, mangleModuleName(conf, nimfile).AbsoluteFile).string + let key = completeCfilePath(conf, withPackageName(conf, nimfile)).string var value = conf.cfileSpecificOptions.getOrDefault(key) if strutils.find(value, option, 0) < 0: addOpt(value, option) @@ -438,13 +425,7 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} = # really: Cross compilation from Linux to Linux for example is entirely # reasonable. # `optGenMapping` is included here for niminst. - # We use absolute paths for vcc / cl, see issue #19883. - let options = - if conf.cCompiler == ccVcc: - {optGenMapping} - else: - {optGenScript, optGenMapping} - result = conf.globalOptions * options != {} + result = conf.globalOptions * {optGenScript, optGenMapping} != {} proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string = result = conf.compileOptions @@ -497,10 +478,7 @@ proc needsExeExt(conf: ConfigRef): bool {.inline.} = (conf.target.hostOS == osWindows) proc useCpp(conf: ConfigRef; cfile: AbsoluteFile): bool = - # 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 + conf.backend == backendCpp and not cfile.string.endsWith(".c") proc envFlags(conf: ConfigRef): string = result = if conf.backend == backendCpp: @@ -508,14 +486,14 @@ proc envFlags(conf: ConfigRef): string = else: getEnv("CFLAGS") -proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; isCpp: bool): string = +proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: AbsoluteFile): string = if compiler == ccEnv: - result = if isCpp: + result = if useCpp(conf, cfile): getEnv("CXX") else: getEnv("CC") else: - result = if isCpp: + result = if useCpp(conf, cfile): CC[compiler].cppCompiler else: CC[compiler].compilerExe @@ -529,36 +507,47 @@ 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 - # NOTE: should we need the full version, use -dumpfullversion - let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except IOError, OSError, ValueError: ("", 1) + let (s, exitCode) = try: execCmdEx(exe & " --version") except: ("", 1) if exitCode == 0: - var major: int - discard parseInt(s, major) - result = major >= 5 + 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 else: result = conf.cCompiler == ccCLang proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe - else: getCompilerExe(conf, compiler, optMixedMode in conf.globalOptions or conf.backend == backendCpp) + elif optMixedMode in conf.globalOptions and conf.backend != backendCpp: CC[compiler].cppCompiler + else: getCompilerExe(conf, compiler, AbsoluteFile"") proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, isMainFile = false; produceOutput = false): string = - let - c = conf.cCompiler - isCpp = useCpp(conf, cfile.cname) + let c = conf.cCompiler # 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 isCpp: + if useCpp(conf, cfile.cname): # 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, isCpp) + if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname) if needsExeExt(conf): exe = addFileExt(exe, "exe") if (optGenDynLib in conf.globalOptions or (conf.hcrOn and not isMainFile)) and @@ -578,14 +567,14 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, compilePattern = joinPath(conf.cCompilerPath, exe) else: - compilePattern = getCompilerExe(conf, c, isCpp) + compilePattern = getCompilerExe(conf, c, cfile.cname) includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)])) - let cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string) + var cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string) else: cfile.cname - let objfile = + var objfile = if cfile.obj.isEmpty: if CfileFlag.External notin cfile.flags or noAbsolutePaths(conf): toObjFile(conf, cf).string @@ -620,7 +609,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, "for the selected C compiler: " & CC[conf.cCompiler].name) result.add(' ') - strutils.addf(result, CC[c].compileTmpl, [ + result.addf(CC[c].compileTmpl, [ "dfile", dfile, "file", cfsh, "objfile", quoteShell(objfile), "options", options, "include", includeCmd, @@ -640,8 +629,8 @@ 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.mangleModuleName(cfile.cname).AbsoluteFile, "sha1") - let currentHash = footprint(conf, cfile) + var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1") + var currentHash = footprint(conf, cfile) var f: File if open(f, hashFile.string, fmRead): let oldHash = parseSecureHash(f.readLine()) @@ -655,10 +644,8 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = close(f) proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) = - # we want to generate the hash file unconditionally - let extFileChanged = externalFileChanged(conf, c) if optForceFullMake notin conf.globalOptions and fileExists(c.obj) and - not extFileChanged: + not externalFileChanged(conf, c): c.flags.incl CfileFlag.Cached else: # make sure Nim keeps recompiling the external file on reruns @@ -673,13 +660,10 @@ proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) = addExternalFileToCompile(conf, c) proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile, - objfiles: string, isDllBuild: bool, removeStaticFile: bool): string = + objfiles: string, isDllBuild: bool): string = if optGenStaticLib in conf.globalOptions: - if removeStaticFile: - removeFile output # fixes: bug #16947 result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output), - "objfiles", objfiles, - "vccplatform", vccplatform(conf)] + "objfiles", objfiles] else: var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe") if linkerExe.len == 0: linkerExe = getLinkerExe(conf, conf.cCompiler) @@ -712,7 +696,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 ' ' - strutils.addf(result, linkTmpl, ["builddll", builddll, + result.addf(linkTmpl, ["builddll", builddll, "mapfile", mapfile, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, @@ -760,9 +744,8 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile, if optCDebug in conf.globalOptions and conf.cCompiler == ccVcc: result.add " /Zi /FS /Od" -template getLinkCmd(conf: ConfigRef; output: AbsoluteFile, objfiles: string, - removeStaticFile = false): string = - getLinkCmd(conf, output, objfiles, optGenDynLib in conf.globalOptions, removeStaticFile) +template getLinkCmd(conf: ConfigRef; output: AbsoluteFile, objfiles: string): string = + getLinkCmd(conf, output, objfiles, optGenDynLib in conf.globalOptions) template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped) = try: @@ -836,15 +819,6 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) = finally: removeFile(linkerArgs) -proc linkViaShellScript(conf: ConfigRef; cmd: string) = - let linkerScript = conf.projectName & "_" & "linkerScript.sh" - writeFile(linkerScript, cmd) - let shell = getEnv("SHELL") - try: - execLinkCmd(conf, shell & " " & linkerScript) - finally: - removeFile(linkerScript) - proc getObjFilePath(conf: ConfigRef, f: Cfile): string = if noAbsolutePaths(conf): f.obj.extractFilename else: f.obj.string @@ -858,23 +832,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] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd) + result = MsgKindToStr[hintCC] % (demanglePackageName(path.splitFile.name) & ": " & compileCmd) else: - result = MsgKindToStr[hintCC] % demangleModuleName(path.splitFile.name) - -proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) = - # Prevent linkcmd from exceeding the maximum command line length. - # Windows's command line limit is about 8K (8191 characters) so C compilers on - # Windows support a feature where the command line can be passed via ``@linkcmd`` - # to them. - const MaxCmdLen = when defined(windows): 8_000 elif defined(macosx): 260_000 else: 32_000 - if linkCmd.len > MaxCmdLen: - when defined(macosx): - linkViaShellScript(conf, linkCmd) - else: - linkViaResponseFile(conf, linkCmd) - else: - execLinkCmd(conf, linkCmd) + result = MsgKindToStr[hintCC] % demanglePackageName(path.splitFile.name) proc callCCompiler*(conf: ConfigRef) = var @@ -884,10 +844,14 @@ proc callCCompiler*(conf: ConfigRef) = return # speed up that call if only compiling and no script shall be # generated #var c = cCompiler - var script: Rope = "" + var script: Rope = nil var cmds: TStringSeq var prettyCmds: TStringSeq - let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) + let prettyCb = proc (idx: int) = + if prettyCmds[idx].len > 0: + flushDot(conf) + # xxx should probably use stderr like other compiler messages, not stdout + echo prettyCmds[idx] for idx, it in conf.toCompile: # call the C compiler for the .c file: @@ -925,7 +889,7 @@ proc callCCompiler*(conf: ConfigRef) = let objFile = conf.getObjFilePath(x) let buildDll = idx != mainFileIdx let linkTarget = conf.hcrLinkTargetName(objFile, not buildDll) - cmds.add(getLinkCmd(conf, linkTarget, objfiles & " " & quoteShell(objFile), buildDll, removeStaticFile = true)) + cmds.add(getLinkCmd(conf, linkTarget, objfiles & " " & quoteShell(objFile), buildDll)) # try to remove all .pdb files for the current binary so they don't accumulate endlessly in the nimcache # for more info check the comment inside of getLinkCmd() where the /PDB: MSVC flag is used if isVSCompatible(conf): @@ -938,8 +902,8 @@ proc callCCompiler*(conf: ConfigRef) = # only if not cached - copy the resulting main file from the nimcache folder to its originally intended destination if CfileFlag.Cached notin conf.toCompile[mainFileIdx].flags: let mainObjFile = getObjFilePath(conf, conf.toCompile[mainFileIdx]) - let src = conf.hcrLinkTargetName(mainObjFile, true) - let dst = conf.prepareToWriteOutput + var src = conf.hcrLinkTargetName(mainObjFile, true) + var dst = conf.prepareToWriteOutput copyFileWithPermissions(src.string, dst.string) else: for x in conf.toCompile: @@ -947,12 +911,18 @@ proc callCCompiler*(conf: ConfigRef) = objfiles.add(' ') objfiles.add(quoteShell(objFile)) let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput - else: AbsoluteFile(conf.outFile) - - linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true) + else: AbsoluteFile(conf.projectName) + linkCmd = getLinkCmd(conf, mainOutput, objfiles) extraCmds = getExtraCmds(conf, mainOutput) if optCompileOnly notin conf.globalOptions: - preventLinkCmdMaxCmdLen(conf, linkCmd) + const MaxCmdLen = when defined(windows): 8_000 else: 32_000 + if linkCmd.len > MaxCmdLen: + # Windows's command line limit is about 8K (don't laugh...) so C compilers on + # Windows support a feature where the command line can be passed via ``@linkcmd`` + # to them. + linkViaResponseFile(conf, linkCmd) + else: + execLinkCmd(conf, linkCmd) for cmd in extraCmds: execExternalProgram(conf, cmd, hintExecuting) else: @@ -962,99 +932,213 @@ proc callCCompiler*(conf: ConfigRef) = script.add("\n") generateScript(conf, script) +#from json import escapeJson +import json, std / sha1 + template hashNimExe(): string = $secureHashFile(os.getAppFilename()) -proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile = - # `outFile` is better than `projectName`, as it allows having different json - # files for a given source file compiled with different options; it also - # works out of the box with `hashMainCompilationParams`. - result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json") - -const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes -type BuildCache = object - cacheVersion: string - outputFile: string - compile: seq[(string, string)] - link: seq[string] - linkcmd: string - extraCmds: seq[string] - configFiles: seq[string] # the hash shouldn't be needed - stdinInput: bool - projectIsCmd: bool - cmdInput: string - currentDir: string - cmdline: string - depfiles: seq[(string, string)] - nimexe: string - proc writeJsonBuildInstructions*(conf: ConfigRef) = - var linkFiles = collect(for it in conf.externalToLink: - var it = it - if conf.noAbsolutePaths: it = it.extractFilename - it.addFileExt(CC[conf.cCompiler].objExt)) - for it in conf.toCompile: linkFiles.add it.obj.string - var bcache = BuildCache( - cacheVersion: cacheVersion, - outputFile: conf.absOutFile.string, - compile: collect(for i, it in conf.toCompile: - if CfileFlag.Cached notin it.flags: (it.cname.string, getCompileCFileCmd(conf, it))), - link: linkFiles, - linkcmd: getLinkCmd(conf, conf.absOutFile, linkFiles.quoteShellCommand), - extraCmds: getExtraCmds(conf, conf.absOutFile), - stdinInput: conf.projectIsStdin, - projectIsCmd: conf.projectIsCmd, - cmdInput: conf.cmdInput, - configFiles: conf.configFiles.mapIt(it.string), - currentDir: getCurrentDir()) - if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"): - bcache.cmdline = conf.commandLine - bcache.depfiles = collect(for it in conf.m.fileInfos: + template lit(x: untyped) = f.write x + template str(x: untyped) = + when compiles(escapeJson(x, buf)): + buf.setLen 0 + escapeJson(x, buf) + f.write buf + else: + f.write escapeJson(x) + + proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) = + var comma = false + for i, it in clist: + if CfileFlag.Cached in it.flags: continue + let compileCmd = getCompileCFileCmd(conf, it) + if comma: lit ",\L" else: comma = true + lit "[" + str it.cname.string + lit ", " + str compileCmd + lit "]" + + proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList; + llist: seq[string]) = + var pastStart = false + for it in llist: + let objfile = if noAbsolutePaths(conf): it.extractFilename + else: it + let objstr = addFileExt(objfile, CC[conf.cCompiler].objExt) + objfiles.add(' ') + objfiles.add(objstr) + if pastStart: lit ",\L" + str objstr + pastStart = true + + for it in clist: + let objstr = quoteShell(it.obj) + objfiles.add(' ') + objfiles.add(objstr) + if pastStart: lit ",\L" + str objstr + pastStart = true + lit "\L" + + proc depfiles(conf: ConfigRef; f: File) = + var i = 0 + for it in conf.m.fileInfos: let path = it.fullPath.string if isAbsolute(path): # TODO: else? - (path, $secureHashFile(path))) - bcache.nimexe = hashNimExe() - conf.jsonBuildFile = conf.jsonBuildInstructionsFile - conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty) + if i > 0: lit "],\L" + lit "[" + str path + lit ", " + str $secureHashFile(path) + inc i + lit "]\L" -proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool = - if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true - var bcache: BuildCache - try: bcache.fromJson(jsonFile.string.parseFile) + + var buf = newStringOfCap(50) + + let jsonFile = conf.getNimcacheDir / RelativeFile(conf.projectName & ".json") + conf.jsonBuildFile = jsonFile + let output = conf.absOutFile + + var f: File + if open(f, jsonFile.string, fmWrite): + lit "{\L" + lit "\"outputFile\": " + str $output + + lit ",\L\"compile\":[\L" + cfiles(conf, f, buf, conf.toCompile, false) + lit "],\L\"link\":[\L" + var objfiles = "" + # XXX add every file here that is to link + linkfiles(conf, f, buf, objfiles, conf.toCompile, conf.externalToLink) + + lit "],\L\"linkcmd\": " + str getLinkCmd(conf, output, objfiles) + + lit ",\L\"extraCmds\": " + lit $(%* getExtraCmds(conf, conf.absOutFile)) + + lit ",\L\"stdinInput\": " + lit $(%* conf.projectIsStdin) + lit ",\L\"projectIsCmd\": " + lit $(%* conf.projectIsCmd) + lit ",\L\"cmdInput\": " + lit $(%* conf.cmdInput) + lit ",\L\"currentDir\": " + lit $(%* getCurrentDir()) + + if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"): + lit ",\L\"cmdline\": " + str conf.commandLine + lit ",\L\"depfiles\":[\L" + depfiles(conf, f) + lit "],\L\"nimexe\": \L" + str hashNimExe() + lit "\L" + + lit "\L}\L" + close(f) + +proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile): bool = + let jsonFile = toGeneratedFile(conf, projectfile, "json") + if not fileExists(jsonFile): return true + if not fileExists(conf.absOutFile): return true + result = false + try: + let data = json.parseFile(jsonFile.string) + for key in "depfiles cmdline stdinInput currentDir".split: + if not data.hasKey(key): return true + if getCurrentDir() != data["currentDir"].getStr: + # fixes bug #16271 + # Note that simply comparing `expandFilename(projectFile)` would + # not be sufficient in case other flags depend implicitly on `getCurrentDir`, + # and would require much more care. Simply re-compiling is safer for now. + # A better strategy for future work would be to cache (with an LRU cache) + # the N most recent unique build instructions, as done with `rdmd`, + # which is both robust and avoids recompilation when switching back and forth + # between projects, see https://github.com/timotheecour/Nim/issues/199 + return true + let oldCmdLine = data["cmdline"].getStr + if conf.commandLine != oldCmdLine: + return true + if hashNimExe() != data["nimexe"].getStr: + return true + let stdinInput = data["stdinInput"].getBool + let projectIsCmd = data["projectIsCmd"].getBool + if conf.projectIsStdin or stdinInput: + # could optimize by returning false if stdin input was the same, + # but I'm not sure how to get full stding input + return true + + if conf.projectIsCmd or projectIsCmd: + if not (conf.projectIsCmd and projectIsCmd): return true + if not data.hasKey("cmdInput"): return true + let cmdInput = data["cmdInput"].getStr + if cmdInput != conf.cmdInput: return true + + let depfilesPairs = data["depfiles"] + doAssert depfilesPairs.kind == JArray + for p in depfilesPairs: + doAssert p.kind == JArray + # >= 2 for forwards compatibility with potential later .json files: + doAssert p.len >= 2 + let depFilename = p[0].getStr + let oldHashValue = p[1].getStr + let newHashValue = $secureHashFile(depFilename) + if oldHashValue != newHashValue: + return true except IOError, OSError, ValueError: - stderr.write "Warning: JSON processing failed for: $#\n" % jsonFile.string - return true - if bcache.currentDir != getCurrentDir() or # fixes bug #16271 - bcache.configFiles != conf.configFiles.mapIt(it.string) or - bcache.cacheVersion != cacheVersion or bcache.outputFile != conf.absOutFile.string or - bcache.cmdline != conf.commandLine or bcache.nimexe != hashNimExe() or - bcache.projectIsCmd != conf.projectIsCmd or conf.cmdInput != bcache.cmdInput: return true - if bcache.stdinInput or conf.projectIsStdin: return true - # xxx optimize by returning false if stdin input was the same - for (file, hash) in bcache.depfiles: - if $secureHashFile(file) != hash: return true + echo "Warning: JSON processing failed: ", getCurrentExceptionMsg() + result = true -proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = - var bcache: BuildCache - try: bcache.fromJson(jsonFile.string.parseFile) - except ValueError, KeyError, JsonKindError: +proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) = + let jsonFile = toGeneratedFile(conf, projectfile, "json") + try: + let data = json.parseFile(jsonFile.string) + + let output = data["outputFile"].getStr + createDir output.parentDir + let outputCurrent = $conf.absOutFile + if output != outputCurrent: + # previously, any specified output file would be silently ignored; + # simply copying won't work in some cases, for example with `extraCmds`, + # so we just make it an error, user should use same command for jsonscript + # as was used with --compileOnly. + globalError(conf, gCmdLineInfo, "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " % [outputCurrent, output, jsonFile.string]) + + let toCompile = data["compile"] + doAssert toCompile.kind == JArray + var cmds: TStringSeq + var prettyCmds: TStringSeq + let prettyCb = proc (idx: int) = + if prettyCmds[idx].len > 0: echo prettyCmds[idx] + + for c in toCompile: + doAssert c.kind == JArray + doAssert c.len >= 2 + + cmds.add(c[1].getStr) + prettyCmds.add displayProgressCC(conf, c[0].getStr, c[1].getStr) + + execCmdsInParallel(conf, cmds, prettyCb) + + let linkCmd = data["linkcmd"] + doAssert linkCmd.kind == JString + execLinkCmd(conf, linkCmd.getStr) + if data.hasKey("extraCmds"): + let extraCmds = data["extraCmds"] + doAssert extraCmds.kind == JArray + for cmd in extraCmds: + doAssert cmd.kind == JString, $cmd.kind + let cmd2 = cmd.getStr + execExternalProgram(conf, cmd2, hintExecuting) + + except: let e = getCurrentException() - conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" % - [e.msg, e.getStackTrace(), jsonFile.string] - let output = bcache.outputFile - createDir output.parentDir - let outputCurrent = $conf.absOutFile - if output != outputCurrent or bcache.cacheVersion != cacheVersion: - globalError(conf, gCmdLineInfo, - "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " % - [outputCurrent, output, jsonFile.string]) - var cmds, prettyCmds: TStringSeq - let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) - for (name, cmd) in bcache.compile: - cmds.add cmd - prettyCmds.add displayProgressCC(conf, name, cmd) - execCmdsInParallel(conf, cmds, prettyCb) - preventLinkCmdMaxCmdLen(conf, bcache.linkcmd) - for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting) + quit "\ncaught exception:\n" & e.msg & "\nstacktrace:\n" & e.getStackTrace() & + "error evaluating JSON file: " & jsonFile.string proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope = for it in list: diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 84ed99164..6165ff2f3 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -22,7 +22,7 @@ type info: TLineInfo indent, emitPar: int x: string # the current input line - outp: PLLStream # the output will be parsed by parser + outp: PLLStream # the output will be parsed by pnimsyn subsChar, nimDirective: char emit, conc, toStr: string curly, bracket, par: int diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim index 8fac06971..d4aeb6a77 100644 --- a/compiler/gorgeimpl.nim +++ b/compiler/gorgeimpl.nim @@ -12,9 +12,6 @@ import msgs, std / sha1, os, osproc, streams, options, lineinfos, pathutils -when defined(nimPreviewSlimSystem): - import std/syncio - proc readOutput(p: Process): (string, int) = result[0] = "" var output = p.outputStream @@ -49,11 +46,7 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str if result[1] == 0: writeFile(filename, result[0]) except IOError, OSError: - if not readSuccessful: - when defined(nimLegacyGorgeErrors): - result = ("", -1) - else: - result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1) + if not readSuccessful: result = ("", -1) else: try: var p = startProcess(cmd, workingDir, @@ -64,7 +57,4 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str result = p.readOutput p.close() except IOError, OSError: - when defined(nimLegacyGorgeErrors): - result = ("", -1) - else: - result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1) + result = ("", -1) diff --git a/compiler/guards.nim b/compiler/guards.nim index 15c6a64e3..4f07df201 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -12,9 +12,6 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents, saturate, modulegraphs, options, lineinfos, int128 -when defined(nimPreviewSlimSystem): - import std/assertions - const someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, mEqStr, mEqSet, mEqCString} @@ -68,12 +65,9 @@ proc isLetLocation(m: PNode, isApprox: bool): bool = case n.kind of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv: n = n[0] - of nkDerefExpr: + of nkDerefExpr, nkHiddenDeref: n = n[0] inc derefs - of nkHiddenDeref: - n = n[0] - if not isApprox: inc derefs of nkBracketExpr: if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv): n = n[0] @@ -88,6 +82,26 @@ proc isLetLocation(m: PNode, isApprox: bool): bool = proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) +type + Operators* = object + opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym + opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym + +proc initOperators*(g: ModuleGraph): Operators = + result.opLe = createMagic(g, "<=", mLeI) + result.opLt = createMagic(g, "<", mLtI) + result.opAnd = createMagic(g, "and", mAnd) + result.opOr = createMagic(g, "or", mOr) + result.opIsNil = createMagic(g, "isnil", mIsNil) + result.opEq = createMagic(g, "==", mEqI) + result.opAdd = createMagic(g, "+", mAddI) + result.opSub = createMagic(g, "-", mSubI) + result.opMul = createMagic(g, "*", mMulI) + result.opDiv = createMagic(g, "div", mDivI) + result.opLen = createMagic(g, "len", mLengthSeq) + result.opNot = createMagic(g, "not", mNot) + result.opContains = createMagic(g, "contains", mInSet) + proc swapArgs(fact: PNode, newOp: PSym): PNode = result = newNodeI(nkCall, fact.info, 3) result[0] = newSymNode(newOp) @@ -203,7 +217,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.astdef.len-1) + nkIntLit.newIntNode(x.sym.ast.len-1) else: o.opAdd.buildCall(o.opLen.buildCall(x), minusOne()) result.info = x.info @@ -390,16 +404,16 @@ proc usefulFact(n: PNode; o: Operators): PNode = type TModel* = object s*: seq[PNode] # the "knowledge base" - g*: ModuleGraph + o*: Operators beSmart*: bool proc addFact*(m: var TModel, nn: PNode) = - let n = usefulFact(nn, m.g.operators) + let n = usefulFact(nn, m.o) if n != nil: if not m.beSmart: m.s.add n else: - let c = canon(n, m.g.operators) + let c = canon(n, m.o) if c.getMagic == mAnd: addFact(m, c[1]) addFact(m, c[2]) @@ -407,7 +421,7 @@ proc addFact*(m: var TModel, nn: PNode) = m.s.add c proc addFactNeg*(m: var TModel, n: PNode) = - let n = n.neg(m.g.operators) + let n = n.neg(m.o) if n != nil: addFact(m, n) proc sameOpr(a, b: PSym): bool = @@ -447,14 +461,8 @@ proc sameTree*(a, b: PNode): bool = proc hasSubTree(n, x: PNode): bool = if n.sameTree(x): result = true else: - case n.kind - of nkEmpty..nkNilLit: - result = n.sameTree(x) - of nkFormalParams: - discard - else: - for i in 0..=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x: return impYes elif a =~ x*dc and b =~ x2*ec + y: #echo "BUG cam ehrer e ", a, " <=? ", b if sameTree(x, x2): - let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne()) + let ec1 = m.o.opAdd.buildCall(ec, minusOne()) if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero(): return impYes @@ -950,12 +963,12 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = var b = bb if replacements.len > 0: m.s = @[] - m.g = model.g + m.o = model.o # make the other facts consistent: for fact in model.s: if fact != nil and fact.getMagic notin someEq: # XXX 'canon' should not be necessary here, but it is - m.s.add applyReplacements(fact, replacements).canon(m.g.operators) + m.s.add applyReplacements(fact, replacements).canon(m.o) a = applyReplacements(aa, replacements) b = applyReplacements(bb, replacements) else: @@ -964,19 +977,19 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication = result = pleViaModelRec(m, a, b) proc proveLe*(m: TModel; a, b: PNode): TImplication = - let x = canon(m.g.operators.opLe.buildCall(a, b), m.g.operators) + let x = canon(m.o.opLe.buildCall(a, b), m.o) #echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2]) result = ple(m, x[1], x[2]) if result == impUnknown: # try an alternative: a <= b iff not (b < a) iff not (b+1 <= a): - let y = canon(m.g.operators.opLe.buildCall(m.g.operators.opAdd.buildCall(b, one()), a), m.g.operators) + let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o) result = ~ple(m, y[1], y[2]) proc addFactLe*(m: var TModel; a, b: PNode) = - m.s.add canon(m.g.operators.opLe.buildCall(a, b), m.g.operators) + m.s.add canon(m.o.opLe.buildCall(a, b), m.o) proc addFactLt*(m: var TModel; a, b: PNode) = - let bb = m.g.operators.opAdd.buildCall(b, minusOne()) + let bb = m.o.opAdd.buildCall(b, minusOne()) addFactLe(m, a, bb) proc settype(n: PNode): PType = @@ -1008,14 +1021,14 @@ proc buildElse(n: PNode; o: Operators): PNode = proc addDiscriminantFact*(m: var TModel, n: PNode) = var fact = newNodeI(nkCall, n.info, 3) - fact[0] = newSymNode(m.g.operators.opEq) + fact[0] = newSymNode(m.o.opEq) fact[1] = n[0] fact[2] = n[1] m.s.add fact proc addAsgnFact*(m: var TModel, key, value: PNode) = var fact = newNodeI(nkCall, key.info, 3) - fact[0] = newSymNode(m.g.operators.opEq) + fact[0] = newSymNode(m.o.opEq) fact[1] = key fact[2] = value m.s.add fact @@ -1031,7 +1044,7 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool = # However, nil checking requires exactly the same mechanism! But for now # we simply use sameTree and live with the unsoundness of the analysis. var check = newNodeI(nkCall, a.info, 3) - check[0] = newSymNode(m.g.operators.opEq) + check[0] = newSymNode(m.o.opEq) check[1] = a check[2] = b result = m.doesImply(check) == impYes @@ -1039,9 +1052,9 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool = proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) = let branch = n[i] if branch.kind == nkOfBranch: - m.s.add buildOf(branch, n[0], m.g.operators) + m.s.add buildOf(branch, n[0], m.o) else: - m.s.add n.buildElse(m.g.operators).neg(m.g.operators) + m.s.add n.buildElse(m.o).neg(m.o) proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode = if check[1].kind == nkCurly: @@ -1057,12 +1070,8 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode = assert check.getMagic == mNot result = buildProperFieldCheck(access, check[1], o).neg(o) -proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef; produceError: bool) = +proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) = for i in 1..= 0 and idx < t.vals.len - proc enlarge[T](t: var BiTable[T]) = var n: seq[LitId] newSeq(n, len(t.keys) * 2) @@ -93,13 +86,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 deleted file mode 100644 index 815078a36..000000000 --- a/compiler/ic/cbackend.nim +++ /dev/null @@ -1,178 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2021 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## New entry point into our C/C++ code generator. Ideally -## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code) -## to work on packed trees directly and produce the C code as an AST which can -## then be rendered to text in a very simple manner. Unfortunately nobody wrote -## this code. So instead we wrap the existing cgen.nim and its friends so that -## we call directly into the existing code generation logic but avoiding the -## naive, outdated `passes` design. Thus you will see some -## `useAliveDataFromDce in flags` checks in the old code -- the old code is -## also doing cross-module dependency tracking and DCE that we don't need -## anymore. DCE is now done as prepass over the entire packed module graph. - -import std/packedsets, algorithm, tables - -when defined(nimPreviewSlimSystem): - import std/assertions - -import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen, - pathutils, extccomp, msgs, modulepaths] - -import packed_ast, ic, dce, rodfiles - -proc unpackTree(g: ModuleGraph; thisModule: int; - tree: PackedTree; n: NodePos): PNode = - var decoder = initPackedDecoder(g.config, g.cache) - result = loadNodes(decoder, g.packed, thisModule, tree, n) - -proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) = - if g.backend == nil: - g.backend = cgendata.newModuleList(g) - assert g.backend != nil - var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config) - bmod.idgen = idgenFromLoadedModule(m) - -proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) = - var bmod = BModuleList(g.backend).modules[m.module.position] - assert bmod != nil - bmod.flags.incl useAliveDataFromDce - bmod.alive = move alive[m.module.position] - - for p in allNodes(m.fromDisk.topLevel): - let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p) - cgen.genTopLevelStmt(bmod, n) - - finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info)) - m.fromDisk.backendFlags = cgen.whichInitProcs(bmod) - -proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) = - for x in mitems(m.fromDisk.emittedTypeInfo): - #echo "found type ", x, " for file ", int(origin) - g.emittedTypeInfo[x] = origin - -proc addFileToLink(config: ConfigRef; m: PSym) = - let filename = AbsoluteFile toFullPath(config, m.position.FileIndex) - let ext = - if config.backend == backendCpp: ".nim.cpp" - elif config.backend == backendObjc: ".nim.m" - else: ".nim.c" - 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, - obj: objFile, - flags: {CfileFlag.Cached}) - addFileToCompile(config, cf) - -when defined(debugDce): - import os, std/packedsets - -proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) = - var f = rodfiles.create(asymFile.string) - f.storeHeader() - f.storeSection aliveSymsSection - f.storeSeq(s) - close f - -template prepare {.dirty.} = - let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms") - var s = newSeqOfCap[int32](alive[position].len) - for a in items(alive[position]): s.add int32(a) - sort(s) - -proc storeAliveSyms(config: ConfigRef; position: int; alive: AliveSyms) = - prepare() - storeAliveSymsImpl(asymFile, s) - -proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool = - prepare() - var f2 = rodfiles.open(asymFile.string) - f2.loadHeader() - f2.loadSection aliveSymsSection - var oldData: seq[int32] - f2.loadSeq(oldData) - f2.close - if f2.err == ok and oldData == s: - result = false - else: - when defined(debugDce): - let oldAsSet = toPackedSet[int32](oldData) - let newAsSet = toPackedSet[int32](s) - echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err - echo "in old but not in new ", oldAsSet.difference(newAsSet), " number of entries in old ", oldAsSet.len - echo "in new but not in old ", newAsSet.difference(oldAsSet), " number of entries in new ", newAsSet.len - #if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0: - # echo "command failed" - result = true - storeAliveSymsImpl(asymFile, s) - -proc genPackedModule(g: ModuleGraph, i: int; alive: var AliveSyms) = - # case statement here to enforce exhaustive checks. - case g.packed[i].status - of undefined: - discard "nothing to do" - of loading, stored: - assert false - of storing, outdated: - storeAliveSyms(g.config, g.packed[i].module.position, alive) - generateCodeForModule(g, g.packed[i], alive) - closeRodFile(g, g.packed[i].module) - of loaded: - if g.packed[i].loadedButAliveSetChanged: - generateCodeForModule(g, g.packed[i], alive) - else: - addFileToLink(g.config, g.packed[i].module) - replayTypeInfo(g, g.packed[i], FileIndex(i)) - - if g.backend == nil: - g.backend = cgendata.newModuleList(g) - registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags) - -proc generateCode*(g: ModuleGraph) = - ## The single entry point, generate C(++) code for the entire - ## Nim program aka `ModuleGraph`. - resetForBackend(g) - var alive = computeAliveSyms(g.packed, g.config) - - when false: - for i in 0..high(g.packed): - echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i)) - - # First pass: Setup all the backend modules for all the modules that have - # changed: - for i in 0..high(g.packed): - # case statement here to enforce exhaustive checks. - case g.packed[i].status - of undefined: - discard "nothing to do" - of loading, stored: - assert false - of storing, outdated: - setupBackendModule(g, g.packed[i]) - of loaded: - # Even though this module didn't change, DCE might trigger a change. - # Consider this case: Module A uses symbol S from B and B does not use - # S itself. A is then edited not to use S either. Thus we have to - # recompile B in order to remove S from the final result. - if aliveSymsChanged(g.config, g.packed[i].module.position, alive): - g.packed[i].loadedButAliveSetChanged = true - setupBackendModule(g, g.packed[i]) - - # Second pass: Code generation. - let mainModuleIdx = g.config.projectMainIdx2.int - # We need to generate the main module last, because only then - # all init procs have been registered: - for i in 0..high(g.packed): - if i != mainModuleIdx: - genPackedModule(g, i, alive) - if mainModuleIdx >= 0: - genPackedModule(g, mainModuleIdx, alive) diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim deleted file mode 100644 index bc61a38de..000000000 --- a/compiler/ic/dce.nim +++ /dev/null @@ -1,165 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2021 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Dead code elimination (=DCE) for IC. - -import std/[intsets, tables] - -when defined(nimPreviewSlimSystem): - import std/assertions - -import ".." / [ast, options, lineinfos, types] - -import packed_ast, ic, bitabs - -type - AliveSyms* = seq[IntSet] - AliveContext* = object ## Purpose is to fill the 'alive' field. - stack: seq[(int, TOptions, NodePos)] ## A stack for marking symbols as alive. - decoder: PackedDecoder ## We need a PackedDecoder for module ID address translations. - thisModule: int ## The module we're currently analysing for DCE. - alive: AliveSyms ## The final result of our computation. - options: TOptions - compilerProcs: Table[string, (int, int32)] - -proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool = - ## "Exported to C" procs are special (these are marked with '.exportc') because these - ## must not be optimized away! - let symPtr = unsafeAddr g[c.thisModule].fromDisk.syms[symId] - let flags = symPtr.flags - # due to a bug/limitation in the lambda lifting, unused inner procs - # are not transformed correctly; issue (#411). However, the whole purpose here - # is to eliminate unused procs. So there is no special logic required for this case. - if sfCompileTime notin flags: - if ({sfExportc, sfCompilerProc} * flags != {}) or - (symPtr.kind == skMethod): - result = true - # XXX: This used to be a condition to: - # (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or - if sfCompilerProc in flags: - c.compilerProcs[g[c.thisModule].fromDisk.strings[symPtr.name]] = (c.thisModule, symId) - -template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty - -proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: int32) = - ## Marks a symbol 'item' as used and later in 'followNow' the symbol's body will - ## be analysed. - if not c.alive[module].containsOrIncl(item): - var body = g[module].fromDisk.syms[item].ast - if body != emptyNodeId: - let opt = g[module].fromDisk.syms[item].options - if g[module].fromDisk.syms[item].kind in routineKinds: - body = NodeId ithSon(g[module].fromDisk.bodies, NodePos body, bodyPos) - c.stack.add((module, opt, NodePos(body))) - - when false: - let nid = g[module].fromDisk.syms[item].name - if nid != LitId(0): - let name = g[module].fromDisk.strings[nid] - if name in ["nimFrame", "callDepthLimitReached"]: - echo "I was called! ", name, " body exists: ", body != emptyNodeId, " ", module, " ", item - -proc requestCompilerProc(c: var AliveContext; g: PackedModuleGraph; name: string) = - let (module, item) = c.compilerProcs[name] - followLater(c, g, module, item) - -proc loadTypeKind(t: PackedItemId; c: AliveContext; g: PackedModuleGraph; toSkip: set[TTypeKind]): TTypeKind = - template kind(t: ItemId): TTypeKind = g[t.module].fromDisk.types[t.item].kind - - var t2 = translateId(t, g, c.thisModule, c.decoder.config) - result = t2.kind - while result in toSkip: - t2 = translateId(g[t2.module].fromDisk.types[t2.item].types[^1], g, t2.module, c.decoder.config) - result = t2.kind - -proc rangeCheckAnalysis(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) = - ## Replicates the logic of `ccgexprs.genRangeChck`. - ## XXX Refactor so that the duplicated logic is avoided. However, for now it's not clear - ## the approach has enough merit. - var dest = loadTypeKind(n.typ, c, g, abstractVar) - if optRangeCheck notin c.options or dest in {tyUInt..tyUInt64}: - discard "no need to generate a check because it was disabled" - else: - let n0t = loadTypeKind(n.firstSon.typ, c, g, {}) - if n0t in {tyUInt, tyUInt64}: - c.requestCompilerProc(g, "raiseRangeErrorNoArgs") - else: - let raiser = - case loadTypeKind(n.typ, c, g, abstractVarRange) - of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU" - of tyFloat..tyFloat128: "raiseRangeErrorF" - else: "raiseRangeErrorI" - c.requestCompilerProc(g, raiser) - -proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) = - ## Marks the symbols we encounter when we traverse the AST at `tree[n]` as alive, unless - ## it is purely in a declarative context (type section etc.). - case n.kind - of nkNone..pred(nkSym), succ(nkSym)..nkNilLit: - discard "ignore non-sym atoms" - of nkSym: - # This symbol is alive and everything its body references. - followLater(c, g, c.thisModule, n.operand) - of nkModuleRef: - let (n1, n2) = sons2(tree, n) - assert n1.kind == nkInt32Lit - assert n2.kind == nkInt32Lit - let m = n1.litId - let item = n2.operand - let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int - followLater(c, g, otherModule, item) - of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr, - nkCommentStmt, nkIncludeStmt, - nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkStaticStmt: - discard - of nkVarSection, nkLetSection, nkConstSection: - # XXX ignore the defining local variable name? - for son in sonsReadonly(tree, n): - aliveCode(c, g, tree, son) - of nkChckRangeF, nkChckRange64, nkChckRange: - rangeCheckAnalysis(c, g, tree, n) - of nkProcDef, nkConverterDef, nkMethodDef, nkFuncDef, nkIteratorDef: - if n.firstSon.kind == nkSym and isNotGeneric(n): - let item = n.firstSon.operand - if isExportedToC(c, g, item): - # This symbol is alive and everything its body references. - followLater(c, g, c.thisModule, item) - else: - for son in sonsReadonly(tree, n): - aliveCode(c, g, tree, son) - -proc followNow(c: var AliveContext; g: PackedModuleGraph) = - ## Mark all entries in the stack. Marking can add more entries - ## to the stack but eventually we have looked at every alive symbol. - while c.stack.len > 0: - let (modId, opt, ast) = c.stack.pop() - c.thisModule = modId - c.options = opt - aliveCode(c, g, g[modId].fromDisk.bodies, ast) - -proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms = - ## Entry point for our DCE algorithm. - var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf), - thisModule: -1, alive: newSeq[IntSet](g.len), - options: conf.options) - for i in countdown(high(g), 0): - if g[i].status != undefined: - c.thisModule = i - for p in allNodes(g[i].fromDisk.topLevel): - aliveCode(c, g, g[i].fromDisk.topLevel, p) - - followNow(c, g) - result = move(c.alive) - -proc isAlive*(a: AliveSyms; module: int, item: int32): bool = - ## Backends use this to query if a symbol is `alive` which means - ## we need to produce (C/C++/etc) code for it. - result = a[module].contains(item) - diff --git a/compiler/ic/design.rst b/compiler/ic/design.rst index b096e3103..60434e4b8 100644 --- a/compiler/ic/design.rst +++ b/compiler/ic/design.rst @@ -7,8 +7,12 @@ The frontend produces a set of `.rod` files. Every `.nim` module produces its own `.rod` file. - The IR must be a faithful representation of the AST in memory. -- The backend can do its own caching but doesn't have to. In the - current implementation the backend also caches its results. +- The backend can do its own caching but doesn't have to. +- We know by comparing 'nim check compiler/nim' against 'nim c compiler/nim' + that 2/3 of the compiler's runtime is spent in the frontend. Hence we + implement IC for the frontend first and only later for the backend. The + backend will recompile everything until we implement its own caching + mechanisms. Advantage of the "set of files" vs the previous global database: - By construction, we either read from the `.rod` file or from the @@ -29,28 +33,9 @@ are rod-file specific too. Global state ------------ -There is no global state. +Global persistent state will be kept in a project specific `.rod` file. Rod File Format --------------- It's a simple binary file format. `rodfiles.nim` contains some details. - - -Backend -------- - -Nim programmers have to come to enjoy whole-program dead code elimination, -by default. Since this is a "whole program" optimization, it does break -modularity. However, thanks to the packed AST representation we can perform -this global analysis without having to unpack anything. This is basically -a mark&sweep GC algorithm: - -- Start with the top level statements. Every symbol that is referenced - from a top level statement is not "dead" and needs to be compiled by - the backend. -- Every symbol referenced from a referenced symbol also has to be - compiled. - -Caching logic: Only if the set of alive symbols is different from the -last run, the module has to be regenerated. diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim deleted file mode 100644 index 36a57f11e..000000000 --- a/compiler/ic/ic.nim +++ /dev/null @@ -1,1231 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2020 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import hashes, tables, intsets, std/sha1 -import packed_ast, bitabs, rodfiles -import ".." / [ast, idents, lineinfos, msgs, ropes, options, - pathutils, condsyms, packages, modulepaths] -#import ".." / [renderer, astalgo] -from os import removeFile, isAbsolute - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions, formatfloat] - -type - PackedConfig* = object - backend: TBackend - selectedGC: TGCMode - cCompiler: TSystemCC - options: TOptions - globalOptions: TGlobalOptions - - ModuleBackendFlag* = enum - HasDatInitProc - HasModuleInitProc - - PackedModule* = object ## the parts of a PackedEncoder that are part of the .rod file - definedSymbols: string - moduleFlags: TSymFlags - includes*: seq[(LitId, string)] # first entry is the module filename itself - imports: seq[LitId] # the modules this module depends on - toReplay*: PackedTree # pragmas and VM specific state to replay. - topLevel*: PackedTree # top level statements - bodies*: PackedTree # other trees. Referenced from typ.n and sym.ast by their position. - #producedGenerics*: Table[GenericKey, SymId] - exports*: seq[(LitId, int32)] - hidden*: seq[(LitId, int32)] - reexports*: seq[(LitId, PackedItemId)] - compilerProcs*: seq[(LitId, int32)] - converters*, methods*, trmacros*, pureEnums*: seq[int32] - - typeInstCache*: seq[(PackedItemId, PackedItemId)] - procInstCache*: seq[PackedInstantiation] - attachedOps*: seq[(PackedItemId, TTypeAttachedOp, PackedItemId)] - methodsPerType*: seq[(PackedItemId, int, PackedItemId)] - enumToStringProcs*: seq[(PackedItemId, PackedItemId)] - - emittedTypeInfo*: seq[string] - backendFlags*: set[ModuleBackendFlag] - - syms*: seq[PackedSym] - types*: seq[PackedType] - strings*: BiTable[string] # we could share these between modules. - numbers*: BiTable[BiggestInt] # we also store floats in here so - # that we can assure that every bit is kept - - cfg: PackedConfig - - PackedEncoder* = object - #m*: PackedModule - thisModule*: int32 - lastFile*: FileIndex # remember the last lookup entry. - lastLit*: LitId - filenames*: Table[FileIndex, LitId] - pendingTypes*: seq[PType] - pendingSyms*: seq[PSym] - typeMarker*: IntSet #Table[ItemId, TypeId] # ItemId.item -> TypeId - symMarker*: IntSet #Table[ItemId, SymId] # ItemId.item -> SymId - config*: ConfigRef - -proc toString*(tree: PackedTree; n: NodePos; m: PackedModule; nesting: int; - result: var string) = - let pos = n.int - if result.len > 0 and result[^1] notin {' ', '\n'}: - result.add ' ' - - result.add $tree[pos].kind - case tree.nodes[pos].kind - of nkNone, nkEmpty, nkNilLit, nkType: discard - of nkIdent, nkStrLit..nkTripleStrLit: - result.add " " - result.add m.strings[LitId tree.nodes[pos].operand] - of nkSym: - result.add " " - result.add m.strings[m.syms[tree.nodes[pos].operand].name] - of directIntLit: - result.add " " - result.addInt tree.nodes[pos].operand - of externSIntLit: - result.add " " - result.addInt m.numbers[LitId tree.nodes[pos].operand] - of externUIntLit: - result.add " " - result.addInt cast[uint64](m.numbers[LitId tree.nodes[pos].operand]) - of nkFloatLit..nkFloat128Lit: - result.add " " - result.addFloat cast[BiggestFloat](m.numbers[LitId tree.nodes[pos].operand]) - else: - result.add "(\n" - for i in 1..(nesting+1)*2: result.add ' ' - for child in sonsReadonly(tree, n): - toString(tree, child, m, nesting + 1, result) - result.add "\n" - for i in 1..nesting*2: result.add ' ' - result.add ")" - #for i in 1..nesting*2: result.add ' ' - -proc toString*(tree: PackedTree; n: NodePos; m: PackedModule): string = - result = "" - toString(tree, n, m, 0, result) - -proc debug*(tree: PackedTree; m: PackedModule) = - stdout.write toString(tree, NodePos 0, m) - -proc isActive*(e: PackedEncoder): bool = e.config != nil -proc disable(e: var PackedEncoder) = e.config = nil - -template primConfigFields(fn: untyped) {.dirty.} = - fn backend - fn selectedGC - fn cCompiler - fn options - fn globalOptions - -proc definedSymbolsAsString(config: ConfigRef): string = - result = newStringOfCap(200) - result.add "config" - for d in definedSymbolNames(config.symbols): - result.add ' ' - result.add d - -proc rememberConfig(c: var PackedEncoder; m: var PackedModule; config: ConfigRef; pc: PackedConfig) = - m.definedSymbols = definedSymbolsAsString(config) - #template rem(x) = - # c.m.cfg.x = config.x - #primConfigFields rem - m.cfg = pc - -const - debugConfigDiff = defined(debugConfigDiff) - -when debugConfigDiff: - import hashes, tables, intsets, sha1, strutils, sets - -proc configIdentical(m: PackedModule; config: ConfigRef): bool = - result = m.definedSymbols == definedSymbolsAsString(config) - when debugConfigDiff: - if not result: - var wordsA = m.definedSymbols.split(Whitespace).toHashSet() - var wordsB = definedSymbolsAsString(config).split(Whitespace).toHashSet() - for c in wordsA - wordsB: - echo "in A but not in B ", c - for c in wordsB - wordsA: - echo "in B but not in A ", c - template eq(x) = - result = result and m.cfg.x == config.x - when debugConfigDiff: - if m.cfg.x != config.x: - echo "B ", m.cfg.x, " ", config.x - primConfigFields eq - -proc rememberStartupConfig*(dest: var PackedConfig, config: ConfigRef) = - template rem(x) = - dest.x = config.x - primConfigFields rem - dest.globalOptions.excl optForceFullMake - -proc hashFileCached(conf: ConfigRef; fileIdx: FileIndex): string = - result = msgs.getHash(conf, fileIdx) - if result.len == 0: - let fullpath = msgs.toFullPath(conf, fileIdx) - result = $secureHashFile(fullpath) - msgs.setHash(conf, fileIdx, result) - -proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModule): LitId = - ## store a file index as a literal - if x == c.lastFile: - result = c.lastLit - else: - result = c.filenames.getOrDefault(x) - if result == LitId(0): - let p = msgs.toFullPath(c.config, x) - result = getOrIncl(m.strings, p) - c.filenames[x] = result - c.lastFile = x - c.lastLit = result - assert result != LitId(0) - -proc toFileIndex*(x: LitId; m: PackedModule; config: ConfigRef): FileIndex = - result = msgs.fileInfoIdx(config, AbsoluteFile m.strings[x]) - -proc includesIdentical(m: var PackedModule; config: ConfigRef): bool = - for it in mitems(m.includes): - if hashFileCached(config, toFileIndex(it[0], m, config)) != it[1]: - return false - result = true - -proc initEncoder*(c: var PackedEncoder; m: var PackedModule; moduleSym: PSym; config: ConfigRef; pc: PackedConfig) = - ## setup a context for serializing to packed ast - c.thisModule = moduleSym.itemId.module - c.config = config - m.moduleFlags = moduleSym.flags - m.bodies = newTreeFrom(m.topLevel) - m.toReplay = newTreeFrom(m.topLevel) - - c.lastFile = FileIndex(-10) - - let thisNimFile = FileIndex c.thisModule - var h = msgs.getHash(config, thisNimFile) - if h.len == 0: - let fullpath = msgs.toFullPath(config, thisNimFile) - if isAbsolute(fullpath): - # For NimScript compiler API support the main Nim file might be from a stream. - h = $secureHashFile(fullpath) - msgs.setHash(config, thisNimFile, h) - m.includes.add((toLitId(thisNimFile, c, m), h)) # the module itself - - rememberConfig(c, m, config, pc) - -proc addIncludeFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex) = - m.includes.add((toLitId(f, c, m), hashFileCached(c.config, f))) - -proc addImportFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex) = - m.imports.add toLitId(f, c, m) - -proc addHidden*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - let nameId = getOrIncl(m.strings, s.name.s) - m.hidden.add((nameId, s.itemId.item)) - -proc addExported*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - let nameId = getOrIncl(m.strings, s.name.s) - m.exports.add((nameId, s.itemId.item)) - -proc addConverter*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - assert c.thisModule == s.itemId.module - m.converters.add(s.itemId.item) - -proc addTrmacro*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - m.trmacros.add(s.itemId.item) - -proc addPureEnum*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - assert s.kind == skType - m.pureEnums.add(s.itemId.item) - -proc addMethod*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - m.methods.add s.itemId.item - -proc addReexport*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - let nameId = getOrIncl(m.strings, s.name.s) - m.reexports.add((nameId, PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), - item: s.itemId.item))) - -proc addCompilerProc*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - let nameId = getOrIncl(m.strings, s.name.s) - m.compilerProcs.add((nameId, s.itemId.item)) - -proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) -proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId -proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId - -proc flush(c: var PackedEncoder; m: var PackedModule) = - ## serialize any pending types or symbols from the context - while true: - if c.pendingTypes.len > 0: - discard storeType(c.pendingTypes.pop, c, m) - elif c.pendingSyms.len > 0: - discard storeSym(c.pendingSyms.pop, c, m) - else: - break - -proc toLitId(x: string; m: var PackedModule): LitId = - ## store a string as a literal - result = getOrIncl(m.strings, x) - -proc toLitId(x: BiggestInt; m: var PackedModule): LitId = - ## store an integer as a literal - result = getOrIncl(m.numbers, x) - -proc toPackedInfo(x: TLineInfo; c: var PackedEncoder; m: var PackedModule): PackedLineInfo = - PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m)) - -proc safeItemId(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId {.inline.} = - ## given a symbol, produce an ItemId with the correct properties - ## for local or remote symbols, packing the symbol as necessary - if s == nil or s.kind == skPackage: - result = nilItemId - #elif s.itemId.module == c.thisModule: - # result = PackedItemId(module: LitId(0), item: s.itemId.item) - else: - assert int(s.itemId.module) >= 0 - result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), - item: s.itemId.item) - -proc addMissing(c: var PackedEncoder; p: PSym) = - ## consider queuing a symbol for later addition to the packed tree - if p != nil and p.itemId.module == c.thisModule: - if p.itemId.item notin c.symMarker: - if not (sfForward in p.flags and p.kind in routineKinds): - c.pendingSyms.add p - -proc addMissing(c: var PackedEncoder; p: PType) = - ## consider queuing a type for later addition to the packed tree - if p != nil and p.uniqueId.module == c.thisModule: - if p.uniqueId.item notin c.typeMarker: - c.pendingTypes.add p - -template storeNode(dest, src, field) = - var nodeId: NodeId - if src.field != nil: - nodeId = getNodeId(m.bodies) - toPackedNode(src.field, m.bodies, c, m) - else: - nodeId = emptyNodeId - dest.field = nodeId - -proc storeTypeLater(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId = - # We store multiple different trees in m.bodies. For this to work out, we - # cannot immediately store types/syms. We enqueue them instead to ensure - # we only write one tree into m.bodies after the other. - if t.isNil: return nilItemId - - assert t.uniqueId.module >= 0 - assert t.uniqueId.item > 0 - result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) - if t.uniqueId.module == c.thisModule: - # the type belongs to this module, so serialize it here, eventually. - addMissing(c, t) - -proc storeSymLater(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId = - if s.isNil: return nilItemId - assert s.itemId.module >= 0 - assert s.itemId.module >= 0 - result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) - if s.itemId.module == c.thisModule: - # the sym belongs to this module, so serialize it here, eventually. - addMissing(c, s) - -proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId = - ## serialize a ptype - if t.isNil: return nilItemId - - assert t.uniqueId.module >= 0 - assert t.uniqueId.item > 0 - result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) - - if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item): - if t.uniqueId.item >= m.types.len: - setLen m.types, t.uniqueId.item+1 - - var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv, - size: t.size, align: t.align, nonUniqueId: t.itemId.item, - paddingAtEnd: t.paddingAtEnd) - storeNode(p, t, n) - p.typeInst = t.typeInst.storeType(c, m) - for kid in items t.sons: - p.types.add kid.storeType(c, m) - c.addMissing t.sym - p.sym = t.sym.safeItemId(c, m) - c.addMissing t.owner - p.owner = t.owner.safeItemId(c, m) - - # fill the reserved slot, nothing else: - m.types[t.uniqueId.item] = p - -proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib = - ## the plib hangs off the psym via the .annex field - if l.isNil: return - result.kind = l.kind - result.generated = l.generated - result.isOverriden = l.isOverriden - result.name = toLitId($l.name, m) - storeNode(result, l, path) - -proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId = - ## serialize a psym - if s.isNil: return nilItemId - - assert s.itemId.module >= 0 - result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) - - if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item): - if s.itemId.item >= m.syms.len: - setLen m.syms, s.itemId.item+1 - - assert sfForward notin s.flags - - var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic, - position: s.position, offset: s.offset, options: s.options, - name: s.name.s.toLitId(m)) - - storeNode(p, s, ast) - storeNode(p, s, constraint) - - if s.kind in {skLet, skVar, skField, skForVar}: - c.addMissing s.guard - p.guard = s.guard.safeItemId(c, m) - p.bitsize = s.bitsize - p.alignment = s.alignment - - p.externalName = toLitId(s.loc.r, m) - p.locFlags = s.loc.flags - c.addMissing s.typ - p.typ = s.typ.storeType(c, m) - c.addMissing s.owner - p.owner = s.owner.safeItemId(c, m) - p.annex = toPackedLib(s.annex, c, m) - when hasFFI: - p.cname = toLitId(s.cname, m) - - # fill the reserved slot, nothing else: - m.syms[s.itemId.item] = p - -proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) = - ## add a remote symbol reference to the tree - let info = n.info.toPackedInfo(c, m) - ir.nodes.add PackedNode(kind: nkModuleRef, operand: 3.int32, # spans 3 nodes in total - typeId: storeTypeLater(n.typ, c, m), info: info) - ir.nodes.add PackedNode(kind: nkInt32Lit, info: info, - operand: toLitId(n.sym.itemId.module.FileIndex, c, m).int32) - ir.nodes.add PackedNode(kind: nkInt32Lit, info: info, - operand: n.sym.itemId.item) - -proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) = - ## serialize a node into the tree - if n == nil: - ir.nodes.add PackedNode(kind: nkNilRodNode, flags: {}, operand: 1) - return - let info = toPackedInfo(n.info, c, m) - case n.kind - of nkNone, nkEmpty, nkNilLit, nkType: - ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, operand: 0, - typeId: storeTypeLater(n.typ, c, m), info: info) - of nkIdent: - ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.strings, n.ident.s), - typeId: storeTypeLater(n.typ, c, m), info: info) - of nkSym: - if n.sym.itemId.module == c.thisModule: - # it is a symbol that belongs to the module we're currently - # packing: - let id = n.sym.storeSymLater(c, m).item - ir.nodes.add PackedNode(kind: nkSym, flags: n.flags, operand: id, - typeId: storeTypeLater(n.typ, c, m), info: info) - else: - # store it as an external module reference: - addModuleRef(n, ir, c, m) - of directIntLit: - ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32(n.intVal), - typeId: storeTypeLater(n.typ, c, m), info: info) - of externIntLit: - ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.numbers, n.intVal), - typeId: storeTypeLater(n.typ, c, m), info: info) - of nkStrLit..nkTripleStrLit: - ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.strings, n.strVal), - typeId: storeTypeLater(n.typ, c, m), info: info) - of nkFloatLit..nkFloat128Lit: - ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.numbers, cast[BiggestInt](n.floatVal)), - typeId: storeTypeLater(n.typ, c, m), info: info) - else: - let patchPos = ir.prepare(n.kind, n.flags, - storeTypeLater(n.typ, c, m), info) - for i in 0.. 0 - - if not g[si].typesInit: - g[si].typesInit = true - setLen g[si].types, g[si].fromDisk.types.len - - if g[si].types[t.item] == nil: - result = typeHeaderFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item) - # store it here early on, so that recursions work properly: - g[si].types[t.item] = result - typeBodyFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item, result) - else: - result = g[si].types[t.item] - assert result.itemId.item > 0 - -proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; - fileIdx: FileIndex; m: var LoadedModule) = - m.iface = initTable[PIdent, seq[PackedItemId]]() - m.ifaceHidden = initTable[PIdent, seq[PackedItemId]]() - template impl(iface, e) = - let nameLit = e[0] - let e2 = - when e[1] is PackedItemId: e[1] - else: PackedItemId(module: LitId(0), item: e[1]) - iface.mgetOrPut(cache.getIdent(m.fromDisk.strings[nameLit]), @[]).add(e2) - - for e in m.fromDisk.exports: - m.iface.impl(e) - m.ifaceHidden.impl(e) - for e in m.fromDisk.reexports: - m.iface.impl(e) - m.ifaceHidden.impl(e) - for e in m.fromDisk.hidden: - m.ifaceHidden.impl(e) - - let filename = AbsoluteFile toFullPath(conf, fileIdx) - # We cannot call ``newSym`` here, because we have to circumvent the ID - # mechanism, which we do in order to assign each module a persistent ID. - m.module = PSym(kind: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32), - name: getIdent(cache, splitFile(filename).name), - info: newLineInfo(fileIdx, 1, 1), - position: int(fileIdx)) - m.module.owner = getPackage(conf, cache, fileIdx) - m.module.flags = m.fromDisk.moduleFlags - -proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; - fileIdx: FileIndex; m: var LoadedModule) = - m.module.ast = newNode(nkStmtList) - if m.fromDisk.toReplay.len > 0: - var decoder = PackedDecoder( - lastModule: int32(-1), - lastLit: LitId(0), - lastFile: FileIndex(-1), - config: conf, - cache: cache) - for p in allNodes(m.fromDisk.toReplay): - m.module.ast.add loadNodes(decoder, g, int(fileIdx), m.fromDisk.toReplay, p) - -proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; - fileIdx: FileIndex; cachedModules: var seq[FileIndex]): bool = - # Does the file belong to the fileIdx need to be recompiled? - let m = int(fileIdx) - if m >= g.len: - g.setLen(m+1) - - case g[m].status - of undefined: - g[m].status = loading - let fullpath = msgs.toFullPath(conf, fileIdx) - let rod = toRodFile(conf, AbsoluteFile fullpath) - let err = loadRodFile(rod, g[m].fromDisk, conf) - if err == ok: - result = optForceFullMake in conf.globalOptions - # check its dependencies: - for dep in g[m].fromDisk.imports: - let fid = toFileIndex(dep, g[m].fromDisk, conf) - # Warning: we need to traverse the full graph, so - # do **not use break here**! - if needsRecompile(g, conf, cache, fid, cachedModules): - result = true - - if not result: - setupLookupTables(g, conf, cache, fileIdx, g[m]) - cachedModules.add fileIdx - g[m].status = loaded - else: - g[m] = LoadedModule(status: outdated, module: g[m].module) - else: - loadError(err, rod, conf) - g[m].status = outdated - result = true - when false: loadError(err, rod, conf) - of loading, loaded: - # For loading: Assume no recompile is required. - result = false - of outdated, storing, stored: - result = true - -proc moduleFromRodFile*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; - fileIdx: FileIndex; cachedModules: var seq[FileIndex]): PSym = - ## Returns 'nil' if the module needs to be recompiled. - if needsRecompile(g, conf, cache, fileIdx, cachedModules): - result = nil - else: - result = g[int fileIdx].module - assert result != nil - assert result.position == int(fileIdx) - for m in cachedModules: - loadToReplayNodes(g, conf, cache, m, g[int m]) - -template setupDecoder() {.dirty.} = - var decoder = PackedDecoder( - lastModule: int32(-1), - lastLit: LitId(0), - lastFile: FileIndex(-1), - config: config, - cache: cache) - -proc loadProcBody*(config: ConfigRef, cache: IdentCache; - g: var PackedModuleGraph; s: PSym): PNode = - let mId = s.itemId.module - var decoder = PackedDecoder( - lastModule: int32(-1), - lastLit: LitId(0), - lastFile: FileIndex(-1), - config: config, - cache: cache) - let pos = g[mId].fromDisk.syms[s.itemId.item].ast - assert pos != emptyNodeId - result = loadProcBody(decoder, g, mId, g[mId].fromDisk.bodies, NodePos pos) - -proc loadTypeFromId*(config: ConfigRef, cache: IdentCache; - g: var PackedModuleGraph; module: int; id: PackedItemId): PType = - if id.item < g[module].types.len: - result = g[module].types[id.item] - else: - result = nil - if result == nil: - var decoder = PackedDecoder( - lastModule: int32(-1), - lastLit: LitId(0), - lastFile: FileIndex(-1), - config: config, - cache: cache) - result = loadType(decoder, g, module, id) - -proc loadSymFromId*(config: ConfigRef, cache: IdentCache; - g: var PackedModuleGraph; module: int; id: PackedItemId): PSym = - if id.item < g[module].syms.len: - result = g[module].syms[id.item] - else: - result = nil - if result == nil: - var decoder = PackedDecoder( - lastModule: int32(-1), - lastLit: LitId(0), - lastFile: FileIndex(-1), - config: config, - cache: cache) - result = loadSym(decoder, g, module, id) - -proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; config: ConfigRef): ItemId = - if id.module == LitId(0): - ItemId(module: thisModule.int32, item: id.item) - else: - ItemId(module: toFileIndex(id.module, g[thisModule].fromDisk, config).int32, item: id.item) - -proc checkForHoles(m: PackedModule; config: ConfigRef; moduleId: int) = - var bugs = 0 - for i in 1 .. high(m.syms): - if m.syms[i].kind == skUnknown: - echo "EMPTY ID ", i, " module ", moduleId, " ", toFullPath(config, FileIndex(moduleId)) - inc bugs - assert bugs == 0 - when false: - var nones = 0 - for i in 1 .. high(m.types): - inc nones, m.types[i].kind == tyNone - assert nones < 1 - -proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; - moduleSym: PSym; m: PackedModule) = - # For now only used for heavy debugging. In the future we could use this to reduce the - # compiler's memory consumption. - let idx = moduleSym.position - assert g[idx].status in {storing} - g[idx].status = loaded - assert g[idx].module == moduleSym - setupLookupTables(g, conf, cache, FileIndex(idx), g[idx]) - loadToReplayNodes(g, conf, cache, FileIndex(idx), g[idx]) - -# ---------------- symbol table handling ---------------- - -type - RodIter* = object - decoder: PackedDecoder - values: seq[PackedItemId] - i, module: int - -template interfSelect(a: LoadedModule, importHidden: bool): auto = - var ret = a.iface.addr - if importHidden: ret = a.ifaceHidden.addr - ret[] - -proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache; - g: var PackedModuleGraph; module: FileIndex; - name: PIdent, importHidden: bool): PSym = - it.decoder = PackedDecoder( - lastModule: int32(-1), - lastLit: LitId(0), - lastFile: FileIndex(-1), - config: config, - cache: cache) - it.values = g[int module].interfSelect(importHidden).getOrDefault(name) - it.i = 0 - it.module = int(module) - if it.i < it.values.len: - result = loadSym(it.decoder, g, int(module), it.values[it.i]) - inc it.i - -proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache; - g: var PackedModuleGraph; module: FileIndex, importHidden: bool): PSym = - it.decoder = PackedDecoder( - lastModule: int32(-1), - lastLit: LitId(0), - lastFile: FileIndex(-1), - config: config, - cache: cache) - it.values = @[] - it.module = int(module) - for v in g[int module].interfSelect(importHidden).values: - it.values.add v - it.i = 0 - if it.i < it.values.len: - result = loadSym(it.decoder, g, int(module), it.values[it.i]) - inc it.i - -proc nextRodIter*(it: var RodIter; g: var PackedModuleGraph): PSym = - if it.i < it.values.len: - result = loadSym(it.decoder, g, it.module, it.values[it.i]) - inc it.i - -iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache; - g: var PackedModuleGraph; module: FileIndex; - name: PIdent, importHidden: bool): PSym = - setupDecoder() - let values = g[int module].interfSelect(importHidden).getOrDefault(name) - for pid in values: - let s = loadSym(decoder, g, int(module), pid) - assert s != nil - yield s - -proc interfaceSymbol*(config: ConfigRef, cache: IdentCache; - g: var PackedModuleGraph; module: FileIndex; - name: PIdent, importHidden: bool): PSym = - setupDecoder() - let values = g[int module].interfSelect(importHidden).getOrDefault(name) - result = loadSym(decoder, g, int(module), values[0]) - -proc idgenFromLoadedModule*(m: LoadedModule): IdGenerator = - IdGenerator(module: m.module.itemId.module, symId: int32 m.fromDisk.syms.len, - typeId: int32 m.fromDisk.types.len) - -proc searchForCompilerproc*(m: LoadedModule; name: string): int32 = - # slow, linear search, but the results are cached: - for it in items(m.fromDisk.compilerProcs): - if m.fromDisk.strings[it[0]] == name: - return it[1] - return -1 - -# ------------------------- .rod file viewer --------------------------------- - -proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = - var m: PackedModule - let err = loadRodFile(rodfile, m, config, ignoreConfig=true) - if err != ok: - config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err - - when true: - echo "exports:" - for ex in m.exports: - echo " ", m.strings[ex[0]], " local ID: ", ex[1] - assert ex[0] == m.syms[ex[1]].name - # ex[1] int32 - - echo "reexports:" - for ex in m.reexports: - echo " ", m.strings[ex[0]] - # reexports*: seq[(LitId, PackedItemId)] - - echo "hidden: " & $m.hidden.len - for ex in m.hidden: - echo " ", m.strings[ex[0]], " local ID: ", ex[1] - - echo "all symbols" - for i in 0..high(m.syms): - if m.syms[i].name != LitId(0): - echo " ", m.strings[m.syms[i].name], " local ID: ", i, " kind ", m.syms[i].kind - else: - echo " local ID: ", i, " kind ", m.syms[i].kind - - echo "symbols: ", m.syms.len, " types: ", m.types.len, - " top level nodes: ", m.topLevel.nodes.len, " other nodes: ", m.bodies.nodes.len, - " strings: ", m.strings.len, " numbers: ", m.numbers.len diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim deleted file mode 100644 index d341fd653..000000000 --- a/compiler/ic/integrity.nim +++ /dev/null @@ -1,153 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2021 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Integrity checking for a set of .rod files. -## The set must cover a complete Nim project. - -import sets - -when defined(nimPreviewSlimSystem): - import std/assertions - -import ".." / [ast, modulegraphs] -import packed_ast, bitabs, ic - -type - CheckedContext = object - g: ModuleGraph - thisModule: int32 - checkedSyms: HashSet[ItemId] - checkedTypes: HashSet[ItemId] - -proc checkType(c: var CheckedContext; typeId: PackedItemId) -proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) -proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) - -proc checkTypeObj(c: var CheckedContext; typ: PackedType) = - for child in typ.types: - checkType(c, child) - if typ.n != emptyNodeId: - checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos typ.n) - if typ.sym != nilItemId: - checkForeignSym(c, typ.sym) - if typ.owner != nilItemId: - checkForeignSym(c, typ.owner) - checkType(c, typ.typeInst) - -proc checkType(c: var CheckedContext; typeId: PackedItemId) = - if typeId == nilItemId: return - let itemId = translateId(typeId, c.g.packed, c.thisModule, c.g.config) - if not c.checkedTypes.containsOrIncl(itemId): - let oldThisModule = c.thisModule - c.thisModule = itemId.module - checkTypeObj c, c.g.packed[itemId.module].fromDisk.types[itemId.item] - c.thisModule = oldThisModule - -proc checkSym(c: var CheckedContext; s: PackedSym) = - if s.name != LitId(0): - assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId s.name - checkType c, s.typ - if s.ast != emptyNodeId: - checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos s.ast) - if s.owner != nilItemId: - checkForeignSym(c, s.owner) - -proc checkLocalSym(c: var CheckedContext; item: int32) = - let itemId = ItemId(module: c.thisModule, item: item) - if not c.checkedSyms.containsOrIncl(itemId): - checkSym c, c.g.packed[c.thisModule].fromDisk.syms[item] - -proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) = - let itemId = translateId(symId, c.g.packed, c.thisModule, c.g.config) - if not c.checkedSyms.containsOrIncl(itemId): - let oldThisModule = c.thisModule - c.thisModule = itemId.module - checkSym c, c.g.packed[itemId.module].fromDisk.syms[itemId.item] - c.thisModule = oldThisModule - -proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) = - if tree[n.int].typeId != nilItemId: - checkType(c, tree[n.int].typeId) - case n.kind - of nkEmpty, nkNilLit, nkType, nkNilRodNode: - discard - of nkIdent: - assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId - of nkSym: - checkLocalSym(c, tree.nodes[n.int].operand) - of directIntLit: - discard - of externIntLit, nkFloatLit..nkFloat128Lit: - assert c.g.packed[c.thisModule].fromDisk.numbers.hasLitId n.litId - of nkStrLit..nkTripleStrLit: - assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId - of nkModuleRef: - let (n1, n2) = sons2(tree, n) - assert n1.kind == nkInt32Lit - assert n2.kind == nkInt32Lit - checkForeignSym(c, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)) - else: - for n0 in sonsReadonly(tree, n): - checkNode(c, tree, n0) - -proc checkTree(c: var CheckedContext; t: PackedTree) = - for p in allNodes(t): checkNode(c, t, p) - -proc checkLocalSymIds(c: var CheckedContext; m: PackedModule; symIds: seq[int32]) = - for symId in symIds: - assert symId >= 0 and symId < m.syms.len, $symId & " " & $m.syms.len - -proc checkModule(c: var CheckedContext; m: PackedModule) = - # We check that: - # - Every symbol references existing types and symbols. - # - Every tree node references existing types and symbols. - for i in 0..high(m.syms): - checkLocalSym c, int32(i) - - checkTree c, m.toReplay - checkTree c, m.topLevel - - for e in m.exports: - assert e[1] >= 0 and e[1] < m.syms.len - assert e[0] == m.syms[e[1]].name - - for e in m.compilerProcs: - assert e[1] >= 0 and e[1] < m.syms.len - assert e[0] == m.syms[e[1]].name - - checkLocalSymIds c, m, m.converters - checkLocalSymIds c, m, m.methods - checkLocalSymIds c, m, m.trmacros - checkLocalSymIds c, m, m.pureEnums - #[ - To do: Check all these fields: - - reexports*: seq[(LitId, PackedItemId)] - macroUsages*: seq[(PackedItemId, PackedLineInfo)] - - typeInstCache*: seq[(PackedItemId, PackedItemId)] - procInstCache*: seq[PackedInstantiation] - attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)] - methodsPerType*: seq[(PackedItemId, int, PackedItemId)] - enumToStringProcs*: seq[(PackedItemId, PackedItemId)] - ]# - -proc checkIntegrity*(g: ModuleGraph) = - var c = CheckedContext(g: g) - for i in 0..high(g.packed): - # case statement here to enforce exhaustive checks. - case g.packed[i].status - of undefined: - discard "nothing to do" - of loading: - assert false, "cannot check integrity: Module still loading" - of stored, storing, outdated, loaded: - c.thisModule = int32 i - checkModule(c, g.packed[i].fromDisk) - diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim deleted file mode 100644 index cbba591c5..000000000 --- a/compiler/ic/navigator.nim +++ /dev/null @@ -1,170 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2021 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Supports the "nim check --ic:on --defusages:FILE,LINE,COL" -## IDE-like features. It uses the set of .rod files to accomplish -## its task. The set must cover a complete Nim project. - -import sets - -from os import nil -from std/private/miscdollars import toLocation - -when defined(nimPreviewSlimSystem): - import std/assertions - -import ".." / [ast, modulegraphs, msgs, options] -import packed_ast, bitabs, ic - -type - NavContext = object - g: ModuleGraph - thisModule: int32 - trackPos: PackedLineInfo - alreadyEmitted: HashSet[string] - outputSep: char # for easier testing, use short filenames and spaces instead of tabs. - -proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool = - if current.file == trackPos.file and current.line == trackPos.line: - let col = trackPos.col - if col >= current.col and col < current.col+tokenLen: - return true - -proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool = - result = s.name != LitId(0) and - isTracked(info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len) - -proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool = - let name = c.g.packed[s.module].fromDisk.syms[s.item].name - result = name != LitId(0) and - isTracked(info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len) - -const - EmptyItemId = ItemId(module: -1'i32, item: -1'i32) - -proc search(c: var NavContext; tree: PackedTree): ItemId = - # We use the linear representation here directly: - for i in 0..high(tree.nodes): - case tree.nodes[i].kind - of nkSym: - let item = tree.nodes[i].operand - if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree.nodes[i].info): - return ItemId(module: c.thisModule, item: item) - of nkModuleRef: - if tree.nodes[i].info.line == c.trackPos.line and tree.nodes[i].info.file == c.trackPos.file: - let (n1, n2) = sons2(tree, NodePos i) - assert n1.kind == nkInt32Lit - assert n2.kind == nkInt32Lit - let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand) - let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config) - if searchForeignSym(c, itemId, tree.nodes[i].info): - return itemId - else: discard - return EmptyItemId - -proc isDecl(tree: PackedTree; n: NodePos): bool = - # XXX This is not correct yet. - const declarativeNodes = procDefs + {nkMacroDef, nkTemplateDef, - nkLetSection, nkVarSection, nkUsingStmt, nkConstSection, nkTypeSection, - nkIdentDefs, nkEnumTy, nkVarTuple} - result = n.int >= 0 and tree[n.int].kind in declarativeNodes - -proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) = - var m = "" - var file = c.g.packed[c.thisModule].fromDisk.strings[info.file] - if c.outputSep == ' ': - file = os.extractFilename file - toLocation(m, file, info.line.int, info.col.int + ColOffset) - if not c.alreadyEmitted.containsOrIncl(m): - msgWriteln c.g.config, (if isDecl: "def" else: "usage") & c.outputSep & m - -proc list(c: var NavContext; tree: PackedTree; sym: ItemId) = - for i in 0..high(tree.nodes): - case tree.nodes[i].kind - of nkSym: - let item = tree.nodes[i].operand - if sym.item == item and sym.module == c.thisModule: - usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i))) - of nkModuleRef: - let (n1, n2) = sons2(tree, NodePos i) - assert n1.kind == nkInt32Lit - assert n2.kind == nkInt32Lit - let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand) - let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config) - if itemId.item == sym.item and sym.module == itemId.module: - usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i))) - else: discard - -proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int = - for i in 0..high(g.packed): - for k in 1..high(g.packed[i].fromDisk.includes): - # we start from 1 because the first "include" file is - # the module's filename. - if os.cmpPaths(g.packed[i].fromDisk.strings[g.packed[i].fromDisk.includes[k][0]], fullPath) == 0: - return i - return -1 - -proc nav(g: ModuleGraph) = - # translate the track position to a packed position: - let unpacked = g.config.m.trackPos - var mid = unpacked.fileIndex.int - - let fullPath = toFullPath(g.config, unpacked.fileIndex) - - if g.packed[mid].status == undefined: - # check if 'mid' is an include file of some other module: - mid = searchForIncludeFile(g, fullPath) - - if mid < 0: - localError(g.config, unpacked, "unknown file name: " & fullPath) - return - - let fileId = g.packed[mid].fromDisk.strings.getKeyId(fullPath) - - if fileId == LitId(0): - internalError(g.config, unpacked, "cannot find a valid file ID") - return - - var c = NavContext( - g: g, - thisModule: int32 mid, - trackPos: PackedLineInfo(line: unpacked.line, col: unpacked.col, file: fileId), - outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t' - ) - var symId = search(c, g.packed[mid].fromDisk.topLevel) - if symId == EmptyItemId: - symId = search(c, g.packed[mid].fromDisk.bodies) - - if symId == EmptyItemId: - localError(g.config, unpacked, "no symbol at this position") - return - - for i in 0..high(g.packed): - # case statement here to enforce exhaustive checks. - case g.packed[i].status - of undefined: - discard "nothing to do" - of loading: - assert false, "cannot check integrity: Module still loading" - of stored, storing, outdated, loaded: - c.thisModule = int32 i - list(c, g.packed[i].fromDisk.topLevel, symId) - list(c, g.packed[i].fromDisk.bodies, symId) - -proc navDefinition*(g: ModuleGraph) = nav(g) -proc navUsages*(g: ModuleGraph) = nav(g) -proc navDefusages*(g: ModuleGraph) = nav(g) - -proc writeRodFiles*(g: ModuleGraph) = - for i in 0..high(g.packed): - case g.packed[i].status - of undefined, loading, stored, loaded: - discard "nothing to do" - of storing, outdated: - closeRodFile(g, g.packed[i].module) diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 87b1516fa..546e495c5 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -12,12 +12,34 @@ ## use this representation directly in all the transformations, ## it is superior. -import hashes, tables, strtabs +import std / [hashes, tables, strtabs, md5] import bitabs import ".." / [ast, options] -when defined(nimPreviewSlimSystem): - import std/assertions +const + localNamePos* = 0 + localExportMarkerPos* = 1 + localPragmaPos* = 2 + localTypePos* = 3 + localValuePos* = 4 + + typeNamePos* = 0 + typeExportMarkerPos* = 1 + typeGenericParamsPos* = 2 + typePragmaPos* = 3 + typeBodyPos* = 4 + + routineNamePos* = 0 + routineExportMarkerPos* = 1 + routinePatternPos* = 2 + routineGenericParamsPos* = 3 + routineParamsPos* = 4 + routineResultPos* = 5 + routinePragmasPos* = 6 + routineBodyPos* = 7 + +const + nkModuleRef* = nkNone # pair of (ModuleId, SymId) type SymId* = distinct int32 @@ -30,7 +52,10 @@ type module*: LitId # 0 if it's this module item*: int32 # same as the in-memory representation + TypeId* = PackedItemId + const + nilTypeId* = PackedItemId(module: LitId(0), item: -1.int32) nilItemId* = PackedItemId(module: LitId(0), item: -1.int32) const @@ -52,7 +77,7 @@ type PackedSym* = object kind*: TSymKind name*: LitId - typ*: PackedItemId + typ*: TypeId flags*: TSymFlags magic*: TMagic info*: PackedLineInfo @@ -65,7 +90,6 @@ type position*: int offset*: int externalName*: LitId # instead of TLoc - locFlags*: TLocFlags annex*: PackedLib when hasFFI: cname*: LitId @@ -76,40 +100,65 @@ type callConv*: TCallingConvention #nodekind*: TNodeKind flags*: TTypeFlags - types*: seq[PackedItemId] + types*: seq[TypeId] n*: NodeId + methods*: seq[(int, PackedItemId)] #nodeflags*: TNodeFlags sym*: PackedItemId owner*: PackedItemId + attachedOps*: array[TTypeAttachedOp, PackedItemId] size*: BiggestInt align*: int16 paddingAtEnd*: int16 + lockLevel*: TLockLevel # lock level as required for deadlock checking # not serialized: loc*: TLoc because it is backend-specific - typeInst*: PackedItemId + typeInst*: TypeId nonUniqueId*: int32 - PackedNode* = object # 28 bytes + PackedNode* = object # 20 bytes kind*: TNodeKind flags*: TNodeFlags operand*: int32 # for kind in {nkSym, nkSymDef}: SymId # for kind in {nkStrLit, nkIdent, nkNumberLit}: LitId # for kind in nkInt32Lit: direct value # for non-atom kinds: the number of nodes (for easy skipping) - typeId*: PackedItemId + typeId*: TypeId info*: PackedLineInfo + ModulePhase* = enum + preLookup, lookedUpTopLevelStmts + + GenericKey* = object + module*: int32 + name*: string + types*: seq[MD5Digest] # is this a joke? + PackedTree* = object ## usually represents a full Nim module nodes*: seq[PackedNode] + #sh*: Shared - PackedInstantiation* = object - key*, sym*: PackedItemId - concreteTypes*: seq[PackedItemId] + Shared* = ref object # shared between different versions of 'Module'. + # (though there is always exactly one valid + # version of a module) + syms*: seq[PackedSym] + types*: seq[PackedType] + strings*: BiTable[string] # we could share these between modules. + integers*: BiTable[BiggestInt] + floats*: BiTable[BiggestFloat] + #config*: ConfigRef + +proc hash*(key: GenericKey): Hash = + var h: Hash = 0 + h = h !& hash(key.module) + h = h !& hash(key.name) + h = h !& hash(key.types) + result = !$h proc `==`*(a, b: SymId): bool {.borrow.} proc hash*(a: SymId): Hash {.borrow.} proc `==`*(a, b: NodePos): bool {.borrow.} -#proc `==`*(a, b: PackedItemId): bool {.borrow.} +#proc `==`*(a, b: TypeId): bool {.borrow.} proc `==`*(a, b: NodeId): bool {.borrow.} proc newTreeFrom*(old: PackedTree): PackedTree = @@ -174,7 +223,7 @@ when false: result = PatchPos tree.nodes.len tree.nodes.add PackedNode(kind: kind, operand: 0, info: info) -proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: PackedItemId; info: PackedLineInfo): PatchPos = +proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: TypeId; info: PackedLineInfo): PatchPos = result = PatchPos tree.nodes.len tree.nodes.add PackedNode(kind: kind, flags: flags, operand: 0, info: info, typeId: typeId) @@ -249,9 +298,9 @@ iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos = proc parentImpl(tree: PackedTree; n: NodePos): NodePos = # finding the parent of a node is rather easy: var pos = n.int - 1 - while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int)): + while pos >= 0 and isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int): dec pos - #assert pos >= 0, "node has no parent" + assert pos >= 0, "node has no parent" result = NodePos(pos) template parent*(n: NodePos): NodePos = parentImpl(tree, n) @@ -284,10 +333,7 @@ template typ*(n: NodePos): PackedItemId = template flags*(n: NodePos): TNodeFlags = tree.nodes[n.int].flags -template operand*(n: NodePos): int32 = - tree.nodes[n.int].operand - -proc span*(tree: PackedTree; pos: int): int {.inline.} = +proc span(tree: PackedTree; pos: int): int {.inline.} = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) = @@ -324,7 +370,6 @@ template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1) when false: - # xxx `nkStrLit` or `nkStrLit..nkTripleStrLit:` below? proc strLit*(tree: PackedTree; n: NodePos): lent string = assert n.kind == nkStrLit result = tree.sh.strings[LitId tree.nodes[n.int].operand] @@ -365,6 +410,48 @@ const externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit} directIntLit* = nkInt32Lit +proc toString*(tree: PackedTree; n: NodePos; sh: Shared; nesting: int; + result: var string) = + let pos = n.int + if result.len > 0 and result[^1] notin {' ', '\n'}: + result.add ' ' + + result.add $tree[pos].kind + case tree.nodes[pos].kind + of nkNone, nkEmpty, nkNilLit, nkType: discard + of nkIdent, nkStrLit..nkTripleStrLit: + result.add " " + result.add sh.strings[LitId tree.nodes[pos].operand] + of nkSym: + result.add " " + result.add sh.strings[sh.syms[tree.nodes[pos].operand].name] + of directIntLit: + result.add " " + result.addInt tree.nodes[pos].operand + of externSIntLit: + result.add " " + result.addInt sh.integers[LitId tree.nodes[pos].operand] + of externUIntLit: + result.add " " + result.add $cast[uint64](sh.integers[LitId tree.nodes[pos].operand]) + else: + result.add "(\n" + for i in 1..(nesting+1)*2: result.add ' ' + for child in sonsReadonly(tree, n): + toString(tree, child, sh, nesting + 1, result) + result.add "\n" + for i in 1..nesting*2: result.add ' ' + result.add ")" + #for i in 1..nesting*2: result.add ' ' + + +proc toString*(tree: PackedTree; n: NodePos; sh: Shared): string = + result = "" + toString(tree, n, sh, 0, result) + +proc debug*(tree: PackedTree; sh: Shared) = + stdout.write toString(tree, NodePos 0, sh) + when false: proc identIdImpl(tree: PackedTree; n: NodePos): LitId = if n.kind == nkIdent: @@ -408,12 +495,67 @@ when false: copyTree(dest, tree, n) patch dest, patchPos -iterator allNodes*(tree: PackedTree): NodePos = - var p = 0 - while p < tree.len: - yield NodePos(p) - let s = span(tree, p) - inc p, s + proc hash*(table: StringTableRef): Hash = + ## XXX: really should be introduced into strtabs... + var h: Hash = 0 + for pair in pairs table: + h = h !& hash(pair) + result = !$h -proc toPackedItemId*(item: int32): PackedItemId {.inline.} = - PackedItemId(module: LitId(0), item: item) + proc hash*(config: ConfigRef): Hash = + ## XXX: vet and/or extend this + var h: Hash = 0 + h = h !& hash(config.selectedGC) + h = h !& hash(config.features) + h = h !& hash(config.legacyFeatures) + h = h !& hash(config.configVars) + h = h !& hash(config.symbols) + result = !$h + + # XXX: lazy hashes for now + type + LazyHashes = PackedSym or PackedType or PackedLib or + PackedLineInfo or PackedTree or PackedNode + + proc hash*(sh: Shared): Hash + proc hash*(s: LazyHashes): Hash + proc hash*(s: seq[LazyHashes]): Hash + + proc hash*(s: LazyHashes): Hash = + var h: Hash = 0 + for k, v in fieldPairs(s): + h = h !& hash((k, v)) + result = !$h + + proc hash*(s: seq[LazyHashes]): Hash = + ## critically, we need to hash the indices alongside their values + var h: Hash = 0 + for i, n in pairs s: + h = h !& hash((i, n)) + result = !$h + + proc hash*(sh: Shared): Hash = + ## might want to edit this... + # XXX: these have too many references + when false: + var h: Hash = 0 + h = h !& hash(sh.syms) + h = h !& hash(sh.types) + h = h !& hash(sh.strings) + h = h !& hash(sh.integers) + h = h !& hash(sh.floats) + h = h !& hash(sh.config) + result = !$h + + proc hash*(m: Module): Hash = + var h: Hash = 0 + h = h !& hash(m.name) + h = h !& hash(m.ast) + result = !$h + + template safeItemId*(x: typed; f: untyped): ItemId = + ## yield a valid ItemId value for the field of a nillable type + if x.isNil: + nilItemId + else: + x.`f` diff --git a/compiler/ic/replayer.nim b/compiler/ic/replayer.nim deleted file mode 100644 index 8eee0b3d8..000000000 --- a/compiler/ic/replayer.nim +++ /dev/null @@ -1,160 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2020 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Module that contains code to replay global VM state changes and pragma -## state like ``{.compile: "foo.c".}``. For IC (= Incremental compilation) -## support. - -import ".." / [ast, modulegraphs, trees, extccomp, btrees, - msgs, lineinfos, pathutils, options, cgmeth] - -import tables - -when defined(nimPreviewSlimSystem): - import std/assertions - -import packed_ast, ic, bitabs - -proc replayStateChanges*(module: PSym; g: ModuleGraph) = - let list = module.ast - assert list != nil - assert list.kind == nkStmtList - for n in list: - assert n.kind == nkReplayAction - # Fortunately only a tiny subset of the available pragmas need to - # be replayed here. This is always a subset of ``pragmas.stmtPragmas``. - if n.len >= 2: - internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit - case n[0].strVal - of "hint": message(g.config, n.info, hintUser, n[1].strVal) - of "warning": message(g.config, n.info, warnUser, n[1].strVal) - of "error": localError(g.config, n.info, errUser, n[1].strVal) - of "compile": - internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit - let cname = AbsoluteFile n[1].strVal - var cf = Cfile(nimname: splitFile(cname).name, cname: cname, - obj: AbsoluteFile n[2].strVal, - flags: {CfileFlag.External}, - customArgs: n[3].strVal) - extccomp.addExternalFileToCompile(g.config, cf) - of "link": - extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal) - of "passl": - extccomp.addLinkOption(g.config, n[1].strVal) - of "passc": - extccomp.addCompileOption(g.config, n[1].strVal) - of "localpassc": - extccomp.addLocalCompileOption(g.config, n[1].strVal, toFullPathConsiderDirty(g.config, module.info.fileIndex)) - of "cppdefine": - options.cppDefine(g.config, n[1].strVal) - of "inc": - let destKey = n[1].strVal - let by = n[2].intVal - let v = getOrDefault(g.cacheCounters, destKey) - g.cacheCounters[destKey] = v+by - of "put": - let destKey = n[1].strVal - let key = n[2].strVal - let val = n[3] - if not contains(g.cacheTables, destKey): - g.cacheTables[destKey] = initBTree[string, PNode]() - if not contains(g.cacheTables[destKey], key): - g.cacheTables[destKey].add(key, val) - else: - internalError(g.config, n.info, "key already exists: " & key) - of "incl": - let destKey = n[1].strVal - let val = n[2] - if not contains(g.cacheSeqs, destKey): - g.cacheSeqs[destKey] = newTree(nkStmtList, val) - else: - block search: - for existing in g.cacheSeqs[destKey]: - if exprStructuralEquivalent(existing, val, strictSymEquality=true): - break search - g.cacheSeqs[destKey].add val - of "add": - let destKey = n[1].strVal - let val = n[2] - if not contains(g.cacheSeqs, destKey): - g.cacheSeqs[destKey] = newTree(nkStmtList, val) - else: - g.cacheSeqs[destKey].add val - else: - internalAssert g.config, false - -proc replayBackendProcs*(g: ModuleGraph; module: int) = - for it in mitems(g.packed[module].fromDisk.attachedOps): - let key = translateId(it[0], g.packed, module, g.config) - let op = it[1] - let tmp = translateId(it[2], g.packed, module, g.config) - let symId = FullId(module: tmp.module, packed: it[2]) - g.attachedOps[op][key] = LazySym(id: symId, sym: nil) - - for it in mitems(g.packed[module].fromDisk.enumToStringProcs): - let key = translateId(it[0], g.packed, module, g.config) - let tmp = translateId(it[1], g.packed, module, g.config) - let symId = FullId(module: tmp.module, packed: it[1]) - g.enumToStringProcs[key] = LazySym(id: symId, sym: nil) - -proc replayGenericCacheInformation*(g: ModuleGraph; module: int) = - ## We remember the generic instantiations a module performed - ## in order to to avoid the code bloat that generic code tends - ## to imply. This is cheaper than deduplication of identical - ## generic instantiations. However, deduplication is more - ## powerful and general and I hope to implement it soon too - ## (famous last words). - assert g.packed[module].status == loaded - for it in g.packed[module].fromDisk.typeInstCache: - let key = translateId(it[0], g.packed, module, g.config) - g.typeInstCache.mgetOrPut(key, @[]).add LazyType(id: FullId(module: module, packed: it[1]), typ: nil) - - for it in mitems(g.packed[module].fromDisk.procInstCache): - let key = translateId(it.key, g.packed, module, g.config) - let sym = translateId(it.sym, g.packed, module, g.config) - var concreteTypes = newSeq[FullId](it.concreteTypes.len) - for i in 0..high(it.concreteTypes): - let tmp = translateId(it.concreteTypes[i], g.packed, module, g.config) - concreteTypes[i] = FullId(module: tmp.module, packed: it.concreteTypes[i]) - - g.procInstCache.mgetOrPut(key, @[]).add LazyInstantiation( - module: module, sym: FullId(module: sym.module, packed: it.sym), - concreteTypes: concreteTypes, inst: nil) - - for it in mitems(g.packed[module].fromDisk.methodsPerType): - let key = translateId(it[0], g.packed, module, g.config) - let col = it[1] - let tmp = translateId(it[2], g.packed, module, g.config) - let symId = FullId(module: tmp.module, packed: it[2]) - g.methodsPerType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil)) - - replayBackendProcs(g, module) - - for it in mitems(g.packed[module].fromDisk.methods): - let sym = loadSymFromId(g.config, g.cache, g.packed, module, - PackedItemId(module: LitId(0), item: it)) - methodDef(g, g.idgen, sym) - - when false: - # not used anymore: - for it in mitems(g.packed[module].fromDisk.compilerProcs): - let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it[1])) - g.lazyCompilerprocs[g.packed[module].fromDisk.sh.strings[it[0]]] = symId - - for it in mitems(g.packed[module].fromDisk.converters): - let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it)) - g.ifaces[module].converters.add LazySym(id: symId, sym: nil) - - for it in mitems(g.packed[module].fromDisk.trmacros): - let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it)) - g.ifaces[module].patterns.add LazySym(id: symId, sym: nil) - - for it in mitems(g.packed[module].fromDisk.pureEnums): - let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it)) - g.ifaces[module].pureEnums.add LazySym(id: symId, sym: nil) diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index e492624d0..99ce183f2 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -7,67 +7,8 @@ # distribution, for details about the copyright. # -## Low level binary format used by the compiler to store and load various AST -## and related data. -## -## NB: this is incredibly low level and if you're interested in how the -## compiler works and less a storage format, you're probably looking for -## the `ic` or `packed_ast` modules to understand the logical format. - from typetraits import supportsCopyMem -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - -## Overview -## ======== -## `RodFile` represents a Rod File (versioned binary format), and the -## associated data for common interactions such as IO and error tracking -## (`RodFileError`). The file format broken up into sections (`RodSection`) -## and preceded by a header (see: `cookie`). The precise layout, section -## ordering and data following the section are determined by the user. See -## `ic.loadRodFile`. -## -## A basic but "wrong" example of the lifecycle: -## --------------------------------------------- -## 1. `create` or `open` - create a new one or open an existing -## 2. `storeHeader` - header info -## 3. `storePrim` or `storeSeq` - save your stuff -## 4. `close` - and we're done -## -## Now read the bits below to understand what's missing. -## -## ### 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` & -## `loadSection`, respectively. -## -## No Error Handling: -## The API is centered around IO and prone to error, each operation checks or -## sets the `RodFile.err` field. A user of this API needs to handle these -## appropriately. -## -## API Notes -## ========= -## -## Valid inputs for Rod files -## -------------------------- -## ASTs, hopes, dreams, and anything as long as it and any children it may have -## support `copyMem`. This means anything that is not a pointer and that does not contain a pointer. At a glance these are: -## * string -## * objects & tuples (fields are recursed) -## * sequences AKA `seq[T]` -## -## Note on error handling style -## ---------------------------- -## A flag based approach is used where operations no-op in case of a -## preexisting error and set the flag if they encounter one. -## -## Misc -## ---- -## * 'Prim' is short for 'primitive', as in a non-sequence type - type RodSection* = enum versionSection @@ -75,31 +16,15 @@ type stringsSection checkSumsSection depsSection - numbersSection - exportsSection - hiddenSection - reexportsSection - compilerProcsSection - trmacrosSection - convertersSection - methodsSection - pureEnumsSection - toReplaySection + integersSection + floatsSection topLevelSection bodiesSection symsSection typesSection - typeInstCacheSection - procInstCacheSection - attachedOpsSection - methodsPerTypeSection - enumToStringProcsSection - typeInfoSection # required by the backend - backendFlagsSection - aliveSymsSection # beware, this is stored in a `.alivesyms` file. RodFileError* = enum - ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch, + ok, tooBig, ioFailure, wrongHeader, wrongSection, configMismatch, includeFileChanged RodFile* = object @@ -111,139 +36,108 @@ type const RodVersion = 1 cookie = [byte(0), byte('R'), byte('O'), byte('D'), - byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)] - -proc setError(f: var RodFile; err: RodFileError) {.inline.} = - f.err = err - #raise newException(IOError, "IO error") + byte(0), byte(0), byte(0), byte(RodVersion)] proc storePrim*(f: var RodFile; s: string) = - ## Stores a string. - ## The len is prefixed to allow for later retreival. if f.err != ok: return if s.len >= high(int32): - setError f, tooBig + f.err = tooBig return var lenPrefix = int32(s.len) if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): - setError f, ioFailure + f.err = ioFailure else: if s.len != 0: if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len: - setError f, ioFailure + f.err = ioFailure proc storePrim*[T](f: var RodFile; x: T) = - ## Stores a non-sequence/string `T`. - ## If `T` doesn't support `copyMem` and is an object or tuple then the fields - ## are written -- the user from context will need to know which `T` to load. if f.err != ok: return when supportsCopyMem(T): if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x): - setError f, ioFailure + f.err = ioFailure elif T is tuple: for y in fields(x): storePrim(f, y) - elif T is object: - for y in fields(x): - when y is seq: - storeSeq(f, y) - else: - storePrim(f, y) else: {.error: "unsupported type for 'storePrim'".} proc storeSeq*[T](f: var RodFile; s: seq[T]) = - ## Stores a sequence of `T`s, with the len as a prefix for later retrieval. if f.err != ok: return if s.len >= high(int32): - setError f, tooBig + f.err = tooBig return var lenPrefix = int32(s.len) if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): - setError f, ioFailure + f.err = ioFailure else: for i in 0.. 0: if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len: - setError f, ioFailure + f.err = ioFailure proc loadPrim*[T](f: var RodFile; x: var T) = - ## Load a non-sequence/string `T`. if f.err != ok: return when supportsCopyMem(T): if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x): - setError f, ioFailure + f.err = ioFailure elif T is tuple: for y in fields(x): loadPrim(f, y) - elif T is object: - for y in fields(x): - when y is seq: - loadSeq(f, y) - else: - loadPrim(f, y) else: {.error: "unsupported type for 'loadPrim'".} proc loadSeq*[T](f: var RodFile; s: var seq[T]) = - ## `T` must be compatible with `copyMem`, see `loadPrim` if f.err != ok: return var lenPrefix = int32(0) if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): - setError f, ioFailure + f.err = ioFailure else: s = newSeq[T](lenPrefix) for i in 0.. TypeId + symMarker*: IntSet #Table[ItemId, SymId] # ItemId.item -> SymId + config*: ConfigRef + +template primConfigFields(fn: untyped) {.dirty.} = + fn backend + fn selectedGC + fn cCompiler + fn options + fn globalOptions + +proc definedSymbolsAsString(config: ConfigRef): string = + result = newStringOfCap(200) + result.add "config" + for d in definedSymbolNames(config.symbols): + result.add ' ' + result.add d + +proc rememberConfig(c: var PackedEncoder; config: ConfigRef) = + c.m.definedSymbols = definedSymbolsAsString(config) + + template rem(x) = + c.m.cfg.x = config.x + primConfigFields rem + +proc configIdentical(m: PackedModule; config: ConfigRef): bool = + result = m.definedSymbols == definedSymbolsAsString(config) + template eq(x) = + result = result and m.cfg.x == config.x + primConfigFields eq + +proc hashFileCached(conf: ConfigRef; fileIdx: FileIndex): string = + result = msgs.getHash(conf, fileIdx) + if result.len == 0: + let fullpath = msgs.toFullPath(conf, fileIdx) + result = $secureHashFile(fullpath) + msgs.setHash(conf, fileIdx, result) + +proc toLitId(x: FileIndex; c: var PackedEncoder): LitId = + ## store a file index as a literal + if x == c.lastFile: + result = c.lastLit + else: + result = c.filenames.getOrDefault(x) + if result == LitId(0): + let p = msgs.toFullPath(c.config, x) + result = getOrIncl(c.m.sh.strings, p) + c.filenames[x] = result + c.lastFile = x + c.lastLit = result + assert result != LitId(0) + +proc toFileIndex(x: LitId; m: PackedModule; config: ConfigRef): FileIndex = + result = msgs.fileInfoIdx(config, AbsoluteFile m.sh.strings[x]) + +proc includesIdentical(m: var PackedModule; config: ConfigRef): bool = + for it in mitems(m.includes): + if hashFileCached(config, toFileIndex(it[0], m, config)) != it[1]: + return false + result = true + +proc initEncoder*(c: var PackedEncoder; m: PSym; config: ConfigRef) = + ## setup a context for serializing to packed ast + c.m.sh = Shared() + c.thisModule = m.itemId.module + c.config = config + c.m.bodies = newTreeFrom(c.m.topLevel) + c.m.hidden = newTreeFrom(c.m.topLevel) + + let thisNimFile = FileIndex c.thisModule + var h = msgs.getHash(config, thisNimFile) + if h.len == 0: + let fullpath = msgs.toFullPath(config, thisNimFile) + if isAbsolute(fullpath): + # For NimScript compiler API support the main Nim file might be from a stream. + h = $secureHashFile(fullpath) + msgs.setHash(config, thisNimFile, h) + c.m.includes.add((toLitId(thisNimFile, c), h)) # the module itself + +proc addIncludeFileDep*(c: var PackedEncoder; f: FileIndex) = + c.m.includes.add((toLitId(f, c), hashFileCached(c.config, f))) + +proc addImportFileDep*(c: var PackedEncoder; f: FileIndex) = + c.m.imports.add toLitId(f, c) + +proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder) +proc toPackedSym*(s: PSym; c: var PackedEncoder): PackedItemId +proc toPackedType(t: PType; c: var PackedEncoder): PackedItemId + +proc flush(c: var PackedEncoder) = + ## serialize any pending types or symbols from the context + while true: + if c.pendingTypes.len > 0: + discard toPackedType(c.pendingTypes.pop, c) + elif c.pendingSyms.len > 0: + discard toPackedSym(c.pendingSyms.pop, c) + else: + break + +proc toLitId(x: string; c: var PackedEncoder): LitId = + ## store a string as a literal + result = getOrIncl(c.m.sh.strings, x) + +proc toLitId(x: BiggestInt; c: var PackedEncoder): LitId = + ## store an integer as a literal + result = getOrIncl(c.m.sh.integers, x) + +proc toPackedInfo(x: TLineInfo; c: var PackedEncoder): PackedLineInfo = + PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c)) + +proc safeItemId(s: PSym; c: var PackedEncoder): PackedItemId {.inline.} = + ## given a symbol, produce an ItemId with the correct properties + ## for local or remote symbols, packing the symbol as necessary + if s == nil: + result = nilItemId + elif s.itemId.module == c.thisModule: + result = PackedItemId(module: LitId(0), item: s.itemId.item) + else: + result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c), + item: s.itemId.item) + +proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder) = + ## add a remote symbol reference to the tree + let info = n.info.toPackedInfo(c) + ir.nodes.add PackedNode(kind: nkModuleRef, operand: 2.int32, # 2 kids... + typeId: toPackedType(n.typ, c), info: info) + ir.nodes.add PackedNode(kind: nkInt32Lit, info: info, + operand: toLitId(n.sym.itemId.module.FileIndex, c).int32) + ir.nodes.add PackedNode(kind: nkInt32Lit, info: info, + operand: n.sym.itemId.item) + +proc addMissing(c: var PackedEncoder; p: PSym) = + ## consider queuing a symbol for later addition to the packed tree + if p != nil and p.itemId.module == c.thisModule: + if p.itemId.item notin c.symMarker: + c.pendingSyms.add p + +proc addMissing(c: var PackedEncoder; p: PType) = + ## consider queuing a type for later addition to the packed tree + if p != nil and p.uniqueId.module == c.thisModule: + if p.uniqueId.item notin c.typeMarker: + c.pendingTypes.add p + +template storeNode(dest, src, field) = + var nodeId: NodeId + if src.field != nil: + nodeId = getNodeId(c.m.bodies) + toPackedNode(src.field, c.m.bodies, c) + else: + nodeId = emptyNodeId + dest.field = nodeId + +proc toPackedType(t: PType; c: var PackedEncoder): PackedItemId = + ## serialize a ptype + if t.isNil: return nilTypeId + + if t.uniqueId.module != c.thisModule: + # XXX Assert here that it already was serialized in the foreign module! + # it is a foreign type: + return PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c), item: t.uniqueId.item) + + if not c.typeMarker.containsOrIncl(t.uniqueId.item): + if t.uniqueId.item >= c.m.sh.types.len: + setLen c.m.sh.types, t.uniqueId.item+1 + + var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv, + size: t.size, align: t.align, nonUniqueId: t.itemId.item, + paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel) + storeNode(p, t, n) + + for op, s in pairs t.attachedOps: + c.addMissing s + p.attachedOps[op] = s.safeItemId(c) + + p.typeInst = t.typeInst.toPackedType(c) + for kid in items t.sons: + p.types.add kid.toPackedType(c) + for i, s in items t.methods: + c.addMissing s + p.methods.add (i, s.safeItemId(c)) + c.addMissing t.sym + p.sym = t.sym.safeItemId(c) + c.addMissing t.owner + p.owner = t.owner.safeItemId(c) + + # fill the reserved slot, nothing else: + c.m.sh.types[t.uniqueId.item] = p + + result = PackedItemId(module: LitId(0), item: t.uniqueId.item) + +proc toPackedLib(l: PLib; c: var PackedEncoder): PackedLib = + ## the plib hangs off the psym via the .annex field + if l.isNil: return + result.kind = l.kind + result.generated = l.generated + result.isOverriden = l.isOverriden + result.name = toLitId($l.name, c) + storeNode(result, l, path) + +proc toPackedSym*(s: PSym; c: var PackedEncoder): PackedItemId = + ## serialize a psym + if s.isNil: return nilItemId + + if s.itemId.module != c.thisModule: + # XXX Assert here that it already was serialized in the foreign module! + # it is a foreign symbol: + return PackedItemId(module: toLitId(s.itemId.module.FileIndex, c), item: s.itemId.item) + + if not c.symMarker.containsOrIncl(s.itemId.item): + if s.itemId.item >= c.m.sh.syms.len: + setLen c.m.sh.syms, s.itemId.item+1 + + var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c), magic: s.magic, + position: s.position, offset: s.offset, options: s.options, + name: s.name.s.toLitId(c)) + + storeNode(p, s, ast) + storeNode(p, s, constraint) + + if s.kind in {skLet, skVar, skField, skForVar}: + c.addMissing s.guard + p.guard = s.guard.safeItemId(c) + p.bitsize = s.bitsize + p.alignment = s.alignment + + p.externalName = toLitId(if s.loc.r.isNil: "" else: $s.loc.r, c) + c.addMissing s.typ + p.typ = s.typ.toPackedType(c) + c.addMissing s.owner + p.owner = s.owner.safeItemId(c) + p.annex = toPackedLib(s.annex, c) + when hasFFI: + p.cname = toLitId(s.cname, c) + + # fill the reserved slot, nothing else: + c.m.sh.syms[s.itemId.item] = p + + result = PackedItemId(module: LitId(0), item: s.itemId.item) + +proc toSymNode(n: PNode; ir: var PackedTree; c: var PackedEncoder) = + ## store a local or remote psym reference in the tree + assert n.kind == nkSym + template s: PSym = n.sym + let id = s.toPackedSym(c).item + if s.itemId.module == c.thisModule: + # it is a symbol that belongs to the module we're currently + # packing: + ir.addSym(id, toPackedInfo(n.info, c)) + else: + # store it as an external module reference: + addModuleRef(n, ir, c) + +proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder) = + ## serialize a node into the tree + if n.isNil: return + let info = toPackedInfo(n.info, c) + case n.kind + of nkNone, nkEmpty, nkNilLit, nkType: + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, operand: 0, + typeId: toPackedType(n.typ, c), info: info) + of nkIdent: + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32 getOrIncl(c.m.sh.strings, n.ident.s), + typeId: toPackedType(n.typ, c), info: info) + of nkSym: + toSymNode(n, ir, c) + of directIntLit: + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32(n.intVal), + typeId: toPackedType(n.typ, c), info: info) + of externIntLit: + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32 getOrIncl(c.m.sh.integers, n.intVal), + typeId: toPackedType(n.typ, c), info: info) + of nkStrLit..nkTripleStrLit: + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32 getOrIncl(c.m.sh.strings, n.strVal), + typeId: toPackedType(n.typ, c), info: info) + of nkFloatLit..nkFloat128Lit: + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32 getOrIncl(c.m.sh.floats, n.floatVal), + typeId: toPackedType(n.typ, c), info: info) + else: + let patchPos = ir.prepare(n.kind, n.flags, + toPackedType(n.typ, c), info) + for i in 0..= g.len: + g.setLen(m+1) + + case g[m].status + of undefined: + g[m].status = loading + let fullpath = msgs.toFullPath(conf, fileIdx) + let rod = toRodFile(conf, AbsoluteFile fullpath) + let err = loadRodFile(rod, g[m].fromDisk, conf) + if err == ok: + result = false + # check its dependencies: + for dep in g[m].fromDisk.imports: + let fid = toFileIndex(dep, g[m].fromDisk, conf) + # Warning: we need to traverse the full graph, so + # do **not use break here**! + if needsRecompile(g, conf, fid): + result = true + + g[m].status = if result: outdated else: loaded + else: + loadError(err, rod) + g[m].status = outdated + result = true + of loading, loaded: + result = false + of outdated: + result = true + +# ------------------------------------------------------------------------- + +proc storeError(err: RodFileError; filename: AbsoluteFile) = + echo "Error: ", $err, "; couldn't write to ", filename.string + removeFile(filename.string) + +proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder) = + rememberConfig(encoder, encoder.config) + + var f = rodfiles.create(filename.string) + f.storeHeader() + f.storeSection configSection + f.storePrim encoder.m.definedSymbols + f.storePrim encoder.m.cfg + + f.storeSection stringsSection + f.store encoder.m.sh.strings + + f.storeSection checkSumsSection + f.storeSeq encoder.m.includes + + f.storeSection depsSection + f.storeSeq encoder.m.imports + + f.storeSection integersSection + f.store encoder.m.sh.integers + + f.storeSection floatsSection + f.store encoder.m.sh.floats + + f.storeSection topLevelSection + f.storeSeq encoder.m.topLevel.nodes + + f.storeSection bodiesSection + f.storeSeq encoder.m.bodies.nodes + + f.storeSection symsSection + f.storeSeq encoder.m.sh.syms + + f.storeSection typesSection + f.storeSeq encoder.m.sh.types + close(f) + if f.err != ok: + loadError(f.err, filename) + + when true: + # basic loader testing: + var m2: PackedModule + discard loadRodFile(filename, m2, encoder.config) + +# ---------------------------------------------------------------------------- + +type + PackedDecoder* = object + thisModule*: int32 + lastLit*: LitId + lastFile*: FileIndex # remember the last lookup entry. + config*: ConfigRef + ident: IdentCache + +proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId): PType +proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): PSym + +proc toFileIndexCached(c: var PackedDecoder; g: var PackedModuleGraph; f: LitId): FileIndex = + if c.lastLit == f: + result = c.lastFile + else: + result = toFileIndex(f, g[c.thisModule].fromDisk, c.config) + c.lastLit = f + c.lastFile = result + +proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; + x: PackedLineInfo): TLineInfo = + assert g[c.thisModule].status == loaded + result = TLineInfo(line: x.line, col: x.col, + fileIndex: toFileIndexCached(c, g, x.file)) + +proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; + tree: PackedTree; n: NodePos): PNode = + let k = n.kind + result = newNodeIT(k, translateLineInfo(c, g, n.info), + loadType(c, g, n.typ)) + result.flags = n.flags + + case k + of nkEmpty, nkNilLit, nkType: + discard + of nkIdent: + result.ident = getIdent(c.ident, g[c.thisModule].fromDisk.sh.strings[n.litId]) + of nkSym: + result.sym = loadSym(c, g, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand)) + of directIntLit: + result.intVal = tree.nodes[n.int].operand + of externIntLit: + result.intVal = g[c.thisModule].fromDisk.sh.integers[n.litId] + of nkStrLit..nkTripleStrLit: + result.strVal = g[c.thisModule].fromDisk.sh.strings[n.litId] + of nkFloatLit..nkFloat128Lit: + result.floatVal = g[c.thisModule].fromDisk.sh.floats[n.litId] + of nkModuleRef: + let (n1, n2) = sons2(tree, n) + assert n1.kind == nkInt32Lit + assert n2.kind == nkInt32Lit + transitionNoneToSym(result) + result.sym = loadSym(c, g, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)) + else: + for n0 in sonsReadonly(tree, n): + result.add loadNodes(c, g, tree, n0) + +proc moduleIndex*(c: var PackedDecoder; g: var PackedModuleGraph; + s: PackedItemId): int32 {.inline.} = + result = if s.module == LitId(0): c.thisModule + else: toFileIndexCached(c, g, s.module).int32 + +proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; + s: PackedSym; si, item: int32): PSym = + result = PSym(itemId: ItemId(module: si, item: item), + kind: s.kind, magic: s.magic, flags: s.flags, + info: translateLineInfo(c, g, s.info), + options: s.options, + position: s.position, + name: getIdent(c.ident, g[si].fromDisk.sh.strings[s.name]) + ) + +template loadAstBody(p, field) = + if p.field != emptyNodeId: + result.field = loadNodes(c, g, g[si].fromDisk.bodies, NodePos p.field) + +proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph; + si, item: int32; l: PackedLib): PLib = + # XXX: hack; assume a zero LitId means the PackedLib is all zero (empty) + if l.name.int == 0: + result = nil + else: + result = PLib(generated: l.generated, isOverriden: l.isOverriden, + kind: l.kind, name: rope g[si].fromDisk.sh.strings[l.name]) + loadAstBody(l, path) + +proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; + s: PackedSym; si, item: int32; result: PSym) = + result.typ = loadType(c, g, s.typ) + loadAstBody(s, constraint) + loadAstBody(s, ast) + result.annex = loadLib(c, g, si, item, s.annex) + when hasFFI: + result.cname = g[si].fromDisk.sh.strings[s.cname] + + if s.kind in {skLet, skVar, skField, skForVar}: + result.guard = loadSym(c, g, s.guard) + result.bitsize = s.bitsize + result.alignment = s.alignment + result.owner = loadSym(c, g, s.owner) + let externalName = g[si].fromDisk.sh.strings[s.externalName] + if externalName != "": + result.loc.r = rope externalName + +proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): PSym = + if s == nilTypeId: + result = nil + else: + let si = moduleIndex(c, g, s) + assert g[si].status == loaded + if not g[si].symsInit: + g[si].symsInit = true + setLen g[si].syms, g[si].fromDisk.sh.syms.len + + if g[si].syms[s.item] == nil: + let packed = addr(g[si].fromDisk.sh.syms[s.item]) + result = symHeaderFromPacked(c, g, packed[], si, s.item) + # store it here early on, so that recursions work properly: + g[si].syms[s.item] = result + symBodyFromPacked(c, g, packed[], si, s.item, result) + else: + result = g[si].syms[s.item] + +proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; + t: PackedType; si, item: int32): PType = + result = PType(itemId: ItemId(module: si, item: t.nonUniqueId), kind: t.kind, + flags: t.flags, size: t.size, align: t.align, + paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel, + uniqueId: ItemId(module: si, item: item)) + +proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; + t: PackedType; si, item: int32; result: PType) = + result.sym = loadSym(c, g, t.sym) + result.owner = loadSym(c, g, t.owner) + for op, item in pairs t.attachedOps: + result.attachedOps[op] = loadSym(c, g, item) + result.typeInst = loadType(c, g, t.typeInst) + for son in items t.types: + result.sons.add loadType(c, g, son) + loadAstBody(t, n) + for gen, id in items t.methods: + result.methods.add((gen, loadSym(c, g, id))) + +proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId): PType = + if t == nilTypeId: + result = nil + else: + let si = moduleIndex(c, g, t) + assert g[si].status == loaded + if not g[si].typesInit: + g[si].typesInit = true + setLen g[si].types, g[si].fromDisk.sh.types.len + + if g[si].types[t.item] == nil: + let packed = addr(g[si].fromDisk.sh.types[t.item]) + result = typeHeaderFromPacked(c, g, packed[], si, t.item) + # store it here early on, so that recursions work properly: + g[si].types[t.item] = result + typeBodyFromPacked(c, g, packed[], si, t.item, result) + else: + result = g[si].types[t.item] + + +when false: + proc initGenericKey*(s: PSym; types: seq[PType]): GenericKey = + result.module = s.owner.itemId.module + result.name = s.name.s + result.types = mapIt types: hashType(it, {CoType, CoDistinct}).MD5Digest + + proc addGeneric*(m: var Module; c: var PackedEncoder; key: GenericKey; s: PSym) = + ## add a generic to the module + if key notin m.generics: + m.generics[key] = toPackedSym(s, m.ast, c) + toPackedNode(s.ast, m.ast, c) diff --git a/compiler/idents.nim b/compiler/idents.nim index f36ce09f3..1e8c912d9 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -14,9 +14,6 @@ import hashes, wordrecg -when defined(nimPreviewSlimSystem): - import std/assertions - type PIdent* = ref TIdent TIdent*{.acyclic.} = object @@ -116,8 +113,3 @@ proc newIdentCache*(): IdentCache = proc whichKeyword*(id: PIdent): TSpecialWord = if id.id < 0: result = wInvalid else: result = TSpecialWord(id.id) - -proc hash*(x: PIdent): Hash {.inline.} = x.h -proc `==`*(a, b: PIdent): bool {.inline.} = - if a.isNil or b.isNil: result = system.`==`(a, b) - else: result = a.id == b.id diff --git a/compiler/importer.nim b/compiler/importer.nim index c4e37c269..645c03b2b 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -11,12 +11,7 @@ import intsets, ast, astalgo, msgs, options, idents, lookups, - semdata, modulepaths, sigmatch, lineinfos, sets, - modulegraphs, wordrecg, tables -from strutils import `%` - -when defined(nimPreviewSlimSystem): - import std/assertions + semdata, modulepaths, sigmatch, lineinfos, sets proc readExceptSet*(c: PContext, n: PNode): IntSet = assert n.kind in {nkImportExceptStmt, nkExportExceptStmt} @@ -106,32 +101,14 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) = else: importPureEnumField(c, e) else: - if s.kind == skConverter: addConverter(c, LazySym(sym: s)) - if hasPattern(s): addPattern(c, LazySym(sym: s)) + if s.kind == skConverter: addConverter(c, s) + if hasPattern(s): addPattern(c, s) if s.owner != origin: c.exportIndirections.incl((origin.id, s.id)) -proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) = - template bail = globalError(c.config, n.info, "invalid pragma") - if n.kind == nkPragmaExpr: - if n.len == 2 and n[1].kind == nkPragma: - result[0] = n[0] - for ni in n[1]: - if ni.kind == nkIdent: result[1].add whichKeyword(ni.ident) - else: bail() - else: bail() - else: - result[0] = n - if result[0].safeLen > 0: - (result[0][^1], result[1]) = splitPragmas(c, result[0][^1]) - proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) = - let (n, kws) = splitPragmas(c, n) - if kws.len > 0: - globalError(c.config, n.info, "unexpected pragma") - let ident = lookups.considerQuotedIdent(c, n) - let s = someSym(c.graph, fromMod, ident) + let s = strTableGet(fromMod.tab, ident) if s == nil: errorUndeclaredIdentifier(c, n.info, ident.s) else: @@ -141,16 +118,16 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) = # for an enumeration we have to add all identifiers if multiImport: # for a overloadable syms add all overloaded routines - var it: ModuleIter - var e = initModuleIter(it, c.graph, fromMod, s.name) + var it: TIdentIter + var e = initIdentIter(it, fromMod.tab, s.name) while e != nil: if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3") if s.kind in ExportableSymKinds: rawImportSymbol(c, e, fromMod, importSet) - e = nextModuleIter(it, c.graph) + e = nextIdentIter(it, fromMod.tab) else: rawImportSymbol(c, s, fromMod, importSet) - suggestSym(c.graph, n.info, s, c.graph.usageSym, false) + suggestSym(c.config, n.info, s, c.graph.usageSym, false) proc addImport(c: PContext; im: sink ImportedModule) = for i in 0..high(c.imports): @@ -185,24 +162,31 @@ proc addImport(c: PContext; im: sink ImportedModule) = c.imports.add im template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} = - for it in mitems c.graph.ifaces[fromMod.position].converters: + for it in c.graph.ifaces[fromMod.position].converters: if filter: - loadPackedSym(c.graph, it) - if sfExported in it.sym.flags: - addConverter(c, it) - for it in mitems c.graph.ifaces[fromMod.position].patterns: + addConverter(c, it) + for it in c.graph.ifaces[fromMod.position].patterns: if filter: - loadPackedSym(c.graph, it) - if sfExported in it.sym.flags: - addPattern(c, it) - for it in mitems c.graph.ifaces[fromMod.position].pureEnums: + addPattern(c, it) + for it in c.graph.ifaces[fromMod.position].pureEnums: if filter: - loadPackedSym(c.graph, it) - importPureEnumFields(c, it.sym, it.sym.typ) + importPureEnumFields(c, it, it.typ) proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet) - addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet) + addUnnamedIt(c, fromMod, it.id notin exceptSet) + + when false: + var i: TTabIter + var s = initTabIter(i, fromMod.tab) + while s != nil: + if s.kind != skModule: + if s.kind != skEnumField: + if s.kind notin ExportableSymKinds: + internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s) + if exceptSet.isNil or s.name.id notin exceptSet: + rawImportSymbol(c, s, fromMod) + s = nextIter(i, fromMod.tab) proc importAllSymbols*(c: PContext, fromMod: PSym) = c.addImport ImportedModule(m: fromMod, mode: importAll) @@ -228,45 +212,18 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im for i in 0..n.safeLen-1: importForwarded(c, n[i], exceptSet, fromMod, importSet) -proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym = +proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym = result = realModule - template createModuleAliasImpl(ident): untyped = - createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options) + c.unusedImports.add((realModule, n.info)) if n.kind != nkImportAs: discard elif n.len != 2 or n[1].kind != nkIdent: localError(c.config, n.info, "module alias must be an identifier") elif n[1].ident.id != realModule.name.id: # some misguided guy will write 'import abc.foo as foo' ... - result = createModuleAliasImpl(n[1].ident) - if result == realModule: - # avoids modifying `realModule`, see D20201209T194412 for `import {.all.}` - result = createModuleAliasImpl(realModule.name) - if importHidden: - result.options.incl optImportHidden - c.unusedImports.add((result, n.info)) - c.importModuleMap[result.id] = realModule.id + result = createModuleAlias(realModule, nextSymId c.idgen, n[1].ident, realModule.info, + c.config.options) -proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] = - var ret: typeof(result) - proc processPragma(n2: PNode): PNode = - let (result2, kws) = splitPragmas(c, n2) - result = result2 - for ai in kws: - case ai - of wImportHidden: ret.importHidden = true - else: globalError(c.config, n.info, "invalid pragma, expected: " & ${wImportHidden}) - - if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as": - ret.node = newNodeI(nkImportAs, n.info) - ret.node.add n[1].processPragma - ret.node.add n[2] - else: - ret.node = n.processPragma - return ret - -proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = - let transf = transformImportAs(c, n) - n = transf.node +proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = let f = checkModuleName(c.config, n) if f != InvalidFileIdx: addImportFileDep(c, f) @@ -282,42 +239,43 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = toFullPath(c.config, c.graph.importStack[i+1]) c.recursiveDep = err - var realModule: PSym discard pushOptionEntry(c) - realModule = c.graph.importModuleCallback(c.graph, c.module, f) - result = importModuleAs(c, n, realModule, transf.importHidden) + result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f)) popOptionEntry(c) #echo "set back to ", L c.graph.importStack.setLen(L) # we cannot perform this check reliably because of - # test: modules/import_in_config) # xxx is that still true? - if realModule == c.module: - localError(c.config, n.info, "module '$1' cannot import itself" % realModule.name.s) - if sfDeprecated in realModule.flags: - var prefix = "" - if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; " - message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated") - suggestSym(c.graph, n.info, result, c.graph.usageSym, false) + # test: modules/import_in_config) + when true: + if result.info.fileIndex == c.module.info.fileIndex and + result.info.fileIndex == n.info.fileIndex: + localError(c.config, n.info, "A module cannot import itself") + if sfDeprecated in result.flags: + if result.constraint != nil: + message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated") + else: + message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated") + suggestSym(c.config, n.info, result, c.graph.usageSym, false) importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) -proc afterImport(c: PContext, m: PSym) = - # fixes bug #17510, for re-exported symbols - let realModuleId = c.importModuleMap[m.id] - for s in allSyms(c.graph, m): - if s.owner.id != realModuleId: - c.exportIndirections.incl((m.id, s.id)) +proc transformImportAs(c: PContext; n: PNode): PNode = + if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as": + result = newNodeI(nkImportAs, n.info) + result.add n[1] + result.add n[2] + else: + result = n proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = - var it = it + let it = transformImportAs(c, it) let m = myImportModule(c, it, importStmtResult) if m != nil: # ``addDecl`` needs to be done before ``importAllSymbols``! addDecl(c, m, it.info) # add symbol to symbol table of module importAllSymbols(c, m) #importForwarded(c, m.ast, emptySet, m) - afterImport(c, m) proc evalImport*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) @@ -346,6 +304,7 @@ proc evalImport*(c: PContext, n: PNode): PNode = proc evalFrom*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) + n[0] = transformImportAs(c, n[0]) var m = myImportModule(c, n[0], result) if m != nil: n[0] = newSymNode(m) @@ -356,15 +315,14 @@ proc evalFrom*(c: PContext, n: PNode): PNode = if n[i].kind != nkNilLit: importSymbol(c, n[i], m, im.imported) c.addImport im - afterImport(c, m) proc evalImportExcept*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) + n[0] = transformImportAs(c, n[0]) var m = myImportModule(c, n[0], result) if m != nil: n[0] = newSymNode(m) addDecl(c, m, n.info) # add symbol to symbol table of module importAllSymbolsExcept(c, m, readExceptSet(c, n)) #importForwarded(c, m.ast, exceptSet, m) - afterImport(c, m) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index d58f28289..76a063faa 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -15,51 +15,39 @@ import intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents, - strutils, options, lowerings, tables, modulegraphs, + strutils, options, dfa, lowerings, tables, modulegraphs, msgs, lineinfos, parampatterns, sighashes, liftdestructors, optimizer, - varpartitions, aliasanalysis, dfa - -when defined(nimPreviewSlimSystem): - import std/assertions + varpartitions from trees import exprStructuralEquivalent, getRoot type - Con = object - owner: PSym - when true: - g: ControlFlowGraph - graph: ModuleGraph - inLoop, inSpawn, inLoopCond: int - uninit: IntSet # set of uninit'ed vars - idgen: IdGenerator - body: PNode - otherUsage: TLineInfo - - Scope = object # we do scope-based memory management. + Scope = object # well we do scope-based memory management. \ # a scope is comparable to an nkStmtListExpr like # (try: statements; dest = y(); finally: destructors(); dest) vars: seq[PSym] wasMoved: seq[PNode] final: seq[PNode] # finally section - locals: seq[PSym] - body: PNode needsTry: bool parent: ptr Scope +type + Con = object + owner: PSym + g: ControlFlowGraph + graph: ModuleGraph + otherRead: PNode + inLoop, inSpawn, inLoopCond: int + uninit: IntSet # set of uninit'ed vars + uninitComputed: bool + idgen: IdGenerator + ProcessMode = enum normal consumed sinkArg const toDebug {.strdefine.} = "" -when toDebug.len > 0: - var shouldDebug = false - -template dbg(body) = - when toDebug.len > 0: - if shouldDebug: - body proc hasDestructor(c: Con; t: PType): bool {.inline.} = result = ast.hasDestructor(t) @@ -68,118 +56,179 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} = if not result and c.graph.config.selectedGC in {gcArc, gcOrc}: assert(not containsGarbageCollectedRef(t)) +template dbg(body) = + when toDebug.len > 0: + if c.owner.name.s == toDebug or toDebug == "always": + body + proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextSymId c.idgen, c.owner, info) sym.typ = typ s.vars.add(sym) result = newSymNode(sym) -proc nestedScope(parent: var Scope; body: PNode): Scope = - Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent)) +proc nestedScope(parent: var Scope): Scope = + Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent)) -proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode +proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode +proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode -type - MoveOrCopyFlag = enum - IsDecl, IsExplicitSink +proc isLastRead(location: PNode; cfg: ControlFlowGraph; otherRead: var PNode; pc, until: int): int = + var pc = pc + while pc < cfg.len and pc < until: + case cfg[pc].kind + of def: + if instrTargets(cfg[pc].n, location) == Full: + # the path leads to a redefinition of 's' --> abandon it. + return high(int) + elif instrTargets(cfg[pc].n, location) == Partial: + # only partially writes to 's' --> can't sink 's', so this def reads 's' + otherRead = cfg[pc].n + return -1 + inc pc + of use: + if instrTargets(cfg[pc].n, location) != None: + otherRead = cfg[pc].n + return -1 + inc pc + of goto: + pc += cfg[pc].dest + of fork: + # every branch must lead to the last read of the location: + var variantA = pc + 1 + var variantB = pc + cfg[pc].dest + while variantA != variantB: + if min(variantA, variantB) < 0: return -1 + if max(variantA, variantB) >= cfg.len or min(variantA, variantB) >= until: + break + if variantA < variantB: + variantA = isLastRead(location, cfg, otherRead, variantA, min(variantB, until)) + else: + variantB = isLastRead(location, cfg, otherRead, variantB, min(variantA, until)) + pc = min(variantA, variantB) + return pc -proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode - -when false: - var - perfCounters: array[InstrKind, int] - - proc showCounters*() = - for i in low(InstrKind)..high(InstrKind): - echo "INSTR ", i, " ", perfCounters[i] - -proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool = - let root = parampatterns.exprRoot(n, allowCalls=false) - if root == nil: return false - - var s = addr(scope) - while s != nil: - if s.locals.contains(root): break - s = s.parent - - c.g = constructCfg(c.owner, if s != nil: s.body else: c.body, root) - dbg: - echo "\n### ", c.owner.name.s, ":\nCFG:" - echoCfg(c.g) - #echo c.body - - var j = 0 - while j < c.g.len: - if c.g[j].kind == use and c.g[j].n == n: break - inc j - c.otherUsage = unknownLineInfo - if j < c.g.len: - var pcs = @[j+1] - var marked = initIntSet() - result = true - while pcs.len > 0: - var pc = pcs.pop() - if not marked.contains(pc): - let oldPc = pc - while pc < c.g.len: - dbg: - echo "EXEC ", c.g[pc].kind, " ", pc, " ", n - when false: - inc perfCounters[c.g[pc].kind] - case c.g[pc].kind - of loop: - let back = pc + c.g[pc].dest - if not marked.containsOrIncl(back): - pc = back - else: - break - of goto: - pc = pc + c.g[pc].dest - of fork: - if not marked.contains(pc+1): - pcs.add pc + 1 - pc = pc + c.g[pc].dest - of use: - if c.g[pc].n.aliases(n) != no or n.aliases(c.g[pc].n) != no: - c.otherUsage = c.g[pc].n.info - return false - inc pc - of def: - if c.g[pc].n.aliases(n) == yes: - # the path leads to a redefinition of 's' --> sink 's'. - break - elif n.aliases(c.g[pc].n) != no: - # only partially writes to 's' --> can't sink 's', so this def reads 's' - # or maybe writes to 's' --> can't sink 's' - c.otherUsage = c.g[pc].n.info - return false - inc pc - marked.incl oldPc - else: - result = false - -proc isLastRead(n: PNode; c: var Con; s: var Scope): bool = - if not hasDestructor(c, n.typ): return true - - let m = skipConvDfa(n) - result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or - isLastReadImpl(n, c, s) - -proc isFirstWrite(n: PNode; c: var Con): bool = - let m = skipConvDfa(n) - result = nfFirstWrite in m.flags - -proc isCursor(n: PNode): bool = +proc isCursor(n: PNode; c: Con): bool = case n.kind of nkSym: sfCursor in n.sym.flags of nkDotExpr: - isCursor(n[1]) + isCursor(n[1], c) of nkCheckedFieldExpr: - isCursor(n[0]) + isCursor(n[0], c) else: false +proc isLastRead(n: PNode; c: var Con): bool = + # first we need to search for the instruction that belongs to 'n': + var instr = -1 + let m = dfa.skipConvDfa(n) + if m.kind == nkSym and sfSingleUsedTemp in m.sym.flags: return true + + for i in 0..= c.g.len: return true + + c.otherRead = nil + result = isLastRead(n, c.g, c.otherRead, instr+1, int.high) >= 0 + dbg: echo "ugh ", c.otherRead.isNil, " ", result + +proc isFirstWrite(location: PNode; cfg: ControlFlowGraph; pc, until: int): int = + var pc = pc + while pc < until: + case cfg[pc].kind + of def: + if instrTargets(cfg[pc].n, location) != None: + # a definition of 's' before ours makes ours not the first write + return -1 + inc pc + of use: + if instrTargets(cfg[pc].n, location) != None: + return -1 + inc pc + of goto: + pc += cfg[pc].dest + of fork: + # every branch must not contain a def/use of our location: + var variantA = pc + 1 + var variantB = pc + cfg[pc].dest + while variantA != variantB: + if min(variantA, variantB) < 0: return -1 + if max(variantA, variantB) > until: + break + if variantA < variantB: + variantA = isFirstWrite(location, cfg, variantA, min(variantB, until)) + else: + variantB = isFirstWrite(location, cfg, variantB, min(variantA, until)) + pc = min(variantA, variantB) + return pc + +proc isFirstWrite(n: PNode; c: var Con): bool = + # first we need to search for the instruction that belongs to 'n': + var instr = -1 + let m = dfa.skipConvDfa(n) + + for i in countdown(c.g.len-1, 0): # We search backwards here to treat loops correctly + if c.g[i].kind == def and c.g[i].n == m: + if instr < 0: + instr = i + break + + if instr < 0: return false + # we go through all paths going to 'instr' and need to + # ensure that we don't find another 'def/use X' instruction. + if instr == 0: return true + + result = isFirstWrite(n, c.g, 0, instr) >= 0 + +proc initialized(code: ControlFlowGraph; pc: int, + init, uninit: var IntSet; until: int): int = + ## Computes the set of definitely initialized variables across all code paths + ## as an IntSet of IDs. + var pc = pc + while pc < code.len: + case code[pc].kind + of goto: + pc += code[pc].dest + of fork: + var initA = initIntSet() + var initB = initIntSet() + var variantA = pc + 1 + var variantB = pc + code[pc].dest + while variantA != variantB: + if max(variantA, variantB) > until: + break + if variantA < variantB: + variantA = initialized(code, variantA, initA, uninit, min(variantB, until)) + else: + variantB = initialized(code, variantB, initB, uninit, min(variantA, until)) + pc = min(variantA, variantB) + # we add vars if they are in both branches: + for v in initA: + if v in initB: + init.incl v + of use: + let v = code[pc].n.sym + if v.kind != skParam and v.id notin init: + # attempt to read an uninit'ed variable + uninit.incl v.id + inc pc + of def: + let v = code[pc].n.sym + init.incl v.id + inc pc + return pc + template isUnpackedTuple(n: PNode): bool = ## we move out all elements of unpacked tuples, ## hence unpacked tuples themselves don't need to be destroyed @@ -191,11 +240,9 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) = m.add "; requires a copy because it's not the last read of '" m.add renderTree(ri) m.add '\'' - if c.otherUsage != unknownLineInfo: - # ri.comment.startsWith('\n'): + if c.otherRead != nil: m.add "; another read is done here: " - m.add c.graph.config $ c.otherUsage - #m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info + m.add c.graph.config $ c.otherRead.info elif ri.kind == nkSym and ri.sym.kind == skParam and not isSinkType(ri.sym.typ): m.add "; try to make " m.add renderTree(ri) @@ -214,18 +261,18 @@ proc genOp(c: var Con; op: PSym; dest: PNode): PNode = result = newTree(nkCall, newSymNode(op), addrExp) proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = - var op = getAttachedOp(c.graph, t, kind) - if op == nil or op.ast.isGenericRoutine: + var op = t.attachedOps[kind] + if op == nil or op.ast[genericParamsPos].kind != nkEmpty: # give up and find the canonical type instead: - let h = sighashes.hashType(t, c.graph.config, {CoType, CoConsiderOwned, CoDistinct}) + let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct}) let canon = c.graph.canonTypes.getOrDefault(h) if canon != nil: - op = getAttachedOp(c.graph, canon, kind) + op = canon.attachedOps[kind] if op == nil: #echo dest.typ.id globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & "' operator not found for type " & typeToString(t)) - elif op.ast.isGenericRoutine: + elif op.ast[genericParamsPos].kind != nkEmpty: globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] & "' operator is generic") dbg: @@ -241,22 +288,22 @@ proc genDestroy(c: var Con; dest: PNode): PNode = proc canBeMoved(c: Con; t: PType): bool {.inline.} = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) if optOwnedRefs in c.graph.config.globalOptions: - result = t.kind != tyRef and getAttachedOp(c.graph, t, attachedSink) != nil + result = t.kind != tyRef and t.attachedOps[attachedSink] != nil else: - result = getAttachedOp(c.graph, t, attachedSink) != nil + result = t.attachedOps[attachedSink] != nil proc isNoInit(dest: PNode): bool {.inline.} = result = dest.kind == nkSym and sfNoInit in dest.sym.flags -proc genSink(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode = - if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or +proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode = + if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or isDecl or (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or isNoInit(dest): # optimize sink call into a bitwise memcopy result = newTree(nkFastAsgn, dest, ri) else: let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) - if getAttachedOp(c.graph, t, attachedSink) != nil: + if t.attachedOps[attachedSink] != nil: result = c.genOp(t, attachedSink, dest, ri) result.add ri else: @@ -297,26 +344,21 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) = if t.kind == tyRef: result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest) else: - let xenv = genBuiltin(c.graph, c.idgen, mAccessEnv, "accessEnv", dest) + let xenv = genBuiltin(c.graph, mAccessEnv, "accessEnv", dest) xenv.typ = getSysType(c.graph, dest.info, tyPointer) result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv) -proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode = +proc genCopyNoCheck(c: var Con; dest, ri: PNode): PNode = let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) - result = c.genOp(t, a, dest, ri) - assert ri.typ != nil + result = c.genOp(t, attachedAsgn, dest, ri) -proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode = +proc genCopy(c: var Con; dest, ri: PNode): PNode = let t = dest.typ if tfHasOwned in t.flags and ri.kind != nkNilLit: # try to improve the error message here: - if IsExplicitSink in flags: - c.checkForErrorPragma(t, ri, "=sink") - else: - c.checkForErrorPragma(t, ri, "=copy") - let a = if IsExplicitSink in flags: attachedSink else: attachedAsgn - result = c.genCopyNoCheck(dest, ri, a) - assert ri.typ != nil + if c.otherRead == nil: discard isLastRead(ri, c) + c.checkForErrorPragma(t, ri, "=copy") + result = c.genCopyNoCheck(dest, ri) proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = # discriminator is ordinal value that doesn't need sink destroy @@ -334,8 +376,8 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = let objType = leDotExpr[0].typ if hasDestructor(c, objType): - if getAttachedOp(c.graph, objType, attachedDestructor) != nil and - sfOverriden in getAttachedOp(c.graph, objType, attachedDestructor).flags: + if objType.attachedOps[attachedDestructor] != nil and + sfOverriden in objType.attachedOps[attachedDestructor].flags: localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor. It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""") result.add newTree(nkFastAsgn, le, tmp) @@ -348,28 +390,21 @@ It is best to factor out piece of object that needs custom destructor into separ cond.add le cond.add tmp let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool)) - notExpr.add newSymNode(createMagic(c.graph, c.idgen, "not", mNot)) + notExpr.add newSymNode(createMagic(c.graph, "not", mNot)) notExpr.add cond result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le))) result.add newTree(nkFastAsgn, le, tmp) proc genWasMoved(c: var Con, n: PNode): PNode = - let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink}) - let op = getAttachedOp(c.graph, n.typ, attachedWasMoved) - if op != nil: - if sfError in op.flags: - c.checkForErrorPragma(n.typ, n, "=wasMoved") - result = genOp(c, op, n) - else: - result = newNodeI(nkCall, n.info) - result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved))) - result.add copyTree(n) #mWasMoved does not take the address - #if n.kind != nkSym: - # message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")") + result = newNodeI(nkCall, n.info) + result.add(newSymNode(createMagic(c.graph, "wasMoved", mWasMoved))) + result.add copyTree(n) #mWasMoved does not take the address + #if n.kind != nkSym: + # message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")") proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = result = newNodeI(nkCall, info) - result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault))) + result.add(newSymNode(createMagic(c.graph, "default", mDefault))) result.typ = t proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = @@ -407,7 +442,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = let tmp = c.getTemp(s, n.typ, n.info) if hasDestructor(c, n.typ): result.add c.genWasMoved(tmp) - var m = c.genCopy(tmp, n, {}) + var m = c.genCopy(tmp, n) m.add p(n, c, s, normal) c.finishCopy(m, n, isFromSink = true) result.add m @@ -452,7 +487,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode = # This was already done in the sink parameter handling logic. result = newNodeIT(nkStmtListExpr, arg.info, arg.typ) let tmp = c.getTemp(s, arg.typ, arg.info) - result.add c.genSink(tmp, arg, {IsDecl}) + result.add c.genSink(tmp, arg, isDecl = true) result.add tmp s.final.add c.genDestroy(tmp) else: @@ -488,19 +523,23 @@ proc cycleCheck(n: PNode; c: var Con) = message(c.graph.config, n.info, warnCycleCreated, msg) break -proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) = +proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = # move the variable declaration to the top of the frame: s.vars.add v.sym if isUnpackedTuple(v): if c.inLoop > 0: # unpacked tuple needs reset at every loop iteration res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info)) - elif sfThread notin v.sym.flags and sfCursor notin v.sym.flags: + elif sfThread notin v.sym.flags: # do not destroy thread vars for now at all for consistency. - if {sfGlobal, sfPure} <= v.sym.flags or sfGlobal in v.sym.flags and s.parent == nil: + if sfGlobal in v.sym.flags and s.parent == nil: #XXX: Rethink this logic (see tarcmisc.test2) c.graph.globalDestructors.add c.genDestroy(v) else: s.final.add c.genDestroy(v) + if ri.kind == nkEmpty and c.inLoop > 0: + res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = true) + elif ri.kind != nkEmpty: + res.add moveOrCopy(v, ri, c, s, isDecl = true) proc processScope(c: var Con; s: var Scope; ret: PNode): PNode = result = newNodeI(nkStmtList, ret.info) @@ -524,17 +563,16 @@ proc processScope(c: var Con; s: var Scope; ret: PNode): PNode = if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry -template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped, tmpFlags: TSymFlags): PNode = +template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped): PNode = assert not ret.typ.isEmptyType - var result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) + var result = newNodeI(nkStmtListExpr, ret.info) # 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 let tmp = c.getTemp(s.parent[], ret.typ, ret.info) - tmp.sym.flags = tmpFlags + tmp.sym.flags.incl sfSingleUsedTemp let cpy = if hasDestructor(c, ret.typ): - s.parent[].final.add c.genDestroy(tmp) - moveOrCopy(tmp, ret, c, s, {IsDecl}) + moveOrCopy(tmp, ret, c, s, isDecl = true) else: newTree(nkFastAsgn, tmp, p(ret, c, s, normal)) @@ -558,8 +596,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt result -template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, - tmpFlags = {sfSingleUsedTemp}) = +template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = template maybeVoid(child, s): untyped = if isEmptyType(child.typ): p(child, c, s, normal) else: processCall(child, s) @@ -583,11 +620,11 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, var branch = shallowCopy(it) for j in 0 ..< it.len-1: branch[j] = copyTree(it[j]) - var ofScope = nestedScope(s, it.lastSon) + var ofScope = nestedScope(s) branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt: processScope(c, ofScope, maybeVoid(it[^1], ofScope)) else: - processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags) + processScopeExpr(c, ofScope, it[^1], processCall) result.add branch of nkWhileStmt: @@ -596,7 +633,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, result = copyNode(n) result.add p(n[0], c, s, normal) dec c.inLoopCond - var bodyScope = nestedScope(s, n[1]) + var bodyScope = nestedScope(s) let bodyResult = p(n[1], c, bodyScope, normal) result.add processScope(c, bodyScope, bodyResult) dec c.inLoop @@ -608,7 +645,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, for i in 0.. 0: - let skipInit = v.kind == nkDotExpr and # Closure var - sfNoInit in v[1].sym.flags - if not skipInit: - result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, if v.kind == nkSym: {IsDecl} else: {}) + pVarTopLevel(v, c, s, ri, result) + else: + if ri.kind == nkEmpty and c.inLoop > 0: + ri = genDefaultCall(v.typ, c, v.info) + if ri.kind != nkEmpty: + result.add moveOrCopy(v, ri, c, s, isDecl = false) else: # keep the var but transform 'ri': var v = copyNode(n) var itCopy = copyNode(it) for j in 0.. 0 and isDangerousSeq(ri.typ): + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) + else: + result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: + result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + of nkSym: + if dest.kind == nkSym and dest.sym == ri.sym: + # rule (self-assignment-removal): + result = newNodeI(nkEmpty, dest.info) + elif isSinkParam(ri.sym) and isLastRead(ri, c): + # Rule 3: `=sink`(x, z); wasMoved(z) + let snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + elif ri.sym.kind != skParam and ri.sym.owner == c.owner and + isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri, c): + # Rule 3: `=sink`(x, z); wasMoved(z) + let snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + else: + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) + of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: + result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl) + of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: + template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) + # We know the result will be a stmt so we use that fact to optimize + handleNestedTempl(ri, process, willProduceStmt = true) + of nkRaiseStmt: + result = pRaiseStmt(ri, c, s) else: - case a.kind - of nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr: - # Reached an endpoint, flip to recurse the other side. - sameLocation(b, a) - of nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref: - # We don't need to check addr/deref levels or differentiate between the two, - # since pointers don't have hooks :) (e.g: var p: ptr pointer; p[] = addr p) - sameLocation(a[0], b) - of nkObjDownConv, nkObjUpConv: sameLocation(a[0], b) - of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b) - else: false - -proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode = - # with side effects - var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info) - temp.typ = ri[1].typ - var v = newNodeI(nkLetSection, ri[1].info) - let tempAsNode = newSymNode(temp) - - var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3) - vpart[0] = tempAsNode - vpart[1] = newNodeI(nkEmpty, tempAsNode.info) - vpart[2] = ri[1] - v.add(vpart) - - var newAccess = copyNode(ri) - newAccess.add ri[0] - newAccess.add tempAsNode - - var snk = c.genSink(dest, newAccess, flags) - result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess)) - -proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode = - if sameLocation(dest, ri): - # rule (self-assignment-removal): - result = newNodeI(nkEmpty, dest.info) - elif isCursor(dest): - case ri.kind: - of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: - template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags) - # We know the result will be a stmt so we use that fact to optimize - handleNestedTempl(ri, process, willProduceStmt = true) + if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and + canBeMoved(c, dest.typ): + # Rule 3: `=sink`(x, z); wasMoved(z) + let snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: - result = newTree(nkFastAsgn, dest, p(ri, c, s, normal)) + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) + +proc computeUninit(c: var Con) = + if not c.uninitComputed: + c.uninitComputed = true + c.uninit = initIntSet() + var init = initIntSet() + discard initialized(c.g, pc = 0, init, c.uninit, int.high) + +proc injectDefaultCalls(n: PNode, c: var Con) = + case n.kind + of nkVarSection, nkLetSection: + for it in n: + if it.kind == nkIdentDefs and it[^1].kind == nkEmpty: + computeUninit(c) + for j in 0.. 0 and isDangerousSeq(ri.typ): - result = c.genCopy(dest, ri, flags) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) - else: - result = c.genSink(dest, p(ri, c, s, consumed), flags) - of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: - result = c.genSink(dest, p(ri, c, s, consumed), flags) - of nkSym: - if isSinkParam(ri.sym) and isLastRead(ri, c, s): - # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, flags) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - elif ri.sym.kind != skParam and ri.sym.owner == c.owner and - isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri): - # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, flags) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - else: - result = c.genCopy(dest, ri, flags) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) - of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast: - result = c.genSink(dest, p(ri, c, s, sinkArg), flags) - of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: - template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags) - # We know the result will be a stmt so we use that fact to optimize - handleNestedTempl(ri, process, willProduceStmt = true) - of nkRaiseStmt: - result = pRaiseStmt(ri, c, s) - else: - if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and - canBeMoved(c, dest.typ): - # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, flags) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - else: - result = c.genCopy(dest, ri, flags) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) - -when false: - proc computeUninit(c: var Con) = - if not c.uninitComputed: - c.uninitComputed = true - c.uninit = initIntSet() - var init = initIntSet() - discard initialized(c.g, pc = 0, init, c.uninit, int.high) - - proc injectDefaultCalls(n: PNode, c: var Con) = - case n.kind - of nkVarSection, nkLetSection: - for it in n: - if it.kind == nkIdentDefs and it[^1].kind == nkEmpty: - computeUninit(c) - for j in 0.. 0: - shouldDebug = toDebug == owner.name.s or toDebug == "always" if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)): return n - var c = Con(owner: owner, graph: g, idgen: idgen, body: n, otherUsage: unknownLineInfo) + var c = Con(owner: owner, graph: g, g: constructCfg(owner, n), idgen: idgen) + dbg: + echo "\n### ", owner.name.s, ":\nCFG:" + echoCfg(c.g) + echo n if optCursorInference in g.config.options: - computeCursors(owner, n, g) + computeCursors(owner, n, g.config) - var scope = Scope(body: n) + var scope: Scope let body = p(n, c, scope, normal) if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}: diff --git a/compiler/installer.ini b/compiler/installer.ini index 226682715..2f11d0df9 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -13,7 +13,6 @@ Platforms: """ netbsd: i386;amd64 openbsd: i386;amd64;arm;arm64 dragonfly: i386;amd64 - crossos: amd64 haiku: i386;amd64 android: i386;arm;arm64 nintendoswitch: arm64 @@ -65,12 +64,11 @@ Files: "compiler" Files: "doc" Files: "doc/html" Files: "tools" -Files: "tools/debug/nim-gdb.py" +Files: "tools/nim-gdb.py" Files: "nimpretty" Files: "testament" Files: "nimsuggest" Files: "nimsuggest/tests/*.nim" -Files: "changelogs/*.md" [Lib] Files: "lib" @@ -78,6 +76,7 @@ Files: "lib" [Other] Files: "examples" Files: "dist/nimble" +Files: "dist/fusion" Files: "tests" @@ -111,7 +110,6 @@ Download: r"Aporia Text Editor|dist|aporia.zip|97997|https://nim-lang.org/downlo Files: "bin/makelink.exe" Files: "bin/7zG.exe" Files: "bin/*.dll" -Files: "bin/cacert.pem" [UnixBin] Files: "bin/nim" @@ -121,7 +119,7 @@ Files: "bin/nim" InstallScript: "yes" UninstallScript: "yes" Files: "bin/nim-gdb" -Files: "build_all.sh" +Files: "bin/nim-gdb.bash" [InnoSetup] @@ -143,5 +141,5 @@ shortDesc: "The Nim Compiler" licenses: "bin/nim,MIT;lib/*,MIT;" [nimble] -pkgName: "nim" +pkgName: "compiler" pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css" diff --git a/compiler/int128.nim b/compiler/int128.nim index b0341eb37..e266b9cd0 100644 --- a/compiler/int128.nim +++ b/compiler/int128.nim @@ -1,16 +1,13 @@ ## This module is for compiler internal use only. For reliable error ## messages and range checks, the compiler needs a data type that can -## hold all from `low(BiggestInt)` to `high(BiggestUInt)`, This +## hold all from ``low(BiggestInt)`` to ``high(BiggestUInt)``, This ## type is for that purpose. from math import trunc -when defined(nimPreviewSlimSystem): - import std/assertions - type Int128* = object - udata: array[4, uint32] + udata: array[4,uint32] template sdata(arg: Int128, idx: int): int32 = # udata and sdata was supposed to be in a union, but unions are @@ -20,12 +17,12 @@ template sdata(arg: Int128, idx: int): int32 = # encoding least significant int first (like LittleEndian) const - Zero* = Int128(udata: [0'u32, 0, 0, 0]) - One* = Int128(udata: [1'u32, 0, 0, 0]) - Ten* = Int128(udata: [10'u32, 0, 0, 0]) - Min = Int128(udata: [0'u32, 0, 0, 0x80000000'u32]) - Max = Int128(udata: [high(uint32), high(uint32), high(uint32), uint32(high(int32))]) - NegOne* = Int128(udata: [0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32]) + Zero* = Int128(udata: [0'u32,0,0,0]) + One* = Int128(udata: [1'u32,0,0,0]) + Ten* = Int128(udata: [10'u32,0,0,0]) + Min = Int128(udata: [0'u32,0,0,0x80000000'u32]) + Max = Int128(udata: [high(uint32),high(uint32),high(uint32),uint32(high(int32))]) + NegOne* = Int128(udata: [0xffffffff'u32,0xffffffff'u32,0xffffffff'u32,0xffffffff'u32]) template low*(t: typedesc[Int128]): Int128 = Min template high*(t: typedesc[Int128]): Int128 = Max @@ -33,34 +30,39 @@ template high*(t: typedesc[Int128]): Int128 = Max proc `$`*(a: Int128): string proc toInt128*[T: SomeInteger | bool](arg: T): Int128 = - {.noSideEffect.}: - when T is bool: result.sdata(0) = int32(arg) - elif T is SomeUnsignedInt: - when sizeof(arg) <= 4: - result.udata[0] = uint32(arg) - else: - result.udata[0] = uint32(arg and T(0xffffffff)) - result.udata[1] = uint32(arg shr 32) - elif sizeof(arg) <= 4: - result.sdata(0) = int32(arg) - if arg < 0: # sign extend - result.sdata(1) = -1 - result.sdata(2) = -1 - result.sdata(3) = -1 + when T is bool: result.sdata(0) = int32(arg) + elif T is SomeUnsignedInt: + when sizeof(arg) <= 4: + result.udata[0] = uint32(arg) else: - let tmp = int64(arg) - result.udata[0] = uint32(tmp and 0xffffffff) - result.sdata(1) = int32(tmp shr 32) - if arg < 0: # sign extend - result.sdata(2) = -1 - result.sdata(3) = -1 + result.udata[0] = uint32(arg and T(0xffffffff)) + result.udata[1] = uint32(arg shr 32) + elif sizeof(arg) <= 4: + result.sdata(0) = int32(arg) + if arg < 0: # sign extend + result.sdata(1) = -1 + result.sdata(2) = -1 + result.sdata(3) = -1 + else: + let tmp = int64(arg) + result.udata[0] = uint32(tmp and 0xffffffff) + result.sdata(1) = int32(tmp shr 32) + if arg < 0: # sign extend + result.sdata(2) = -1 + result.sdata(3) = -1 template isNegative(arg: Int128): bool = arg.sdata(3) < 0 -proc bitconcat(a, b: uint32): uint64 = +template isNegative(arg: int32): bool = + arg < 0 + +proc bitconcat(a,b: uint32): uint64 = (uint64(a) shl 32) or uint64(b) +proc bitsplit(a: uint64): (uint32,uint32) = + (cast[uint32](a shr 32), cast[uint32](a)) + proc toInt64*(arg: Int128): int64 = if isNegative(arg): assert(arg.sdata(3) == -1, "out of range") @@ -174,6 +176,7 @@ proc toHex*(arg: Int128): string = result.addToHex(arg) proc inc*(a: var Int128, y: uint32 = 1) = + let input = a a.udata[0] += y if unlikely(a.udata[0] < y): a.udata[1].inc @@ -183,7 +186,7 @@ proc inc*(a: var Int128, y: uint32 = 1) = a.udata[3].inc doAssert(a.sdata(3) != low(int32), "overflow") -proc cmp*(a, b: Int128): int = +proc cmp*(a,b: Int128): int = let tmp1 = cmp(a.sdata(3), b.sdata(3)) if tmp1 != 0: return tmp1 let tmp2 = cmp(a.udata[2], b.udata[2]) @@ -193,38 +196,44 @@ proc cmp*(a, b: Int128): int = let tmp4 = cmp(a.udata[0], b.udata[0]) return tmp4 -proc `<`*(a, b: Int128): bool = - cmp(a, b) < 0 +proc `<`*(a,b: Int128): bool = + cmp(a,b) < 0 -proc `<=`*(a, b: Int128): bool = - cmp(a, b) <= 0 +proc `<=`*(a,b: Int128): bool = + cmp(a,b) <= 0 -proc `==`*(a, b: Int128): bool = +proc `==`*(a,b: Int128): bool = if a.udata[0] != b.udata[0]: return false if a.udata[1] != b.udata[1]: return false if a.udata[2] != b.udata[2]: return false if a.udata[3] != b.udata[3]: return false return true +proc inplaceBitnot(a: var Int128) = + a.udata[0] = not a.udata[0] + a.udata[1] = not a.udata[1] + a.udata[2] = not a.udata[2] + a.udata[3] = not a.udata[3] + proc bitnot*(a: Int128): Int128 = result.udata[0] = not a.udata[0] result.udata[1] = not a.udata[1] result.udata[2] = not a.udata[2] result.udata[3] = not a.udata[3] -proc bitand*(a, b: Int128): Int128 = +proc bitand*(a,b: Int128): Int128 = result.udata[0] = a.udata[0] and b.udata[0] result.udata[1] = a.udata[1] and b.udata[1] result.udata[2] = a.udata[2] and b.udata[2] result.udata[3] = a.udata[3] and b.udata[3] -proc bitor*(a, b: Int128): Int128 = +proc bitor*(a,b: Int128): Int128 = result.udata[0] = a.udata[0] or b.udata[0] result.udata[1] = a.udata[1] or b.udata[1] result.udata[2] = a.udata[2] or b.udata[2] result.udata[3] = a.udata[3] or b.udata[3] -proc bitxor*(a, b: Int128): Int128 = +proc bitxor*(a,b: Int128): Int128 = result.udata[0] = a.udata[0] xor b.udata[0] result.udata[1] = a.udata[1] xor b.udata[1] result.udata[2] = a.udata[2] xor b.udata[2] @@ -279,7 +288,7 @@ proc `shl`*(a: Int128, b: int): Int128 = result.udata[2] = 0 result.udata[3] = a.udata[0] shl (b and 31) -proc `+`*(a, b: Int128): Int128 = +proc `+`*(a,b: Int128): Int128 = let tmp0 = uint64(a.udata[0]) + uint64(b.udata[0]) result.udata[0] = cast[uint32](tmp0) let tmp1 = uint64(a.udata[1]) + uint64(b.udata[1]) + (tmp0 shr 32) @@ -296,7 +305,7 @@ proc `-`*(a: Int128): Int128 = result = bitnot(a) result.inc -proc `-`*(a, b: Int128): Int128 = +proc `-`*(a,b: Int128): Int128 = a + (-b) proc `-=`*(a: var Int128, b: Int128) = @@ -333,7 +342,7 @@ proc `*`*(a: Int128, b: int32): Int128 = proc `*=`*(a: var Int128, b: int32): Int128 = result = result * b -proc makeInt128(high, low: uint64): Int128 = +proc makeInt128(high,low: uint64): Int128 = result.udata[0] = cast[uint32](low) result.udata[1] = cast[uint32](low shr 32) result.udata[2] = cast[uint32](high) @@ -345,11 +354,24 @@ proc high64(a: Int128): uint64 = proc low64(a: Int128): uint64 = bitconcat(a.udata[1], a.udata[0]) -proc `*`*(lhs, rhs: Int128): Int128 = - let a32 = uint64(lhs.udata[1]) - let a00 = uint64(lhs.udata[0]) - let b32 = uint64(rhs.udata[1]) - let b00 = uint64(rhs.udata[0]) +proc `*`*(lhs,rhs: Int128): Int128 = + let + a = cast[uint64](lhs.udata[0]) + b = cast[uint64](lhs.udata[1]) + c = cast[uint64](lhs.udata[2]) + d = cast[uint64](lhs.udata[3]) + + e = cast[uint64](rhs.udata[0]) + f = cast[uint64](rhs.udata[1]) + g = cast[uint64](rhs.udata[2]) + h = cast[uint64](rhs.udata[3]) + + + let a32 = cast[uint64](lhs.udata[1]) + let a00 = cast[uint64](lhs.udata[0]) + let b32 = cast[uint64](rhs.udata[1]) + let b00 = cast[uint64](rhs.udata[0]) + result = makeInt128(high64(lhs) * low64(rhs) + low64(lhs) * high64(rhs) + a32 * b32, a00 * b00) result += toInt128(a32 * b00) shl 32 result += toInt128(a00 * b32) shl 32 @@ -367,7 +389,7 @@ proc fastLog2*(a: Int128): int = if a.udata[1] != 0: return 32 + fastLog2(a.udata[1]) if a.udata[0] != 0: - return fastLog2(a.udata[0]) + return fastLog2(a.udata[0]) proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] = assert(divisor != Zero) @@ -419,18 +441,18 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] = else: result.remainder = dividend -proc `div`*(a, b: Int128): Int128 = - let (a, _) = divMod(a, b) +proc `div`*(a,b: Int128): Int128 = + let (a,b) = divMod(a,b) return a -proc `mod`*(a, b: Int128): Int128 = - let (_, b) = divMod(a, b) +proc `mod`*(a,b: Int128): Int128 = + let (a,b) = divMod(a,b) return b proc addInt128*(result: var string; value: Int128) = let initialSize = result.len if value == Zero: - result.add '0' + result.add "0" elif value == low(Int128): result.add "-170141183460469231731687303715884105728" else: @@ -451,13 +473,11 @@ proc addInt128*(result: var string; value: Int128) = j -= 1 proc `$`*(a: Int128): string = - # "-170141183460469231731687303715884105728".len == 41 - result = newStringOfCap(41) result.addInt128(a) proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 = assert(pos < arg.len) - assert(arg[pos] in {'-', '0'..'9'}) + assert(arg[pos] in {'-','0'..'9'}) var isNegative = false var pos = pos @@ -477,13 +497,13 @@ proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 = # fluff proc `<`*(a: Int128, b: BiggestInt): bool = - cmp(a, toInt128(b)) < 0 + cmp(a,toInt128(b)) < 0 proc `<`*(a: BiggestInt, b: Int128): bool = cmp(toInt128(a), b) < 0 proc `<=`*(a: Int128, b: BiggestInt): bool = - cmp(a, toInt128(b)) <= 0 + cmp(a,toInt128(b)) <= 0 proc `<=`*(a: BiggestInt, b: Int128): bool = cmp(toInt128(a), b) <= 0 @@ -519,7 +539,7 @@ proc toFloat64*(arg: Int128): float64 = proc ldexp(x: float64, exp: cint): float64 {.importc: "ldexp", header: "".} -template bitor(a, b, c: Int128): Int128 = bitor(bitor(a, b), c) +template bitor(a,b,c: Int128): Int128 = bitor(bitor(a,b), c) proc toInt128*(arg: float64): Int128 = let isNegative = arg < 0 diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 2674605dc..01f0a002a 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -13,9 +13,6 @@ import ast, types, renderer, intsets -when defined(nimPreviewSlimSystem): - import std/assertions - proc canAlias(arg, ret: PType; marker: var IntSet): bool proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool = @@ -67,30 +64,19 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool = else: result = false -proc isValueOnlyType(t: PType): bool = +proc isValueOnlyType(t: PType): bool = # t doesn't contain pointers and references proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent} result = not types.searchTypeFor(t, wrap) proc canAlias*(arg, ret: PType): bool = if isValueOnlyType(arg): - # can alias only with addr(arg.x) and we don't care if it is not safe + # can alias only with unsafeAddr(arg.x) and we don't care if it is not safe result = false else: var marker = initIntSet() result = canAlias(arg, ret, marker) -proc containsVariable(n: PNode): bool = - case n.kind - of nodesToIgnoreSet: - result = false - of nkSym: - result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp} - else: - for ch in n: - if containsVariable(ch): return true - result = false - proc checkIsolate*(n: PNode): bool = if types.containsTyRef(n.typ): # XXX Maybe require that 'n.typ' is acyclic. This is not much @@ -99,10 +85,7 @@ proc checkIsolate*(n: PNode): bool = of nkCharLit..nkNilLit: result = true of nkCallKinds: - # XXX: as long as we don't update the analysis while examining arguments - # we can do an early check of the return type, otherwise this is a - # bug and needs to be moved below - if tfNoSideEffect notin n[0].typ.flags: + if n[0].typ.flags * {tfGcSafe, tfNoSideEffect} == {}: return false for i in 1.. can stay expression based - case n.kind - of nkCallKinds, nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr, - nkObjConstr, nkBracket, nkCurly, - nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, - nkConv, nkHiddenStdConv, nkHiddenSubConv: + if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or + (n.kind in {nkObjConstr, nkBracket, nkCurly}): for c in n: if not p.isSimpleExpr(c): return false result = true - of nkStmtListExpr: - for i in 0.. 2: @@ -617,7 +589,7 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = template applyFormat(frmtA, frmtB) = if i == 0: applyFormat(frmtA) else: applyFormat(frmtB) - case op + case op: of mAddI: applyFormat("addInt($1, $2)", "($1 + $2)") of mSubI: applyFormat("subInt($1, $2)", "($1 - $2)") of mMulI: applyFormat("mulInt($1, $2)", "($1 * $2)") @@ -634,12 +606,12 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = if n[1].typ.size <= 4: applyFormat("($1 << $2)", "($1 << $2)") else: - applyFormat("($1 * Math.pow(2, $2))", "($1 * Math.pow(2, $2))") + applyFormat("($1 * Math.pow(2,$2))", "($1 * Math.pow(2,$2))") of mAshrI: if n[1].typ.size <= 4: applyFormat("($1 >> $2)", "($1 >> $2)") else: - applyFormat("Math.floor($1 / Math.pow(2, $2))", "Math.floor($1 / Math.pow(2, $2))") + applyFormat("Math.floor($1 / Math.pow(2,$2))", "Math.floor($1 / Math.pow(2,$2))") of mBitandI: applyFormat("($1 & $2)", "($1 & $2)") of mBitorI: applyFormat("($1 | $2)", "($1 | $2)") of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)") @@ -683,10 +655,13 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mUnaryMinusF64: applyFormat("-($1)", "-($1)") of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)") of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)") - of mIntToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") - of mInt64ToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") + of mIntToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")") + of mInt64ToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")") + of mFloatToStr: + useMagic(p, "nimFloatToString") + applyFormat "cstrToNimstr(nimFloatToString($1))" of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)") - of mStrToStr, mUnown, mIsolate, mFinished: applyFormat("$1", "$1") + of mStrToStr, mUnown, mIsolate: applyFormat("$1", "$1") else: assert false, $op @@ -695,10 +670,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mAddU: binaryUintExpr(p, n, r, "+") of mSubU: binaryUintExpr(p, n, r, "-") of mMulU: binaryUintExpr(p, n, r, "*") - of mDivU: - binaryUintExpr(p, n, r, "/") - if n[1].typ.skipTypes(abstractRange).size == 8: - r.res = "Math.trunc($1)" % [r.res] + of mDivU: binaryUintExpr(p, n, r, "/") of mDivI: arithAux(p, n, r, op) of mModI: @@ -707,8 +679,10 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = var x, y: TCompRes gen(p, n[1], x) gen(p, n[2], y) - r.res = "($1 >>> $2)" % [x.rdLoc, y.rdLoc] - of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr: + let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) + r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc] + of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, + mCStrToStr, mStrToStr, mEnumToStr: arithAux(p, n, r, op) of mEqRef: if mapType(n[1].typ) != etyBaseIndex: @@ -727,16 +701,13 @@ proc hasFrameInfo(p: PProc): bool = ((p.prc == nil) or not (sfPure in p.prc.flags)) proc lineDir(config: ConfigRef, info: TLineInfo, line: int): Rope = - "/* line $2:$3 \"$1\" */$n" % [ - rope(toFullPath(config, info)), rope(line), rope(info.toColumn) - ] + ropes.`%`("// line $2 \"$1\"$n", + [rope(toFullPath(config, info)), rope(line)]) proc genLineDir(p: PProc, n: PNode) = let line = toLinenumber(n.info) if line < 0: return - if optEmbedOrigSrc in p.config.globalOptions: - lineF(p, "//$1$n", [sourceLine(p.config, n.info)]) if optLineDir in p.options or optLineDir in p.config.options: lineF(p, "$1", [lineDir(p.config, n.info, line)]) if hasFrameInfo(p): @@ -751,9 +722,9 @@ proc genWhileStmt(p: PProc, n: PNode) = p.blocks[^1].id = -p.unique p.blocks[^1].isLoop = true let labl = p.unique.rope - lineF(p, "Label$1: while (true) {$n", [labl]) + lineF(p, "L$1: while (true) {$n", [labl]) p.nested: gen(p, n[0], cond) - lineF(p, "if (!$1) break Label$2;$n", + lineF(p, "if (!$1) break L$2;$n", [cond.res, labl]) p.nested: genStmt(p, n[1]) lineF(p, "}$n", [labl]) @@ -766,7 +737,7 @@ proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = else: lineF(p, "$1;$n", [src.rdLoc]) src.kind = resNone - src.res = "" + src.res = nil proc genTry(p: PProc, n: PNode, r: var TCompRes) = # code to generate: @@ -776,8 +747,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = # try { # stmts; # --excHandler; - # } catch (EXCEPTION) { - # var prevJSError = lastJSError; lastJSError = EXCEPTION; + # } catch (EXC) { + # var prevJSError = lastJSError; lastJSError = EXC; # framePtr = tmpFramePtr; # --excHandler; # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { @@ -802,16 +773,18 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if catchBranchesExist: p.body.add("++excHandler;\L") var tmpFramePtr = rope"F" + if optStackTrace notin p.options: + tmpFramePtr = p.getTemp(true) + line(p, tmpFramePtr & " = framePtr;\L") lineF(p, "try {$n", []) var a: TCompRes gen(p, n[0], a) moveInto(p, a, r) var generalCatchBranchExists = false if catchBranchesExist: - p.body.addf("--excHandler;$n} catch (EXCEPTION) {$n var prevJSError = lastJSError;$n" & - " lastJSError = EXCEPTION;$n --excHandler;$n", []) - if hasFrameInfo(p): - line(p, "framePtr = $1;$n" % [tmpFramePtr]) + p.body.addf("--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" & + " lastJSError = EXC;$n --excHandler;$n", []) + line(p, "framePtr = $1;$n" % [tmpFramePtr]) while i < n.len and n[i].kind == nkExceptBranch: if n[i].len == 1: # general except section: @@ -821,7 +794,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = moveInto(p, a, r) if i > 1: lineF(p, "}$n", []) else: - var orExpr: Rope = "" + var orExpr: Rope = nil var excAlias: PNode = nil useMagic(p, "isObj") @@ -834,13 +807,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 == "" + doAssert orExpr == nil elif it.kind == nkType: throwObj = it else: internalError(p.config, n.info, "genTryStmt") - if orExpr != "": orExpr.add("||") + if orExpr != nil: 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): @@ -870,8 +843,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = line(p, "}\L") lineF(p, "lastJSError = prevJSError;$n") line(p, "} finally {\L") - if hasFrameInfo(p): - line(p, "framePtr = $1;$n" % [tmpFramePtr]) + line(p, "framePtr = $1;$n" % [tmpFramePtr]) if i < n.len and n[i].kind == nkFinally: genStmt(p, n[i][0]) line(p, "}\L") @@ -892,18 +864,14 @@ proc genRaiseStmt(p: PProc, n: PNode) = proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var - a, b, cond, stmt: TCompRes + cond, stmt: TCompRes + totalRange = 0 genLineDir(p, n) gen(p, n[0], cond) - let typeKind = skipTypes(n[0].typ, abstractVar).kind - var transferRange = false - let anyString = typeKind in {tyString, tyCstring} - case typeKind - of tyString: + let stringSwitch = skipTypes(n[0].typ, abstractVar).kind == tyString + if stringSwitch: useMagic(p, "toJSStr") lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc]) - of tyFloat..tyFloat128, tyInt..tyInt64, tyUInt..tyUInt64: - transferRange = true else: lineF(p, "switch ($1) {$n", [cond.rdLoc]) if not isEmptyType(n.typ): @@ -911,71 +879,41 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = r.res = getTemp(p) for i in 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]) - while v.intVal <= e[1].intVal: - gen(p, v, cond) - lineF(p, "case $1:$n", [cond.rdLoc]) - inc(v.intVal) + 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 anyString: + if stringSwitch: 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: - 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, "){", []) + gen(p, e, cond) + lineF(p, "case $1:$n", [cond.rdLoc]) p.nested: gen(p, lastSon(it), stmt) moveInto(p, stmt, r) - if transferRange: - lineF(p, "}$n", []) - else: - lineF(p, "break;$n", []) + lineF(p, "break;$n", []) of nkElse: - if transferRange: - lineF(p, "else{$n", []) - else: - lineF(p, "default: $n", []) + lineF(p, "default: $n", []) p.nested: gen(p, it[0], stmt) moveInto(p, stmt, r) - if transferRange: - lineF(p, "}$n", []) - else: - lineF(p, "break;$n", []) + lineF(p, "break;$n", []) else: internalError(p.config, it.info, "jsgen.genCaseStmt") - if not transferRange: - lineF(p, "}$n", []) + lineF(p, "}$n", []) proc genBlock(p: PProc, n: PNode, r: var TCompRes) = inc(p.unique) @@ -987,12 +925,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: {$n", [labl.rope]) + lineF(p, "L$1: do {$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, "};$n", [labl.rope]) + lineF(p, "} while(false);$n", [labl.rope]) proc genBreakStmt(p: PProc, n: PNode) = var idx: int @@ -1010,11 +948,11 @@ proc genBreakStmt(p: PProc, n: PNode) = if idx < 0 or not p.blocks[idx].isLoop: internalError(p.config, n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used - lineF(p, "break Label$1;$n", [rope(p.blocks[idx].id)]) + lineF(p, "break L$1;$n", [rope(p.blocks[idx].id)]) proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) - p.body.add p.indentLine("") + p.body.add p.indentLine(nil) for i in 0..= 2 and x[0].typ.skipTypes(abstractInst).kind == tyCstring: + if x.kind == nkBracketExpr and x.len >= 2 and x[0].typ.skipTypes(abstractInst).kind == tyCString: localError(p.config, x.info, "cstring doesn't support `[]=` operator") gen(p, x, a) @@ -1125,24 +1063,24 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = xtyp = etySeq case xtyp of etySeq: - if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") lineF(p, "$1 = nimCopy(null, $2, $3);$n", [a.rdLoc, b.res, genTypeInfo(p, y.typ)]) of etyObject: - if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + if x.typ.kind in {tyVar} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") # supports proc getF(): var T if x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].kind in nkCallKinds: - lineF(p, "nimCopy($1, $2, $3);$n", - [a.res, b.res, genTypeInfo(p, x.typ)]) + lineF(p, "nimCopy($1, $2, $3);$n", + [a.res, b.res, genTypeInfo(p, y.typ)]) else: lineF(p, "$1 = nimCopy($1, $2, $3);$n", - [a.res, b.res, genTypeInfo(p, x.typ)]) + [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if y.kind == nkCall: @@ -1151,24 +1089,16 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = elif b.typ == etyBaseIndex: lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res]) elif b.typ == etyNone: - internalAssert p.config, b.address == "" + internalAssert p.config, b.address == nil lineF(p, "$# = [$#, 0];$n", [a.address, b.res]) elif x.typ.kind == tyVar and y.typ.kind == tyPtr: lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res]) lineF(p, "$1 = $2;$n", [a.address, b.res]) lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) - elif a.typ == etyBaseIndex: - # array indexing may not map to var type - if b.address != "": - lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) - else: - lineF(p, "$1 = $2;$n", [a.address, b.res]) else: internalError(p.config, x.info, $("genAsgn", b.typ, a.typ)) - elif b.address != "": - lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: - lineF(p, "$1 = $2;$n", [a.address, b.res]) + lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) @@ -1217,7 +1147,7 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = else: if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr") var f = b[1].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) r.res = makeJSString($f.loc.r) internalAssert p.config, a.typ != etyBaseIndex r.address = a.res @@ -1245,7 +1175,7 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = else: if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess") var f = n[1].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) r.res = "$1.$2" % [r.res, f.loc.r] mkTemp(1) r.kind = resExpr @@ -1266,11 +1196,11 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = # Field symbol var field = accessExpr[1].sym internalAssert p.config, field.kind == skField - if field.loc.r == "": field.loc.r = mangleName(p.module, field) + if field.loc.r == nil: field.loc.r = mangleName(p.module, field) # Discriminant symbol let disc = checkExpr[2].sym internalAssert p.config, disc.kind == skField - if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc) + if disc.loc.r == nil: disc.loc.r = mangleName(p.module, disc) var setx: TCompRes gen(p, checkExpr[1], setx) @@ -1282,13 +1212,12 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = let tmp = p.getTemp() lineF(p, "var $1 = $2;$n", tmp, obj.res) - useMagic(p, "raiseFieldError2") + useMagic(p, "raiseFieldError") useMagic(p, "makeNimstrLit") - useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen - let msg = genFieldDefect(p.config, field.name.s, disc) - lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n", - setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===", - makeJSString(msg), genTypeInfo(p, disc.typ)) + let msg = genFieldDefect(field, disc) + lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError(makeNimstrLit($5)); }$n", + setx.res, tmp, disc.loc.r, if negCheck: ~"!==" else: ~"===", + makeJSString(msg)) if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex: r.typ = etyBaseIndex @@ -1316,12 +1245,12 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = if optBoundsCheck in p.options: useMagic(p, "chckIndx") if first == 0: # save a couple chars - r.res = "chckIndx($1, 0, ($2).length - 1)" % [b.res, tmp] + r.res = "chckIndx($1, 0, ($2).length-1)" % [b.res, tmp] else: - r.res = "chckIndx($1, $2, ($3).length + ($2) - 1) - ($2)" % [ + r.res = "chckIndx($1, $2, ($3).length+($2)-1)-($2)" % [ b.res, rope(first), tmp] elif first != 0: - r.res = "($1) - ($2)" % [b.res, rope(first)] + r.res = "($1)-($2)" % [b.res, rope(first)] else: r.res = b.res r.kind = resExpr @@ -1330,14 +1259,14 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = var ty = skipTypes(n[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind - of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: + of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n, r) of tyTuple: genFieldAddr(p, n, r) else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') r.typ = mapType(n.typ) - if r.res == "": internalError(p.config, n.info, "genArrayAccess") - if ty.kind == tyCstring: + if r.res == nil: internalError(p.config, n.info, "genArrayAccess") + if ty.kind == tyCString: r.res = "$1.charCodeAt($2)" % [r.address, r.res] elif r.typ == etyBaseIndex: if needsTemp(p, n[0]): @@ -1361,96 +1290,67 @@ template isIndirect(x: PSym): bool = v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, skConst, skTemp, skLet}) -proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = - let s = n.sym - if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3") - case s.kind - of skParam: - r.res = s.loc.r - r.address = "" - r.typ = etyNone - of skVar, skLet, skResult: - r.kind = resExpr - let jsType = mapType(p): - if typ.isNil: - n.typ - else: - typ - if jsType == etyObject: - # make addr() a no-op: - r.typ = etyNone - if isIndirect(s): - r.res = s.loc.r & "[0]" - else: - r.res = s.loc.r - r.address = "" - elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: - # for ease of code generation, we do not distinguish between - # sfAddrTaken and sfGlobal. - r.typ = etyBaseIndex - r.address = s.loc.r - r.res = rope("0") - else: - # 'var openArray' for instance produces an 'addr' but this is harmless: - gen(p, n, r) - #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n)) - else: internalError(p.config, n.info, $("genAddr: 2", s.kind)) - proc genAddr(p: PProc, n: PNode, r: var TCompRes) = - if n.kind == nkSym: - genSymAddr(p, n, nil, r) - else: - case n[0].kind - of nkSym: - genSymAddr(p, n[0], n.typ, r) - of nkCheckedFieldExpr: - genCheckedFieldOp(p, n[0], n.typ, r) - of nkDotExpr: - if mapType(p, n.typ) == etyBaseIndex: - genFieldAddr(p, n[0], r) + case n[0].kind + of nkSym: + let s = n[0].sym + if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3") + case s.kind + of skParam: + r.res = s.loc.r + r.address = nil + r.typ = etyNone + of skVar, skLet, skResult: + r.kind = resExpr + let jsType = mapType(p, n.typ) + if jsType == etyObject: + # make addr() a no-op: + r.typ = etyNone + if isIndirect(s): + r.res = s.loc.r & "[0]" + else: + r.res = s.loc.r + r.address = nil + elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: + # for ease of code generation, we do not distinguish between + # sfAddrTaken and sfGlobal. + r.typ = etyBaseIndex + r.address = s.loc.r + r.res = rope("0") else: - genFieldAccess(p, n[0], r) - of nkBracketExpr: - var ty = skipTypes(n[0].typ, abstractVarRange) - if ty.kind in MappedToObject: - gen(p, n[0], r) - else: - let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind - case kindOfIndexedExpr - of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: - genArrayAddr(p, n[0], r) - of tyTuple: - genFieldAddr(p, n[0], r) - of tyGenericBody: - genAddr(p, n[^1], r) - else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') - of nkObjDownConv: - gen(p, n[0], r) - of nkHiddenDeref: - gen(p, n[0], r) - of nkDerefExpr: - var x = n[0] - if n.kind == nkHiddenAddr: - x = n[0][0] - if n.typ.skipTypes(abstractVar).kind != tyOpenArray: - x.typ = n.typ - gen(p, x, r) - of nkHiddenAddr: - gen(p, n[0], r) - of nkConv: - genAddr(p, n[0], r) - of nkStmtListExpr: - if n.len == 1: gen(p, n[0], r) - else: internalError(p.config, n[0].info, "genAddr for complex nkStmtListExpr") - of nkCallKinds: - if n[0].typ.kind == tyOpenArray: # 'var openArray' for instance produces an 'addr' but this is harmless: - # namely toOpenArray(a, 1, 3) gen(p, n[0], r) - else: - internalError(p.config, n[0].info, "genAddr: " & $n[0].kind) + #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n)) + else: internalError(p.config, n.info, $("genAddr: 2", s.kind)) + of nkCheckedFieldExpr: + genCheckedFieldOp(p, n[0], n.typ, r) + of nkDotExpr: + if mapType(p, n.typ) == etyBaseIndex: + genFieldAddr(p, n[0], r) else: - internalError(p.config, n[0].info, "genAddr: " & $n[0].kind) + genFieldAccess(p, n[0], r) + of nkBracketExpr: + var ty = skipTypes(n[0].typ, abstractVarRange) + if ty.kind in MappedToObject: + gen(p, n[0], r) + else: + let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind + case kindOfIndexedExpr + of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: + genArrayAddr(p, n[0], r) + of tyTuple: + genFieldAddr(p, n[0], r) + else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') + of nkObjDownConv: + gen(p, n[0], r) + of nkHiddenDeref: + gen(p, n[0], r) + of nkHiddenAddr: + gen(p, n[0], r) + of nkStmtListExpr: + if n.len == 1: gen(p, n[0], r) + else: internalError(p.config, n[0].info, "genAddr for complex nkStmtListExpr") + else: internalError(p.config, n[0].info, "genAddr: " & $n[0].kind) proc attachProc(p: PProc; content: Rope; s: PSym) = p.g.code.add(content) @@ -1489,10 +1389,10 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = var s = n.sym case s.kind of skVar, skLet, skParam, skTemp, skResult, skForVar: - if s.loc.r == "": + if s.loc.r == nil: internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) if sfCompileTime in s.flags: - genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info)) + genVarInit(p, s, if s.ast != nil: s.ast else: newNodeI(nkEmpty, s.info)) if s.kind == skParam: genCopyForParamIfNeeded(p, n) let k = mapType(p, s.typ) @@ -1514,7 +1414,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = r.res = s.loc.r of skConst: genConstant(p, s) - if s.loc.r == "": + if s.loc.r == nil: internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r of skProc, skFunc, skConverter, skMethod: @@ -1523,10 +1423,10 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = s.name.s) discard mangleName(p.module, s) r.res = s.loc.r - if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or + if lfNoDecl in s.loc.flags or s.magic != mNone or {sfImportc, sfInfixCall} * s.flags != {}: discard - elif s.kind == skMethod and getBody(p.module.graph, s).kind == nkEmpty: + elif s.kind == skMethod and s.getBody.kind == nkEmpty: # we cannot produce code for the dispatcher yet: discard elif sfForward in s.flags: @@ -1534,19 +1434,15 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = else: genProcForSymIfNeeded(p, s) else: - if s.loc.r == "": + if s.loc.r == nil: internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) - if mapType(p, s.typ) == etyBaseIndex: - r.address = s.loc.r - r.res = s.loc.r & "_Idx" - else: - r.res = s.loc.r + r.res = s.loc.r r.kind = resVal proc genDeref(p: PProc, n: PNode, r: var TCompRes) = let it = n[0] let t = mapType(p, it.typ) - if t == etyObject or it.typ.kind == tyLent: + if t == etyObject: gen(p, it, r) else: var a: TCompRes @@ -1559,7 +1455,7 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) = r.res = "$1[1]" % [tmp] r.tmpLoc = tmp elif a.typ == etyBaseIndex: - if a.tmpLoc != "": + if a.tmpLoc != nil: r.tmpLoc = a.tmpLoc r.res = a.rdLoc else: @@ -1682,9 +1578,9 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = # don't call '$' here for efficiency: let f = n[0].sym - if f.loc.r == "": f.loc.r = mangleName(p.module, f) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) if sfInfixCall in f.flags: - let pat = $n[0].sym.loc.r + let pat = n[0].sym.loc.r.data internalAssert p.config, pat.len > 0 if pat.contains({'#', '(', '@'}): var typ = skipTypes(n[0].typ, abstractInst) @@ -1694,10 +1590,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 == "": + if r.address == nil: globalError(p.config, n.info, "cannot invoke with infix syntax") r.res = "$1[$2]" % [r.address, r.res] - r.address = "" + r.address = nil r.typ = etyNone r.res.add(".") var op: TCompRes @@ -1765,22 +1661,16 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v t = t[0] proc arrayTypeForElemType(typ: PType): string = - let typ = typ.skipTypes(abstractRange) + # XXX This should also support tyEnum and tyBool case typ.kind of tyInt, tyInt32: "Int32Array" of tyInt16: "Int16Array" of tyInt8: "Int8Array" of tyUInt, tyUInt32: "Uint32Array" of tyUInt16: "Uint16Array" - of tyUInt8, tyChar, tyBool: "Uint8Array" + of tyUInt8: "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 = @@ -1793,7 +1683,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("0", indirect) of tyFloat..tyFloat128: result = putToSeq("0.0", indirect) - of tyRange, tyGenericInst, tyAlias, tySink, tyOwned, tyLent: + of tyRange, tyGenericInst, tyAlias, tySink, tyOwned: result = createVar(p, lastSon(typ), indirect) of tySet: result = putToSeq("{}", indirect) @@ -1835,26 +1725,26 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = createObjInitList(p, t, initIntSet(), initList) result = ("({$1})") % [initList] if indirect: result = "[$1]" % [result] - of tyVar, tyPtr, tyRef, tyPointer: + of tyVar, tyPtr, tyLent, tyRef, tyPointer: if mapType(p, t) == etyBaseIndex: result = putToSeq("[null, 0]", indirect) else: result = putToSeq("null", indirect) of tySequence, tyString: result = putToSeq("[]", indirect) - of tyCstring, tyProc: + of tyCString, tyProc: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: result = createVar(p, lastSon t, indirect) else: internalError(p.config, "createVar: " & $t.kind) - result = "" + result = nil else: internalError(p.config, "createVar: " & $t.kind) - result = "" + result = nil -template returnType: untyped = "" +template returnType: untyped = ~"" proc genVarInit(p: PProc, v: PSym, n: PNode) = var @@ -1873,7 +1763,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = inc p.extraIndent elif useGlobalPragmas: lineF(p, "if (globalThis.$1 === undefined) {$n", varName) - varCode = "globalThis." & $varName + varCode = $varName inc p.extraIndent else: varCode = "var $2" @@ -1952,9 +1842,10 @@ 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 = move p.body - #genLineDir(p, c.astdef) - genVarInit(p, c, c.astdef) + let oldBody = p.body + p.body = nil + #genLineDir(p, c.ast) + genVarInit(p, c, c.ast) p.g.constants.add(p.body) p.body = oldBody @@ -1974,13 +1865,13 @@ proc genNewSeq(p: PProc, n: PNode) = gen(p, n[1], x) gen(p, n[2], y) let t = skipTypes(n[1].typ, abstractVar)[0] - lineF(p, "$1 = new Array($2); for (var i = 0 ; i < $2 ; ++i) { $1[i] = $3; }", [ + lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ x.rdLoc, y.rdLoc, createVar(p, t, false)]) proc genOrd(p: PProc, n: PNode, r: var TCompRes) = case skipTypes(n[1].typ, abstractVar + abstractRange).kind of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n[1], r) - of tyBool: unaryExpr(p, n, r, "", "($1 ? 1 : 0)") + of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)") else: internalError(p.config, n.info, "genOrd") proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = @@ -2006,7 +1897,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 = "") = +proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) = useMagic(p, magic) r.res.add(magic & "(") var a: TCompRes @@ -2015,7 +1906,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.len == 0: + if a.address.isNil: r.res.add(a.res) r.res.add(", null") else: @@ -2023,14 +1914,14 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = else: r.res.add(a.res) - if typ != "": + if not typ.isNil: 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: @@ -2091,23 +1982,6 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) = genReset(p, n) #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) -proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes - r.res = rope("[") - r.kind = resExpr - for i in 0 ..< n.len: - if i > 0: r.res.add(", ") - gen(p, n[i], a) - if a.typ == etyBaseIndex: - r.res.addf("[$1, $2]", [a.address, a.res]) - else: - if not needsNoCopy(p, n[i]): - let typ = n[i].typ.skipTypes(abstractInst) - useMagic(p, "nimCopy") - a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] - r.res.add(a.res) - r.res.add("]") - proc genMagic(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes @@ -2127,10 +2001,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[1], lhs) gen(p, n[2], rhs) - if skipTypes(n[1].typ, abstractVarRange).kind == tyCstring: - let (b, tmp) = maybeMakeTemp(p, n[2], rhs) - r.res = "if (null != $1) { if (null == $2) $2 = $3; else $2 += $3; }" % - [b, lhs.rdLoc, tmp] + if skipTypes(n[1].typ, abstractVarRange).kind == tyCString: + r.res = "$1 += $2;" % [lhs.rdLoc, rhs.rdLoc] else: let (a, tmp) = maybeMakeTemp(p, n[1], lhs) r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp] @@ -2171,36 +2043,19 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mNew, mNewFinalize: genNew(p, n) of mChr: gen(p, n[1], r) of mArrToSeq: - # only array literals doesn't need copy - if n[1].kind == nkBracket: - genJSArrayConstr(p, n[1], r) + if needsNoCopy(p, n[1]): + gen(p, n[1], r) else: var x: 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 mDestroy: discard "ignore calls to the default destructor" of mOrd: genOrd(p, n, r) of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray: - var x: TCompRes - gen(p, n[1], x) - if skipTypes(n[1].typ, abstractInst).kind == tyCstring: - let (a, tmp) = maybeMakeTemp(p, n[1], x) - r.res = "(($1) == null ? 0 : ($2).length)" % [a, tmp] - else: - r.res = "($1).length" % [x.rdLoc] - r.kind = resExpr + unaryExpr(p, n, r, "", "($1).length") of mHigh: - var x: TCompRes - gen(p, n[1], x) - if skipTypes(n[1].typ, abstractInst).kind == tyCstring: - let (a, tmp) = maybeMakeTemp(p, n[1], x) - r.res = "(($1) == null ? -1 : ($2).length - 1)" % [a, tmp] - else: - r.res = "($1).length - 1" % [x.rdLoc] - r.kind = resExpr + unaryExpr(p, n, r, "", "(($1).length-1)") of mInc: if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: binaryUintExpr(p, n, r, "+", true) @@ -2214,9 +2069,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)", true) of mSetLengthStr: - binaryExpr(p, n, r, "mnewString", - """if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); } - else {$3.length = $4; }""") + binaryExpr(p, n, r, "mnewString", "($1.length = $2)") of mSetLengthSeq: var x, y: TCompRes gen(p, n[1], x) @@ -2224,7 +2077,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = let t = skipTypes(n[1].typ, abstractVar)[0] let (a, tmp) = maybeMakeTemp(p, n[1], x) let (b, tmp2) = maybeMakeTemp(p, n[2], y) - r.res = """if ($1.length < $2) { for (var i = $4.length ; i < $5 ; ++i) $4.push($3); } + r.res = """if ($1.length < $2) { for (var i=$4.length;i<$5;++i) $4.push($3); } else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2] r.kind = resExpr of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") @@ -2241,7 +2094,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mNewSeq: genNewSeq(p, n) of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]") of mOf: genOf(p, n, r) - of mDefault, mZeroDefault: genDefault(p, n, r) + of mDefault: genDefault(p, n, r) of mReset, mWasMoved: genReset(p, n) of mEcho: genEcho(p, n, r) of mNLen..mNError, mSlurp, mStaticExec: @@ -2261,7 +2114,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[1], x) gen(p, n[2], y) gen(p, n[3], z) - r.res = "($1.slice($2, $3 + 1))" % [x.rdLoc, y.rdLoc, z.rdLoc] + r.res = "($1.slice($2, $3+1))" % [x.rdLoc, y.rdLoc, z.rdLoc] r.kind = resExpr of mMove: genMove(p, n, r) @@ -2281,11 +2134,7 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = if it.kind == nkRange: gen(p, it[0], a) gen(p, it[1], b) - - if it[0].typ.kind == tyBool: - r.res.addf("$1, $2", [a.res, b.res]) - else: - r.res.addf("[$1, $2]", [a.res, b.res]) + r.res.addf("[$1, $2]", [a.res, b.res]) else: gen(p, it, a) r.res.add(a.res) @@ -2298,26 +2147,21 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = r.res = tmp proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = - ## Constructs array or sequence. - ## Nim array of uint8..uint32, int8..int32 maps to JS typed arrays. - ## Nim sequence maps to JS array. - var t = skipTypes(n.typ, abstractInst) - let e = elemType(t) - let jsTyp = arrayTypeForElemType(e) - if skipTypes(n.typ, abstractVarRange).kind != tySequence and jsTyp.len > 0: - # generate typed array - # for example Nim generates `new Uint8Array([1, 2, 3])` for `[byte(1), 2, 3]` - # TODO use `set` or loop to initialize typed array which improves performances in some situations - var a: TCompRes - r.res = "new $1([" % [rope(jsTyp)] - r.kind = resExpr - for i in 0 ..< n.len: - if i > 0: r.res.add(", ") - gen(p, n[i], a) + var a: TCompRes + r.res = rope("[") + r.kind = resExpr + for i in 0.. 0: r.res.add(", ") + gen(p, n[i], a) + if a.typ == etyBaseIndex: + r.res.addf("[$1, $2]", [a.address, a.res]) + else: + if not needsNoCopy(p, n[i]): + let typ = n[i].typ.skipTypes(abstractInst) + useMagic(p, "nimCopy") + a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] r.res.add(a.res) - r.res.add("])") - else: - genJSArrayConstr(p, n, r) + r.res.add("]") proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes @@ -2343,7 +2187,6 @@ 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] @@ -2351,8 +2194,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 == "": f.loc.r = mangleName(p.module, f) - fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id) + if f.loc.r == nil: f.loc.r = mangleName(p.module, f) + fieldIDs.incl(f.id) let typ = val.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: @@ -2384,7 +2227,7 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) = r.res = "(!!($1))" % [r.res] r.kind = resExpr elif toInt: - r.res = "(($1) | 0)" % [r.res] + r.res = "(($1)|0)" % [r.res] else: # TODO: What types must we handle here? discard @@ -2412,7 +2255,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 == "": internalError(p.config, n.info, "convStrToCStr") + if r.res == nil: internalError(p.config, n.info, "convStrToCStr") useMagic(p, "toJSStr") r.res = "toJSStr($1)" % [r.res] r.kind = resExpr @@ -2424,7 +2267,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 == "": internalError(p.config, n.info, "convCStrToStr") + if r.res == nil: internalError(p.config, n.info, "convCStrToStr") useMagic(p, "cstrToNimstr") r.res = "cstrToNimstr($1)" % [r.res] r.kind = resExpr @@ -2440,7 +2283,7 @@ proc genReturnStmt(p: PProc, n: PNode) = proc frameCreate(p: PProc; procname, filename: Rope): Rope = const frameFmt = - "var F = {procname: $1, prev: framePtr, filename: $2, line: 0};$n" + "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" result = p.indentLine(frameFmt % [procname, filename]) result.add p.indentLine(ropes.`%`("framePtr = F;$n", [])) @@ -2454,11 +2297,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 = "" + result = nil if p.beforeRetNeeded: - result.add p.indentLine("BeforeRet: {\n") + result.add p.indentLine(~"BeforeRet: do {$n") result.add p.body - result.add p.indentLine("};\n") + result.add p.indentLine(~"} while (false);$n") else: result.add(p.body) if prc.typ.callConv == ccSysCall: @@ -2468,13 +2311,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope = result.add(frameDestroy(p)) proc optionalLine(p: Rope): Rope = - if p == "": - return "" + if p == nil: + return nil else: return p & "\L" proc genProc(oldProc: PProc, prc: PSym): Rope = - ## Generate a JS procedure ('function'). var resultSym: PSym a: TCompRes @@ -2482,18 +2324,16 @@ 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 = "" - var resultAsgn: Rope = "" + var returnStmt: Rope = nil + var resultAsgn: Rope = nil var name = mangleName(p.module, prc) let header = generateHeader(p, prc.typ) if prc.typ[0] != nil and sfPure notin prc.flags: resultSym = prc.ast[resultPos].sym let mname = mangleName(p.module, resultSym) - # otherwise uses "fat pointers" - let useRawPointer = not isIndirect(resultSym) and + if not isIndirect(resultSym) and resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and - mapType(p, resultSym.typ) == etyBaseIndex - if useRawPointer: + mapType(p, resultSym.typ) == etyBaseIndex: resultAsgn = p.indentLine(("var $# = null;$n") % [mname]) resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) else: @@ -2505,7 +2345,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = else: returnStmt = "return $#;$n" % [a.res] - var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, dontUseCache) + var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, cache = false) if sfInjectDestructors in prc.flags: transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody) @@ -2528,7 +2368,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 @@ -2537,10 +2377,10 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = # references will end up calling the reloaded code. var thunkName = name name = name & "IMLP" - result.add("\Lfunction $#() { return $#.apply(this, arguments); }$n" % + result.add("function $#() { return $#.apply(this, arguments); }$n" % [thunkName, name]) - def = "\Lfunction $#($#) {$n$#$#$#$#$#" % + def = "function $#($#) {$n$#$#$#$#$#" % [ name, header, optionalLine(p.globals), @@ -2551,7 +2391,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 @@ -2559,17 +2399,12 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = proc genStmt(p: PProc, n: PNode) = var r: TCompRes gen(p, n, r) - if r.res != "": lineF(p, "$#;$n", [r.res]) + if r.res != nil: lineF(p, "$#;$n", [r.res]) proc genPragma(p: PProc, n: PNode) = - 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 = "" + var s: Rope = nil for i in 0.. which cannot be captured as it would violate memory" & - " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." & - " Consider using a which can be captured.") % + " safety, declared here: $3; using '-d:nimWorkaround14447' helps in some cases") % [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>" % @@ -441,7 +436,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if innerProc: if s.isIterator: c.somethingToDo = true if not c.processed.containsOrIncl(s.id): - let body = transformBody(c.graph, c.idgen, s, useCache) + let body = transformBody(c.graph, c.idgen, s, cache = true) detectCapturedVars(body, s, c) let ow = s.skipGenericOwner if ow == owner: @@ -456,9 +451,9 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if s.name.id == getIdent(c.graph.cache, ":state").id: obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item) else: - discard addField(obj, s, c.graph.cache, c.idgen) + addField(obj, s, c.graph.cache, c.idgen) # direct or indirect dependency: - elif (innerProc and not s.isIterator and s.typ.callConv == ccClosure) or interestingVar(s): + elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s): discard """ proc outer() = var x: int @@ -478,7 +473,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id): let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr}) #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) - discard addField(obj, s, c.graph.cache, c.idgen) + addField(obj, s, c.graph.cache, c.idgen) # create required upFields: var w = owner.skipGenericOwner if isInnerProc(w) or owner.isIterator: @@ -502,8 +497,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = w = up of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, - nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, - nkTypeOfExpr, nkMixinStmt, nkBindStmt: + nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, nkTypeOfExpr: discard of nkLambdaKinds, nkIteratorDef: if n.typ != nil: @@ -604,7 +598,7 @@ proc rawClosureCreation(owner: PSym; addVar(v, unowned) # add 'new' statement: - result.add genCreateEnv(env) + result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env)) if optOwnedRefs in d.graph.config.globalOptions: let unowned = c.unownedEnvVars[owner.id] assert unowned != nil @@ -666,7 +660,7 @@ proc closureCreationForIter(iter: PNode; var vs = newNodeI(nkVarSection, iter.info) addVar(vs, vnode) result.add(vs) - result.add genCreateEnv(vnode) + result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode)) createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner) let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName)) @@ -737,7 +731,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass; # echo renderTree(s.getBody, {renderIds}) let oldInContainer = c.inContainer c.inContainer = 0 - var body = transformBody(d.graph, d.idgen, s, dontUseCache) + var body = transformBody(d.graph, d.idgen, s, cache = false) body = liftCapturedVars(body, s, d, c) if c.envVars.getOrDefault(s.id).isNil: s.transformedBody = body @@ -758,7 +752,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass; result = accessViaEnvVar(n, owner, d, c) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom, nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef, - nkMacroDef, nkFuncDef, nkMixinStmt, nkBindStmt: + nkMacroDef, nkFuncDef: discard of nkClosure: if n[1].kind == nkNilLit: @@ -782,7 +776,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass; n[1] = liftCapturedVars(n[1], owner, d, c) if n[1].kind == nkClosure: result = n[1] of nkReturnStmt: - if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: + if n[0].kind in {nkAsgn, nkFastAsgn}: # we have a `result = result` expression produced by the closure # transform, let's not touch the LHS in order to make the lifting pass # correct when `result` is lifted @@ -859,7 +853,7 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType; fn.typ.callConv = oldCC proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool; - idgen: IdGenerator, force: bool): PNode = + idgen: IdGenerator): PNode = # XXX backend == backendJs does not suffice! The compiletime stuff needs # the transformation even when compiling to JS ... @@ -868,7 +862,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool; if body.kind == nkEmpty or ( g.config.backend == backendJs and not isCompileTime) or - (fn.skipGenericOwner.kind != skModule and not force): + fn.skipGenericOwner.kind != skModule: # ignore forward declaration: result = body @@ -951,7 +945,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym): addVar(v, newSymNode(env)) result.add(v) # add 'new' statement: - result.add genCreateEnv(env.newSymNode) + result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode)) createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner) elif op.kind == nkStmtListExpr: diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 6e8280e67..c603c16ff 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -510,12 +510,11 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = rememberSplit(splitComma) wrSpace em of openPars: - if tok.strongSpaceA and not em.endsInWhite and + if tok.strongSpaceA > 0 and not em.endsInWhite and (not em.wasExportMarker or tok.tokType == tkCurlyDotLe): wrSpace em wr(em, $tok.tokType, ltSomeParLe) - if tok.tokType != tkCurlyDotLe: - rememberSplit(splitParLe) + rememberSplit(splitParLe) of closedPars: wr(em, $tok.tokType, ltSomeParRi) of tkColonColon: @@ -528,7 +527,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = wr(em, $tok.tokType, ltOther) if not em.inquote: wrSpace(em) of tkOpr, tkDotDot: - if em.inquote or (((not tok.strongSpaceA) and tok.strongSpaceB == tsNone) and + if em.inquote or ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) and tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]): # bug #9504: remember to not spacify a keyword: lastTokWasTerse = true @@ -538,7 +537,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = if not em.endsInWhite: wrSpace(em) wr(em, tok.ident.s, ltOpr) template isUnary(tok): bool = - tok.strongSpaceB == tsNone and tok.strongSpaceA + tok.strongSpaceB == 0 and tok.strongSpaceA > 0 if not isUnary(tok): rememberSplit(splitBinary) @@ -551,15 +550,11 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = if not preventComment: emitComment(em, tok, dontIndent = false) of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit: - if not em.inquote: - let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB) - if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em) - em.lineSpan = countNewlines(lit) - if em.lineSpan > 0: calcCol(em, lit) - wr em, lit, ltLit - else: - if endsInAlpha(em): wrSpace(em) - wr em, tok.literal, ltLit + let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB) + if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em) + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + wr em, lit, ltLit of tkEof: discard else: let lit = if tok.ident != nil: tok.ident.s else: tok.literal diff --git a/compiler/lexer.nim b/compiler/lexer.nim index a62d40e54..b55dd3585 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -7,21 +7,18 @@ # distribution, for details about the copyright. # -# This lexer is handwritten for efficiency. I used an elegant buffering +# This scanner is handwritten for efficiency. I used an elegant buffering # scheme which I have not seen anywhere else: # We guarantee that a whole line is in the buffer. Thus only when scanning # the \n or \r character we have to check whether we need to read in the next # chunk. (\n or \r already need special handling for incrementing the line -# counter; choosing both \n and \r allows the lexer to properly read Unix, +# counter; choosing both \n and \r allows the scanner to properly read Unix, # DOS or Macintosh text files, even when it is not the native format. import hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, wordrecg, lineinfos, pathutils, parseutils -when defined(nimPreviewSlimSystem): - import std/[assertions, formatfloat] - const MaxLineLength* = 80 # lines longer than this lead to a warning numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'} @@ -29,7 +26,6 @@ const SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} OpChars*: set[char] = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', '|', '=', '%', '&', '$', '@', '~', ':'} - UnaryMinusWhitelist = {' ', '\t', '\n', '\r', ',', ';', '(', '[', '{'} # don't forget to update the 'highlite' module if these charsets should change @@ -55,27 +51,26 @@ type tkVar = "var", tkWhen = "when", tkWhile = "while", tkXor = "xor", tkYield = "yield", # end of keywords - tkIntLit = "tkIntLit", tkInt8Lit = "tkInt8Lit", tkInt16Lit = "tkInt16Lit", + tkIntLit = "tkIntLit", tkInt8Lit = "tkInt8Lit", tkInt16Lit = "tkInt16Lit", tkInt32Lit = "tkInt32Lit", tkInt64Lit = "tkInt64Lit", - tkUIntLit = "tkUIntLit", tkUInt8Lit = "tkUInt8Lit", tkUInt16Lit = "tkUInt16Lit", + tkUIntLit = "tkUIntLit", tkUInt8Lit = "tkUInt8Lit", tkUInt16Lit = "tkUInt16Lit", tkUInt32Lit = "tkUInt32Lit", tkUInt64Lit = "tkUInt64Lit", tkFloatLit = "tkFloatLit", tkFloat32Lit = "tkFloat32Lit", tkFloat64Lit = "tkFloat64Lit", tkFloat128Lit = "tkFloat128Lit", tkStrLit = "tkStrLit", tkRStrLit = "tkRStrLit", tkTripleStrLit = "tkTripleStrLit", - tkGStrLit = "tkGStrLit", tkGTripleStrLit = "tkGTripleStrLit", tkCharLit = "tkCharLit", - tkCustomLit = "tkCustomLit", - + tkGStrLit = "tkGStrLit", tkGTripleStrLit = "tkGTripleStrLit", tkCharLit = "tkCharLit", + tkParLe = "(", tkParRi = ")", tkBracketLe = "[", tkBracketRi = "]", tkCurlyLe = "{", tkCurlyRi = "}", tkBracketDotLe = "[.", tkBracketDotRi = ".]", tkCurlyDotLe = "{.", tkCurlyDotRi = ".}", tkParDotLe = "(.", tkParDotRi = ".)", tkComma = ",", tkSemiColon = ";", - tkColon = ":", tkColonColon = "::", tkEquals = "=", + tkColon = ":", tkColonColon = "::", tkEquals = "=", tkDot = ".", tkDotDot = "..", tkBracketLeColon = "[:", tkOpr, tkComment, tkAccent = "`", # these are fake tokens used by renderer.nim - tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, tkHideableStart, tkHideableEnd + tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr TokTypes* = set[TokType] @@ -93,22 +88,19 @@ type # so that it is the correct default value base2, base8, base16 - TokenSpacing* = enum - tsNone, tsTrailing, tsEof - - Token* = object # a Nim token - tokType*: TokType # the type of the token - indent*: int # the indentation; != -1 if the token has been - # preceded with indentation - ident*: PIdent # the parsed identifier - iNumber*: BiggestInt # the parsed integer literal - fNumber*: BiggestFloat # the parsed floating point literal - base*: NumericalBase # the numerical base; only valid for int - # or float literals - strongSpaceA*: bool # leading spaces of an operator - strongSpaceB*: TokenSpacing # trailing spaces of an operator - literal*: string # the parsed (string) literal; and - # documentation comments are here too + Token* = object # a Nim token + tokType*: TokType # the type of the token + indent*: int # the indentation; != -1 if the token has been + # preceded with indentation + ident*: PIdent # the parsed identifier + iNumber*: BiggestInt # the parsed integer literal + fNumber*: BiggestFloat # the parsed floating point literal + base*: NumericalBase # the numerical base; only valid for int + # or float literals + strongSpaceA*: int8 # leading spaces of an operator + strongSpaceB*: int8 # trailing spaces of an operator + literal*: string # the parsed (string) literal; and + # documentation comments are here too line*, col*: int when defined(nimpretty): offsetA*, offsetB*: int # used for pretty printing so that literals @@ -127,8 +119,6 @@ type cache*: IdentCache when defined(nimsuggest): previousToken: TLineInfo - tokenEnd*: TLineInfo - previousTokenEnd*: TLineInfo config*: ConfigRef proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} = @@ -171,14 +161,13 @@ proc prettyTok*(tok: Token): string = else: $tok proc printTok*(conf: ConfigRef; tok: Token) = - # xxx factor with toLocation msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & $tok.tokType & " " & $tok) proc initToken*(L: var Token) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 - L.strongSpaceA = false + L.strongSpaceA = 0 L.literal = "" L.fNumber = 0.0 L.base = base10 @@ -191,7 +180,7 @@ proc fillToken(L: var Token) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 - L.strongSpaceA = false + L.strongSpaceA = 0 setLen(L.literal, 0) L.fNumber = 0.0 L.base = base10 @@ -323,7 +312,8 @@ proc getNumber(L: var Lexer, result: var Token) = proc lexMessageLitNum(L: var Lexer, msg: string, startpos: int, msgKind = errGenerated) = # Used to get slightly human friendlier err messages. - const literalishChars = {'A'..'Z', 'a'..'z', '0'..'9', '_', '.', '\''} + const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O', + 'c', 'C', 'b', 'B', '_', '.', '\'', 'd', 'i', 'u'} var msgPos = L.bufpos var t: Token t.literal = "" @@ -335,14 +325,15 @@ proc getNumber(L: var Lexer, result: var Token) = t.literal.add(L.buf[L.bufpos]) inc(L.bufpos) matchChars(L, t, literalishChars) - if L.buf[L.bufpos] in literalishChars: - t.literal.add(L.buf[L.bufpos]) + if L.buf[L.bufpos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}: inc(L.bufpos) + t.literal.add(L.buf[L.bufpos]) matchChars(L, t, {'0'..'9'}) L.bufpos = msgPos lexMessage(L, msgKind, msg % t.literal) var + startpos, endpos: int xi: BiggestInt isBase10 = true numDigits = 0 @@ -354,17 +345,8 @@ proc getNumber(L: var Lexer, result: var Token) = result.tokType = tkIntLit # int literal until we know better result.literal = "" result.base = base10 - tokenBegin(result, L.bufpos) - - var isPositive = true - if L.buf[L.bufpos] == '-': - eatChar(L, result) - isPositive = false - - let startpos = L.bufpos - - template setNumber(field, value) = - field = (if isPositive: value else: -value) + startpos = L.bufpos + tokenBegin(result, startpos) # First stage: find out base, make verifications, build token literal string # {'c', 'C'} is added for deprecation reasons to provide a clear error message @@ -404,184 +386,200 @@ proc getNumber(L: var Lexer, result: var Token) = discard matchUnderscoreChars(L, result, {'0'..'9'}) if L.buf[L.bufpos] in {'e', 'E'}: result.tokType = tkFloatLit - eatChar(L, result) + eatChar(L, result, 'e') if L.buf[L.bufpos] in {'+', '-'}: eatChar(L, result) discard matchUnderscoreChars(L, result, {'0'..'9'}) - let endpos = L.bufpos + endpos = L.bufpos # Second stage, find out if there's a datatype suffix and handle it var postPos = endpos - if L.buf[postPos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}: - let errPos = postPos - var customLitPossible = false if L.buf[postPos] == '\'': inc(postPos) - customLitPossible = true - if L.buf[postPos] in SymChars: - var suffix = newStringOfCap(10) - while true: - suffix.add L.buf[postPos] - inc postPos - if L.buf[postPos] notin SymChars+{'_'}: break - let suffixAsLower = suffix.toLowerAscii - case suffixAsLower - of "f", "f32": result.tokType = tkFloat32Lit - of "d", "f64": result.tokType = tkFloat64Lit - of "f128": result.tokType = tkFloat128Lit - of "i8": result.tokType = tkInt8Lit - of "i16": result.tokType = tkInt16Lit - of "i32": result.tokType = tkInt32Lit - of "i64": result.tokType = tkInt64Lit - of "u": result.tokType = tkUIntLit - of "u8": result.tokType = tkUInt8Lit - of "u16": result.tokType = tkUInt16Lit - of "u32": result.tokType = tkUInt32Lit - of "u64": result.tokType = tkUInt64Lit - elif customLitPossible: - # remember the position of the `'` so that the parser doesn't - # have to reparse the custom literal: - result.iNumber = len(result.literal) - result.literal.add '\'' - result.literal.add suffix - result.tokType = tkCustomLit + case L.buf[postPos] + of 'f', 'F': + inc(postPos) + if (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'): + result.tokType = tkFloat32Lit + inc(postPos, 2) + elif (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): + result.tokType = tkFloat64Lit + inc(postPos, 2) + elif (L.buf[postPos] == '1') and + (L.buf[postPos + 1] == '2') and + (L.buf[postPos + 2] == '8'): + result.tokType = tkFloat128Lit + inc(postPos, 3) + else: # "f" alone defaults to float32 + result.tokType = tkFloat32Lit + of 'd', 'D': # ad hoc convenience shortcut for f64 + inc(postPos) + result.tokType = tkFloat64Lit + of 'i', 'I': + inc(postPos) + if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): + result.tokType = tkInt64Lit + inc(postPos, 2) + elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'): + result.tokType = tkInt32Lit + inc(postPos, 2) + elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'): + result.tokType = tkInt16Lit + inc(postPos, 2) + elif (L.buf[postPos] == '8'): + result.tokType = tkInt8Lit + inc(postPos) else: - lexMessageLitNum(L, "invalid number suffix: '$1'", errPos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) + of 'u', 'U': + inc(postPos) + if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): + result.tokType = tkUInt64Lit + inc(postPos, 2) + elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'): + result.tokType = tkUInt32Lit + inc(postPos, 2) + elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'): + result.tokType = tkUInt16Lit + inc(postPos, 2) + elif (L.buf[postPos] == '8'): + result.tokType = tkUInt8Lit + inc(postPos) + else: + result.tokType = tkUIntLit else: - lexMessageLitNum(L, "invalid number suffix: '$1'", errPos) + lexMessageLitNum(L, "invalid number: '$1'", startpos) # Is there still a literalish char awaiting? Then it's an error! if L.buf[postPos] in literalishChars or (L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}): lexMessageLitNum(L, "invalid number: '$1'", startpos) - if result.tokType != tkCustomLit: - # Third stage, extract actual number - L.bufpos = startpos # restore position - var pos = startpos - try: - if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars): - inc(pos, 2) - xi = 0 # it is a base prefix + # Third stage, extract actual number + L.bufpos = startpos # restore position + var pos: int = startpos + try: + if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars): + inc(pos, 2) + xi = 0 # it is a base prefix - case L.buf[pos - 1] - of 'b', 'B': - result.base = base2 - while pos < endpos: - if L.buf[pos] != '_': - xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) + case L.buf[pos - 1] + of 'b', 'B': + result.base = base2 + while pos < endpos: + if L.buf[pos] != '_': + xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) + inc(pos) + # 'c', 'C' is deprecated + of 'o', 'c', 'C': + result.base = base8 + while pos < endpos: + if L.buf[pos] != '_': + xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0')) + inc(pos) + of 'x', 'X': + result.base = base16 + while pos < endpos: + case L.buf[pos] + of '_': inc(pos) - # 'c', 'C' is deprecated (a warning is issued elsewhere) - of 'o', 'c', 'C': - result.base = base8 - while pos < endpos: - if L.buf[pos] != '_': - xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0')) + of '0'..'9': + xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) inc(pos) - of 'x', 'X': - result.base = base16 - while pos < endpos: - case L.buf[pos] - of '_': - inc(pos) - of '0'..'9': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - of 'a'..'f': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) - inc(pos) - of 'A'..'F': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) - inc(pos) - else: - break - else: - internalError(L.config, getLineInfo(L), "getNumber") - - case result.tokType - of tkIntLit, tkInt64Lit: setNumber result.iNumber, xi - of tkInt8Lit: setNumber result.iNumber, ashr(xi shl 56, 56) - of tkInt16Lit: setNumber result.iNumber, ashr(xi shl 48, 48) - of tkInt32Lit: setNumber result.iNumber, ashr(xi shl 32, 32) - of tkUIntLit, tkUInt64Lit: setNumber result.iNumber, xi - of tkUInt8Lit: setNumber result.iNumber, xi and 0xff - of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff - of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff - of tkFloat32Lit: - 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[ptr float64](addr(xi)))[] - else: internalError(L.config, getLineInfo(L), "getNumber") - - # Bounds checks. Non decimal literals are allowed to overflow the range of - # the datatype as long as their pattern don't overflow _bitwise_, hence - # below checks of signed sizes against uint*.high is deliberate: - # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK) - if result.tokType notin floatTypes: - let outOfRange = - case result.tokType - of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi - of tkInt8Lit: (xi > BiggestInt(uint8.high)) - of tkInt16Lit: (xi > BiggestInt(uint16.high)) - of tkInt32Lit: (xi > BiggestInt(uint32.high)) - else: false - - if outOfRange: - #echo "out of range num: ", result.iNumber, " vs ", xi - lexMessageLitNum(L, "number out of range: '$1'", startpos) - + of 'a'..'f': + xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) + inc(pos) + of 'A'..'F': + xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) + inc(pos) + else: + break else: - case result.tokType - of floatTypes: - result.fNumber = parseFloat(result.literal) - of tkUInt64Lit, tkUIntLit: - var iNumber: uint64 - var len: int - try: - len = parseBiggestUInt(result.literal, iNumber) - except ValueError: - raise newException(OverflowDefect, "number out of range: " & result.literal) - if len != result.literal.len: - raise newException(ValueError, "invalid integer: " & result.literal) - result.iNumber = cast[int64](iNumber) - else: - var iNumber: int64 - var len: int - try: - len = parseBiggestInt(result.literal, iNumber) - except ValueError: - raise newException(OverflowDefect, "number out of range: " & result.literal) - if len != result.literal.len: - raise newException(ValueError, "invalid integer: " & result.literal) - result.iNumber = iNumber + internalError(L.config, getLineInfo(L), "getNumber") - # Explicit bounds checks. - let outOfRange = - case result.tokType - of tkInt8Lit: result.iNumber > int8.high or result.iNumber < int8.low - of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high) or result.iNumber < 0 - of tkInt16Lit: result.iNumber > int16.high or result.iNumber < int16.low - of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high) or result.iNumber < 0 - of tkInt32Lit: result.iNumber > int32.high or result.iNumber < int32.low - of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high) or result.iNumber < 0 - else: false + case result.tokType + of tkIntLit, tkInt64Lit: result.iNumber = xi + of tkInt8Lit: result.iNumber = ashr(xi shl 56, 56) + of tkInt16Lit: result.iNumber = ashr(xi shl 48, 48) + of tkInt32Lit: result.iNumber = ashr(xi shl 32, 32) + of tkUIntLit, tkUInt64Lit: result.iNumber = xi + of tkUInt8Lit: result.iNumber = xi and 0xff + of tkUInt16Lit: result.iNumber = xi and 0xffff + of tkUInt32Lit: result.iNumber = xi and 0xffffffff + of tkFloat32Lit: + result.fNumber = (cast[PFloat32](addr(xi)))[] + # note: this code is endian neutral! + # XXX: Test this on big endian machine! + of tkFloat64Lit, tkFloatLit: + result.fNumber = (cast[PFloat64](addr(xi)))[] + else: internalError(L.config, getLineInfo(L), "getNumber") + + # Bounds checks. Non decimal literals are allowed to overflow the range of + # the datatype as long as their pattern don't overflow _bitwise_, hence + # below checks of signed sizes against uint*.high is deliberate: + # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK) + if result.tokType notin floatTypes: + let outOfRange = case result.tokType: + of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi + of tkInt8Lit: (xi > BiggestInt(uint8.high)) + of tkInt16Lit: (xi > BiggestInt(uint16.high)) + of tkInt32Lit: (xi > BiggestInt(uint32.high)) + else: false if outOfRange: + #echo "out of range num: ", result.iNumber, " vs ", xi lexMessageLitNum(L, "number out of range: '$1'", startpos) - # Promote int literal to int64? Not always necessary, but more consistent - if result.tokType == tkIntLit: - if result.iNumber > high(int32) or result.iNumber < low(int32): - result.tokType = tkInt64Lit + else: + case result.tokType + of floatTypes: + result.fNumber = parseFloat(result.literal) + of tkUInt64Lit, tkUIntLit: + var iNumber: uint64 + var len: int + try: + len = parseBiggestUInt(result.literal, iNumber) + except ValueError: + raise newException(OverflowDefect, "number out of range: " & $result.literal) + if len != result.literal.len: + raise newException(ValueError, "invalid integer: " & $result.literal) + result.iNumber = cast[int64](iNumber) + else: + var iNumber: int64 + var len: int + try: + len = parseBiggestInt(result.literal, iNumber) + except ValueError: + raise newException(OverflowDefect, "number out of range: " & $result.literal) + if len != result.literal.len: + raise newException(ValueError, "invalid integer: " & $result.literal) + result.iNumber = iNumber - except ValueError: - lexMessageLitNum(L, "invalid number: '$1'", startpos) - except OverflowDefect, RangeDefect: - lexMessageLitNum(L, "number out of range: '$1'", startpos) + # Explicit bounds checks. Only T.high needs to be considered + # since result.iNumber can't be negative. + let outOfRange = + case result.tokType + of tkInt8Lit: result.iNumber > int8.high + of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high) + of tkInt16Lit: result.iNumber > int16.high + of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high) + of tkInt32Lit: result.iNumber > int32.high + of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high) + else: false + + if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos) + + # Promote int literal to int64? Not always necessary, but more consistent + if result.tokType == tkIntLit: + if result.iNumber > high(int32): + result.tokType = tkInt64Lit + + except ValueError: + lexMessageLitNum(L, "invalid number: '$1'", startpos) + except OverflowDefect, RangeDefect: + lexMessageLitNum(L, "number out of range: '$1'", startpos) tokenEnd(result, postPos-1) L.bufpos = postPos @@ -822,9 +820,8 @@ proc getString(L: var Lexer, tok: var Token, mode: StringMode) = inc(pos) L.bufpos = pos -proc getCharacter(L: var Lexer; tok: var Token) = +proc getCharacter(L: var Lexer, tok: var Token) = tokenBegin(tok, L.bufpos) - let startPos = L.bufpos inc(L.bufpos) # skip ' var c = L.buf[L.bufpos] case c @@ -835,59 +832,10 @@ proc getCharacter(L: var Lexer; tok: var Token) = else: tok.literal = $c inc(L.bufpos) - if L.buf[L.bufpos] == '\'': - tokenEndIgnore(tok, L.bufpos) - inc(L.bufpos) # skip ' - else: - if startPos > 0 and L.buf[startPos-1] == '`': - tok.literal = "'" - L.bufpos = startPos+1 - else: - lexMessage(L, errGenerated, "missing closing ' for character literal") - tokenEndIgnore(tok, L.bufpos) - -const - UnicodeOperatorStartChars = {'\226', '\194', '\195'} - # the allowed unicode characters ("∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔") - # all start with one of these. - -type - UnicodeOprPred = enum - Mul, Add - -proc unicodeOprLen(buf: cstring; pos: int): (int8, UnicodeOprPred) = - template m(len): untyped = (int8(len), Mul) - template a(len): untyped = (int8(len), Add) - result = 0.m - case buf[pos] - of '\226': - if buf[pos+1] == '\136': - if buf[pos+2] == '\152': result = 3.m # ∘ - elif buf[pos+2] == '\153': result = 3.m # ∙ - elif buf[pos+2] == '\167': result = 3.m # ∧ - elif buf[pos+2] == '\168': result = 3.a # ∨ - elif buf[pos+2] == '\169': result = 3.m # ∩ - elif buf[pos+2] == '\170': result = 3.a # ∪ - elif buf[pos+1] == '\138': - if buf[pos+2] == '\147': result = 3.m # ⊓ - elif buf[pos+2] == '\148': result = 3.a # ⊔ - elif buf[pos+2] == '\149': result = 3.a # ⊕ - elif buf[pos+2] == '\150': result = 3.a # ⊖ - elif buf[pos+2] == '\151': result = 3.m # ⊗ - elif buf[pos+2] == '\152': result = 3.m # ⊘ - elif buf[pos+2] == '\153': result = 3.m # ⊙ - elif buf[pos+2] == '\155': result = 3.m # ⊛ - elif buf[pos+2] == '\158': result = 3.a # ⊞ - elif buf[pos+2] == '\159': result = 3.a # ⊟ - elif buf[pos+2] == '\160': result = 3.m # ⊠ - elif buf[pos+2] == '\161': result = 3.m # ⊡ - elif buf[pos+1] == '\152' and buf[pos+2] == '\133': result = 3.m # ★ - of '\194': - if buf[pos+1] == '\177': result = 2.a # ± - of '\195': - if buf[pos+1] == '\151': result = 2.m # × - else: - discard + if L.buf[L.bufpos] != '\'': + lexMessage(L, errGenerated, "missing closing ' for character literal") + tokenEndIgnore(tok, L.bufpos) + inc(L.bufpos) # skip ' proc getSymbol(L: var Lexer, tok: var Token) = var h: Hash = 0 @@ -897,7 +845,7 @@ proc getSymbol(L: var Lexer, tok: var Token) = while true: var c = L.buf[pos] case c - of 'a'..'z', '0'..'9': + of 'a'..'z', '0'..'9', '\x80'..'\xFF': h = h !& ord(c) inc(pos) of 'A'..'Z': @@ -911,16 +859,10 @@ proc getSymbol(L: var Lexer, tok: var Token) = break inc(pos) suspicious = true - of '\x80'..'\xFF': - if c in UnicodeOperatorStartChars and unicodeOprLen(L.buf, pos)[0] != 0: - break - else: - h = h !& ord(c) - inc(pos) else: break tokenEnd(tok, pos-1) h = !$h - tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h) + tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or (tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): tok.tokType = tkSymbol @@ -934,7 +876,7 @@ proc getSymbol(L: var Lexer, tok: var Token) = proc endOperator(L: var Lexer, tok: var Token, pos: int, hash: Hash) {.inline.} = var h = !$hash - tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h) + tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr else: tok.tokType = TokType(tok.ident.id - oprLow + ord(tkColon)) L.bufpos = pos @@ -945,63 +887,42 @@ proc getOperator(L: var Lexer, tok: var Token) = var h: Hash = 0 while true: var c = L.buf[pos] - if c in OpChars: - h = h !& ord(c) - inc(pos) - 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 0 + tok.ident.s[^2] in {'-', '~', '='}: return 1 template considerAsgn(value: untyped) = result = if tok.ident.s[^1] == '=': 1 else: value case relevantChar of '$', '^': considerAsgn(10) - of '*', '%', '/', '\\': considerAsgn(MulPred) + of '*', '%', '/', '\\': considerAsgn(9) of '~': result = 8 - of '+', '-', '|': considerAsgn(PlusPred) + of '+', '-', '|': considerAsgn(8) of '&': considerAsgn(7) of '=', '<', '>', '!': result = 5 of '.': considerAsgn(6) of '?': result = 2 - of UnicodeOperatorStartChars: - if tok.ident.s[^1] == '=': - result = 1 - else: - let (len, pred) = unicodeOprLen(cstring(tok.ident.s), 0) - if len != 0: - result = if pred == Mul: MulPred else: PlusPred - else: - result = 2 else: considerAsgn(2) of tkDiv, tkMod, tkShl, tkShr: result = 9 of tkDotDot: result = 6 @@ -1034,10 +955,8 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int; # detect the amount of indentation: if isDoc: toStrip = getColNumber(L, pos) - while L.buf[pos] == ' ': - inc pos - inc toStrip - while L.buf[pos] in {CR, LF}: # skip blank lines + while L.buf[pos] == ' ': inc pos + if L.buf[pos] in {CR, LF}: pos = handleCRLF(L, pos) toStrip = 0 while L.buf[pos] == ' ': @@ -1109,17 +1028,11 @@ proc scanComment(L: var Lexer, tok: var Token) = inc(pos, 2) var toStrip = 0 - var stripInit = false + while L.buf[pos] == ' ': + inc pos + inc toStrip while true: - if not stripInit: # find baseline indentation inside comment - while L.buf[pos] == ' ': - inc pos - inc toStrip - if L.buf[pos] in {CR, LF}: # don't set toStrip in blank comment lines - toStrip = 0 - else: # found first non-whitespace character - stripInit = true var lastBackslash = -1 while L.buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: if L.buf[pos] == '\\': lastBackslash = pos+1 @@ -1135,12 +1048,11 @@ proc scanComment(L: var Lexer, tok: var Token) = if L.buf[pos] == '#' and L.buf[pos+1] == '#': tok.literal.add "\n" inc(pos, 2) - if stripInit: - var c = toStrip - while L.buf[pos] == ' ' and c > 0: - inc pos - dec c - inc tok.iNumber + var c = toStrip + while L.buf[pos] == ' ' and c > 0: + inc pos + dec c + inc tok.iNumber else: if L.buf[pos] > ' ': L.indentAhead = indent @@ -1153,7 +1065,7 @@ proc scanComment(L: var Lexer, tok: var Token) = proc skip(L: var Lexer, tok: var Token) = var pos = L.bufpos tokenBegin(tok, pos) - tok.strongSpaceA = false + tok.strongSpaceA = 0 when defined(nimpretty): var hasComment = false var commentIndent = L.currLineIndent @@ -1164,8 +1076,7 @@ proc skip(L: var Lexer, tok: var Token) = case L.buf[pos] of ' ': inc(pos) - if not tok.strongSpaceA: - tok.strongSpaceA = true + inc(tok.strongSpaceA) of '\t': if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead") inc(pos) @@ -1187,7 +1098,7 @@ proc skip(L: var Lexer, tok: var Token) = pos = L.bufpos else: break - tok.strongSpaceA = false + tok.strongSpaceA = 0 when defined(nimpretty): if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'): @@ -1226,10 +1137,6 @@ proc skip(L: var Lexer, tok: var Token) = proc rawGetTok*(L: var Lexer, tok: var Token) = template atTokenEnd() {.dirty.} = when defined(nimsuggest): - L.previousTokenEnd.line = L.tokenEnd.line - L.previousTokenEnd.col = L.tokenEnd.col - L.tokenEnd.line = tok.line.uint16 - L.tokenEnd.col = getColNumber(L, L.bufpos).int16 # we attach the cursor to the last *strong* token if tok.tokType notin weakTokens: L.previousToken.line = tok.line.uint16 @@ -1250,15 +1157,10 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = var c = L.buf[L.bufpos] tok.line = L.lineNumber tok.col = getColNumber(L, L.bufpos) - if c in SymStartChars - {'r', 'R'} - UnicodeOperatorStartChars: + if c in SymStartChars - {'r', 'R'}: getSymbol(L, tok) else: case c - of UnicodeOperatorStartChars: - if unicodeOprLen(L.buf, L.bufpos)[0] != 0: - getOperator(L, tok) - else: - getSymbol(L, tok) of '#': scanComment(L, tok) of '*': @@ -1366,28 +1268,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 - unicodeOprLen(L.buf, L.bufpos)[0] != 0: - discard - else: - lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") - of '-': - if L.buf[L.bufpos+1] in {'0'..'9'} and - (L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist): - # x)-23 # binary minus - # ,-23 # unary minus - # \n-78 # unary minus? Yes. - # =-3 # parsed as `=-` anyway - getNumber(L, tok) - let c = L.buf[L.bufpos] - if c in SymChars+{'_'}: - if c in UnicodeOperatorStartChars and - unicodeOprLen(L.buf, L.bufpos)[0] != 0: - discard - else: - lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") - else: - getOperator(L, tok) + lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") else: if c in OpChars: getOperator(L, tok) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 467f015d8..5ffa25e53 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -8,16 +8,13 @@ # ## This module implements lifting for type-bound operations -## (``=sink``, ``=copy``, ``=destroy``, ``=deepCopy``). +## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``). import modulegraphs, lineinfos, idents, ast, renderer, semdata, - sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils + sighashes, lowerings, options, types, msgs, magicsys, tables from trees import isCaseObj -when defined(nimPreviewSlimSystem): - import std/assertions - type TLiftCtx = object g: ModuleGraph @@ -32,10 +29,6 @@ type c: PContext # c can be nil, then we are called from lambdalifting! idgen: IdGenerator -template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor) -template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn) -template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink) - proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo; idgen: IdGenerator): PSym @@ -49,9 +42,8 @@ proc at(a, i: PNode, elemType: PType): PNode = result[1] = i result.typ = elemType -proc destructorOverriden(g: ModuleGraph; t: PType): bool = - let op = getAttachedOp(g, t, attachedDestructor) - op != nil and sfOverriden in op.flags +proc destructorOverriden(t: PType): bool = + t.attachedOps[attachedDestructor] != nil and sfOverriden in t.attachedOps[attachedDestructor].flags proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = for i in 0.. 0: - addIncStmt(c, whileLoop[1], i) - body.add whileLoop - else: - body.sons.setLen counterIdx + addIncStmt(c, whileLoop[1], i) + body.add whileLoop proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -532,7 +476,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add setLenSeqCall(c, t, x, y) forallElements(c, t, body, x, y) of attachedSink: - let moveCall = genBuiltin(c, mMove, "move", x) + let moveCall = genBuiltin(c.g, mMove, "move", x) moveCall.add y doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) @@ -540,12 +484,13 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDestructor: # destroy all elements: forallElements(c, t, body, x, y) - body.add genBuiltin(c, mDestroy, "destroy", x) + body.add genBuiltin(c.g, mDestroy, "destroy", x) of attachedTrace: - if canFormAcycle(t.elemType): - # follow all elements: - forallElements(c, t, body, x, y) - of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + # follow all elements: + forallElements(c, t, body, x, y) + of attachedDispose: + forallElements(c, t, body, x, y) + body.add genBuiltin(c.g, mDestroy, "destroy", x) proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = createTypeBoundOps(c.g, c.c, t, body.info, c.idgen) @@ -553,7 +498,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # operation here: var t = t if t.assignment == nil or t.destructor == nil: - let h = sighashes.hashType(t,c.g.config, {CoType, CoConsiderOwned, CoDistinct}) + let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct}) let canon = c.g.canonTypes.getOrDefault(h) if canon != nil: t = canon @@ -565,7 +510,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newHookCall(c, t.assignment, x, y) of attachedSink: # we always inline the move for better performance: - let moveCall = genBuiltin(c, mMove, "move", x) + let moveCall = genBuiltin(c.g, mMove, "move", x) moveCall.add y doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) @@ -578,28 +523,28 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = doAssert t.destructor != nil body.add destructorCall(c, t.destructor, x) of attachedTrace: - if t.kind != tyString and canFormAcycle(t.elemType): - let op = getAttachedOp(c.g, t, c.kind) - if op == nil: - return # protect from recursion - body.add newHookCall(c, op, x, y) - of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + if t.attachedOps[c.kind] == nil: + return # protect from recursion + body.add newHookCall(c, t.attachedOps[c.kind], x, y) + of attachedDispose: + if t.attachedOps[c.kind] == nil: + return # protect from recursion + body.add newHookCall(c, t.attachedOps[c.kind], x, nil) proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind of attachedAsgn, attachedDeepCopy: body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y) of attachedSink: - let moveCall = genBuiltin(c, mMove, "move", x) + let moveCall = genBuiltin(c.g, mMove, "move", x) moveCall.add y doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) body.add moveCall - of attachedDestructor: - body.add genBuiltin(c, mDestroy, "destroy", x) + of attachedDestructor, attachedDispose: + body.add genBuiltin(c.g, mDestroy, "destroy", x) of attachedTrace: discard "strings are atomic and have no inner elements that are to trace" - of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc cyclicType*(t: PType): bool = case t.kind @@ -633,11 +578,6 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen) let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType) - let isInheritableAcyclicRef = c.g.config.selectedGC == gcOrc and - (not isPureObject(elemType)) and - tfAcyclic in skipTypes(elemType, abstractInst+{tyOwned}-{tyTypeDesc}).flags - # dynamic Acyclic refs need to use dyn decRef - let tmp = if isCyclic and c.kind in {attachedAsgn, attachedSink}: declareTempOf(c, body, x) @@ -646,7 +586,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if isFinal(elemType): addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr)) - var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) + var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) alignOf.typ = getSysType(c.g, c.info, tyInt) actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf) else: @@ -656,13 +596,11 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var cond: PNode if isCyclic: if isFinal(elemType): - let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) + let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) typInfo.typ = getSysType(c.g, c.info, tyPointer) cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo) else: cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp) - elif isInheritableAcyclicRef: - cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x) else: cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x) cond.typ = getSysType(c.g, x.info, tyBool) @@ -689,22 +627,26 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, cond, actions) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: - if isCyclic: - if isFinal(elemType): - let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) - typInfo.typ = getSysType(c.g, c.info, tyPointer) - body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y) - else: - # If the ref is polymorphic we have to account for this - body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y) - #echo "can follow ", elemType, " static ", isFinal(elemType) - of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) - + if isFinal(elemType): + let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) + typInfo.typ = getSysType(c.g, c.info, tyPointer) + body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y) + else: + # If the ref is polymorphic we have to account for this + body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y) + of attachedDispose: + # this is crucial! dispose is like =destroy but we don't follow refs + # as that is dealt within the cycle collector. + when false: + let cond = copyTree(x) + cond.typ = getSysType(c.g, x.info, tyBool) + actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x) + body.add genIf(c, cond, actions) proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = ## Closures are really like refs except they always use a virtual destructor ## and we need to do the refcounting only on the ref field which we call 'xenv': - let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x) + let xenv = genBuiltin(c.g, mAccessEnv, "accessEnv", x) xenv.typ = getSysType(c.g, c.info, tyPointer) let isCyclic = c.g.config.selectedGC == gcOrc @@ -732,7 +674,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, cond, actions) body.add newAsgnStmt(x, y) of attachedAsgn: - let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y) + let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y) yenv.typ = getSysType(c.g, c.info, tyPointer) if isCyclic: body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c))) @@ -748,7 +690,14 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y) - of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDispose: + # this is crucial! dispose is like =destroy but we don't follow refs + # as that is dealt within the cycle collector. + when false: + let cond = copyTree(xenv) + cond.typ = getSysType(c.g, xenv.info, tyBool) + actions.add callCodegenProc(c.g, "nimRawDispose", c.info, xenv) + body.add genIf(c, cond, actions) proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -772,19 +721,18 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = else: body.sons.insert(des, 0) of attachedDeepCopy: assert(false, "cannot happen") - of attachedTrace: discard - of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedTrace, attachedDispose: discard proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var actions = newNodeI(nkStmtList, c.info) let elemType = t.lastSon #fillBody(c, elemType, actions, genDeref(x), genDeref(y)) - #var disposeCall = genBuiltin(c, mDispose, "dispose", x) + #var disposeCall = genBuiltin(c.g, mDispose, "dispose", x) if isFinal(elemType): addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr)) - var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) + var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) alignOf.typ = getSysType(c.g, c.info, tyInt) actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf) else: @@ -798,8 +746,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDestructor: body.add genIf(c, x, actions) of attachedDeepCopy: assert(false, "cannot happen") - of attachedTrace: discard - of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedTrace, attachedDispose: discard proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.kind == attachedDeepCopy: @@ -807,12 +754,12 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # have to go through some indirection; we delegate this to the codegen: let call = newNodeI(nkCall, c.info, 2) call.typ = t - call[0] = newSymNode(createMagic(c.g, c.idgen, "deepCopy", mDeepCopy)) + call[0] = newSymNode(createMagic(c.g, "deepCopy", mDeepCopy)) call[1] = y body.add newAsgnStmt(x, call) elif (optOwnedRefs in c.g.config.globalOptions and optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}: - let xx = genBuiltin(c, mAccessEnv, "accessEnv", x) + let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x) xx.typ = getSysType(c.g, c.info, tyPointer) case c.kind of attachedSink: @@ -821,7 +768,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx)) body.add newAsgnStmt(x, y) of attachedAsgn: - let yy = genBuiltin(c, mAccessEnv, "accessEnv", y) + let yy = genBuiltin(c.g, mAccessEnv, "accessEnv", y) yy.typ = getSysType(c.g, c.info, tyPointer) body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy)) body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx)) @@ -833,11 +780,10 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = else: body.sons.insert(des, 0) of attachedDeepCopy: assert(false, "cannot happen") - of attachedTrace: discard - of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedTrace, attachedDispose: discard proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = - let xx = genBuiltin(c, mAccessEnv, "accessEnv", x) + let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x) xx.typ = getSysType(c.g, c.info, tyPointer) var actions = newNodeI(nkStmtList, c.info) #discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx)) @@ -849,13 +795,12 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDestructor: body.add genIf(c, xx, actions) of attachedDeepCopy: assert(false, "cannot happen") - of attachedTrace: discard - of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedTrace, attachedDispose: discard proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind of tyNone, tyEmpty, tyVoid: discard - of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCstring, + of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPtr, tyUncheckedArray, tyVar, tyLent: defaultOp(c, t, body, x, y) of tyRef: @@ -901,7 +846,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = discard considerUserDefinedOp(c, t, body, x, y) elif tfHasAsgn in t.flags: if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}: - body.add newSeqCall(c, x, y) + body.add newSeqCall(c.g, x, y) forallElements(c, t, body, x, y) else: defaultOp(c, t, body, x, y) @@ -938,17 +883,17 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = of tyOrdinal, tyRange, tyInferred, tyGenericInst, tyAlias, tySink: fillBody(c, lastSon(t), body, x, y) - of tyConcept, tyIterable: doAssert false + of tyOptDeprecated: doAssert false proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo; idgen: IdGenerator): PSym = assert typ.kind == tyDistinct let baseType = typ[0] - if getAttachedOp(g, baseType, kind) == nil: + if baseType.attachedOps[kind] == nil: discard produceSym(g, c, baseType, kind, info, idgen) - result = getAttachedOp(g, baseType, kind) - setAttachedOp(g, idgen.module, typ, kind, result) + typ.attachedOps[kind] = baseType.attachedOps[kind] + result = typ.attachedOps[kind] proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp; info: TLineInfo; idgen: IdGenerator): PSym = @@ -966,7 +911,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp result.typ = newProcType(info, nextTypeId(idgen), owner) result.typ.addParam dest - if kind notin {attachedDestructor, attachedWasMoved}: + if kind notin {attachedDestructor, attachedDispose}: result.typ.addParam src if kind == attachedAsgn and g.config.selectedGC == gcOrc and @@ -985,19 +930,13 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp incl result.flags, sfFromGeneric incl result.flags, sfGeneratedOp -proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) = - let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x) - let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y) - xx.typ = getSysType(c.g, c.info, tyPointer) - yy.typ = xx.typ - body.add newAsgnStmt(xx, yy) proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo; idgen: IdGenerator): PSym = if typ.kind == tyDistinct: return produceSymDistinctType(g, c, typ, kind, info, idgen) - result = getAttachedOp(g, typ, kind) + result = typ.attachedOps[kind] if result == nil: result = symPrototype(g, typ, typ.owner, kind, info, idgen) @@ -1006,16 +945,16 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; let dest = result.typ.n[1].sym let d = newDeref(newSymNode(dest)) - let src = if kind in {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) + let src = if kind in {attachedDestructor, attachedDispose}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) else: newSymNode(result.typ.n[2].sym) # register this operation already: - setAttachedOpPartial(g, idgen.module, typ, kind, result) + typ.attachedOps[kind] = result - if kind == attachedSink and destructorOverriden(g, typ): + if kind == attachedSink and destructorOverriden(typ): ## compiler can use a combination of `=destroy` and memCopy for sink op dest.flags.incl sfCursor - result.ast[bodyPos].add newOpCall(a, getAttachedOp(g, typ, attachedDestructor), d[0]) + result.ast[bodyPos].add newOpCall(a, typ.attachedOps[attachedDestructor], d[0]) result.ast[bodyPos].add newAsgnStmt(d, src) else: var tk: TTypeKind @@ -1030,12 +969,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; fillStrOp(a, typ, result.ast[bodyPos], d, src) else: fillBody(a, typ, result.ast[bodyPos], d, src) - if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy} and not lacksMTypeField(typ): - # bug #19205: Do not forget to also copy the hidden type field: - genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src) - if not a.canRaise: incl result.flags, sfNeverRaises - completePartialOp(g, idgen.module, typ, kind, result) proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym, @@ -1068,28 +1002,30 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id if n.kind in nkCallKinds: if n[0].kind == nkSym and n[0].sym.magic == mDestroy: let t = n[1].typ.skipTypes(abstractVar) - if getAttachedOp(g, t, attachedDestructor) == nil: + if t.destructor == nil: discard produceSym(g, c, t, attachedDestructor, info, idgen) - let op = getAttachedOp(g, t, attachedDestructor) - if op != nil: - if op.ast.isGenericRoutine: + if t.destructor != nil: + if t.destructor.ast[genericParamsPos].kind != nkEmpty: internalError(g.config, info, "resolved destructor is generic") - if op.magic == mDestroy: + if t.destructor.magic == mDestroy: internalError(g.config, info, "patching mDestroy with mDestroy?") - n[0] = newSymNode(op) + n[0] = newSymNode(t.destructor) for x in n: patchBody(g, c, x, info, idgen) -proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: IdGenerator; - info: TLineInfo) = - let op = getAttachedOp(g, t, kind) - if op != nil and op.ast != nil and op.ast.isGenericRoutine: +template inst(field, t, idgen) = + if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty: if t.typeInst != nil: - var a = TLiftCtx(info: info, g: g, kind: kind, c: c, idgen: idgen) - let opInst = instantiateGeneric(a, op, t, t.typeInst) - if opInst.ast != nil: - patchBody(g, c, opInst.ast, info, a.idgen) - setAttachedOp(g, idgen.module, t, kind, opInst) + var a: TLiftCtx + a.info = info + a.g = g + a.kind = k + a.c = c + a.idgen = idgen + + field = instantiateGeneric(a, field, t, t.typeInst) + if field.ast != nil: + patchBody(g, c, field.ast, info, a.idgen) else: localError(g.config, info, "unresolved generic parameter") @@ -1107,7 +1043,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink}) if isEmptyContainer(skipped) or skipped.kind == tyStatic: return - let h = sighashes.hashType(skipped, g.config, {CoType, CoConsiderOwned, CoDistinct}) + let h = sighashes.hashType(skipped, {CoType, CoConsiderOwned, CoDistinct}) var canon = g.canonTypes.getOrDefault(h) if canon == nil: g.canonTypes[h] = skipped @@ -1120,32 +1056,30 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf # 4. We have a custom destructor. # 5. We have a (custom) generic destructor. - # we do not generate '=trace' procs if we + # we do not generate '=trace' nor '=dispose' procs if we # have the cycle detection disabled, saves code size. - let lastAttached = if g.config.selectedGC == gcOrc: attachedTrace + let lastAttached = if g.config.selectedGC == gcOrc: attachedDispose else: attachedSink # bug #15122: We need to produce all prototypes before entering the - # mind boggling recursion. Hacks like these imply we should rewrite + # mind boggling recursion. Hacks like these imply we shoule rewrite # this module. - var generics: array[attachedWasMoved..attachedTrace, bool] - for k in attachedWasMoved..lastAttached: - generics[k] = getAttachedOp(g, canon, k) != nil + var generics: array[attachedDestructor..attachedDispose, bool] + for k in attachedDestructor..lastAttached: + generics[k] = canon.attachedOps[k] != nil if not generics[k]: - setAttachedOp(g, idgen.module, canon, k, - symPrototype(g, canon, canon.owner, k, info, idgen)) + canon.attachedOps[k] = symPrototype(g, canon, canon.owner, k, info, idgen) # we generate the destructor first so that other operators can depend on it: - for k in attachedWasMoved..lastAttached: + for k in attachedDestructor..lastAttached: if not generics[k]: discard produceSym(g, c, canon, k, info, idgen) else: - inst(g, c, canon, k, idgen, info) + inst(canon.attachedOps[k], canon, idgen) if canon != orig: - setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k)) + orig.attachedOps[k] = canon.attachedOps[k] - if not isTrival(getAttachedOp(g, orig, attachedDestructor)): + if not isTrival(orig.destructor): #or not isTrival(orig.assignment) or # not isTrival(orig.sink): orig.flags.incl tfHasAsgn - # ^ XXX Breaks IC! diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim index 7ca46ab1b..0b46c73a2 100644 --- a/compiler/liftlocals.nim +++ b/compiler/liftlocals.nim @@ -43,7 +43,7 @@ proc liftLocals(n: PNode; i: int; c: var Ctx) = of nkSym: if interestingVar(it.sym): n[i] = lookupOrAdd(c, it.sym, it.info) - of procDefs, nkTypeSection, nkMixinStmt, nkBindStmt: discard + of procDefs, nkTypeSection: discard else: for i in 0..= s.len: - discard "trailing underscores should be stripped off" - elif i > 0 and s[i-1] in {'A'..'Z'}: + if i > 0 and s[i-1] in {'A'..'Z'}: # don't skip '_' as it's essential for e.g. 'GC_disable' result.add('_') inc i @@ -86,33 +82,24 @@ 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*(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*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = + if {optStyleHint, optStyleError} * conf.globalOptions != {}: + nep1CheckDefImpl(conf, info, s, 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) +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) proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string = let line = sourceLine(conf, info) @@ -126,27 +113,21 @@ proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string = let last = first+identLen(line, first)-1 result = differ(line, first, last, newName) -proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) = +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 + let newName = s.name.s - let badName = differs(conf, info, newName) - if badName.len > 0: - lintReport(conf, info, newName, badName, "".dup(addDeclaredLoc(conf, s))) + let oldName = differs(conf, info, newName) + if oldName.len > 0: + lintReport(conf, info, newName, oldName) -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) = +proc checkPragmaUse*(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/llstream.nim b/compiler/llstream.nim index 004d990fa..bfc377a16 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -12,9 +12,6 @@ import pathutils -when defined(nimPreviewSlimSystem): - import std/syncio - # support `useGnuReadline`, `useLinenoise` for backwards compatibility const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and not defined(windows) @@ -150,11 +147,11 @@ proc llStreamReadLine*(s: PLLStream, line: var string): bool = of llsString: while s.rd < s.s.len: case s.s[s.rd] - of '\r': + of '\x0D': inc(s.rd) - if s.s[s.rd] == '\n': inc(s.rd) + if s.s[s.rd] == '\x0A': inc(s.rd) break - of '\n': + of '\x0A': inc(s.rd) break else: diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 3f028a52f..3c0aabe9a 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -8,14 +8,10 @@ # # This module implements lookup helpers. -import std/[algorithm, strutils] - -when defined(nimPreviewSlimSystem): - import std/assertions import intsets, ast, astalgo, idents, semdata, types, msgs, options, - renderer, nimfix/prettybase, lineinfos, modulegraphs, astmsgs, sets + renderer, nimfix/prettybase, lineinfos, strutils proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) @@ -48,11 +44,6 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent = case x.kind of nkIdent: id.add(x.ident.s) of nkSym: id.add(x.sym.name.s) - of nkSymChoices: - if x[0].kind == nkSym: - id.add(x[0].sym.name.s) - else: - handleError(n, origin) of nkLiterals - nkFloatLiterals: id.add(x.renderTree) else: handleError(n, origin) result = getIdent(c.cache, id) @@ -83,7 +74,7 @@ proc closeScope*(c: PContext) = ensureNoMissingOrUnusedSymbols(c, c.currentScope) rawCloseScope(c) -iterator allScopes*(scope: PScope): PScope = +iterator allScopes(scope: PScope): PScope = var current = scope while current != nil: yield current @@ -116,9 +107,8 @@ proc localSearchInScope*(c: PContext, s: PIdent): PSym = scope = scope.parent result = strTableGet(scope.symbols, s) -proc initIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; name: PIdent; - g: ModuleGraph): PSym = - result = initModuleIter(ti, g, im.m, name) +proc initIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule; name: PIdent): PSym = + result = initIdentIter(ti, im.m.tab, name) while result != nil: let b = case im.mode @@ -127,12 +117,11 @@ proc initIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; n of importExcept: name.id notin im.exceptSet if b and not containsOrIncl(marked, result.id): return result - result = nextModuleIter(ti, g) + result = nextIdentIter(ti, im.m.tab) -proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; - g: ModuleGraph): PSym = +proc nextIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule): PSym = while true: - result = nextModuleIter(ti, g) + result = nextIdentIter(ti, im.m.tab) if result == nil: return nil case im.mode of importAll: @@ -145,17 +134,17 @@ proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; if result.name.id notin im.exceptSet and not containsOrIncl(marked, result.id): return result -iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym = - var ti: ModuleIter - var candidate = initIdentIter(ti, marked, im, name, g) +iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent): PSym = + var ti: TIdentIter + var candidate = initIdentIter(ti, marked, im, name) while candidate != nil: yield candidate - candidate = nextIdentIter(ti, marked, im, g) + candidate = nextIdentIter(ti, marked, im) iterator importedItems*(c: PContext; name: PIdent): PSym = var marked = initIntSet() for im in c.imports.mitems: - for s in symbols(im, marked, name, c.graph): + for s in symbols(im, marked, name): yield s proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] = @@ -170,7 +159,6 @@ iterator allSyms*(c: PContext): (PSym, int, bool) = # really iterate over all symbols in all the scopes. This is expensive # and only used by suggest.nim. var isLocal = true - var scopeN = 0 for scope in allScopes(c.currentScope): if scope == c.topLevelScope: isLocal = false @@ -181,33 +169,20 @@ iterator allSyms*(c: PContext): (PSym, int, bool) = dec scopeN isLocal = false for im in c.imports.mitems: - for s in modulegraphs.allSyms(c.graph, im.m): - assert s != nil - yield (s, scopeN, isLocal) - -iterator uniqueSyms*(c: PContext): (PSym, int, bool) = - ## Like [allSyms] except only returns unique symbols (Uniqueness determined by line + name) - # Track seen symbols so we don't duplicate them. - # The int is for the symbols name, and line info is - # to be able to tell apart symbols with same name but on different lines - var seen = initHashSet[(TLineInfo, int)]() - for res in allSyms(c): - if not seen.containsOrIncl((res[0].info, res[0].name.id)): - yield res - + for s in im.m.tab.data: + if s != nil: + yield (s, scopeN, isLocal) proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym = var marked = initIntSet() - var symSet = OverloadableSyms result = nil - block outer: - for im in c.imports.mitems: - for s in symbols(im, marked, name, c.graph): - if result == nil: - result = s - elif s.kind notin symSet or result.kind notin symSet: + for im in c.imports.mitems: + for s in symbols(im, marked, name): + if result == nil: + result = s + else: + if s.kind notin OverloadableSyms or result.kind notin OverloadableSyms: ambiguous = true - break outer proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym = for scope in allScopes(c.currentScope): @@ -215,53 +190,31 @@ proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym = if result != nil: return result result = someSymFromImportTable(c, s, ambiguous) -proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} = +proc debugScopes*(c: PContext; limit=0) {.deprecated.} = var i = 0 - var count = 0 for scope in allScopes(c.currentScope): echo "scope ", i for h in 0..high(scope.symbols.data): if scope.symbols.data[h] != nil: - if count >= max: return - echo count, ": ", scope.symbols.data[h].name.s - count.inc - if i == limit: return + echo scope.symbols.data[h].name.s + if i == limit: break inc i -proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = +proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = result = @[] for scope in allScopes(c.currentScope): var ti: TIdentIter var candidate = initIdentIter(ti, scope.symbols, s) while candidate != nil: if candidate.kind in filter: - result.add candidate + if result.len == 0: + result.add candidate candidate = nextIdentIter(ti, scope.symbols) if result.len == 0: var marked = initIntSet() for im in c.imports.mitems: - for s in symbols(im, marked, s, c.graph): - if s.kind in filter: - result.add s - -proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = - result = @[] - block outer: - for scope in allScopes(c.currentScope): - var ti: TIdentIter - var candidate = initIdentIter(ti, scope.symbols, s) - while candidate != nil: - if candidate.kind in filter: - result.add candidate - # Break here, because further symbols encountered would be shadowed - break outer - candidate = nextIdentIter(ti, scope.symbols) - - if result.len == 0: - var marked = initIntSet() - for im in c.imports.mitems: - for s in symbols(im, marked, s, c.graph): + for s in symbols(im, marked, s): if s.kind in filter: result.add s @@ -287,7 +240,6 @@ type oimSymChoiceLocalLookup TOverloadIter* = object it*: TIdentIter - mit*: ModuleIter m*: PSym mode*: TOverloadIterMode symChoiceIndex*: int @@ -309,7 +261,6 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = var it: TTabIter var s = initTabIter(it, scope.symbols) var missingImpls = 0 - var unusedSyms: seq[tuple[sym: PSym, key: string]] while s != nil: if sfForward in s.flags and s.kind notin {skType, skModule}: # too many 'implementation of X' errors are annoying @@ -324,75 +275,45 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # maybe they can be made skGenericParam as well. if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and s.typ.kind != tyGenericParam: - unusedSyms.add (s, toFileLineCol(c.config, s.info)) + message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s) s = nextIter(it, scope.symbols) - for (s, _) in sortedByIt(unusedSyms, it.key): - message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s) proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; - conflictsWith: TLineInfo, note = errGenerated) = - ## Emit a redefinition error if in non-interactive mode + conflictsWith: TLineInfo) = if c.config.cmd != cmdInteractive: - localError(c.config, info, note, + localError(c.config, info, "redefinition of '$1'; previous declaration here: $2" % [s, c.config $ conflictsWith]) -# xxx pending bootstrap >= 1.4, replace all those overloads with a single one: -# proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} = -proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) = - if sym.name.s == "_": return - let conflict = scope.addUniqueSym(sym) +proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) = + let conflict = c.currentScope.addUniqueSym(sym) if conflict != nil: - if sym.kind == skModule and conflict.kind == skModule and - sym.position == conflict.position: - # e.g.: import foo; import foo - # xxx we could refine this by issuing a different hint for the case - # where a duplicate import happens inside an include. - localError(c.config, info, hintDuplicateModuleImport, - "duplicate import of '$1'; previous import here: $2" % - [sym.name.s, c.config $ conflict.info]) - else: - wrongRedefinition(c, info, sym.name.s, conflict.info, errGenerated) + wrongRedefinition(c, info, sym.name.s, conflict.info) -proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) {.inline.} = - addDeclAt(c, scope, sym, sym.info) - -proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) {.inline.} = - addDeclAt(c, c.currentScope, sym, info) - -proc addDecl*(c: PContext, sym: PSym) {.inline.} = - addDeclAt(c, c.currentScope, sym) +proc addDecl*(c: PContext, sym: PSym) = + let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true) + if conflict != nil: + wrongRedefinition(c, sym.info, sym.name.s, conflict.info) proc addPrelimDecl*(c: PContext, sym: PSym) = discard c.currentScope.addUniqueSym(sym) -from ic / ic import addHidden +proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) = + let conflict = scope.addUniqueSym(sym) + if conflict != nil: + wrongRedefinition(c, sym.info, sym.name.s, conflict.info) proc addInterfaceDeclAux(c: PContext, sym: PSym) = - ## adds symbol to the module for either private or public access. if sfExported in sym.flags: # add to interface: - if c.module != nil: exportSym(c, sym) + if c.module != nil: strTableAdd(c.module.tab, sym) else: internalError(c.config, sym.info, "addInterfaceDeclAux") - elif sym.kind in ExportableSymKinds and c.module != nil and isTopLevelInsideDeclaration(c, sym): - strTableAdd(semtabAll(c.graph, c.module), sym) - if c.config.symbolFiles != disabledSf: - addHidden(c.encoder, c.packedRepr, sym) proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = - ## adds a symbol on the scope and the interface if appropriate addDeclAt(c, scope, sym) - if not scope.isShadowScope: - # adding into a non-shadow scope, we need to handle exports, etc - addInterfaceDeclAux(c, sym) - -proc addInterfaceDecl*(c: PContext, sym: PSym) {.inline.} = - ## adds a decl and the interface if appropriate - addInterfaceDeclAt(c, c.currentScope, sym) + addInterfaceDeclAux(c, sym) proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = - ## adds an symbol to the given scope, will check for and raise errors if it's - ## a redefinition as opposed to an overload. if fn.kind notin OverloadableSyms: internalError(c.config, fn.info, "addOverloadableSymAt") return @@ -402,33 +323,25 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = else: scope.addSym(fn) +proc addInterfaceDecl*(c: PContext, sym: PSym) = + # it adds the symbol to the interface if appropriate + addDecl(c, sym) + addInterfaceDeclAux(c, sym) + proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) = - ## adds an overloadable symbol on the scope and the interface if appropriate + # it adds the symbol to the interface if appropriate addOverloadableSymAt(c, scope, sym) - if not scope.isShadowScope: - # adding into a non-shadow scope, we need to handle exports, etc - addInterfaceDeclAux(c, sym) + addInterfaceDeclAux(c, sym) proc openShadowScope*(c: PContext) = - ## opens a shadow scope, just like any other scope except the depth is the - ## same as the parent -- see `isShadowScope`. c.currentScope = PScope(parent: c.currentScope, symbols: newStrTable(), depthLevel: c.scopeDepth) proc closeShadowScope*(c: PContext) = - ## closes the shadow scope, but doesn't merge any of the symbols - ## Does not check for unused symbols or missing forward decls since a macro - ## or template consumes this AST - rawCloseScope(c) + c.closeScope proc mergeShadowScope*(c: PContext) = - ## close the existing scope and merge in all defined symbols, this will also - ## trigger any export related code if this is into a non-shadow scope. - ## - ## Merges: - ## shadow -> shadow: add symbols to the parent but check for redefinitions etc - ## shadow -> non-shadow: the above, but also handle exports and all that let shadowScope = c.currentScope c.rawCloseScope for sym in shadowScope.symbols: @@ -437,116 +350,47 @@ proc mergeShadowScope*(c: PContext) = else: c.addInterfaceDecl(sym) -when false: - # `nimfix` used to call `altSpelling` and prettybase.replaceDeprecated(n.info, ident, alt) - proc altSpelling(c: PContext, x: PIdent): PIdent = +when defined(nimfix): + # when we cannot find the identifier, retry with a changed identifier: + proc altSpelling(x: PIdent): PIdent = case x.s[0] - of 'A'..'Z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1)) - of 'a'..'z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1)) + of 'A'..'Z': result = getIdent(toLowerAscii(x.s[0]) & x.s.substr(1)) + of 'a'..'z': result = getIdent(toLowerAscii(x.s[0]) & x.s.substr(1)) else: result = x -import std/editdistance, heapqueue - -type SpellCandidate = object - dist: int - depth: int - msg: string - sym: PSym - -template toOrderTup(a: SpellCandidate): (int, int, string) = - # `dist` is first, to favor nearby matches - # `depth` is next, to favor nearby enclosing scopes among ties - # `sym.name.s` is last, to make the list ordered and deterministic among ties - (a.dist, a.depth, a.msg) - -proc `<`(a, b: SpellCandidate): bool = - a.toOrderTup < b.toOrderTup - -proc mustFixSpelling(c: PContext): bool {.inline.} = - result = c.config.spellSuggestMax != 0 and c.compilesContextId == 0 - # don't slowdown inside compiles() - -proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) = - ## when we cannot find the identifier, suggest nearby spellings - var list = initHeapQueue[SpellCandidate]() - let name0 = ident.s.nimIdentNormalize - - for (sym, depth, isLocal) in allSyms(c): - let depth = -depth - 1 - let dist = editDistance(name0, sym.name.s.nimIdentNormalize) - var msg: string - msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s] - list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym) - - if list.len == 0: return - let e0 = list[0] - var - count = 0 - last: PIdent = nil - while true: - # pending https://github.com/timotheecour/Nim/issues/373 use more efficient `itemsSorted`. - if list.len == 0: break - let e = list.pop() - if c.config.spellSuggestMax == spellSuggestSecretSauce: - const - smallThres = 2 - maxCountForSmall = 4 - # avoids ton of operator matches when mis-matching short symbols such as `i` - # other heuristics could be devised, such as only suggesting operators if `name0` - # is an operator (likewise with non-operators). - if e.dist > e0.dist or (name0.len <= smallThres and count >= maxCountForSmall): break - elif count >= c.config.spellSuggestMax: break - if count == 0: - result.add "\ncandidates (edit distance, scope distance); see '--spellSuggest': " - if e.sym.name != last: - result.add e.msg - count.inc - last = e.sym.name - -proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PSym = - var err = "ambiguous identifier: '" & s.name.s & "'" - var i = 0 - var ignoredModules = 0 - for candidate in importedItems(c, s.name): - if i == 0: err.add " -- use one of the following:\n" - else: err.add "\n" - err.add " " & candidate.owner.name.s & "." & candidate.name.s - err.add ": " & typeToString(candidate.typ) - if candidate.kind == skModule: - inc ignoredModules - else: - result = candidate - inc i - if ignoredModules != i-1: - localError(c.config, info, errGenerated, err) - result = nil - else: - amb = false + template fixSpelling(n: PNode; ident: PIdent; op: untyped) = + let alt = ident.altSpelling + result = op(c, alt).skipAlias(n) + if result != nil: + prettybase.replaceDeprecated(n.info, ident, alt) + return result +else: + template fixSpelling(n: PNode; ident: PIdent; op: untyped) = discard proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = - var amb: bool - discard errorUseQualifier(c, info, s, amb) - -proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") = - var err = "ambiguous identifier: '" & candidates[0].name.s & "'" + var err = "ambiguous identifier: '" & s.name.s & "'" var i = 0 - for candidate in candidates: - if i == 0: err.add " -- $1 the following:\n" % prefix + for candidate in importedItems(c, s.name): + if i == 0: err.add " -- use one of the following:\n" else: err.add "\n" err.add " " & candidate.owner.name.s & "." & candidate.name.s err.add ": " & typeToString(candidate.typ) inc i localError(c.config, info, errGenerated, err) -proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) = - var candidates = newSeq[PSym](choices.len) - let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate" - for i, n in choices: - candidates[i] = n.sym - errorUseQualifier(c, info, candidates, prefix) +proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]) = + var err = "ambiguous identifier: '" & candidates[0].name.s & "'" + var i = 0 + for candidate in candidates: + if i == 0: err.add " -- use one of the following:\n" + else: err.add "\n" + err.add " " & candidate.owner.name.s & "." & candidate.name.s + err.add ": " & typeToString(candidate.typ) + inc i + localError(c.config, info, errGenerated, err) -proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") = - var err = "undeclared identifier: '" & name & "'" & extra +proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) = + var err = "undeclared identifier: '" & name & "'" if c.recursiveDep.len > 0: err.add "\nThis might be caused by a recursive module dependency:\n" err.add c.recursiveDep @@ -554,31 +398,31 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extr c.recursiveDep = "" localError(c.config, info, errGenerated, err) -proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym = - var extra = "" - if c.mustFixSpelling: fixSpelling(c, n, ident, extra) - errorUndeclaredIdentifier(c, n.info, ident.s, extra) - result = errorSym(c, n) - proc lookUp*(c: PContext, n: PNode): PSym = # Looks up a symbol. Generates an error in case of nil. var amb = false case n.kind of nkIdent: result = searchInScopes(c, n.ident, amb).skipAlias(n, c.config) - if result == nil: result = errorUndeclaredIdentifierHint(c, n, n.ident) + if result == nil: + fixSpelling(n, n.ident, searchInScopes) + errorUndeclaredIdentifier(c, n.info, n.ident.s) + result = errorSym(c, n) of nkSym: result = n.sym of nkAccQuoted: var ident = considerQuotedIdent(c, n) result = searchInScopes(c, ident, amb).skipAlias(n, c.config) - if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident) + if result == nil: + fixSpelling(n, ident, searchInScopes) + errorUndeclaredIdentifier(c, n.info, ident.s) + result = errorSym(c, n) else: internalError(c.config, n.info, "lookUp") return if amb: #contains(c.ambiguousSymbols, result.id): - result = errorUseQualifier(c, n.info, result, amb) + errorUseQualifier(c, n.info, result) when false: if result.kind == skStub: loadStub(result) @@ -610,9 +454,11 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = errorUseQualifier(c, n.info, candidates) if result == nil and checkUndeclared in flags: - result = errorUndeclaredIdentifierHint(c, n, ident) + fixSpelling(n, ident, searchInScopes) + errorUndeclaredIdentifier(c, n.info, ident.s) + result = errorSym(c, n) elif checkAmbiguity in flags and result != nil and amb: - result = errorUseQualifier(c, n.info, result, amb) + errorUseQualifier(c, n.info, result) c.isAmbiguous = amb of nkSym: result = n.sym @@ -629,9 +475,11 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config) else: - result = someSym(c.graph, m, ident).skipAlias(n, c.config) + result = strTableGet(m.tab, ident).skipAlias(n, c.config) if result == nil and checkUndeclared in flags: - result = errorUndeclaredIdentifierHint(c, n[1], ident) + fixSpelling(n[1], ident, searchInScopes) + errorUndeclaredIdentifier(c, n[1].info, ident.s) + result = errorSym(c, n[1]) elif n[1].kind == nkSym: result = n[1].sym elif checkUndeclared in flags and @@ -661,7 +509,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = scope = scope.parent if scope == nil: for i in 0..c.imports.high: - result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph).skipAlias(n, c.config) + result = initIdentIter(o.it, o.marked, c.imports[i], ident).skipAlias(n, c.config) if result != nil: o.currentScope = nil o.importIdx = i @@ -687,7 +535,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = ident).skipAlias(n, c.config) o.mode = oimSelfModule else: - result = initModuleIter(o.mit, c.graph, o.m, ident).skipAlias(n, c.config) + result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n, c.config) else: noidentError(c.config, n[1], n) result = errorSym(c, n[1]) @@ -720,7 +568,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym var idx = o.importIdx+1 o.importIdx = c.imports.len # assume the other imported modules lack this symbol too while idx < c.imports.len: - result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph).skipAlias(n, c.config) + result = initIdentIter(o.it, o.marked, c.imports[idx], o.it.name).skipAlias(n, c.config) if result != nil: # oh, we were wrong, some other module had the symbol, so remember that: o.importIdx = idx @@ -730,7 +578,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym = assert o.currentScope == nil while o.importIdx < c.imports.len: - result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config) + result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config) #while result != nil and result.id in o.marked: # result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]) if result != nil: @@ -754,12 +602,12 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = else: o.importIdx = 0 if c.imports.len > 0: - result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config) + result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config) if result == nil: result = nextOverloadIterImports(o, c, n) break elif o.importIdx < c.imports.len: - result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config) + result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config) if result == nil: result = nextOverloadIterImports(o, c, n) else: @@ -767,7 +615,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = of oimSelfModule: result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config) of oimOtherModule: - result = nextModuleIter(o.mit, c.graph).skipAlias(n, c.config) + result = nextIdentIter(o.it, o.m.tab).skipAlias(n, c.config) of oimSymChoice: if o.symChoiceIndex < n.len: result = n[o.symChoiceIndex].sym @@ -806,7 +654,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = incl o.marked, result.id elif o.importIdx < c.imports.len: - result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config) + result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config) #assert result.id notin o.marked #while result != nil and result.id in o.marked: # result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index fc66fc9fa..37405d8d9 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -15,9 +15,6 @@ const import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs, lineinfos -when defined(nimPreviewSlimSystem): - import std/assertions - proc newDeref*(n: PNode): PNode {.inline.} = result = newNodeIT(nkHiddenDeref, n.info, n.typ[0]) result.add n @@ -74,20 +71,14 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: P let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var tempAsNode: PNode - let avoidTemp = value.kind == nkSym - if avoidTemp: - tempAsNode = value - else: - var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen), - owner, value.info, g.config.options) - temp.typ = skipTypes(value.typ, abstractInst) - incl(temp.flags, sfFromGeneric) - tempAsNode = newSymNode(temp) + var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen), + owner, value.info, g.config.options) + temp.typ = skipTypes(value.typ, abstractInst) + incl(temp.flags, sfFromGeneric) var v = newNodeI(nkVarSection, value.info) - if not avoidTemp: - v.addVar(tempAsNode, value) + let tempAsNode = newSymNode(temp) + v.addVar(tempAsNode, value) result.add(v) for i in 0..= low(g.intTypeCache) and value <= high(g.intTypeCache): + result = g.intTypeCache[value.int] + if result == nil: + let ti = getSysType(g, literal.info, tyInt) + result = copyType(ti, nextTypeId(g.idgen), ti.owner) + result.n = literal + g.intTypeCache[value.int] = result + else: + let ti = getSysType(g, literal.info, tyInt) + result = copyType(ti, nextTypeId(g.idgen), ti.owner) + result.n = literal + proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType = # for now we do not cache these: result = newSysType(g, tyFloat, size=8) @@ -99,15 +122,44 @@ proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} = result = t proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) = + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] let s = son.skipIntLit(id) father.sons.add(s) propagateToOwner(father, s) +proc setIntLitType*(g: ModuleGraph; result: PNode) = + let i = result.intVal + case g.config.target.intSize + of 8: result.typ = getIntLitType(g, result) + of 4: + if i >= low(int32) and i <= high(int32): + result.typ = getIntLitType(g, result) + else: + result.typ = getSysType(g, result.info, tyInt64) + of 2: + if i >= low(int16) and i <= high(int16): + result.typ = getIntLitType(g, result) + elif i >= low(int32) and i <= high(int32): + result.typ = getSysType(g, result.info, tyInt32) + else: + result.typ = getSysType(g, result.info, tyInt64) + of 1: + # 8 bit CPUs are insane ... + if i >= low(int8) and i <= high(int8): + result.typ = getIntLitType(g, result) + elif i >= low(int16) and i <= high(int16): + result.typ = getSysType(g, result.info, tyInt16) + elif i >= low(int32) and i <= high(int32): + result.typ = getSysType(g, result.info, tyInt32) + else: + result.typ = getSysType(g, result.info, tyInt64) + else: + internalError(g.config, result.info, "invalid int size") + proc getCompilerProc*(g: ModuleGraph; name: string): PSym = let ident = getIdent(g.cache, name) result = strTableGet(g.compilerprocs, ident) - if result == nil: - result = loadCompilerProc(g, name) proc registerCompilerProc*(g: ModuleGraph; s: PSym) = strTableAdd(g.compilerprocs, s) diff --git a/compiler/main.nim b/compiler/main.nim index ff870a14a..4c67aaea7 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -13,26 +13,20 @@ when not defined(nimcore): {.error: "nimcore MUST be defined for Nim's core tooling".} import - std/[strutils, os, times, tables, sha1, with, json], - llstream, ast, lexer, syntaxes, options, msgs, - condsyms, - idents, extccomp, - cgen, nversion, - platform, nimconf, depends, + llstream, strutils, os, ast, lexer, syntaxes, options, msgs, + condsyms, times, + sem, idents, passes, extccomp, + cgen, json, nversion, + platform, nimconf, passaux, depends, vm, modules, - modulegraphs, lineinfos, pathutils, vmprofiler - - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - -import ic / [cbackend, integrity, navigator] -from ic / ic import rodViewer - -import pipelines + modulegraphs, tables, lineinfos, pathutils, vmprofiler when not defined(leanCompiler): - import docgen + import jsgen, docgen, docgen2 + +proc semanticPasses(g: ModuleGraph) = + registerPass g, verbosePass + registerPass g, semPass proc writeDepsFile(g: ModuleGraph) = let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps") @@ -45,148 +39,107 @@ proc writeDepsFile(g: ModuleGraph) = f.writeLine(toFullPath(g.config, k)) f.close() -proc writeCMakeDepsFile(conf: ConfigRef) = - ## write a list of C files for build systems like CMake. - ## only updated when the C file list changes. - let fname = getNimcacheDir(conf) / conf.outFile.changeFileExt("cdeps") - # generate output files list - var cfiles: seq[string] = @[] - for it in conf.toCompile: cfiles.add(it.cname.string) - let fileset = cfiles.toCountTable() - # read old cfiles list - var fl: File - var prevset = initCountTable[string]() - if open(fl, fname.string, fmRead): - for line in fl.lines: prevset.inc(line) - fl.close() - # write cfiles out - if fileset != prevset: - fl = open(fname.string, fmWrite) - for line in cfiles: fl.writeLine(line) - fl.close() - proc commandGenDepend(graph: ModuleGraph) = - setPipeLinePass(graph, GenDependPass) - compilePipelineProject(graph) + semanticPasses(graph) + registerPass(graph, gendependPass) + compileProject(graph) let project = graph.config.projectFull writeDepsFile(graph) generateDot(graph, project) - - # dot in graphivz tool kit is required - let graphvizDotPath = findExe("dot") - if graphvizDotPath.len == 0: - quit("gendepend: Graphviz's tool dot is required," & - "see https://graphviz.org/download for downloading") - execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png").string & ' ' & changeFileExt(project, "dot").string) proc commandCheck(graph: ModuleGraph) = - let conf = graph.config - conf.setErrorMaxHighMaybe - defineSymbol(conf.symbols, "nimcheck") - if optWasNimscript in conf.globalOptions: - defineSymbol(conf.symbols, "nimscript") - defineSymbol(conf.symbols, "nimconfig") - elif conf.backend == backendJs: - setTarget(conf.target, osJS, cpuJS) - setPipeLinePass(graph, SemPass) - compilePipelineProject(graph) - - if conf.symbolFiles != disabledSf: - case conf.ideCmd - of ideDef: navDefinition(graph) - of ideUse: navUsages(graph) - of ideDus: navDefusages(graph) - else: discard - writeRodFiles(graph) + graph.config.setErrorMaxHighMaybe + defineSymbol(graph.config.symbols, "nimcheck") + semanticPasses(graph) # use an empty backend for semantic checking only + compileProject(graph) when not defined(leanCompiler): - proc commandDoc2(graph: ModuleGraph; ext: string) = + proc commandDoc2(graph: ModuleGraph; json: bool) = handleDocOutputOptions graph.config graph.config.setErrorMaxHighMaybe - case ext: - of TexExt: - setPipeLinePass(graph, Docgen2TexPass) - of JsonExt: - setPipeLinePass(graph, Docgen2JsonPass) - of HtmlExt: - setPipeLinePass(graph, Docgen2Pass) - else: doAssert false, $ext - compilePipelineProject(graph) + semanticPasses(graph) + if json: registerPass(graph, docgen2JsonPass) + else: registerPass(graph, docgen2Pass) + compileProject(graph) + finishDoc2Pass(graph.config.projectName) proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config + setOutFile(conf) extccomp.initVars(conf) - if conf.symbolFiles == disabledSf: - if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"): - if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile): - # nothing changed - graph.config.notes = graph.config.mainPackageNotes - return + semanticPasses(graph) + registerPass(graph, cgenPass) + + if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"): + let proj = changeFileExt(conf.projectFull, "") + if not changeDetectedViaJsonBuildInstructions(conf, proj): + # nothing changed + graph.config.notes = graph.config.mainPackageNotes + return if not extccomp.ccHasSaneOverflow(conf): conf.symbols.defineSymbol("nimEmulateOverflowChecks") - if conf.symbolFiles == disabledSf: - setPipeLinePass(graph, CgenPass) - else: - setPipeLinePass(graph, SemPass) - compilePipelineProject(graph) + compileProject(graph) if graph.config.errorCounter > 0: return # issue #9933 - if conf.symbolFiles == disabledSf: - cgenWriteModules(graph.backend, conf) - else: - if isDefined(conf, "nimIcIntegrityChecks"): - checkIntegrity(graph) - generateCode(graph) - # graph.backend can be nil under IC when nothing changed at all: - if graph.backend != nil: - cgenWriteModules(graph.backend, conf) - if conf.cmd != cmdTcc and graph.backend != nil: + cgenWriteModules(graph.backend, conf) + if conf.cmd != cmdTcc: extccomp.callCCompiler(conf) # for now we do not support writing out a .json file with the build instructions when HCR is on if not conf.hcrOn: extccomp.writeJsonBuildInstructions(conf) if optGenScript in graph.config.globalOptions: writeDepsFile(graph) - if optGenCDeps in graph.config.globalOptions: - writeCMakeDepsFile(conf) proc commandJsonScript(graph: ModuleGraph) = - extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile) + let proj = changeFileExt(graph.config.projectFull, "") + extccomp.runJsonBuildInstructions(graph.config, proj) proc commandCompileToJS(graph: ModuleGraph) = - let conf = graph.config when defined(leanCompiler): - globalError(conf, unknownLineInfo, "compiler wasn't built with JS code generator") + globalError(graph.config, unknownLineInfo, "compiler wasn't built with JS code generator") else: + let conf = graph.config conf.exc = excCpp - setTarget(conf.target, osJS, cpuJS) - defineSymbol(conf.symbols, "ecmascript") # For backward compatibility - setPipeLinePass(graph, JSgenPass) - compilePipelineProject(graph) - if optGenScript in conf.globalOptions: + + if conf.outFile.isEmpty: + conf.outFile = RelativeFile(conf.projectName & ".js") + + #incl(gGlobalOptions, optSafeCode) + setTarget(graph.config.target, osJS, cpuJS) + #initDefines() + defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility + semanticPasses(graph) + registerPass(graph, JSgenPass) + compileProject(graph) + if optGenScript in graph.config.globalOptions: writeDepsFile(graph) -proc commandInteractive(graph: ModuleGraph) = - graph.config.setErrorMaxHighMaybe +proc interactivePasses(graph: ModuleGraph) = initDefines(graph.config.symbols) defineSymbol(graph.config.symbols, "nimscript") # note: seems redundant with -d:nimHasLibFFI when hasFFI: defineSymbol(graph.config.symbols, "nimffi") - setPipeLinePass(graph, InterpreterPass) - compilePipelineSystemModule(graph) + registerPass(graph, verbosePass) + registerPass(graph, semPass) + registerPass(graph, evalPass) + +proc commandInteractive(graph: ModuleGraph) = + graph.config.setErrorMaxHighMaybe + interactivePasses(graph) + compileSystemModule(graph) if graph.config.commandArgs.len > 0: - discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) + discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) else: var m = graph.makeStdinModule() incl(m.flags, sfMainModule) var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0) let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config)) - discard processPipelineModule(graph, m, idgen, s) + processModule(graph, m, idgen, s) proc commandScan(cache: IdentCache, config: ConfigRef) = var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt) @@ -205,42 +158,15 @@ proc commandScan(cache: IdentCache, config: ConfigRef) = else: rawMessage(config, errGenerated, "cannot open file: " & f.string) -proc commandView(graph: ModuleGraph) = - let f = toAbsolute(mainCommandArg(graph.config), AbsoluteDir getCurrentDir()).addFileExt(RodExt) - rodViewer(f, graph.config, graph.cache) - const PrintRopeCacheStats = false -proc hashMainCompilationParams*(conf: ConfigRef): string = - ## doesn't have to be complete; worst case is a cache hit and recompilation. - var state = newSha1State() - with state: - update os.getAppFilename() # nim compiler - update conf.commandLine # excludes `arguments`, as it should - update $conf.projectFull # so that running `nim r main` from 2 directories caches differently - result = $SecureHash(state.finalize()) - -proc setOutFile*(conf: ConfigRef) = - proc libNameTmpl(conf: ConfigRef): string {.inline.} = - result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a" - - if conf.outFile.isEmpty: - var base = conf.projectName - if optUseNimcache in conf.globalOptions: - base.add "_" & hashMainCompilationParams(conf) - let targetName = - if conf.backend == backendJs: base & ".js" - elif optGenDynLib in conf.globalOptions: - platform.OS[conf.target.targetOS].dllFrmt % base - elif optGenStaticLib in conf.globalOptions: libNameTmpl(conf) % base - else: base & platform.OS[conf.target.targetOS].exeExt - conf.outFile = RelativeFile targetName - proc mainCommand*(graph: ModuleGraph) = let conf = graph.config let cache = graph.cache + # In "nim serve" scenario, each command must reset the registered passes + clearPasses(graph) conf.lastCmdTime = epochTime() conf.searchPaths.add(conf.libpath) @@ -269,7 +195,6 @@ proc mainCommand*(graph: ModuleGraph) = proc compileToBackend() = customizeForBackend(conf.backend) - setOutFile(conf) case conf.backend of backendC: commandCompileToC(graph) of backendCpp: commandCompileToC(graph) @@ -279,26 +204,25 @@ proc mainCommand*(graph: ModuleGraph) = template docLikeCmd(body) = when defined(leanCompiler): - conf.quitOrRaise "compiler wasn't built with documentation generator" + quit "compiler wasn't built with documentation generator" else: wantMainModule(conf) - let docConf = if conf.cmd == cmdDoc2tex: DocTexConfig else: DocConfig - loadConfigs(docConf, cache, conf, graph.idgen) + loadConfigs(DocConfig, cache, conf, graph.idgen) defineSymbol(conf.symbols, "nimdoc") body - ## command prepass - if conf.cmd == cmdCrun: conf.globalOptions.incl {optRun, optUseNimcache} - if conf.cmd notin cmdBackends + {cmdTcc}: customizeForBackend(backendC) - if conf.outDir.isEmpty: - # doc like commands can generate a lot of files (especially with --project) - # so by default should not end up in $PWD nor in $projectPath. - 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, cmdMd2html, cmdMd2tex}: - ret = ret / htmldocsDir - conf.outDir = ret + block: ## command prepass + if conf.cmd == cmdCrun: conf.globalOptions.incl {optRun, optUseNimcache} + if conf.cmd notin cmdBackends + {cmdTcc}: customizeForBackend(backendC) + if conf.outDir.isEmpty: + # doc like commands can generate a lot of files (especially with --project) + # so by default should not end up in $PWD nor in $projectPath. + conf.outDir = block: + 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 + ret ## process all commands case conf.cmd @@ -312,43 +236,32 @@ proc mainCommand*(graph: ModuleGraph) = else: rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc") of cmdDoc0: docLikeCmd commandDoc(cache, conf) - of cmdDoc: + of cmdDoc2: docLikeCmd(): - conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # issue #13218 + conf.setNoteDefaults(warnLockLevel, false) # issue #13218 + conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218 # because currently generates lots of false positives due to conflation # of labels links in doc comments, e.g. for random.rand: # ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer # ## * `rand proc<#rand,Rand,range[]>`_ that returns a float - commandDoc2(graph, HtmlExt) + commandDoc2(graph, false) if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions: commandBuildIndex(conf, $conf.outDir) - of cmdRst2html, cmdMd2html: - # XXX: why are warnings disabled by default for rst2html and rst2tex? - for warn in rstWarnings: - conf.setNoteDefaults(warn, true) - conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # similar to issue #13218 + of cmdRst2html: + conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218 when defined(leanCompiler): - conf.quitOrRaise "compiler wasn't built with documentation generator" + quit "compiler wasn't built with documentation generator" else: loadConfigs(DocConfig, cache, conf, graph.idgen) - commandRst2Html(cache, conf, preferMarkdown = (conf.cmd == cmdMd2html)) - of cmdRst2tex, cmdMd2tex, cmdDoc2tex: - for warn in rstWarnings: - conf.setNoteDefaults(warn, true) + commandRst2Html(cache, conf) + of cmdRst2tex: when defined(leanCompiler): - conf.quitOrRaise "compiler wasn't built with documentation generator" + quit "compiler wasn't built with documentation generator" else: - if conf.cmd in {cmdRst2tex, cmdMd2tex}: - loadConfigs(DocTexConfig, cache, conf, graph.idgen) - commandRst2TeX(cache, conf, preferMarkdown = (conf.cmd == cmdMd2tex)) - else: - docLikeCmd commandDoc2(graph, TexExt) + loadConfigs(DocTexConfig, cache, conf, graph.idgen) + commandRst2TeX(cache, conf) of cmdJsondoc0: docLikeCmd commandJson(cache, conf) - of cmdJsondoc: - docLikeCmd(): - commandDoc2(graph, JsonExt) - if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions: - commandBuildIndexJson(conf, $conf.outDir) + of cmdJsondoc: docLikeCmd commandDoc2(graph, true) of cmdCtags: docLikeCmd commandTags(cache, conf) of cmdBuildindex: docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile) of cmdGendepend: commandGenDepend(graph) @@ -387,29 +300,29 @@ proc mainCommand*(graph: ModuleGraph) = (key: "warnings", val: warnings), ] - msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook, msgNoUnitSep}) - # `msgNoUnitSep` to avoid generating invalid json, refs bug #17853 + msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook}) else: msgWriteln(conf, "-- list of currently defined symbols --", - {msgStdout, msgSkipHook, msgNoUnitSep}) - for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook, msgNoUnitSep}) + {msgStdout, msgSkipHook}) + for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook}) msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook}) for it in conf.searchPaths: msgWriteln(conf, it.string) - of cmdCheck: - commandCheck(graph) + of cmdCheck: commandCheck(graph) of cmdParse: wantMainModule(conf) discard parseFile(conf.projectMainIdx, cache, conf) - of cmdRod: + of cmdScan: wantMainModule(conf) - commandView(graph) - #msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") + commandScan(cache, conf) + msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") of cmdInteractive: commandInteractive(graph) of cmdNimscript: if conf.projectIsCmd or conf.projectIsStdin: discard elif not fileExists(conf.projectFull): rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string) + elif not conf.projectFull.string.endsWith(".nims"): + rawMessage(conf, errGenerated, "not a NimScript file: " & conf.projectFull.string) # main NimScript logic handled in `loadConfigs`. of cmdNop: discard of cmdJsonscript: @@ -419,9 +332,35 @@ proc mainCommand*(graph: ModuleGraph) = rawMessage(conf, errGenerated, "invalid command: " & conf.command) if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}: + let mem = + when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" + else: formatSize(getTotalMem()) & " totmem" + let loc = $conf.linesCompiled + let build = if isDefined(conf, "danger"): "Dangerous Release" + elif isDefined(conf, "release"): "Release" + else: "Debug" + let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) + let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName + + var output: string + if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript: + output = $conf.jsonBuildFile + elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends: + # for some cmd we expect a valid absOutFile + output = "unknownOutput" + else: + output = $conf.absOutFile + if optListFullPaths notin conf.globalOptions: output = output.AbsoluteFile.extractFilename if optProfileVM in conf.globalOptions: echo conf.dump(conf.vmProfileData) - genSuccessX(conf) + rawMessage(conf, hintSuccessX, [ + "loc", loc, + "sec", sec, + "mem", mem, + "build", build, + "project", project, + "output", output, + ]) when PrintRopeCacheStats: echo "rope cache stats: " diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 8ffbe20a5..5544668cc 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -10,78 +10,38 @@ ## This module implements the module graph data structure. The module graph ## represents a complete Nim project. Single modules can either be kept in RAM ## or stored in a rod-file. +## +## The caching of modules is critical for 'nimsuggest' and is tricky to get +## right. If module E is being edited, we need autocompletion (and type +## checking) for E but we don't want to recompile depending +## modules right away for faster turnaround times. Instead we mark the module's +## dependencies as 'dirty'. Let D be a dependency of E. If D is dirty, we +## need to recompile it and all of its dependencies that are marked as 'dirty'. +## 'nimsuggest sug' actually is invoked for the file being edited so we know +## its content changed and there is no need to compute any checksums. +## Instead of a recursive algorithm, we use an iterative algorithm: +## +## - If a module gets recompiled, its dependencies need to be updated. +## - Its dependent module stays the same. +## -import intsets, tables, hashes, md5 -import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages -import ic / [packed_ast, ic] +import ast, intsets, tables, options, lineinfos, hashes, idents, + btrees, md5 -when defined(nimPreviewSlimSystem): - import std/assertions +# import ic / packed_ast type SigHash* = distinct MD5Digest - LazySym* = object - id*: FullId - sym*: PSym - Iface* = object ## data we don't want to store directly in the ## ast.PSym type for s.kind == skModule module*: PSym ## module this "Iface" belongs to - converters*: seq[LazySym] - patterns*: seq[LazySym] - pureEnums*: seq[LazySym] - interf: TStrTable - interfHidden: TStrTable - uniqueName*: Rope + converters*: seq[PSym] + patterns*: seq[PSym] + pureEnums*: seq[PSym] - Operators* = object - opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym - opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym - - FullId* = object - module*: int - packed*: PackedItemId - - LazyType* = object - id*: FullId - typ*: PType - - LazyInstantiation* = object - module*: int - sym*: FullId - concreteTypes*: seq[FullId] - inst*: PInstantiation - - SymInfoPair* = object - sym*: PSym - info*: TLineInfo - - PipelinePass* = enum - NonePass - SemPass - JSgenPass - CgenPass - EvalPass - InterpreterPass - GenDependPass - Docgen2TexPass - Docgen2JsonPass - Docgen2Pass - - ModuleGraph* {.acyclic.} = ref object + ModuleGraph* = ref object ifaces*: seq[Iface] ## indexed by int32 fileIdx - packed*: PackedModuleGraph - encoders*: seq[PackedEncoder] - - typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId. - procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId. - attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc. - methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods - enumToStringProcs*: Table[ItemId, LazySym] - emittedTypeInfo*: Table[string, FileIndex] - - startupPackedConfig*: PackedConfig packageSyms*: TStrTable deps*: IntSet # the dependency graph or potentially its transitive closure. importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies @@ -99,24 +59,23 @@ 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] compilerprocs*: TStrTable exposed*: TStrTable - packageTypes*: TStrTable + intTypeCache*: array[-5..64, PType] + opContains*, opNot*: PSym emptyNode*: PNode canonTypes*: Table[SigHash, PType] symBodyHashes*: Table[int, SigHash] # symId to digest mapping importModuleCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PSym {.nimcall.} includeFileCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PNode {.nimcall.} - cacheSeqs*: Table[string, PNode] # state that is shared to support the 'macrocache' API; IC: implemented - cacheCounters*: Table[string, BiggestInt] # IC: implemented - cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented + recordStmt*: proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} + cacheSeqs*: Table[string, PNode] # state that is shared to support the 'macrocache' API + cacheCounters*: Table[string, BiggestInt] + cacheTables*: Table[string, BTree[string, PNode]] passes*: seq[TPass] - pipelinePass*: PipelinePass onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} @@ -124,7 +83,6 @@ type strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.} compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.} idgen*: IdGenerator - operators*: Operators TPassContext* = object of RootObj # the pass's context idgen*: IdGenerator @@ -139,14 +97,6 @@ type close: TPassClose, isFrontend: bool] -proc resetForBackend*(g: ModuleGraph) = - initStrTable(g.compilerprocs) - g.typeInstCache.clear() - g.procInstCache.clear() - for a in mitems(g.attachedOps): - a.clear() - g.methodsPerType.clear() - g.enumToStringProcs.clear() const cb64 = [ @@ -182,206 +132,6 @@ proc toBase64a(s: cstring, len: int): string = result.add cb64[a shr 2] result.add cb64[(a and 3) shl 4] -template interfSelect(iface: Iface, importHidden: bool): TStrTable = - var ret = iface.interf.addr # without intermediate ptr, it creates a copy and compiler becomes 15x slower! - if importHidden: ret = iface.interfHidden.addr - ret[] - -template semtab(g: ModuleGraph, m: PSym): TStrTable = - g.ifaces[m.position].interf - -template semtabAll*(g: ModuleGraph, m: PSym): TStrTable = - g.ifaces[m.position].interfHidden - -proc initStrTables*(g: ModuleGraph, m: PSym) = - initStrTable(semtab(g, m)) - initStrTable(semtabAll(g, m)) - -proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) = - strTableAdd(semtab(g, m), s) - strTableAdd(semtabAll(g, m), s) - -proc isCachedModule(g: ModuleGraph; module: int): bool {.inline.} = - result = module < g.packed.len and g.packed[module].status == loaded - -proc isCachedModule(g: ModuleGraph; m: PSym): bool {.inline.} = - isCachedModule(g, m.position) - -proc simulateCachedModule*(g: ModuleGraph; moduleSym: PSym; m: PackedModule) = - when false: - echo "simulating ", moduleSym.name.s, " ", moduleSym.position - simulateLoadedModule(g.packed, g.config, g.cache, moduleSym, m) - -proc initEncoder*(g: ModuleGraph; module: PSym) = - let id = module.position - if id >= g.encoders.len: - setLen g.encoders, id+1 - ic.initEncoder(g.encoders[id], - g.packed[id].fromDisk, module, g.config, g.startupPackedConfig) - -type - ModuleIter* = object - fromRod: bool - modIndex: int - ti: TIdentIter - rodIt: RodIter - importHidden: bool - -proc initModuleIter*(mi: var ModuleIter; g: ModuleGraph; m: PSym; name: PIdent): PSym = - assert m.kind == skModule - mi.modIndex = m.position - mi.fromRod = isCachedModule(g, mi.modIndex) - mi.importHidden = optImportHidden in m.options - if mi.fromRod: - result = initRodIter(mi.rodIt, g.config, g.cache, g.packed, FileIndex mi.modIndex, name, mi.importHidden) - else: - result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden), name) - -proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym = - if mi.fromRod: - result = nextRodIter(mi.rodIt, g.packed) - else: - result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden)) - -iterator allSyms*(g: ModuleGraph; m: PSym): PSym = - let importHidden = optImportHidden in m.options - if isCachedModule(g, m): - var rodIt: RodIter - var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden) - while r != nil: - yield r - r = nextRodIter(rodIt, g.packed) - else: - for s in g.ifaces[m.position].interfSelect(importHidden).data: - if s != nil: - yield s - -proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym = - let importHidden = optImportHidden in m.options - if isCachedModule(g, m): - result = interfaceSymbol(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden) - else: - result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name) - -proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym = - result = someSym(g, g.systemModule, name) - -iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym = - var mi: ModuleIter - var r = initModuleIter(mi, g, g.systemModule, name) - while r != nil: - yield r - r = nextModuleIter(mi, g) - -proc resolveType(g: ModuleGraph; t: var LazyType): PType = - result = t.typ - if result == nil and isCachedModule(g, t.id.module): - result = loadTypeFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed) - t.typ = result - assert result != nil - -proc resolveSym(g: ModuleGraph; t: var LazySym): PSym = - result = t.sym - if result == nil and isCachedModule(g, t.id.module): - result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed) - t.sym = result - assert result != nil - -proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation = - result = t.inst - if result == nil and isCachedModule(g, t.module): - result = PInstantiation(sym: loadSymFromId(g.config, g.cache, g.packed, t.sym.module, t.sym.packed)) - result.concreteTypes = newSeq[PType](t.concreteTypes.len) - for i in 0..high(result.concreteTypes): - result.concreteTypes[i] = loadTypeFromId(g.config, g.cache, g.packed, - t.concreteTypes[i].module, t.concreteTypes[i].packed) - t.inst = result - assert result != nil - -proc resolveAttachedOp(g: ModuleGraph; t: var LazySym): PSym = - result = t.sym - if result == nil: - result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed) - t.sym = result - assert result != nil - -iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType = - if g.typeInstCache.contains(s.itemId): - let x = addr(g.typeInstCache[s.itemId]) - for t in mitems(x[]): - yield resolveType(g, t) - -iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation = - if g.procInstCache.contains(s.itemId): - let x = addr(g.procInstCache[s.itemId]) - for t in mitems(x[]): - yield resolveInst(g, t) - -proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym = - ## returns the requested attached operation for type `t`. Can return nil - ## if no such operation exists. - if g.attachedOps[op].contains(t.itemId): - result = resolveAttachedOp(g, g.attachedOps[op][t.itemId]) - else: - result = nil - -proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) = - ## we also need to record this to the packed module. - g.attachedOps[op][t.itemId] = LazySym(sym: value) - -proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) = - ## we also need to record this to the packed module. - g.attachedOps[op][t.itemId] = LazySym(sym: value) - -proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) = - if g.config.symbolFiles != disabledSf: - assert module < g.encoders.len - assert isActive(g.encoders[module]) - toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk) - #storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk) - -proc getToStringProc*(g: ModuleGraph; t: PType): PSym = - result = resolveSym(g, g.enumToStringProcs[t.itemId]) - assert result != nil - -proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) = - g.enumToStringProcs[t.itemId] = LazySym(sym: value) - -iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) = - if g.methodsPerType.contains(t.itemId): - for it in mitems g.methodsPerType[t.itemId]: - yield (it[0], resolveSym(g, it[1])) - -proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) = - g.methodsPerType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m)) - -proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool = - let op = getAttachedOp(g, t, attachedAsgn) - result = op != nil and sfError in op.flags - -proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) = - for k in low(TTypeAttachedOp)..high(TTypeAttachedOp): - let op = getAttachedOp(g, src, k) - if op != nil: - setAttachedOp(g, module, dest, k, op) - -proc loadCompilerProc*(g: ModuleGraph; name: string): PSym = - if g.config.symbolFiles == disabledSf: return nil - - # slow, linear search, but the results are cached: - for module in 0..high(g.packed): - #if isCachedModule(g, module): - let x = searchForCompilerproc(g.packed[module], name) - if x >= 0: - result = loadSymFromId(g.config, g.cache, g.packed, module, toPackedItemId(x)) - if result != nil: - strTableAdd(g.compilerprocs, result) - return result - -proc loadPackedSym*(g: ModuleGraph; s: var LazySym) = - if s.sym == nil: - s.sym = loadSymFromId(g.config, g.cache, g.packed, s.id.module, s.id.packed) - proc `$`*(u: SigHash): string = toBase64a(cast[cstring](unsafeAddr u), sizeof(u)) @@ -395,13 +145,27 @@ proc hash*(u: SigHash): Hash = proc hash*(x: FileIndex): Hash {.borrow.} -template getPContext(): untyped = - when c is PContext: c - else: c.c +when defined(nimfind): + template onUse*(info: TLineInfo; s: PSym) = + when compiles(c.c.graph): + if c.c.graph.onUsage != nil: c.c.graph.onUsage(c.c.graph, s, info) + else: + if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info) + + template onDef*(info: TLineInfo; s: PSym) = + when compiles(c.c.graph): + if c.c.graph.onDefinition != nil: c.c.graph.onDefinition(c.c.graph, s, info) + else: + if c.graph.onDefinition != nil: c.graph.onDefinition(c.graph, s, info) + + template onDefResolveForward*(info: TLineInfo; s: PSym) = + when compiles(c.c.graph): + if c.c.graph.onDefinitionResolveForward != nil: + c.c.graph.onDefinitionResolveForward(c.c.graph, s, info) + else: + 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 @@ -410,51 +174,21 @@ else: proc stopCompile*(g: ModuleGraph): bool {.inline.} = result = g.doStopCompile != nil and g.doStopCompile() -proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic): PSym = - result = newSym(skProc, getIdent(g.cache, name), nextSymId(idgen), nil, unknownLineInfo, {}) +proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym = + result = newSym(skProc, getIdent(g.cache, name), nextSymId(g.idgen), nil, unknownLineInfo, {}) result.magic = m result.flags = {sfNeverRaises} -proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym = - result = createMagic(g, g.idgen, name, m) - proc registerModule*(g: ModuleGraph; m: PSym) = assert m != nil assert m.kind == skModule if m.position >= g.ifaces.len: setLen(g.ifaces, m.position + 1) + g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[]) - if m.position >= g.packed.len: - setLen(g.packed, m.position + 1) - - g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[], - uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position)))) - initStrTables(g, m) - -proc registerModuleById*(g: ModuleGraph; m: FileIndex) = - registerModule(g, g.packed[int m].module) - -proc initOperators*(g: ModuleGraph): Operators = - # These are safe for IC. - # Public because it's used by DrNim. - result.opLe = createMagic(g, "<=", mLeI) - result.opLt = createMagic(g, "<", mLtI) - result.opAnd = createMagic(g, "and", mAnd) - result.opOr = createMagic(g, "or", mOr) - result.opIsNil = createMagic(g, "isnil", mIsNil) - result.opEq = createMagic(g, "==", mEqI) - result.opAdd = createMagic(g, "+", mAddI) - result.opSub = createMagic(g, "-", mSubI) - result.opMul = createMagic(g, "*", mMulI) - result.opDiv = createMagic(g, "div", mDivI) - result.opLen = createMagic(g, "len", mLengthSeq) - result.opNot = createMagic(g, "not", mNot) - result.opContains = createMagic(g, "contains", mInSet) - -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: +proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = + result = ModuleGraph() result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32) initStrTable(result.packageSyms) result.deps = initIntSet() @@ -462,27 +196,22 @@ proc initModuleGraphFields(result: 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) - initStrTable(result.packageTypes) + result.opNot = createMagic(result, "not", mNot) + result.opContains = createMagic(result, "contains", mInSet) result.emptyNode = newNode(nkEmpty) + result.recordStmt = proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} = + discard result.cacheSeqs = initTable[string, PNode]() result.cacheCounters = initTable[string, BiggestInt]() result.cacheTables = initTable[string, BTree[string, PNode]]() result.canonTypes = initTable[SigHash, PType]() result.symBodyHashes = initTable[int, SigHash]() - 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) @@ -495,51 +224,10 @@ 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: - if isCachedModule(g, fileIdx.int32): - result = g.packed[fileIdx.int32].module - elif fileIdx.int32 < g.ifaces.len: - result = g.ifaces[fileIdx.int32].module - -proc moduleOpenForCodegen*(g: ModuleGraph; m: FileIndex): bool {.inline.} = - if g.config.symbolFiles == disabledSf: - result = true - else: - result = g.packed[m.int32].status notin {undefined, stored, loaded} - -proc rememberEmittedTypeInfo*(g: ModuleGraph; m: FileIndex; ti: string) = - #assert(not isCachedModule(g, m.int32)) - if g.config.symbolFiles != disabledSf: - #assert g.encoders[m.int32].isActive - assert g.packed[m.int32].status != stored - g.packed[m.int32].fromDisk.emittedTypeInfo.add ti - #echo "added typeinfo ", m.int32, " ", ti, " suspicious ", not g.encoders[m.int32].isActive - -proc rememberFlag*(g: ModuleGraph; m: PSym; flag: ModuleBackendFlag) = - if g.config.symbolFiles != disabledSf: - #assert g.encoders[m.int32].isActive - assert g.packed[m.position].status != stored - g.packed[m.position].fromDisk.backendFlags.incl flag - -proc closeRodFile*(g: ModuleGraph; m: PSym) = - if g.config.symbolFiles in {readOnlySf, v2Sf}: - # For stress testing we seek to reload the symbols from memory. This - # way much of the logic is tested but the test is reproducible as it does - # not depend on the hard disk contents! - let mint = m.position - saveRodFile(toRodFile(g.config, AbsoluteFile toFullPath(g.config, FileIndex(mint))), - g.encoders[mint], g.packed[mint].fromDisk) - g.packed[mint].status = stored - - elif g.config.symbolFiles == stressTest: - # debug code, but maybe a good idea for production? Could reduce the compiler's - # memory consumption considerably at the cost of more loads from disk. - let mint = m.position - simulateCachedModule(g, m, g.packed[mint].fromDisk) - g.packed[mint].status = loaded + if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len: + result = g.ifaces[fileIdx.int32].module proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b @@ -574,19 +262,7 @@ proc transitiveClosure(g: var IntSet; n: int) = proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = let m = g.getModule fileIdx - 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) = getPathVersion(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" @@ -69,18 +163,3 @@ 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 0aa1c8930..a8a9c4df8 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -10,9 +10,9 @@ ## Implements the module handling, including the caching of modules. import - ast, magicsys, msgs, options, - idents, lexer, syntaxes, modulegraphs, - lineinfos, pathutils + ast, astalgo, magicsys, msgs, options, + idents, lexer, passes, syntaxes, llstream, modulegraphs, + lineinfos, pathutils, tables proc resetSystemArtifacts*(g: ModuleGraph) = magicsys.resetSysTypes(g) @@ -20,12 +20,56 @@ proc resetSystemArtifacts*(g: ModuleGraph) = template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent = getIdent(graph.cache, splitFile(filename).name) -proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = +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) + var packSym = graph.packageSyms.strTableGet(pack) + if packSym == nil: + packSym = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info) + initStrTable(packSym.tab) + graph.packageSyms.strTableAdd(packSym) + else: + let existing = strTableGet(packSym.tab, name) + 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 `packSym`'s owner be the original `packSym` + packSym = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), packSym, info) + initStrTable(packSym.tab) + graph.packageSyms.strTableAdd(packSym) + result = packSym + +proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = let packSym = getPackage(graph, fileIdx) result.owner = packSym result.position = int fileIdx -proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym = + graph.registerModule(result) + + initStrTable(result.tab) + 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 + strTableAdd(packSym.tab, 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 # mechanism, which we do in order to assign each module a persistent ID. @@ -33,21 +77,95 @@ proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym = name: getModuleIdent(graph, filename), info: newLineInfo(fileIdx, 1, 1)) if not isNimIdentifier(result.name.s): - rawMessage(graph.config, errGenerated, "invalid module name: '" & result.name.s & - "'; a module name must be a valid Nim identifier.") + rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) partialInitModule(result, graph, fileIdx, filename) - graph.registerModule(result) + +proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym = + var flags = flags + if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule + result = graph.getModule(fileIdx) + + template processModuleAux = + var s: PLLStream + if sfMainModule in flags: + if graph.config.projectIsStdin: s = stdin.llStreamOpen + elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput) + discard processModule(graph, result, idGeneratorFromModule(result), s) + if result == nil: + let filename = AbsoluteFile toFullPath(graph.config, fileIdx) + when false: + # XXX entry point for module loading from the rod file + result = loadModuleSym(graph, fileIdx, filename) + when true: + result = newModule(graph, fileIdx) + result.flags.incl flags + registerModule(graph, result) + else: + partialInitModule(result, graph, fileIdx, filename) + processModuleAux() + elif graph.isDirty(result): + result.flags.excl sfDirty + # reset module fields: + initStrTable(result.tab) + result.ast = nil + processModuleAux() + graph.markClientsDirty(fileIdx) + +proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = + # this is called by the semantic checking phase + assert graph.config != nil + result = compileModule(graph, fileIdx, {}) + graph.addDep(s, fileIdx) + # keep track of import relationships + if graph.config.hcrOn: + graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx) + #if sfSystemModule in result.flags: + # 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 + else: graph.config.foreignPackageNotes proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode = result = syntaxes.parseFile(fileIdx, graph.cache, graph.config) graph.addDep(s, fileIdx) graph.addIncludeDep(s.position.FileIndex, fileIdx) +proc connectCallbacks*(graph: ModuleGraph) = + graph.includeFileCallback = includeModule + graph.importModuleCallback = importModule + +proc compileSystemModule*(graph: ModuleGraph) = + if graph.systemModule == nil: + connectCallbacks(graph) + graph.config.m.systemFileIdx = fileInfoIdx(graph.config, + graph.config.libpath / RelativeFile"system.nim") + discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule}) + proc wantMainModule*(conf: ConfigRef) = if conf.projectFull.isEmpty: - fatal(conf, gCmdLineInfo, "command expects a filename") + fatal(conf, newLineInfo(conf, AbsoluteFile(commandLineDesc), 1, 1), errGenerated, + "command expects a filename") conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) +proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = + connectCallbacks(graph) + let conf = graph.config + wantMainModule(conf) + let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim") + let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx + conf.projectMainIdx2 = projectFile + + let packSym = getPackage(graph, projectFile) + graph.config.mainPackageId = packSym.getnimblePkgId + graph.importStack.add projectFile + + if projectFile == systemFileIdx: + discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule}) + else: + graph.compileSystemModule() + discard graph.compileModule(projectFile, {sfMainModule}) + proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym = result = graph.newModule(fileInfoIdx(graph.config, filename)) registerModule(graph, result) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8e391f2fb..afb51da09 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -8,26 +8,17 @@ # import - std/[strutils, os, tables, terminal, macros, times], - std/private/miscdollars, - options, lineinfos, pathutils - -import ropes except `%` - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - + options, strutils, os, tables, ropes, terminal, macros, + lineinfos, pathutils +import std/private/miscdollars +import strutils2 type InstantiationInfo* = typeof(instantiationInfo()) -template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) +template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) template toStdOrrKind(stdOrr): untyped = if stdOrr == stdout: stdOrrStdout else: stdOrrStderr -proc toLowerAscii(a: var string) {.inline.} = - for c in mitems(a): - if isUpperAscii(c): c = char(uint8(c) xor 0b0010_0000'u8) - proc flushDot*(conf: ConfigRef) = ## safe to call multiple times # xxx one edge case not yet handled is when `printf` is called at CT with `compiletimeFFI`. @@ -37,7 +28,7 @@ proc flushDot*(conf: ConfigRef) = conf.lastMsgWasDot.excl stdOrrKind write(stdOrr, "\n") -proc toCChar*(c: char; result: var string) {.inline.} = +proc toCChar*(c: char; result: var string) = case c of '\0'..'\x1F', '\x7F'..'\xFF': result.add '\\' @@ -49,16 +40,18 @@ proc toCChar*(c: char; result: var string) {.inline.} = result.add c proc makeCString*(s: string): Rope = - result = newStringOfCap(int(s.len.toFloat * 1.1) + 1) - result.add("\"") + result = nil + var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1) + res.add("\"") for i in 0..= 0 - when defined(gcArc) or defined(gcOrc): - conf.m.fileInfos[fileIdx.int32].hash = hash - else: - shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash) - + shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash) proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string = assert fileIdx.int32 >= 0 - when defined(gcArc) or defined(gcOrc): - result = conf.m.fileInfos[fileIdx.int32].hash - else: - shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash) + shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash) proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile = if fileIdx.int32 < 0: @@ -259,30 +240,36 @@ template toFullPath*(conf: ConfigRef; info: TLineInfo): string = template toFullPathConsiderDirty*(conf: ConfigRef; info: TLineInfo): string = string toFullPathConsiderDirty(conf, info.fileIndex) +type FilenameOption* = enum + foAbs # absolute path, e.g.: /pathto/bar/foo.nim + foRelProject # relative to project path, e.g.: ../foo.nim + foMagicSauce # magic sauce, shortest of (foAbs, foRelProject) + foName # lastPathPart, e.g.: foo.nim + foStacktrace # if optExcessiveStackTrace: foAbs else: foName + proc toFilenameOption*(conf: ConfigRef, fileIdx: FileIndex, opt: FilenameOption): string = case opt of foAbs: result = toFullPath(conf, fileIdx) of foRelProject: result = toProjPath(conf, fileIdx) - of foCanonical: - let absPath = toFullPath(conf, fileIdx) - result = canonicalImportAux(conf, absPath.AbsoluteFile) - of foName: result = toProjPath(conf, fileIdx).lastPathPart - of foLegacyRelProj: + of foMagicSauce: let absPath = toFullPath(conf, fileIdx) relPath = toProjPath(conf, fileIdx) - result = if (relPath.len > absPath.len) or (relPath.count("..") > 2): + result = if (optListFullPaths in conf.globalOptions) or + (relPath.len > absPath.len) or + (relPath.count("..") > 2): absPath else: relPath + of foName: result = toProjPath(conf, fileIdx).lastPathPart of foStacktrace: if optExcessiveStackTrace in conf.globalOptions: result = toFilenameOption(conf, fileIdx, foAbs) else: result = toFilenameOption(conf, fileIdx, foName) -proc toMsgFilename*(conf: ConfigRef; fileIdx: FileIndex): string = - toFilenameOption(conf, fileIdx, conf.filenameOption) +proc toMsgFilename*(conf: ConfigRef; info: FileIndex): string = + toFilenameOption(conf, info, foMagicSauce) template toMsgFilename*(conf: ConfigRef; info: TLineInfo): string = toMsgFilename(conf, info.fileIndex) @@ -311,7 +298,6 @@ type MsgFlag* = enum ## flags altering msgWriteln behavior msgStdout, ## force writing to stdout, even stderr is default msgSkipHook ## skip message hook even if it is present - msgNoUnitSep ## the message is a complete "paragraph". MsgFlags* = set[MsgFlag] proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = @@ -323,20 +309,17 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = ## This is used for 'nim dump' etc. where we don't have nimsuggest ## support. #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return - let sep = if msgNoUnitSep notin flags: conf.unitSep else: "" if not isNil(conf.writelnHook) and msgSkipHook notin flags: - conf.writelnHook(s & sep) + conf.writelnHook(s) elif optStdout in conf.globalOptions or msgStdout in flags: if eStdOut in conf.m.errorOutputs: flushDot(conf) - write stdout, s - writeLine(stdout, sep) + writeLine(stdout, s) flushFile(stdout) else: if eStdErr in conf.m.errorOutputs: flushDot(conf) - write stderr, s - writeLine(stderr, sep) + writeLine(stderr, s) # On Windows stderr is fully-buffered when piped, regardless of C std. when defined(windows): flushFile(stderr) @@ -382,7 +365,7 @@ proc msgWrite(conf: ConfigRef; s: string) = flushFile(stdOrr) conf.lastMsgWasDot.incl stdOrr.toStdOrrKind() # subsequent writes need `flushDot` -template styledMsgWriteln(args: varargs[typed]) = +template styledMsgWriteln*(args: varargs[typed]) = if not isNil(conf.writelnHook): callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in conf.globalOptions: @@ -414,8 +397,7 @@ proc log*(s: string) = close(f) proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} = - if conf.isDefined("nimDebug"): quitOrRaise(conf, $msg) - elif defined(debug) or msg == errInternal or conf.hasHint(hintStackTrace): + if defined(debug) or msg == errInternal or conf.hasHint(hintStackTrace): {.gcsafe.}: if stackTraceAvailable() and isNil(conf.writelnHook): writeStackTrace() @@ -423,21 +405,19 @@ proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} = styledMsgWriteln(fgRed, """ No stack traceback available To create a stacktrace, rerun compilation with './koch temp $1 ', see $2 for details""" % - [conf.command, "intern.html#debugging-the-compiler".createDocLink], conf.unitSep) + [conf.command, "intern.html#debugging-the-compiler".createDocLink]) quit 1 -proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) = - if msg in fatalMsgs: +proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) = + if msg >= fatalMin and msg <= fatalMax: 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 and not ignoreMsg): + (msg in warnMin..warnMax and msg in conf.warningAsErrors): inc(conf.errorCounter) conf.exitcode = 1'i8 if conf.errorCounter >= conf.errorMax: - # only really quit when we're not in the new 'nim check --def' mode: - if conf.ideCmd == ideNone: - quit(conf, msg) + quit(conf, msg) elif eh == doAbort and conf.cmd != cmdIdeTools: quit(conf, msg) elif eh == doRaise: @@ -460,11 +440,10 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) = conf.structuredErrorHook(conf, context.info, instantiationFrom, Severity.Hint) else: - let message = - if context.detail == "": - instantiationFrom - else: - instantiationOfFrom.format(context.detail) + let message = if context.detail == "": + instantiationFrom + else: + instantiationOfFrom.format(context.detail) styledMsgWriteln(styleBright, conf.toFileLineCol(context.info), " ", resetStyle, message) info = context.info @@ -481,7 +460,7 @@ proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int = if result == 0: try: for line in lines(toFullPathConsiderDirty(conf, fileIdx).string): - addSourceLine conf, fileIdx, line + addSourceLine conf, fileIdx, line.string except IOError: discard result = conf.m.fileInfos[fileIdx.int32].lines.len @@ -496,12 +475,11 @@ proc sourceLine*(conf: ConfigRef; i: TLineInfo): string = result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1] -proc getSurroundingSrc(conf: ConfigRef; info: TLineInfo): string = - if conf.hasHint(hintSource) and info != unknownLineInfo: - const indent = " " - result = "\n" & indent & $sourceLine(conf, info) - if info.col >= 0: - result.add "\n" & indent & spaces(info.col) & '^' +proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) = + const indent = " " + msgWriteln(conf, indent & $sourceLine(conf, info)) + if info.col >= 0: + msgWriteln(conf, indent & spaces(info.col) & '^') proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string = let title = case msg @@ -511,18 +489,12 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg) proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, - eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.gcsafe, noinline.} = + eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.noinline.} = var title: string color: ForegroundColor ignoreMsg = false sev: Severity - let errorOutputsOld = conf.m.errorOutputs - if msg in fatalMsgs: - # don't gag, refs bug #7080, bug #18278; this can happen with `{.fatal.}` - # or inside a `tryConstExpr`. - conf.m.errorOutputs = {eStdOut, eStdErr} - let kind = if msg in warnMin..hintMax and msg != hintUserRaw: $msg else: "" # xxx not sure why hintUserRaw is special case msg of errMin..errMax: @@ -539,21 +511,18 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, of warnMin..warnMax: sev = Severity.Warning ignoreMsg = not conf.hasWarn(msg) - if not ignoreMsg and msg in conf.warningAsErrors: + if msg in conf.warningAsErrors: + ignoreMsg = false title = ErrorTitle - color = ErrorColor else: title = WarningTitle - color = WarningColor if not ignoreMsg: writeContext(conf, info) + color = WarningColor inc(conf.warnCounter) of hintMin..hintMax: sev = Severity.Hint ignoreMsg = not conf.hasHint(msg) - if not ignoreMsg and msg in conf.warningAsErrors: - title = ErrorTitle - else: - title = HintTitle + title = HintTitle color = HintColor inc(conf.hintCounter) @@ -565,21 +534,18 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, if conf.structuredErrorHook != nil: conf.structuredErrorHook(conf, info, s & kindmsg, sev) if not ignoreMsgBecauseOfIdeTools(conf, msg): - if msg == hintProcessing and conf.hintProcessingDots: + if msg == hintProcessing: msgWrite(conf, ".") else: - styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg, - resetStyle, conf.getSurroundingSrc(info), conf.unitSep) + styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg) + if conf.hasHint(hintSource) and info != unknownLineInfo: + conf.writeSurroundingSrc(info) if hintMsgOrigin in conf.mainPackageNotes: - # xxx needs a bit of refactoring to honor `conf.filenameOption` styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, " compiler msg initiated here", KindColor, KindFormat % $hintMsgOrigin, - resetStyle, conf.unitSep) - 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 + resetStyle) + handleError(conf, msg, eh, s) template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = let arg = msgKindToString(msg) % args @@ -588,7 +554,9 @@ template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) = liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc()) -template fatal*(conf: ConfigRef; info: TLineInfo, arg = "", msg = errFatal) = +template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + # this fixes bug #7080 so that it is at least obvious 'fatal' was executed. + conf.m.errorOutputs = {eStdOut, eStdErr} liMessage(conf, info, msg, arg, doAbort, instLoc()) template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") = @@ -612,12 +580,12 @@ template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") template localError*(conf: ConfigRef; info: TLineInfo, arg: string) = liMessage(conf, info, errGenerated, arg, doNothing, instLoc()) +template localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openArray[string]) = + liMessage(conf, info, errGenerated, format % params, doNothing, instLoc()) + template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(conf, info, msg, arg, doNothing, instLoc()) -proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "") {.inline.} = - message(conf, info, warnDeprecated, msg) - proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) = if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return writeContext(conf, info) @@ -636,8 +604,8 @@ template internalAssert*(conf: ConfigRef, e: bool) = let arg = info2.toFileLineCol internalErrorImpl(conf, unknownLineInfo, arg, info2) -template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") = - let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg] +template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string) = + let m = "'$2' should be: '$1'" % [beau, got] let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName liMessage(conf, info, msg, m, doNothing, instLoc()) @@ -650,84 +618,8 @@ proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = result = conf.m.fileInfos[i.fileIndex.int32].quotedName template listMsg(title, r) = - msgWriteln(conf, title, {msgNoUnitSep}) - for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", $a], {msgNoUnitSep}) + msgWriteln(conf, title) + for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", $a]) proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax) proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax) - -proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string = - ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'} - ## so that it is useful as a C identifier snippet. - let path = AbsoluteFile toFullPath(conf, fid) - let rel = - if path.string.startsWith(conf.libpath.string): - relativeTo(path, conf.libpath).string - else: - relativeTo(path, conf.projectPath).string - let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len - result = newStringOfCap(trunc) - for i in 0.. 0: build.add "; options:" & flags - let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) - let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName - # xxx honor conf.filenameOption more accurately - var output: string - if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript: - output = $conf.jsonBuildFile - elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends: - # for some cmd we expect a valid absOutFile - output = "unknownOutput" - else: - output = $conf.absOutFile - if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename - # xxx honor filenameOption more accurately - rawMessage(conf, hintSuccessX, [ - "build", build, - "loc", loc, - "sec", sec, - "mem", mem, - "project", project, - "output", output, - ]) diff --git a/compiler/ndi.nim b/compiler/ndi.nim index a9d9cfe79..5af87237f 100644 --- a/compiler/ndi.nim +++ b/compiler/ndi.nim @@ -12,9 +12,6 @@ import ast, msgs, ropes, options, pathutils -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - type NdiFile* = object enabled: bool diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim index 91d76b2b8..d0ef45d04 100644 --- a/compiler/nilcheck.nim +++ b/compiler/nilcheck.nim @@ -8,10 +8,7 @@ # import ast, renderer, intsets, tables, msgs, options, lineinfos, strformat, idents, treetab, hashes -import sequtils, strutils, sets - -when defined(nimPreviewSlimSystem): - import std/assertions +import sequtils, strutils, std / sets # IMPORTANT: notes not up to date, i'll update this comment again # @@ -1242,7 +1239,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = result = check(n.sons[0], ctx, map) of nkIfStmt, nkIfExpr: result = checkIf(n, ctx, map) - of nkAsgn, nkFastAsgn, nkSinkAsgn: + of nkAsgn: result = checkAsgn(n[0], n[1], ctx, map) of nkVarSection: result.map = map @@ -1263,8 +1260,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, - nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, - nkTypeOfExpr, nkMixinStmt, nkBindStmt: + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: discard "don't follow this : same as varpartitions" result = Check(nilability: Nil, map: map) @@ -1286,7 +1282,7 @@ proc typeNilability(typ: PType): Nilability = # echo "typeNilability ", $typ.flags, " ", $typ.kind result = if tfNotNil in typ.flags: Safe - elif typ.kind in {tyRef, tyCstring, tyPtr, tyPointer}: + elif typ.kind in {tyRef, tyCString, tyPtr, tyPointer}: # # tyVar ? tyVarargs ? tySink ? tyLent ? # TODO spec? tests? diff --git a/compiler/nim.cfg b/compiler/nim.cfg index b9c8fdc08..cb586a9cb 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -4,17 +4,13 @@ hint[XDeclaredButNotUsed]:off define:booting define:nimcore -define:nimPreviewFloatRoundtrip -define:nimPreviewSlimSystem -define:nimPreviewCstringConversion -define:nimPreviewRangeDefault -threads:off - +#define:nimIncremental #import:"$projectpath/testability" +#define:staticSqlite + @if windows: cincludes: "$lib/wrappers/libffi/common" - tlsEmulation:off @end define:useStdoutAsStdmsg @@ -28,21 +24,5 @@ define:useStdoutAsStdmsg #gc:markAndSweep @if nimHasWarningObservableStores: - warning[ObservableStores]:off -@end - -@if nimHasWarnCastSizes: - warning[CastSizes]:on -@end - -@if nimHasWarningAsError: - warningAsError[GcUnsafe2]:on -@end - -@if nimHasWarnUnnamedBreak: - warningAserror[UnnamedBreak]:on -@end - -@if nimHasWarnBareExcept: - warningAserror[BareExcept]:on + warning[ObservableStores]: off @end diff --git a/compiler/nim.nim b/compiler/nim.nim index 420579b97..46654e352 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -7,31 +7,24 @@ # distribution, for details about the copyright. # -import std/[os, strutils, parseopt] +when defined(gcc) and defined(windows): + when defined(x86): + {.link: "../icons/nim.res".} + else: + {.link: "../icons/nim_icon.o".} -when defined(nimPreviewSlimSystem): - import std/assertions - -when defined(windows) and not defined(nimKochBootstrap): - # remove workaround pending bootstrap >= 1.5.1 - # refs https://github.com/nim-lang/Nim/issues/18334#issuecomment-867114536 - # alternative would be to prepend `currentSourcePath.parentDir.quoteShell` - when defined(gcc): - when defined(x86): - {.link: "../icons/nim.res".} - else: - {.link: "../icons/nim_icon.o".} - - when defined(amd64) and defined(vcc): - {.link: "../icons/nim-amd64-windows-vcc.res".} - when defined(i386) and defined(vcc): - {.link: "../icons/nim-i386-windows-vcc.res".} +when defined(amd64) and defined(windows) and defined(vcc): + {.link: "../icons/nim-amd64-windows-vcc.res".} +when defined(i386) and defined(windows) and defined(vcc): + {.link: "../icons/nim-i386-windows-vcc.res".} import - commands, options, msgs, extccomp, main, idents, lineinfos, cmdlinehelper, + commands, options, msgs, + extccomp, strutils, os, main, parseopt, + idents, lineinfos, cmdlinehelper, pathutils, modulegraphs -from browsers import openDefaultBrowser +from std/browsers import openDefaultBrowser from nodejs import findNodeJs when hasTinyCBackend: @@ -41,15 +34,6 @@ when defined(profiler) or defined(memProfiler): {.hint: "Profiling support is turned on!".} import nimprof -proc nimbleLockExists(config: ConfigRef): bool = - const nimbleLock = "nimble.lock" - let pd = if not config.projectPath.isEmpty: config.projectPath else: AbsoluteDir(getCurrentDir()) - if optSkipParentConfigFiles notin config.globalOptions: - for dir in parentDirs(pd.string, fromRoot=true, inclusive=false): - if fileExists(dir / nimbleLock): - return true - return fileExists(pd.string / nimbleLock) - proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = var p = parseopt.initOptParser(cmd) var argsCount = 0 @@ -83,18 +67,6 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript, cmdCrun}: rawMessage(config, errGenerated, errArgsNeedRunOption) - if config.nimbleLockExists: - # disable nimble path if nimble.lock is present. - # see https://github.com/nim-lang/nimble/issues/1004 - disableNimblePath(config) - -proc getNimRunExe(conf: ConfigRef): string = - # xxx consider defining `conf.getConfigVar("nimrun.exe")` to allow users to - # customize the binary to run the command with, e.g. for custom `nodejs` or `wine`. - if conf.isDefined("mingw"): - if conf.isDefined("i386"): result = "wine" - elif conf.isDefined("amd64"): result = "wine64" - proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let self = NimProg( supportsStdinFile: true, @@ -106,19 +78,9 @@ 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.cmd == cmdCheck and optWasNimscript notin conf.globalOptions and - conf.backend == backendInvalid: - conf.backend = backendC - - 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()) @@ -130,23 +92,13 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let output = conf.absOutFile case conf.cmd of cmdBackends, cmdTcc: - let nimRunExe = getNimRunExe(conf) var cmdPrefix = "" - if nimRunExe.len > 0: cmdPrefix.add nimRunExe.quoteShell case conf.backend of backendC, backendCpp, backendObjc: discard - of backendJs: - # D20210217T215950:here this flag is needed for node < v15.0.0, otherwise - # tasyncjs_fail` would fail, refs https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode - if cmdPrefix.len == 0: cmdPrefix = findNodeJs().quoteShell - cmdPrefix.add " --unhandled-rejections=strict" + of backendJs: cmdPrefix = findNodeJs() & " " else: doAssert false, $conf.backend - if cmdPrefix.len > 0: cmdPrefix.add " " - # without the `cmdPrefix.len > 0` check, on windows you'd get a cryptic: - # `The parameter is incorrect` - let cmd = cmdPrefix & output.quoteShell & ' ' & conf.arguments - execExternalProgram(conf, cmd.strip(leading=false,trailing=true)) - of cmdDocLike, cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex: # bugfix(cmdRst2tex was missing) + execExternalProgram(conf, cmdPrefix & output.quoteShell & ' ' & conf.arguments) + of cmdDocLike, cmdRst2html, cmdRst2tex: # bugfix(cmdRst2tex was missing) if conf.arguments.len > 0: # reserved for future use rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd]) @@ -158,7 +110,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = when declared(GC_setMaxPause): GC_setMaxPause 2_000 -when compileOption("gc", "refc"): +when compileOption("gc", "v2") or compileOption("gc", "refc"): # the new correct mark&sweet collector is too slow :-/ GC_disableMarkAndSweep() diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index b6b08ccd4..28398289c 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -9,11 +9,8 @@ ## Implements some helper procs for Nimble (Nim's package manager) support. -import parseutils, strutils, os, options, msgs, sequtils, lineinfos, pathutils, - std/sha1, tables - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] +import parseutils, strutils, strtabs, os, options, msgs, sequtils, + lineinfos, pathutils proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) = if not conf.searchPaths.contains(path): @@ -21,7 +18,6 @@ proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) = type Version* = distinct string - PackageInfo = Table[string, tuple[version, checksum: string]] proc `$`*(ver: Version): string {.borrow.} @@ -66,64 +62,43 @@ proc `<`*(ver: Version, ver2: Version): bool = else: return false -proc getPathVersionChecksum*(p: string): tuple[name, version, checksum: string] = - ## Splits path ``p`` in the format - ## ``/home/user/.nimble/pkgs/package-0.1-febadeaea2345e777f0f6f8433f7f0a52edd5d1b`` into - ## ``("/home/user/.nimble/pkgs/package", "0.1", "febadeaea2345e777f0f6f8433f7f0a52edd5d1b")`` +proc getPathVersion*(p: string): tuple[name, version: string] = + ## Splits path ``p`` in the format ``/home/user/.nimble/pkgs/package-0.1`` + ## into ``(/home/user/.nimble/pkgs/package, 0.1)`` + result.name = "" + result.version = "" - const checksumSeparator = '-' - const versionSeparator = '-' - const specialVersionSepartator = "-#" - const separatorNotFound = -1 + const specialSeparator = "-#" + let last = p.rfind(p.lastPathPart) # the index where the last path part begins + var sepIdx = p.find(specialSeparator, start = last) + if sepIdx == -1: + sepIdx = p.rfind('-', start = last) - var checksumSeparatorIndex = p.rfind(checksumSeparator) - if checksumSeparatorIndex != separatorNotFound: - result.checksum = p.substr(checksumSeparatorIndex + 1) - if not result.checksum.isValidSha1Hash(): - result.checksum = "" - checksumSeparatorIndex = p.len() - else: - checksumSeparatorIndex = p.len() + if sepIdx == -1: + result.name = p + return - var versionSeparatorIndex = p.rfind( - specialVersionSepartator, 0, checksumSeparatorIndex - 1) - if versionSeparatorIndex != separatorNotFound: - result.version = p.substr( - versionSeparatorIndex + 1, checksumSeparatorIndex - 1) - else: - versionSeparatorIndex = p.rfind( - versionSeparator, 0, checksumSeparatorIndex - 1) - if versionSeparatorIndex != separatorNotFound: - result.version = p.substr( - versionSeparatorIndex + 1, checksumSeparatorIndex - 1) - else: - versionSeparatorIndex = checksumSeparatorIndex + for i in sepIdx.. 0 and (b.hasReturn or b.hasBreak): discard "cannot optimize away the destructor" else: c.wasMovedDestroyPair b, n special = true - elif name == "=sink": + elif s.name.s == "=sink": reverse = true if not special: @@ -130,11 +127,11 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) = for i in 0 ..< n.len: analyse(c, b, n[i]) else: - #[ Test destructor/tmatrix.test3: + #[ Test tmatrix.test3: Prevent this from being elided. We should probably find a better solution... - `=sink`(b, - + `=sink`(b, - ( let blitTmp = b; wasMoved(b); blitTmp + a) @@ -153,11 +150,10 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) = of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, - nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, - nkTypeOfExpr, nkMixinStmt, nkBindStmt: + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: discard "do not follow the construct" - of nkAsgn, nkFastAsgn, nkSinkAsgn: + of nkAsgn, nkFastAsgn: # reverse order, see remark for `=sink`: analyse(c, b, n[1]) analyse(c, b, n[0]) @@ -181,7 +177,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, tyCstring} or + abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString} or n[^1].kind == nkElse analyse(c, b, n[0]) @@ -253,8 +249,7 @@ proc opt(c: Con; n, parent: PNode; parentPos: int) = of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, - nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr, - nkMixinStmt, nkBindStmt: + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: parent[parentPos] = n else: diff --git a/compiler/options.nim b/compiler/options.nim index 51a8ff6f0..4a013f8fc 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -13,20 +13,13 @@ import from terminal import isatty from times import utc, fromUnix, local, getTime, format, DateTime -from std/private/globs import nativeToUnixPath - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - const hasTinyCBackend* = defined(tinyc) useEffectSystem* = true useWriteTracking* = false hasFFI* = defined(nimHasLibFFI) - copyrightYear* = "2023" - - nimEnableCovariance* = defined(nimEnableCovariance) + copyrightYear* = "2020" type # please make sure we have under 32 options # (improves code efficiency a lot!) @@ -46,12 +39,13 @@ type # please make sure we have under 32 options # evaluation optTrMacros, # en/disable pattern matching optMemTracker, + optNilSeqs, optSinkInference # 'sink T' inference optCursorInference - optImportHidden + TOptions* = set[TOption] - TGlobalOption* = enum + TGlobalOption* = enum # **keep binary compatible** gloptNone, optForceFullMake, optWasNimscript, # redundant with `cmdNimscript`, could be removed optListCmd, optCompileOnly, optNoLinking, @@ -60,13 +54,11 @@ type # please make sure we have under 32 options optGenStaticLib, # generate a static library optGenGuiApp, # generate a GUI application optGenScript, # generate a script file to compile the *.c files - optGenCDeps, # generate a list of *.c files to be read by CMake optGenMapping, # generate a mapping file optRun, # run the compiled project optUseNimcache, # save artifacts (including binary) in $nimcache optStyleHint, # check that the names adhere to NEP-1 optStyleError, # enforce that the names adhere to NEP-1 - optStyleUsages, # only enforce consistent **usages** of the symbol optSkipSystemConfigFile, # skip the system's cfg/nims config file optSkipProjConfigFile, # skip the project's cfg/nims config file optSkipUserConfigFile, # skip the users's cfg/nims config file @@ -76,10 +68,9 @@ type # please make sure we have under 32 options optThreads, # support for multi-threading optStdout, # output to stdout optThreadAnalysis, # thread analysis pass + optTaintMode, # taint mode turned on optTlsEmulation, # thread var emulation turned on optGenIndex # generate index file for documentation; - optGenIndexOnly # generate only index file for documentation - optNoImportdoc # disable loading external documentation files optEmbedOrigSrc # embed the original source in the generated code # also: generate header file optIdeDebug # idetools: debug mode @@ -89,6 +80,7 @@ type # please make sure we have under 32 options optWholeProject # for 'doc': output any dependency optDocInternal # generate documentation for non-exported symbols optMixedMode # true if some module triggered C++ codegen + optListFullPaths # use full paths in toMsgFilename optDeclaredLocs # show declaration locations in messages optNoNimblePath optHotCodeReloading @@ -104,32 +96,15 @@ type # please make sure we have under 32 options optPanics # turn panics (sysFatal) into a process termination optNimV1Emulation # emulate Nim v1.0 optNimV12Emulation # emulate Nim v1.2 - optNimV16Emulation # emulate Nim v1.6 optSourcemap optProfileVM # enable VM profiler optEnableDeepCopy # ORC specific: enable 'deepcopy' for all types. - optShowNonExportedFields # for documentation: show fields that are not exported TGlobalOptions* = set[TGlobalOption] const - harmlessOptions* = {optForceFullMake, optNoLinking, optRun, optUseColors, optStdout} - genSubDir* = RelativeDir"nimcache" - NimExt* = "nim" - RodExt* = "rod" - HtmlExt* = "html" - JsonExt* = "json" - TagsExt* = "tags" - TexExt* = "tex" - IniExt* = "ini" - DefaultConfig* = RelativeFile"nim.cfg" - DefaultConfigNims* = RelativeFile"config.nims" - DocConfig* = RelativeFile"nimdoc.cfg" - DocTexConfig* = RelativeFile"nimdoc.tex.cfg" - htmldocsDir* = htmldocsDirname.RelativeDir - docRootDefault* = "@default" # using `@` instead of `$` to avoid shell quoting complications - oKeepVariableNames* = true - spellSuggestSecretSauce* = -1 + harmlessOptions* = {optForceFullMake, optNoLinking, optRun, + optUseColors, optStdout} type TBackend* = enum @@ -149,16 +124,13 @@ type cmdTcc # run the project via TCC backend cmdCheck # semantic checking for whole project cmdParse # parse a single file (for debugging) - cmdRod # .rod to some text representation (for debugging) + cmdScan # scan a single file (for debugging) cmdIdeTools # ide tools (e.g. nimsuggest) cmdNimscript # evaluate nimscript cmdDoc0 - cmdDoc # convert .nim doc comments to HTML - cmdDoc2tex # convert .nim doc comments to LaTeX + cmdDoc2 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 @@ -173,32 +145,22 @@ type const cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun} - cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc, - cmdCtags, cmdBuildindex} + cmdDocLike* = {cmdDoc0, cmdDoc2, cmdJsondoc0, cmdJsondoc, cmdCtags, cmdBuildindex} type - NimVer* = tuple[major: int, minor: int, patch: int] TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcUnselected = "unselected" - gcNone = "none" - gcBoehm = "boehm" - gcRegions = "regions" - gcArc = "arc" - gcOrc = "orc" - gcMarkAndSweep = "markAndSweep" - gcHooks = "hooks" - gcRefc = "refc" - gcGo = "go" + gcUnselected, gcNone, gcBoehm, gcRegions, gcArc, gcOrc, + gcMarkAndSweep, gcHooks, gcRefc, gcV2, gcGo # 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, ideChkFile, ideMod, - ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols, - ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand + ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, + ideHighlight, ideOutline, ideKnown, ideMsg, ideProject Feature* = enum ## experimental features; DO NOT RENAME THESE! + implicitDeref, dotOperators, callOperator, parallel, @@ -206,7 +168,7 @@ type notnil, dynamicBindSym, forLoopMacros, # not experimental anymore; remains here for backwards compatibility - caseStmtMacros, # ditto + caseStmtMacros, codeReordering, compiletimeFFI, ## This requires building nim with `-d:nimHasLibFFI` @@ -215,13 +177,7 @@ type vmopsDanger, strictFuncs, views, - strictNotNil, - overloadableEnums, # deadcode - strictEffects, - unicodeOperators, # deadcode - flexibleOptionalParams, - strictDefs, - strictCaseObjects + strictNotNil LegacyFeature* = enum allowSemcheckedAstModification, @@ -232,12 +188,9 @@ type ## Historically and especially in version 1.0.0 of the language ## conversions to unsigned numbers were checked. In 1.0.4 they ## are not anymore. - laxEffects - ## Lax effects system prior to Nim 2.0. - verboseTypeMismatch SymbolFilesOption* = enum - disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest + disabledSf, writeOnlySf, readOnlySf, v2Sf TSystemCC* = enum ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc, @@ -279,9 +232,6 @@ type scope*, localUsages*, globalUsages*: int # more usages is better tokenLen*: int version*: int - endLine*: uint16 - endCol*: int - Suggestions* = seq[Suggest] ProfileInfo* = object @@ -295,15 +245,7 @@ type stdOrrStdout stdOrrStderr - FilenameOption* = enum - foAbs # absolute path, e.g.: /pathto/bar/foo.nim - foRelProject # relative to project path, e.g.: ../foo.nim - foCanonical # canonical module name - foLegacyRelProj # legacy, shortest of (foAbs, foRelProject) - foName # lastPathPart, e.g.: foo.nim - foStacktrace # if optExcessiveStackTrace: foAbs else: foName - - ConfigRef* {.acyclic.} = ref object ## every global configuration + ConfigRef* = ref object ## every global configuration ## fields marked with '*' are subject to ## the incremental compilation mechanisms ## (+) means "part of the dependency" @@ -315,8 +257,6 @@ type macrosToExpand*: StringTableRef arcToExpand*: StringTableRef m*: MsgConfig - filenameOption*: FilenameOption # how to render paths in compiler messages - unitSep*: string evalTemplateCounter*: int evalMacroCounter*: int exitcode*: int8 @@ -326,12 +266,10 @@ type implicitCmd*: bool # whether some flag triggered an implicit `command` selectedGC*: TGCMode # the selected GC (+) exc*: ExceptionSystem - hintProcessingDots*: bool # true for dots, false for filenames verbosity*: int # how verbose the compiler is numberOfProcessors*: int # number of processors lastCmdTime*: float # when caas is enabled, we measure each command symbolFiles*: SymbolFilesOption - spellSuggestMax*: int # max number of spelling suggestions for typos cppDefines*: HashSet[string] # (*) headerFile*: string @@ -341,7 +279,7 @@ type ## should be run ideCmd*: IdeCmd oldNewlines*: bool - cCompiler*: TSystemCC # the used compiler + cCompiler*: TSystemCC modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline foreignPackageNotes*: TNoteKinds @@ -354,7 +292,6 @@ type warnCounter*: int errorMax*: int maxLoopIterationsVM*: int ## VM: max iterations of all loops - isVmTrace*: bool configVars*: StringTableRef symbols*: StringTableRef ## We need to use a StringTableRef here as defined ## symbols are always guaranteed to be style @@ -367,7 +304,6 @@ type outDir*: AbsoluteDir jsonBuildFile*: AbsoluteFile prefixDir*, libpath*, nimcacheDir*: AbsoluteDir - nimStdlibVersion*: NimVer dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef projectName*: string # holds a name like 'nim' projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/ @@ -388,7 +324,7 @@ type docRoot*: string ## see nim --fullhelp for --docRoot docCmd*: string ## see nim --fullhelp for --docCmd - configFiles*: seq[AbsoluteFile] # config files (cfg,nims) + # the used compiler cIncludes*: seq[AbsoluteDir] # directories to search for included files cLibs*: seq[AbsoluteDir] # directories to search for lib files cLinkedLibs*: seq[string] # libraries to link @@ -405,28 +341,12 @@ type suggestVersion*: int suggestMaxResults*: int lastLineInfo*: TLineInfo - writelnHook*: proc (output: string) {.closure, gcsafe.} + writelnHook*: proc (output: string) {.closure.} # cannot make this gcsafe yet because of Nimble structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string; severity: Severity) {.closure, gcsafe.} cppCustomNamespace*: string - nimMainPrefix*: string vmProfileData*: ProfileData - expandProgress*: bool - expandLevels*: int - expandNodeResult*: string - expandPosition*: TLineInfo - -proc parseNimVersion*(a: string): NimVer = - # could be moved somewhere reusable - if a.len > 0: - let b = a.split(".") - assert b.len == 3, a - template fn(i) = result[i] = b[i].parseInt # could be optimized if needed - fn(0) - fn(1) - fn(2) - proc assignIfDefault*[T](result: var T, val: T, def = default(T)) = ## if `result` was already assigned to a value (that wasn't `def`), this is a noop. if result == def: result = val @@ -456,19 +376,18 @@ proc hasHint*(conf: ConfigRef, note: TNoteKind): bool = note in conf.mainPackageNotes else: note in conf.notes -proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool {.inline.} = +proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool = optWarns in conf.options and note in conf.notes proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions -when false: - template depConfigFields*(fn) {.dirty.} = # deadcode - fn(target) - fn(options) - fn(globalOptions) - fn(selectedGC) +template depConfigFields*(fn) {.dirty.} = + fn(target) + fn(options) + fn(globalOptions) + fn(selectedGC) -const oldExperimentalFeatures* = {dotOperators, callOperator, parallel} +const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} const ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, @@ -479,7 +398,8 @@ const optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck, optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs` optTrMacros, optStyleCheck, optCursorInference} - DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace} + DefaultGlobalOptions* = {optThreadAnalysis, + optExcessiveStackTrace, optListFullPaths} proc getSrcTimestamp(): DateTime = try: @@ -508,36 +428,21 @@ template newPackageCache*(): untyped = proc newProfileData(): ProfileData = ProfileData(data: newTable[TLineInfo, ProfileInfo]()) -const foreignPackageNotesDefault* = { - hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting, hintUser, warnUser} - -proc isDefined*(conf: ConfigRef; symbol: string): bool - -when defined(nimDebugUtils): - # this allows inserting debugging utilties in all modules that import `options` - # with a single switch, which is useful when debugging compiler. - import debugutils - export debugutils - -proc initConfigRefCommon(conf: ConfigRef) = - conf.selectedGC = gcUnselected - conf.verbosity = 1 - conf.hintProcessingDots = true - conf.options = DefaultOptions - conf.globalOptions = DefaultGlobalOptions - conf.filenameOption = foAbs - conf.foreignPackageNotes = foreignPackageNotesDefault - conf.notes = NotesVerbosity[1] - conf.mainPackageNotes = NotesVerbosity[1] +const foreignPackageNotesDefault* = {hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting} proc newConfigRef*(): ConfigRef = result = ConfigRef( + selectedGC: gcRefc, cCompiler: ccGcc, + verbosity: 1, + options: DefaultOptions, + globalOptions: DefaultGlobalOptions, macrosToExpand: newStringTable(modeStyleInsensitive), arcToExpand: newStringTable(modeStyleInsensitive), m: initMsgConfig(), cppDefines: initHashSet[string](), - headerFile: "", features: {}, legacyFeatures: {}, + headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: foreignPackageNotesDefault, + notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1], configVars: newStringTable(modeStyleInsensitive), symbols: newStringTable(modeStyleInsensitive), packageCache: newPackageCache(), @@ -577,33 +482,25 @@ proc newConfigRef*(): ConfigRef = suggestMaxResults: 10_000, maxLoopIterationsVM: 10_000_000, vmProfileData: newProfileData(), - spellSuggestMax: spellSuggestSecretSauce, ) - initConfigRefCommon(result) setTargetFromSystem(result.target) # enable colors by default on terminals if terminal.isatty(stderr): incl(result.globalOptions, optUseColors) - when defined(nimDebugUtils): - onNewConfigRef(result) proc newPartialConfigRef*(): ConfigRef = ## create a new ConfigRef that is only good enough for error reporting. - when defined(nimDebugUtils): - result = getConfigRef() - else: - result = ConfigRef() - initConfigRefCommon(result) + result = ConfigRef( + selectedGC: gcRefc, + verbosity: 1, + options: DefaultOptions, + globalOptions: DefaultGlobalOptions, + foreignPackageNotes: foreignPackageNotesDefault, + notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1]) proc cppDefine*(c: ConfigRef; define: string) = c.cppDefines.incl define -proc getStdlibVersion*(conf: ConfigRef): NimVer = - if conf.nimStdlibVersion == (0,0,0): - let s = conf.symbols.getOrDefault("nimVersion", "") - conf.nimStdlibVersion = s.parseNimVersion - result = conf.nimStdlibVersion - proc isDefined*(conf: ConfigRef; symbol: string): bool = if conf.symbols.hasKey(symbol): result = true @@ -621,13 +518,11 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osQnx, osAtari, osAix, osHaiku, osVxWorks, osSolaris, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos, - osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos, osZephyr, osNuttX} + osAndroid, osNintendoSwitch, osFreeRTOS} of "linux": result = conf.target.targetOS in {osLinux, osAndroid} of "bsd": - result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos} - of "freebsd": - result = conf.target.targetOS in {osFreebsd, osCrossos} + result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly} of "emulatedthreadvars": result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars) of "msdos": result = conf.target.targetOS == osDos @@ -639,14 +534,12 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = of "sunos": result = conf.target.targetOS == osSolaris of "nintendoswitch": result = conf.target.targetOS == osNintendoSwitch - of "freertos", "lwip": + of "freertos": result = conf.target.targetOS == osFreeRTOS - of "zephyr": - result = conf.target.targetOS == osZephyr - of "nuttx": - result = conf.target.targetOS == osNuttX - of "littleendian": result = CPU[conf.target.targetCPU].endian == littleEndian - of "bigendian": result = CPU[conf.target.targetCPU].endian == bigEndian + of "lwip": + result = conf.target.targetOS in {osFreeRTOS} + of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian + of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian of "cpu8": result = CPU[conf.target.targetCPU].bit == 8 of "cpu16": result = CPU[conf.target.targetCPU].bit == 16 of "cpu32": result = CPU[conf.target.targetCPU].bit == 32 @@ -656,13 +549,6 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osDragonfly, osMacosx} else: discard -template quitOrRaise*(conf: ConfigRef, msg = "") = - # xxx in future work, consider whether to also intercept `msgQuit` calls - if conf.isDefined("nimDebug"): - doAssert false, msg - else: - quit(msg) # quits with QuitFailure - proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools} proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc @@ -673,6 +559,23 @@ template compilationCachePresent*(conf: ConfigRef): untyped = template optPreserveOrigSource*(conf: ConfigRef): untyped = optEmbedOrigSrc in conf.globalOptions +const + genSubDir* = RelativeDir"nimcache" + NimExt* = "nim" + RodExt* = "rod" + HtmlExt* = "html" + JsonExt* = "json" + TagsExt* = "tags" + TexExt* = "tex" + IniExt* = "ini" + DefaultConfig* = RelativeFile"nim.cfg" + DefaultConfigNims* = RelativeFile"config.nims" + DocConfig* = RelativeFile"nimdoc.cfg" + DocTexConfig* = RelativeFile"nimdoc.tex.cfg" + htmldocsDir* = htmldocsDirname.RelativeDir + docRootDefault* = "@default" # using `@` instead of `$` to avoid shell quoting complications + oKeepVariableNames* = true + proc mainCommandArg*(conf: ConfigRef): string = ## This is intended for commands like check or parse ## which will work on the main project file unless @@ -719,24 +622,22 @@ proc getPrefixDir*(conf: ConfigRef): AbsoluteDir = ## clone or using installed nim, so that these exist: `result/doc/advopt.txt` ## and `result/lib/system.nim` if not conf.prefixDir.isEmpty: result = conf.prefixDir - else: - let binParent = AbsoluteDir splitPath(getAppDir()).head - when defined(posix): - if binParent == AbsoluteDir"/usr": - result = AbsoluteDir"/usr/lib/nim" - elif binParent == AbsoluteDir"/usr/local": - result = AbsoluteDir"/usr/local/lib/nim" - else: - result = binParent - else: - result = binParent + else: result = AbsoluteDir splitPath(getAppDir()).head proc setDefaultLibpath*(conf: ConfigRef) = # set default value (can be overwritten): if conf.libpath.isEmpty: # choose default libpath: var prefix = getPrefixDir(conf) - conf.libpath = prefix / RelativeDir"lib" + when defined(posix): + if prefix == AbsoluteDir"/usr": + conf.libpath = AbsoluteDir"/usr/lib/nim" + elif prefix == AbsoluteDir"/usr/local": + conf.libpath = AbsoluteDir"/usr/local/lib/nim" + else: + conf.libpath = prefix / RelativeDir"lib" + else: + conf.libpath = prefix / RelativeDir"lib" # Special rule to support other tools (nimble) which import the compiler # modules and make use of them. @@ -750,16 +651,6 @@ proc setDefaultLibpath*(conf: ConfigRef) = proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = result = AbsoluteFile path.string.expandFilename -proc setFromProjectName*(conf: ConfigRef; projectName: string) = - try: - conf.projectFull = canonicalizePath(conf, AbsoluteFile projectName) - except OSError: - conf.projectFull = AbsoluteFile projectName - let p = splitFile(conf.projectFull) - let dir = if p.dir.isEmpty: AbsoluteDir getCurrentDir() else: p.dir - conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir) - conf.projectName = p.name - proc removeTrailingDirSep*(path: string): string = if (path.len > 0) and (path[^1] == DirSep): result = substr(path, 0, path.len - 2) @@ -784,22 +675,14 @@ proc getOsCacheDir(): string = result = getHomeDir() / genSubDir.string proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = - proc nimcacheSuffix(conf: ConfigRef): string = - if conf.cmd == cmdCheck: "_check" - elif isDefined(conf, "release") or isDefined(conf, "danger"): "_r" - else: "_d" - # XXX projectName should always be without a file extension! result = if not conf.nimcacheDir.isEmpty: conf.nimcacheDir elif conf.backend == backendJs: - if conf.outDir.isEmpty: - conf.projectPath / genSubDir - else: - conf.outDir / genSubDir + conf.projectPath / genSubDir else: AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name & - nimcacheSuffix(conf)) + (if isDefined(conf, "release") or isDefined(conf, "danger"): "_r" else: "_d")) proc pathSubs*(conf: ConfigRef; p, config: string): string = let home = removeTrailingDirSep(os.getHomeDir()) @@ -829,15 +712,19 @@ 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: createDir(subdir.string) except OSError: - conf.quitOrRaise "cannot create directory: " & subdir.string + writeLine(stdout, "cannot create directory: " & subdir.string) + quit(1) result = subdir / RelativeFile f.string.splitPath.tail + #echo "completeGeneratedFilePath(", f, ") = ", result + +proc toRodFile*(conf: ConfigRef; f: AbsoluteFile): AbsoluteFile = + result = changeFileExt(completeGeneratedFilePath(conf, + withPackageName(conf, f)), RodExt) proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile = for it in conf.searchPaths: @@ -874,26 +761,8 @@ when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo): let ret = relativePath(path, base) result = path.len > 0 and not ret.startsWith ".." -const stdlibDirs* = [ - "pure", "core", "arch", - "pure/collections", - "pure/concurrency", - "pure/unidecode", "impure", - "wrappers", "wrappers/linenoise", - "windows", "posix", "js", - "deprecated/pure"] - -const - pkgPrefix = "pkg/" - stdPrefix = "std/" - -proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile, isTitle = false): RelativeFile = +proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile): RelativeFile = let f = $f - if isTitle: - for dir in stdlibDirs: - let path = conf.libpath.string / dir / f.lastPathPart - if path.cmpPaths(f) == 0: - return RelativeFile(stdPrefix & f.splitFile.name) template search(paths) = for it in paths: let it = $it @@ -915,8 +784,18 @@ proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile result = rawFindFile2(conf, RelativeFile f.toLowerAscii) patchModule(conf) +const stdlibDirs = [ + "pure", "core", "arch", + "pure/collections", + "pure/concurrency", + "pure/unidecode", "impure", + "wrappers", "wrappers/linenoise", + "windows", "posix", "js"] + proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile = # returns path to module + const pkgPrefix = "pkg/" + const stdPrefix = "std/" var m = addFileExt(modulename, NimExt) if m.startsWith(pkgPrefix): result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true) @@ -926,11 +805,10 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFi for candidate in stdlibDirs: let path = (conf.libpath.string / candidate / stripped) if fileExists(path): - result = AbsoluteFile path + m = path break - else: # If prefixed with std/ why would we add the current module path! - let currentPath = currentModule.splitFile.dir - result = AbsoluteFile currentPath / m + let currentPath = currentModule.splitFile.dir + result = AbsoluteFile currentPath / m if not fileExists(result): result = findFile(conf, m) patchModule(conf) @@ -975,25 +853,6 @@ proc findProjectNimFile*(conf: ConfigRef; pkg: string): string = if dir == "": break return "" -proc canonicalImportAux*(conf: ConfigRef, file: AbsoluteFile): string = - ##[ - Shows the canonical module import, e.g.: - system, std/tables, fusion/pointers, system/assertions, std/private/asciitables - ]## - var ret = getRelativePathFromConfigPath(conf, file, isTitle = true) - let dir = getNimbleFile(conf, $file).parentDir.AbsoluteDir - if not dir.isEmpty: - let relPath = relativeTo(file, dir) - if not relPath.isEmpty and (ret.isEmpty or relPath.string.len < ret.string.len): - ret = relPath - if ret.isEmpty: - ret = relativeTo(file, conf.projectPath) - result = ret.string - -proc canonicalImport*(conf: ConfigRef, file: AbsoluteFile): string = - let ret = canonicalImportAux(conf, file) - result = ret.nativeToUnixPath.changeFileExt("") - proc canonDynlibName(s: string): string = let start = if s.startsWith("lib"): 3 else: 0 let ende = strutils.find(s, {'(', ')', '.'}) @@ -1009,12 +868,6 @@ proc isDynlibOverride*(conf: ConfigRef; lib: string): bool = result = optDynlibOverrideAll in conf.globalOptions or conf.dllOverrides.hasKey(lib.canonDynlibName) -proc showNonExportedFields*(conf: ConfigRef) = - incl(conf.globalOptions, optShowNonExportedFields) - -proc expandDone*(conf: ConfigRef): bool = - result = conf.ideCmd == ideExpand and conf.expandLevels == 0 and conf.expandProgress - proc parseIdeCmd*(s: string): IdeCmd = case s: of "sug": ideSug @@ -1023,17 +876,12 @@ 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 = @@ -1044,7 +892,6 @@ 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" @@ -1052,12 +899,6 @@ proc `$`*(c: IdeCmd): string = of ideKnown: "known" of ideMsg: "msg" of ideProject: "project" - of ideGlobalSymbols: "globalSymbols" - of ideDeclaration: "declaration" - of ideExpand: "expand" - of ideRecompile: "recompile" - of ideChanged: "changed" - of ideType: "type" proc floatInt64Align*(conf: ConfigRef): int16 = ## Returns either 4 or 8 depending on reasons. @@ -1068,3 +909,18 @@ proc floatInt64Align*(conf: ConfigRef): int16 = # to 4bytes (except with -malign-double) return 4 return 8 + +proc setOutFile*(conf: ConfigRef) = + proc libNameTmpl(conf: ConfigRef): string {.inline.} = + result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a" + + if conf.outFile.isEmpty: + let base = conf.projectName + let targetName = + if optGenDynLib in conf.globalOptions: + platform.OS[conf.target.targetOS].dllFrmt % base + elif optGenStaticLib in conf.globalOptions: + libNameTmpl(conf) % base + else: + base & platform.OS[conf.target.targetOS].exeExt + conf.outFile = RelativeFile targetName diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 8cf209779..a781f1d51 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -37,7 +37,23 @@ 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) - if path.len > 0: - return path.splitFile.name + 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) else: - return "unknown" + result = p / RelativeFile(fakePackageName(conf, path)) diff --git a/compiler/packages.nim b/compiler/packages.nim deleted file mode 100644 index d8b97e374..000000000 --- a/compiler/packages.nim +++ /dev/null @@ -1,53 +0,0 @@ -# -# -# 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 534c3b5d1..eb99004ab 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -143,11 +143,8 @@ proc checkForSideEffects*(n: PNode): TSideEffectAnalysis = let s = op.sym if sfSideEffect in s.flags: return seSideEffect - elif tfNoSideEffect in op.typ.flags: - result = seNoSideEffect - else: - # assume side effect: - result = seSideEffect + # assume no side effect: + result = seNoSideEffect elif tfNoSideEffect in op.typ.flags: # indirect call without side effects: result = seNoSideEffect @@ -179,11 +176,10 @@ type arLocalLValue, # is an l-value, but local var; must not escape # its stack frame! arDiscriminant, # is a discriminant - arAddressableConst, # an addressable const arLentValue, # lent value arStrange # it is a strange beast like 'typedesc[var T]' -proc exprRoot*(n: PNode; allowCalls = true): PSym = +proc exprRoot*(n: PNode): PSym = var it = n while true: case it.kind @@ -204,7 +200,7 @@ proc exprRoot*(n: PNode; allowCalls = true): PSym = if it.len > 0 and it.typ != nil: it = it.lastSon else: break of nkCallKinds: - if allowCalls and it.typ != nil and it.typ.kind in {tyVar, tyLent} and it.len > 1: + if it.typ != nil and it.typ.kind in {tyVar, tyLent} and it.len > 1: # See RFC #7373, calls returning 'var T' are assumed to # return a view into the first argument (if there is one): it = it[1] @@ -213,7 +209,7 @@ proc exprRoot*(n: PNode; allowCalls = true): PSym = else: break -proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = +proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult = ## 'owner' can be nil! result = arNone case n.kind @@ -221,20 +217,20 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = if n.typ != nil and n.typ.kind in {tyVar}: result = arLValue of nkSym: - 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.astdef): - result = arAddressableConst + let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet, skForVar} + else: {skVar, skResult, skTemp} + if n.sym.kind == skParam and n.sym.typ.kind in {tyVar, tySink}: + result = arLValue + elif isUnsafeAddr and n.sym.kind == skParam: + result = arLValue + elif isUnsafeAddr and n.sym.kind == skConst and dontInlineConstant(n, n.sym.ast): + result = arLValue elif n.sym.kind in kinds: - if n.sym.kind in {skParam, skLet, skForVar}: - result = arAddressableConst + if owner != nil and owner == n.sym.owner and + sfGlobal notin n.sym.flags: + result = arLocalLValue else: - if owner != nil and owner == n.sym.owner and - sfGlobal notin n.sym.flags: - result = arLocalLValue - else: - result = arLValue + result = arLValue elif n.sym.kind == skType: let t = n.sym.typ.skipTypes({tyTypeDesc}) if t.kind in {tyVar}: result = arStrange @@ -242,10 +238,10 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc}) if t.kind in {tyVar, tySink, tyPtr, tyRef}: result = arLValue - elif t.kind == tyLent: - result = arAddressableConst + elif isUnsafeAddr and t.kind == tyLent: + result = arLValue else: - result = isAssignable(owner, n[0]) + result = isAssignable(owner, n[0], isUnsafeAddr) if result != arNone and n[1].kind == nkSym and sfDiscriminant in n[1].sym.flags: result = arDiscriminant @@ -253,23 +249,23 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc}) if t.kind in {tyVar, tySink, tyPtr, tyRef}: result = arLValue - elif t.kind == tyLent: - result = arAddressableConst + elif isUnsafeAddr and t.kind == tyLent: + result = arLValue else: - result = isAssignable(owner, n[0]) + result = isAssignable(owner, n[0], isUnsafeAddr) of nkHiddenStdConv, nkHiddenSubConv, nkConv: # Object and tuple conversions are still addressable, so we skip them # XXX why is 'tyOpenArray' allowed here? if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in {tyOpenArray, tyTuple, tyObject}: - result = isAssignable(owner, n[1]) + result = isAssignable(owner, n[1], isUnsafeAddr) elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct): # types that are equal modulo distinction preserve l-value: - result = isAssignable(owner, n[1]) + result = isAssignable(owner, n[1], isUnsafeAddr) of nkHiddenDeref: let n0 = n[0] if n0.typ.kind == tyLent: - if n0.kind == nkSym and n0.sym.kind == skResult: + if isUnsafeAddr or (n0.kind == nkSym and n0.sym.kind == skResult): result = arLValue else: result = arLentValue @@ -278,19 +274,18 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = of nkDerefExpr, nkHiddenAddr: result = arLValue of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: - result = isAssignable(owner, n[0]) + result = isAssignable(owner, n[0], isUnsafeAddr) of nkCallKinds: # builtin slice keeps lvalue-ness: if getMagic(n) in {mArrGet, mSlice}: - result = isAssignable(owner, n[1]) - elif n.typ != nil: - case n.typ.kind - of tyVar: result = arLValue - of tyLent: result = arLentValue - else: discard + result = isAssignable(owner, n[1], isUnsafeAddr) + elif n.typ != nil and n.typ.kind in {tyVar}: + result = arLValue + elif isUnsafeAddr and n.typ != nil and n.typ.kind == tyLent: + result = arLValue of nkStmtList, nkStmtListExpr: if n.typ != nil: - result = isAssignable(owner, n.lastSon) + result = isAssignable(owner, n.lastSon, isUnsafeAddr) of nkVarTy: # XXX: The fact that this is here is a bit of a hack. # The goal is to allow the use of checks such as "foo(var T)" diff --git a/compiler/parser.nim b/compiler/parser.nim index abfc7b323..01ec6fe95 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -16,37 +16,18 @@ # In fact the grammar is generated from this file: -when isMainModule or defined(nimTestGrammar): +when isMainModule: # Leave a note in grammar.txt that it is generated: #| # This file is generated by compiler/parser.nim. - import std/pegs - when defined(nimPreviewSlimSystem): - import std/syncio + import pegs + var outp = open("doc/grammar.txt", fmWrite) + for line in lines("compiler/parser.nim"): + if line =~ peg" \s* '#| ' {.*}": + outp.write matches[0], "\L" + outp.close - proc writeGrammarFile(x: string) = - var outp = open(x, fmWrite) - for line in lines("compiler/parser.nim"): - if line =~ peg" \s* '#| ' {.*}": - outp.write matches[0], "\L" - outp.close - - when defined(nimTestGrammar): - import std/os - from ../testament/lib/stdtest/specialpaths import buildDir - const newGrammarText = buildDir / "grammar.txt" - - if not dirExists(buildDir): - createDir(buildDir) - - writeGrammarFile(newGrammarText) - - proc checkSameGrammar*() = - doAssert sameFileContent(newGrammarText, "doc/grammar.txt"), - "execute 'nim r compiler/parser.nim' to keep grammar.txt up-to-date" - else: - writeGrammarFile("doc/grammar.txt") - import ".." / tools / grammar_nanny - checkGrammarFile() + import ".." / tools / grammar_nanny + checkGrammarFile() import llstream, lexer, idents, strutils, ast, msgs, options, lineinfos, @@ -55,9 +36,6 @@ import when defined(nimpretty): import layouter -when defined(nimPreviewSlimSystem): - import std/assertions - type Parser* = object # A Parser object represents a file that # is being parsed @@ -66,9 +44,6 @@ type hasProgress: bool # some while loop requires progress ensurance lex*: Lexer # The lexer that is used for parsing tok*: Token # The current token - lineStartPrevious*: int - lineNumberPrevious*: int - bufposPrevious*: int inPragma*: int # Pragma level inSemiStmtList*: int emptyNode: PNode @@ -79,7 +54,7 @@ type smNormal, smAllowNil, smAfterDot PrimaryMode = enum - pmNormal, pmTypeDesc, pmTypeDef, pmTrySimple + pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix proc parseAll*(p: var Parser): PNode proc closeParser*(p: var Parser) @@ -102,7 +77,7 @@ proc eat*(p: var Parser, tokType: TokType) proc skipInd*(p: var Parser) proc optPar*(p: var Parser) proc optInd*(p: var Parser, n: PNode) -proc indAndComment*(p: var Parser, n: PNode, maybeMissEquals = false) +proc indAndComment*(p: var Parser, n: PNode) proc setBaseFlags*(n: PNode, base: NumericalBase) proc parseSymbol*(p: var Parser, mode = smNormal): PNode proc parseTry(p: var Parser; isExpr: bool): PNode @@ -125,9 +100,6 @@ template prettySection(body) = proc getTok(p: var Parser) = ## Get the next token from the parser's lexer, and store it in the parser's ## `tok` member. - p.lineNumberPrevious = p.lex.lineNumber - p.lineStartPrevious = p.lex.lineStart - p.bufposPrevious = p.lex.bufpos rawGetTok(p.lex, p.tok) p.hasProgress = true when defined(nimpretty): @@ -192,15 +164,15 @@ proc validInd(p: var Parser): bool {.inline.} = proc rawSkipComment(p: var Parser, node: PNode) = if p.tok.tokType == tkComment: if node != nil: - var rhs = node.comment + when not defined(nimNoNilSeqs): + if node.comment == nil: node.comment = "" when defined(nimpretty): if p.tok.commentOffsetB > p.tok.commentOffsetA: - rhs.add fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) + node.comment.add fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) else: - rhs.add p.tok.literal + node.comment.add p.tok.literal else: - rhs.add p.tok.literal - node.comment = move rhs + node.comment.add p.tok.literal else: parMessage(p, errInternal, "skipComment") getTok(p) @@ -253,13 +225,9 @@ proc parLineInfo(p: Parser): TLineInfo = ## Retrieve the line information associated with the parser's current state. result = getLineInfo(p.lex, p.tok) -proc indAndComment(p: var Parser, n: PNode, maybeMissEquals = false) = +proc indAndComment(p: var Parser, n: PNode) = if p.tok.indent > p.currInd: if p.tok.tokType == tkComment: rawSkipComment(p, n) - elif maybeMissEquals: - let col = p.bufposPrevious - p.lineStartPrevious - var info = newLineInfo(p.lex.fileIdx, p.lineNumberPrevious, col) - parMessage(p, "invalid indentation, maybe you forgot a '=' at $1 ?" % [p.lex.config$info]) else: parMessage(p, errInvalidIndentation) else: skipComment(p, n) @@ -286,8 +254,7 @@ proc newIdentNodeP(ident: PIdent, p: Parser): PNode = proc parseExpr(p: var Parser): PNode proc parseStmt(p: var Parser): PNode -proc parseTypeDesc(p: var Parser, fullExpr = false): PNode -proc parseTypeDefValue(p: var Parser): PNode +proc parseTypeDesc(p: var Parser): PNode proc parseParamList(p: var Parser, retColon = true): PNode proc isSigilLike(tok: Token): bool {.inline.} = @@ -298,17 +265,23 @@ proc isRightAssociative(tok: Token): bool {.inline.} = result = tok.tokType == tkOpr and tok.ident.s[0] == '^' # or (tok.ident.s.len > 1 and tok.ident.s[^1] == '>') +proc isOperator(tok: Token): bool = + ## Determines if the given token is an operator type token. + tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, + tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd, + tkOr, tkXor} + proc isUnary(tok: Token): bool = ## Check if the given token is a unary operator tok.tokType in {tkOpr, tkDotDot} and - tok.strongSpaceB == tsNone and - tok.strongSpaceA + tok.strongSpaceB == 0 and + tok.strongSpaceA > 0 proc checkBinary(p: Parser) {.inline.} = ## Check if the current parser token is a binary operator. # we don't check '..' here as that's too annoying if p.tok.tokType == tkOpr: - if p.tok.strongSpaceB == tsTrailing and not p.tok.strongSpaceA: + if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA == 0: parMessage(p, warnInconsistentSpacing, prettyTok(p.tok)) #| module = stmt ^* (';' / IND{=}) @@ -340,26 +313,12 @@ proc checkBinary(p: Parser) {.inline.} = #| mulExpr = dollarExpr (OP9 optInd dollarExpr)* #| dollarExpr = primary (OP10 optInd primary)* -proc isOperator(tok: Token): bool = - #| operatorB = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 | - #| 'div' | 'mod' | 'shl' | 'shr' | 'in' | 'notin' | - #| 'is' | 'isnot' | 'not' | 'of' | 'as' | 'from' | '..' | 'and' | 'or' | 'xor' - tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, - tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd, - tkOr, tkXor} - proc colcom(p: var Parser, n: PNode) = eat(p, tkColon) skipComment(p, n) const tkBuiltInMagics = {tkType, tkStatic, tkAddr} -template setEndInfo() = - when defined(nimsuggest): - result.endInfo = TLineInfo(fileIndex: p.lex.fileIdx, - line: p.lex.previousTokenEnd.line, - col: p.lex.previousTokenEnd.col) - proc parseSymbol(p: var Parser, mode = smNormal): PNode = #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| | IDENT | KEYW @@ -398,7 +357,7 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode = let node = newNodeI(nkIdent, lineinfo) node.ident = p.lex.cache.getIdent(accm) result.add(node) - of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCustomLit: + of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit: result.add(newIdentNodeP(p.lex.cache.getIdent($p.tok), p)) getTok(p) else: @@ -412,17 +371,6 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode = # if it is a keyword: #if not isKeyword(p.tok.tokType): getTok(p) result = p.emptyNode - setEndInfo() - -proc equals(p: var Parser, a: PNode): PNode = - if p.tok.tokType == tkEquals: - result = newNodeP(nkExprEqExpr, p) - getTok(p) - #optInd(p, result) - result.add(a) - result.add(parseExpr(p)) - else: - result = a proc colonOrEquals(p: var Parser, a: PNode): PNode = if p.tok.tokType == tkColon: @@ -432,8 +380,14 @@ proc colonOrEquals(p: var Parser, a: PNode): PNode = #optInd(p, result) result.add(a) result.add(parseExpr(p)) + elif p.tok.tokType == tkEquals: + result = newNodeP(nkExprEqExpr, p) + getTok(p) + #optInd(p, result) + result.add(a) + result.add(parseExpr(p)) else: - result = equals(p, a) + result = a proc exprColonEqExpr(p: var Parser): PNode = #| exprColonEqExpr = expr (':'|'=' expr)? @@ -443,14 +397,6 @@ proc exprColonEqExpr(p: var Parser): PNode = else: result = colonOrEquals(p, a) -proc exprEqExpr(p: var Parser): PNode = - #| exprEqExpr = expr ('=' expr)? - var a = parseExpr(p) - if p.tok.tokType == tkDo: - result = postExprBlocks(p, a) - else: - result = equals(p, a) - proc exprList(p: var Parser, endTok: TokType, result: PNode) = #| exprList = expr ^+ comma when defined(nimpretty): @@ -458,24 +404,6 @@ proc exprList(p: var Parser, endTok: TokType, result: PNode) = getTok(p) optInd(p, result) # progress guaranteed - var a = parseExpr(p) - result.add(a) - while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof): - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, a) - var a = parseExpr(p) - result.add(a) - when defined(nimpretty): - dec p.em.doIndentMore - -proc optionalExprList(p: var Parser, endTok: TokType, result: PNode) = - #| optionalExprList = expr ^* comma - when defined(nimpretty): - inc p.em.doIndentMore - getTok(p) - optInd(p, result) - # progress guaranteed while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof): var a = parseExpr(p) result.add(a) @@ -495,9 +423,10 @@ proc exprColonEqExprListAux(p: var Parser, endTok: TokType, result: PNode) = var a = exprColonEqExpr(p) result.add(a) if p.tok.tokType != tkComma: break - elif result.kind == nkPar: - result.transitionSonsKind(nkTupleConstr) getTok(p) + # (1,) produces a tuple expression + if endTok == tkParRi and p.tok.tokType == tkParRi and result.kind == nkPar: + result.transitionSonsKind(nkTupleConstr) skipComment(p, a) optPar(p) eat(p, endTok) @@ -509,13 +438,15 @@ proc exprColonEqExprList(p: var Parser, kind: TNodeKind, exprColonEqExprListAux(p, endTok, result) proc dotExpr(p: var Parser, a: PNode): PNode = + #| dotExpr = expr '.' optInd (symbol | '[:' exprList ']') + #| explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? var info = p.parLineInfo getTok(p) result = newNodeI(nkDotExpr, info) optInd(p, result) result.add(a) result.add(parseSymbol(p, smAfterDot)) - if p.tok.tokType == tkBracketLeColon and not p.tok.strongSpaceA: + if p.tok.tokType == tkBracketLeColon and p.tok.strongSpaceA <= 0: var x = newNodeI(nkBracketExpr, p.parLineInfo) # rewrite 'x.y[:z]()' to 'y[z](x)' x.add result[1] @@ -524,20 +455,10 @@ proc dotExpr(p: var Parser, a: PNode): PNode = var y = newNodeI(nkCall, p.parLineInfo) y.add x y.add result[0] - if p.tok.tokType == tkParLe and not p.tok.strongSpaceA: + if p.tok.tokType == tkParLe and p.tok.strongSpaceA <= 0: exprColonEqExprListAux(p, tkParRi, y) result = y -proc dotLikeExpr(p: var Parser, a: PNode): PNode = - var info = p.parLineInfo - result = newNodeI(nkInfix, info) - optInd(p, result) - var opNode = newIdentNodeP(p.tok.ident, p) - getTok(p) - result.add(opNode) - result.add(a) - result.add(parseSymbol(p, smAfterDot)) - proc qualifiedIdent(p: var Parser): PNode = #| qualifiedIdent = symbol ('.' optInd symbol)? result = parseSymbol(p) @@ -584,7 +505,6 @@ proc parseCast(p: var Parser): PNode = result.add(exprColonEqExpr(p)) optPar(p) eat(p, tkParRi) - setEndInfo() proc setBaseFlags(n: PNode, base: NumericalBase) = case base @@ -607,20 +527,16 @@ proc parseGStrLit(p: var Parser, a: PNode): PNode = getTok(p) else: result = a - setEndInfo() proc complexOrSimpleStmt(p: var Parser): PNode proc simpleExpr(p: var Parser, mode = pmNormal): PNode -proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode +proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode proc semiStmtList(p: var Parser, result: PNode) = inc p.inSemiStmtList withInd(p): # Be lenient with the first stmt/expr - let a = case p.tok.tokType - of tkIf: parseIfOrWhenExpr(p, nkIfStmt) - of tkWhen: parseIfOrWhenExpr(p, nkWhenStmt) - else: complexOrSimpleStmt(p) + let a = if p.tok.tokType == tkIf: parseIfExpr(p, nkIfStmt) else: complexOrSimpleStmt(p) result.add a while p.tok.tokType != tkEof: @@ -633,7 +549,6 @@ proc semiStmtList(p: var Parser, result: PNode) = let a = complexOrSimpleStmt(p) if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) - getTok(p) else: result.add a dec p.inSemiStmtList @@ -644,10 +559,10 @@ proc parsePar(p: var Parser): PNode = #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' #| | 'when' | 'var' | 'mixin' #| par = '(' optInd - #| ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';' - #| | ';' (ifExpr / complexOrSimpleStmt) ^+ ';' + #| ( &parKeyw (ifExpr \ complexOrSimpleStmt) ^+ ';' + #| | ';' (ifExpr \ complexOrSimpleStmt) ^+ ';' #| | pragmaStmt - #| | simpleExpr ( ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? ) + #| | simpleExpr ( ('=' expr (';' (ifExpr \ complexOrSimpleStmt) ^+ ';' )? ) #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) #| optPar ')' # @@ -671,10 +586,7 @@ proc parsePar(p: var Parser): PNode = semiStmtList(p, result) elif p.tok.tokType == tkCurlyDotLe: result.add(parseStmtPragma(p)) - elif p.tok.tokType == tkParRi: - # Empty tuple '()' - result.transitionSonsKind(nkTupleConstr) - else: + elif p.tok.tokType != tkParRi: var a = simpleExpr(p) if p.tok.tokType == tkDo: result = postExprBlocks(p, a) @@ -695,14 +607,13 @@ proc parsePar(p: var Parser): PNode = semiStmtList(p, result) else: a = colonOrEquals(p, a) - if a.kind == nkExprColonExpr: - result.transitionSonsKind(nkTupleConstr) result.add(a) if p.tok.tokType == tkComma: getTok(p) skipComment(p, a) # (1,) produces a tuple expression: - result.transitionSonsKind(nkTupleConstr) + if p.tok.tokType == tkParRi: + result.transitionSonsKind(nkTupleConstr) # progress guaranteed while p.tok.tokType != tkParRi and p.tok.tokType != tkEof: var a = exprColonEqExpr(p) @@ -712,18 +623,17 @@ proc parsePar(p: var Parser): PNode = skipComment(p, a) optPar(p) eat(p, tkParRi) - setEndInfo() proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode = #| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT #| | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT #| | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT #| | STR_LIT | RSTR_LIT | TRIPLESTR_LIT - #| | CHAR_LIT | CUSTOM_NUMERIC_LIT + #| | CHAR_LIT #| | NIL #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT #| identOrLiteral = generalizedLit | symbol | literal - #| | par | arrayConstr | setOrTableConstr | tupleConstr + #| | par | arrayConstr | setOrTableConstr #| | castExpr #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' @@ -802,14 +712,6 @@ proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode = of tkCharLit: result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p) getTok(p) - of tkCustomLit: - let splitPos = p.tok.iNumber.int - let str = newStrNodeP(nkRStrLit, p.tok.literal.substr(0, splitPos-1), p) - let callee = newIdentNodeP(getIdent(p.lex.cache, p.tok.literal.substr(splitPos)), p) - result = newNodeP(nkDotExpr, p) - result.add str - result.add callee - getTok(p) of tkNil: result = newNodeP(nkNilLit, p) getTok(p) @@ -843,37 +745,33 @@ proc namedParams(p: var Parser, callee: PNode, proc commandParam(p: var Parser, isFirstParam: var bool; mode: PrimaryMode): PNode = if mode == pmTypeDesc: result = simpleExpr(p, mode) - elif not isFirstParam: - result = exprEqExpr(p) else: result = parseExpr(p) - if p.tok.tokType == tkDo: - result = postExprBlocks(p, result) + if p.tok.tokType == tkDo: + result = postExprBlocks(p, result) + elif p.tok.tokType == tkEquals and not isFirstParam: + let lhs = result + result = newNodeP(nkExprEqExpr, p) + getTok(p) + result.add(lhs) + result.add(parseExpr(p)) isFirstParam = false proc commandExpr(p: var Parser; r: PNode; mode: PrimaryMode): PNode = - if mode == pmTrySimple: - result = r - else: - result = newNodeP(nkCommand, p) - result.add(r) - var isFirstParam = true - # progress NOT guaranteed - p.hasProgress = false - result.add commandParam(p, isFirstParam, mode) - -proc isDotLike(tok: Token): bool = - result = tok.tokType == tkOpr and tok.ident.s.len > 1 and - tok.ident.s[0] == '.' and tok.ident.s[1] != '.' + result = newNodeP(nkCommand, p) + result.add(r) + var isFirstParam = true + # progress NOT guaranteed + p.hasProgress = false + result.add commandParam(p, isFirstParam, mode) proc primarySuffix(p: var Parser, r: PNode, baseIndent: int, mode: PrimaryMode): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' - #| | '.' optInd symbol ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit? - #| | DOTLIKEOP optInd symbol generalizedLit? + #| | '.' optInd symbol generalizedLit? #| | '[' optInd exprColonEqExprList optPar ']' #| | '{' optInd exprColonEqExprList optPar '}' - # XXX strong spaces need to be reflected above + #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax result = r # progress guaranteed @@ -882,8 +780,15 @@ proc primarySuffix(p: var Parser, r: PNode, case p.tok.tokType of tkParLe: # progress guaranteed - if p.tok.strongSpaceA: - result = commandExpr(p, result, mode) + if p.tok.strongSpaceA > 0: + # inside type sections, expressions such as `ref (int, bar)` + # are parsed as a nkCommand with a single tuple argument (nkPar) + if mode == pmTypeDef: + result = newNodeP(nkCommand, p) + result.add r + result.add primary(p, pmNormal) + else: + result = commandExpr(p, result, mode) break result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result[1].kind == nkExprColonExpr: @@ -894,17 +799,17 @@ proc primarySuffix(p: var Parser, r: PNode, result = parseGStrLit(p, result) of tkBracketLe: # progress guaranteed - if p.tok.strongSpaceA: + if p.tok.strongSpaceA > 0: result = commandExpr(p, result, mode) break result = namedParams(p, result, nkBracketExpr, tkBracketRi) of tkCurlyLe: # progress guaranteed - if p.tok.strongSpaceA: + if p.tok.strongSpaceA > 0: result = commandExpr(p, result, mode) break result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) - of tkSymbol, tkAccent, tkIntLit..tkCustomLit, tkNil, tkCast, + of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkOpr, tkDotDot, tkVar, tkOut, tkStatic, tkType, tkEnum, tkTuple, tkObject, tkProc: # XXX: In type sections we allow the free application of the @@ -912,19 +817,11 @@ proc primarySuffix(p: var Parser, r: PNode, # `foo ref` or `foo ptr`. Unfortunately, these two are also # used as infix operators for the memory regions feature and # the current parsing rules don't play well here. - let isDotLike2 = p.tok.isDotLike - if isDotLike2 and p.lex.config.isDefined("nimPreviewDotLikeOps"): - # synchronize with `tkDot` branch - result = dotLikeExpr(p, result) - result = parseGStrLit(p, result) - else: - if isDotLike2: - parMessage(p, warnDotLikeOps, "dot-like operators will be parsed differently with `-d:nimPreviewDotLikeOps`") - if p.inPragma == 0 and (isUnary(p.tok) or p.tok.tokType notin {tkOpr, tkDotDot}): - # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet - # solution, but pragmas.nim can't handle that - result = commandExpr(p, result, mode) - break + if p.inPragma == 0 and (isUnary(p.tok) or p.tok.tokType notin {tkOpr, tkDotDot}): + # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet + # solution, but pragmas.nim can't handle that + result = commandExpr(p, result, mode) + break else: break @@ -951,13 +848,9 @@ proc parseOperators(p: var Parser, headNode: PNode, a.add(b) result = a opPrec = getPrecedence(p.tok) - setEndInfo() proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode = - var mode = mode result = primary(p, mode) - if mode == pmTrySimple: - mode = pmNormal if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)) and mode == pmNormal: var pragmaExp = newNodeP(nkPragmaExpr, p) @@ -1001,7 +894,6 @@ proc parsePragma(p: var Parser): PNode = when defined(nimpretty): dec p.em.doIndentMore dec p.em.keepIndents - setEndInfo() proc identVis(p: var Parser; allowDot=false): PNode = #| identVis = symbol OPR? # postfix position @@ -1039,9 +931,9 @@ type proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode = #| declColonEquals = identWithPragma (comma identWithPragma)* comma? - #| (':' optInd typeDescExpr)? ('=' optInd expr)? + #| (':' optInd typeDesc)? ('=' optInd expr)? #| identColonEquals = IDENT (comma IDENT)* comma? - #| (':' optInd typeDescExpr)? ('=' optInd expr)?) + #| (':' optInd typeDesc)? ('=' optInd expr)?) var a: PNode result = newNodeP(nkIdentDefs, p) # progress guaranteed @@ -1059,7 +951,7 @@ proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode = if p.tok.tokType == tkColon: getTok(p) optInd(p, result) - result.add(parseTypeDesc(p, fullExpr = true)) + result.add(parseTypeDesc(p)) else: result.add(newNodeP(nkEmpty, p)) if p.tok.tokType != tkEquals and withBothOptional notin flags: @@ -1070,13 +962,13 @@ proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode = result.add(parseExpr(p)) else: result.add(newNodeP(nkEmpty, p)) - setEndInfo() proc parseTuple(p: var Parser, indentAllowed = false): PNode = - #| tupleTypeBracket = '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' - #| tupleType = 'tuple' tupleTypeBracket - #| tupleDecl = 'tuple' (tupleTypeBracket / - #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?) + #| inlTupleDecl = 'tuple' + #| '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' + #| extTupleDecl = 'tuple' + #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? + #| tupleClass = 'tuple' result = newNodeP(nkTupleTy, p) getTok(p) if p.tok.tokType == tkBracketLe: @@ -1115,7 +1007,6 @@ proc parseTuple(p: var Parser, indentAllowed = false): PNode = parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'") else: result = newNodeP(nkTupleClassTy, p) - setEndInfo() proc parseParamList(p: var Parser, retColon = true): PNode = #| paramList = '(' declColonEquals ^* (comma/semicolon) ')' @@ -1164,7 +1055,6 @@ proc parseParamList(p: var Parser, retColon = true): PNode = when defined(nimpretty): dec p.em.doIndentMore dec p.em.keepIndents - setEndInfo() proc optPragmas(p: var Parser): PNode = if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)): @@ -1174,24 +1064,20 @@ proc optPragmas(p: var Parser): PNode = proc parseDoBlock(p: var Parser; info: TLineInfo): PNode = #| doBlock = 'do' paramListArrow pragma? colcom stmt - var params = parseParamList(p, retColon=false) + let params = parseParamList(p, retColon=false) let pragmas = optPragmas(p) colcom(p, result) result = parseStmt(p) - if params.kind != nkEmpty or pragmas.kind != nkEmpty: - if params.kind == nkEmpty: - params = newNodeP(nkFormalParams, p) - params.add(p.emptyNode) # return type + if params.kind != nkEmpty: result = newProcNode(nkDo, info, body = result, params = params, name = p.emptyNode, pattern = p.emptyNode, genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode) - setEndInfo() proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode = - #| routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)? - #| routineType = ('proc' | 'iterator') paramListColon pragma? + #| procExpr = 'proc' paramListColon pragma? ('=' COMMENT? stmt)? # either a proc type or a anonymous proc let info = parLineInfo(p) + getTok(p) let hasSignature = p.tok.tokType in {tkParLe, tkColon} and p.tok.indent < 0 let params = parseParamList(p) let pragmas = optPragmas(p) @@ -1202,20 +1088,19 @@ proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode = params = params, name = p.emptyNode, pattern = p.emptyNode, genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode) else: - result = newNodeI(if kind == nkIteratorDef: nkIteratorTy else: nkProcTy, info) + result = newNodeI(nkProcTy, info) if hasSignature: result.add(params) if kind == nkFuncDef: parMessage(p, "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead") result.add(pragmas) - setEndInfo() proc isExprStart(p: Parser): bool = case p.tok.tokType of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkFor, tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics, - tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCustomLit, tkVar, tkRef, tkPtr, - tkEnum, tkTuple, tkObject, tkWhen, tkCase, tkOut, tkTry, tkBlock: + tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, + tkTuple, tkObject, tkWhen, tkCase, tkOut: result = true else: result = false @@ -1228,20 +1113,16 @@ proc parseSymbolList(p: var Parser, result: PNode) = if p.tok.tokType != tkComma: break getTok(p) optInd(p, s) - setEndInfo() proc parseTypeDescKAux(p: var Parser, kind: TNodeKind, mode: PrimaryMode): PNode = + #| distinct = 'distinct' optInd typeDesc result = newNodeP(kind, p) getTok(p) if p.tok.indent != -1 and p.tok.indent <= p.currInd: return optInd(p, result) - let isTypedef = mode == pmTypeDef and p.tok.tokType in {tkObject, tkTuple} if not isOperator(p.tok) and isExprStart(p): - if isTypedef: - result.add(parseTypeDefValue(p)) - else: - result.add(primary(p, mode)) + result.add(primary(p, mode)) if kind == nkDistinctTy and p.tok.tokType == tkSymbol: # XXX document this feature! var nodeKind: TNodeKind @@ -1255,14 +1136,11 @@ proc parseTypeDescKAux(p: var Parser, kind: TNodeKind, let list = newNodeP(nodeKind, p) result.add list parseSymbolList(p, list) - if mode == pmTypeDef and not isTypedef: - result = parseOperators(p, result, -1, mode) - setEndInfo() proc parseVarTuple(p: var Parser): PNode proc parseFor(p: var Parser): PNode = - #| forStmt = 'for' ((varTuple / identWithPragma) ^+ comma) 'in' expr colcom stmt + #| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt #| forExpr = forStmt getTokNoInd(p) result = newNodeP(nkForStmt, p) @@ -1283,7 +1161,6 @@ proc parseFor(p: var Parser): PNode = result.add(parseExpr(p)) colcom(p, result) result.add(parseStmt(p)) - setEndInfo() template nimprettyDontTouch(body) = when defined(nimpretty): @@ -1306,13 +1183,13 @@ proc parseExpr(p: var Parser): PNode = result = parseBlock(p) of tkIf: nimprettyDontTouch: - result = parseIfOrWhenExpr(p, nkIfExpr) + result = parseIfExpr(p, nkIfExpr) of tkFor: nimprettyDontTouch: result = parseFor(p) of tkWhen: nimprettyDontTouch: - result = parseIfOrWhenExpr(p, nkWhenExpr) + result = parseIfExpr(p, nkWhenExpr) of tkCase: # Currently we think nimpretty is good enough with case expressions, # so it is allowed to touch them: @@ -1322,67 +1199,78 @@ proc parseExpr(p: var Parser): PNode = nimprettyDontTouch: result = parseTry(p, isExpr=true) else: result = simpleExpr(p) - setEndInfo() proc parseEnum(p: var Parser): PNode proc parseObject(p: var Parser): PNode proc parseTypeClass(p: var Parser): PNode proc primary(p: var Parser, mode: PrimaryMode): PNode = - #| simplePrimary = SIGILLIKEOP? identOrLiteral primarySuffix* - #| commandStart = &('`'|IDENT|literal|'cast'|'addr'|'type'|'var'|'out'| - #| 'static'|'enum'|'tuple'|'object'|'proc') - #| primary = simplePrimary (commandStart expr) - #| / operatorB primary - #| / routineExpr - #| / rawTypeDesc - #| / prefixOperator primary - # XXX strong spaces need to be reflected in commandStart - # command part is handled in the primarySuffix proc - - # prefix operators: + #| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple' + #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' + #| primary = typeKeyw optInd typeDesc + #| / prefixOperator* identOrLiteral primarySuffix* + #| / 'bind' primary if isOperator(p.tok): - # Note 'sigil like' operators are currently not reflected in the grammar - # and should be removed for Nim 2.0, I don't think anybody uses them. let isSigil = isSigilLike(p.tok) result = newNodeP(nkPrefix, p) var a = newIdentNodeP(p.tok.ident, p) result.add(a) getTok(p) optInd(p, a) - const identOrLiteralKinds = tkBuiltInMagics + {tkSymbol, tkAccent, tkNil, - tkIntLit..tkCustomLit, tkCast, tkOut, tkParLe, tkBracketLe, tkCurlyLe} - if isSigil and p.tok.tokType in identOrLiteralKinds: + if isSigil: + #XXX prefix operators let baseInd = p.lex.currLineIndent - result.add(identOrLiteral(p, mode)) + result.add(primary(p, pmSkipSuffix)) result = primarySuffix(p, result, baseInd, mode) else: result.add(primary(p, pmNormal)) return case p.tok.tokType - of tkProc: - getTok(p) - result = parseProcExpr(p, mode != pmTypeDesc, nkLambda) - of tkFunc: - getTok(p) - result = parseProcExpr(p, mode != pmTypeDesc, nkFuncDef) + of tkTuple: result = parseTuple(p, mode == pmTypeDef) + of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda) + of tkFunc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkFuncDef) of tkIterator: - getTok(p) - result = parseProcExpr(p, mode != pmTypeDesc, nkIteratorDef) + result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda) + if result.kind == nkLambda: result.transitionSonsKind(nkIteratorDef) + else: result.transitionSonsKind(nkIteratorTy) + of tkEnum: + if mode == pmTypeDef: + prettySection: + result = parseEnum(p) + else: + result = newNodeP(nkEnumTy, p) + getTok(p) + of tkObject: + if mode == pmTypeDef: + prettySection: + result = parseObject(p) + else: + result = newNodeP(nkObjectTy, p) + getTok(p) + of tkConcept: + if mode == pmTypeDef: + result = parseTypeClass(p) + else: + parMessage(p, "the 'concept' keyword is only valid in 'type' sections") of tkBind: - # legacy syntax, no-op in current nim result = newNodeP(nkBind, p) getTok(p) optInd(p, result) result.add(primary(p, pmNormal)) - of tkTuple, tkEnum, tkObject, tkConcept, - tkVar, tkOut, tkRef, tkPtr, tkDistinct: - result = parseTypeDesc(p) + of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode) + of tkOut: + # I like this parser extension to be in 1.4 as it still might turn out + # useful in the long run. + result = parseTypeDescKAux(p, nkMutableTy, mode) + of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode) + of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode) + of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode) else: let baseInd = p.lex.currLineIndent result = identOrLiteral(p, mode) - result = primarySuffix(p, result, baseInd, mode) + if mode != pmSkipSuffix: + result = primarySuffix(p, result, baseInd, mode) proc binaryNot(p: var Parser; a: PNode): PNode = if p.tok.tokType == tkNot: @@ -1397,73 +1285,17 @@ proc binaryNot(p: var Parser; a: PNode): PNode = else: result = a -proc parseTypeDesc(p: var Parser, fullExpr = false): PNode = - #| rawTypeDesc = (tupleType | routineType | 'enum' | 'object' | - #| ('var' | 'out' | 'ref' | 'ptr' | 'distinct') typeDesc?) - #| ('not' expr)? - #| typeDescExpr = (routineType / simpleExpr) ('not' expr)? - #| typeDesc = rawTypeDesc / typeDescExpr +proc parseTypeDesc(p: var Parser): PNode = + #| typeDesc = simpleExpr ('not' expr)? newlineWasSplitting(p) - if fullExpr: - result = simpleExpr(p, pmTypeDesc) - else: - case p.tok.tokType - of tkTuple: - result = parseTuple(p, false) - of tkProc: - getTok(p) - result = parseProcExpr(p, false, nkLambda) - of tkIterator: - getTok(p) - result = parseProcExpr(p, false, nkIteratorDef) - of tkEnum: - result = newNodeP(nkEnumTy, p) - getTok(p) - of tkObject: - result = newNodeP(nkObjectTy, p) - getTok(p) - of tkConcept: - parMessage(p, "the 'concept' keyword is only valid in 'type' sections") - of tkVar: result = parseTypeDescKAux(p, nkVarTy, pmTypeDesc) - of tkOut: result = parseTypeDescKAux(p, nkOutTy, pmTypeDesc) - of tkRef: result = parseTypeDescKAux(p, nkRefTy, pmTypeDesc) - of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, pmTypeDesc) - of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, pmTypeDesc) - else: - result = simpleExpr(p, pmTypeDesc) + result = simpleExpr(p, pmTypeDesc) result = binaryNot(p, result) - setEndInfo() -proc parseTypeDefValue(p: var Parser): PNode = - #| typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl | - #| ('ref' | 'ptr' | 'distinct') (tupleDecl | objectDecl)) - #| / (simpleExpr (exprEqExpr ^+ comma postExprBlocks)?)) - #| ('not' expr)? - case p.tok.tokType - of tkTuple: result = parseTuple(p, true) - of tkRef: result = parseTypeDescKAux(p, nkRefTy, pmTypeDef) - of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, pmTypeDef) - of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, pmTypeDef) - of tkEnum: - prettySection: - result = parseEnum(p) - of tkObject: - prettySection: - result = parseObject(p) - of tkConcept: - result = parseTypeClass(p) - else: - result = simpleExpr(p, pmTypeDef) - if p.tok.tokType != tkNot: - if result.kind == nkCommand: - var isFirstParam = false - while p.tok.tokType == tkComma: - getTok(p) - optInd(p, result) - result.add(commandParam(p, isFirstParam, pmTypeDef)) - result = postExprBlocks(p, result) +proc parseTypeDefAux(p: var Parser): PNode = + #| typeDefAux = simpleExpr ('not' expr)? + #| | 'concept' typeClass + result = simpleExpr(p, pmTypeDef) result = binaryNot(p, result) - setEndInfo() proc makeCall(n: PNode): PNode = ## Creates a call if the given node isn't already a call. @@ -1477,8 +1309,7 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode = #| postExprBlocks = ':' stmt? ( IND{=} doBlock #| | IND{=} 'of' exprList ':' stmt #| | IND{=} 'elif' expr ':' stmt - #| | IND{=} 'except' optionalExprList ':' stmt - #| | IND{=} 'finally' ':' stmt + #| | IND{=} 'except' exprList ':' stmt #| | IND{=} 'else' ':' stmt )* result = x if p.tok.indent >= 0: return @@ -1496,17 +1327,14 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode = result = makeCall(result) getTok(p) skipComment(p, result) - if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept, tkFinally}: + if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: var stmtList = newNodeP(nkStmtList, p) stmtList.add parseStmt(p) # to keep backwards compatibility (see tests/vm/tstringnil) if stmtList[0].kind == nkStmtList: stmtList = stmtList[0] stmtList.flags.incl nfBlockArg - if openingParams.kind != nkEmpty or openingPragmas.kind != nkEmpty: - if openingParams.kind == nkEmpty: - openingParams = newNodeP(nkFormalParams, p) - openingParams.add(p.emptyNode) # return type + if openingParams.kind != nkEmpty: result.add newProcNode(nkDo, stmtList.info, body = stmtList, params = openingParams, name = p.emptyNode, pattern = p.emptyNode, @@ -1535,10 +1363,7 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode = nextBlock.add parseExpr(p) of tkExcept: nextBlock = newNodeP(nkExceptBranch, p) - optionalExprList(p, tkColon, nextBlock) - of tkFinally: - nextBlock = newNodeP(nkFinally, p) - getTok(p) + exprList(p, tkColon, nextBlock) of tkElse: nextBlock = newNodeP(nkElse, p) getTok(p) @@ -1549,16 +1374,18 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode = nextBlock.flags.incl nfBlockArg result.add nextBlock - if nextBlock.kind in {nkElse, nkFinally}: break + if nextBlock.kind == nkElse: break else: if openingParams.kind != nkEmpty: parMessage(p, "expected ':'") proc parseExprStmt(p: var Parser): PNode = - #| exprStmt = simpleExpr postExprBlocks? - #| / simplePrimary (exprEqExpr ^+ comma) postExprBlocks? - #| / simpleExpr '=' optInd (expr postExprBlocks?) - var a = simpleExpr(p, pmTrySimple) + #| exprStmt = simpleExpr + #| (( '=' optInd expr colonBody? ) + #| / ( expr ^+ comma + #| postExprBlocks + #| ))? + var a = simpleExpr(p) if p.tok.tokType == tkEquals: result = newNodeP(nkAsgn, p) getTok(p) @@ -1568,23 +1395,25 @@ proc parseExprStmt(p: var Parser): PNode = result.add(a) result.add(b) else: + # simpleExpr parsed 'p a' from 'p a, b'? var isFirstParam = false - # if an expression is starting here, a simplePrimary was parsed and - # this is the start of a command - if p.tok.indent < 0 and isExprStart(p): + if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand: + result = a + while true: + getTok(p) + optInd(p, result) + result.add(commandParam(p, isFirstParam, pmNormal)) + if p.tok.tokType != tkComma: break + elif p.tok.indent < 0 and isExprStart(p): result = newTreeI(nkCommand, a.info, a) - let baseIndent = p.currInd while true: result.add(commandParam(p, isFirstParam, pmNormal)) - if p.tok.tokType != tkComma or - (p.tok.indent >= 0 and p.tok.indent < baseIndent): - break + if p.tok.tokType != tkComma: break getTok(p) optInd(p, result) else: result = a result = postExprBlocks(p, result) - setEndInfo() proc parseModuleName(p: var Parser, kind: TNodeKind): PNode = result = parseExpr(p) @@ -1596,7 +1425,6 @@ proc parseModuleName(p: var Parser, kind: TNodeKind): PNode = getTok(p) result.add(a) result.add(parseExpr(p)) - setEndInfo() proc parseImport(p: var Parser, kind: TNodeKind): PNode = #| importStmt = 'import' optInd expr @@ -1625,7 +1453,6 @@ proc parseImport(p: var Parser, kind: TNodeKind): PNode = getTok(p) optInd(p, a) #expectNl(p) - setEndInfo() proc parseIncludeStmt(p: var Parser): PNode = #| includeStmt = 'include' optInd expr ^+ comma @@ -1642,7 +1469,6 @@ proc parseIncludeStmt(p: var Parser): PNode = getTok(p) optInd(p, a) #expectNl(p) - setEndInfo() proc parseFromStmt(p: var Parser): PNode = #| fromStmt = 'from' expr 'import' optInd expr (comma expr)* @@ -1663,7 +1489,6 @@ proc parseFromStmt(p: var Parser): PNode = getTok(p) optInd(p, a) #expectNl(p) - setEndInfo() proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode = #| returnStmt = 'return' optInd expr? @@ -1671,7 +1496,7 @@ proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode = #| yieldStmt = 'yield' optInd expr? #| discardStmt = 'discard' optInd expr? #| breakStmt = 'break' optInd expr? - #| continueStmt = 'continue' optInd expr? + #| continueStmt = 'break' optInd expr? result = newNodeP(kind, p) getTok(p) if p.tok.tokType == tkComment: @@ -1685,7 +1510,6 @@ proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode = var e = parseExpr(p) e = postExprBlocks(p, e) result.add(e) - setEndInfo() proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode = #| condStmt = expr colcom stmt COMMENT? @@ -1710,9 +1534,8 @@ proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode = colcom(p, branch) branch.add(parseStmt(p)) result.add(branch) - setEndInfo() -proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode = +proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode = #| condExpr = expr colcom expr optInd #| ('elif' expr colcom expr optInd)* #| 'else' colcom expr @@ -1735,7 +1558,6 @@ proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode = colcom(p, branch) branch.add(parseStmt(p)) result.add(branch) - setEndInfo() proc parseWhile(p: var Parser): PNode = #| whileStmt = 'while' expr colcom stmt @@ -1745,7 +1567,6 @@ proc parseWhile(p: var Parser): PNode = result.add(parseExpr(p)) colcom(p, result) result.add(parseStmt(p)) - setEndInfo() proc parseCase(p: var Parser): PNode = #| ofBranch = 'of' exprList colcom stmt @@ -1793,27 +1614,24 @@ proc parseCase(p: var Parser): PNode = if wasIndented: p.currInd = oldInd - setEndInfo() proc parseTry(p: var Parser; isExpr: bool): PNode = #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') - #| (IND{=}? 'except' optionalExprList colcom stmt)* + #| (IND{=}? 'except' exprList colcom stmt)* #| (IND{=}? 'finally' colcom stmt)? #| tryExpr = 'try' colcom stmt &(optInd 'except'|'finally') - #| (optInd 'except' optionalExprList colcom stmt)* + #| (optInd 'except' exprList colcom stmt)* #| (optInd 'finally' colcom stmt)? result = newNodeP(nkTryStmt, p) - let parentIndent = p.currInd # isExpr getTok(p) colcom(p, result) result.add(parseStmt(p)) var b: PNode = nil - - while sameOrNoInd(p) or (isExpr and parentIndent <= p.tok.indent): + while sameOrNoInd(p) or isExpr: case p.tok.tokType of tkExcept: b = newNodeP(nkExceptBranch, p) - optionalExprList(p, tkColon, b) + exprList(p, tkColon, b) of tkFinally: b = newNodeP(nkFinally, p) getTok(p) @@ -1822,14 +1640,13 @@ proc parseTry(p: var Parser; isExpr: bool): PNode = b.add(parseStmt(p)) result.add(b) if b == nil: parMessage(p, "expected 'except'") - setEndInfo() proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode = + #| exceptBlock = 'except' colcom stmt result = newNodeP(kind, p) getTok(p) colcom(p, result) result.add(parseStmt(p)) - setEndInfo() proc parseBlock(p: var Parser): PNode = #| blockStmt = 'block' symbol? colcom stmt @@ -1840,7 +1657,6 @@ proc parseBlock(p: var Parser): PNode = else: result.add(parseSymbol(p)) colcom(p, result) result.add(parseStmt(p)) - setEndInfo() proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode = #| staticStmt = 'static' colcom stmt @@ -1849,7 +1665,6 @@ proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode = getTok(p) colcom(p, result) result.add(parseStmt(p)) - setEndInfo() proc parseAsm(p: var Parser): PNode = #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT) @@ -1866,7 +1681,6 @@ proc parseAsm(p: var Parser): PNode = result.add(p.emptyNode) return getTok(p) - setEndInfo() proc parseGenericParam(p: var Parser): PNode = #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)? @@ -1902,7 +1716,6 @@ proc parseGenericParam(p: var Parser): PNode = result.add(parseExpr(p)) else: result.add(p.emptyNode) - setEndInfo() proc parseGenericParamList(p: var Parser): PNode = #| genericParamList = '[' optInd @@ -1921,14 +1734,12 @@ proc parseGenericParamList(p: var Parser): PNode = skipComment(p, a) optPar(p) eat(p, tkBracketRi) - setEndInfo() proc parsePattern(p: var Parser): PNode = #| pattern = '{' stmt '}' eat(p, tkCurlyLe) result = parseStmt(p) eat(p, tkCurlyRi) - setEndInfo() proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = #| indAndComment = (IND{>} COMMENT)? | COMMENT? @@ -1937,12 +1748,6 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = result = newNodeP(kind, p) getTok(p) optInd(p, result) - if kind in {nkProcDef, nkLambda, nkIteratorDef, nkFuncDef} and - p.tok.tokType notin {tkSymbol, tokKeywordLow..tokKeywordHigh, tkAccent}: - # no name; lambda or proc type - # in every context that we can parse a routine, we can also parse these - result = parseProcExpr(p, true, if kind == nkProcDef: nkLambda else: kind) - return result.add(identVis(p)) if p.tok.tokType == tkCurlyLe and p.validInd: result.add(p.parsePattern) else: result.add(p.emptyNode) @@ -1955,25 +1760,13 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = else: result.add(p.emptyNode) # empty exception tracking: result.add(p.emptyNode) - let maybeMissEquals = p.tok.tokType != tkEquals - if (not maybeMissEquals) and p.validInd: + if p.tok.tokType == tkEquals and p.validInd: getTok(p) skipComment(p, result) result.add(parseStmt(p)) else: result.add(p.emptyNode) - indAndComment(p, result, maybeMissEquals) - let body = result[^1] - if body.kind == nkStmtList and body.len > 0 and body[0].comment.len > 0 and body[0].kind != nkCommentStmt: - if result.comment.len == 0: - # proc fn*(a: int): int = a ## foo - # => moves comment `foo` to `fn` - result.comment = body[0].comment - body[0].comment = "" - #else: - # assert false, p.lex.config$body.info # avoids hard to track bugs, fail early. - # Yeah, that worked so well. There IS a bug in this logic, now what? - setEndInfo() + indAndComment(p, result) proc newCommentStmt(p: var Parser): PNode = #| commentStmt = COMMENT @@ -2009,10 +1802,9 @@ proc parseSection(p: var Parser, kind: TNodeKind, result.add(defparser(p)) else: parMessage(p, errIdentifierExpected, p.tok) - setEndInfo() proc parseEnum(p: var Parser): PNode = - #| enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ + #| enum = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ result = newNodeP(nkEnumTy, p) getTok(p) result.add(p.emptyNode) @@ -2025,7 +1817,7 @@ proc parseEnum(p: var Parser): PNode = var symPragma = a var pragma: PNode - if (p.tok.indent < 0 or p.tok.indent >= p.currInd) and p.tok.tokType == tkCurlyDotLe: + if p.tok.tokType == tkCurlyDotLe: pragma = optPragmas(p) symPragma = newNodeP(nkPragmaExpr, p) symPragma.add(a) @@ -2056,7 +1848,6 @@ proc parseEnum(p: var Parser): PNode = break if result.len <= 1: parMessage(p, errIdentifierExpected, p.tok) - setEndInfo() proc parseObjectPart(p: var Parser): PNode proc parseObjectWhen(p: var Parser): PNode = @@ -2082,19 +1873,22 @@ proc parseObjectWhen(p: var Parser): PNode = branch.add(parseObjectPart(p)) flexComment(p, branch) result.add(branch) - setEndInfo() proc parseObjectCase(p: var Parser): PNode = #| objectBranch = 'of' exprList colcom objectPart #| objectBranches = objectBranch (IND{=} objectBranch)* #| (IND{=} 'elif' expr colcom objectPart)* #| (IND{=} 'else' colcom objectPart)? - #| objectCase = 'case' declColonEquals ':'? COMMENT? + #| objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT? #| (IND{>} objectBranches DED #| | IND{=} objectBranches) result = newNodeP(nkRecCase, p) getTokNoInd(p) - var a = parseIdentColonEquals(p, {withPragma}) + var a = newNodeP(nkIdentDefs, p) + a.add(identWithPragma(p)) + eat(p, tkColon) + a.add(parseTypeDesc(p)) + a.add(p.emptyNode) result.add(a) if p.tok.tokType == tkColon: getTok(p) flexComment(p, result) @@ -2124,7 +1918,6 @@ proc parseObjectCase(p: var Parser): PNode = if b.kind == nkElse: break if wasIndented: p.currInd = oldInd - setEndInfo() proc parseObjectPart(p: var Parser): PNode = #| objectPart = IND{>} objectPart^+IND{=} DED @@ -2157,13 +1950,17 @@ proc parseObjectPart(p: var Parser): PNode = result = p.emptyNode else: result = p.emptyNode - setEndInfo() proc parseObject(p: var Parser): PNode = - #| objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart + #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart result = newNodeP(nkObjectTy, p) getTok(p) - result.add(p.emptyNode) # compatibility with old pragma node + 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) if p.tok.tokType == tkOf and p.tok.indent < 0: var a = newNodeP(nkOfInherit, p) getTok(p) @@ -2178,11 +1975,9 @@ proc parseObject(p: var Parser): PNode = result.add(p.emptyNode) else: result.add(parseObjectPart(p)) - setEndInfo() proc parseTypeClassParam(p: var Parser): PNode = - let modifier = - case p.tok.tokType + let modifier = case p.tok.tokType of tkOut, tkVar: nkVarTy of tkPtr: nkPtrTy of tkRef: nkRefTy @@ -2196,26 +1991,19 @@ proc parseTypeClassParam(p: var Parser): PNode = result.add(p.parseSymbol) else: result = p.parseSymbol - setEndInfo() proc parseTypeClass(p: var Parser): PNode = - #| conceptParam = ('var' | 'out')? symbol - #| conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? + #| typeClassParam = ('var' | 'out')? symbol + #| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? #| &IND{>} stmt result = newNodeP(nkTypeClassTy, p) getTok(p) - if p.tok.tokType == tkComment: - skipComment(p, result) - - if p.tok.indent < 0: - var args = newNodeP(nkArgList, p) - result.add(args) + var args = newNodeP(nkArgList, p) + result.add(args) + args.add(p.parseTypeClassParam) + while p.tok.tokType == tkComma: + getTok(p) args.add(p.parseTypeClassParam) - while p.tok.tokType == tkComma: - getTok(p) - args.add(p.parseTypeClassParam) - else: - result.add(p.emptyNode) # see ast.isNewStyleConcept if p.tok.tokType == tkCurlyDotLe and p.validInd: result.add(parsePragma(p)) else: @@ -2235,33 +2023,45 @@ proc parseTypeClass(p: var Parser): PNode = skipComment(p, result) # an initial IND{>} HAS to follow: if not realInd(p): - if result.isNewStyleConcept: - parMessage(p, "routine expected, but found '$1' (empty new-styled concepts are not allowed)", p.tok) result.add(p.emptyNode) else: result.add(parseStmt(p)) - setEndInfo() proc parseTypeDef(p: var Parser): PNode = #| - #| typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue + #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux + #| indAndComment? / 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 - pragma = optPragmas(p) - if pragma.kind != nkEmpty: - identPragma = newNodeP(nkPragmaExpr, p) - identPragma.add(identifier) - identPragma.add(pragma) + 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") result.add(identPragma) result.add(genericParam) @@ -2270,11 +2070,10 @@ proc parseTypeDef(p: var Parser): PNode = result.info = parLineInfo(p) getTok(p) optInd(p, result) - result.add(parseTypeDefValue(p)) + result.add(parseTypeDefAux(p)) else: result.add(p.emptyNode) indAndComment(p, result) # special extension! - setEndInfo() proc parseVarTuple(p: var Parser): PNode = #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr @@ -2291,7 +2090,6 @@ proc parseVarTuple(p: var Parser): PNode = result.add(p.emptyNode) # no type desc optPar(p) eat(p, tkParRi) - setEndInfo() proc parseVariable(p: var Parser): PNode = #| colonBody = colcom stmt postExprBlocks? @@ -2304,7 +2102,6 @@ proc parseVariable(p: var Parser): PNode = else: result = parseIdentColonEquals(p, {withPragma, withDot}) result[^1] = postExprBlocks(p, result[^1]) indAndComment(p, result) - setEndInfo() proc parseConstant(p: var Parser): PNode = #| constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment @@ -2324,7 +2121,6 @@ proc parseConstant(p: var Parser): PNode = result.add(parseExpr(p)) result[^1] = postExprBlocks(p, result[^1]) indAndComment(p, result) - setEndInfo() proc parseBind(p: var Parser, k: TNodeKind): PNode = #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma @@ -2340,7 +2136,6 @@ proc parseBind(p: var Parser, k: TNodeKind): PNode = getTok(p) optInd(p, a) #expectNl(p) - setEndInfo() proc parseStmtPragma(p: var Parser): PNode = #| pragmaStmt = pragma (':' COMMENT? stmt)? @@ -2352,7 +2147,6 @@ proc parseStmtPragma(p: var Parser): PNode = skipComment(p, result) result.add a result.add parseStmt(p) - setEndInfo() proc simpleStmt(p: var Parser): PNode = #| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt @@ -2495,7 +2289,6 @@ proc parseStmt(p: var Parser): PNode = if p.tok.tokType != tkSemiColon: break getTok(p) if err and p.tok.tokType == tkEof: break - setEndInfo() proc parseAll(p: var Parser): PNode = ## Parses the rest of the input stream held by the parser into a PNode. @@ -2511,11 +2304,6 @@ proc parseAll(p: var Parser): PNode = getTok(p) if p.tok.indent != 0: parMessage(p, errInvalidIndentation) - setEndInfo() - -proc checkFirstLineIndentation*(p: var Parser) = - if p.tok.indent != 0 and p.tok.strongSpaceA: - parMessage(p, errInvalidIndentation) proc parseTopLevelStmt(p: var Parser): PNode = ## Implements an iterator which, when called repeatedly, returns the next @@ -2545,7 +2333,6 @@ proc parseTopLevelStmt(p: var Parser): PNode = result = complexOrSimpleStmt(p) if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) break - setEndInfo() proc parseString*(s: string; cache: IdentCache; config: ConfigRef; filename: string = ""; line: int = 0; @@ -2557,10 +2344,9 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef; var stream = llStreamOpen(s) stream.lineOffset = line - var p: Parser - p.lex.errorHandler = errorHandler - openParser(p, AbsoluteFile filename, stream, cache, config) + var parser: Parser + parser.lex.errorHandler = errorHandler + openParser(parser, AbsoluteFile filename, stream, cache, config) - result = p.parseAll - closeParser(p) - setEndInfo() + result = parser.parseAll + closeParser(parser) diff --git a/compiler/passaux.nim b/compiler/passaux.nim index af507d210..715b7d676 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -10,7 +10,7 @@ ## implements some little helper passes import - ast, passes, msgs, options, lineinfos + ast, passes, idents, msgs, options, lineinfos from modulegraphs import ModuleGraph, PPassContext @@ -19,15 +19,17 @@ type config: ConfigRef proc verboseOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = - # xxx consider either removing this or keeping for documentation for how to add a pass + #MessageOut('compiling ' + s.name.s); result = VerboseRef(config: graph.config, idgen: idgen) - -import std/objectdollar + rawMessage(graph.config, hintProcessing, s.name.s) proc verboseProcess(context: PPassContext, n: PNode): PNode = - # called from `process` in `processTopLevelStmt`. result = n let v = VerboseRef(context) - message(v.config, n.info, hintProcessingStmt, $v.idgen[]) + if v.config.verbosity == 3: + # system.nim deactivates all hints, for verbosity:3 we want the processing + # messages nonetheless, so we activate them again (but honor cmdlineNotes) + v.config.setNote(hintProcessing) + message(v.config, n.info, hintProcessing, $v.idgen[]) const verbosePass* = makePass(open = verboseOpen, process = verboseProcess) diff --git a/compiler/passes.nim b/compiler/passes.nim index 536a64714..997a10cd8 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -14,22 +14,7 @@ import options, ast, llstream, msgs, idents, syntaxes, modulegraphs, reorder, - lineinfos, - pipelineutils, - modules, pathutils, packages, - sem, semdata - -import ic/replayer - -export skipCodegen, resolveMod, prepareConfigNotes - -when defined(nimsuggest): - import std/sha1 - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - -import std/tables + lineinfos, pathutils type TPassData* = tuple[input: PNode, closeOutput: PNode] @@ -47,6 +32,12 @@ proc makePass*(open: TPassOpen = nil, result.process = process result.isFrontend = isFrontend +proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} = + # can be used by codegen passes to determine whether they should do + # something with `n`. Currently, this ignores `n` and uses the global + # error count instead. + result = config.errorCounter > 0 + const maxPasses = 10 @@ -83,6 +74,13 @@ proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray) if isNil(m): return false result = true +proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex = + let fullPath = findModule(conf, module, relativeTo) + if fullPath.isEmpty: + result = InvalidFileIdx + else: + result = fileInfoIdx(conf, fullPath) + proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind, a: var TPassContextArray; m: PSym) = # XXX fixme this should actually be relative to the config file! @@ -96,6 +94,22 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod importStmt.add str if not processTopLevelStmt(graph, importStmt, a): break +const + imperativeCode = {low(TNodeKind)..high(TNodeKind)} - {nkTemplateDef, nkProcDef, nkMethodDef, + nkMacroDef, nkConverterDef, nkIteratorDef, nkFuncDef, nkPragma, + nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt} + +proc prepareConfigNotes(graph: ModuleGraph; module: PSym) = + # don't be verbose unless the module belongs to the main package: + if module.getnimblePkgId == graph.config.mainPackageId: + graph.config.notes = graph.config.mainPackageNotes + else: + if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes + graph.config.notes = graph.config.foreignPackageNotes + +proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} = + result = module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange") + proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; stream: PLLStream): bool {.discardable.} = if graph.stopCompile(): return true @@ -114,15 +128,10 @@ 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 belongsToStdlib(graph, module)) or module.name.s == "distros": + if module.owner == nil or module.owner.name.s != "stdlib" or 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 @@ -131,125 +140,44 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; processImplicits graph, graph.config.implicitImports, nkImportStmt, a, module processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module - checkFirstLineIndentation(p) - block processCode: - if graph.stopCompile(): break processCode + while true: + if graph.stopCompile(): break var n = parseTopLevelStmt(p) - if n.kind == nkEmpty: break processCode - - # read everything, no streaming possible - var sl = newNodeI(nkStmtList, n.info) - sl.add n - while true: - var n = parseTopLevelStmt(p) - if n.kind == nkEmpty: break + if n.kind == nkEmpty: break + if (sfSystemModule notin module.flags and + ({sfNoForward, sfReorder} * module.flags != {} or + codeReordering in graph.config.features)): + # read everything, no streaming possible + var sl = newNodeI(nkStmtList, n.info) sl.add n - if sfReorder in module.flags or codeReordering in graph.config.features: - sl = reorder(graph, sl, module) - discard processTopLevelStmt(graph, sl, a) - + while true: + var n = parseTopLevelStmt(p) + if n.kind == nkEmpty: break + sl.add n + if sfReorder in module.flags or codeReordering in graph.config.features: + sl = reorder(graph, sl, module) + discard processTopLevelStmt(graph, sl, a) + break + elif n.kind in imperativeCode: + # read everything until the next proc declaration etc. + var sl = newNodeI(nkStmtList, n.info) + sl.add n + var rest: PNode = nil + while true: + var n = parseTopLevelStmt(p) + if n.kind == nkEmpty or n.kind notin imperativeCode: + rest = n + break + sl.add n + #echo "-----\n", sl + if not processTopLevelStmt(graph, sl, a): break + if rest != nil: + #echo "-----\n", rest + if not processTopLevelStmt(graph, rest, a): break + else: + #echo "----- single\n", n + if not processTopLevelStmt(graph, n, a): break closeParser(p) if s.kind != llsStdIn: break closePasses(graph, a) - if graph.config.backend notin {backendC, backendCpp, backendObjc}: - # We only write rod files here if no C-like backend is active. - # The C-like backends have been patched to support the IC mechanism. - # They are responsible for closing the rod files. See `cbackend.nim`. - closeRodFile(graph, module) result = true - -proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym = - var flags = flags - if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule - result = graph.getModule(fileIdx) - - template processModuleAux(moduleStatus) = - onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule) - var s: PLLStream - if sfMainModule in flags: - if graph.config.projectIsStdin: s = stdin.llStreamOpen - elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput) - discard processModule(graph, result, idGeneratorFromModule(result), s) - if result == nil: - var cachedModules: seq[FileIndex] - result = moduleFromRodFile(graph, fileIdx, cachedModules) - let filename = AbsoluteFile toFullPath(graph.config, fileIdx) - if result == nil: - result = newModule(graph, fileIdx) - result.flags.incl flags - registerModule(graph, result) - processModuleAux("import") - else: - if sfSystemModule in flags: - graph.systemModule = result - partialInitModule(result, graph, fileIdx, filename) - for m in cachedModules: - registerModuleById(graph, m) - replayStateChanges(graph.packed[m.int].module, graph) - replayGenericCacheInformation(graph, m.int) - elif graph.isDirty(result): - result.flags.excl sfDirty - # reset module fields: - initStrTables(graph, result) - result.ast = nil - processModuleAux("import(dirty)") - graph.markClientsDirty(fileIdx) - -proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = - # this is called by the semantic checking phase - assert graph.config != nil - result = compileModule(graph, fileIdx, {}, s) - graph.addDep(s, fileIdx) - # keep track of import relationships - if graph.config.hcrOn: - graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx) - #if sfSystemModule in result.flags: - # localError(result.info, errAttemptToRedefine, result.name.s) - # restore the notes for outer module: - graph.config.notes = - if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes - else: graph.config.foreignPackageNotes - -proc connectCallbacks*(graph: ModuleGraph) = - graph.includeFileCallback = modules.includeModule - graph.importModuleCallback = importModule - -proc compileSystemModule*(graph: ModuleGraph) = - if graph.systemModule == nil: - connectCallbacks(graph) - graph.config.m.systemFileIdx = fileInfoIdx(graph.config, - graph.config.libpath / RelativeFile"system.nim") - discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule}) - -proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = - connectCallbacks(graph) - let conf = graph.config - wantMainModule(conf) - configComplete(graph) - - let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim") - let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx - conf.projectMainIdx2 = projectFile - - let packSym = getPackage(graph, projectFile) - graph.config.mainPackageId = packSym.getPackageId - graph.importStack.add projectFile - - if projectFile == systemFileIdx: - discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule}) - else: - graph.compileSystemModule() - discard graph.compileModule(projectFile, {sfMainModule}) - -proc mySemOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = - result = preparePContext(graph, module, idgen) - -proc mySemClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = - var c = PContext(context) - closePContext(graph, c, n) - -proc mySemProcess(context: PPassContext, n: PNode): PNode = - result = semWithPContext(PContext(context), n) - -const semPass* = makePass(mySemOpen, mySemProcess, mySemClose, - isFrontend = true) diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index d9779deab..d8f3613b0 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -10,10 +10,7 @@ ## Path handling utilities for Nim. Strictly typed code in order ## to avoid the never ending time sink in getting path handling right. -import os, pathnorm, strutils - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] +import os, pathnorm type AbsoluteFile* = distinct string @@ -102,52 +99,3 @@ when true: proc addFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.} proc writeFile*(x: AbsoluteFile; content: string) {.borrow.} - -proc skipHomeDir(x: string): int = - when defined(windows): - if x.continuesWith("Users/", len("C:/")): - result = 3 - else: - result = 0 - else: - if x.startsWith("/home/") or x.startsWith("/Users/"): - result = 3 - elif x.startsWith("/mnt/") and x.continuesWith("/Users/", len("/mnt/c")): - result = 5 - else: - result = 0 - -proc relevantPart(s: string; afterSlashX: int): string = - result = newStringOfCap(s.len - 8) - var slashes = afterSlashX - for i in 0.. - # //user/ - if not isAbsolute(x): - result = customPathImpl(canonSlashes(getCurrentDir() / x)) - else: - let slashes = skipHomeDir(x) - if slashes > 0: - result = "//user/" & relevantPart(x, slashes) - else: - result = x - -proc customPath*(x: string): string = - customPathImpl canonSlashes x diff --git a/compiler/patterns.nim b/compiler/patterns.nim index ff9a9efa3..f24d25c5c 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -13,9 +13,6 @@ import ast, types, semdata, sigmatch, idents, aliases, parampatterns, trees -when defined(nimPreviewSlimSystem): - import std/assertions - type TPatternContext = object owner: PSym @@ -39,12 +36,12 @@ proc putLazy(c: PPatternContext, sym: PSym, n: PNode) = proc matches(c: PPatternContext, p, n: PNode): bool proc canonKind(n: PNode): TNodeKind = - ## nodekind canonicalization for pattern matching + ## nodekind canonilization for pattern matching result = n.kind case result of nkCallKinds: result = nkCall of nkStrLit..nkTripleStrLit: result = nkStrLit - of nkFastAsgn, nkSinkAsgn: result = nkAsgn + of nkFastAsgn: result = nkAsgn else: discard proc sameKinds(a, b: PNode): bool {.inline.} = @@ -146,7 +143,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.astdef): result = true + elif matches(c, p, n.sym.ast): result = true elif p.kind == nkPattern: # pattern operators: | * let opr = p[0].ident.s @@ -213,8 +210,11 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode = for j in 0.. 0 - -proc resolveMod*(conf: ConfigRef; module, relativeTo: string): FileIndex = - let fullPath = findModule(conf, module, relativeTo) - if fullPath.isEmpty: - result = InvalidFileIdx - else: - result = fileInfoIdx(conf, fullPath) - -proc prepareConfigNotes*(graph: ModuleGraph; module: PSym) = - # don't be verbose unless the module belongs to the main package: - if graph.config.belongsToProjectPackage(module): - graph.config.notes = graph.config.mainPackageNotes - else: - if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes - graph.config.notes = graph.config.foreignPackageNotes - -proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} = - result = true - #module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange") diff --git a/compiler/platform.nim b/compiler/platform.nim index b6d6a119e..714946315 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -16,18 +16,13 @@ import strutils -when defined(nimPreviewSlimSystem): - import std/assertions - - type TSystemOS* = enum # Also add OS in initialization section and alias # conditionals to condsyms (end of module). osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, - osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos, osAix, osPalmos, osQnx, + osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx, osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks - osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osZephyr, - osNuttX, osAny + osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osAny type TInfoOSProp* = enum @@ -110,10 +105,6 @@ const scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {ospNeedsPIC, ospPosix}), - (name: "CROSSOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", - objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", - scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", - props: {ospNeedsPIC, ospPosix}), (name: "AIX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", @@ -190,14 +181,6 @@ const objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {ospPosix}), - (name: "Zephyr", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", - objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", - scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", - props: {ospPosix}), - (name: "NuttX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", - objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", - scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", - props: {ospPosix}), (name: "Any", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", @@ -210,15 +193,16 @@ type cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430, - cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32, - cpuE2k, cpuLoongArch64 + cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32 type - TInfoCPU* = tuple[name: string, intSize: int, endian: Endianness, + TEndian* = enum + littleEndian, bigEndian + TInfoCPU* = tuple[name: string, intSize: int, endian: TEndian, floatSize, bit: int] const - EndianToStr*: array[Endianness, string] = ["littleEndian", "bigEndian"] + EndianToStr*: array[TEndian, string] = ["littleEndian", "bigEndian"] CPU*: array[succ(low(TSystemCPU))..high(TSystemCPU), TInfoCPU] = [ (name: "i386", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "m68k", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), @@ -230,14 +214,13 @@ const (name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "hppa", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), - (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), # a.k.a. x86_64, covers both amd and intel + (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), - (name: "js", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), + (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), (name: "nimvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), - # xxx this seems buggy; on a 64bit machine, sizeof(int) is 64 in nimvm. (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64), @@ -246,9 +229,7 @@ const (name: "riscv32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "esp", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), - (name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), - (name: "e2k", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), - (name: "loongarch64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)] + (name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32)] type Target* = object diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim index 24e26b2b7..78c79bf59 100644 --- a/compiler/plugins/itersgen.nim +++ b/compiler/plugins/itersgen.nim @@ -9,7 +9,7 @@ ## Plugin to transform an inline iterator into a data structure. -import ".." / [ast, modulegraphs, lookups, semdata, lambdalifting, msgs] +import ".." / [ast, lookups, semdata, lambdalifting, msgs] proc iterToProcImpl*(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) @@ -29,7 +29,7 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode = localError(c.config, n[2].info, "type must be a non-generic ref|ptr to object with state field") return - let body = liftIterToProc(c.graph, iter.sym, getBody(c.graph, iter.sym), t, c.idgen) + let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t, c.idgen) let prc = newSym(skProc, n[3].ident, nextSymId c.idgen, iter.sym.owner, iter.sym.info) prc.typ = copyType(iter.sym.typ, nextTypeId c.idgen, prc) diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim index 384780f7b..c03a6986e 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(nkTupleConstr, n.info, tupleType) + result = newNodeIT(nkPar, 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 97182abab..a79a471de 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -14,79 +14,69 @@ import wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees, types, lookups, lineinfos, pathutils, linter -when defined(nimPreviewSlimSystem): - import std/assertions - -from ic / ic import addCompilerProc - const FirstCallConv* = wNimcall LastCallConv* = wNoconv const declPragmas = {wImportc, wImportObjC, wImportCpp, wImportJs, wExportc, wExportCpp, - wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed} + wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed, wAlign} ## common pragmas for declarations, to a good approximation procPragmas* = declPragmas + {FirstCallConv..LastCallConv, wMagic, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime, wMerge, wBorrow, wImportCompilerProc, wThread, wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, - wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe, + wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy, - wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect} + wRequires, wEnsures} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty, - wDelegator, wExportNims, wUsed, wPragma, wRedefine, wCallsite} + wDelegator, wExportNims, wUsed, wPragma} 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, wForbids, wLocks, wGcSafe, wRequires, wEnsures} + wDiscardable, wGensym, wInject, wRaises, + wTags, wLocks, wGcSafe, wRequires, wEnsures} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect} - stmtPragmas* = { - wHint, wWarning, wError, + stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, + wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks, + wStyleChecks, wAssertions, + wWarnings, wHints, + wLineDir, wStackTrace, wLineTrace, wOptimization, wHint, wWarning, wError, wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, wPassl, wPassc, wLocalPassc, wDeadCodeElimUnused, # deprecated, always on wDeprecated, - wPragma, wEmit, wUnroll, + wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto, - wExperimental, wDoctype, wThis, wUsed, wInvariant, wAssume, wAssert} - stmtPragmasTopLevel* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, - wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks, - wStyleChecks, wAssertions, - wWarnings, wHints, - wLineDir, wStackTrace, wLineTrace, wOptimization, - wFloatChecks, wInfChecks, wNanChecks} - lambdaPragmas* = {FirstCallConv..LastCallConv, + wInjectStmt, wExperimental, wThis, wUsed, wInvariant, wAssume, wAssert} + lambdaPragmas* = declPragmas + {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, wThread, wAsmNoStackFrame, - wRaises, wLocks, wTags, wForbids, wRequires, wEnsures, wEffectsOf, - wGcSafe, wCodegenDecl, wNoInit, wCompileTime} + wRaises, wLocks, wTags, wRequires, wEnsures, + wGcSafe, wCodegenDecl} - {wExportNims, wError, wUsed} # why exclude these? typePragmas* = declPragmas + {wMagic, wAcyclic, wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow, wIncompleteStruct, wCompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, - wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage} - fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor, - wRequiresInit, wNoalias, wAlign} - {wExportNims, wNodecl} # why exclude these? + wBorrow, wGcSafe, wPartial, wExplain, wPackage} + fieldPragmas* = declPragmas + { + wGuard, wBitsize, wCursor, wRequiresInit, wNoalias} - {wExportNims, wNodecl} # why exclude these? varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar, wMagic, wHeader, wCompilerProc, wCore, wDynlib, wNoInit, wCompileTime, wGlobal, - wGensym, wInject, wCodegenDecl, - wGuard, wGoto, wCursor, wNoalias, wAlign} + wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor, wNoalias} constPragmas* = declPragmas + {wHeader, wMagic, wGensym, wInject, - wIntDefine, wStrDefine, wBoolDefine, wDefine, - wCompilerProc, wCore} + wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore} paramPragmas* = {wNoalias, wInject, wGensym} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, - wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe, + wThread, wRaises, wLocks, wTags, wGcSafe, wRequires, wEnsures} forVarPragmas* = {wInject, wGensym} allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas @@ -104,10 +94,10 @@ proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords; isStatement: bool = false) proc recordPragma(c: PContext; n: PNode; args: varargs[string]) = - var recorded = newNodeI(nkReplayAction, n.info) + var recorded = newNodeI(nkCommentStmt, n.info) for i in 0..args.high: recorded.add newStrNode(args[i], n.info) - addPragmaComputation(c, recorded) + c.graph.recordStmt(c.graph, c.module, recorded) const errStringLiteralExpected = "string literal expected" @@ -314,7 +304,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.astdef # look it up + result = result.sym.ast # 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) @@ -360,11 +350,10 @@ proc processNote(c: PContext, n: PNode) = of wHint: handleNote(hintMin .. hintMax, c.config.notes) of wWarning: handleNote(warnMin .. warnMax, c.config.notes) of wWarningAsError: handleNote(warnMin .. warnMax, c.config.warningAsErrors) - of wHintAsError: handleNote(hintMin .. hintMax, c.config.warningAsErrors) else: invalidPragma(c, n) else: invalidPragma(c, n) -proc pragmaToOptions*(w: TSpecialWord): TOptions {.inline.} = +proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} = case w of wChecks: ChecksOptions of wObjChecks: {optObjCheck} @@ -477,16 +466,8 @@ proc processPop(c: PContext, n: PNode) = when defined(debugOptions): echo c.config $ n.info, " POP config is now ", c.config.options -proc processDefineConst(c: PContext, n: PNode, sym: PSym, kind: TMagic) = - sym.magic = kind - if n.kind in nkPragmaCallKinds and n.len == 2: - # could also use TLib - n[1] = getStrLitNode(c, n) - -proc processDefine(c: PContext, n: PNode, sym: PSym) = - if sym != nil and sym.kind == skConst: - processDefineConst(c, n, sym, mGenericDefine) - elif (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): +proc processDefine(c: PContext, n: PNode) = + if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): defineSymbol(c.config.symbols, n[1].ident.s) else: invalidPragma(c, n) @@ -513,20 +494,14 @@ proc processCompile(c: PContext, n: PNode) = var cf = Cfile(nimname: splitFile(src).name, cname: src, obj: dest, flags: {CfileFlag.External}, customArgs: customArgs) - if not fileExists(src): - localError(c.config, n.info, "cannot find: " & src.string) - else: - extccomp.addExternalFileToCompile(c.config, cf) - recordPragma(c, it, "compile", src.string, dest.string, customArgs) + extccomp.addExternalFileToCompile(c.config, cf) + recordPragma(c, it, "compile", src.string, dest.string, customArgs) proc getStrLit(c: PContext, n: PNode; i: int): string = n[i] = c.semConstExpr(c, n[i]) case n[i].kind of nkStrLit, nkRStrLit, nkTripleStrLit: - when defined(gcArc) or defined(gcOrc): - result = n[i].strVal - else: - shallowCopy(result, n[i].strVal) + shallowCopy(result, n[i].strVal) else: localError(c.config, n.info, errStringLiteralExpected) result = "" @@ -656,13 +631,12 @@ 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), c.module, it.info, c.config.options) + var userPragma = newSym(skTemplate, it[1].ident, nextSymId(c.idgen), nil, it.info, c.config.options) userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1]) strTableAdd(c.userPragmas, userPragma) @@ -672,7 +646,7 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) = x.typ = makeTypeFromExpr(c, x) else: var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs) - if t.kind notin {tyObject, tyOr}: + if t.kind != tyObject and not t.isMetaType: localError(c.config, x.info, errGenerated, "invalid type for raises/tags list") x.typ = t @@ -696,6 +670,23 @@ proc pragmaLockStmt(c: PContext; it: PNode) = for i in 0.. MaxLockLevel: + localError(c.config, it[1].info, "integer must be within 0.." & $MaxLockLevel) + else: + result = TLockLevel(x) + proc typeBorrow(c: PContext; sym: PSym, n: PNode) = if n.kind in nkPragmaCallKinds and n.len == 2: let it = n[1] @@ -704,15 +695,13 @@ proc typeBorrow(c: PContext; sym: PSym, n: PNode) = incl(sym.typ.flags, tfBorrowDot) proc markCompilerProc(c: PContext; s: PSym) = - # minor hack ahead: FlowVar is the only generic .compilerproc type which + # minor hack ahead: FlowVar is the only generic .compilerProc type which # should not have an external name set: if s.kind != skType or s.name.s != "FlowVar": makeExternExport(c, s, "$1", s.info) incl(s.flags, sfCompilerProc) incl(s.flags, sfUsed) registerCompilerProc(c.graph, s) - if c.config.symbolFiles != disabledSf: - addCompilerProc(c.encoder, c.packedRepr, s) proc deprecatedStmt(c: PContext; outerPragma: PNode) = let pragma = outerPragma[1] @@ -784,26 +773,6 @@ proc semCustomPragma(c: PContext, n: PNode): PNode = # pragma(arg) -> pragma: arg result.transitionSonsKind(n.kind) -proc processEffectsOf(c: PContext, n: PNode; owner: PSym) = - proc processParam(c: PContext; n: PNode) = - let r = c.semExpr(c, n) - if r.kind == nkSym and r.sym.kind == skParam: - if r.sym.owner == owner: - incl r.sym.flags, sfEffectsDelayed - else: - localError(c.config, n.info, errGenerated, "parameter cannot be declared as .effectsOf") - else: - localError(c.config, n.info, errGenerated, "parameter name expected") - - if n.kind notin nkPragmaCallKinds or n.len != 2: - localError(c.config, n.info, errGenerated, "parameter name expected") - else: - let it = n[1] - if it.kind in {nkCurly, nkBracket}: - for x in items(it): processParam(c, x) - else: - processParam(c, it) - proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, validPragmas: TSpecialWords, comesFromPush, isStatement: bool): bool = @@ -818,7 +787,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, wForbids: pragmaRaisesOrTags(c, key[1]) + of wRaises, wTags: pragmaRaisesOrTags(c, key[1]) else: discard return elif key.kind notin nkIdentKinds: @@ -827,7 +796,8 @@ 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: - styleCheckUse(c, key.info, userPragma) + if {optStyleHint, optStyleError} * c.config.globalOptions != {}: + styleCheckUse(c.config, key.info, userPragma) # number of pragmas increase/decrease with user pragma expansion inc c.instCounter @@ -841,13 +811,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: let k = whichKeyword(ident) if k in validPragmas: - checkPragmaUse(c.config, key.info, k, ident.s) + if {optStyleHint, optStyleError} * c.config.globalOptions != {}: + checkPragmaUse(c.config, key.info, k, ident.s) case k of wExportc, wExportCpp: makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) if k == wExportCpp: if c.config.backend != backendCpp: - localError(c.config, it.info, "exportcpp requires `cpp` backend, got: " & $c.config.backend) + localError(c.config, it.info, "exportcpp requires `cpp` backend, got " & $c.config.backend) else: incl(sym.flags, sfMangleCpp) incl(sym.flags, sfUsed) # avoid wrong hints @@ -865,16 +836,8 @@ 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 wCallsite: - if sym.kind == skTemplate: incl(sym.flags, sfCallsite) - else: invalidPragma(c, it) of wImportCpp: processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info) - of wCppNonPod: - incl(sym.flags, sfCppNonPod) of wImportJs: if c.config.backend != backendJs: localError(c.config, it.info, "`importjs` pragma requires the JavaScript target") @@ -924,13 +887,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wNoalias: noVal(c, it) incl(sym.flags, sfNoalias) - of wEffectsOf: - processEffectsOf(c, it, sym) of wThreadVar: noVal(c, it) incl(sym.flags, {sfThread, sfGlobal}) - of wDeadCodeElimUnused: - warningDeprecated(c.config, n.info, "'{.deadcodeelim: on.}' is deprecated, now a noop") # deprecated, dead code elim always on + of wDeadCodeElimUnused: discard # deprecated, dead code elim always on of wNoForward: pragmaNoForward(c, it) of wReorder: pragmaNoForward(c, it, flag = sfReorder) of wMagic: processMagic(c, it, sym) @@ -959,7 +919,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 == "": sym.loc.r = rope(sym.name.s) + if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) of wNoSideEffect: noVal(c, it) if sym != nil: @@ -992,12 +952,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, skConst}: + if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}: if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it) incl(sym.flags, sfDeprecated) elif sym != nil and sym.kind != skModule: @@ -1042,6 +1002,7 @@ 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 @@ -1078,8 +1039,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, let s = expectStrLit(c, it) recordPragma(c, it, "error", s) localError(c.config, it.info, errUser, s) - of wFatal: fatal(c.config, it.info, expectStrLit(c, it)) - of wDefine: processDefine(c, it, sym) + of wFatal: fatal(c.config, it.info, errUser, expectStrLit(c, it)) + of wDefine: processDefine(c, it) of wUndef: processUndef(c, it) of wCompile: processCompile(c, it) of wLink: processLink(c, it) @@ -1188,11 +1149,11 @@ 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, wForbids: pragmaRaisesOrTags(c, it) + of wRaises, wTags: pragmaRaisesOrTags(c, it) of wLocks: if sym == nil: pragmaLockStmt(c, it) elif sym.typ == nil: invalidPragma(c, it) - else: warningDeprecated(c.config, n.info, "'Lock levels' are deprecated, now a noop") + else: sym.typ.lockLevel = pragmaLocks(c, it) of wBitsize: if sym == nil or sym.kind != skField: invalidPragma(c, it) @@ -1213,24 +1174,35 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wExportNims: if sym == nil: invalidPragma(c, it) else: magicsys.registerNimScriptSymbol(c.graph, sym) + of wInjectStmt: + if it.kind notin nkPragmaCallKinds or it.len != 2: + localError(c.config, it.info, "expression expected") + else: + it[1] = c.semExpr(c, it[1]) of wExperimental: 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 wDoctype: - if not isTopLevel(c): - localError(c.config, n.info, "\"doctype\" pragma only valid as top-level statement") + of wThis: + if it.kind in nkPragmaCallKinds and it.len == 2: + c.selfName = considerQuotedIdent(c, it[1]) + message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated") + elif it.kind == nkIdent or it.len == 1: + c.selfName = getIdent(c.cache, "self") + message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated") + else: + localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") of wNoRewrite: noVal(c, it) of wBase: noVal(c, it) sym.flags.incl sfBase of wIntDefine: - processDefineConst(c, n, sym, mIntDefine) + sym.magic = mIntDefine of wStrDefine: - processDefineConst(c, n, sym, mStrDefine) + sym.magic = mStrDefine of wBoolDefine: - processDefineConst(c, n, sym, mBoolDefine) + sym.magic = mBoolDefine of wUsed: noVal(c, it) if sym == nil: invalidPragma(c, it) @@ -1240,15 +1212,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, pragmaProposition(c, it) of wEnsures: pragmaEnsures(c, it) - of wEnforceNoRaises: - sym.flags.incl sfNeverRaises - of wSystemRaisesDefect: - sym.flags.incl sfSystemRaisesDefect else: invalidPragma(c, it) 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, skConst, skParam, skIterator, + if sym == nil or (sym.kind in {skVar, skLet, skParam, skField, skProc, skFunc, skConverter, skMethod, skType}): n[i] = semCustomPragma(c, it) elif sym != nil: @@ -1269,29 +1237,29 @@ proc mergePragmas(n, pragmas: PNode) = else: for p in pragmas: n[pragmasPos].add p -proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, +proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = if sym != nil and sym.kind != skModule: for it in c.optionStack: let o = it.otherPragmas if not o.isNil and sfFromGeneric notin sym.flags: # see issue #12985 - pushInfoContext(c.config, info) + pushInfoContext(c.config, n.info) var i = 0 while i < o.len: if singlePragma(c, sym, o, i, validPragmas, true, false): - internalError(c.config, info, "implicitPragmas") + internalError(c.config, n.info, "implicitPragmas") inc i popInfoContext(c.config) if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o) if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: - localError(c.config, info, ".dynlib requires .exportc") + localError(c.config, n.info, ".dynlib requires .exportc") var lib = c.optionStack[^1].dynlib if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and sfImportc in sym.flags and lib != nil: incl(sym.loc.flags, lfDynamicLib) addToLib(lib, sym) - if sym.loc.r == "": sym.loc.r = rope(sym.name.s) + if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = if n == nil: return false @@ -1315,11 +1283,4 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords; isStatement: bool) = if n == nil: return pragmaRec(c, sym, n, validPragmas, isStatement) - # XXX: in the case of a callable def, this should use its info - implicitPragmas(c, sym, n.info, validPragmas) - -proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords, - isStatement: bool = false) = - if n == nil: return - if n[pragmasPos].kind != nkEmpty: - pragmaRec(c, sym, n[pragmasPos], validPragmas, isStatement) + implicitPragmas(c, sym, n, validPragmas) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 4ed9ec177..765bcded5 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -9,22 +9,19 @@ # This module implements the renderer of the standard Nim representation. -# 'import renderer' is so useful for debugging -# that Nim shouldn't produce a warning for that: -{.used.} +when defined(nimHasUsed): + # 'import renderer' is so useful for debugging + # that Nim shouldn't produce a warning for that: + {.used.} import lexer, options, idents, strutils, ast, msgs, lineinfos -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions, formatfloat] - type TRenderFlag* = enum renderNone, renderNoBody, renderNoComments, renderDocComments, renderNoPragmas, renderIds, renderNoProcDefs, renderSyms, renderRunnableExamples, - renderIr, renderNonExportedFields, renderExpandUsing - + renderIr TRenderFlags* = set[TRenderFlag] TRenderTok* = object kind*: TokType @@ -54,8 +51,6 @@ type config*: ConfigRef mangler: seq[PSym] -proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string - # We render the source code in a two phases: The first # determines how long the subtree will likely be, the second # phase appends to a buffer that will be the output. @@ -165,7 +160,6 @@ proc putNL(g: var TSrcGen) = proc optNL(g: var TSrcGen, indent: int) = g.pendingNL = indent g.lineLen = indent - g.col = g.indent when defined(nimpretty): g.pendingNewlineCount = 0 proc optNL(g: var TSrcGen) = @@ -174,7 +168,6 @@ proc optNL(g: var TSrcGen) = proc optNL(g: var TSrcGen; a, b: PNode) = g.pendingNL = g.indent g.lineLen = g.indent - g.col = g.indent when defined(nimpretty): g.pendingNewlineCount = lineDiff(a, b) proc indentNL(g: var TSrcGen) = @@ -192,7 +185,7 @@ proc dedent(g: var TSrcGen) = proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = if kind != tkSpaces: addPendingNL(g) - if s.len > 0 or kind in {tkHideableStart, tkHideableEnd}: + if s.len > 0: addTok(g, kind, s, sym) else: g.pendingWhitespace = s.len @@ -210,18 +203,18 @@ proc putComment(g: var TSrcGen, s: string) = case s[i] of '\0': break - of '\r': + of '\x0D': put(g, tkComment, com) com = "## " inc(i) - if i <= hi and s[i] == '\n': inc(i) + if i <= hi and s[i] == '\x0A': inc(i) optNL(g, ind) - of '\n': + of '\x0A': put(g, tkComment, com) com = "## " inc(i) optNL(g, ind) - of ' ', '\t': + of ' ', '\x09': com.add(s[i]) inc(i) else: @@ -249,12 +242,12 @@ proc maxLineLength(s: string): int = case s[i] of '\0': break - of '\r': + of '\x0D': inc(i) - if i <= hi and s[i] == '\n': inc(i) + if i <= hi and s[i] == '\x0A': inc(i) result = max(result, lineLen) lineLen = 0 - of '\n': + of '\x0A': inc(i) result = max(result, lineLen) lineLen = 0 @@ -268,13 +261,13 @@ proc putRawStr(g: var TSrcGen, kind: TokType, s: string) = var str = "" while i <= hi: case s[i] - of '\r': + of '\x0D': put(g, kind, str) str = "" inc(i) - if i <= hi and s[i] == '\n': inc(i) + if i <= hi and s[i] == '\x0A': inc(i) optNL(g, 0) - of '\n': + of '\x0A': put(g, kind, str) str = "" inc(i) @@ -287,7 +280,7 @@ proc putRawStr(g: var TSrcGen, kind: TokType, s: string) = proc containsNL(s: string): bool = for i in 0.. 0 +proc shouldRenderComment(g: var TSrcGen, n: PNode): bool = + result = false + if n.comment.len > 0: + result = (renderNoComments notin g.flags) or + (renderDocComments in g.flags) proc gcom(g: var TSrcGen, n: PNode) = assert(n != nil) @@ -398,24 +391,24 @@ 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[ptr int64](addr(n.floatVal)))[] , 8) + else: result = litAux(g, n, (cast[PInt64](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[ptr int32](addr(f)))[], 4) & "\'f32" + result = litAux(g, n, (cast[PInt32](addr(f)))[], 4) & "\'f32" of nkFloat64Lit: if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $n.floatVal & "\'f64" else: - result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64" + result = litAux(g, n, (cast[PInt64](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 else: result = "[type node]" else: - internalError(g.config, "renderer.atom " & $n.kind) + internalError(g.config, "rnimsyn.atom " & $n.kind) result = "" proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int = @@ -434,28 +427,10 @@ proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int = result = 0 for i in start..n.len + theEnd: inc(result, lsub(g, n[i])) -proc origUsingType(n: PNode): PSym {.inline.} = - ## Returns the type that a parameter references. Check with referencesUsing first - ## to check `n` is actually referencing a using node - # If the node is untyped the typ field will be nil - if n[0].sym.typ != nil: - n[0].sym.typ.sym - else: nil - -proc referencesUsing(n: PNode): bool = - ## Returns true if n references a using statement. - ## e.g. proc foo(x) # x doesn't have type or def value so it references a using - result = n.kind == nkIdentDefs and - # Sometimes the node might not have been semmed (e.g. doc0) and will be nkIdent instead - n[0].kind == nkSym and - # Templates/macros can have parameters with no type (But their orig type will be nil) - n.origUsingType != nil and - n[1].kind == nkEmpty and n[2].kind == nkEmpty - proc lsub(g: TSrcGen; n: PNode): int = # computes the length of a tree if isNil(n): return 0 - if shouldRenderComment(g, n): return MaxLineLen + 1 + if n.comment.len > 0: return MaxLineLen + 1 case n.kind of nkEmpty: result = 0 of nkTripleStrLit: @@ -492,11 +467,8 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkDo: result = lsons(g, n) + len("do__:_") of nkConstDef, nkIdentDefs: result = lcomma(g, n, 0, - 3) - if n.referencesUsing: - result += lsub(g, newSymNode(n.origUsingType)) + 2 - else: - if n[^2].kind != nkEmpty: result += lsub(g, n[^2]) + 2 - if n[^1].kind != nkEmpty: result += lsub(g, n[^1]) + 3 + if n[^2].kind != nkEmpty: result += lsub(g, n[^2]) + 2 + if n[^1].kind != nkEmpty: result += lsub(g, n[^1]) + 3 of nkVarTuple: if n[^1].kind == nkEmpty: result = lcomma(g, n, 0, - 2) + len("()") @@ -527,7 +499,7 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n[0]) else: 0)+len("typeof()") of nkRefTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ref") of nkPtrTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ptr") - of nkVarTy, nkOutTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("var") + of nkVarTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("var") of nkDistinctTy: result = len("distinct") + (if n.len > 0: lsub(g, n[0])+1 else: 0) if n.len > 1: @@ -539,7 +511,7 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkOfInherit: result = lsub(g, n[0]) + len("of_") of nkProcTy: result = lsons(g, n) + len("proc_") of nkIteratorTy: result = lsons(g, n) + len("iterator_") - of nkSinkAsgn: result = lsons(g, n) + len("`=sink`(, )") + of nkSharedTy: result = lsons(g, n) + len("shared_") of nkEnumTy: if n.len > 0: result = lsub(g, n[0]) + lcomma(g, n, 1) + len("enum_") @@ -595,11 +567,11 @@ proc initContext(c: var TContext) = c.spacing = 0 c.flags = {} -proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) -proc gsub(g: var TSrcGen, n: PNode, fromStmtList = false) = +proc gsub(g: var TSrcGen, n: PNode, c: TContext) +proc gsub(g: var TSrcGen, n: PNode) = var c: TContext initContext(c) - gsub(g, n, c, fromStmtList = fromStmtList) + gsub(g, n, c) proc hasCom(n: PNode): bool = result = false @@ -615,45 +587,23 @@ proc putWithSpace(g: var TSrcGen, kind: TokType, s: string) = put(g, kind, s) put(g, tkSpaces, Space) -proc isHideable(config: ConfigRef, n: PNode): bool = - # xxx compare `ident` directly with `getIdent(cache, wRaises)`, but - # this requires a `cache`. - case n.kind - of nkExprColonExpr: - result = n[0].kind == nkIdent and - n[0].ident.s.nimIdentNormalize in ["raises", "tags", "extern", "deprecated", "forbids", "stacktrace"] - of nkIdent: result = n.ident.s in ["gcsafe", "deprecated"] - else: result = false - proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0, theEnd: int = - 1, separator = tkComma) = - let inPragma = g.inPragma == 1 # just the top-level - var inHideable = false for i in start..n.len + theEnd: - let c = i < n.len + theEnd - let sublen = lsub(g, n[i]) + ord(c) + var c = i < n.len + theEnd + var sublen = lsub(g, n[i]) + ord(c) if not fits(g, g.lineLen + sublen) and (ind + sublen < MaxLineLen): optNL(g, ind) let oldLen = g.tokens.len - if inPragma: - if not inHideable and isHideable(g.config, n[i]): - inHideable = true - put(g, tkHideableStart, "") - elif inHideable and not isHideable(g.config, n[i]): - inHideable = false - put(g, tkHideableEnd, "") gsub(g, n[i]) if c: if g.tokens.len > oldLen: putWithSpace(g, separator, $separator) - if shouldRenderComment(g) and hasCom(n[i]): + if hasCom(n[i]): gcoms(g) optNL(g, ind) - if inHideable: - put(g, tkHideableEnd, "") - inHideable = false proc gcomma(g: var TSrcGen, n: PNode, c: TContext, start: int = 0, - theEnd: int = -1) = + theEnd: int = - 1) = var ind: int if rfInConstExpr in c.flags: ind = g.indent + IndentWidth @@ -689,7 +639,7 @@ proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TokType, dedent(g) proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool = - result = shouldRenderComment(g, n) + result = n.comment.len > 0 if not result: # check further for i in start..n.len + theEnd: @@ -709,7 +659,7 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) = if n[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: gstmts(g, n[i], c, doIndent=false) else: - gsub(g, n[i], fromStmtList = true) + gsub(g, n[i]) gcoms(g) if doIndent: dedent(g) else: @@ -870,28 +820,14 @@ proc gTypeClassTy(g: var TSrcGen, n: PNode) = dedent(g) proc gblock(g: var TSrcGen, n: PNode) = - # you shouldn't simplify it to `n.len < 2` - # because the following codes should be executed - # even when block stmt has only one child for getting - # better error messages. - if n.len == 0: - return - var c: TContext initContext(c) - if n[0].kind != nkEmpty: putWithSpace(g, tkBlock, "block") gsub(g, n[0]) else: put(g, tkBlock, "block") - - # block stmt should have two children - if n.len == 1: - return - putWithSpace(g, tkColon, ":") - if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) @@ -954,7 +890,6 @@ proc doParamsAux(g: var TSrcGen, params: PNode) = put(g, tkParRi, ")") if params.len > 0 and params[0].kind != nkEmpty: - put(g, tkSpaces, Space) putWithSpace(g, tkOpr, "->") gsub(g, params[0]) @@ -993,12 +928,14 @@ proc skipHiddenNodes(n: PNode): PNode = else: break proc accentedName(g: var TSrcGen, n: PNode) = - # This is for cases where ident should've really been a `nkAccQuoted`, e.g. `:tmp` - # or if user writes a macro with `ident":foo"`. It's unclear whether these should be legal. - const backticksNeeded = OpChars + {'[', '{', '\''} + const backticksNeeded = OpChars + {'[', '{'} if n == nil: return - let ident = n.getPIdent - if ident != nil and ident.s[0] in backticksNeeded: + let isOperator = + if n.kind == nkIdent and n.ident.s.len > 0 and n.ident.s[0] in backticksNeeded: true + elif n.kind == nkSym and n.sym.name.s.len > 0 and n.sym.name.s[0] in backticksNeeded: true + else: false + + if isOperator: put(g, tkAccent, "`") gident(g, n) put(g, tkAccent, "`") @@ -1025,40 +962,11 @@ proc infixArgument(g: var TSrcGen, n: PNode, i: int) = if needsParenthesis: put(g, tkParRi, ")") -const postExprBlocks = {nkStmtList, nkStmtListExpr, - nkOfBranch, nkElifBranch, nkElse, - nkExceptBranch, nkFinally, nkDo} - -proc postStatements(g: var TSrcGen, n: PNode, i: int, fromStmtList: bool) = - var i = i - if n[i].kind in {nkStmtList, nkStmtListExpr}: - if fromStmtList: - put(g, tkColon, ":") - else: - put(g, tkSpaces, Space) - put(g, tkDo, "do") - put(g, tkColon, ":") - gsub(g, n, i) - i.inc - for j in i ..< n.len: - if n[j].kind == nkDo: - optNL(g) - elif n[j].kind in {nkStmtList, nkStmtListExpr}: - optNL(g) - put(g, tkDo, "do") - put(g, tkColon, ":") - gsub(g, n, j) - -proc isCustomLit(n: PNode): bool = - if n.len == 2 and n[0].kind == nkRStrLit: - let ident = n[1].getPIdent - result = ident != nil and ident.s.startsWith('\'') - -proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = +proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if isNil(n): return var a: TContext - if shouldRenderComment(g, n): pushCom(g, n) + if n.comment.len > 0: pushCom(g, n) case n.kind # atoms: of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n)) of nkEmpty: discard @@ -1083,15 +991,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = of nkCharLit: put(g, tkCharLit, atom(g, n)) of nkNilLit: put(g, tkNil, atom(g, n)) # complex expressions of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr: - if n.len > 1 and n.lastSon.kind in postExprBlocks: + if n.len > 1 and n.lastSon.kind in {nkStmtList, nkStmtListExpr}: accentedName(g, n[0]) - var i = 1 - while i < n.len and n[i].kind notin postExprBlocks: i.inc - if i > 1: + if n.len > 2: put(g, tkParLe, "(") - gcomma(g, n, 1, i - 1 - n.len) + gcomma(g, n, 1, -2) put(g, tkParRi, ")") - postStatements(g, n, i, fromStmtList) + put(g, tkColon, ":") + gsub(g, n, n.len-1) elif n.len >= 1: case bracketKind(g, n[0]) of bkBracket: @@ -1191,12 +1098,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = of nkCommand: accentedName(g, n[0]) put(g, tkSpaces, Space) - if n.len > 1 and n.lastSon.kind in postExprBlocks: - var i = 1 - while i < n.len and n[i].kind notin postExprBlocks: i.inc - if i > 1: - gcomma(g, n, 1, i - 1 - n.len) - postStatements(g, n, i, fromStmtList) + if n[^1].kind == nkStmtList: + for i, child in n: + if i > 1 and i < n.len - 1: + put(g, tkComma, ",") + elif i == n.len - 1: + put(g, tkColon, ":") + if i > 0: + gsub(g, child) else: gcomma(g, n, 1) of nkExprEqExpr, nkAsgn, nkFastAsgn: @@ -1204,11 +1113,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n, 1) - of nkSinkAsgn: - put(g, tkSymbol, "`=sink`") - put(g, tkParLe, "(") - gcomma(g, n) - put(g, tkParRi, ")") of nkChckRangeF: put(g, tkSymbol, "chckRangeF") put(g, tkParLe, "(") @@ -1225,9 +1129,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gcomma(g, n) put(g, tkParRi, ")") of nkObjDownConv, nkObjUpConv: - let typ = if (n.typ != nil) and (n.typ.sym != nil): n.typ.sym.name.s else: "" - put(g, tkParLe, typ & "(") if n.len >= 1: gsub(g, n[0]) + put(g, tkParLe, "(") + gcomma(g, n, 1) put(g, tkParRi, ")") of nkClosedSymChoice, nkOpenSymChoice: if renderIds in g.flags: @@ -1252,7 +1156,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = of nkTupleConstr: put(g, tkParLe, "(") gcomma(g, n, c) - if n.len == 1 and n[0].kind != nkExprColonExpr: put(g, tkComma, ",") + if n.len == 1: put(g, tkComma, ",") put(g, tkParRi, ")") of nkCurly: put(g, tkCurlyLe, "{") @@ -1270,25 +1174,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gcomma(g, n, c) put(g, tkBracketRi, "]") of nkDotExpr: - if isCustomLit(n): - put(g, tkCustomLit, n[0].strVal) - gsub(g, n, 1) - else: - gsub(g, n, 0) - put(g, tkDot, ".") - assert n.len == 2, $n.len - accentedName(g, n[1]) + gsub(g, n, 0) + put(g, tkDot, ".") + gsub(g, n, 1) of nkBind: putWithSpace(g, tkBind, "bind") gsub(g, n, 0) of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString: - if renderIds in g.flags: - put(g, tkAddr, $n.kind) - put(g, tkParLe, "(") gsub(g, n, 0) - if renderIds in g.flags: - put(g, tkParRi, ")") - of nkLambda: putWithSpace(g, tkProc, "proc") gsub(g, n, paramsPos) @@ -1307,11 +1200,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gcomma(g, n, 0, -3) if n.len >= 2 and n[^2].kind != nkEmpty: putWithSpace(g, tkColon, ":") - gsub(g, n[^2], c) - elif n.referencesUsing and renderExpandUsing in g.flags: - putWithSpace(g, tkColon, ":") - gsub(g, newSymNode(n.origUsingType), c) - + gsub(g, n, n.len - 2) if n.len >= 1 and n[^1].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") @@ -1343,12 +1232,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = else: put(g, tkSpaces, Space) infixArgument(g, n, 2) - if n.len > 3 and n.lastSon.kind in postExprBlocks: - var i = 3 - while i < n.len and n[i].kind notin postExprBlocks: i.inc - postStatements(g, n, i, fromStmtList) of nkPrefix: - gsub(g, n, 0) + if n.len > 0 and n[0].kind == nkIdent and n[0].ident.s == "": + discard "XXX Remove this hack after 0.20 has been released!" + else: + gsub(g, n, 0) if n.len > 1: let opr = if n[0].kind == nkIdent: n[0].ident elif n[0].kind == nkSym: n[0].sym.name @@ -1363,10 +1251,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = put(g, tkParRi, ")") else: gsub(g, n[1]) - if n.len > 2 and n.lastSon.kind in postExprBlocks: - var i = 2 - while i < n.len and n[i].kind notin postExprBlocks: i.inc - postStatements(g, n, i, fromStmtList) of nkPostfix: gsub(g, n, 1) gsub(g, n, 0) @@ -1379,26 +1263,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = put(g, tkOpr, "[]") of nkAccQuoted: put(g, tkAccent, "`") - for i in 0.. 0 and tmp[0] in {'a'..'z', 'A'..'Z'} - var useSpace = false - if i == 1 and n[0].kind == nkIdent and n[0].ident.s in ["=", "'"]: - if not n[1].isAlpha: # handle `=destroy`, `'big' - useSpace = true - elif i == 1 and n[1].kind == nkIdent and n[1].ident.s == "=": - if not n[0].isAlpha: # handle setters, e.g. `foo=` - useSpace = true - elif i > 0: useSpace = true - if useSpace: put(g, tkSpaces, Space) + if n.len > 0: gsub(g, n[0]) + for i in 1.. 0: - putWithSpace(g, tkOut, "out") - gsub(g, n[0]) - else: - put(g, tkOut, "out") of nkDistinctTy: if n.len > 0: putWithSpace(g, tkDistinct, "distinct") @@ -1482,11 +1343,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = of nkRecList: indentNL(g) for i in 0.." @@ -1784,7 +1633,7 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = proc `$`*(n: PNode): string = n.renderTree -proc renderModule*(n: PNode, outfile: string, +proc renderModule*(n: PNode, infile, outfile: string, renderFlags: TRenderFlags = {}; fid = FileIndex(-1); conf: ConfigRef = nil) = @@ -1826,3 +1675,15 @@ proc getTokSym*(r: TSrcGen): PSym = result = r.tokens[r.idx-1].sym else: result = nil + +proc quoteExpr*(a: string): string {.inline.} = + ## can be used for quoting expressions in error msgs. + "'" & a & "'" + +proc genFieldDefect*(field: PSym, disc: PSym): string = + ## this needs to be in a module accessible by jsgen, ccgexprs, and vm to + ## provide this error msg FieldDefect; msgs would be better but it does not + ## import ast + result = field.name.s.quoteExpr & " is not accessible using discriminant " & + disc.name.s.quoteExpr & " of type " & + disc.owner.name.s.quoteExpr diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim index 792079b3f..02d405844 100644 --- a/compiler/renderverbatim.nim +++ b/compiler/renderverbatim.nim @@ -1,10 +1,8 @@ import strutils +from xmltree import addEscaped import ast, options, msgs - -when defined(nimPreviewSlimSystem): - import std/assertions - +import packages/docutils/highlite const isDebug = false when isDebug: @@ -87,7 +85,7 @@ proc startOfLineInsideTriple(ldata: LineData, line: int): bool = if index >= ldata.lines.len: false else: ldata.lines[index] -proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode, indent = 0): string = +proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string = ## TLineInfo.offsetA,offsetB would be cleaner but it's only enabled for nimpretty, ## we'd need to check performance impact to enable it for nimdoc. var first = n.lastSon.info @@ -106,7 +104,7 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode, indent = 0): stri let last = n.lastNodeRec.info var info = first - var indent2 = info.col + var indent = info.col let numLines = numLines(conf, info.fileIndex).uint16 var lastNonemptyPos = 0 @@ -122,15 +120,33 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode, indent = 0): stri info.line = line let src = sourceLine(conf, info) let special = startOfLineInsideTriple(ldata, line.int) - if line > last.line and not special and not isInIndentationBlock(src, indent2): + if line > last.line and not special and not isInIndentationBlock(src, indent): break if line > first.line: result.add "\n" if special: result.add src lastNonemptyPos = result.len - elif src.len > indent2: - for i in 0.. indent: + result.add src[indent..^1] lastNonemptyPos = result.len result.setLen lastNonemptyPos +proc renderNimCode*(result: var string, code: string, isLatex = false) = + var toknizr: GeneralTokenizer + initGeneralTokenizer(toknizr, code) + var buf = "" + template append(kind, val) = + buf.setLen 0 + buf.addEscaped(val) + let class = tokenClassToStr[kind] + if isLatex: + result.addf "\\span$1{$2}", [class, buf] + else: + result.addf "$2", [class, buf] + while true: + getNextToken(toknizr, langNim) + case toknizr.kind + of gtEof: break # End Of File (or string) + else: + # TODO: avoid alloc; maybe toOpenArray + append(toknizr.kind, substr(code, toknizr.start, toknizr.length + toknizr.start - 1)) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 4ad3f1219..72f8666a2 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -4,12 +4,6 @@ import msgs, modulegraphs, syntaxes, options, modulepaths, lineinfos -when defined(nimPreviewSlimSystem): - import std/assertions - -when defined(nimDebugReorder): - import std/tables - type DepN = ref object pnode: PNode @@ -17,11 +11,11 @@ type onStack: bool kids: seq[DepN] hAQ, hIS, hB, hCmd: int - when defined(nimDebugReorder): + when defined(debugReorder): expls: seq[string] DepG = seq[DepN] -when defined(nimDebugReorder): +when defined(debugReorder): var idNames = newTable[int, string]() proc newDepN(id: int, pnode: PNode): DepN = @@ -36,14 +30,17 @@ proc newDepN(id: int, pnode: PNode): DepN = result.hIS = -1 result.hB = -1 result.hCmd = -1 - when defined(nimDebugReorder): + when defined(debugReorder): result.expls = @[] proc accQuoted(cache: IdentCache; n: PNode): PIdent = var id = "" for i in 0..", nodecl, varargs.} - -when not declared(signbit): - proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "".} - proc signbit*(x: SomeFloat): bool {.inline.} = - result = c_signbit(x) != 0 - -import std/formatfloat - -proc toStrMaxPrecision*(f: BiggestFloat | float32): string = - const literalPostfix = when f is float32: "f" else: "" +proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = case classify(f) of fcNan: - if signbit(f): - result = "-NAN" - else: - result = "NAN" + result = "NAN" of fcNegZero: result = "-0.0" & literalPostfix of fcZero: @@ -61,8 +46,9 @@ proc toStrMaxPrecision*(f: BiggestFloat | float32): string = of fcNegInf: result = "-INF" else: - result.addFloatRoundtrip(f) - result.add literalPostfix + result = newString(81) + let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring) + setLen(result, n) proc encodeStr*(s: string, result: var string) = for i in 0.. 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)) 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. - write(f, r) + for s in leaves(r): write(f, s) proc writeRope*(head: Rope, filename: AbsoluteFile): bool = var f: File if open(f, filename.string, fmWrite): - writeRope(f, head) + if head != nil: 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 = newRopeAppender() + result = nil var num = 0 while i < frmt.len: if frmt[i] == '$': @@ -108,6 +266,7 @@ 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) @@ -116,10 +275,21 @@ 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*(s: Rope, f: File): bool = +proc equalsFile*(r: Rope, f: File): bool = ## returns true if the contents of the file `f` equal `r`. var buf: array[bufSize, char] @@ -128,7 +298,7 @@ proc equalsFile*(s: Rope, f: File): bool = btotal = 0 rtotal = 0 - when true: + for s in leaves(r): var spos = 0 rtotal += s.len while spos < s.len: @@ -159,3 +329,10 @@ proc equalsFile*(r: Rope, filename: AbsoluteFile): bool = if result: result = equalsFile(r, f) close(f) + +proc writeRopeIfNotEqual*(r: Rope, filename: AbsoluteFile): bool = + # returns true if overwritten + if not equalsFile(r, filename): + result = writeRope(r, filename) + else: + result = false diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 27ea94aae..ef1ceb12b 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -11,13 +11,10 @@ ## language. import - ast, modules, idents, condsyms, - options, llstream, vm, vmdef, commands, + ast, modules, idents, passes, condsyms, + options, sem, llstream, vm, vmdef, commands, os, times, osproc, wordrecg, strtabs, modulegraphs, - pathutils, pipelines - -when defined(nimPreviewSlimSystem): - import std/syncio + pathutils # we support 'cmpIgnoreStyle' natively for efficiency: from strutils import cmpIgnoreStyle, contains @@ -154,9 +151,18 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1)) cbconf setCommand: conf.setCommandEarly(a.getString 0) + # xxx move remaining logic to commands.nim or other let arg = a.getString 1 incl(conf.globalOptions, optWasNimscript) - if arg.len > 0: setFromProjectName(conf, arg) + if arg.len > 0: + conf.projectName = arg + let path = + if conf.projectName.isAbsolute: AbsoluteFile(conf.projectName) + else: conf.projectPath / RelativeFile(conf.projectName) + try: + conf.projectFull = canonicalizePath(conf, path) + except OSError: + conf.projectFull = path cbconf getCommand: setResult(a, conf.command) cbconf switch: @@ -181,13 +187,15 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; options.cppDefine(conf, a.getString(0)) cbexc stdinReadLine, EOFError: if defined(nimsuggest) or graph.config.cmd == cmdCheck: - setResult(a, "") + discard else: + setResult(a, "") setResult(a, stdin.readLine()) cbexc stdinReadAll, EOFError: if defined(nimsuggest) or graph.config.cmd == cmdCheck: - setResult(a, "") + discard else: + setResult(a, "") setResult(a, stdin.readAll()) proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; @@ -197,11 +205,13 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; conf.symbolFiles = disabledSf let graph = newModuleGraph(cache, conf) - connectPipelineCallbacks(graph) + connectCallbacks(graph) if freshDefines: initDefines(conf.symbols) defineSymbol(conf.symbols, "nimscript") defineSymbol(conf.symbols, "nimconfig") + registerPass(graph, semPass) + registerPass(graph, evalPass) conf.searchPaths.add(conf.libpath) @@ -216,9 +226,8 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; var vm = setupVM(m, cache, scriptName.string, graph, idgen) graph.vm = vm - graph.setPipeLinePass(EvalPass) - graph.compilePipelineSystemModule() - discard graph.processPipelineModule(m, vm.idgen, stream) + graph.compileSystemModule() + discard graph.processModule(m, vm.idgen, stream) # watch out, "newruntime" can be set within NimScript itself and then we need # to remember this: diff --git a/compiler/sem.nim b/compiler/sem.nim index 48a7d56c8..d5ae5a21d 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -13,11 +13,13 @@ import ast, strutils, options, astalgo, trees, wordrecg, ropes, msgs, idents, renderer, types, platform, math, magicsys, nversion, nimsets, semfold, modulepaths, importer, - procfind, lookups, pragmas, semdata, semtypinst, sigmatch, + procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, lowerings, plugins/active, lineinfos, strtabs, int128, - isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs + isolation_check, typeallowed + +from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward when defined(nimfix): import nimfix/prettybase @@ -25,20 +27,18 @@ when defined(nimfix): when not defined(leanCompiler): import spawn -when defined(nimPreviewSlimSystem): - import std/formatfloat - # implementation -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 semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode +proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semExprNoType(c: PContext, n: PNode): PNode proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode -proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode +proc semProcBody(c: PContext, n: PNode): PNode proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode proc changeType(c: PContext; n: PNode, newType: PType, check: bool) +proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode proc semTypeNode(c: PContext, n: PNode, prev: PType): PType proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode proc semOpAux(c: PContext, n: PNode) @@ -51,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; expectedType: PType = nil): PNode +proc semStaticExpr(c: PContext, n: PNode): PNode proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType proc semTypeOf(c: PContext; n: PNode): PNode proc computeRequiresInit(c: PContext, t: PType): bool @@ -80,11 +80,10 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode = let x = arg.skipConv - if (x.kind == nkCurly and formal.kind == tySet and formal.base.kind != tyGenericParam) or - (x.kind in {nkPar, nkTupleConstr}) and formal.kind notin {tyUntyped, tyBuiltInTypeClass, tyAnything}: + if x.kind in {nkPar, nkTupleConstr, nkCurly} and formal.kind != tyUntyped: changeType(c, x, formal, check=true) result = arg - result = skipHiddenSubConv(result, c.graph, c.idgen) + result = skipHiddenSubConv(result, c.idgen) proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = @@ -94,12 +93,6 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = # error correction: result = copyTree(arg) result.typ = formal - elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum: - # Pick the right 'sym' from the sym choice by looking at 'formal' type: - for ch in arg: - if sameType(ch.typ, formal): - return getConstExpr(c.module, ch, c.idgen, c.graph) - typeMismatch(c.config, info, formal, arg.typ, arg) else: result = indexTypesMatch(c, formal, arg.typ, arg) if result == nil: @@ -110,16 +103,6 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = else: result = fitNodePostMatch(c, formal, result) -proc fitNodeConsiderViewType(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = - let a = fitNode(c, formal, arg, info) - if formal.kind in {tyVar, tyLent}: - #classifyViewType(formal) != noView: - result = newNodeIT(nkHiddenAddr, a.info, formal) - result.add a - formal.flags.incl tfVarIsPtr - else: - result = a - proc inferWithMetatype(c: PContext, formal: PType, arg: PNode, coerceDistincts = false): PNode @@ -154,10 +137,7 @@ proc commonType*(c: PContext; x, y: PType): PType = let aEmpty = isEmptyContainer(a[i]) let bEmpty = isEmptyContainer(b[i]) if aEmpty != bEmpty: - if nt.isNil: - nt = copyType(a, nextTypeId(c.idgen), a.owner) - copyTypeProps(c.graph, c.idgen.module, nt, a) - + if nt.isNil: nt = copyType(a, nextTypeId(c.idgen), a.owner) nt[i] = if aEmpty: b[i] else: a[i] if not nt.isNil: result = nt #elif b[idx].kind == tyEmpty: return x @@ -166,7 +146,7 @@ proc commonType*(c: PContext; x, y: PType): PType = # range[0..4]. But then why is (range[0..4], 6) not range[0..6]? # But then why is (2,4) not range[2..4]? But I think this would break # too much code. So ... it's the same range or the base type. This means - # typeof(if b: 0 else 1) == int and not range[0..1]. For now. In the long + # type(if b: 0 else 1) == int and not range[0..1]. For now. In the long # run people expect ranges to work properly within a tuple. if not sameType(a, b): result = skipTypes(a, {tyRange}).skipIntLit(c.idgen) @@ -179,12 +159,6 @@ proc commonType*(c: PContext; x, y: PType): PType = result = b #.skipIntLit elif a.kind in IntegralTypes and a.n != nil: result = a #.skipIntLit - elif a.kind == tyProc and b.kind == tyProc: - if a.callConv == ccClosure and b.callConv != ccClosure: - result = x - elif compatibleEffects(a, b) != efCompat or - (b.flags * {tfNoSideEffect, tfGcSafe}) < (a.flags * {tfNoSideEffect, tfGcSafe}): - result = y else: var k = tyNone if a.kind in {tyRef, tyPtr}: @@ -272,12 +246,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; expectedType: PType = nil): PNode +proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode proc semTemplateExpr(c: PContext, n: PNode, s: PSym, - flags: TExprFlags = {}; expectedType: PType = nil): PNode + flags: TExprFlags = {}): PNode proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, - flags: TExprFlags = {}; expectedType: PType = nil): PNode + flags: TExprFlags = {}): PNode proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym = if t.sym != nil: return t.sym @@ -322,7 +296,8 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = result = evaluated let expectedType = eOrig.typ.skipTypes({tyStatic}) if hasCycle(result): - result = localErrorNode(c, eOrig, "the resulting AST is cyclic and cannot be processed further") + globalError(c.config, eOrig.info, "the resulting AST is cyclic and cannot be processed further") + result = errorNode(c, eOrig) else: semmacrosanity.annotateType(result, expectedType, c.config) else: @@ -337,8 +312,8 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = isArrayConstr(arg): arg.typ = eOrig.typ -proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = - var e = semExprWithType(c, n, expectedType = expectedType) +proc tryConstExpr(c: PContext, n: PNode): PNode = + var e = semExprWithType(c, n) if e == nil: return result = getConstExpr(c.module, e, c.idgen, c.graph) @@ -368,13 +343,11 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = const errConstExprExpected = "constant expression expected" -proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = - var e = semExprWithType(c, n, expectedType = expectedType) +proc semConstExpr(c: PContext, n: PNode): PNode = + var e = semExprWithType(c, n) if e == nil: localError(c.config, n.info, errConstExprExpected) return n - if e.kind in nkSymChoices and e[0].typ.skipTypes(abstractInst).kind == tyEnum: - return e result = getConstExpr(c.module, e, c.idgen, c.graph) if result == nil: #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected) @@ -391,30 +364,32 @@ proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = else: result = fixupTypeAfterEval(c, result, e) -proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = +proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags): PNode = if efNeedStatic in flags: if efPreferNilResult in flags: - return tryConstExpr(c, n, expectedType) + return tryConstExpr(c, n) else: - return semConstExpr(c, n, expectedType) + return semConstExpr(c, n) else: - result = semExprWithType(c, n, flags, expectedType) + result = semExprWithType(c, n, flags) if efPreferStatic in flags: var evaluated = getConstExpr(c.module, result, c.idgen, c.graph) if evaluated != nil: return evaluated evaluated = evalAtCompileTime(c, result) if evaluated != nil: return evaluated +when not defined(nimHasSinkInference): + {.pragma: nosinks.} + include hlo, seminst, semcall proc resetSemFlag(n: PNode) = - if n != nil: - excl n.flags, nfSem - for i in 0.. 0: - var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTypSkip)) - asgnExpr.typ = aTypSkip - asgnExpr.sons.add child - result = semExpr(c, asgnExpr) - elif aTypSkip.kind == tyArray: - let child = defaultNodeField(c, a, aTypSkip[1]) - - if child != nil: - let node = newNode(nkIntLit) - node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip)) - result = semExpr(c, newTree(nkCall, newSymNode(getCompilerProc(c.graph, "nimArrayWith"), a.info), - semExprWithType(c, child), - node - )) - result.typ = aTyp - elif aTypSkip.kind == tyTuple: - var hasDefault = false - if aTypSkip.n != nil: - let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault) - if hasDefault and children.len > 0: - result = newNodeI(nkTupleConstr, a.info) - result.typ = aTyp - result.sons.add children - result = semExpr(c, result) - -proc defaultNodeField(c: PContext, a: PNode): PNode = - result = defaultNodeField(c, a, a.typ) - -include semtempl, semgnrc, semstmts, semexprs +include semtypes, semtempl, semgnrc, semstmts, semexprs proc addCodeForGenerics(c: PContext, n: PNode) = for i in c.lastGenericIdx.. high(syms): - # we have reached the end + noSyms = false + if noSyms: + sym = nextOverloadIter(o, c, headSymbol) + scope = o.lastOverloadScope + elif nextSymIndex < syms.len: + # rare case: retrieve the next pre-calculated symbol + sym = syms[nextSymIndex].s + scope = syms[nextSymIndex].scope + nextSymIndex += 1 + else: break - # advance to next sym - sym = syms[nextSymIndex].s - scope = syms[nextSymIndex].scope - inc(nextSymIndex) - - proc effectProblem(f, a: PType; result: var string; c: PContext) = if f.kind == tyProc and a.kind == tyProc: if tfThread in f.flags and tfThread notin a.flags: @@ -164,10 +145,9 @@ proc effectProblem(f, a: PType; result: var string; c: PContext) = of efTagsUnknown: result.add "\n The `.tags` requirements differ. Annotate the " & "proc with {.tags: [].} 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." + of efLockLevelsDiffer: + result.add "\n The `.locks` requirements differ. Annotate the " & + "proc with {.locks: 0.} to get extended error information." when defined(drnim): if not c.graph.compatibleProps(c.graph, f, a): result.add "\n The `.requires` or `.ensures` properties are incompatible." @@ -207,7 +187,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): # argument in order to remove plenty of candidates. This is # comparable to what C# does and C# is doing fine. var filterOnlyFirst = false - if optShowAllMismatches notin c.config.globalOptions and verboseTypeMismatch in c.config.legacyFeatures: + if optShowAllMismatches notin c.config.globalOptions: for err in errors: if err.firstMismatch.arg > 1: filterOnlyFirst = true @@ -223,10 +203,6 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): if filterOnlyFirst and err.firstMismatch.arg == 1: inc skipped continue - - if verboseTypeMismatch notin c.config.legacyFeatures: - candidates.add "[" & $err.firstMismatch.arg & "] " - if err.sym.kind in routineKinds and err.sym.ast != nil: candidates.add(renderTree(err.sym.ast, {renderNoBody, renderNoComments, renderNoPragmas})) @@ -236,7 +212,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add("\n") let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: "" - if n.len > 1 and verboseTypeMismatch in c.config.legacyFeatures: + if n.len > 1: candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg) # candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging case err.firstMismatch.kind @@ -251,10 +227,11 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): of kMissingParam: candidates.add("\n missing parameter: " & nameParam) of kTypeMismatch, kVarNeeded: doAssert nArg != nil - let wanted = err.firstMismatch.formal.typ + var wanted = err.firstMismatch.formal.typ doAssert err.firstMismatch.formal != nil candidates.add("\n required type for " & nameParam & ": ") - candidates.addTypeDeclVerboseMaybe(c.config, wanted) + candidates.add typeToString(wanted) + candidates.addDeclaredLocMaybe(c.config, wanted) candidates.add "\n but expression '" if err.firstMismatch.kind == kVarNeeded: candidates.add renderNotLValue(nArg) @@ -262,16 +239,11 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): else: candidates.add renderTree(nArg) candidates.add "' is of type: " - let got = nArg.typ - candidates.addTypeDeclVerboseMaybe(c.config, got) + var got = nArg.typ + candidates.add typeToString(got) + candidates.addDeclaredLocMaybe(c.config, got) doAssert wanted != nil - if got != nil: - if got.kind == tyProc and wanted.kind == tyProc: - # These are proc mismatches so, - # add the extra explict detail of the mismatch - candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) - effectProblem(wanted, got, candidates, c) - + if got != nil: effectProblem(wanted, got, candidates, c) of kUnknown: discard "do not break 'nim check'" candidates.add "\n" if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and @@ -292,21 +264,24 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): const errTypeMismatch = "type mismatch: got <" - errButExpected = "but expected one of:" - errExpectedPosition = "Expected one of (first mismatch at position [#]):" + errButExpected = "but expected one of: " errUndeclaredField = "undeclared field: '$1'" errUndeclaredRoutine = "attempting to call undeclared routine: '$1'" errBadRoutine = "attempting to call routine: '$1'$2" errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3" -proc describeParamList(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string = - result = "Expression: " & $n - for i in startIdx.. 0: " " & result else: "" - result = errUndeclaredField % ident & typeHint & suffix + result = errUndeclaredField % ident & typeHint & " " & result else: if result.len == 0: result = errUndeclaredRoutine % ident else: result = errBadRoutine % [ident, result] @@ -416,18 +353,30 @@ proc resolveOverloads(c: PContext, n, orig: PNode, else: initialBinding = nil - pickBestCandidate(c, f, n, orig, initialBinding, - filter, result, alt, errors, efExplain in flags, - errorsEnabled, flags) - - var dummyErrors: CandidateErrors - template pickSpecialOp(headSymbol) = + template pickBest(headSymbol) = pickBestCandidate(c, headSymbol, n, orig, initialBinding, - filter, result, alt, dummyErrors, efExplain in flags, - false, flags) + filter, result, alt, errors, efExplain in flags, + errorsEnabled) + pickBest(f) 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 @@ -440,7 +389,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, let op = newIdentNode(getIdent(c.cache, x), n.info) n[0] = op orig[0] = op - pickSpecialOp(op) + pickBest(op) if nfExplicitCall in n.flags: tryOp ".()" @@ -454,14 +403,10 @@ proc resolveOverloads(c: PContext, n, orig: PNode, let callOp = newIdentNode(getIdent(c.cache, ".="), n.info) n.sons[0..1] = [callOp, n[1], calleeName] orig.sons[0..1] = [callOp, orig[1], calleeName] - pickSpecialOp(callOp) + pickBest(callOp) if overloadsState == csEmpty and result.state == csEmpty: if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim - result.state = csNoMatch - if efNoDiagnostics in flags: - return - # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident) localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f)) return elif result.state != csMatch: @@ -499,7 +444,7 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) = let a = if a.kind == nkHiddenDeref: a[0] else: a if a.kind == nkHiddenCallConv and a[0].kind == nkSym: let s = a[0].sym - if s.isGenericRoutineStrict: + if s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty: let finalCallee = generateInstance(c, s, x.bindings, a.info) a[0].sym = finalCallee a[0].typ = finalCallee.typ @@ -578,7 +523,7 @@ proc semResolvedCall(c: PContext, x: TCandidate, if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor return let gp = finalCallee.ast[genericParamsPos] - if gp.isGenericParams: + if gp.kind != nkEmpty: if x.calleeSym.kind notin {skMacro, skTemplate}: if x.calleeSym.magic in {mArrGet, mArrPut}: finalCallee = x.calleeSym @@ -590,8 +535,8 @@ proc semResolvedCall(c: PContext, x: TCandidate, for s in instantiateGenericParamList(c, gp, x.bindings): case s.kind of skConst: - if not s.astdef.isNil: - x.call.add s.astdef + if not s.ast.isNil: + x.call.add s.ast else: x.call.add c.graph.emptyNode of skType: @@ -615,7 +560,7 @@ proc tryDeref(n: PNode): PNode = result.add n proc semOverloadedCall(c: PContext, n, nOrig: PNode, - filter: TSymKinds, flags: TExprFlags): PNode = + filter: TSymKinds, flags: TExprFlags): PNode {.nosinks.} = var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) if r.state == csMatch: @@ -626,6 +571,27 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, "Non-matching candidates for " & renderTree(n) & "\n" & candidates) result = semResolvedCall(c, r, n, flags) + elif implicitDeref in c.features and canDeref(n): + # try to deref the first argument and then try overloading resolution again: + # + # XXX: why is this here? + # it could be added to the long list of alternatives tried + # inside `resolveOverloads` or it could be moved all the way + # into sigmatch with hidden conversion produced there + # + n[1] = n[1].tryDeref + var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) + if r.state == csMatch: result = semResolvedCall(c, r, n, flags) + else: + # get rid of the deref again for a better error message: + n[1] = n[1][0] + #notFoundError(c, n, errors) + if efExplain notin flags: + # repeat the overload resolution, + # this time enabling all the diagnostic output (this should fail again) + discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) + elif efNoUndeclared notin flags: + notFoundError(c, n, errors) else: if efExplain notin flags: # repeat the overload resolution, @@ -665,7 +631,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = assert n.kind == nkBracketExpr for i in 1..= low(c.intTypeCache) and value <= high(c.intTypeCache): - result = c.intTypeCache[value.int] - if result == nil: - let ti = getSysType(c.graph, literal.info, tyInt) - result = copyType(ti, nextTypeId(c.idgen), ti.owner) - result.n = literal - c.intTypeCache[value.int] = result - else: - let ti = getSysType(c.graph, literal.info, tyInt) - result = copyType(ti, nextTypeId(c.idgen), ti.owner) - result.n = literal - -proc setIntLitType*(c: PContext; result: PNode) = - let i = result.intVal - case c.config.target.intSize - of 8: result.typ = getIntLitType(c, result) - of 4: - if i >= low(int32) and i <= high(int32): - result.typ = getIntLitType(c, result) - else: - result.typ = getSysType(c.graph, result.info, tyInt64) - of 2: - if i >= low(int16) and i <= high(int16): - result.typ = getIntLitType(c, result) - elif i >= low(int32) and i <= high(int32): - result.typ = getSysType(c.graph, result.info, tyInt32) - else: - result.typ = getSysType(c.graph, result.info, tyInt64) - of 1: - # 8 bit CPUs are insane ... - if i >= low(int8) and i <= high(int8): - result.typ = getIntLitType(c, result) - elif i >= low(int16) and i <= high(int16): - result.typ = getSysType(c.graph, result.info, tyInt16) - elif i >= low(int32) and i <= high(int32): - result.typ = getSysType(c.graph, result.info, tyInt32) - else: - result.typ = getSysType(c.graph, result.info, tyInt64) - else: - internalError(c.config, result.info, "invalid int size") - proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = result.genericSym = s result.inst = inst @@ -267,9 +214,7 @@ proc getGenSym*(c: PContext; s: PSym): PSym = result = s proc considerGenSyms*(c: PContext; n: PNode) = - if n == nil: - discard "can happen for nkFormalParams/nkArgList" - elif n.kind == nkSym: + if n.kind == nkSym: let s = getGenSym(c, n.sym) if n.sym != s: n.sym = s @@ -305,6 +250,7 @@ proc popOptionEntry*(c: PContext) = proc newContext*(graph: ModuleGraph; module: PSym): PContext = new(result) + result.enforceVoidContext = PType(kind: tyTyped) result.optionStack = @[newOptionEntry(graph.config)] result.libs = @[] result.module = module @@ -319,67 +265,36 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext = result.cache = graph.cache result.graph = graph initStrTable(result.signatures) + result.typesWithOps = @[] result.features = graph.config.features if graph.config.symbolFiles != disabledSf: - let id = module.position - assert graph.packed[id].status in {undefined, outdated} - graph.packed[id].status = storing - graph.packed[id].module = module - initEncoder graph, module - -template packedRepr*(c): untyped = c.graph.packed[c.module.position].fromDisk -template encoder*(c): untyped = c.graph.encoders[c.module.position] + initEncoder result.encoder, module, graph.config proc addIncludeFileDep*(c: PContext; f: FileIndex) = if c.config.symbolFiles != disabledSf: - addIncludeFileDep(c.encoder, c.packedRepr, f) + addIncludeFileDep(c.encoder, f) proc addImportFileDep*(c: PContext; f: FileIndex) = if c.config.symbolFiles != disabledSf: - addImportFileDep(c.encoder, c.packedRepr, f) + addImportFileDep(c.encoder, f) -proc addPragmaComputation*(c: PContext; n: PNode) = - if c.config.symbolFiles != disabledSf: - addPragmaComputation(c.encoder, c.packedRepr, n) - -proc inclSym(sq: var seq[PSym], s: PSym): bool = +proc inclSym(sq: var seq[PSym], s: PSym) = for i in 0.. 0: - result = ambiguousSymChoice(c, n, result) - elif result.typ == nil or result.typ == c.enforceVoidContext: +proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = + result = semExprCheck(c, n, flags) + if result.typ == nil or result.typ == c.enforceVoidContext: localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) result.typ = errorType(c) - elif result.typ.kind == tyError: - # associates the type error to the current owner - result.typ = errorType(c) else: if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) @@ -137,7 +93,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.astdef) + result = copyTree(s.ast) if result.isNil: localError(c.config, n.info, "constant of type '" & typeToString(s.typ) & "' has no value") result = newSymNode(s) @@ -197,9 +153,6 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers) elif (targetBaseTyp.kind in IntegralTypes) and (srcBaseTyp.kind in IntegralTypes): - if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum: - message(c.config, src.info, warnSuspiciousEnumConv, "suspicious code: enum to enum conversion") - # `elif` would be incorrect here if targetTyp.kind == tyBool: discard "convOk" elif targetTyp.isOrdinalType: @@ -226,10 +179,10 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = else: discard -proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool = +proc isCastable(c: PContext; dst, src: PType): bool = ## Checks whether the source type can be cast to the destination type. ## Casting is very unrestrictive; casts are allowed as long as - ## dst.size >= src.size, and typeAllowed(dst, skParam) + ## castDest.size >= src.size, and typeAllowed(dst, skParam) #const # castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString, # tySequence, tyPointer, tyNil, tyOpenArray, @@ -247,8 +200,6 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool = let s = skipTypes(src, abstractInst) if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal: return false - elif d.kind in IntegralTypes and s.kind in {tyString, tySequence}: - return false var dstSize, srcSize: BiggestInt dstSize = computeSize(conf, dst) @@ -258,24 +209,19 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool = # Just assume the programmer knows what he is doing. return true if dstSize < 0: - return false + result = false elif srcSize < 0: - return false + result = false elif typeAllowed(dst, skParam, c) != nil: - return false + result = false elif dst.kind == tyProc and dst.callConv == ccClosure: - return src.kind == tyProc and src.callConv == ccClosure + result = 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: - return dst.size <= conf.target.ptrSize + result = dst.size <= conf.target.ptrSize proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices @@ -294,7 +240,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; expectedType: PType = nil): PNode = +proc semConv(c: PContext, n: PNode): PNode = if n.len != 2: localError(c.config, n.info, "a type conversion takes exactly one argument") return n @@ -310,7 +256,7 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = else: targetType = targetType.base of tyStatic: - var evaluated = semStaticExpr(c, n[1], expectedType) + var evaluated = semStaticExpr(c, n[1]) if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc: result = n result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil) @@ -319,8 +265,6 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = return evaluated else: targetType = targetType.base - of tyAnything, tyUntyped, tyTyped: - localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType)) else: discard maybeLiftType(targetType, c, n[0].info) @@ -394,14 +338,13 @@ proc semCast(c: PContext, n: PNode): PNode = checkSonsLen(n, 2, c.config) let targetType = semTypeNode(c, n[0], nil) let castedExpr = semExprWithType(c, n[1]) - if castedExpr.kind == nkClosedSymChoice: - errorUseQualifier(c, n[1].info, castedExpr) - if targetType == nil: - localError(c.config, n.info, "Invalid usage of cast, cast requires a type to convert to, e.g., cast[int](0d).") 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, n.info): - localError(c.config, n.info, "expression cannot be cast to '$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) result = newNodeI(nkCast, n.info) result.typ = targetType result.add copyTree(n[0]) @@ -416,12 +359,10 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = n[1] = semExprWithType(c, n[1], {efDetermineType}) var typ = skipTypes(n[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst}) case typ.kind - of tySequence, tyString, tyCstring, tyOpenArray, tyVarargs: + of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: n.typ = getSysType(c.graph, n.info, tyInt) of tyArray: n.typ = typ[0] # indextype - if n.typ.kind == tyRange and emptyRange(n.typ.n[0], n.typ.n[1]): #Invalid range - n.typ = getSysType(c.graph, n.info, tyInt) of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt..tyUInt64, tyFloat..tyFloat64: n.typ = n[1].typ.skipTypes({tyTypeDesc}) of tyGenericParam: @@ -560,7 +501,7 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode = result = newNodeI(nkCall, n.info) result.add newIdentNode(par, n.info) for i in 0.. 0: - discard "allow access within a cast(unsafeAssign) section" - elif strictDefs in c.features and aa == arAddressableConst and - sym != nil and sym.kind == skLet and isOutParam: - discard "allow let varaibles to be passed to out parameters" - else: - localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n)) + if isAssignable(c, n) notin {arLValue, arLocalLValue}: + localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n)) -proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode = +proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = result = n case n.kind of nkSym: @@ -770,7 +683,7 @@ proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode = if n.sym.typ != nil and skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: incl(n.sym.flags, sfAddrTaken) - result = newHiddenAddrTaken(c, n, isOutParam) + result = newHiddenAddrTaken(c, n) of nkDotExpr: checkSonsLen(n, 2, c.config) if n[1].kind != nkSym: @@ -778,14 +691,14 @@ proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode = return if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: incl(n[1].sym.flags, sfAddrTaken) - result = newHiddenAddrTaken(c, n, isOutParam) + result = newHiddenAddrTaken(c, n) of nkBracketExpr: checkMinSonsLen(n, 1, c.config) if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}: if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken) - result = newHiddenAddrTaken(c, n, isOutParam) + result = newHiddenAddrTaken(c, n) else: - result = newHiddenAddrTaken(c, n, isOutParam) + result = newHiddenAddrTaken(c, n) proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = checkMinSonsLen(n, 1, c.config) @@ -806,13 +719,9 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = if i < t.len and t[i] != nil and skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}: let it = n[i] - let aa = isAssignable(c, it) - if aa notin {arLValue, arLocalLValue}: + if isAssignable(c, it) notin {arLValue, arLocalLValue}: if it.kind != nkHiddenAddr: - if aa == arDiscriminant and c.inUncheckedAssignSection > 0: - discard "allow access within a cast(unsafeAssign) section" - else: - localError(c.config, it.info, errVarForOutParamNeededX % $it) + localError(c.config, it.info, errVarForOutParamNeededX % $it) # bug #5113: disallow newSeq(result) where result is a 'var T': if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}: var arg = n[1] #.skipAddr @@ -831,7 +740,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = if i < t.len and skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}: if n[i].kind != nkHiddenAddr: - n[i] = analyseIfAddressTaken(c, n[i], isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc}))) + n[i] = analyseIfAddressTaken(c, n[i]) include semmagic @@ -913,10 +822,10 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = #if result != n: # echo "SUCCESS evaluated at compile time: ", call.renderTree -proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = +proc semStaticExpr(c: PContext, n: PNode): PNode = inc c.inStaticContext openScope(c) - let a = semExprWithType(c, n, expectedType = expectedType) + let a = semExprWithType(c, n) closeScope(c) dec c.inStaticContext if a.findUnresolvedStatic != nil: return a @@ -929,11 +838,11 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = - if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: + if flags*{efInTypeof, efWantIterator} != {}: # consider: 'for x in pReturningArray()' --> we don't want the restriction # to 'skIterator' anymore; skIterator is preferred in sigmatch already # for typeof support. - # for ``typeof(countup(1,3))``, see ``tests/ttoseq``. + # for ``type(countup(1,3))``, see ``tests/ttoseq``. result = semOverloadedCall(c, n, nOrig, {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags) else: @@ -948,22 +857,24 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, case callee.kind of skMacro, skTemplate: discard else: - if callee.kind == skIterator and callee.id == c.p.owner.id and - not isClosureIterator(c.p.owner.typ): + if callee.kind == skIterator and callee.id == c.p.owner.id: localError(c.config, n.info, errRecursiveDependencyIteratorX % callee.name.s) # error correction, prevents endless for loop elimination in transf. # See bug #2051: result[0] = newSymNode(errorSym(c, n)) - elif callee.kind == skIterator: - if efWantIterable in flags: - let typ = newTypeS(tyIterable, c) - rawAddSon(typ, result.typ) - result.typ = typ + +proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode proc resolveIndirectCall(c: PContext; n, nOrig: PNode; t: PType): TCandidate = initCandidate(c, result, t) matches(c, n, nOrig, result) + if result.state != csMatch: + # try to deref the first argument: + if implicitDeref in c.features and canDeref(n): + n[1] = n[1].tryDeref + initCandidate(c, result, t) + matches(c, n, nOrig, result) proc bracketedMacro(n: PNode): PSym = if n.len >= 1 and n[0].kind == nkSym: @@ -975,30 +886,19 @@ proc setGenericParams(c: PContext, n: PNode) = for i in 1..\nbut expected one of:\n" & + msg.add(">\nbut expected one of: \n" & typeToString(typ)) # prefer notin preferToResolveSymbols # t.sym != nil @@ -1084,11 +977,14 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType result = m.call instGenericConvertersSons(c, result, m) + elif t != nil and t.kind == tyTypeDesc: + if n.len == 1: return semObjConstr(c, n, flags) + return semConv(c, n) else: - result = overloadedCallOpr(c, n) # this uses efNoUndeclared + result = overloadedCallOpr(c, n) # Now that nkSym does not imply an iteration over the proc/iterator space, # the old ``prc`` (which is likely an nkIdent) has to be restored: - if result == nil or result.kind == nkEmpty: + if result == nil: # XXX: hmm, what kind of symbols will end up here? # do we really need to try the overload resolution? n[0] = prc @@ -1102,29 +998,29 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType return result #result = afterCallActions(c, result, nOrig, flags) if result[0].kind == nkSym: - result = afterCallActions(c, result, nOrig, flags, expectedType) + result = afterCallActions(c, result, nOrig, flags) else: fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) -proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = +proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # this seems to be a hotspot in the compiler! let nOrig = n.copyTree #semLazyOpAux(c, n) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) - if result != nil: result = afterCallActions(c, result, nOrig, flags, expectedType) + if result != nil: result = afterCallActions(c, result, nOrig, flags) else: result = errorNode(c, n) proc buildEchoStmt(c: PContext, n: PNode): PNode = # we MUST not check 'n' for semantics again here! But for now we give up: result = newNodeI(nkCall, n.info) - let e = systemModuleSym(c.graph, getIdent(c.cache, "echo")) + var e = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "echo")) if e != nil: result.add(newSymNode(e)) else: - result.add localErrorNode(c, n, "system needs: echo") + localError(c.config, n.info, "system needs: echo") + result.add(errorNode(c, n)) result.add(n) - result.add(newStrNode(nkStrLit, ": " & n.typ.typeToString)) result = semExpr(c, result) proc semExprNoType(c: PContext, n: PNode): PNode = @@ -1176,7 +1072,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, s = newNodeIT(nkCurly, n.info, setType) for j in 0.. 0 or + (n.kind notin nkCallKinds and s.requiredParams > 0): + markUsed(c, n.info, s) + onUse(n.info, s) + result = symChoice(c, n, s, scClosed) + else: + result = semMacroExpr(c, n, n, s, flags) + of skTemplate: if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or (n.kind notin nkCallKinds and s.requiredParams > 0) or sfCustomPragma in sym.flags: @@ -1294,10 +1197,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = onUse(info, s) result = symChoice(c, n, s, scClosed) else: - case s.kind - of skMacro: result = semMacroExpr(c, n, n, s, flags) - of skTemplate: result = semTemplateExpr(c, n, s, flags) - else: discard # unreachable + result = semTemplateExpr(c, n, s, flags) of skParam: markUsed(c, n.info, s) onUse(n.info, s) @@ -1312,17 +1212,13 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if s.magic == mNimvm: localError(c.config, n.info, "illegal context for 'nimvm' magic") - if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess - markUsed(c, n.info, s) + markUsed(c, n.info, s) onUse(n.info, s) result = newSymNode(s, n.info) # We cannot check for access to outer vars for example because it's still # not sure the symbol really ends up being used: # var len = 0 # but won't be called # genericThatUsesLen(x) # marked as taking a closure? - if hasWarn(c.config, warnResultUsed): - message(c.config, n.info, warnResultUsed) - of skGenericParam: onUse(n.info, s) if s.typ.kind == tyStatic: @@ -1334,14 +1230,40 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = n.typ = s.typ return n of skType: - if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess - markUsed(c, n.info, s) + markUsed(c, n.info, s) onUse(n.info, s) if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil: return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) of skField: + var p = c.p + while p != nil and p.selfSym == nil: + p = p.next + if p != nil and p.selfSym != nil: + var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, + tyAlias, tySink, tyOwned}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) + var check: PNode = nil + if ty.kind == tyObject: + while true: + check = nil + let f = lookupInRecordAndBuildCheck(c, n, ty.n, s.name, check) + if f != nil and fieldVisible(c, f): + # is the access to a public field or in the same module or in a friend? + doAssert f == s + markUsed(c, n.info, f) + onUse(n.info, f) + result = newNodeIT(nkDotExpr, n.info, f.typ) + result.add makeDeref(newSymNode(p.selfSym)) + result.add newSymNode(f) # we now have the correct field + if check != nil: + check[0] = result + check.typ = result.typ + result = check + return result + if ty[0] == nil: break + ty = skipTypes(ty[0], skipPtrs) # old code, not sure if it's live code: markUsed(c, n.info, s) onUse(n.info, s) @@ -1401,7 +1323,7 @@ proc tryReadingTypeField(c: PContext, n: PNode, i: PIdent, ty: PType): PNode = else: result = tryReadingGenericParam(c, n, i, ty) -proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = +proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if it's not a built-in field access checkSonsLen(n, 2, c.config) # tests/bind/tbindoverload.nim wants an early exit here, but seems to @@ -1424,7 +1346,7 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = onUse(n[1].info, s) return - n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable}) + n[0] = semExprWithType(c, n[0], flags+{efDetermineType}) #restoreOldStyleType(n[0]) var i = considerQuotedIdent(c, n[1], n) var ty = n[0].typ @@ -1439,24 +1361,18 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = # field access and we leave the compiler to compile a normal call: if getCurrOwner(c).kind != skMacro: n.typ = makeTypeFromExpr(c, n.copyTree) - flags.incl efCannotBeDotCall return n else: return nil else: - flags.incl efCannotBeDotCall return tryReadingTypeField(c, n, i, ty.base) elif isTypeExpr(n.sons[0]): - flags.incl efCannotBeDotCall return tryReadingTypeField(c, n, i, ty) - elif ty.kind == tyError: - # a type error doesn't have any builtin fields - return nil if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: ty = ty.lastSon - ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink, tyStatic}) - while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst, tyAlias}) + ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) var check: PNode = nil if ty.kind == tyObject: while true: @@ -1474,10 +1390,8 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = # is the access to a public field or in the same module or in a friend? markUsed(c, n[1].info, f) onUse(n[1].info, f) - let info = n[1].info n[0] = makeDeref(n[0]) n[1] = newSymNode(f) # we now have the correct field - n[1].info = info # preserve the original info n.typ = f.typ if check == nil: result = n @@ -1499,7 +1413,6 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = if result == nil: let t = n[0].typ.skipTypes(tyDotOpTransparent) result = tryReadingGenericParam(c, n, i, t) - flags.incl efCannotBeDotCall proc dotTransformation(c: PContext, n: PNode): PNode = if isSymChoice(n[1]): @@ -1516,11 +1429,8 @@ proc dotTransformation(c: PContext, n: PNode): PNode = proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # this is difficult, because the '.' is used in many different contexts # in Nim. We first allow types in the semantic checking. - var f = flags - {efIsDotCall} - result = builtinFieldAccess(c, n, f) - if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and - efIsDotCall in flags and callOperator notin c.features and - efCannotBeDotCall notin f): + result = builtinFieldAccess(c, n, flags) + if result == nil: result = dotTransformation(c, n) proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = @@ -1543,28 +1453,6 @@ proc semDeref(c: PContext, n: PNode): PNode = else: result = nil #GlobalError(n[0].info, errCircumNeedsPointer) -proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode = - ## Instantiates generic if not lacking implicit generics, - ## otherwise returns n. - let - neededGenParams = s.ast[genericParamsPos].len - heldGenParams = n.len - 1 - var implicitParams = 0 - for x in s.ast[genericParamsPos]: - if tfImplicitTypeParam in x.typ.flags: - inc implicitParams - if heldGenParams != neededGenParams and implicitParams + heldGenParams == neededGenParams: - # This is an implicit + explicit generic procedure without all args passed, - # kicking back the sem'd symbol fixes #17212 - # Uncertain the hackiness of this solution. - result = n - else: - result = explicitGenericInstantiation(c, n, s) - if result == n: - n[0] = copyTree(result[0]) - else: - n[0] = result - proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if not a built-in subscript operator; also called for the ## checking of assignments @@ -1591,7 +1479,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = arr = arr.base case arr.kind - of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCstring, + of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCString, tyUncheckedArray: if n.len != 2: return nil n[0] = makeDeref(n[0]) @@ -1640,7 +1528,11 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = of skProc, skFunc, skMethod, skConverter, skIterator: # type parameters: partial generic specialization n[0] = semSymGenericInstantiation(c, n[0], s) - result = maybeInstantiateGeneric(c, n, s) + result = explicitGenericInstantiation(c, n, s) + if result == n: + n[0] = copyTree(result) + else: + n[0] = result of skMacro, skTemplate: if efInCall in flags: # We are processing macroOrTmpl[] in macroOrTmpl[](...) call. @@ -1660,11 +1552,11 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = else: discard -proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = +proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semSubscript(c, n, flags) if result == nil: # overloaded [] operator: - result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")), flags, expectedType) + result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]"))) proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = var id = considerQuotedIdent(c, a[1], a) @@ -1705,28 +1597,23 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = # `proc fun(a: var int): var int = a` discard else: discard - let valid = isAssignable(c, n) + let valid = isAssignable(c, n, isLent) if valid != arLValue: - if valid in {arAddressableConst, arLentValue} and isLent: - discard "ok" - elif valid == arLocalLValue: + if valid == arLocalLValue: localError(c.config, n.info, errXStackEscape % renderTree(n, {renderNoComments})) else: localError(c.config, n.info, errExprHasNoAddress) - result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ)) + result = newNodeIT(nkHiddenAddr, n.info, makePtrType(c, n.typ)) result.add(n) proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: var x = le[0] - if x.kind == nkSym: - if x.sym.kind == skResult and (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView): - n[0] = x # 'result[]' --> 'result' - n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) - x.typ.flags.incl tfVarIsPtr - #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info - elif sfGlobal in x.sym.flags: - x.typ.flags.incl tfVarIsPtr + if (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView) and x.kind == nkSym and x.sym.kind == skResult: + n[0] = x # 'result[]' --> 'result' + n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) + x.typ.flags.incl tfVarIsPtr + #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info proc borrowCheck(c: PContext, n, le, ri: PNode) = const @@ -1788,8 +1675,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # r.f = x # --> `f=` (r, x) let nOrig = n.copyTree - var flags = {efLValue} - a = builtinFieldAccess(c, a, flags) + a = builtinFieldAccess(c, a, {efLValue}) if a == nil: a = propertyWriteAccess(c, n, nOrig, n[0]) if a != nil: return a @@ -1831,30 +1717,27 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # a = b # both are vars, means: a[] = b[] # a = b # b no 'var T' means: a = addr(b) var le = a.typ - let assignable = isAssignable(c, a) - let root = getRoot(a) - let useStrictDefLet = root != nil and root.kind == skLet and - assignable == arAddressableConst and - strictDefs in c.features and isLocalSym(root) if le == nil: localError(c.config, a.info, "expression has no type") elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind notin {tyVar} and - assignable in {arNone, arLentValue, arAddressableConst} and not useStrictDefLet - ) or (skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs} and views notin c.features): + isAssignable(c, a) in {arNone, arLentValue}) or ( + skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs} and views notin c.features): # Direct assignment to a discriminant is allowed! localError(c.config, a.info, errXCannotBeAssignedTo % renderTree(a, {renderNoComments})) else: - let lhs = n[0] - let rhs = semExprWithType(c, n[1], {efTypeAllowed}, le) - if lhs.kind == nkSym and lhs.sym.kind == skResult: + let + lhs = n[0] + lhsIsResult = lhs.kind == nkSym and lhs.sym.kind == skResult + var + rhs = semExprWithType(c, n[1], + if lhsIsResult: {efAllowDestructor} else: {}) + if lhsIsResult: n.typ = c.enforceVoidContext if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ): var rhsTyp = rhs.typ if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass: rhsTyp = rhsTyp.lastSon - if lhs.sym.typ.kind == tyAnything: - rhsTyp = rhsTyp.skipIntLit(c.idgen) if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}: internalAssert c.config, c.p.resultSym != nil # Make sure the type is valid for the result variable @@ -1897,12 +1780,9 @@ proc semReturn(c: PContext, n: PNode): PNode = else: localError(c.config, n.info, "'return' not allowed here") -proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode = - when defined(nimsuggest): - if c.graph.config.expandDone(): - return n +proc semProcBody(c: PContext, n: PNode): PNode = openScope(c) - result = semExpr(c, n, expectedType = expectedType) + result = semExpr(c, n) if c.p.resultSym != nil and not isEmptyType(result.typ): if result.kind == nkNilLit: # or ImplicitlyDiscardable(result): @@ -1931,8 +1811,8 @@ proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode = else: localError(c.config, c.p.resultSym.info, errCannotInferReturnType % c.p.owner.name.s) - if isIterator(c.p.owner.typ) and c.p.owner.typ[0] != nil and - c.p.owner.typ[0].kind == tyAnything: + if isInlineIterator(c.p.owner.typ) and c.p.owner.typ[0] != nil and + c.p.owner.typ[0].kind == tyUntyped: localError(c.config, c.p.owner.info, errCannotInferReturnType % c.p.owner.name.s) closeScope(c) @@ -1958,8 +1838,6 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = tupleConstr[i] = takeImplicitAddr(c, tupleConstr[i], e.kind == tyLent) else: localError(c.config, n[0].info, errXExpected, "tuple constructor") - elif e.kind == tyEmpty: - localError(c.config, n[0].info, errTypeExpected) else: when false: # XXX investigate what we really need here. @@ -1992,23 +1870,11 @@ proc semYield(c: PContext, n: PNode): PNode = elif c.p.owner.typ[0] != nil: localError(c.config, n.info, errGenerated, "yield statement must yield a value") -proc considerQuotedIdentOrDot(c: PContext, n: PNode, origin: PNode = nil): PIdent = - if n.kind == nkDotExpr: - let a = considerQuotedIdentOrDot(c, n[0], origin).s - let b = considerQuotedIdentOrDot(c, n[1], origin).s - var s = newStringOfCap(a.len + b.len + 1) - s.add(a) - s.add('.') - s.add(b) - result = getIdent(c.cache, s) - else: - result = considerQuotedIdent(c, n, origin) - proc semDefined(c: PContext, n: PNode): PNode = checkSonsLen(n, 2, c.config) # we replace this node by a 'true' or 'false' node: result = newIntNode(nkIntLit, 0) - result.intVal = ord isDefined(c.config, considerQuotedIdentOrDot(c, n[1], n).s) + result.intVal = ord isDefined(c.config, considerQuotedIdent(c, n[1], n).s) result.info = n.info result.typ = getSysType(c.graph, n.info, tyBool) @@ -2031,7 +1897,7 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident) else: - result = someSym(c.graph, m, ident) + result = strTableGet(m.tab, ident) of nkSym: result = n.sym of nkOpenSymChoice, nkClosedSymChoice: @@ -2140,27 +2006,17 @@ proc processQuotations(c: PContext; n: var PNode, op: string, ids.add n return - template handlePrefixOp(prefixed) = - if prefixed[0].kind == nkIdent: - let examinedOp = prefixed[0].ident.s - if examinedOp == op: - returnQuote prefixed[1] - elif examinedOp.startsWith(op): - prefixed[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), prefixed.info) if n.kind == nkPrefix: checkSonsLen(n, 2, c.config) - handlePrefixOp(n) - elif n.kind == nkAccQuoted: - if op == "``": - returnQuote n[0] - else: # [bug #7589](https://github.com/nim-lang/Nim/issues/7589) - if n.len == 2 and n[0].ident.s == op: - var tempNode = nkPrefix.newTree() - tempNode.newSons(2) - tempNode[0] = n[0] - tempNode[1] = n[1] - handlePrefixOp(tempNode) + if n[0].kind == nkIdent: + var examinedOp = n[0].ident.s + if examinedOp == op: + returnQuote n[1] + elif examinedOp.startsWith(op): + n[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info) + elif n.kind == nkAccQuoted and op == "``": + returnQuote n[0] elif n.kind == nkIdent: if n.ident.s == "result": n = ids[0] @@ -2191,25 +2047,20 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = ids[0] = newAnonSym(c, skParam, n.info).newSymNode processQuotations(c, quotedBlock, op, quotes, ids) - let dummyTemplateSym = newAnonSym(c, skTemplate, n.info) - incl(dummyTemplateSym.flags, sfTemplateRedefinition) var dummyTemplate = newProcNode( nkTemplateDef, quotedBlock.info, body = quotedBlock, params = c.graph.emptyNode, - name = dummyTemplateSym.newSymNode, + name = newAnonSym(c, skTemplate, n.info).newSymNode, pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode, pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode) if ids.len > 0: dummyTemplate[paramsPos] = newNodeI(nkFormalParams, n.info) dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type - dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode) - for i in 1.. 1 and n[1].kind notin nkCallKinds: - return localErrorNode(c, n, n[1].info, "'spawn' takes a call expression; got: " & $n[1]) - let typ = result[^1].typ if not typ.isEmptyType: if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar: @@ -2454,21 +2302,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P of mSizeOf: markUsed(c, n.info, s) result = semSizeof(c, setMs(n, s)) - of mArrToSeq, mOpenArrayToSeq: - if expectedType != nil and ( - let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); - expected.kind in {tySequence, tyOpenArray}): - # seq type inference - var arrayType = newType(tyOpenArray, nextTypeId(c.idgen), expected.owner) - arrayType.rawAddSon(expected[0]) - if n[0].kind == nkSym and sfFromGeneric in n[0].sym.flags: - # may have been resolved to `@`[empty] at some point, - # reset to `@` to deal with this - n[0] = newSymNode(n[0].sym.owner, n[0].info) - n[1] = semExpr(c, n[1], flags, arrayType) - result = semDirectOp(c, n, flags, expectedType) else: - result = semDirectOp(c, n, flags, expectedType) + result = semDirectOp(c, n, flags) proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = # If semCheck is set to false, ``when`` will return the verbatim AST of @@ -2486,8 +2321,8 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = # ... var whenNimvm = false var typ = commonTypeBegin - if n.len in 1..2 and n[0].kind == nkElifBranch and ( - n.len == 1 or n[1].kind == nkElse): + if n.len == 2 and n[0].kind == nkElifBranch and + n[1].kind == nkElse: let exprNode = n[0][0] if exprNode.kind == nkIdent: whenNimvm = lookUp(c, exprNode).magic == mNimvm @@ -2513,7 +2348,6 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = discard elif e.intVal != 0 and result == nil: setResult(it[1]) - return # we're not in nimvm and we already have a result of nkElse, nkElseExpr: checkSonsLen(it, 1, c.config) if result == nil or whenNimvm: @@ -2525,56 +2359,36 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = else: illFormedAst(n, c.config) if result == nil: result = newNodeI(nkEmpty, n.info) - if whenNimvm: - result.typ = typ - if n.len == 1: - result.add(newTree(nkElse, newNode(nkStmtList))) + if whenNimvm: result.typ = typ -proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode = +proc semSetConstr(c: PContext, n: PNode): PNode = result = newNodeI(nkCurly, n.info) result.typ = newTypeS(tySet, c) - result.typ.flags.incl tfIsConstructor - var expectedElementType: PType = nil - if expectedType != nil and ( - let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); - expected.kind == tySet): - expectedElementType = expected[0] if n.len == 0: - rawAddSon(result.typ, - if expectedElementType != nil and - typeAllowed(expectedElementType, skLet, c) == nil: - expectedElementType - else: - newTypeS(tyEmpty, c)) + rawAddSon(result.typ, newTypeS(tyEmpty, c)) else: # only semantic checking for all elements, later type checking: var typ: PType = nil for i in 0.. MaxSetElements: typ = makeRangeType(c, 0, MaxSetElements-1, n.info) @@ -2591,7 +2405,7 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode = m = fitNode(c, typ, n[i], info) result.add m -proc semTableConstr(c: PContext, n: PNode; expectedType: PType = nil): PNode = +proc semTableConstr(c: PContext, n: PNode): PNode = # we simply transform ``{key: value, key2, key3: value}`` to # ``[(key, value), (key2, value2), (key3, value2)]`` result = newNodeI(nkBracket, n.info) @@ -2613,7 +2427,7 @@ proc semTableConstr(c: PContext, n: PNode; expectedType: PType = nil): PNode = lastKey = i+1 if lastKey != n.len: illFormedAst(n, c.config) - result = semExpr(c, result, expectedType = expectedType) + result = semExpr(c, result) type TParKind = enum @@ -2640,13 +2454,8 @@ proc checkPar(c: PContext; n: PNode): TParKind = localError(c.config, n[i].info, errNamedExprNotAllowed) return paNone -proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = +proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): 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() @@ -2656,9 +2465,8 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType let id = considerQuotedIdent(c, n[i][0]) if containsOrIncl(ids, id.id): localError(c.config, n[i].info, errFieldInitTwice % id.s) - # 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) + n[i][1] = semExprWithType(c, n[i][1], + flags*{efAllowDestructor}) if n[i][1].typ.kind == tyTypeDesc: localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.") @@ -2673,28 +2481,20 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType result.add n[i] result.typ = typ -proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = +proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): 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 # check if either everything or nothing is tyTypeDesc for i in 1.. 1 and isGeneric(c, n[1]): - let b = n[0] - if b.kind in nkSymChoices: - for i in 0.. 1: break - a = nextOverloadIter(o, c, n) - let info = getCallLineInfo(n) - if i <= 1: - if sfGenSym notin s.flags: - result = newSymNode(s, info) - markUsed(c, info, s) - onUse(info, s) - else: - result = n - else: - result = newNodeIT(nkClosedSymChoice, info, newTypeS(tyNone, c)) - a = initOverloadIter(o, c, n) - while a != nil: - if a.kind == skEnumField: - incl(a.flags, sfUsed) - markOwnerModuleAsUsed(c, a) - result.add newSymNode(a, info) - onUse(info, a) - a = nextOverloadIter(o, c, n) - -proc semPragmaStmt(c: PContext; n: PNode) = - if c.p.owner.kind == skModule: - pragma(c, c.p.owner, n, stmtPragmas+stmtPragmasTopLevel, true) - else: - pragma(c, c.p.owner, n, stmtPragmas, true) - -proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = - when defined(nimCompilerStacktraceHints): +proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = + when defined(nimCompilerStackraceHints): setFrameMsg c.config$n.info & " " & $n.kind - when false: # see `tdebugutils` - if isCompilerDebug(): - echo (">", c.config$n.info, n, flags, n.kind) - defer: - 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 - when defined(nimsuggest): - var expandStarted = false - if c.config.ideCmd == ideExpand and not c.config.expandProgress and - ((n.kind in {nkFuncDef, nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkConverterDef} and - n.info.exactEquals(c.config.expandPosition)) or - (n.kind in {nkCall, nkCommand} and - n[0].info.exactEquals(c.config.expandPosition))): - expandStarted = true - c.config.expandProgress = true - if c.config.expandLevels == 0: - c.config.expandNodeResult = $n - suggestQuit() - if c.config.cmd == cmdIdeTools: suggestExpr(c, n) if nfSem in n.flags: return case n.kind of nkIdent, nkAccQuoted: - 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) + 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) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) - case s.kind - of skProc, skFunc, skMethod, skConverter, skIterator: + if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) if result.kind == nkSym: @@ -2966,14 +2673,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType # "procs literals" are 'owned' if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) - of skEnumField: - 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! @@ -2981,56 +2682,39 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType of nkEmpty, nkNone, nkCommentStmt, nkType: discard of nkNilLit: - 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) + if result.typ == nil: result.typ = getSysType(c.graph, n.info, tyNil) of nkIntLit: - 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) + if result.typ == nil: setIntLitType(c.graph, 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) of nkStrLit..nkTripleStrLit: - 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) + 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) of nkDotExpr: result = semFieldAccess(c, n, flags) if result.kind == nkDotCall: @@ -3038,8 +2722,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType result = semExpr(c, result, flags) of nkBind: message(c.config, n.info, warnDeprecated, "bind is deprecated") - result = semExpr(c, n[0], flags, expectedType) - of nkTypeOfExpr..nkTupleClassTy, nkStaticTy, nkRefTy..nkEnumTy: + result = semExpr(c, n[0], flags) + of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy: if c.matchedConcept != nil and n.len == 1: let modifier = n.modifierTypeKindOfNode if modifier != tyNone: @@ -3064,40 +2748,34 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType # pretty.checkUse(n[0][1].info, s) case s.kind of skMacro, skTemplate: - result = semDirectOp(c, n, flags, expectedType) + result = semDirectOp(c, n, flags) 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, expectedType) + result = semConv(c, n) elif ambig and n.len == 1: errorUseQualifier(c, n.info, s) elif n.len == 1: - result = semObjConstr(c, n, flags, expectedType) - elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType) - else: result = semMagic(c, n, s, flags, expectedType) + result = semObjConstr(c, n, flags) + elif s.magic == mNone: result = semDirectOp(c, n, flags) + else: result = semMagic(c, n, s, flags) of skProc, skFunc, skMethod, skConverter, skIterator: if s.magic == mNone: result = semDirectOp(c, n, flags) - else: result = semMagic(c, n, s, flags, expectedType) + else: result = semMagic(c, n, s, flags) else: #liMessage(n.info, warnUser, renderTree(n)); - result = semIndirectOp(c, n, flags, expectedType) + result = semIndirectOp(c, n, flags) 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, expectedType) - elif nfDotField in 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, expectedType) - else: - result = semDirectOp(c, n, flags, expectedType) + result = semDirectOp(c, n, flags) + elif isSymChoice(n[0]) or nfDotField in n.flags: + result = semDirectOp(c, n, flags) else: - result = semIndirectOp(c, n, flags, expectedType) + result = semIndirectOp(c, n, flags) if nfDefaultRefsParam in result.flags: result = result.copyTree #XXX: Figure out what causes default param nodes to be shared.. (sigmatch bug?) @@ -3116,12 +2794,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType # This is a "when nimvm" stmt. result = semWhen(c, n, true) else: - result = semExpr(c, result, flags, expectedType) + result = semExpr(c, result, flags) of nkBracketExpr: checkMinSonsLen(n, 1, c.config) - result = semArrayAccess(c, n, flags, expectedType) + result = semArrayAccess(c, n, flags) of nkCurlyExpr: - result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags, expectedType) + result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags) of nkPragmaExpr: var pragma = n[1] @@ -3143,14 +2821,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType of nkPar, nkTupleConstr: case checkPar(c, n) of paNone: result = errorNode(c, n) - 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 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 nkLambdaKinds: result = semLambda(c, n, flags) of nkDerefExpr: result = semDeref(c, n) of nkAddr: result = n @@ -3159,9 +2836,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType result.typ = makePtrType(c, result[0].typ) of nkHiddenAddr, nkHiddenDeref: checkSonsLen(n, 1, c.config) - n[0] = semExpr(c, n[0], flags, expectedType) + n[0] = semExpr(c, n[0], flags) of nkCast: result = semCast(c, n) - of nkIfExpr, nkIfStmt: result = semIf(c, n, flags, expectedType) + of nkIfExpr, nkIfStmt: result = semIf(c, n, flags) of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: checkSonsLen(n, 2, c.config) considerGenSyms(c, n) @@ -3175,15 +2852,15 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType checkMinSonsLen(n, 2, c.config) considerGenSyms(c, n) of nkTableConstr: - result = semTableConstr(c, n, expectedType) + result = semTableConstr(c, n) 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], expectedType) - of nkAsgn, nkFastAsgn: result = semAsgn(c, n) - of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags, expectedType) - of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags, expectedType) + of nkStaticExpr: result = semStaticExpr(c, n[0]) + of nkAsgn: result = semAsgn(c, n) + of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags) + of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags) of nkRaiseStmt: result = semRaise(c, n) of nkVarSection: result = semVarOrLet(c, n, skVar) of nkLetSection: result = semVarOrLet(c, n, skLet) @@ -3191,15 +2868,15 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType 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, expectedType) + of nkTryStmt, nkHiddenTryStmt: result = semTry(c, n, flags) of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n) of nkForStmt, nkParForStmt: result = semFor(c, n, flags) - of nkCaseStmt: result = semCase(c, n, flags, expectedType) + of nkCaseStmt: result = semCase(c, n, flags) of nkReturnStmt: result = semReturn(c, n) of nkUsingStmt: result = semUsing(c, n) of nkAsmStmt: result = semAsm(c, n) of nkYieldStmt: result = semYield(c, n) - of nkPragma: semPragmaStmt(c, n) + of nkPragma: pragma(c, c.p.owner, n, stmtPragmas, true) of nkIteratorDef: result = semIterator(c, n) of nkProcDef: result = semProc(c, n) of nkFuncDef: result = semFunc(c, n) @@ -3233,7 +2910,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "export") result = semExportExcept(c, n) of nkPragmaBlock: - result = semPragmaBlock(c, n, expectedType) + result = semPragmaBlock(c, n) of nkStaticStmt: result = semStaticStmt(c, n) of nkDefer: @@ -3248,20 +2925,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType for i in 0.. 0 and n[0].kind == nkSym: - c.p.localBindStmts.add n - else: - localError(c.config, n.info, "invalid context for 'bind' statement: " & - renderTree(n, {renderNoComments})) else: localError(c.config, n.info, "invalid expression: " & renderTree(n, {renderNoComments})) if result != nil: incl(result.flags, nfSem) - - when defined(nimsuggest): - if expandStarted: - c.config.expandNodeResult = $result - suggestQuit() diff --git a/compiler/semfields.nim b/compiler/semfields.nim index 36e3b57e7..7e8fffc01 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -106,7 +106,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = # so that 'break' etc. work as expected, we produce # a 'while true: stmt; break' loop ... result = newNodeI(nkWhileStmt, n.info, 2) - var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true")) + var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true")) if trueSymbol == nil: localError(c.config, n.info, "system needs: 'true'") trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), nextSymId c.idgen, getCurrOwner(c), n.info) @@ -133,8 +133,6 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = typeMismatch(c.config, calli.info, tupleTypeA, tupleTypeB, calli) inc(c.p.nestedLoopCounter) - let oldBreakInLoop = c.p.breakInLoop - c.p.breakInLoop = true if tupleTypeA.kind == tyTuple: var loopBody = n[^1] for i in 0.. argB: argA else: argB, n, idgen, g) + result = newIntNodeT(if argA > argB: argA else: argB, n, g) of mShlI: case skipTypes(n.typ, abstractRange).kind - of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) - of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) - of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) - of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) + of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl toInt64(getInt(b))), n, g) + of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl toInt64(getInt(b))), n, g) + of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, g) + of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, g) of tyInt: if g.config.target.intSize == 4: - result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) + result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, g) else: - result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) - of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) - of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) - of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) - of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) + result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, g) + of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl toInt64(getInt(b))), n, g) + of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl toInt64(getInt(b))), n, g) + of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, g) + of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, g) of tyUInt: if g.config.target.intSize == 4: - result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) + result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, g) else: - result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g) + result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, g) else: internalError(g.config, n.info, "constant folding for shl") of mShrI: var a = cast[uint64](getInt(a)) @@ -206,75 +192,75 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P # unsigned and 64 bit integers don't need masking discard let c = cast[BiggestInt](a shr b) - result = newIntNodeT(toInt128(c), n, idgen, g) + result = newIntNodeT(toInt128(c), n, g) of mAshrI: case skipTypes(n.typ, abstractRange).kind - of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), toInt8(getInt(b)))), n, idgen, g) - of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), toInt16(getInt(b)))), n, idgen, g) - of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), toInt32(getInt(b)))), n, idgen, g) + of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), toInt8(getInt(b)))), n, g) + of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), toInt16(getInt(b)))), n, g) + of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), toInt32(getInt(b)))), n, g) of tyInt64, tyInt: - result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), toInt64(getInt(b)))), n, idgen, g) + result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), toInt64(getInt(b)))), n, g) else: internalError(g.config, n.info, "constant folding for ashr") of mDivI: let argA = getInt(a) let argB = getInt(b) if argB != Zero and (argA != firstOrd(g.config, n.typ) or argB != NegOne): - result = newIntNodeT(argA div argB, n, idgen, g) + result = newIntNodeT(argA div argB, n, g) of mModI: let argA = getInt(a) let argB = getInt(b) if argB != Zero and (argA != firstOrd(g.config, n.typ) or argB != NegOne): - result = newIntNodeT(argA mod argB, n, idgen, g) + result = newIntNodeT(argA mod argB, n, g) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g) of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n, g) of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g) of mDivF64: result = newFloatNodeT(getFloat(a) / getFloat(b), n, g) - of mIsNil: result = newIntNodeT(toInt128(ord(a.kind == nkNilLit)), n, idgen, g) + of mIsNil: result = newIntNodeT(toInt128(ord(a.kind == nkNilLit)), n, g) of mLtI, mLtB, mLtEnum, mLtCh: - result = newIntNodeT(toInt128(ord(getOrdValue(a) < getOrdValue(b))), n, idgen, g) + result = newIntNodeT(toInt128(ord(getOrdValue(a) < getOrdValue(b))), n, g) of mLeI, mLeB, mLeEnum, mLeCh: - result = newIntNodeT(toInt128(ord(getOrdValue(a) <= getOrdValue(b))), n, idgen, g) + result = newIntNodeT(toInt128(ord(getOrdValue(a) <= getOrdValue(b))), n, g) of mEqI, mEqB, mEqEnum, mEqCh: - result = newIntNodeT(toInt128(ord(getOrdValue(a) == getOrdValue(b))), n, idgen, g) - of mLtF64: result = newIntNodeT(toInt128(ord(getFloat(a) < getFloat(b))), n, idgen, g) - of mLeF64: result = newIntNodeT(toInt128(ord(getFloat(a) <= getFloat(b))), n, idgen, g) - of mEqF64: result = newIntNodeT(toInt128(ord(getFloat(a) == getFloat(b))), n, idgen, g) - of mLtStr: result = newIntNodeT(toInt128(ord(getStr(a) < getStr(b))), n, idgen, g) - of mLeStr: result = newIntNodeT(toInt128(ord(getStr(a) <= getStr(b))), n, idgen, g) - of mEqStr: result = newIntNodeT(toInt128(ord(getStr(a) == getStr(b))), n, idgen, g) + result = newIntNodeT(toInt128(ord(getOrdValue(a) == getOrdValue(b))), n, g) + of mLtF64: result = newIntNodeT(toInt128(ord(getFloat(a) < getFloat(b))), n, g) + of mLeF64: result = newIntNodeT(toInt128(ord(getFloat(a) <= getFloat(b))), n, g) + of mEqF64: result = newIntNodeT(toInt128(ord(getFloat(a) == getFloat(b))), n, g) + of mLtStr: result = newIntNodeT(toInt128(ord(getStr(a) < getStr(b))), n, g) + of mLeStr: result = newIntNodeT(toInt128(ord(getStr(a) <= getStr(b))), n, g) + of mEqStr: result = newIntNodeT(toInt128(ord(getStr(a) == getStr(b))), n, g) of mLtU: - result = newIntNodeT(toInt128(ord(`<%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, idgen, g) + result = newIntNodeT(toInt128(ord(`<%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, g) of mLeU: - result = newIntNodeT(toInt128(ord(`<=%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, idgen, g) - of mBitandI, mAnd: result = newIntNodeT(bitand(a.getInt, b.getInt), n, idgen, g) - of mBitorI, mOr: result = newIntNodeT(bitor(getInt(a), getInt(b)), n, idgen, g) - of mBitxorI, mXor: result = newIntNodeT(bitxor(getInt(a), getInt(b)), n, idgen, g) + result = newIntNodeT(toInt128(ord(`<=%`(toInt64(getOrdValue(a)), toInt64(getOrdValue(b))))), n, g) + of mBitandI, mAnd: result = newIntNodeT(bitand(a.getInt, b.getInt), n, g) + of mBitorI, mOr: result = newIntNodeT(bitor(getInt(a), getInt(b)), n, g) + of mBitxorI, mXor: result = newIntNodeT(bitxor(getInt(a), getInt(b)), n, g) of mAddU: - let val = maskBytes(getInt(a) + getInt(b), int(getSize(g.config, n.typ))) - result = newIntNodeT(val, n, idgen, g) + let val = maskBytes(getInt(a) + getInt(b), int(n.typ.size)) + result = newIntNodeT(val, n, g) of mSubU: - let val = maskBytes(getInt(a) - getInt(b), int(getSize(g.config, n.typ))) - result = newIntNodeT(val, n, idgen, g) + let val = maskBytes(getInt(a) - getInt(b), int(n.typ.size)) + result = newIntNodeT(val, n, g) # echo "subU: ", val, " n: ", n, " result: ", val of mMulU: - let val = maskBytes(getInt(a) * getInt(b), int(getSize(g.config, n.typ))) - result = newIntNodeT(val, n, idgen, g) + let val = maskBytes(getInt(a) * getInt(b), int(n.typ.size)) + result = newIntNodeT(val, n, g) of mModU: - let argA = maskBytes(getInt(a), int(getSize(g.config, a.typ))) - let argB = maskBytes(getInt(b), int(getSize(g.config, a.typ))) + let argA = maskBytes(getInt(a), int(a.typ.size)) + let argB = maskBytes(getInt(b), int(a.typ.size)) if argB != Zero: - result = newIntNodeT(argA mod argB, n, idgen, g) + result = newIntNodeT(argA mod argB, n, g) of mDivU: - let argA = maskBytes(getInt(a), int(getSize(g.config, a.typ))) - let argB = maskBytes(getInt(b), int(getSize(g.config, a.typ))) + let argA = maskBytes(getInt(a), int(a.typ.size)) + let argB = maskBytes(getInt(b), int(a.typ.size)) if argB != Zero: - result = newIntNodeT(argA div argB, n, idgen, g) - of mLeSet: result = newIntNodeT(toInt128(ord(containsSets(g.config, a, b))), n, idgen, g) - of mEqSet: result = newIntNodeT(toInt128(ord(equalSets(g.config, a, b))), n, idgen, g) + result = newIntNodeT(argA div argB, n, g) + of mLeSet: result = newIntNodeT(toInt128(ord(containsSets(g.config, a, b))), n, g) + of mEqSet: result = newIntNodeT(toInt128(ord(equalSets(g.config, a, b))), n, g) of mLtSet: result = newIntNodeT(toInt128(ord( - containsSets(g.config, a, b) and not equalSets(g.config, a, b))), n, idgen, g) + containsSets(g.config, a, b) and not equalSets(g.config, a, b))), n, g) of mMulSet: result = nimsets.intersectSets(g.config, a, b) result.info = n.info @@ -285,7 +271,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P result = nimsets.diffSets(g.config, a, b) result.info = n.info of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g) - of mInSet: result = newIntNodeT(toInt128(ord(inSet(a, b))), n, idgen, g) + of mInSet: result = newIntNodeT(toInt128(ord(inSet(a, b))), n, g) of mRepr: # BUGFIX: we cannot eval mRepr here for reasons that I forgot. discard @@ -295,20 +281,26 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P else: result = newStrNodeT("true", n, g) of mFloatToStr: result = newStrNodeT($getFloat(a), n, g) of mCStrToStr, mCharToStr: - result = newStrNodeT(getStrOrChar(a), n, g) + if a.kind == nkBracket: + var s = "" + for b in a.sons: + s.add b.getStrOrChar + result = newStrNodeT(s, n, g) + else: + result = newStrNodeT(getStrOrChar(a), n, g) of mStrToStr: result = newStrNodeT(getStrOrChar(a), n, g) of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g) of mArrToSeq: result = copyTree(a) result.typ = n.typ of mCompileOption: - result = newIntNodeT(toInt128(ord(commands.testCompileOption(g.config, a.getStr, n.info))), n, idgen, g) + result = newIntNodeT(toInt128(ord(commands.testCompileOption(g.config, a.getStr, n.info))), n, g) of mCompileOptionArg: result = newIntNodeT(toInt128(ord( - testCompileOptionArg(g.config, getStr(a), getStr(b), n.info))), n, idgen, g) + testCompileOptionArg(g.config, getStr(a), getStr(b), n.info))), n, g) of mEqProc: result = newIntNodeT(toInt128(ord( - exprStructuralEquivalent(a, b, strictSymEquality=true))), n, idgen, g) + exprStructuralEquivalent(a, b, strictSymEquality=true))), n, g) else: discard proc getConstIfExpr(c: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = @@ -354,7 +346,7 @@ proc magicCall(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = if n.len > 3: c = getConstExpr(m, n[3], idgen, g) if c == nil: return - result = evalOp(s.magic, n, a, b, c, idgen, g) + result = evalOp(s.magic, n, a, b, c, g) proc getAppType(n: PNode; g: ModuleGraph): PNode = if g.config.globalOptions.contains(optGenDynLib): @@ -371,12 +363,7 @@ proc rangeCheck(n: PNode, value: Int128; g: ModuleGraph) = localError(g.config, n.info, "cannot convert " & $value & " to " & typeToString(n.typ)) -proc floatRangeCheck(n: PNode, value: BiggestFloat; g: ModuleGraph) = - if value < firstFloat(n.typ) or value > lastFloat(n.typ): - localError(g.config, n.info, "cannot convert " & $value & - " to " & typeToString(n.typ)) - -proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): PNode = +proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode = let dstTyp = skipTypes(n.typ, abstractRange - {tyTypeDesc}) let srcTyp = skipTypes(a.typ, abstractRange - {tyTypeDesc}) @@ -390,9 +377,9 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P of tyBool: case srcTyp.kind of tyFloat..tyFloat64: - result = newIntNodeT(toInt128(getFloat(a) != 0.0), n, idgen, g) + result = newIntNodeT(toInt128(getFloat(a) != 0.0), n, g) of tyChar, tyUInt..tyUInt64, tyInt..tyInt64: - result = newIntNodeT(toInt128(a.getOrdValue != 0), n, idgen, g) + result = newIntNodeT(toInt128(a.getOrdValue != 0), n, g) of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`? result = a result.typ = n.typ @@ -400,11 +387,11 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P of tyInt..tyInt64, tyUInt..tyUInt64: case srcTyp.kind of tyFloat..tyFloat64: - result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g) + result = newIntNodeT(toInt128(getFloat(a)), n, g) of tyChar, tyUInt..tyUInt64, tyInt..tyInt64: var val = a.getOrdValue if check: rangeCheck(n, val, g) - result = newIntNodeT(val, n, idgen, g) + result = newIntNodeT(val, n, g) if dstTyp.kind in {tyUInt..tyUInt64}: result.transitionIntKind(nkUIntLit) else: @@ -414,7 +401,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, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: + of tyInt..tyInt64, tyEnum, tyBool, tyChar: result = newFloatNodeT(toFloat64(getOrdValue(a)), n, g) else: result = a @@ -462,7 +449,6 @@ proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNo proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = # a real field access; proc calls have already been transformed - if n[1].kind != nkSym: return nil var x = getConstExpr(m, n[0], idgen, g) if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return @@ -495,83 +481,6 @@ proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode = else: result.typ = s.typ -proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = - var name = s.name.s - let prag = extractPragma(s) - if prag != nil: - for it in prag: - if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent: - let word = whichKeyword(it[0].ident) - if word in {wStrDefine, wIntDefine, wBoolDefine, wDefine}: - # should be processed in pragmas.nim already - if it[1].kind in {nkStrLit, nkRStrLit, nkTripleStrLit}: - name = it[1].strVal - if isDefined(g.config, name): - let str = g.config.symbols[name] - case s.magic - of mIntDefine: - try: - result = newIntNodeT(toInt128(str.parseInt), n, idgen, g) - except ValueError: - localError(g.config, s.info, - "{.intdefine.} const was set to an invalid integer: '" & - str & "'") - of mStrDefine: - result = newStrNodeT(str, n, g) - of mBoolDefine: - try: - result = newIntNodeT(toInt128(str.parseBool.int), n, idgen, g) - except ValueError: - localError(g.config, s.info, - "{.booldefine.} const was set to an invalid bool: '" & - str & "'") - of mGenericDefine: - let rawTyp = s.typ - # pretend we don't support distinct types - let typ = rawTyp.skipTypes(abstractVarRange-{tyDistinct}) - try: - template intNode(value): PNode = - let val = toInt128(value) - rangeCheck(n, val, g) - newIntNodeT(val, n, idgen, g) - case typ.kind - of tyString, tyCstring: - result = newStrNodeT(str, n, g) - of tyInt..tyInt64: - result = intNode(str.parseBiggestInt) - of tyUInt..tyUInt64: - result = intNode(str.parseBiggestUInt) - of tyBool: - result = intNode(str.parseBool.int) - of tyEnum: - # compile time parseEnum - let ident = getIdent(g.cache, str) - for e in typ.n: - if e.kind != nkSym: internalError(g.config, "foldDefine for enum") - let es = e.sym - let match = - if es.ast.isNil: - es.name.id == ident.id - else: - es.ast.strVal == str - if match: - result = intNode(es.position) - break - if result.isNil: - raise newException(ValueError, "invalid enum value: " & str) - else: - localError(g.config, s.info, "unsupported type $1 for define '$2'" % - [name, typeToString(rawTyp)]) - except ValueError as e: - localError(g.config, s.info, - "could not process define '$1' of type $2; $3" % - [name, typeToString(rawTyp), e.msg]) - else: result = copyTree(s.astdef) # unreachable - else: - result = copyTree(s.astdef) - if result != nil: - result.info = n.info - proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = result = nil case n.kind @@ -579,24 +488,45 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode var s = n.sym case s.kind of skEnumField: - result = newIntNodeT(toInt128(s.position), n, idgen, g) + result = newIntNodeT(toInt128(s.position), n, g) of skConst: case s.magic - of mIsMainModule: result = newIntNodeT(toInt128(ord(sfMainModule in m.flags)), n, idgen, g) + of mIsMainModule: result = newIntNodeT(toInt128(ord(sfMainModule in m.flags)), n, g) of mCompileDate: result = newStrNodeT(getDateStr(), n, g) of mCompileTime: result = newStrNodeT(getClockStr(), n, g) - of mCpuEndian: result = newIntNodeT(toInt128(ord(CPU[g.config.target.targetCPU].endian)), n, idgen, g) + of mCpuEndian: result = newIntNodeT(toInt128(ord(CPU[g.config.target.targetCPU].endian)), n, g) of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.targetOS].name), n, g) of mHostCPU: result = newStrNodeT(platform.CPU[g.config.target.targetCPU].name.toLowerAscii, n, g) of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.hostOS].name), n, g) of mBuildCPU: result = newStrNodeT(platform.CPU[g.config.target.hostCPU].name.toLowerAscii, n, g) of mAppType: result = getAppType(n, g) - of mIntDefine, mStrDefine, mBoolDefine, mGenericDefine: - result = foldDefine(m, s, n, idgen, g) + of mIntDefine: + if isDefined(g.config, s.name.s): + try: + result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseInt), n, g) + except ValueError: + localError(g.config, s.info, + "{.intdefine.} const was set to an invalid integer: '" & + g.config.symbols[s.name.s] & "'") + else: + result = copyTree(s.ast) + of mStrDefine: + if isDefined(g.config, s.name.s): + result = newStrNodeT(g.config.symbols[s.name.s], n, g) + else: + result = copyTree(s.ast) + of mBoolDefine: + if isDefined(g.config, s.name.s): + try: + result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseBool.int), n, g) + except ValueError: + localError(g.config, s.info, + "{.booldefine.} const was set to an invalid bool: '" & + g.config.symbols[s.name.s] & "'") + else: + result = copyTree(s.ast) else: - result = copyTree(s.astdef) - if result != nil: - result.info = n.info + result = copyTree(s.ast) of skProc, skFunc, skMethod: result = n of skParam: @@ -634,30 +564,30 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode if skipTypes(n[1].typ, abstractVarRange).kind in tyFloat..tyFloat64: result = newFloatNodeT(firstFloat(n[1].typ), n, g) else: - result = newIntNodeT(firstOrd(g.config, n[1].typ), n, idgen, g) + result = newIntNodeT(firstOrd(g.config, n[1].typ), n, g) of mHigh: if skipTypes(n[1].typ, abstractVar+{tyUserTypeClassInst}).kind notin - {tySequence, tyString, tyCstring, tyOpenArray, tyVarargs}: + {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}: if skipTypes(n[1].typ, abstractVarRange).kind in tyFloat..tyFloat64: result = newFloatNodeT(lastFloat(n[1].typ), n, g) else: - result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, idgen, g) + result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g) else: var a = getArrayConstr(m, n[1], idgen, g) if a.kind == nkBracket: # we can optimize it away: - result = newIntNodeT(toInt128(a.len-1), n, idgen, g) + result = newIntNodeT(toInt128(a.len-1), n, g) of mLengthOpenArray: var a = getArrayConstr(m, n[1], idgen, g) if a.kind == nkBracket: # we can optimize it away! This fixes the bug ``len(134)``. - result = newIntNodeT(toInt128(a.len), n, idgen, g) + result = newIntNodeT(toInt128(a.len), n, g) else: result = magicCall(m, n, idgen, g) of mLengthArray: # It doesn't matter if the argument is const or not for mLengthArray. # This fixes bug #544. - result = newIntNodeT(lengthOrd(g.config, n[1].typ), n, idgen, g) + result = newIntNodeT(lengthOrd(g.config, n[1].typ), n, g) of mSizeOf: result = foldSizeOf(g.config, n, nil) of mAlignOf: @@ -685,7 +615,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode n[0] = a of nkBracket, nkCurly: result = copyNode(n) - for son in n.items: + for i, son in n.pairs: var a = getConstExpr(m, son, idgen, g) if a == nil: return nil result.add a @@ -709,7 +639,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode # tuple constructor result = copyNode(n) if (n.len > 0) and (n[0].kind == nkExprColonExpr): - for expr in n.items: + for i, expr in n.pairs: let exprNew = copyNode(expr) # nkExprColonExpr exprNew.add expr[0] let a = getConstExpr(m, expr[1], idgen, g) @@ -717,7 +647,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode exprNew.add a result.add exprNew else: - for expr in n.items: + for i, expr in n.pairs: let a = getConstExpr(m, expr, idgen, g) if a == nil: return nil result.add a @@ -740,12 +670,11 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of nkHiddenStdConv, nkHiddenSubConv, nkConv: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return - result = foldConv(n, a, idgen, g, check=true) + result = foldConv(n, a, g, check=true) of nkDerefExpr, nkHiddenDeref: let a = getConstExpr(m, n[0], idgen, g) if a != nil and a.kind == nkNilLit: - result = nil - #localError(g.config, n.info, "nil dereference is not allowed") + localError(g.config, n.info, "nil dereference is not allowed") of nkCast: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 0827e6845..1f633549a 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -107,8 +107,6 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, else: result = n onUse(n.info, s) - of skEnumField: - result = symChoice(c, n, s, scOpen) else: result = newSymNode(s, n.info) onUse(n.info, s) @@ -175,7 +173,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, n.info, s, kind) + styleCheckDef(c.config, n.info, s, kind) onDef(n.info, s) proc semGenericStmt(c: PContext, n: PNode, @@ -191,9 +189,6 @@ proc semGenericStmt(c: PContext, n: PNode, case n.kind of nkIdent, nkAccQuoted: result = lookup(c, n, flags, ctx) - if result != nil and result.kind == nkSym: - assert result.sym != nil - markUsed(c, n.info, result.sym) of nkDotExpr: #let luf = if withinMixin notin flags: {checkUndeclared} else: {} #var s = qualifiedLookUp(c, n, luf) @@ -305,8 +300,9 @@ proc semGenericStmt(c: PContext, n: PNode, result = newNodeI(nkCall, n.info) result.add newIdentNode(getIdent(c.cache, "[]"), n.info) for i in 0.. 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'" iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = internalAssert c.config, n.kind == nkGenericParams - for a in n.items: + for i, a in n.pairs: internalAssert c.config, a.kind == nkSym var q = a.sym - if q.typ.kind in {tyTypeDesc, tyGenericParam, tyStatic, tyConcept}+tyTypeClasses: - let symKind = if q.typ.kind == tyStatic: skConst else: skType - var s = newSym(symKind, q.name, nextSymId(c.idgen), getCurrOwner(c), q.info) - s.flags.incl {sfUsed, sfFromGeneric} - var t = PType(idTableGet(pt, q.typ)) - if t == nil: - if tfRetType in q.typ.flags: - # keep the generic type and allow the return type to be bound - # later by semAsgn in return type inference scenario - t = q.typ - else: - 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) + if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses: + continue + let symKind = if q.typ.kind == tyStatic: skConst else: skType + var s = newSym(symKind, q.name, nextSymId c.idgen, getCurrOwner(c), q.info) + s.flags.incl {sfUsed, sfFromGeneric} + var t = PType(idTableGet(pt, q.typ)) + if t == nil: + if tfRetType in q.typ.flags: + # keep the generic type and allow the return type to be bound + # later by semAsgn in return type inference scenario + t = q.typ + else: + localError(c.config, a.info, errCannotInstantiateX % s.name.s) t = errorType(c) - elif t.kind == tyGenericInvocation: - #t = instGenericContainer(c, a, t) - t = generateTypeInstance(c, pt, a, t) - #t = ReplaceTypeVarsT(cl, t) - s.typ = t - if t.kind == tyStatic: s.ast = t.n - yield s + elif t.kind == tyGenericParam: + localError(c.config, a.info, errCannotInstantiateX % q.name.s) + t = errorType(c) + elif t.kind == tyGenericInvocation: + #t = instGenericContainer(c, a, t) + t = generateTypeInstance(c, pt, a, t) + #t = ReplaceTypeVarsT(cl, t) + s.typ = t + if t.kind == tyStatic: s.ast = t.n + yield s proc sameInstantiation(a, b: TInstantiation): bool = if a.concreteTypes.len == b.concreteTypes.len: for i in 0..a.concreteTypes.high: if not compareTypes(a.concreteTypes[i], b.concreteTypes[i], flags = {ExactTypeDescValues, - ExactGcSafety, - PickyCAliases}): return + ExactGcSafety}): return result = true -proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation; +proc genericCacheGet(genericSym: PSym, entry: TInstantiation; id: CompilesId): PSym = - for inst in procInstCacheItems(g, genericSym): + for inst in genericSym.procInstCache: if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]): return inst.sym @@ -127,21 +145,10 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = if sfGenSym in param.flags: idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym) freshGenSyms(c, b, result, orig, symMap) - - if sfBorrow notin orig.flags: - # We do not want to generate a body for generic borrowed procs. - # As body is a sym to the borrowed proc. - let resultType = # todo probably refactor it into a function - if result.kind == skMacro: - sysTypeFromName(c.graph, n.info, "NimNode") - elif not isInlineIterator(result.typ): - result.typ[0] - else: - nil - b = semProcBody(c, b, resultType) + b = semProcBody(c, b) result.ast[bodyPos] = hloBody(c, b) - excl(result.flags, sfForward) trackProc(c, result, result.ast[bodyPos]) + excl(result.flags, sfForward) dec c.inGenericInst proc fixupInstantiatedSymbols(c: PContext, s: PSym) = @@ -153,7 +160,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) = pushInfoContext(c.config, oldPrc.info) openScope(c) var n = oldPrc.ast - n[bodyPos] = copyTree(getBody(c.graph, s)) + n[bodyPos] = copyTree(s.getBody) instantiateBody(c, n, oldPrc.typ.n, oldPrc, s) closeScope(c) popInfoContext(c.config) @@ -225,11 +232,11 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # time adding the instantiated proc params into the current scope. # This is necessary, because the instantiation process may refer to # these params in situations like this: - # proc foo[Container](a: Container, b: a.type.Item): typeof(b.x) + # proc foo[Container](a: Container, b: a.type.Item): type(b.x) # # Alas, doing this here is probably not enough, because another # proc signature could appear in the params: - # proc foo[T](a: proc (x: T, b: typeof(x.y)) + # proc foo[T](a: proc (x: T, b: type(x.y)) # # The solution would be to move this logic into semtypinst, but # at this point semtypinst have to become part of sem, because it @@ -312,16 +319,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable, prc.typ = result popInfoContext(c.config) -proc fillMixinScope(c: PContext) = - var p = c.p - while p != nil: - for bnd in p.localBindStmts: - for n in bnd: - addSym(c.currentScope, n.sym) - p = p.next - proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, - info: TLineInfo): PSym = + info: TLineInfo): PSym {.nosinks.} = ## Generates a new instance of a generic procedure. ## The `pt` parameter is a type-unsafe mapping table used to link generic ## parameters to their concrete types within the generic instance. @@ -346,13 +345,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, result.ast = n pushOwner(c, result) - # mixin scope: - openScope(c) - fillMixinScope(c) - openScope(c) let gp = n[genericParamsPos] - internalAssert c.config, gp.kind == nkGenericParams + internalAssert c.config, gp.kind != nkEmpty n[namePos] = newSymNode(result) pushInfoContext(c.config, info, fn.detailedInfo) var entry = TInstantiation.new @@ -366,7 +361,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, addDecl(c, s) entry.concreteTypes[i] = s.typ inc i - pushProcCon(c, result) + rawPushProcCon(c, result) instantiateProcType(c, pt, result, info) for j in 1..= x.len: globalError conf, n.info, "invalid field at index " & $i else: annotateType(n[i], x[i], conf) elif x.kind == tyProc and x.callConv == ccClosure: - n.typ = t - elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples - if n.kind == nkTupleConstr: - let - bracketExpr = newNodeI(nkBracket, n.info) - left = int n[1].intVal - right = int n[2].intVal - bracketExpr.flags = n.flags - case n[0].kind # is this a string slice or a array slice - of nkStrKinds: - for i in left..right: - bracketExpr.add newIntNode(nkCharLit, BiggestInt n[0].strVal[i]) - annotateType(bracketExpr[^1], t[0], conf) - of nkBracket: - for i in left..right: - bracketExpr.add n[0][i] - annotateType(bracketExpr[^1], t[0], conf) - else: - globalError(conf, n.info, "Incorrectly generated tuple constr") - n[] = bracketExpr[] - n.typ = t else: globalError(conf, n.info, "() must have a tuple type") @@ -113,7 +92,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = else: globalError(conf, n.info, "integer literal must have some int type") of nkStrLit..nkTripleStrLit: - if x.kind in {tyString, tyCstring}: + if x.kind in {tyString, tyCString}: n.typ = t else: globalError(conf, n.info, "string literal must be of some string type") diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index b4c6cd275..69e414fa8 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -10,32 +10,17 @@ # This include file implements the semantic checking for magics. # included from sem.nim -proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode - - -proc addDefaultFieldForNew(c: PContext, n: PNode): PNode = - result = n - let typ = result[1].typ # new(x) - if typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyRef and typ.skipTypes({tyGenericInst, tyAlias, tySink})[0].kind == tyObject: - var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[1].info, typ)) - asgnExpr.typ = typ - var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0] - while true: - asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n) - let base = t[0] - if base == nil: - break - t = skipTypes(base, skipPtrs) - - if asgnExpr.sons.len > 1: - result = newTree(nkAsgn, result[1], asgnExpr) - -proc semAddrArg(c: PContext; n: PNode): PNode = +proc semAddrArg(c: PContext; n: PNode; isUnsafeAddr = false): PNode = let x = semExprWithType(c, n) if x.kind == nkSym: x.sym.flags.incl(sfAddrTaken) - if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}: - localError(c.config, n.info, errExprHasNoAddress) + if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}: + # Do not suggest the use of unsafeAddr if this expression already is a + # unsafeAddr + if isUnsafeAddr: + localError(c.config, n.info, errExprHasNoAddress) + else: + localError(c.config, n.info, errExprHasNoAddress & "; maybe use 'unsafeAddr'") result = x proc semTypeOf(c: PContext; n: PNode): PNode = @@ -78,15 +63,15 @@ proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode = result[1] = n.lastSon result = semAsgn(c, result, noOverloadedSubscript) -proc semAsgnOpr(c: PContext; n: PNode; k: TNodeKind): PNode = - result = newNodeI(k, n.info, 2) +proc semAsgnOpr(c: PContext; n: PNode): PNode = + result = newNodeI(nkAsgn, n.info, 2) result[0] = n[1] result[1] = n[2] result = semAsgn(c, result, noOverloadedAsgn) proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode = var r = isPartOf(n[1], n[2]) - result = newIntNodeT(toInt128(ord(r)), n, c.idgen, c.graph) + result = newIntNodeT(toInt128(ord(r)), n, c.graph) proc expectIntLit(c: PContext, n: PNode): int = let x = c.semConstExpr(c, n) @@ -194,22 +179,26 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred}) let complexObj = containsGarbageCollectedRef(t) or hasDestructor(t) - result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.idgen, c.graph) + result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.graph) of "isNamedTuple": var operand = operand.skipTypes({tyGenericInst}) let cond = operand.kind == tyTuple and operand.n != nil - result = newIntNodeT(toInt128(ord(cond)), traitCall, c.idgen, c.graph) + result = newIntNodeT(toInt128(ord(cond)), traitCall, c.graph) of "tupleLen": var operand = operand.skipTypes({tyGenericInst}) assert operand.kind == tyTuple, $operand.kind - result = newIntNodeT(toInt128(operand.len), traitCall, c.idgen, c.graph) + result = newIntNodeT(toInt128(operand.len), traitCall, c.graph) of "distinctBase": var arg = operand.skipTypes({tyGenericInst}) - let rec = semConstExpr(c, traitCall[2]).intVal != 0 - while arg.kind == tyDistinct: - arg = arg.base.skipTypes(skippedTypes + {tyGenericInst}) - if not rec: break - result = getTypeDescNode(c, arg, operand.owner, traitCall.info) + if arg.kind == tyDistinct: + while arg.kind == tyDistinct: + arg = arg.base + arg = arg.skipTypes(skippedTypes + {tyGenericInst}) + result = getTypeDescNode(c, arg, operand.owner, traitCall.info) + else: + localError(c.config, traitCall.info, + "distinctBase expects a distinct type as argument. The given type was " & typeToString(operand)) + result = newType(tyError, nextTypeId c.idgen, context).toNode(traitCall.info) else: localError(c.config, traitCall.info, "unknown trait: " & s) result = newNodeI(nkEmpty, traitCall.info) @@ -231,8 +220,12 @@ proc semOrd(c: PContext, n: PNode): PNode = let parType = n[1].typ if isOrdinalType(parType, allowEnumWithHoles=true): discard + elif parType.kind == tySet: + let a = toInt64(firstOrd(c.config, parType)) + let b = toInt64(lastOrd(c.config, parType)) + result.typ = makeRangeType(c, a, b, n.info) else: - localError(c.config, n.info, errOrdinalTypeExpected % typeToString(parType, preferDesc)) + localError(c.config, n.info, errOrdinalTypeExpected) result.typ = errorType(c) proc semBindSym(c: PContext, n: PNode): PNode = @@ -241,12 +234,14 @@ proc semBindSym(c: PContext, n: PNode): PNode = let sl = semConstExpr(c, n[1]) if sl.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: - return localErrorNode(c, n, n[1].info, errStringLiteralExpected) + localError(c.config, n[1].info, errStringLiteralExpected) + return errorNode(c, n) let isMixin = semConstExpr(c, n[2]) if isMixin.kind != nkIntLit or isMixin.intVal < 0 or isMixin.intVal > high(TSymChoiceRule).int: - return localErrorNode(c, n, n[2].info, errConstExprExpected) + localError(c.config, n[2].info, errConstExprExpected) + return errorNode(c, n) let id = newIdentNode(getIdent(c.cache, sl.strVal), n.info) let s = qualifiedLookUp(c, id, {checkUndeclared}) @@ -263,10 +258,12 @@ proc semBindSym(c: PContext, n: PNode): PNode = proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode = if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}: - return localErrorNode(c, n, info.info, errStringOrIdentNodeExpected) + localError(c.config, info.info, errStringOrIdentNodeExpected) + return errorNode(c, n) if isMixin < 0 or isMixin > high(TSymChoiceRule).int: - return localErrorNode(c, n, info.info, errConstExprExpected) + localError(c.config, info.info, errConstExprExpected) + return errorNode(c, n) let id = if n.kind == nkIdent: n else: newIdentNode(getIdent(c.cache, n.strVal), info.info) @@ -385,8 +382,6 @@ proc semUnown(c: PContext; n: PNode): PNode = let b = unownedType(c, t[^1]) if b != t[^1]: result = copyType(t, nextTypeId c.idgen, t.owner) - copyTypeProps(c.graph, c.idgen.module, result, t) - result[^1] = b result.flags.excl tfHasOwned else: @@ -404,14 +399,18 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym # Replace nkDerefExpr by nkHiddenDeref # nkDeref is for 'ref T': x[].field # nkHiddenDeref is for 'var T': x.field - proc transform(c: PContext; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode = + proc transform(c: PContext; procSym: PSym; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode = result = shallowCopy(n) if sameTypeOrNil(n.typ, old): result.typ = fresh - if n.kind == nkSym and n.sym == oldParam: - result.sym = newParam + if n.kind == nkSym: + if n.sym == oldParam: + result.sym = newParam + elif n.sym.owner == orig: + result.sym = copySym(n.sym, nextSymId c.idgen) + result.sym.owner = procSym for i in 0 ..< safeLen(n): - result[i] = transform(c, n[i], old, fresh, oldParam, newParam) + result[i] = transform(c, procSym, n[i], old, fresh, oldParam, newParam) #if n.kind == nkDerefExpr and sameType(n[0].typ, old): # result = @@ -425,7 +424,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym let newParam = newSym(skParam, oldParam.name, nextSymId c.idgen, result, result.info) newParam.typ = newParamType # proc body: - result.ast = transform(c, orig.ast, origParamType, newParamType, oldParam, newParam) + result.ast = transform(c, result, orig.ast, origParamType, newParamType, oldParam, newParam) # proc signature: result.typ = newProcType(result.info, nextTypeId c.idgen, result) result.typ.addParam newParam @@ -444,7 +443,7 @@ proc semQuantifier(c: PContext; n: PNode): PNode = let op = considerQuotedIdent(c, it[0]) if op.id == ord(wIn): let v = newSymS(skForVar, it[1], c) - styleCheckDef(c, v) + styleCheckDef(c.config, v) onDef(it[1].info, v) let domain = semExprWithType(c, it[2], {efWantIterator}) v.typ = domain.typ @@ -465,68 +464,8 @@ proc semOld(c: PContext; n: PNode): PNode = localError(c.config, n[1].info, n[1].sym.name.s & " does not belong to " & getCurrOwner(c).name.s) result = n -proc semNewFinalize(c: PContext; n: PNode): PNode = - # Make sure the finalizer procedure refers to a procedure - if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}: - localError(c.config, n.info, "finalizer must be a direct reference to a proc") - elif optTinyRtti in c.config.globalOptions: - let nfin = skipConvCastAndClosure(n[^1]) - let fin = case nfin.kind - of nkSym: nfin.sym - of nkLambda, nkDo: nfin[namePos].sym - else: - localError(c.config, n.info, "finalizer must be a direct reference to a proc") - nil - if fin != nil: - if fin.kind notin {skProc, skFunc}: - # calling convention is checked in codegen - localError(c.config, n.info, "finalizer must be a direct reference to a proc") - - # check if we converted this finalizer into a destructor already: - let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef})) - if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and - getAttachedOp(c.graph, t, attachedDestructor).owner == fin: - discard "already turned this one into a finalizer" - else: - let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), nextSymId c.idgen, fin.owner, fin.info) - let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, nextSymId c.idgen)) - selfSymNode.typ = fin.typ[1] - wrapperSym.flags.incl sfUsed - - let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode), - params = nkFormalParams.newTree(c.graph.emptyNode, - newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType, - fin.ast[paramsPos][1][1].info, fin.typ[1]), c.graph.emptyNode) - ), - name = newSymNode(wrapperSym), pattern = fin.ast[patternPos], - genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {}) - - var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info) - transFormedSym.owner = fin - if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags: - let origParamType = transFormedSym.ast[bodyPos][1].typ - let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs)) - let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info) - selfPtr.add transFormedSym.ast[bodyPos][1] - selfPtr.typ = selfSymbolType - transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr) - bindTypeHook(c, transFormedSym, n, attachedDestructor) - result = addDefaultFieldForNew(c, n) - -proc semPrivateAccess(c: PContext, n: PNode): PNode = - let t = n[1].typ[0].toObjectFromRefPtrGeneric - c.currentScope.allowPrivateAccess.add t.sym - result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid)) - -proc checkDefault(c: PContext, n: PNode): PNode = - result = n - c.config.internalAssert result[1].typ.kind == tyTypeDesc - let constructed = result[1].typ.base - if constructed.requiresInit: - message(c.config, n.info, warnUnsafeDefault, typeToString(constructed)) - proc magicsAfterOverloadResolution(c: PContext, n: PNode, - flags: TExprFlags; expectedType: PType = nil): PNode = + flags: TExprFlags): PNode = ## This is the preferred code point to implement magics. ## ``c`` the current module, a symbol table to a very good approximation ## ``n`` the ast like it would be passed to a real macro @@ -537,7 +476,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mAddr: checkSonsLen(n, 2, c.config) result = n - result[1] = semAddrArg(c, n[1]) + result[1] = semAddrArg(c, n[1], n[0].sym.name.s == "unsafeAddr") result.typ = makePtrType(c, result[1].typ) of mTypeOf: result = semTypeOf(c, n) @@ -553,9 +492,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result = semArrPut(c, n, flags) of mAsgn: if n[0].sym.name.s == "=": - result = semAsgnOpr(c, n, nkAsgn) - elif n[0].sym.name.s == "=sink": - result = semAsgnOpr(c, n, nkSinkAsgn) + result = semAsgnOpr(c, n) else: result = semShallowCopy(c, n, flags) of mIsPartOf: result = semIsPartOf(c, n, flags) @@ -585,25 +522,35 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result = n else: result = plugin(c, n) - of mNew: - if n[0].sym.name.s == "unsafeNew": # special case for unsafeNew - result = n - else: - result = addDefaultFieldForNew(c, n) of mNewFinalize: - result = semNewFinalize(c, n) + # Make sure the finalizer procedure refers to a procedure + if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}: + localError(c.config, n.info, "finalizer must be a direct reference to a proc") + elif optTinyRtti in c.config.globalOptions: + let nfin = skipConvCastAndClosure(n[^1]) + let fin = case nfin.kind + of nkSym: nfin.sym + of nkLambda, nkDo: nfin[namePos].sym + else: + localError(c.config, n.info, "finalizer must be a direct reference to a proc") + nil + if fin != nil: + if fin.kind notin {skProc, skFunc}: + # calling convention is checked in codegen + localError(c.config, n.info, "finalizer must be a direct reference to a proc") + + # check if we converted this finalizer into a destructor already: + let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef})) + if t != nil and t.attachedOps[attachedDestructor] != nil and t.attachedOps[attachedDestructor].owner == fin: + discard "already turned this one into a finalizer" + else: + bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor) + result = n of mDestroy: result = n let t = n[1].typ.skipTypes(abstractVar) - let op = getAttachedOp(c.graph, t, attachedDestructor) - if op != nil: - result[0] = newSymNode(op) - of mTrace: - result = n - let t = n[1].typ.skipTypes(abstractVar) - let op = getAttachedOp(c.graph, t, attachedTrace) - if op != nil: - result[0] = newSymNode(op) + if t.destructor != nil: + result[0] = newSymNode(t.destructor) of mUnown: result = semUnown(c, n) of mExists, mForall: @@ -618,13 +565,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if seqType.kind == tySequence and seqType.base.requiresInit: message(c.config, n.info, warnUnsafeSetLen, typeToString(seqType.base)) of mDefault: - result = checkDefault(c, n) - let typ = result[^1].typ.skipTypes({tyTypeDesc}) - let defaultExpr = defaultNodeField(c, result[^1], typ) - if defaultExpr != nil: - result = defaultExpr - of mZeroDefault: - result = checkDefault(c, n) + result = n + c.config.internalAssert result[1].typ.kind == tyTypeDesc + let constructed = result[1].typ.base + if constructed.requiresInit: + message(c.config, n.info, warnUnsafeDefault, typeToString(constructed)) of mIsolate: if not checkIsolate(n[1]): localError(c.config, n.info, "expression cannot be isolated: " & $n[1]) @@ -633,11 +578,5 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if n[1].typ.skipTypes(abstractInst).kind in {tyUInt..tyUInt64}: n[0].sym.magic = mSubU result = n - of mPrivateAccess: - result = semPrivateAccess(c, n) - of mArrToSeq: - result = n - if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and expectedType.kind == tySequence and result.typ[0].kind == tyEmpty: - result.typ = expectedType # type inference for empty sequence # bug #21377 else: result = n diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 29067cfa4..792488c9f 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -11,8 +11,6 @@ # included from sem.nim -from sugar import dup - type ObjConstrContext = object typ: PType # The constructed type @@ -29,10 +27,6 @@ type initNone # None of the fields have been initialized initConflict # Fields from different branches have been initialized - -proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, - flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] - proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) = case newStatus of initConflict: @@ -76,21 +70,28 @@ proc semConstrField(c: PContext, flags: TExprFlags, let assignment = locateFieldInInitExpr(c, field, initExpr) if assignment != nil: if nfSem in assignment.flags: return assignment[1] - if nfUseDefaultField in assignment[1].flags: - discard - elif not fieldVisible(c, field): + if not fieldVisible(c, field): localError(c.config, initExpr.info, "the field '$1' is not accessible." % [field.name.s]) return - var initValue = semExprFlagDispatched(c, assignment[1], flags, field.typ) + var initValue = semExprFlagDispatched(c, assignment[1], flags) if initValue != nil: - initValue = fitNodeConsiderViewType(c, field.typ, initValue, assignment.info) + initValue = fitNode(c, field.typ, initValue, assignment.info) assignment[0] = newSymNode(field) assignment[1] = initValue assignment.flags.incl nfSem return initValue +proc caseBranchMatchesExpr(branch, matched: PNode): bool = + for i in 0.. 0 proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = @@ -402,30 +361,26 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = assert objType != nil if objType.kind == tyObject: var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) - let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults}) - if constrCtx.missingFields.len > 0: - localError(c.config, info, - "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) + let initResult = semConstructTypeAux(c, constrCtx, {}) + assert constrCtx.missingFields.len > 0 + localError(c.config, info, + "The $1 type doesn't have a default value. The following fields must " & + "be initialized: $2.", + [typeToString(t), listSymbolNames(constrCtx.missingFields)]) elif objType.kind == tyDistinct: localError(c.config, info, - "The $1 distinct type doesn't have a default value." % typeToString(t)) + "The $1 distinct type doesn't have a default value.", [typeToString(t)]) else: assert false, "Must not enter here." -proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = +proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n[0], nil) result = newNodeIT(nkObjConstr, n.info, t) - for i in 0.. 0: - hasError = true localError(c.config, result.info, - "The $1 type requires the following fields to be initialized: $2." % + "The $1 type requires the following fields to be initialized: $2.", [t.sym.name.s, listSymbolNames(constrCtx.missingFields)]) # Since we were traversing the object fields, it's possible that @@ -466,7 +415,6 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType if nfSem notin field.flags: if field.kind != nkExprColonExpr: invalidObjConstr(c, field) - hasError = true continue let id = considerQuotedIdent(c, field[0]) # This node was not processed. There are two possible reasons: @@ -475,18 +423,10 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType let prevId = considerQuotedIdent(c, result[j][0]) if prevId.id == id.id: localError(c.config, field.info, errFieldInitTwice % id.s) - hasError = true - break + return # 2) No such field exists in the constructed type - let msg = errUndeclaredField % id.s & " for type " & getProcHeader(c.config, t.sym) - localError(c.config, field.info, msg) - hasError = true - break - - result.sons.add defaults + localError(c.config, field.info, errUndeclaredFieldX % id.s) + return if initResult == initFull: incl result.flags, nfAllFieldsSet - - # wrap in an error see #17437 - if hasError: result = errorNode(c, result) diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index ced479dbe..3948ac748 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -81,7 +81,7 @@ proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx = result.slices = @[] result.args = @[] result.guards.s = @[] - result.guards.g = g + result.guards.o = initOperators(g) result.graph = g proc lookupSlot(c: AnalysisCtx; s: PSym): int = @@ -138,7 +138,7 @@ proc checkLe(c: AnalysisCtx; a, b: PNode) = proc checkBounds(c: AnalysisCtx; arr, idx: PNode) = checkLe(c, lowBound(c.graph.config, arr), idx) - checkLe(c, idx, highBound(c.graph.config, arr, c.graph.operators)) + checkLe(c, idx, highBound(c.graph.config, arr, c.guards.o)) proc addLowerBoundAsFacts(c: var AnalysisCtx) = for v in c.locals: @@ -147,8 +147,8 @@ proc addLowerBoundAsFacts(c: var AnalysisCtx) = proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) = checkLocal(c, n) - let le = le.canon(c.graph.operators) - let ri = ri.canon(c.graph.operators) + let le = le.canon(c.guards.o) + let ri = ri.canon(c.guards.o) # perform static bounds checking here; and not later! let oldState = c.guards.s.len addLowerBoundAsFacts(c) @@ -192,7 +192,7 @@ proc subStride(c: AnalysisCtx; n: PNode): PNode = if isLocal(n): let s = c.lookupSlot(n.sym) if s >= 0 and c.locals[s].stride != nil: - result = buildAdd(n, c.locals[s].stride.intVal, c.graph.operators) + result = buildAdd(n, c.locals[s].stride.intVal, c.guards.o) else: result = n elif n.safeLen > 0: @@ -307,23 +307,23 @@ proc analyseCase(c: var AnalysisCtx; n: PNode) = proc analyseIf(c: var AnalysisCtx; n: PNode) = analyse(c, n[0][0]) let oldFacts = c.guards.s.len - addFact(c.guards, canon(n[0][0], c.graph.operators)) + addFact(c.guards, canon(n[0][0], c.guards.o)) analyse(c, n[0][1]) for i in 1.. 1: - addFact(c.guards, canon(branch[0], c.graph.operators)) + addFact(c.guards, canon(branch[0], c.guards.o)) for i in 0.. 0: result = shallowCopy(n) for i in 0.. 0: @@ -463,10 +459,10 @@ proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: result = n proc checkArgs(a: var AnalysisCtx; n: PNode) = - discard "to implement" + discard "too implement" proc generateAliasChecks(a: AnalysisCtx; result: PNode) = - discard "to implement" + discard "too implement" proc liftParallel*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode = # this needs to be called after the 'for' loop elimination diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 396ea6606..64ee72290 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -10,11 +10,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg, strutils, options, guards, lineinfos, semfold, semdata, - modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling, tables, - semstrictfuncs - -when defined(nimPreviewSlimSystem): - import std/assertions + modulegraphs, varpartitions, typeallowed, nilcheck when defined(useDfa): import dfa @@ -67,27 +63,26 @@ type TEffects = object exc: PNode # stack of exceptions tags: PNode # list of tags - forbids: PNode # list of tags - bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt, currentBlock: int + bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn: int owner: PSym ownerModule: PSym init: seq[int] # list of initialized variables - scopes: Table[int, int] # maps var-id to its scope (see also `currentBlock`). guards: TModel # nested guards locked: seq[PNode] # locked locations gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool - isInnerProc: bool + hasDangerousAssign: bool inEnforcedNoSideEffects: bool + maxLockLevel, currLockLevel: TLockLevel currOptions: TOptions config: ConfigRef graph: ModuleGraph c: PContext - escapingParams: IntSet PEffects = var TEffects -const - errXCannotBeAssignedTo = "'$1' cannot be assigned to" - errLetNeedsInit = "'let' symbol requires an initialization" +proc `<`(a, b: TLockLevel): bool {.borrow.} +proc `<=`(a, b: TLockLevel): bool {.borrow.} +proc `==`(a, b: TLockLevel): bool {.borrow.} +proc max(a, b: TLockLevel): TLockLevel {.borrow.} proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) = if typ == nil: return @@ -102,16 +97,38 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) = optSeqDestructors in tracked.config.globalOptions: tracked.owner.flags.incl sfInjectDestructors -proc isLocalSym(a: PEffects, s: PSym): bool = - s.typ != nil and (s.kind in {skLet, skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and - sfGlobal notin s.flags and s.owner == a.owner +proc isLocalVar(a: PEffects, s: PSym): bool = + # and (s.kind != skParam or s.typ.kind == tyOut) + s.kind in {skVar, skResult} and sfGlobal notin s.flags and + s.owner == a.owner and s.typ != nil + +proc getLockLevel(t: PType): TLockLevel = + var t = t + # tyGenericInst(TLock {tyGenericBody}, tyStatic, tyObject): + if t.kind == tyGenericInst and t.len == 3: t = t[1] + if t.kind == tyStatic and t.n != nil and t.n.kind in {nkCharLit..nkInt64Lit}: + result = t.n.intVal.TLockLevel proc lockLocations(a: PEffects; pragma: PNode) = if pragma.kind != nkExprColonExpr: localError(a.config, pragma.info, "locks pragma without argument") return + var firstLL = TLockLevel(-1'i16) for x in pragma[1]: + let thisLL = getLockLevel(x.typ) + if thisLL != 0.TLockLevel: + if thisLL < 0.TLockLevel or thisLL > MaxLockLevel.TLockLevel: + localError(a.config, x.info, "invalid lock level: " & $thisLL) + elif firstLL < 0.TLockLevel: firstLL = thisLL + elif firstLL != thisLL: + localError(a.config, x.info, + "multi-lock requires the same static lock level for every operand") + a.maxLockLevel = max(a.maxLockLevel, firstLL) a.locked.add x + if firstLL >= 0.TLockLevel and firstLL != a.currLockLevel: + if a.currLockLevel > 0.TLockLevel and a.currLockLevel <= firstLL: + localError(a.config, pragma.info, "invalid nested locking") + a.currLockLevel = firstLL proc guardGlobal(a: PEffects; n: PNode; guard: PSym) = # check whether the corresponding lock is held: @@ -120,7 +137,7 @@ proc guardGlobal(a: PEffects; n: PNode; guard: PSym) = # we allow accesses nevertheless in top level statements for # easier initialization: #if a.isTopLevel: - # message(a.config, n.info, warnUnguardedAccess, renderTree(n)) + # message(n.info, warnUnguardedAccess, renderTree(n)) #else: if not a.isTopLevel: localError(a.config, n.info, "unguarded access: " & renderTree(n)) @@ -167,54 +184,23 @@ proc makeVolatile(a: PEffects; s: PSym) {.inline.} = if a.inTryStmt > 0 and a.config.exc == excSetjmp: incl(s.flags, sfVolatile) -proc varDecl(a: PEffects; n: PNode) {.inline.} = - if n.kind == nkSym: - a.scopes[n.sym.id] = a.currentBlock - -proc skipHiddenDeref(n: PNode): PNode {.inline.} = - result = if n.kind == nkHiddenDeref: n[0] else: n - proc initVar(a: PEffects, n: PNode; volatileCheck: bool) = - let n = skipHiddenDeref(n) if n.kind != nkSym: return let s = n.sym - if isLocalSym(a, s): + if isLocalVar(a, s): if volatileCheck: makeVolatile(a, s) for x in a.init: - if x == s.id: - if strictDefs in a.c.features and s.kind == skLet: - localError(a.config, n.info, errXCannotBeAssignedTo % - renderTree(n, {renderNoComments} - )) - return + if x == s.id: return a.init.add s.id - if a.scopes.getOrDefault(s.id) == a.currentBlock: - #[ Consider this case: - - var x: T - while true: - if cond: - x = T() #1 - else: - x = T() #2 - use x - - Even though both #1 and #2 are first writes we must use the `=copy` - here so that the old value is destroyed because `x`'s destructor is - run outside of the while loop. This is why we need the check here that - the assignment is done in the same logical block as `x` was declared in. - ]# - n.flags.incl nfFirstWrite proc initVarViaNew(a: PEffects, n: PNode) = - let n = skipHiddenDeref(n) if n.kind != nkSym: return let s = n.sym if {tfRequiresInit, tfNotNil} * s.typ.flags <= {tfNotNil}: # 'x' is not nil, but that doesn't mean its "not nil" children # are initialized: initVar(a, n, volatileCheck=true) - elif isLocalSym(a, s): + elif isLocalVar(a, s): makeVolatile(a, s) proc warnAboutGcUnsafe(n: PNode; conf: ConfigRef) = @@ -236,21 +222,13 @@ proc markGcUnsafe(a: PEffects; reason: PNode) = a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, nextSymId a.c.idgen, a.owner, reason.info, {}) -proc markSideEffect(a: PEffects; reason: PNode | PSym; useLoc: TLineInfo) = - if not a.inEnforcedNoSideEffects: - a.hasSideEffect = true - if a.owner.kind in routineKinds: - var sym: PSym - when reason is PNode: - if reason.kind == nkSym: - sym = reason.sym - else: - let kind = if reason.kind == nkHiddenDeref: skParam else: skUnknown - sym = newSym(kind, a.owner.name, nextSymId a.c.idgen, a.owner, reason.info, {}) - else: - sym = reason - a.c.sideEffects.mgetOrPut(a.owner.id, @[]).add (useLoc, sym) - when false: markGcUnsafe(a, reason) +when true: + template markSideEffect(a: PEffects; reason: typed) = + if not a.inEnforcedNoSideEffects: a.hasSideEffect = true +else: + template markSideEffect(a: PEffects; reason: typed) = + if not a.inEnforcedNoSideEffects: a.hasSideEffect = true + markGcUnsafe(a, reason) proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) = let u = s.gcUnsafetyReason @@ -258,26 +236,16 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: Co let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated case u.kind of skLet, skVar: - if u.typ.skipTypes(abstractInst).kind == tyProc: - message(conf, s.info, msgKind, - "'$#' is not GC-safe as it calls '$#'" % - [s.name.s, u.name.s]) - else: - message(conf, s.info, msgKind, - ("'$#' is not GC-safe as it accesses '$#'" & - " which is a global using GC'ed memory") % [s.name.s, u.name.s]) + message(conf, s.info, msgKind, + ("'$#' is not GC-safe as it accesses '$#'" & + " which is a global using GC'ed memory") % [s.name.s, u.name.s]) of routineKinds: # recursive call *always* produces only a warning so the full error # message is printed: - if u.kind == skMethod and {sfBase, sfThread} * u.flags == {sfBase}: - message(conf, u.info, msgKind, - "Base method '$#' requires explicit '{.gcsafe.}' to be GC-safe" % - [u.name.s]) - else: - listGcUnsafety(u, true, cycleCheck, conf) - message(conf, s.info, msgKind, - "'$#' is not GC-safe as it calls '$#'" % - [s.name.s, u.name.s]) + listGcUnsafety(u, true, cycleCheck, conf) + message(conf, s.info, msgKind, + "'$#' is not GC-safe as it calls '$#'" % + [s.name.s, u.name.s]) of skParam, skForVar: message(conf, s.info, msgKind, "'$#' is not GC-safe as it performs an indirect call via '$#'" % @@ -290,31 +258,6 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) = var cycleCheck = initIntSet() listGcUnsafety(s, onlyWarning, cycleCheck, conf) -proc listSideEffects(result: var string; s: PSym; cycleCheck: var IntSet; - conf: ConfigRef; context: PContext; indentLevel: int) = - template addHint(msg; lineInfo; sym; level = indentLevel) = - result.addf("$# $# Hint: '$#' $#\n", repeat(">", level), conf $ lineInfo, sym, msg) - if context.sideEffects.hasKey(s.id): - for (useLineInfo, u) in context.sideEffects[s.id]: - if u != nil and not cycleCheck.containsOrIncl(u.id): - case u.kind - of skLet, skVar: - addHint("accesses global state '$#'" % u.name.s, useLineInfo, s.name.s) - addHint("accessed by '$#'" % s.name.s, u.info, u.name.s, indentLevel + 1) - of routineKinds: - addHint("calls `.sideEffect` '$#'" % u.name.s, useLineInfo, s.name.s) - addHint("called by '$#'" % s.name.s, u.info, u.name.s, indentLevel + 1) - listSideEffects(result, u, cycleCheck, conf, context, indentLevel + 2) - of skParam, skForVar: - addHint("calls routine via hidden pointer indirection", useLineInfo, s.name.s) - else: - addHint("calls routine via pointer indirection", useLineInfo, s.name.s) - -proc listSideEffects(result: var string; s: PSym; conf: ConfigRef; context: PContext) = - var cycleCheck = initIntSet() - result.addf("'$#' can have side effects\n", s.name.s) - listSideEffects(result, s, cycleCheck, conf, context, 1) - proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) = if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and s.magic != mNimvm: @@ -323,16 +266,15 @@ proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) = (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): #if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n) markGcUnsafe(a, s) - markSideEffect(a, s, n.info) - if s.owner != a.owner and s.kind in {skVar, skLet, skForVar, skResult, skParam} and - {sfGlobal, sfThread} * s.flags == {}: - a.isInnerProc = true + markSideEffect(a, s) + else: + markSideEffect(a, s) proc useVar(a: PEffects, n: PNode) = let s = n.sym if a.inExceptOrFinallyStmt > 0: incl s.flags, sfUsedInFinallyOrExcept - if isLocalSym(a, s): + if isLocalVar(a, s): if sfNoInit in s.flags: # If the variable is explicitly marked as .noinit. do not emit any error a.init.add s.id @@ -340,11 +282,7 @@ proc useVar(a: PEffects, n: PNode) = if s.typ.requiresInit: message(a.config, n.info, warnProveInit, s.name.s) elif a.leftPartOfAsgn <= 0: - if strictDefs in a.c.features: - if s.kind == skLet: - localError(a.config, n.info, errLetNeedsInit) - else: - message(a.config, n.info, warnUninit, s.name.s) + message(a.config, n.info, warnUninit, s.name.s) # prevent superfluous warnings about the same variable: a.init.add s.id useVarNoInitCheck(a, n, s) @@ -363,8 +301,8 @@ proc addToIntersection(inter: var TIntersection, s: int) = proc throws(tracked, n, orig: PNode) = if n.typ == nil or n.typ.kind != tyError: if orig != nil: - let x = copyTree(orig) - x.typ = n.typ + let x = copyNode(n) + x.info = orig.info tracked.add x else: tracked.add n @@ -388,7 +326,7 @@ proc createTag(g: ModuleGraph; n: PNode): PNode = if not n.isNil: result.info = n.info proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) = - #assert e.kind != nkRaiseStmt + assert e.kind != nkRaiseStmt var aa = a.exc for i in a.bottom..= tracked.currLockLevel: + # if in lock section: + if tracked.currLockLevel > 0.TLockLevel: + localError tracked.config, n.info, errGenerated, + "expected lock level < " & $tracked.currLockLevel & + " but got lock level " & $lockLevel + tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel) + proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = let pragma = s.ast[pragmasPos] let spec = effectSpec(pragma, wRaises) @@ -556,14 +498,14 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, s) if tfNoSideEffect notin s.typ.flags: - markSideEffect(tracked, s, n.info) + markSideEffect(tracked, s) + mergeLockLevels(tracked, n, s.getLockLevel) proc procVarCheck(n: PNode; conf: ConfigRef) = if n.kind in nkSymChoices: for x in n: procVarCheck(x, conf) elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds: - localError(conf, n.info, ("'$1' is a built-in and cannot be used as " & - "a first-class procedure") % n.sym.name.s) + localError(conf, n.info, "'$1' cannot be passed to a procvar" % n.sym.name.s) proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = let n = n.skipConv @@ -596,32 +538,28 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) = addRaiseEffect(tracked, createRaise(tracked.graph, n), nil) addTag(tracked, createTag(tracked.graph, n), nil) + let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel + else: op.lockLevel + #if lockLevel == UnknownLockLevel: + # message(n.info, warnUser, "had to assume the worst here") + mergeLockLevels(tracked, n, lockLevel) -proc isOwnedProcVar(tracked: PEffects; n: PNode): bool = +proc isOwnedProcVar(n: PNode; owner: PSym): bool = # XXX prove the soundness of this effect system rule - result = n.kind == nkSym and n.sym.kind == skParam and - tracked.owner == n.sym.owner - #if result and sfPolymorphic notin n.sym.flags: - # echo tracked.config $ n.info, " different here!" - if laxEffects notin tracked.c.config.legacyFeatures: - result = result and sfEffectsDelayed in n.sym.flags + result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner proc isNoEffectList(n: PNode): bool {.inline.} = assert n.kind == nkEffectList - n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil and n[forbiddenEffects] == nil) + n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil) proc isTrival(caller: PNode): bool {.inline.} = result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved, mSwap} -proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) = +proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) = let a = skipConvCastAndClosure(n) let op = a.typ - let param = if formals != nil and argIndex < formals.len and formals.n != nil: formals.n[argIndex].sym else: nil # assume indirect calls are taken here: - if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and - not isTrival(caller) and - ((param != nil and sfEffectsDelayed in param.flags) or laxEffects in tracked.c.config.legacyFeatures): - + if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and not isTrival(caller): internalAssert tracked.config, op.n[0].kind == nkEffectList var effectList = op.n[0] var s = n.skipConv @@ -634,15 +572,15 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar # we have no explicit effects but it's a forward declaration and so it's # stated there are no additional effects, so simply propagate them: propagateEffects(tracked, n, n.sym) - elif not isOwnedProcVar(tracked, a): + elif not isOwnedProcVar(a, tracked.owner): # we have no explicit effects so assume the worst: assumeTheWorst(tracked, n, op) # assume GcUnsafe unless in its type; 'forward' does not matter: - if notGcSafe(op) and not isOwnedProcVar(tracked, a): + if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner): if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, a) - elif tfNoSideEffect notin op.flags and not isOwnedProcVar(tracked, a): - markSideEffect(tracked, a, n.info) + elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner): + markSideEffect(tracked, a) else: mergeRaises(tracked, effectList[exceptionEffects], n) mergeTags(tracked, effectList[tagEffects], n) @@ -650,11 +588,10 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, a) elif tfNoSideEffect notin op.flags: - markSideEffect(tracked, a, n.info) - let paramType = if formals != nil and argIndex < formals.len: formals[argIndex] else: nil + markSideEffect(tracked, a) if paramType != nil and paramType.kind in {tyVar}: invalidateFacts(tracked.guards, n) - if n.kind == nkSym and isLocalSym(tracked, n.sym): + if n.kind == nkSym and isLocalVar(tracked, n.sym): makeVolatile(tracked, n.sym) if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags: let argtype = skipTypes(a.typ, abstractInst) @@ -676,13 +613,12 @@ proc breaksBlock(n: PNode): bool = proc trackCase(tracked: PEffects, n: PNode) = track(tracked, n[0]) - inc tracked.inIfStmt let oldState = tracked.init.len let oldFacts = tracked.guards.s.len let stringCase = n[0].typ != nil and skipTypes(n[0].typ, - abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString, tyCstring} + abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString} let interesting = not stringCase and interestingCaseExpr(n[0]) and - (tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features) + tracked.config.hasWarn(warnProveField) var inter: TIntersection = @[] var toCover = 0 for i 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 @@ -738,7 +672,6 @@ 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}: @@ -756,8 +689,11 @@ proc trackBlock(tracked: PEffects, n: PNode) = else: track(tracked, n) +proc paramType(op: PType, i: int): PType = + if op != nil and i < op.len: result = op[i] + proc cstringCheck(tracked: PEffects; n: PNode) = - if n[0].typ.kind == tyCstring and (let a = skipConv(n[1]); + if n[0].typ.kind == tyCString and (let a = skipConv(n[1]); a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}): message(tracked.config, n.info, warnUnsafeCode, renderTree(n)) @@ -787,7 +723,7 @@ proc checkLe(c: PEffects; a, b: PNode) = proc checkBounds(c: PEffects; arr, idx: PNode) = checkLe(c, lowBound(c.config, arr), idx) - checkLe(c, idx, highBound(c.config, arr, c.guards.g.operators)) + checkLe(c, idx, highBound(c.config, arr, c.guards.o)) proc checkRange(c: PEffects; value: PNode; typ: PType) = let t = typ.skipTypes(abstractInst - {tyRange}) @@ -799,29 +735,6 @@ proc checkRange(c: PEffects; value: PNode; typ: PType) = checkLe(c, lowBound, value) checkLe(c, value, highBound) -#[ -proc passedToEffectsDelayedParam(tracked: PEffects; n: PNode) = - let t = n.typ.skipTypes(abstractInst) - if t.kind == tyProc: - if n.kind == nkSym and tracked.owner == n.sym.owner and sfEffectsDelayed in n.sym.flags: - discard "the arg is itself a delayed parameter, so do nothing" - else: - var effectList = t.n[0] - if effectList.len == effectListLen: - mergeRaises(tracked, effectList[exceptionEffects], n) - mergeTags(tracked, effectList[tagEffects], n) - if not importedFromC(n): - if notGcSafe(t): - if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) - markGcUnsafe(tracked, n) - if tfNoSideEffect notin t.flags: - markSideEffect(tracked, n, n.info) -]# - -proc checkForSink(tracked: PEffects; n: PNode) = - if tracked.inIfStmt == 0 and optSinkInference in tracked.config.options: - checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n) - proc trackCall(tracked: PEffects; n: PNode) = template gcsafeAndSideeffectCheck() = if notGcSafe(op) and not importedFromC(a): @@ -832,7 +745,7 @@ proc trackCall(tracked: PEffects; n: PNode) = if tfNoSideEffect notin op.flags and not importedFromC(a): # and it's not a recursive call: if not (a.kind == nkSym and a.sym == tracked.owner): - markSideEffect(tracked, a, n.info) + markSideEffect(tracked, a) # p's effects are ours too: var a = n[0] @@ -853,32 +766,25 @@ proc trackCall(tracked: PEffects; n: PNode) = if a.kind == nkSym: if a.sym == tracked.owner: tracked.isRecursive = true # even for recursive calls we need to check the lock levels (!): - if sfSideEffect in a.sym.flags: markSideEffect(tracked, a, n.info) + mergeLockLevels(tracked, n, a.sym.getLockLevel) + if sfSideEffect in a.sym.flags: markSideEffect(tracked, a) else: - discard + mergeLockLevels(tracked, n, op.lockLevel) var effectList = op.n[0] if a.kind == nkSym and a.sym.kind == skMethod: - if {sfBase, sfThread} * a.sym.flags == {sfBase}: - if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) - markGcUnsafe(tracked, a) propagateEffects(tracked, n, a.sym) elif isNoEffectList(effectList): if isForwardedProc(a): propagateEffects(tracked, n, a.sym) - elif isIndirectCall(tracked, a): + elif isIndirectCall(a, tracked.owner): assumeTheWorst(tracked, n, op) gcsafeAndSideeffectCheck() - else: - if laxEffects notin tracked.c.config.legacyFeatures and a.kind == nkSym and - a.sym.kind in routineKinds: - propagateEffects(tracked, n, a.sym) else: mergeRaises(tracked, effectList[exceptionEffects], n) mergeTags(tracked, effectList[tagEffects], n) gcsafeAndSideeffectCheck() - if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished, mExpandToAst, mQuoteAst}: - for i in 1.. 0: rr = rr.lastSon - message(g.config, r.info, if emitWarnings: warnEffect else: errGenerated, - renderTree(rr) & " " & msg & typeToString(r.typ)) + localError(g.config, r.info, errGenerated, msg & typeToString(r.typ)) popInfoContext(g.config) # hint about unnecessarily listed exception types: if hints: @@ -1359,16 +1175,12 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) = let p = disp.ast[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): - checkRaisesSpec(g, false, raisesSpec, actual[exceptionEffects], + checkRaisesSpec(g, raisesSpec, actual[exceptionEffects], "can raise an unlisted exception: ", hints=off, subtypeRelation) let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): - checkRaisesSpec(g, false, tagsSpec, actual[tagEffects], + checkRaisesSpec(g, tagsSpec, actual[tagEffects], "can have an unlisted effect: ", hints=off, subtypeRelation) - let forbidsSpec = effectSpec(p, wForbids) - if not isNil(forbidsSpec): - checkRaisesSpec(g, false, forbidsSpec, actual[tagEffects], - "has an illegal effect: ", hints=off, subtypeRelation, isForbids=true) if sfThread in disp.flags and notGcSafe(branch.typ): localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" % branch.name.s) @@ -1377,7 +1189,18 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) = localError(g.config, branch.info, "for method '" & branch.name.s & "' the `.requires` or `.ensures` properties are incompatible.") -proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) = + if branch.typ.lockLevel > disp.typ.lockLevel: + when true: + message(g.config, branch.info, warnLockLevel, + "base method has lock level $1, but dispatcher has $2" % + [$branch.typ.lockLevel, $disp.typ.lockLevel]) + else: + # XXX make this an error after bigbreak has been released: + localError(g.config, branch.info, + "base method has lock level $1, but dispatcher has $2" % + [$branch.typ.lockLevel, $disp.typ.lockLevel]) + +proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = var effects = t.n[0] if t.kind != tyProc or effects.kind != nkEffectList: return if n.kind != nkEmpty: @@ -1386,20 +1209,9 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) = let raisesSpec = effectSpec(n, wRaises) if not isNil(raisesSpec): effects[exceptionEffects] = raisesSpec - elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}): - effects[exceptionEffects] = newNodeI(nkArgList, effects.info) - let tagsSpec = effectSpec(n, wTags) if not isNil(tagsSpec): effects[tagEffects] = tagsSpec - elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}): - effects[tagEffects] = newNodeI(nkArgList, effects.info) - - let forbidsSpec = effectSpec(n, wForbids) - if not isNil(forbidsSpec): - effects[forbiddenEffects] = forbidsSpec - elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}): - effects[forbiddenEffects] = newNodeI(nkArgList, effects.info) let requiresSpec = propSpec(n, wRequires) if not isNil(requiresSpec): @@ -1409,30 +1221,22 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) = effects[ensuresEffects] = ensuresSpec effects[pragmasEffects] = n - if s != nil and s.magic != mNone: - if s.magic != mEcho: - t.flags.incl tfNoSideEffect -proc rawInitEffects(g: ModuleGraph; effects: PNode) = +proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) = newSeq(effects.sons, effectListLen) - effects[exceptionEffects] = newNodeI(nkArgList, effects.info) - effects[tagEffects] = newNodeI(nkArgList, effects.info) - effects[forbiddenEffects] = newNodeI(nkArgList, effects.info) + effects[exceptionEffects] = newNodeI(nkArgList, s.info) + effects[tagEffects] = newNodeI(nkArgList, s.info) effects[requiresEffects] = g.emptyNode effects[ensuresEffects] = g.emptyNode effects[pragmasEffects] = g.emptyNode -proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) = - rawInitEffects(g, effects) - t.exc = effects[exceptionEffects] t.tags = effects[tagEffects] - t.forbids = effects[forbiddenEffects] t.owner = s t.ownerModule = s.getModule t.init = @[] t.guards.s = @[] - t.guards.g = g + t.guards.o = initOperators(g) when defined(drnim): t.currOptions = g.config.options + s.options - {optStaticBoundsCheck} else: @@ -1442,7 +1246,6 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PC t.graph = g t.config = g.config t.c = c - t.currentBlock = 1 proc hasRealBody(s: PSym): bool = ## also handles importc procs with runnableExamples, which requires `=`, @@ -1451,27 +1254,14 @@ proc hasRealBody(s: PSym): bool = proc trackProc*(c: PContext; s: PSym, body: PNode) = let g = c.graph - when defined(nimsuggest): - if g.config.expandDone(): - return var effects = s.typ.n[0] if effects.kind != nkEffectList: return # effects already computed? if not s.hasRealBody: return - let emitWarnings = tfEffectSystemWorkaround in s.typ.flags - if effects.len == effectListLen and not emitWarnings: return - - var inferredEffects = newNodeI(nkEffectList, s.info) + if effects.len == effectListLen: return var t: TEffects - initEffects(g, inferredEffects, s, t, c) - rawInitEffects g, effects - - if not isEmptyType(s.typ[0]) and - s.kind in {skProc, skFunc, skConverter, skMethod}: - var res = s.ast[resultPos].sym # get result symbol - t.scopes[res.id] = t.currentBlock - + initEffects(g, effects, s, t, c) track(t, body) if s.kind != skMacro: @@ -1480,49 +1270,32 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = let param = params[i].sym let typ = param.typ if isSinkTypeForParam(typ) or - (t.config.selectedGC in {gcArc, gcOrc} and - (isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)): + (t.config.selectedGC in {gcArc, gcOrc} and isClosure(typ.skipTypes(abstractInst))): createTypeBoundOps(t, typ, param.info) - if isOutParam(typ) and param.id notin t.init: - message(g.config, param.info, warnProveInit, param.name.s) + when false: + if typ.kind == tyOut and param.id notin t.init: + message(g.config, param.info, warnProveInit, param.name.s) if not isEmptyType(s.typ[0]) and - (s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar or - strictDefs in c.features) and - s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone: + (s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar) and + s.kind in {skProc, skFunc, skConverter, skMethod}: var res = s.ast[resultPos].sym # get result symbol if res.id notin t.init: - if tfRequiresInit in s.typ[0].flags: - localError(g.config, body.info, "'$1' requires explicit initialization" % "result") - else: - message(g.config, body.info, warnProveInit, "result") + message(g.config, body.info, warnProveInit, "result") let p = s.ast[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): - checkRaisesSpec(g, false, raisesSpec, t.exc, "can raise an unlisted exception: ", + checkRaisesSpec(g, raisesSpec, t.exc, "can raise an unlisted exception: ", hints=on, subtypeRelation, hintsArg=s.ast[0]) # after the check, use the formal spec: effects[exceptionEffects] = raisesSpec - else: - effects[exceptionEffects] = t.exc let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): - checkRaisesSpec(g, false, tagsSpec, t.tags, "can have an unlisted effect: ", + checkRaisesSpec(g, tagsSpec, t.tags, "can have an unlisted effect: ", hints=off, subtypeRelation) # after the check, use the formal spec: effects[tagEffects] = tagsSpec - else: - effects[tagEffects] = t.tags - - let forbidsSpec = effectSpec(p, wForbids) - if not isNil(forbidsSpec): - checkRaisesSpec(g, false, forbidsSpec, t.tags, "has an illegal effect: ", - hints=off, subtypeRelation, isForbids=true) - # after the check, use the formal spec: - effects[forbiddenEffects] = forbidsSpec - else: - effects[forbiddenEffects] = t.forbids let requiresSpec = propSpec(p, wRequires) if not isNil(requiresSpec): @@ -1533,9 +1306,15 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = effects[ensuresEffects] = ensuresSpec var mutationInfo = MutationInfo() - if views in c.features: - var partitions = computeGraphPartitions(s, body, g, {borrowChecking}) - checkBorrowedLocations(partitions, body, g.config) + if {strictFuncs, views} * c.features != {}: + var goals: set[Goal] = {} + if strictFuncs in c.features: goals.incl constParameters + if views in c.features: goals.incl borrowChecking + var partitions = computeGraphPartitions(s, body, g.config, goals) + if not t.hasSideEffect and t.hasDangerousAssign: + t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo) + if views in c.features: + checkBorrowedLocations(partitions, body, g.config) if sfThread in s.flags and t.gcUnsafe: if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions: @@ -1548,22 +1327,24 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = when false: listGcUnsafety(s, onlyWarning=false, g.config) else: - if c.compilesContextId == 0: # don't render extended diagnostic messages in `system.compiles` context - var msg = "" - listSideEffects(msg, s, g.config, t.c) - message(g.config, s.info, errGenerated, msg) - else: - localError(g.config, s.info, "") # simple error for `system.compiles` context + localError(g.config, s.info, ("'$1' can have side effects" % s.name.s) & (g.config $ mutationInfo)) if not t.gcUnsafe: s.typ.flags.incl tfGcSafe if not t.hasSideEffect and sfSideEffect notin s.flags: s.typ.flags.incl tfNoSideEffect + if s.typ.lockLevel == UnspecifiedLockLevel: + s.typ.lockLevel = t.maxLockLevel + elif t.maxLockLevel > s.typ.lockLevel: + #localError(s.info, + message(g.config, s.info, warnLockLevel, + "declared lock level is $1, but real lock level is $2" % + [$s.typ.lockLevel, $t.maxLockLevel]) when defined(drnim): if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, s, body) when defined(useDfa): if s.name.s == "testp": dataflowAnalysis(s, body) - + when false: trackWrites(s, body) if strictNotNil in c.features and s.kind == skProc: checkNil(s, body, g.config, c.idgen) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 6d1ce388d..c2c59bb31 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -36,16 +36,6 @@ const errRecursiveDependencyX = "recursive dependency: '$1'" errRecursiveDependencyIteratorX = "recursion is not supported in iterators: '$1'" errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1" - errCannotAssignToGlobal = "cannot assign local to global variable" - -proc implicitlyDiscardable(n: PNode): bool - -proc hasEmpty(typ: PType): bool = - if typ.kind in {tySequence, tyArray, tySet}: - result = typ.lastSon.kind == tyEmpty - elif typ.kind == tyTuple: - for s in typ.sons: - result = result or hasEmpty(s) proc semDiscard(c: PContext, n: PNode): PNode = result = n @@ -54,9 +44,7 @@ proc semDiscard(c: PContext, n: PNode): PNode = n[0] = semExprWithType(c, n[0]) let sonType = n[0].typ let sonKind = n[0].kind - if isEmptyType(sonType) or hasEmpty(sonType) or - sonType.kind in {tyNone, tyTypeDesc} or - sonKind == nkTypeOfExpr: + if isEmptyType(sonType) or sonType.kind in {tyNone, tyTypeDesc} or sonKind == nkTypeOfExpr: localError(c.config, n.info, errInvalidDiscard) if sonType.kind == tyProc and sonKind notin nkCallKinds: # tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant. @@ -78,14 +66,12 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = x.info = n.info incl(s.flags, sfUsed) n[0] = x - suggestSym(c.graph, x.info, s, c.graph.usageSym) + suggestSym(c.config, x.info, s, c.graph.usageSym) onUse(x.info, s) else: localError(c.config, n.info, errInvalidControlFlowX % s.name.s) else: localError(c.config, n.info, errGenerated, "'continue' cannot have a label") - elif c.p.nestedBlockCounter > 0 and n.kind == nkBreakStmt and not c.p.breakInLoop: - localError(c.config, n.info, warnUnnamedBreak) elif (c.p.nestedLoopCounter <= 0) and ((c.p.nestedBlockCounter <= 0) or n.kind == nkContinueStmt): localError(c.config, n.info, errInvalidControlFlowX % renderTree(n, {renderNoComments})) @@ -100,32 +86,27 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode = result = n checkSonsLen(n, 2, c.config) openScope(c) - n[0] = forceBool(c, semExprWithType(c, n[0], expectedType = getSysType(c.graph, n.info, tyBool))) + n[0] = forceBool(c, semExprWithType(c, n[0])) inc(c.p.nestedLoopCounter) - let oldBreakInLoop = c.p.breakInLoop - c.p.breakInLoop = true n[1] = semStmt(c, n[1], flags) - c.p.breakInLoop = oldBreakInLoop dec(c.p.nestedLoopCounter) closeScope(c) if n[1].typ == c.enforceVoidContext: result.typ = c.enforceVoidContext elif efInTypeof in flags: result.typ = n[1].typ - elif implicitlyDiscardable(n[1]): - result[1].typ = c.enforceVoidContext proc semProc(c: PContext, n: PNode): PNode -proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}; expectedType: PType = nil): PNode = - result = semExpr(c, n, flags, expectedType) +proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}): PNode = + result = semExpr(c, n, flags) if result.typ != nil: # XXX tyGenericInst here? if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) -proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode = +proc semExprBranchScope(c: PContext, n: PNode): PNode = openScope(c) - result = semExprBranch(c, n, expectedType = expectedType) + result = semExprBranch(c, n) closeScope(c) const @@ -158,13 +139,8 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) = var n = newNodeI(nkDiscardStmt, result.info, 1) n[0] = result elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: - if result.typ.kind == tyNone: - localError(c.config, result.info, "expression has no type: " & - renderTree(result, {renderNoComments})) var n = result - while n.kind in skipForDiscardable: - if n.kind == nkTryStmt: n = n[0] - else: n = n.lastSon + while n.kind in skipForDiscardable: n = n.lastSon var s = "expression '" & $n & "' is of type '" & result.typ.typeToString & "' and has to be used (or discarded)" if result.info.line != n.info.line or @@ -174,25 +150,22 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) = s.add "; for a function call use ()" localError(c.config, n.info, s) -proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode = +proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode = result = n var typ = commonTypeBegin - var expectedType = expectedType var hasElse = false for i in 0.. ```except a, b, c: body``` - a.sons[0..0] = move a[0].sons + a.sons[0..0] = a[0].sons if a.len == 2 and a[0].isInfixAs(): # support ``except Exception as ex: body`` @@ -261,8 +231,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) elif a.len == 1: # count number of ``except: body`` blocks inc catchAllExcepts - message(c.config, a.info, warnBareExcept, - "The bare except clause is deprecated; use `except CatchableError:` instead") + else: # support ``except KeyError, ValueError, ... : body`` if catchAllExcepts > 0: @@ -291,15 +260,12 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) localError(c.config, a.info, "Only one general except clause is allowed after more specific exceptions") # last child of an nkExcept/nkFinally branch is a statement: - if a.kind != nkFinally: - a[^1] = semExprBranchScope(c, a[^1], expectedType) - typ = commonType(c, typ, a[^1]) - expectedType = typ - else: - a[^1] = semExprBranchScope(c, a[^1]) - dec last + a[^1] = semExprBranchScope(c, a[^1]) + if a.kind != nkFinally: typ = commonType(c, typ, a[^1]) + else: dec last closeScope(c) + dec c.p.inTryStmt if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped}: discardCheck(c, n[0], flags) for i in 1..= 1: it[0] else: it + var amb = false + let sym = searchInScopes(c, ident, amb) + # XXX what if amb is true? + if sym == nil or sfCustomPragma in sym.flags: return nil + # skip if not in scope; skip `template myAttr() {.pragma.}` + let lhs = b[0] + let clash = strTableGet(c.currentScope.symbols, lhs.ident) + if clash != nil: + # refs https://github.com/nim-lang/Nim/issues/8275 + wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info) - if isPossibleMacroPragma(c, it, key): - # we transform ``var p {.m, rest.}`` into ``m(do: var p {.rest.})`` and - # let the semantic checker deal with it: - var x = newNodeI(nkCall, key.info) - x.add(key) - - if it.kind in nkPragmaCallKinds and it.len > 1: - # pass pragma arguments to the macro too: - for i in 1.. 0 and not isException(typ.lastSon): - localError(c.config, n.info, "raised object of type $1 does not inherit from Exception" % typeToString(typ)) + localError(c.config, n.info, "raised object of type $1 does not inherit from Exception", + [typeToString(typ)]) proc addGenericParamListToScope(c: PContext, n: PNode) = if n.kind != nkGenericParams: illFormedAst(n, c.config) @@ -1184,7 +1044,7 @@ proc typeSectionTypeName(c: PContext; n: PNode): PNode = if result.kind != nkSym: illFormedAst(n, c.config) proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = - let typeDef = typeSection[i] + let typeDef= typeSection[i] checkSonsLen(typeDef, 3, c.config) var name = typeDef[0] var s: PSym @@ -1195,19 +1055,18 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = if pkg.isNil or pkg.kind != skPackage: localError(c.config, name.info, "unknown package name: " & pkgName.s) else: - let typsym = c.graph.packageTypes.strTableGet(typName) + let typsym = pkg.tab.strTableGet(typName) if typsym.isNil: s = semIdentDef(c, name[1], skType) onDef(name[1].info, s) s.typ = newTypeS(tyObject, c) s.typ.sym = s s.flags.incl sfForward - c.graph.packageTypes.strTableAdd s + pkg.tab.strTableAdd s addInterfaceDecl(c, s) elif typsym.kind == skType and sfForward in typsym.flags: s = typsym addInterfaceDecl(c, s) - # PRTEMP no onDef here? else: localError(c.config, name.info, typsym.name.s & " is not a type that can be forwarded") s = typsym @@ -1219,12 +1078,7 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = if name.kind == nkPragmaExpr: let rewritten = applyTypeSectionPragmas(c, name[1], typeDef) if rewritten != nil: - case rewritten.kind - of nkTypeDef: - typeSection[i] = rewritten - of nkTypeSection: - typeSection.sons[i .. i] = rewritten.sons - else: illFormedAst(rewritten, c.config) + typeSection[i] = rewritten typeDefLeftSidePass(c, typeSection, i) return pragma(c, s, name[1], typePragmas) @@ -1234,7 +1088,7 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = if not isTopLevel(c) or pkg.isNil: localError(c.config, name.info, "only top level types in a package can be 'package'") else: - let typsym = c.graph.packageTypes.strTableGet(s.name) + let typsym = pkg.tab.strTableGet(s.name) if typsym != nil: if sfForward notin typsym.flags or sfNoForward notin typsym.flags: typeCompleted(typsym) @@ -1255,19 +1109,16 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = proc typeSectionLeftSidePass(c: PContext, n: PNode) = # process the symbols on the left side for the whole type section, before # we even look at the type definitions on the right - var i = 0 - while i < n.len: # n may grow due to type pragma macros + for i in 0.. 0: - s.typ.n[0] = b.typ.n[0] - s.typ.flags = b.typ.flags else: localError(c.config, n.info, errNoSymbolToBorrowFromFound) -proc swapResult(n: PNode, sRes: PSym, dNode: PNode) = - ## Swap nodes that are (skResult) symbols to d(estination)Node. - for i in 0.. resultPos and n[resultPos] != nil: - if n[resultPos].sym.kind != skResult: + if n[resultPos].sym.kind != skResult or n[resultPos].sym.owner != getCurrOwner(c): localError(c.config, n.info, "incorrect result proc symbol") - if n[resultPos].sym.owner != getCurrOwner(c): - # re-write result with new ownership, and re-write the proc accordingly - let sResSym = n[resultPos].sym - genResSym(s) - n[resultPos] = newSymNode(s) - swapResult(n, sResSym, n[resultPos]) c.p.resultSym = n[resultPos].sym else: - genResSym(s) + var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId c.idgen, getCurrOwner(c), n.info) + s.typ = t + incl(s.flags, sfUsed) c.p.resultSym = s n.add newSymNode(c.p.resultSym) addParamOrResult(c, c.p.resultSym, owner) +proc copyExcept(n: PNode, i: int): PNode = + result = copyNode(n) + for j in 0..= 1: it[0] else: it - if isPossibleMacroPragma(c, it, key): - # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and - # let the semantic checker deal with it: - var x = newNodeI(nkCall, key.info) - x.add(key) - - if it.kind in nkPragmaCallKinds and it.len > 1: - # pass pragma arguments to the macro too: - for i in 1.. 1: + # pass pragma arguments to the macro too: + for i in 1.. 0 and n[genericParamsPos].kind == nkEmpty: + # we have a list of implicit type parameters: + n[genericParamsPos] = gp + else: + s.typ = newProcType(c, n.info) + if n[pragmasPos].kind != nkEmpty: + pragma(c, s, n[pragmasPos], lambdaPragmas) + s.options = c.config.options + if n[bodyPos].kind != nkEmpty: + if sfImportc in s.flags: + localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) + #if efDetermineType notin flags: + # XXX not good enough; see tnamedparamanonproc.nim + if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags): + pushProcCon(c, s) + addResult(c, n, s.typ[0], skProc) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) + trackProc(c, s, s.ast[bodyPos]) + popProcCon(c) + elif efOperand notin flags: + localError(c.config, n.info, errGenericLambdaNotAllowed) + sideEffectsCheck(c, s) + else: + localError(c.config, n.info, errImplOfXexpected % s.name.s) + closeScope(c) # close scope for parameters + popOwner(c) + result.typ = s.typ + if optOwnedRefs in c.config.globalOptions: + result.typ = makeVarType(c, result.typ, tyOwned) + +proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = var n = n + let original = n[namePos].sym let s = original #copySym(original, false) #incl(s.flags, sfFromGeneric) @@ -1720,7 +1622,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = addParams(c, params, skProc) pushProcCon(c, s) addResult(c, n, n.typ[0], skProc) - s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ[0])) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) trackProc(c, s, s.ast[bodyPos]) popProcCon(c) popOwner(c) @@ -1779,10 +1681,8 @@ proc whereToBindTypeHook(c: PContext; t: PType): PType = proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = let t = s.typ var noError = false - let cond = if op in {attachedDestructor, attachedWasMoved}: + let cond = if op == attachedDestructor: t.len == 2 and t[0] == nil and t[1].kind == tyVar - elif op == attachedTrace: - t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer else: t.len >= 2 and t[0] == nil @@ -1795,24 +1695,19 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: obj = canonType(c, obj) - let ao = getAttachedOp(c.graph, obj, op) - if ao == s: + if obj.attachedOps[op] == s: discard "forward declared destructor" - elif ao.isNil and tfCheckedForDestructor notin obj.flags: - setAttachedOp(c.graph, c.module.position, obj, op, s) + elif obj.attachedOps[op].isNil and tfCheckedForDestructor notin obj.flags: + obj.attachedOps[op] = s else: - prevDestructor(c, ao, obj, n.info) + prevDestructor(c, obj.attachedOps[op], obj, n.info) noError = true if obj.owner.getModule != s.getModule: localError(c.config, n.info, errGenerated, "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") if not noError and sfSystemModule notin s.owner.flags: - if op == attachedTrace: - localError(c.config, n.info, errGenerated, - "signature for '=trace' must be proc[T: object](x: var T; env: pointer)") - else: - localError(c.config, n.info, errGenerated, - "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") + localError(c.config, n.info, errGenerated, + "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") incl(s.flags, sfUsed) incl(s.flags, sfOverriden) @@ -1833,8 +1728,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = elif t.kind == tyGenericInvocation: t = t[0] else: break if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}: - if getAttachedOp(c.graph, t, attachedDeepCopy).isNil: - setAttachedOp(c.graph, c.module.position, t, attachedDeepCopy, s) + if t.attachedOps[attachedDeepCopy].isNil: t.attachedOps[attachedDeepCopy] = s else: localError(c.config, n.info, errGenerated, "cannot bind another 'deepCopy' to: " & typeToString(t)) @@ -1855,8 +1749,6 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if s.magic == mAsgn: return incl(s.flags, sfUsed) incl(s.flags, sfOverriden) - if name == "=": - message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead") let t = s.typ if t.len == 3 and t[0] == nil and t[1].kind == tyVar: var obj = t[1][0] @@ -1876,13 +1768,12 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = obj = canonType(c, obj) #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj) let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink - let ao = getAttachedOp(c.graph, obj, k) - if ao == s: + if obj.attachedOps[k] == s: discard "forward declared op" - elif ao.isNil and tfCheckedForDestructor notin obj.flags: - setAttachedOp(c.graph, c.module.position, obj, k, s) + elif obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags: + obj.attachedOps[k] = s else: - prevDestructor(c, ao, obj, n.info) + prevDestructor(c, obj.attachedOps[k], obj, n.info) if obj.owner.getModule != s.getModule: localError(c.config, n.info, errGenerated, "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") @@ -1892,11 +1783,9 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)") of "=trace": - if s.magic != mTrace: - bindTypeHook(c, s, n, attachedTrace) - of "=wasmoved": - if s.magic != mWasMoved: - bindTypeHook(c, s, n, attachedWasMoved) + bindTypeHook(c, s, n, attachedTrace) + of "=dispose": + bindTypeHook(c, s, n, attachedDispose) else: if sfOverriden in s.flags: localError(c.config, n.info, errGenerated, @@ -1911,6 +1800,11 @@ proc cursorInProc(conf: ConfigRef; n: PNode): bool = if n.info.fileIndex == conf.m.trackPos.fileIndex: result = cursorInProcAux(conf, n) +type + TProcCompilationSteps = enum + stepRegisterSymbol, + stepDetermineType, + proc hasObjParam(s: PSym): bool = var t = s.typ for col in 1.. 0: + if n[genericParamsPos].kind == nkEmpty: + # we have a list of implicit type parameters: + n[genericParamsPos] = gp + # check for semantics again: + # semParamList(c, n[ParamsPos], nil, s) else: s.typ = newProcType(c, n.info) - - if n[genericParamsPos].safeLen == 0: - # if there exist no explicit or implicit generic parameters, then this is - # at most a nullary generic (generic with no type params). Regardless of - # whether it's a nullary generic or non-generic, we restore the original. - # In the case of `nkEmpty` it's non-generic and an empty `nkGeneircParams` - # is a nullary generic. - # - # Remarks about nullary generics vs non-generics: - # The difference between a non-generic and nullary generic is minor in - # most cases but there are subtle and significant differences as well. - # Due to instantiation that generic procs go through, a static echo in the - # body of a nullary generic will not be executed immediately, as it's - # instantiated and not immediately evaluated. - n[genericParamsPos] = n[miscPos][1] - n[miscPos] = c.graph.emptyNode - if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime) if n[patternPos].kind != nkEmpty: - n[patternPos] = semPattern(c, n[patternPos], s) + n[patternPos] = semPattern(c, n[patternPos]) if s.kind == skIterator: s.typ.flags.incl(tfIterator) elif s.kind == skFunc: incl(s.flags, sfNoSideEffect) incl(s.typ.flags, tfNoSideEffect) - - var (proto, comesFromShadowScope) = - if isAnon: (nil, false) - else: searchForProc(c, declarationScope, s) - if proto == nil and sfForward in s.flags and n[bodyPos].kind != nkEmpty: - ## In cases such as a macro generating a proc with a gensymmed name we - ## know `searchForProc` will not find it and sfForward will be set. In - ## such scenarios the sym is shared between forward declaration and we - ## can treat the `s` as the proto. - ## To differentiate between that happening and a macro just returning a - ## forward declaration that has been typed before we check if the body - ## is not empty. This has the sideeffect of allowing multiple forward - ## declarations if they share the same sym. - ## See the "doubly-typed forward decls" case in tmacros_issues.nim + var (proto, comesFromShadowScope) = if isAnon: (nil, false) + else: searchForProc(c, oldScope, s) + if proto == nil and sfForward in s.flags: + #This is a definition that shares its sym with its forward declaration (generated by a macro), + #if the symbol is also gensymmed we won't find it with searchForProc, so we check here proto = s - let hasProto = proto != nil - - # set the default calling conventions - case s.kind - of skIterator: - if s.typ.callConv != ccClosure: - s.typ.callConv = if isAnon: ccClosure else: ccInline - of skMacro, skTemplate: - # we don't bother setting calling conventions for macros and templates - discard - else: - # NB: procs with a forward decl have theirs determined by the forward decl - if not hasProto: - # in this case we're either a forward declaration or we're an impl without - # a forward decl. We set the calling convention or will be set during - # pragma analysis further down. - s.typ.callConv = lastOptionEntry(c).defaultCC - - if not hasProto and sfGenSym notin s.flags: #and not isAnon: - if s.kind in OverloadableSyms: - addInterfaceOverloadableSymAt(c, declarationScope, s) + if proto == nil: + if s.kind == skIterator: + if s.typ.callConv != ccClosure: + s.typ.callConv = if isAnon: ccClosure else: ccInline else: - addInterfaceDeclAt(c, declarationScope, s) - - pragmaCallable(c, s, n, validPragmas) - if not hasProto: - implicitPragmas(c, s, n.info, validPragmas) - - if n[pragmasPos].kind != nkEmpty and sfBorrow notin s.flags: - setEffectsForProcType(c.graph, s.typ, n[pragmasPos], s) - s.typ.flags.incl tfEffectSystemWorkaround - - # To ease macro generation that produce forwarded .async procs we now - # allow a bit redundancy in the pragma declarations. The rule is - # a prototype's pragma list must be a superset of the current pragma - # list. - # XXX This needs more checks eventually, for example that external - # linking names do agree: - if hasProto and ( - # calling convention mismatch - tfExplicitCallConv in s.typ.flags and proto.typ.callConv != s.typ.callConv or - # implementation has additional pragmas - proto.typ.flags < s.typ.flags): - localError(c.config, n[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % - ("'" & proto.name.s & "' from " & c.config$proto.info & - " '" & s.name.s & "' from " & c.config$s.info)) - - styleCheckDef(c, s) - if hasProto: - onDefResolveForward(n[namePos].info, proto) - else: + s.typ.callConv = lastOptionEntry(c).defaultCC + # add it here, so that recursive procs are possible: + if sfGenSym in s.flags: + if s.owner == nil: s.owner = getCurrOwner(c) + elif kind in OverloadableSyms: + if not typeIsDetermined: + addInterfaceOverloadableSymAt(c, oldScope, s) + else: + if not typeIsDetermined: + addInterfaceDeclAt(c, oldScope, s) + if n[pragmasPos].kind != nkEmpty: + pragma(c, s, n[pragmasPos], validPragmas) + else: + implicitPragmas(c, s, n, validPragmas) + styleCheckDef(c.config, s) onDef(n[namePos].info, s) - - if hasProto: + else: + if n[pragmasPos].kind != nkEmpty: + pragma(c, s, n[pragmasPos], validPragmas) + # To ease macro generation that produce forwarded .async procs we now + # allow a bit redundancy in the pragma declarations. The rule is + # a prototype's pragma list must be a superset of the current pragma + # list. + # XXX This needs more checks eventually, for example that external + # linking names do agree: + if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: + localError(c.config, n[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % + ("'" & proto.name.s & "' from " & c.config$proto.info)) + styleCheckDef(c.config, s) + onDefResolveForward(n[namePos].info, proto) if sfForward notin proto.flags and proto.magic == mNone: wrongRedefinition(c, n.info, proto.name.s, proto.info) if not comesFromShadowScope: excl(proto.flags, sfForward) incl(proto.flags, sfWasForwarded) - suggestSym(c.graph, s.info, proto, c.graph.usageSym) + suggestSym(c.config, s.info, proto, c.graph.usageSym) closeScope(c) # close scope with wrong parameter symbols openScope(c) # open scope for old (correct) parameter symbols - if proto.ast[genericParamsPos].isGenericParams: + if proto.ast[genericParamsPos].kind != nkEmpty: addGenericParamListToScope(c, proto.ast[genericParamsPos]) addParams(c, proto.typ.n, proto.kind) proto.info = s.info # more accurate line information @@ -2122,81 +1976,66 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, popOwner(c) pushOwner(c, s) - if not isAnon: - if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) - elif s.name.s[0] in {'.', '('}: - if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}: - localError(c.config, n.info, "the overloaded " & s.name.s & - " operator has to be enabled with {.experimental: \"dotOperators\".}") - elif s.name.s == "()" and callOperator notin c.features: - localError(c.config, n.info, "the overloaded " & s.name.s & - " operator has to be enabled with {.experimental: \"callOperator\".}") - - if sfBorrow in s.flags and c.config.cmd notin cmdDocLike: - result[bodyPos] = c.graph.emptyNode + if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) + if s.name.s[0] in {'.', '('}: + if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}: + localError(c.config, n.info, "the overloaded " & s.name.s & + " operator has to be enabled with {.experimental: \"dotOperators\".}") + elif s.name.s == "()" and callOperator notin c.features: + localError(c.config, n.info, "the overloaded " & s.name.s & + " operator has to be enabled with {.experimental: \"callOperator\".}") if n[bodyPos].kind != nkEmpty and sfError notin s.flags: # for DLL generation we allow sfImportc to have a body, for use in VM - if c.config.ideCmd in {ideSug, ideCon} and s.kind notin {skMacro, skTemplate} and not + if sfBorrow in s.flags: + localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) + let usePseudoGenerics = kind in {skMacro, skTemplate} + # Macros and Templates can have generic parameters, but they are + # only used for overload resolution (there is no instantiation of + # the symbol, so we must process the body now) + if not usePseudoGenerics and c.config.ideCmd in {ideSug, ideCon} and not cursorInProc(c.config, n[bodyPos]): - # speed up nimsuggest + discard "speed up nimsuggest" if s.kind == skMethod: semMethodPrototype(c, s, n) - elif isAnon: - let gp = n[genericParamsPos] - if gp.kind == nkEmpty or (gp.len == 1 and tfRetType in gp[0].typ.flags): - # absolutely no generics (empty) or a single generic return type are - # 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.typ[0])) - trackProc(c, s, s.ast[bodyPos]) - popProcCon(c) - elif efOperand notin flags: - localError(c.config, n.info, errGenericLambdaNotAllowed) else: pushProcCon(c, s) - if n[genericParamsPos].kind == nkEmpty or s.kind in {skMacro, skTemplate}: - # Macros and Templates can have generic parameters, but they are only - # used for overload resolution (there is no instantiation of the symbol) - if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ) + if n[genericParamsPos].kind == nkEmpty or usePseudoGenerics: + if not usePseudoGenerics 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], resultType)) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) # 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]) + if s.kind == skMethod: semMethodPrototype(c, s, n) else: - if (s.typ[0] != nil and s.kind != skIterator): - addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, s, n.info)) + if (s.typ[0] != nil and kind != skIterator) or kind == skMacro: + addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, nil, n.info)) openScope(c) n[bodyPos] = semGenericStmt(c, n[bodyPos]) closeScope(c) if s.magic == mNone: fixupInstantiatedSymbols(c, s) - if s.kind == skMethod: semMethodPrototype(c, s, n) + if s.kind == skMethod: semMethodPrototype(c, s, n) + if sfImportc in s.flags: + # don't ignore the body in case used in VM + # n[bodyPos] = c.graph.emptyNode + discard popProcCon(c) else: - if s.kind == skMethod: semMethodPrototype(c, s, n) - if hasProto: localError(c.config, n.info, errImplOfXexpected % proto.name.s) - if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone: - # this is a forward declaration and we're building the prototype - if s.kind in {skProc, skFunc} and s.typ[0] != nil and s.typ[0].kind == tyAnything: - localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations") + if s.kind in {skProc, skFunc} and s.typ[0] != nil and s.typ[0].kind == tyUntyped: + # `auto` is represented as `tyUntyped` at this point in compilation. + localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations") + if s.kind == skMethod: semMethodPrototype(c, s, n) + if proto != nil: localError(c.config, n.info, errImplOfXexpected % proto.name.s) + if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone: incl(s.flags, sfForward) incl(s.flags, sfWasForwarded) elif sfBorrow in s.flags: semBorrow(c, n, s) sideEffectsCheck(c, s) - closeScope(c) # close scope for parameters # c.currentScope = oldScope popOwner(c) @@ -2207,14 +2046,15 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, result.typ = s.typ if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) - elif isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure: + if isTopLevel(c) and s.kind != skIterator and + s.typ.callConv == ccClosure: localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid") proc determineType(c: PContext, s: PSym) = if s.typ != nil: return #if s.magic != mNone: return #if s.ast.isNil: return - discard semProcAux(c, s.ast, s.kind, {}) + discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType) proc semIterator(c: PContext, n: PNode): PNode = # gensym'ed iterator? @@ -2238,7 +2078,7 @@ proc semIterator(c: PContext, n: PNode): PNode = incl(s.typ.flags, tfCapturesEnv) else: s.typ.callConv = ccInline - if n[bodyPos].kind == nkEmpty and s.magic == mNone and c.inConceptDecl == 0: + if n[bodyPos].kind == nkEmpty and s.magic == mNone: localError(c.config, n.info, errImplOfXexpected % s.name.s) if optOwnedRefs in c.config.globalOptions and result.typ != nil: result.typ = makeVarType(c, result.typ, tyOwned) @@ -2248,9 +2088,7 @@ proc semProc(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skProc, procPragmas) proc semFunc(c: PContext, n: PNode): PNode = - let validPragmas = if n[namePos].kind != nkEmpty: procPragmas - else: lambdaPragmas - result = semProcAux(c, n, skFunc, validPragmas) + result = semProcAux(c, n, skFunc, procPragmas) proc semMethod(c: PContext, n: PNode): PNode = if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "method") @@ -2287,7 +2125,7 @@ proc semConverterDef(c: PContext, n: PNode): PNode = var t = s.typ if t[0] == nil: localError(c.config, n.info, errXNeedsReturnType % "converter") if t.len != 2: localError(c.config, n.info, "a converter takes exactly one argument") - addConverterDef(c, LazySym(sym: s)) + addConverter(c, s) proc semMacroDef(c: PContext, n: PNode): PNode = checkSonsLen(n, bodyPos + 1, c.config) @@ -2312,7 +2150,6 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) = var f = checkModuleName(c.config, it) if f != InvalidFileIdx: addIncludeFileDep(c, f) - onProcessing(c.graph, f, "include", c.module) if containsOrIncl(c.includedFiles, f.int): localError(c.config, n.info, errRecursiveDependencyX % toMsgFilename(c.config, f)) else: @@ -2340,36 +2177,21 @@ proc evalInclude(c: PContext, n: PNode): PNode = else: incMod(c, n, it, result) -proc recursiveSetFlag(n: PNode, flag: TNodeFlag) = - if n != nil: - for i in 0.. 0: # from templates - m = m.lastSon - if m.kind in nkLastBlockStmts or - m.kind in nkCallKinds and m[0].kind == nkSym and - sfNoReturn in m[0].sym.flags: + if n[i].kind in nkLastBlockStmts or + n[i].kind in nkCallKinds and n[i][0].kind == nkSym and + sfNoReturn in n[i][0].sym.flags: for j in i + 1.. 1: - if (n.typ != nil and classifyViewType(n.typ) != noView) or getMagic(n) == mSlice: - # borrow from first parameter: - n = n[1] - else: - break - else: - break - else: - break - if n.kind == nkSym: - # dangerous if contains a pointer deref or if it doesn't belong to us: - result = hasDeref or n.sym.owner != owner - when false: - # store to something that belongs to a `var` parameter is fine: - let s = n.sym - if s.kind == skParam: - # dangerous unless a `var T` parameter: - result = s.typ.kind != tyVar - else: - # dangerous if contains a pointer deref or if it doesn't belong to us: - result = hasDeref or s.owner != owner - else: - # dangerous if it contains a pointer deref - result = hasDeref diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 3b2f2dd9e..14c3b9a11 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -78,7 +78,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; result = newNodeIT(kind, info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: - if a.kind != skModule and (not isField or sfGenSym notin a.flags): + if a.kind != skModule and (not isField or sfGenSym notin s.flags): incl(a.flags, sfUsed) markOwnerModuleAsUsed(c, a) result.add newSymNode(a, info) @@ -86,7 +86,6 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; a = nextOverloadIter(o, c, n) proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = - result = copyNode(n) for i in 0..= 2 and n[1].kind == nkPragma): let pragmaNode = n[1] for i in 0.. 0: @@ -250,14 +235,8 @@ 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-{skTemplate,skMacro}: + of OverloadableSyms: 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) @@ -271,10 +250,9 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = 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: - styleCheckUse(c, n.info, s) + suggestSym(c.config, n.info, s, c.graph.usageSym, false) + if {optStyleHint, optStyleError} * c.config.globalOptions != {}: + styleCheckUse(c.config, n.info, s) proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = result = n @@ -299,7 +277,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, n.info, s) + styleCheckDef(c.c.config, n.info, s) onDef(n.info, s) n[namePos] = newSymNode(s, n[namePos].info) else: @@ -327,26 +305,22 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = # close scope for parameters closeScope(c) -proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start = 0) = +proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) = for i in start.. 0) of nkBind: result = semTemplBody(c, n[0]) @@ -435,7 +407,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, s) + styleCheckDef(c.c.config, s) onDef(n[0].info, s) n[0] = newSymNode(s, n[0].info) n[1] = semTemplBody(c, n[1]) @@ -459,9 +431,17 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkLetSection: semTemplSomeDecl(c, n, skLet) of nkFormalParams: checkMinSonsLen(n, 1, c.c.config) - semTemplSomeDecl(c, n, skParam, 1) n[0] = semTemplBody(c, n[0]) - of nkConstSection: semTemplSomeDecl(c, n, skConst) + semTemplSomeDecl(c, n, skParam, 1) + of nkConstSection: + for i in 0..=", ">", "incl", "excl", "in", "notin", "isnot"] + if sfSystemModule in s.owner.flags and s.name.s in names or + s.owner.name.s == "vm" and s.name.s == "stackTrace": + incl(s.flags, sfCallsite) + + styleCheckDef(c.config, s) + onDef(n[0].info, s) # check parameter list: #s.scope = c.currentScope pushOwner(c, s) openScope(c) - n[namePos] = newSymNode(s) - pragmaCallable(c, s, n, templatePragmas) - implicitPragmas(c, s, n.info, templatePragmas) + n[namePos] = newSymNode(s, n[namePos].info) + if n[pragmasPos].kind != nkEmpty: + pragma(c, s, n[pragmasPos], templatePragmas) - setGenericParamsMisc(c, n) + var gp: PNode + if n[genericParamsPos].kind != nkEmpty: + n[genericParamsPos] = semGenericParamList(c, n[genericParamsPos]) + gp = n[genericParamsPos] + else: + gp = newNodeI(nkGenericParams, n.info) # process parameters: var allUntyped = true if n[paramsPos].kind != nkEmpty: - semParamList(c, n[paramsPos], n[genericParamsPos], s) + semParamList(c, n[paramsPos], gp, s) # a template's parameters are not gensym'ed even if that was originally the # case as we determine whether it's a template parameter in the template # body by the absence of the sfGenSym flag: for i in 1.. 0: + if n[genericParamsPos].kind == nkEmpty: + # we have a list of implicit type parameters: + n[genericParamsPos] = gp else: s.typ = newTypeS(tyProc, c) # XXX why do we need tyTyped as a return type again? s.typ.n = newNodeI(nkFormalParams, n.info) rawAddSon(s.typ, newTypeS(tyTyped, c)) s.typ.n.add newNodeIT(nkType, n.info, s.typ[0]) - if n[genericParamsPos].safeLen == 0: - # restore original generic type params as no explicit or implicit were found - n[genericParamsPos] = n[miscPos][1] - n[miscPos] = c.graph.emptyNode if allUntyped: incl(s.flags, sfAllUntyped) - if n[patternPos].kind != nkEmpty: - n[patternPos] = semPattern(c, n[patternPos], s) - + n[patternPos] = semPattern(c, n[patternPos]) var ctx: TemplCtx ctx.toBind = initIntSet() ctx.toMixin = initIntSet() @@ -675,12 +655,8 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = semIdeForTemplateOrGeneric(c, n[bodyPos], ctx.cursorInBody) closeScope(c) popOwner(c) - - # set the symbol AST after pragmas, at least. This stops pragma that have - # been pushed (implicit) to be explicitly added to the template definition - # and misapplied to the body. see #18113 s.ast = n - + result = n if sfCustomPragma in s.flags: if n[bodyPos].kind != nkEmpty: localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) @@ -690,9 +666,6 @@ 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, warnImplicitTemplateRedefinition, s.name.s) symTabReplace(c.currentScope.symbols, proto, s) if n[patternPos].kind != nkEmpty: c.patterns.add(s) @@ -815,7 +788,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = for i in 0.. MaxSetElements: localError(c.config, n.info, errSetTooBig) else: @@ -195,60 +196,21 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errXExpectsOneTypeParam % "varargs") addSonSkipIntLit(result, errorType(c), c.idgen) -proc semVarOutType(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType = +proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType = if n.len == 1: - result = newOrPrevType(tyVar, prev, c) - result.flags = flags - var base = semTypeNode(c, n[0], nil) - if base.kind == tyTypeDesc and not isSelf(base): - base = base[0] + result = newOrPrevType(kind, prev, c) + var base = semTypeNode(c, n[0], nil).skipTypes({tyTypeDesc}) if base.kind == tyVar: localError(c.config, n.info, "type 'var var' is not allowed") base = base[0] addSonSkipIntLit(result, base, c.idgen) else: - result = newConstraint(c, tyVar) - -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 fitDefaultNode(c: PContext, n: PNode): PType = - let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil - let oldType = n[^1].typ - n[^1] = semConstExpr(c, n[^1], expectedType = expectedType) - n[^1].flags.incl nfSem - if n[^2].kind != nkEmpty: - if expectedType != nil and oldType != expectedType: - n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info) - result = n[^1].typ - else: - result = n[^1].typ - -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) + result = newConstraint(c, kind) proc semDistinct(c: PContext, n: PNode, prev: PType): PType = if n.len == 0: return newConstraint(c, tyDistinct) result = newOrPrevType(tyDistinct, prev, c) - addSonSkipIntLitChecked(c, result, semTypeNode(c, n[0], nil), n[0], c.idgen) + addSonSkipIntLit(result, semTypeNode(c, n[0], nil), c.idgen) if n.len > 1: result.n = n[1] proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = @@ -276,8 +238,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = if not hasUnknownTypes: if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): - typeMismatch(c.config, n.info, rangeT[0], rangeT[1], n) - + localError(c.config, n.info, "type mismatch") elif not isOrdinalType(rangeT[0]) and rangeT[0].kind notin {tyFloat..tyFloat128} or rangeT[0].kind == tyBool: localError(c.config, n.info, "ordinal or float type expected") @@ -291,8 +252,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = else: result.n.add semConstExpr(c, range[i]) - if (result.n[0].kind in {nkFloatLit..nkFloat64Lit} and result.n[0].floatVal.isNaN) or - (result.n[1].kind in {nkFloatLit..nkFloat64Lit} and result.n[1].floatVal.isNaN): + if (result.n[0].kind in {nkFloatLit..nkFloat64Lit} and classify(result.n[0].floatVal) == fcNan) or + (result.n[1].kind in {nkFloatLit..nkFloat64Lit} and classify(result.n[1].floatVal) == fcNan): localError(c.config, n.info, "NaN is not a valid start or end for a range") if weakLeValue(result.n[0], result.n[1]) == impNo: @@ -305,18 +266,17 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = if n.len == 2: if isRange(n[1]): result = semRangeAux(c, n[1], prev) - if not isDefined(c.config, "nimPreviewRangeDefault"): - let n = result.n - if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0: - incl(result.flags, tfRequiresInit) - elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0: - incl(result.flags, tfRequiresInit) - elif n[0].kind in {nkFloatLit..nkFloat64Lit} and - n[0].floatVal > 0.0: - incl(result.flags, tfRequiresInit) - elif n[1].kind in {nkFloatLit..nkFloat64Lit} and - n[1].floatVal < 0.0: - incl(result.flags, tfRequiresInit) + let n = result.n + if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0: + incl(result.flags, tfRequiresInit) + elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0: + incl(result.flags, tfRequiresInit) + elif n[0].kind in {nkFloatLit..nkFloat64Lit} and + n[0].floatVal > 0.0: + incl(result.flags, tfRequiresInit) + elif n[1].kind in {nkFloatLit..nkFloat64Lit} and + n[1].floatVal < 0.0: + incl(result.flags, tfRequiresInit) else: if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<": localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported") @@ -330,28 +290,26 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = proc semArrayIndex(c: PContext, n: PNode): PType = if isRange(n): result = semRangeAux(c, n, nil) - elif n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "..<": - result = errorType(c) else: let e = semExprWithType(c, n, {efDetermineType}) if e.typ.kind == tyFromExpr: result = makeRangeWithStaticExpr(c, e.typ.n) elif e.kind in {nkIntLit..nkUInt64Lit}: if e.intVal < 0: - localError(c.config, n.info, + localError(c.config, n[1].info, "Array length can't be negative, but was " & $e.intVal) result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) elif e.kind == nkSym and e.typ.kind == tyStatic: if e.sym.ast != nil: return semArrayIndex(c, e.sym.ast) - if e.typ.lastSon.kind != tyGenericParam and not isOrdinalType(e.typ.lastSon): + if not isOrdinalType(e.typ.lastSon): let info = if n.safeLen > 1: n[1].info else: n.info - localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) + localError(c.config, info, errOrdinalTypeExpected) 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 % typeToString(e.typ, preferDesc)) + localError(c.config, n[1].info, errOrdinalTypeExpected) # 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 @@ -380,7 +338,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 % typeToString(indxB, preferDesc)) + localError(c.config, n[1].info, errOrdinalTypeExpected) elif enumHasHoles(indxB): localError(c.config, n[1].info, "enum '$1' has holes" % typeToString(indxB.skipTypes({tyRange}))) @@ -395,22 +353,13 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errArrayExpectsTwoTypeParams) result = newOrPrevType(tyError, prev, c) -proc semIterableType(c: PContext, n: PNode, prev: PType): PType = - result = newOrPrevType(tyIterable, prev, c) - if n.len == 2: - let base = semTypeNode(c, n[1], nil) - addSonSkipIntLit(result, base, c.idgen) - else: - localError(c.config, n.info, errXExpectsOneTypeParam % "iterable") - result = newOrPrevType(tyError, prev, c) - proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyOrdinal, prev, c) if n.len == 2: var base = semTypeNode(c, n[1], nil) if base.kind != tyGenericParam: if not isOrdinalType(base): - localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(base, preferDesc)) + localError(c.config, n[1].info, errOrdinalTypeExpected) addSonSkipIntLit(result, base, c.idgen) else: localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal") @@ -483,8 +432,7 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyTuple, prev, c) for it in n: - let t = semTypeNode(c, it, nil) - addSonSkipIntLitChecked(c, result, t, it, c.idgen) + addSonSkipIntLit(result, semTypeNode(c, it, nil), c.idgen) proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType @@ -496,18 +444,13 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = var a = n[i] if (a.kind != nkIdentDefs): illFormedAst(a, c.config) checkMinSonsLen(a, 3, c.config) - var hasDefaultField = a[^1].kind != nkEmpty - if hasDefaultField: - typ = fitDefaultNode(c, a) - elif a[^2].kind != nkEmpty: + if a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) - if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange: - a[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ)) - a[^1].typ = typ - hasDefaultField = true else: localError(c.config, a.info, errTypeExpected) typ = errorType(c) + if a[^1].kind != nkEmpty: + localError(c.config, a[^1].info, errInitHereNotAllowed) for j in 0.. 0: - father.add a + father.add n elif branch != nil: semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields) elif father.kind in {nkElse, nkOfBranch}: @@ -830,20 +762,14 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, var a: PNode if father.kind != nkRecList and n.len >= 4: a = newNodeI(nkRecList, n.info) else: a = newNodeI(nkEmpty, n.info) + if n[^1].kind != nkEmpty: + localError(c.config, n[^1].info, errInitHereNotAllowed) var typ: PType - var hasDefaultField = n[^1].kind != nkEmpty - if hasDefaultField: - typ = fitDefaultNode(c, n) - propagateToOwner(rectype, typ) - elif n[^2].kind == nkEmpty: + if n[^2].kind == nkEmpty: localError(c.config, n.info, errTypeExpected) typ = errorType(c) else: typ = semTypeNode(c, n[^2], nil) - if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange: - n[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ)) - n[^1].typ = typ - hasDefaultField = true propagateToOwner(rectype, typ) var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner else: rectype.sym @@ -853,25 +779,21 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, n[i][1].info else: n[i].info - suggestSym(c.graph, info, f, c.graph.usageSym) + suggestSym(c.config, info, f, c.graph.usageSym) f.typ = typ f.position = pos f.options = c.config.options if fieldOwner != nil and {sfImportc, sfExportc} * fieldOwner.flags != {} and - not hasCaseFields and f.loc.r == "": + not hasCaseFields and f.loc.r == nil: f.loc.r = rope(f.name.s) f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags inc(pos) if containsOrIncl(check, f.name.id): localError(c.config, info, "attempt to redefine: '" & f.name.s & "'") - let fSym = newSymNode(f) - if hasDefaultField: - fSym.sym.ast = n[^1] - fSym.sym.ast.flags.incl nfUseDefaultField - if a.kind == nkEmpty: father.add fSym - else: a.add fSym - styleCheckDef(c, f) + if a.kind == nkEmpty: father.add newSymNode(f) + else: a.add newSymNode(f) + styleCheckDef(c.config, f) onDef(f.info, f) if a.kind != nkEmpty: father.add a of nkSym: @@ -919,7 +841,7 @@ proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, addInheritedFields(c, check, pos, obj[0].skipGenericInvocation) addInheritedFieldsAux(c, check, pos, obj.n) -proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType = +proc semObjectNode(c: PContext, n: PNode, prev: PType; isInheritable: bool): PType = if n.len == 0: return newConstraint(c, tyObject) var check = initIntSet() @@ -955,9 +877,8 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType if n.kind != nkObjectTy: internalError(c.config, n.info, "semObjectNode") result = newOrPrevType(tyObject, prev, c) rawAddSon(result, realBase) - if realBase == nil and tfInheritable in flags: + if realBase == nil and isInheritable: result.flags.incl tfInheritable - if tfAcyclic in flags: result.flags.incl tfAcyclic if result.n.isNil: result.n = newNodeI(nkRecList, n.info) else: @@ -982,8 +903,8 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = let n = if n[0].kind == nkBracket: n[0] else: n checkMinSonsLen(n, 1, c.config) let body = n.lastSon - var t = if prev != nil and prev.kind != tyGenericBody and body.kind == nkObjectTy: - semObjectNode(c, body, nil, prev.flags) + var t = if prev != nil and body.kind == nkObjectTy and tfInheritable in prev.flags: + semObjectNode(c, body, nil, isInheritable=true) else: semTypeNode(c, body, nil) if t.kind == tyTypeDesc and tfUnresolved notin t.flags: @@ -1103,7 +1024,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name else: nil - case paramType.kind + case paramType.kind: of tyAnything: result = addImplicitGeneric(c, newTypeS(tyGenericParam, c), nil, info, genericParams, paramName) @@ -1138,13 +1059,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # disable the bindOnce behavior for the type class result = recurse(paramType.base, true) - of tyTuple: - for i in 0.. 0: - defTyp = nil def = semGenericStmt(c, def) if hasUnresolvedArgs(c, def): def.typ = makeTypeFromExpr(c, def.copyTree) break determineType - def = semExprWithType(c, def, {efDetermineType}, defTyp) + def = semExprWithType(c, def, {efDetermineType}) if def.referencesAnotherParam(getCurrOwner(c)): def.flags.incl nfDefaultRefsParam @@ -1365,7 +1272,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, let param = strTableGet(c.signatures, arg.name) if param != nil: typ = param.typ else: - localError(c.config, a.info, "parameter '$1' requires a type" % arg.name.s) + localError(c.config, a.info, "typeless parameters are obsolete") typ = errorType(c) let lifted = liftParamType(c, kind, genericParams, typ, arg.name.s, arg.info) @@ -1376,14 +1283,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, inc(counter) if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) - if arg.name.s == "_": - arg.flags.incl(sfGenSym) - elif containsOrIncl(check, arg.name.id): + if containsOrIncl(check, arg.name.id): localError(c.config, a[j].info, "attempt to redefine: '" & arg.name.s & "'") result.n.add newSymNode(arg) rawAddSon(result, finalType) addParamOrResult(c, arg, kind) - styleCheckDef(c, a[j].info, arg) + styleCheckDef(c.config, a[j].info, arg) onDef(a[j].info, arg) if {optNimV1Emulation, optNimV12Emulation} * c.config.globalOptions == {}: a[j] = newSymNode(arg) @@ -1413,7 +1318,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, "' is only valid for macros and templates") # 'auto' as a return type does not imply a generic: elif r.kind == tyAnything: - discard + # 'p(): auto' and 'p(): untyped' are equivalent, but the rest of the + # compiler is hardly aware of 'auto': + r = newTypeS(tyUntyped, c) elif r.kind == tyStatic: # type allowed should forbid this type discard @@ -1429,7 +1336,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, r = skipIntLit(r, c.idgen) if kind == skIterator: # see tchainediterators - # in cases like iterator foo(it: iterator): typeof(it) + # in cases like iterator foo(it: iterator): type(it) # we don't need to change the return type to iter[T] result.flags.incl tfIterator # XXX Would be nice if we could get rid of this @@ -1464,8 +1371,6 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = proc semBlockType(c: PContext, n: PNode, prev: PType): PType = inc(c.p.nestedBlockCounter) - let oldBreakInLoop = c.p.breakInLoop - c.p.breakInLoop = false checkSonsLen(n, 2, c.config) openScope(c) if n[0].kind notin {nkEmpty, nkSym}: @@ -1474,7 +1379,6 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType = n[1].typ = result n.typ = result closeScope(c) - c.p.breakInLoop = oldBreakInLoop dec(c.p.nestedBlockCounter) proc semGenericParamInInvocation(c: PContext, n: PNode): PType = @@ -1535,9 +1439,9 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = matches(c, n, copyTree(n), m) if m.state != csMatch: - var err = "cannot instantiate " - err.addTypeHeader(c.config, t) - err.add "\ngot: <$1>\nbut expected: <$2>" % [describeArgs(c, n), describeArgs(c, t.n, 0)] + let err = "cannot instantiate " & typeToString(t) & "\n" & + "got: <" & describeArgs(c, n) & ">\n" & + "but expected: <" & describeArgs(c, t.n, 0) & ">" localError(c.config, n.info, errGenerated, err) return newOrPrevType(tyError, prev, c) @@ -1582,23 +1486,21 @@ proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType = result = newTypeS(tyAlias, c) result.rawAddSon typeExpr result.sym = prev.sym - if prev.kind != tyGenericBody: - assignType(prev, result) + assignType(prev, result) proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) = if prev != nil: let result = newTypeS(tyAlias, c) result.rawAddSon typExpr.typ result.sym = prev.sym - if prev.kind != tyGenericBody: - assignType(prev, result) + assignType(prev, result) proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType = var n = semExprWithType(c, n, {efDetermineType}) if n.typ.kind == tyTypeDesc: result = n.typ.base # fix types constructed by macros/template: - if prev != nil and prev.kind != tyGenericBody and prev.sym != nil: + if prev != nil and prev.sym != nil: if result.sym.isNil: # Behold! you're witnessing enormous power yielded # by macros. Only macros can summon unnamed types @@ -1619,9 +1521,8 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType = result = errorType(c) proc freshType(c: PContext; res, prev: PType): PType {.inline.} = - if prev.isNil or prev.kind == tyGenericBody: + if prev.isNil: result = copyType(res, nextTypeId c.idgen, res.owner) - copyTypeProps(c.graph, c.idgen.module, result, res) else: result = res @@ -1636,12 +1537,6 @@ template modifierTypeKindOfNode(n: PNode): TTypeKind = proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = # if n.len == 0: return newConstraint(c, tyTypeClass) - if isNewStyleConcept(n): - result = newOrPrevType(tyConcept, prev, c) - result.flags.incl tfCheckedForDestructor - result.n = semConceptDeclaration(c, n) - return result - let pragmas = n[1] inherited = n[2] @@ -1752,7 +1647,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode, # we construct a fake 'nkProcDef' for the 'mergePragmas' inside 'implicitPragmas'... s.ast = newTree(nkProcDef, newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info)) - implicitPragmas(c, s, n.info, {wTags, wRaises}) + implicitPragmas(c, s, n, {wTags, wRaises}) when useEffectSystem: setEffectsForProcType(c.graph, result, s.ast[pragmasPos]) closeScope(c) @@ -1769,14 +1664,14 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType = result.rawAddSon(base) result.flags.incl tfHasStatic -proc semTypeOf(c: PContext; n: PNode; prev: PType): PType = +proc semTypeof(c: PContext; n: PNode; prev: PType): PType = openScope(c) let t = semExprWithType(c, n, {efInTypeof}) closeScope(c) fixupTypeOf(c, prev, t) result = t.typ -proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType = +proc semTypeof2(c: PContext; n: PNode; prev: PType): PType = openScope(c) var m = BiggestInt 1 # typeOfIter if n.len == 3: @@ -1798,9 +1693,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = case n.kind of nkEmpty: result = n.typ of nkTypeOfExpr: - # for ``typeof(countup(1,3))``, see ``tests/ttoseq``. + # for ``type(countup(1,3))``, see ``tests/ttoseq``. checkSonsLen(n, 1, c.config) - result = semTypeOf(c, n[0], prev) + result = semTypeof(c, n[0], prev) if result.kind == tyTypeDesc: result.flags.incl tfExplicit of nkPar: if n.len == 1: result = semTypeNode(c, n[0], prev) @@ -1902,9 +1797,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = semAnyRef(c, n, tyRef, prev) elif op.id == ord(wType): checkSonsLen(n, 2, c.config) - result = semTypeOf(c, n[1], prev) + result = semTypeof(c, n[1], prev) elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf: - result = semTypeOf2(c, n, prev) + result = semTypeof2(c, n, prev) elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2: result = semTypeExpr(c, n[1], prev) else: @@ -1928,7 +1823,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mRange: result = semRange(c, n, prev) of mSet: result = semSet(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev) - of mIterableType: result = semIterableType(c, n, prev) of mSeq: result = semContainer(c, n, tySequence, "seq", prev) if optSeqDestructors in c.config.globalOptions: @@ -1942,9 +1836,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mExpr: result = semTypeNode(c, n[0], nil) if result != nil: - let old = result result = copyType(result, nextTypeId c.idgen, getCurrOwner(c)) - copyTypeProps(c.graph, c.idgen.module, result, old) for i in 1.. 0: newSons(result, n.len) @@ -308,7 +306,6 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = result = t.exactReplica else: result = copyType(t, nextTypeId(cl.c.idgen), t.owner) - copyTypeProps(cl.c.graph, cl.c.idgen.module, result, t) #cl.typeMap.topLayer.idTablePut(result, t) if cl.allowMetaTypes: return @@ -330,12 +327,12 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = var body = t[0] if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body") - var header = t + var header: PType = t # search for some instantiation here: if cl.allowMetaTypes: result = PType(idTableGet(cl.localCache, t)) else: - result = searchInstTypes(cl.c.graph, t) + result = searchInstTypes(t) if result != nil and sameFlags(result, t): when defined(reportCacheHits): @@ -354,7 +351,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = if header != t: # search again after first pass: - result = searchInstTypes(cl.c.graph, header) + result = searchInstTypes(header) if result != nil and sameFlags(result, t): when defined(reportCacheHits): echo "Generic instantiation cached ", typeToString(result), " for ", @@ -371,7 +368,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # we need to add the candidate here, before it's fully instantiated for # recursive instantions: if not cl.allowMetaTypes: - cacheTypeInst(cl.c, result) + cacheTypeInst(result) else: idTablePut(cl.localCache, t, result) @@ -381,11 +378,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = cl.typeMap = newTypeMapLayer(cl) for i in 1.. 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 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 - if t.kind in {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses: + if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses: let lookup = cl.typeMap.lookup(t) if lookup != nil: return lookup @@ -564,7 +546,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = n.typ.skipTypes({tyTypeDesc}) # result = n.typ.base else: - if n.typ.kind != tyStatic and n.kind != nkType: + if n.typ.kind != tyStatic: # XXX: In the future, semConstExpr should # return tyStatic values to let anyone make # use of this knowledge. The patching here @@ -660,6 +642,23 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result.size = -1 result.n = replaceObjBranches(cl, result.n) +template typeBound(c, newty, oldty, field, info) = + let opr = newty.attachedOps[field] + if opr != nil and sfFromGeneric notin opr.flags: + # '=' needs to be instantiated for generics when the type is constructed: + #echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty) + newty.attachedOps[field] = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1) + +proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) = + var i = 0 + while i < c.typesWithOps.len: + let (newty, oldty) = c.typesWithOps[i] + typeBound(c, newty, oldty, attachedDestructor, info) + typeBound(c, newty, oldty, attachedSink, info) + typeBound(c, newty, oldty, attachedAsgn, info) + inc i + setLen(c.typesWithOps, 0) + proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo; owner: PSym): TReplTypeVars = initIdTable(result.symMap) diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 7e2a0b660..9a16cc3e0 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -9,21 +9,14 @@ ## Computes hash values for routine (proc, method etc) signatures. -import ast, tables, ropes, md5, modulegraphs, options, msgs, packages, pathutils +import ast, tables, ropes, md5, modulegraphs from hashes import Hash import types -import std/os - -when defined(nimPreviewSlimSystem): - import std/assertions - - 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, cast[cstring](unsafeAddr ch), 1) - +proc `&=`(c: var MD5Context, ch: char) = md5Update(c, unsafeAddr ch, 1) +proc `&=`(c: var MD5Context, r: Rope) = + for l in leaves(r): md5Update(c, l, l.len) proc `&=`(c: var MD5Context, i: BiggestInt) = md5Update(c, cast[cstring](unsafeAddr i), sizeof(i)) proc `&=`(c: var MD5Context, f: BiggestFloat) = @@ -44,7 +37,8 @@ type CoDistinct CoHashTypeInsideNode -proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: ConfigRef) +proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) + proc hashSym(c: var MD5Context, s: PSym) = if sfAnon in s.flags or s.kind == skGenericParam: c &= ":anon" @@ -55,21 +49,20 @@ proc hashSym(c: var MD5Context, s: PSym) = c &= "." it = it.owner -proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) = +proc hashTypeSym(c: var MD5Context, s: PSym) = if sfAnon in s.flags or s.kind == skGenericParam: c &= ":anon" else: var it = s - c &= customPath(conf.toFullPath(s.info)) while it != nil: if sfFromGeneric in it.flags and it.kind in routineKinds and it.typ != nil: - hashType c, it.typ, {CoProc}, conf + hashType c, it.typ, {CoProc} c &= it.name.s c &= "." it = it.owner -proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: ConfigRef) = +proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]) = if n == nil: c &= "\255" return @@ -84,7 +77,7 @@ proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: Confi of nkSym: hashSym(c, n.sym) if CoHashTypeInsideNode in flags and n.sym.typ != nil: - hashType(c, n.sym.typ, flags, conf) + hashType(c, n.sym.typ, flags) of nkCharLit..nkUInt64Lit: let v = n.intVal lowlevel v @@ -94,9 +87,9 @@ proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: Confi of nkStrLit..nkTripleStrLit: c &= n.strVal else: - for i in 0.. 0: - let oldFlags = symWithFlags.flags - # Hack to prevent endless recursion - # xxx instead, use a hash table to indicate we've already visited a type, which - # would also be more efficient. - symWithFlags.flags.excl {sfAnon, sfGenSym} - hashTree(c, t.n, flags + {CoHashTypeInsideNode}, conf) - symWithFlags.flags = oldFlags + let oldFlags = t.sym.flags + # Mild hack to prevent endless recursion. + t.sym.flags.excl {sfAnon, sfGenSym} + hashTree(c, t.n, flags + {CoHashTypeInsideNode}) + t.sym.flags = oldFlags else: # The object has no fields: we _must_ add something here in order to # make the hash different from the one we produce by hashing only the @@ -184,15 +168,14 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi else: c &= t.id if t.len > 0 and t[0] != nil: - hashType c, t[0], flags, conf + hashType c, t[0], flags of tyRef, tyPtr, tyGenericBody, tyVar: c &= char(t.kind) - if t.sons.len > 0: - c.hashType t.lastSon, flags, conf + c.hashType t.lastSon, flags if tfVarIsPtr in t.flags: c &= ".varisptr" of tyFromExpr: c &= char(t.kind) - c.hashTree(t.n, {}, conf) + c.hashTree(t.n, {}) of tyTuple: c &= char(t.kind) if t.n != nil and CoType notin flags: @@ -201,19 +184,19 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi assert(t.n[i].kind == nkSym) c &= t.n[i].sym.name.s c &= ':' - c.hashType(t[i], flags+{CoIgnoreRange}, conf) + c.hashType(t[i], flags+{CoIgnoreRange}) c &= ',' else: - for i in 0.. 1) order by hash; -proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): SigHash = +proc hashType*(t: PType; flags: set[ConsiderFlag] = {CoType}): SigHash = var c: MD5Context md5Init c - hashType c, t, flags+{CoOwnerSig}, conf + hashType c, t, flags+{CoOwnerSig} md5Final c, result.MD5Digest when defined(debugSigHashes): db.exec(sql"INSERT OR IGNORE INTO sighashes(type, hash) VALUES (?, ?)", typeToString(t), $result) -proc hashProc*(s: PSym; conf: ConfigRef): SigHash = +proc hashProc*(s: PSym): SigHash = var c: MD5Context md5Init c - hashType c, s.typ, {CoProc}, conf + hashType c, s.typ, {CoProc} var m = s while m.kind != skModule: m = m.owner @@ -317,9 +300,9 @@ proc hashOwner*(s: PSym): SigHash = md5Final c, result.MD5Digest -proc sigHash*(s: PSym; conf: ConfigRef): SigHash = +proc sigHash*(s: PSym): SigHash = if s.kind in routineKinds and s.typ != nil: - result = hashProc(s, conf) + result = hashProc(s) else: result = hashNonProc(s) @@ -336,7 +319,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 in {nkIdentDefs, nkConstDef}: + if s.ast != nil and s.ast.kind == nkIdentDefs: hashBodyTree(graph, c, s.ast[^1]) else: hashBodyTree(graph, c, s.ast) @@ -380,7 +363,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = var c: MD5Context md5Init(c) - c.hashType(sym.typ, {CoProc}, graph.config) + c.hashType(sym.typ, {CoProc}) c &= char(sym.kind) c.md5Final(result.MD5Digest) graph.symBodyHashes[sym.id] = result # protect from recursion in the body @@ -388,17 +371,17 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = if sym.ast != nil: md5Init(c) c.md5Update(cast[cstring](result.addr), sizeof(result)) - hashBodyTree(graph, c, getBody(graph, sym)) + hashBodyTree(graph, c, sym.ast[bodyPos]) c.md5Final(result.MD5Digest) graph.symBodyHashes[sym.id] = result proc idOrSig*(s: PSym, currentModule: string, - sigCollisions: var CountTable[SigHash]; conf: ConfigRef): Rope = + sigCollisions: var CountTable[SigHash]): Rope = if s.kind in routineKinds and s.typ != nil: # signatures for exported routines are reliable enough to - # produce a unique name and this means produced C++ is more stable regarding + # produce a unique name and this means produced C++ is more stable wrt # Nim changes: - let sig = hashProc(s, conf) + let sig = hashProc(s) result = rope($sig) #let m = if s.typ.callConv != ccInline: findPendingModule(m, s) else: m let counter = sigCollisions.getOrDefault(sig) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 87a4fdf1c..744a24935 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -13,10 +13,7 @@ import intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, magicsys, idents, lexer, options, parampatterns, strutils, trees, - linter, lineinfos, lowerings, modulegraphs, concepts - -when defined(nimPreviewSlimSystem): - import std/assertions + linter, lineinfos, lowerings, modulegraphs type MismatchKind* = enum @@ -86,10 +83,24 @@ type trDontBind trNoCovariance trBindGenericParam # bind tyGenericParam even with trDontBind - trIsOutParam TTypeRelFlags* = set[TTypeRelFlag] + TTypeRelation* = enum # order is important! + isNone, isConvertible, + isIntConv, + isSubtype, + isSubrange, # subrange of the wanted type; no type conversion + # but apart from that counts as ``isSubtype`` + isBothMetaConvertible # generic proc parameter was matched against + # generic type, e.g., map(mySeq, x=>x+1), + # maybe recoverable by rerun if the parameter is + # the proc's return value + isInferred, # generic proc was matched against a concrete type + isInferredConvertible, # same as above, but requiring proc CC conversion + isGeneric, + isFromIntLit, # conversion *from* int literal; proven safe + isEqual const isNilConversion = isConvertible # maybe 'isIntConv' fits better? @@ -249,7 +260,7 @@ proc sumGeneric(t: PType): int = of tyGenericParam, tyUntyped, tyTyped: break of tyAlias, tySink: t = t.lastSon of tyBool, tyChar, tyEnum, tyObject, tyPointer, - tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128, + tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64, tyCompositeTypeClass: return isvar else: @@ -322,32 +333,26 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = else: result = arg.typ.typeToString(prefer) -template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName) = - var arg = n[i] - if n[i].kind == nkExprEqExpr: - result.add renderTree(n[i][0]) - result.add ": " - if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}: - # XXX we really need to 'tryExpr' here! - arg = c.semOperand(c, n[i][1]) - n[i].typ = arg.typ - n[i][1] = arg - else: - if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse, - nkOfBranch, nkElifBranch, - nkExceptBranch}: - arg = c.semOperand(c, n[i]) - n[i] = arg - if arg.typ != nil and arg.typ.kind == tyError: return - result.add argTypeToString(arg, prefer) - -proc describeArg*(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName): string = - describeArgImpl(c, n, i, startIdx, prefer) - proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string = result = "" for i in startIdx..= firstOrd(nil, f) and getInt(ab.n) <= lastOrd(nil, f): @@ -397,27 +400,23 @@ proc handleRange(c: PContext, f, a: PType, min, max: TTypeKind): TTypeRelation = # integer literal in the proper range; we want ``i16 + 4`` to stay an # ``int16`` operation so we declare the ``4`` pseudo-equal to int16 result = isFromIntLit - elif a.kind == tyInt and nf == c.config.targetSizeSignedToKind: + elif f.kind == tyInt and k in {tyInt8..tyInt32}: result = isIntConv - elif a.kind == tyUInt and nf == c.config.targetSizeUnsignedToKind: - result = isIntConv - elif f.kind == tyInt and na in {tyInt8 .. pred(c.config.targetSizeSignedToKind)}: - result = isIntConv - elif f.kind == tyUInt and na in {tyUInt8 .. pred(c.config.targetSizeUnsignedToKind)}: + elif f.kind == tyUInt and k in {tyUInt8..tyUInt32}: result = isIntConv elif k >= min and k <= max: result = isConvertible elif a.kind == tyRange and # Make sure the conversion happens between types w/ same signedness (f.kind in {tyInt..tyInt64} and a[0].kind in {tyInt..tyInt64} or - f.kind in {tyUInt8..tyUInt32} and a[0].kind in {tyUInt8..tyUInt32}) and + f.kind in {tyUInt8..tyUInt32} and a[0].kind in {tyUInt8..tyInt32}) and a.n[0].intVal >= firstOrd(nil, f) and a.n[1].intVal <= lastOrd(nil, f): # passing 'nil' to firstOrd/lastOrd here as type checking rules should # not depend on the target integer size configurations! result = isConvertible else: result = isNone -proc isConvertibleToRange(c: PContext, f, a: PType): bool = +proc isConvertibleToRange(f, a: PType): bool = if f.kind in {tyInt..tyInt64, tyUInt..tyUInt64} and a.kind in {tyInt..tyInt64, tyUInt..tyUInt64}: case f.kind @@ -425,14 +424,13 @@ proc isConvertibleToRange(c: PContext, f, a: PType): bool = of tyInt16: result = isIntLit(a) or a.kind in {tyInt8, tyInt16} of tyInt32: result = isIntLit(a) or a.kind in {tyInt8, tyInt16, tyInt32} # This is wrong, but seems like there's a lot of code that relies on it :( - of tyInt, tyUInt: result = true - # of tyInt: result = isIntLit(a) or a.kind in {tyInt8 .. c.config.targetSizeSignedToKind} + of tyInt, tyUInt, tyUInt64: result = true of tyInt64: result = isIntLit(a) or a.kind in {tyInt8, tyInt16, tyInt32, tyInt, tyInt64} of tyUInt8: result = isIntLit(a) or a.kind in {tyUInt8} of tyUInt16: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16} of tyUInt32: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32} - # of tyUInt: result = isIntLit(a) or a.kind in {tyUInt8 .. c.config.targetSizeUnsignedToKind} - of tyUInt64: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt64} + #of tyUInt: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt} + #of tyUInt64: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt, tyUInt64} else: result = false elif f.kind in {tyFloat..tyFloat128}: # `isIntLit` is correct and should be used above as well, see PR: @@ -558,17 +556,23 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = proc allowsNil(f: PType): TTypeRelation {.inline.} = result = if tfNotNil notin f.flags: isSubtype else: isNone +proc allowsNilDeprecated(c: TCandidate, f: PType): TTypeRelation = + if optNilSeqs in c.c.config.options: + result = allowsNil(f) + else: + result = isNone + proc inconsistentVarTypes(f, a: PType): bool {.inline.} = - result = (f.kind != a.kind and - (f.kind in {tyVar, tyLent, tySink} or a.kind in {tyVar, tyLent, tySink})) or - isOutParam(f) != isOutParam(a) + result = f.kind != a.kind and + (f.kind in {tyVar, tyLent, tySink} or a.kind in {tyVar, tyLent, tySink}) 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: @@ -612,7 +616,8 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = # if f is metatype. result = typeRel(c, f, a) - if result <= isSubrange or inconsistentVarTypes(f, a): + # v--- is this correct? + if result <= isIntConv or inconsistentVarTypes(f, a): result = isNone #if result == isEqual: @@ -642,8 +647,22 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = elif a[0] != nil: return isNone - result = getProcConvMismatch(c.c.config, f, a, result)[1] - + if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: + return isNone + elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and + optThreadAnalysis in c.c.config.globalOptions: + # noSideEffect implies ``tfThread``! + return isNone + elif f.flags * {tfIterator} != a.flags * {tfIterator}: + return isNone + elif f.callConv != a.callConv: + # valid to pass a 'nimcall' thingie to 'closure': + if f.callConv == ccClosure and a.callConv == ccNimCall: + result = if result == isInferred: isInferredConvertible + elif result == isBothMetaConvertible: isBothMetaConvertible + else: isConvertible + else: + return isNone when useEffectSystem: if compatibleEffects(f, a) != efCompat: return isNone when defined(drnim): @@ -998,7 +1017,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, when declared(deallocatedRefId): let corrupt = deallocatedRefId(cast[pointer](f)) if corrupt != 0: - c.c.config.quitOrRaise "it's corrupt " & $corrupt + quit "it's corrupt " & $corrupt if f.kind == tyUntyped: if aOrig != nil: put(c, f, aOrig) @@ -1106,8 +1125,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return if x >= isGeneric: isGeneric else: x return isNone - of tyIterable: - if f.kind != tyIterable: return isNone of tyNot: case f.kind of tyNot: @@ -1161,34 +1178,25 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let f = skipTypes(f, {tyRange}) if f.kind == a.kind and (f.kind != tyEnum or sameEnumTypes(f, a)): result = isIntConv - elif isConvertibleToRange(c.c, f, a): + elif isConvertibleToRange(f, a): result = isConvertible # a convertible to f - of tyInt: result = handleRange(c.c, f, a, tyInt8, c.c.config.targetSizeSignedToKind) - of tyInt8: result = handleRange(c.c, f, a, tyInt8, tyInt8) - of tyInt16: result = handleRange(c.c, f, a, tyInt8, tyInt16) - of tyInt32: result = handleRange(c.c, f, a, tyInt8, tyInt32) - of tyInt64: result = handleRange(c.c, f, a, tyInt, tyInt64) - of tyUInt: result = handleRange(c.c, f, a, tyUInt8, c.c.config.targetSizeUnsignedToKind) - of tyUInt8: result = handleRange(c.c, f, a, tyUInt8, tyUInt8) - of tyUInt16: result = handleRange(c.c, f, a, tyUInt8, tyUInt16) - of tyUInt32: result = handleRange(c.c, f, a, tyUInt8, tyUInt32) - of tyUInt64: result = handleRange(c.c, f, a, tyUInt, tyUInt64) + of tyInt: result = handleRange(f, a, tyInt8, tyInt32) + of tyInt8: result = handleRange(f, a, tyInt8, tyInt8) + of tyInt16: result = handleRange(f, a, tyInt8, tyInt16) + of tyInt32: result = handleRange(f, a, tyInt8, tyInt32) + of tyInt64: result = handleRange(f, a, tyInt, tyInt64) + of tyUInt: result = handleRange(f, a, tyUInt8, tyUInt32) + of tyUInt8: result = handleRange(f, a, tyUInt8, tyUInt8) + of tyUInt16: result = handleRange(f, a, tyUInt8, tyUInt16) + of tyUInt32: result = handleRange(f, a, tyUInt8, tyUInt32) + of tyUInt64: result = handleRange(f, a, tyUInt, tyUInt64) of tyFloat: result = handleFloatRange(f, a) of tyFloat32: result = handleFloatRange(f, a) of tyFloat64: result = handleFloatRange(f, a) of tyFloat128: result = handleFloatRange(f, a) - of tyVar: - let flags = if isOutParam(f): flags + {trIsOutParam} else: flags - if aOrig.kind == f.kind and (isOutParam(aOrig) == isOutParam(f)): - result = typeRel(c, f.base, aOrig.base, flags) - else: - result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) - subtypeCheck() - of tyLent: - if aOrig.kind == f.kind: - result = typeRel(c, f.base, aOrig.base, flags) - else: - result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) + of tyVar, tyLent: + if aOrig.kind == f.kind: result = typeRel(c, f.base, aOrig.base, flags) + else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) subtypeCheck() of tyArray: case a.kind @@ -1292,7 +1300,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isNone elif tfNotNil in f.flags and tfNotNil notin a.flags: result = isNilConversion - of tyNil: result = isNone + of tyNil: result = allowsNilDeprecated(c, f) else: discard of tyOrdinal: if isOrdinalType(a, allowEnumWithHoles = optNimV1Emulation in c.c.config.globalOptions): @@ -1317,7 +1325,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if sameObjectTypes(f, a): result = isEqual # elif tfHasMeta in f.flags: result = recordRel(c, f, a) - elif trIsOutParam notin flags: + else: var depth = isObjectSubtype(c, a, f, nil) if depth > 0: inc(c.inheritancePenalty, depth) @@ -1328,6 +1336,8 @@ 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: @@ -1335,13 +1345,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isSubtype else: result = typeRel(c, f[0], a[0], flags) - if result < isGeneric: - if result <= isConvertible: - result = isNone - elif tfIsConstructor notin a.flags: - # set constructors are a bit special... - result = isNone - + if result <= isConvertible: + result = isNone # BUGFIX! of tyPtr, tyRef: skipOwned(a) if a.kind == f.kind: @@ -1382,7 +1387,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # 'pointer' is NOT compatible to regionized pointers # so 'dealloc(regionPtr)' fails: if a.len == 1: result = isConvertible - of tyCstring: result = isConvertible + of tyCString: result = isConvertible else: discard of tyString: case a.kind @@ -1391,12 +1396,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isNilConversion else: result = isEqual - of tyNil: result = isNone + of tyNil: result = allowsNilDeprecated(c, f) else: discard - of tyCstring: + of tyCString: # conversion from string to cstring is automatic: case a.kind - of tyCstring: + of tyCString: if tfNotNil in f.flags and tfNotNil notin a.flags: result = isNilConversion else: @@ -1404,18 +1409,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyNil: result = f.allowsNil of tyString: result = isConvertible of tyPtr: - if isDefined(c.c.config, "nimPreviewCstringConversion"): - result = isNone - else: - if a.len == 1: - let pointsTo = a[0].skipTypes(abstractInst) - if pointsTo.kind == tyChar: result = isConvertible - elif pointsTo.kind == tyUncheckedArray and pointsTo[0].kind == tyChar: - result = isConvertible - elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo[0]) == 0 and - skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} and - pointsTo[1].kind == tyChar: - result = isConvertible + # ptr[Tag, char] is not convertible to 'cstring' for now: + if a.len == 1: + let pointsTo = a[0].skipTypes(abstractInst) + if pointsTo.kind == tyChar: result = isConvertible + elif pointsTo.kind == tyUncheckedArray and pointsTo[0].kind == tyChar: + result = isConvertible + elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo[0]) == 0 and + skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} and + pointsTo[1].kind == tyChar: + result = isConvertible else: discard of tyEmpty, tyVoid: @@ -1424,15 +1427,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyAlias, tySink: result = typeRel(c, lastSon(f), a, flags) - of tyIterable: - if a.kind == tyIterable: - if f.len == 1: - result = typeRel(c, lastSon(f), lastSon(a), flags) - else: - # f.len = 3, for some reason - result = isGeneric - else: - result = isNone of tyGenericInst: var prev = PType(idTableGet(c.bindings, f)) let origF = f @@ -1487,7 +1481,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, elif aAsObject.kind == fKind: aAsObject = aAsObject.base - if aAsObject.kind == tyObject and trIsOutParam notin flags: + if aAsObject.kind == tyObject: let baseType = aAsObject.base if baseType != nil: c.inheritancePenalty += 1 @@ -1511,8 +1505,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyGenericInvocation: var x = a.skipGenericAlias - let concpt = f[0].skipTypes({tyGenericBody}) - var preventHack = concpt.kind == tyConcept + + var preventHack = false if x.kind == tyOwned and f[0].kind != tyOwned: preventHack = true x = x.lastSon @@ -1539,9 +1533,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # Workaround for regression #4589 if f[i].kind != tyTypeDesc: return result = isGeneric - elif x.kind == tyGenericInst and concpt.kind == tyConcept: - result = if concepts.conceptMatch(c.c, concpt, x, c.bindings, f): isGeneric - else: isNone else: let genericBody = f[0] var askip = skippedNone @@ -1610,9 +1601,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, c.inheritancePenalty = 0 let x = typeRel(c, branch, aOrig, flags) maxInheritance = max(maxInheritance, c.inheritancePenalty) + # 'or' implies maximum matching result: if x > result: result = x - if result >= isIntConv: + if result >= isSubtype: if result > isGeneric: result = isGeneric bindingRet result else: @@ -1659,22 +1651,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, bindConcreteTypeToUserTypeClass(matched, a) if doBind: put(c, f, matched) result = isGeneric - elif a.len > 0 and a.lastSon == f: - # Needed for checking `Y` == `Addable` in the following - #[ - type - Addable = concept a, type A - a + a is A - MyType[T: Addable; Y: static T] = object - ]# - result = isGeneric else: result = isNone - of tyConcept: - result = if concepts.conceptMatch(c.c, f, a, c.bindings, nil): isGeneric - else: isNone - of tyCompositeTypeClass: considerPreviousT: let roota = a.skipGenericAlias @@ -1713,8 +1692,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, var aa = a while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0: aa = lastSon(aa) - if aa.kind in {tyGenericParam} + tyTypeClasses: - # If the constraint is a genericParam or typeClass this isGeneric + if aa.kind == tyGenericParam: return isGeneric result = typeRel(c, f.base, aa, flags) if result > isGeneric: result = isGeneric @@ -1774,22 +1752,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let prev = PType(idTableGet(c.bindings, f)) if prev == nil: if aOrig.kind == tyStatic: - if f.base.kind notin {tyNone, tyGenericParam}: + if f.base.kind != tyNone: result = typeRel(c, f.base, a, flags) if result != isNone and f.n != nil: if not exprStructuralEquivalent(f.n, aOrig.n): result = isNone - elif f.base.kind == tyGenericParam: - # Handling things like `type A[T; Y: static T] = object` - if f.base.len > 0: # There is a constraint, handle it - result = typeRel(c, f.base.lastSon, a, flags) - else: - # No constraint - if tfGenericTypeParam in f.flags: - result = isGeneric - else: - # for things like `proc fun[T](a: static[T])` - result = typeRel(c, f.base, a, flags) else: result = isGeneric if result != isNone: put(c, f, aOrig) @@ -1840,9 +1807,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, elif f.base.kind == tyNone: result = isGeneric else: - let r = typeRel(c, f.base, a.base, flags) - if r >= isIntConv: - result = r + result = typeRel(c, f.base, a.base, flags) if result != isNone: put(c, f, a) @@ -1850,9 +1815,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if tfUnresolved in f.flags: result = typeRel(c, prev.base, a, flags) elif a.kind == tyTypeDesc: - let r = typeRel(c, prev.base, a.base, flags) - if r >= isIntConv: - result = r + result = typeRel(c, prev.base, a.base, flags) else: result = isNone @@ -1924,24 +1887,11 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, else: result.typ = errorType(c) else: - result.typ = f.skipTypes({tySink, tyVar}) + result.typ = f.skipTypes({tySink}) if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv") result.add c.graph.emptyNode result.add arg -proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} = - let aa = isAssignable(nil, n) - case aa - of arLValue, arLocalLValue, arStrange: - result = true - of arDiscriminant: - result = c.inUncheckedAssignSection > 0 - of arAddressableConst: - let sym = getRoot(n) - result = strictDefs in c.features and sym != nil and sym.kind == skLet and isOutParam - else: - result = false - proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, arg: PNode): PNode = result = nil @@ -1958,7 +1908,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, let constraint = c.converters[i].typ.n[1].sym.constraint if not constraint.isNil and not matchNodeKinds(constraint, arg): continue - if src.kind in {tyVar, tyLent} and not isLValue(c, arg): + if src.kind in {tyVar, tyLent} and not arg.isLValue: continue let destIsGeneric = containsGenericType(dest) @@ -1980,7 +1930,6 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c) elif src.kind in {tyVar}: # Analyse the converter return type - arg.sym.flags.incl sfAddrTaken param = newNodeIT(nkHiddenAddr, arg.info, s.typ[1]) param.add copyTree(arg) else: @@ -2047,6 +1996,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, arg = argSemantized a = a c = m.c + if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to @@ -2066,7 +2016,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, a.n == nil and tfGenericTypeParam notin a.flags: return newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg)) - elif arg.kind != nkEmpty: + else: var evaluated = c.semTryConstExpr(c, arg) if evaluated != nil: # Don't build the type in-place because `evaluated` and `arg` may point @@ -2130,15 +2080,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, case r of isConvertible: - if f.skipTypes({tyRange}).kind in {tyInt, tyUInt}: - inc(m.convMatches) inc(m.convMatches) result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isIntConv: # I'm too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: - if f.skipTypes({tyRange}).kind notin {tyInt, tyUInt}: - inc(m.intConvMatches) inc(m.intConvMatches) result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isSubtype: @@ -2158,11 +2104,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, result = c.semInferredLambda(c, m.bindings, arg) elif arg.kind != nkSym: return nil - elif arg.sym.kind in {skMacro, skTemplate}: - return nil else: - if arg.sym.ast == nil: - return nil let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) result = newSymNode(inferred, arg.info) if r == isInferredConvertible: @@ -2193,8 +2135,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, of isEqual: inc(m.exactMatches) result = arg - let ff = skipTypes(f, abstractVar-{tyTypeDesc}) - if ff.kind == tyTuple or + if skipTypes(f, abstractVar-{tyTypeDesc}).kind == tyTuple or (arg.typ != nil and skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple): result = implicitConv(nkHiddenSubConv, f, arg, m, c) of isNone: @@ -2205,8 +2146,6 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, return arg elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList: # lift do blocks without params to lambdas - # now deprecated - message(c.config, argOrig.info, warnStmtListLambda) let p = c.graph let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, body = argOrig, params = nkFormalParams.newTree(p.emptyNode), name = p.emptyNode, pattern = p.emptyNode, @@ -2270,7 +2209,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, var best = -1 for i in 0.. 1: m.callee.n[1].sym else: nil # current routine parameter container: PNode = nil # constructed container - let firstArgBlock = findFirstArgBlock(m, n) + while a < n.len: c.openShadowScope @@ -2498,7 +2420,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int n[a] = prepareOperand(c, n[a]) if skipTypes(n[a].typ, abstractVar-{tyTypeDesc}).kind==tyString: m.call.add implicitConv(nkHiddenStdConv, - getSysType(c.graph, n[a].info, tyCstring), + getSysType(c.graph, n[a].info, tyCString), copyTree(n[a]), m, c) else: m.call.add copyTree(n[a]) @@ -2525,8 +2447,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int if m.callee.n[f].kind != nkSym: internalError(c.config, n[a].info, "matches") noMatch() - if flexibleOptionalParams in c.features and a >= firstArgBlock: - f = max(f, m.callee.n.len - (n.len - a)) formal = m.callee.n[f].sym m.firstMismatch.kind = kTypeMismatch if containsOrIncl(marker, formal.position) and container.isNil: @@ -2580,7 +2500,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int noMatch() checkConstraint(n[a]) - if m.state == csMatch and not (m.calleeSym != nil and m.calleeSym.kind in {skTemplate, skMacro}): + if m.state == csMatch and not(m.calleeSym != nil and m.calleeSym.kind in {skTemplate, skMacro}): c.mergeShadowScope else: c.closeShadowScope @@ -2672,9 +2592,11 @@ proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool = # pattern templates do not allow for conversions except from int literal res != nil and m.convMatches == 0 and m.intConvMatches in [0, 256] +when not defined(nimHasSinkInference): + {.pragma: nosinks.} proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; - op: TTypeAttachedOp; col: int): PSym = + op: TTypeAttachedOp; col: int): PSym {.nosinks.} = var m = newCandidate(c, dc.typ) if col >= dc.typ.len: localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") diff --git a/compiler/sinkparameter_inference.nim b/compiler/sinkparameter_inference.nim index aa53e7429..fa9f2b445 100644 --- a/compiler/sinkparameter_inference.nim +++ b/compiler/sinkparameter_inference.nim @@ -19,6 +19,7 @@ proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNod var local = p # sink parameter? passToSink(local) ]# + if optSinkInference notin config.options: return case arg.kind of nkSym: if arg.sym.kind == skParam and diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 987bcde99..b50777c9e 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -40,15 +40,15 @@ proc inc(arg: var OffsetAccum; value: int) = else: arg.offset += value -proc alignmentMax(a, b: int): int = +proc alignmentMax(a,b: int): int = if unlikely(a == szIllegalRecursion or b == szIllegalRecursion): raiseIllegalTypeRecursion() if a == szUnknownSize or b == szUnknownSize: szUnknownSize else: - max(a, b) + max(a,b) proc align(arg: var OffsetAccum; value: int) = - if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion() + if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion() if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize: arg.maxAlign = szUnknownSize arg.offset = szUnknownSize @@ -112,7 +112,7 @@ proc setOffsetsToUnknown(n: PNode) = for i in 0.. 0, "No lines have been added yet" - var segment = Segment(original: original, generated: generated, name: -1) - if name != "": - # Make name be index into names list - segment.name = info.names.find(name) - if segment.name == -1: - segment.name = info.names.len - info.names &= name + SourceMapGenerator = ref object + file: string + sourceRoot: string + skipValidation: bool + sources: seq[string] + names: seq[string] + mappings: seq[Mapping] - assert info.mappings[^1].inSource, "Current line isn't in Nim source" - info.mappings[^1].segments &= segment + Mapping* = ref object + source*: string + original*: tuple[line: int, column: int] + generated*: tuple[line: int, column: int] + name*: string + noSource*: bool + noName*: bool -func newLine(info: var SourceInfo) {.raises: [].} = - ## Add new mapping which doesn't appear in the Nim source - info.mappings &= Mapping(inSource: false) -func newLine(info: var SourceInfo, file: string, line: int) {.raises: [].} = - ## Starts a new line in the mappings. Call addSegment after this to add - ## segments into the line - var mapping = Mapping(inSource: true, line: line) - # Set file to file position. Add in if needed - mapping.file = info.files.find(file) - if mapping.file == -1: - mapping.file = info.files.len - info.files &= file - info.mappings &= mapping +proc child*(s: string): Child = + Child(kind: cString, s: s) + + +proc child*(node: SourceNode): Child = + Child(kind: cSourceNode, node: node) + + +proc newSourceNode(line: int, column: int, path: string, node: SourceNode, name: string = ""): SourceNode = + SourceNode(line: line, column: column, source: path, name: name, children: @[child(node)]) + + +proc newSourceNode(line: int, column: int, path: string, s: string, name: string = ""): SourceNode = + SourceNode(line: line, column: column, source: path, name: name, children: @[child(s)]) + + +proc newSourceNode(line: int, column: int, path: string, children: seq[Child], name: string = ""): SourceNode = + SourceNode(line: line, column: column, source: path, name: name, children: children) + + + + +# debugging + + +proc text*(sourceNode: SourceNode, depth: int): string = + let empty = " " + result = &"{repeat(empty, depth)}SourceNode({sourceNode.source}:{sourceNode.line}:{sourceNode.column}):\n" + for child in sourceNode.children: + if child.kind == cString: + result.add(&"{repeat(empty, depth + 1)}{child.s}\n") + else: + result.add(child.node.text(depth + 1)) + + +proc `$`*(sourceNode: SourceNode): string = text(sourceNode, 0) # base64_VLQ -func encode*(values: seq[int]): string {.raises: [].} = - ## Encodes a series of integers into a VLQ base64 encoded string - # References: - # - https://www.lucidchart.com/techblog/2019/08/22/decode-encoding-base64-vlqs-source-maps/ - # - https://github.com/rails/sprockets/blob/main/guides/source_maps.md#source-map-file - const - alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - shift = 5 - continueBit = 1 shl 5 - mask = continueBit - 1 - for val in values: - # Sign is stored in first bit - var newVal = abs(val) shl 1 - if val < 0: - newVal = newVal or 1 - # Now comes the variable length part - # This is how we are able to store large numbers - while true: - # We only encode 5 bits. - var masked = newVal and mask - newVal = newVal shr shift - # If there is still something left - # then signify with the continue bit that the - # decoder should keep decoding - if newVal > 0: - masked = masked or continueBit - result &= alphabet[masked] - # If the value is zero then we have nothing left to encode - if newVal == 0: - break -iterator tokenize*(line: string): (int, string) = - ## Goes through a line and splits it into Nim identifiers and - ## normal JS code. This allows us to map mangled names back to Nim names. - ## Yields (column, name). Doesn't yield anything but identifiers. - ## See mangleName in compiler/jsgen.nim for how name mangling is done - var - col = 0 - token = "" - while col < line.len: - var - token: string - name: string - # First we find the next identifier - col += line.skipWhitespace(col) - col += line.skipUntil(IdentStartChars, col) - let identStart = col - col += line.parseIdent(token, col) - # Idents will either be originalName_randomInt or HEXhexCode_randomInt - if token.startsWith("HEX"): - var hex: int - # 3 = "HEX".len and we only want to parse the two integers after it - discard token[3 ..< 5].parseHex(hex) - name = $chr(hex) - elif not token.endsWith("_Idx"): # Ignore address indexes - # It might be in the form originalName_randomInt - let lastUnderscore = token.rfind('_') - if lastUnderscore != -1: - name = token[0.. 0: + var e = n and 31 + n = n shr 5 + if n > 0: + e = e or 32 + + result.add(integers[e]) + z += 1 + + +type TokenState = enum Normal, String, Ident, Mangled + +iterator tokenize*(line: string): (bool, string) = + # result = @[] + var state = Normal + var token = "" + var isMangled = false + for z, ch in line: + if ch.isAlphaAscii: + if state == Normal: + state = Ident + if token.len > 0: + yield (isMangled, token) + token = $ch + isMangled = false + else: + token.add(ch) + elif ch == '_': + if state == Ident: + state = Mangled + isMangled = true + token.add($ch) + elif ch != '"' and not ch.isAlphaNumeric: + if state in {Ident, Mangled}: + state = Normal + if token.len > 0: + yield (isMangled, token) + token = $ch + isMangled = false + else: + token.add($ch) + elif ch == '"': + if state != String: + state = String + if token.len > 0: + yield (isMangled, token) + token = $ch + isMangled = false + else: + state = Normal + token.add($ch) + if token.len > 0: + yield (isMangled, token) + isMangled = false + token = "" else: - result.newLine() + token.add($ch) + if token.len > 0: + yield (isMangled, token) -func toSourceMap*(info: SourceInfo, file: string): SourceMap {.raises: [].} = - ## Convert from high level SourceInfo into the required SourceMap object - # Add basic info - result.version = 3 - result.file = file - result.sources = info.files - result.names = info.names - # Convert nodes into mappings. - # Mappings are split into blocks where each block referes to a line in the outputted JS. - # Blocks can be separated into statements which refere to tokens on the line. - # Since the mappings depend on previous values we need to - # keep track of previous file, name, etc - var - prevFile = 0 - prevLine = 0 - prevName = 0 - prevNimCol = 0 +proc parse*(source: string, path: string): SourceNode = + let lines = source.splitLines() + var lastLocation: SourceNode = nil + result = newSourceNode(0, 0, path, @[]) + + # we just use one single parent and add all nim lines + # as its children, I guess in typical codegen + # that happens recursively on ast level + # we also don't have column info, but I doubt more one nim lines can compile to one js + # maybe in macros? - for mapping in info.mappings: - # We know need to encode segments with the following fields - # All these fields are relative to their previous values - # - 0: Column in generated code - # - 1: Index of Nim file in source list - # - 2: Line in Nim source - # - 3: Column in Nim source - # - 4: Index in names list - if mapping.inSource: - # JS Column is special in that it is reset after every line - var prevJSCol = 0 - for segment in mapping.segments: - var values = @[segment.generated - prevJSCol, mapping.file - prevFile, mapping.line - prevLine, segment.original - prevNimCol] - # Add name field if needed - if segment.name != -1: - values &= segment.name - prevName - prevName = segment.name - prevJSCol = segment.generated - prevNimCol = segment.original - prevFile = mapping.file - prevLine = mapping.line - result.mappings &= encode(values) & "," - # Remove trailing , - if mapping.segments.len > 0: - result.mappings.setLen(result.mappings.len - 1) + for i, originalLine in lines: + let line = originalLine.strip + if line.len == 0: + continue + + # this shouldn't be a problem: + # jsgen doesn't generate comments + # and if you emit // line you probably know what you're doing + if line.startsWith("// line"): + if result.children.len > 0: + result.children[^1].node.children.add(child(line & "\n")) + let pos = line.find(" ", 8) + let lineNumber = line[8 .. pos - 1].parseInt + let linePath = line[pos + 2 .. ^2] # quotes + + lastLocation = newSourceNode( + lineNumber, + 0, + linePath, + @[]) + result.children.add(child(lastLocation)) + else: + var last: SourceNode + for token in line.tokenize(): + var name = "" + if token[0]: + name = token[1].split('_', 1)[0] + + + if result.children.len > 0: + result.children[^1].node.children.add( + child( + newSourceNode( + result.children[^1].node.line, + 0, + result.children[^1].node.source, + token[1], + name))) + last = result.children[^1].node.children[^1].node + else: + result.children.add( + child( + newSourceNode(i + 1, 0, path, token[1], name))) + last = result.children[^1].node + let nl = "\n" + if not last.isNil: + last.source.add(nl) - result.mappings &= ";" +proc cmp(a: Mapping, b: Mapping): int = + var c = cmp(a.generated, b.generated) + if c != 0: + return c -proc genSourceMap*(source: string, outFile: string): SourceMap = - let node = parse(source) - result = node.toSourceMap(outFile) + c = cmp(a.source, b.source) + if c != 0: + return c + + c = cmp(a.original, b.original) + if c != 0: + return c + + return cmp(a.name, b.name) + + +proc index*[T](elements: seq[T], element: T): int = + for z in 0 ..< elements.len: + if elements[z] == element: + return z + return -1 + + +proc serializeMappings(map: SourceMapGenerator, mappings: seq[Mapping]): string = + var previous = Mapping(generated: (line: 1, column: 0), original: (line: 0, column: 0), name: "", source: "") + var previousSourceId = 0 + var previousNameId = 0 + var next = "" + var nameId = 0 + var sourceId = 0 + result = "" + + for z, mapping in mappings: + next = "" + + if mapping.generated.line != previous.generated.line: + previous.generated.column = 0 + + while mapping.generated.line != previous.generated.line: + next.add(";") + previous.generated.line += 1 + + else: + if z > 0: + if cmp(mapping, mappings[z - 1]) == 0: + continue + next.add(",") + + next.add(encode(mapping.generated.column - previous.generated.column)) + previous.generated.column = mapping.generated.column + + if not mapping.noSource and mapping.source.len > 0: + sourceId = map.sources.index(mapping.source) + next.add(encode(sourceId - previousSourceId)) + previousSourceId = sourceId + next.add(encode(mapping.original.line - 1 - previous.original.line)) + previous.original.line = mapping.original.line - 1 + next.add(encode(mapping.original.column - previous.original.column)) + previous.original.column = mapping.original.column + + if not mapping.noName and mapping.name.len > 0: + nameId = map.names.index(mapping.name) + next.add(encode(nameId - previousNameId)) + previousNameId = nameId + + result.add(next) + + +proc gen*(map: SourceMapGenerator): SourceMap = + var mappings = map.mappings.sorted do (a: Mapping, b: Mapping) -> int: + cmp(a, b) + result = SourceMap( + file: map.file, + version: 3, + sources: map.sources[0..^1], + names: map.names[0..^1], + mappings: map.serializeMappings(mappings)) + + + +proc addMapping*(map: SourceMapGenerator, mapping: Mapping) = + if not mapping.noSource and mapping.source notin map.sources: + map.sources.add(mapping.source) + + if not mapping.noName and mapping.name.len > 0 and mapping.name notin map.names: + map.names.add(mapping.name) + + # echo "map ", mapping.source, " ", mapping.original, " ", mapping.generated, " ", mapping.name + map.mappings.add(mapping) + + +proc walk*(node: SourceNode, fn: proc(line: string, original: SourceNode)) = + for child in node.children: + if child.kind == cString and child.s.len > 0: + fn(child.s, node) + else: + child.node.walk(fn) + + +proc toSourceMap*(node: SourceNode, file: string): SourceMapGenerator = + var map = SourceMapGenerator(file: file, sources: @[], names: @[], mappings: @[]) + + var generated = (line: 1, column: 0) + var sourceMappingActive = false + var lastOriginal = SourceNode(source: "", line: -1, column: 0, name: "", children: @[]) + + node.walk do (line: string, original: SourceNode): + if original.source.endsWith(".js"): + # ignore it + discard + else: + if original.line != -1: + if lastOriginal.source != original.source or + lastOriginal.line != original.line or + lastOriginal.column != original.column or + lastOriginal.name != original.name: + map.addMapping( + Mapping( + source: original.source, + original: (line: original.line, column: original.column), + generated: (line: generated.line, column: generated.column), + name: original.name)) + + lastOriginal = SourceNode( + source: original.source, + line: original.line, + column: original.column, + name: original.name, + children: lastOriginal.children) + sourceMappingActive = true + elif sourceMappingActive: + map.addMapping( + Mapping( + noSource: true, + noName: true, + generated: (line: generated.line, column: generated.column), + original: (line: -1, column: -1))) + lastOriginal.line = -1 + sourceMappingActive = false + + for z in 0 ..< line.len: + if line[z] in Newlines: + generated.line += 1 + generated.column = 0 + + if z == line.len - 1: + lastOriginal.line = -1 + sourceMappingActive = false + elif sourceMappingActive: + map.addMapping( + Mapping( + source: original.source, + original: (line: original.line, column: original.column), + generated: (line: generated.line, column: generated.column), + name: original.name)) + else: + generated.column += 1 + + map + + +proc genSourceMap*(source: string, outFile: string): (Rope, SourceMap) = + let node = parse(source, outFile) + let map = node.toSourceMap(file = outFile) + ((&"{source}\n//# sourceMappingURL={outFile}.map").rope, map.gen) diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 581f722d5..61bcc424b 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -192,11 +192,6 @@ proc createCastExpr(argsParam: PSym; objType: PType; idgen: IdGenerator): PNode result.typ = newType(tyPtr, nextTypeId idgen, objType.owner) result.typ.rawAddSon(objType) -template checkMagicProcs(g: ModuleGraph, n: PNode, formal: PNode) = - if (formal.typ.kind == tyVarargs and formal.typ[0].kind in {tyTyped, tyUntyped}) or - formal.typ.kind in {tyTyped, tyUntyped}: - localError(g.config, n.info, "'spawn'ed function cannot have a 'typed' or 'untyped' parameter") - proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; idgen: IdGenerator; owner: PSym; scratchObj: PSym, castExpr, call, @@ -210,9 +205,6 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; if i < formals.len: if formals[i].typ.kind in {tyVar, tyLent}: localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter") - - checkMagicProcs(g, n[i], formals[i]) - if formals[i].typ.kind in {tyTypeDesc, tyStatic}: continue #elif containsTyRef(argType): @@ -221,7 +213,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; let fieldname = if i < formals.len: formals[i].sym.name else: tmpName var field = newSym(skField, fieldname, nextSymId idgen, objType.owner, n.info, g.config.options) field.typ = argType - discard objType.addField(field, g.cache, idgen) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i]) let temp = addLocalVar(g, varSection, varInit, idgen, owner, argType, @@ -241,9 +233,6 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; let n = n[i] if i < formals.len and formals[i].typ.kind in {tyStatic, tyTypeDesc}: continue - - checkMagicProcs(g, n, formals[i]) - let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ, abstractInst) #if containsTyRef(argType): @@ -256,21 +245,21 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; # important special case: we always create a zero-copy slice: let slice = newNodeI(nkCall, n.info, 4) slice.typ = n.typ - slice[0] = newSymNode(createMagic(g, idgen, "slice", mSlice)) + slice[0] = newSymNode(createMagic(g, "slice", mSlice)) slice[0].typ = getSysType(g, n.info, tyInt) # fake type var fieldB = newSym(skField, tmpName, nextSymId idgen, objType.owner, n.info, g.config.options) fieldB.typ = getSysType(g, n.info, tyInt) - discard objType.addField(fieldB, g.cache, idgen) + objType.addField(fieldB, g.cache, idgen) if getMagic(n) == mSlice: let a = genAddrOf(n[1], idgen) field.typ = a.typ - discard objType.addField(field, g.cache, idgen) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) var fieldA = newSym(skField, tmpName, nextSymId idgen, objType.owner, n.info, g.config.options) fieldA.typ = getSysType(g, n.info, tyInt) - discard objType.addField(fieldA, g.cache, idgen) + objType.addField(fieldA, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2]) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3]) @@ -281,7 +270,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; else: let a = genAddrOf(n, idgen) field.typ = a.typ - discard objType.addField(field, g.cache, idgen) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n)) @@ -299,7 +288,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; # it is more efficient to pass a pointer instead: let a = genAddrOf(n, idgen) field.typ = a.typ - discard objType.addField(field, g.cache, idgen) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, field.typ, indirectAccess(castExpr, field, n.info), @@ -308,7 +297,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; else: # boring case field.typ = argType - discard objType.addField(field, g.cache, idgen) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n) let threadLocal = addLocalVar(g, varSection, varInit, idgen, owner, field.typ, @@ -334,7 +323,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp result = newNodeI(nkStmtList, n.info) if n.kind notin nkCallKinds: - localError(g.config, n.info, "'spawn' takes a call expression; got: " & $n) + localError(g.config, n.info, "'spawn' takes a call expression; got " & $n) return if optThreadAnalysis in g.config.globalOptions: if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: @@ -377,11 +366,13 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp var argType = n[0].typ.skipTypes(abstractInst) var field = newSym(skField, getIdent(g.cache, "fn"), nextSymId idgen, owner, n.info, g.config.options) field.typ = argType - discard objType.addField(field, g.cache, idgen) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0]) fn = indirectAccess(castExpr, field, n.info) elif fn.kind == nkSym and fn.sym.kind == skIterator: localError(g.config, n.info, "iterator in spawn environment is not allowed") + elif fn.typ.callConv == ccClosure: + localError(g.config, n.info, "closure in spawn environment is not allowed") call.add(fn) var varSection = newNodeI(nkVarSection, n.info) @@ -399,7 +390,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ) var field = newSym(skField, getIdent(g.cache, "barrier"), nextSymId idgen, owner, n.info, g.config.options) field.typ = typ - discard objType.addField(field, g.cache, idgen) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier) barrierAsExpr = indirectAccess(castExpr, field, n.info) @@ -407,7 +398,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp if spawnKind == srFlowVar: var field = newSym(skField, getIdent(g.cache, "fv"), nextSymId idgen, owner, n.info, g.config.options) field.typ = retType - discard objType.addField(field, g.cache, idgen) + objType.addField(field, g.cache, idgen) fvField = newDotExpr(scratchObj, field) fvAsExpr = indirectAccess(castExpr, field, n.info) # create flowVar: @@ -419,7 +410,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp var field = newSym(skField, getIdent(g.cache, "fv"), nextSymId idgen, owner, n.info, g.config.options) field.typ = newType(tyPtr, nextTypeId idgen, objType.owner) field.typ.rawAddSon(retType) - discard objType.addField(field, g.cache, idgen) + objType.addField(field, g.cache, idgen) fvAsExpr = indirectAccess(castExpr, field, n.info) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest, idgen)) @@ -431,3 +422,4 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp wrapperProc.newSymNode, genAddrOf(scratchObj.newSymNode, idgen), nil, spawnExpr) if spawnKind == srFlowVar: result.add fvField + diff --git a/compiler/strutils2.nim b/compiler/strutils2.nim new file mode 100644 index 000000000..f44b811c7 --- /dev/null +++ b/compiler/strutils2.nim @@ -0,0 +1,57 @@ +##[ +internal API for now, subject to modifications and moving around + +string API's focusing on performance, that can be used as building blocks +for other routines. + +Un-necessary allocations are avoided and appropriate algorithms are used at the +expense of code clarity when justified. +]## + +proc dataPointer*[T](a: T): pointer = + ## same as C++ `data` that works with std::string, std::vector etc. + ## Note: safe to use when a.len == 0 but whether the result is nil or not + ## is implementation defined for performance reasons. + # this could be improved with ocmpiler support to avoid the `if`, e.g. in C++ + # `&a[0]` is well defined even if a.size() == 0 + when T is string | seq: + if a.len == 0: nil else: cast[pointer](a[0].unsafeAddr) + elif T is array: + when a.len > 0: a.unsafeAddr + else: nil + elif T is cstring: + cast[pointer](a) + else: static: doAssert false, $T + +proc setLen*(result: var string, n: int, isInit: bool) = + ## when isInit = false, elements are left uninitialized, analog to `{.noinit.}` + ## else, there are 0-initialized. + # xxx placeholder until system.setLen supports this + # to distinguish between algorithms that need 0-initialization vs not; note + # that `setLen` for string is inconsistent with `setLen` for seq. + # likwise with `newString` vs `newSeq`. This should be fixed in `system`. + let n0 = result.len + result.setLen(n) + if isInit and n > n0: + zeroMem(result[n0].addr, n - n0) + +proc forceCopy*(result: var string, a: string) = + ## also forces a copy if `a` is shallow + # the naitve `result = a` would not work if `a` is shallow + let n = a.len + result.setLen n, isInit = false + copyMem(result.dataPointer, a.dataPointer, n) + +proc isUpperAscii(c: char): bool {.inline.} = + # avoids import strutils.isUpperAscii + c in {'A'..'Z'} + +proc toLowerAscii*(a: var string) = + ## optimized and inplace overload of strutils.toLowerAscii + # refs https://github.com/timotheecour/Nim/pull/54 + # this is 10X faster than a naive implementation using a an optimization trick + # that can be adapted in similar contexts. Predictable writes avoid write + # hazards and lead to better machine code, compared to random writes arising + # from: `if c.isUpperAscii: c = ...` + for c in mitems(a): + c = chr(c.ord + (if c.isUpperAscii: (ord('a') - ord('A')) else: 0)) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 384ceb271..73929f813 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -32,11 +32,11 @@ # included from sigmatch.nim -import algorithm, sets, prefixmatches, parseutils, tables +import algorithm, sets, prefixmatches, lineinfos, parseutils, linter from wordrecg import wDeprecated, wError, wAddr, wYield when defined(nimsuggest): - import tables, pathutils # importer + import passes, tables, pathutils # importer const sep = '\t' @@ -53,13 +53,13 @@ proc findDocComment(n: PNode): PNode = if result != nil: return if n.len > 1: result = findDocComment(n[1]) - elif n.kind in {nkAsgn, nkFastAsgn, nkSinkAsgn} and n.len == 2: + elif n.kind in {nkAsgn, nkFastAsgn} and n.len == 2: result = findDocComment(n[1]) -proc extractDocComment(g: ModuleGraph; s: PSym): string = +proc extractDocComment(s: PSym): string = var n = findDocComment(s.ast) if n.isNil and s.kind in routineKinds and s.ast != nil: - n = findDocComment(getBody(g, s)) + n = findDocComment(s.ast[bodyPos]) if not n.isNil: result = n.comment.replace("\n##", "\n").strip else: @@ -70,11 +70,11 @@ proc cmpSuggestions(a, b: Suggest): int = result = b.field.int - a.field.int if result != 0: return result - cf prefix - cf contextFits cf scope + cf prefix # when the first type matches, it's better when it's a generic match: cf quality + cf contextFits cf localUsages cf globalUsages # if all is equal, sort alphabetically for deterministic output, @@ -112,17 +112,15 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int if ident[^1] == '=' and ident[0] in linter.Letters: if sourceIdent != "=": result = 0 - elif sourceIdent.len > ident.len and sourceIdent[0..ident.high] == ident: + elif sourceIdent.len > ident.len and sourceIdent[..ident.high] == ident: result = ident.len elif sourceIdent != ident: result = 0 -proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; +proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; quality: range[0..100]; prefix: PrefixMatch; inTypeContext: bool; scope: int; - useSuppliedInfo = false, - endLine: uint16 = 0, - endCol = 0): Suggest = + useSuppliedInfo = false): Suggest = new(result) result.section = section result.quality = quality @@ -138,7 +136,7 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info if u.fileIndex == info.fileIndex: inc c result.localUsages = c result.symkind = byte s.kind - if optIdeTerse notin g.config.globalOptions: + if optIdeTerse notin conf.globalOptions: result.qualifiedPath = @[] if not isLocal and s.kind != skModule: let ow = s.owner @@ -158,28 +156,20 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info else: result.forth = "" when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler): - result.doc = extractDocComment(g, s) - 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, ideDeclaration}: - 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.endLine = endLine - result.endCol = endCol + result.doc = s.extractDocComment + let infox = + if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline}: + info + else: + s.info + result.filePath = toFullPath(conf, infox) + result.line = toLinenumber(infox) + result.column = toColumn(infox) + result.version = conf.suggestVersion + result.tokenLen = if section != ideHighlight: + s.name.s.len + else: + getTokenLenFromSource(conf, s.name.s, infox) proc `$`*(suggest: Suggest): string = result = $suggest.section @@ -213,20 +203,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 in {0, 3}: + if suggest.version == 0: result.add(sep) result.add($suggest.quality) if suggest.section == ideSug: result.add(sep) result.add($suggest.prefix) - if (suggest.version == 3 and suggest.section in {ideOutline, ideExpand}): - result.add(sep) - result.add($suggest.endLine) - result.add(sep) - result.add($suggest.endCol) - -proc suggestResult*(conf: ConfigRef; s: Suggest) = +proc suggestResult(conf: ConfigRef; s: Suggest) = if not isNil(conf.suggestionResultHook): conf.suggestionResultHook(s) else: @@ -269,41 +253,29 @@ proc filterSymNoOpr(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = let fmoduleId = getModule(f).id result = sfExported in f.flags or fmoduleId == c.module.id - - if not result: - for module in c.friendModules: - if fmoduleId == module.id: return true - if f.kind == skField: - var symObj = f.owner - if symObj.typ.skipTypes({tyGenericBody, tyGenericInst, tyGenericInvocation, tyAlias}).kind in {tyRef, tyPtr}: - symObj = symObj.typ.toObjectFromRefPtrGeneric.sym - for scope in allScopes(c.currentScope): - for sym in scope.allowPrivateAccess: - if symObj.id == sym.id: return true - -proc getQuality(s: PSym): range[0..100] = - result = 100 - if s.typ != nil and s.typ.len > 1: - var exp = s.typ[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) - if exp.kind == tyVarargs: exp = elemType(exp) - if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: result = 50 - - # penalize deprecated symbols - if sfDeprecated in s.flags: - result = result - 5 + for module in c.friendModules: + if fmoduleId == module.id: + result = true + break proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) = var pm: PrefixMatch if filterSym(s, f, pm) and fieldVisible(c, s): - outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, info, - s.getQuality, pm, c.inTypeContext > 0, 0)) + outputs.add(symToSuggest(c.config, s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0)) + +proc getQuality(s: PSym): range[0..100] = + if s.typ != nil and s.typ.len > 1: + var exp = s.typ[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink}) + if exp.kind == tyVarargs: exp = elemType(exp) + if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return 50 + return 100 template wholeSymTab(cond, section: untyped) {.dirty.} = - for (item, scopeN, isLocal) in uniqueSyms(c): + for (item, scopeN, isLocal) in allSyms(c): let it = item var pm: PrefixMatch if cond: - outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, section, info, getQuality(it), + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it), pm, c.inTypeContext > 0, scopeN)) proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) = @@ -326,7 +298,6 @@ proc suggestObject(c: PContext, n, f: PNode; info: TLineInfo, outputs: var Sugge proc nameFits(c: PContext, s: PSym, n: PNode): bool = var op = if n.kind in nkCallKinds: n[0] else: n if op.kind in {nkOpenSymChoice, nkClosedSymChoice}: op = op[0] - if op.kind == nkDotExpr: op = op[1] var opr: PIdent case op.kind of nkSym: opr = op.sym.name @@ -372,11 +343,11 @@ proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Sugges proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) = # do not produce too many symbols: - for (it, scopeN, isLocal) in uniqueSyms(c): + for (it, scopeN, isLocal) in allSyms(c): var pm: PrefixMatch if filterSym(it, f, pm): - outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, n.info, - it.getQuality, pm, c.inTypeContext > 0, scopeN)) + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, n.info, 0, pm, + c.inTypeContext > 0, scopeN)) proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) = # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but @@ -394,14 +365,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) let m = c.graph.importModuleCallback(c.graph, c.module, fileInfoIdx(c.config, fullPath)) if m == nil: typ = nil else: - for it in allSyms(c.graph, n.sym): + for it in items(n.sym.tab): if filterSym(it, field, pm): - outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug, - n.info, it.getQuality, pm, - c.inTypeContext > 0, -100)) - outputs.add(symToSuggest(c.graph, m, isLocal=false, ideMod, n.info, - 100, PrefixMatch.None, c.inTypeContext > 0, - -99)) + outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100)) + outputs.add(symToSuggest(c.config, m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None, + c.inTypeContext > 0, -99)) if typ == nil: # a module symbol has no type for example: @@ -410,29 +378,25 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) # all symbols accessible, because we are in the current module: for it in items(c.topLevelScope.symbols): if filterSym(it, field, pm): - outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug, - n.info, it.getQuality, pm, - c.inTypeContext > 0, -99)) + outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) else: - for it in allSyms(c.graph, n.sym): + for it in items(n.sym.tab): if filterSym(it, field, pm): - outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug, - n.info, it.getQuality, pm, - c.inTypeContext > 0, -99)) + outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) else: # fallback: suggestEverything(c, n, field, outputs) + elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: + # look up if the identifier belongs to the enum: + var t = typ + while t != nil: + suggestSymList(c, t.n, field, n.info, outputs) + t = t[0] + suggestOperations(c, n, field, typ, outputs) else: - let orig = typ - typ = skipTypes(orig, {tyTypeDesc, tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink, tyOwned}) - - if typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: - # look up if the identifier belongs to the enum: - var t = typ - while t != nil: - suggestSymList(c, t.n, field, n.info, outputs) - t = t[0] - elif typ.kind == tyObject: + let orig = typ # skipTypes(typ, {tyGenericInst, tyAlias, tySink}) + typ = skipTypes(typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink, tyOwned}) + if typ.kind == tyObject: var t = typ while true: suggestObject(c, t.n, field, n.info, outputs) @@ -440,7 +404,6 @@ 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) @@ -477,28 +440,28 @@ when defined(nimsuggest): if infoB.infoToInt == infoAsInt: return s.allUsages.add(info) -proc findUsages(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) = - if g.config.suggestVersion == 1: - if usageSym == nil and isTracked(info, g.config.m.trackPos, s.name.s.len): +proc findUsages(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) = + if conf.suggestVersion == 1: + if usageSym == nil and isTracked(info, conf.m.trackPos, s.name.s.len): usageSym = s - suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) elif s == usageSym: - if g.config.lastLineInfo != info: - suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) - g.config.lastLineInfo = info + if conf.lastLineInfo != info: + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) + conf.lastLineInfo = info when defined(nimsuggest): - proc listUsages*(g: ModuleGraph; s: PSym) = + proc listUsages*(conf: ConfigRef; s: PSym) = #echo "usages ", s.allUsages.len for info in s.allUsages: let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse - suggestResult(g.config, symToSuggest(g, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0)) + suggestResult(conf, symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0)) -proc findDefinition(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) = +proc findDefinition(conf: ConfigRef; 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 and g.config.suggestVersion != 3: + if isTracked(info, conf.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags): + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym)) + if sfForward notin s.flags: suggestQuit() else: usageSym = s @@ -509,12 +472,9 @@ proc ensureIdx[T](x: var T, y: int) = proc ensureSeq[T](x: var seq[T]) = if x == nil: newSeq(x, 0) -proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} = +proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} = ## 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] @@ -522,15 +482,15 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i s.addNoDup(info) if conf.ideCmd == ideUse: - findUsages(g, info, s, usageSym) + findUsages(conf, info, s, usageSym) elif conf.ideCmd == ideDef: - findDefinition(g, info, s, usageSym) + findDefinition(conf, info, s, usageSym) elif conf.ideCmd == ideDus and s != nil: if isTracked(info, conf.m.trackPos, s.name.s.len): - suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) - findUsages(g, info, s, usageSym) + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) + findUsages(conf, info, s, usageSym) elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex: - suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) elif conf.ideCmd == ideOutline and isDecl: # if a module is included then the info we have is inside the include and # we need to walk up the owners until we find the outer most module, @@ -543,7 +503,17 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i parentModule = parentModule.owner if parentFileIndex == conf.m.trackPos.fileIndex: - suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) + suggestResult(conf, symToSuggest(conf, 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 @@ -580,8 +550,7 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) = var i = 0 while i <= high(c.unusedImports): let candidate = c.unusedImports[i][0] - if candidate == module or c.importModuleMap.getOrDefault(candidate.id, int.low) == module.id or - c.exportIndirections.contains((candidate.id, s.id)): + if candidate == module or c.exportIndirections.contains((candidate.id, s.id)): # mark it as used: c.unusedImports.del(i) else: @@ -603,8 +572,9 @@ 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) - styleCheckUse(c, info, s) + suggestSym(conf, info, s, c.graph.usageSym, false) + if {optStyleHint, optStyleError} * conf.globalOptions != {}: + styleCheckUse(conf, info, s) markOwnerModuleAsUsed(c, s) proc safeSemExpr*(c: PContext, n: PNode): PNode = @@ -627,11 +597,6 @@ proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) = #if optIdeDebug in gGlobalOptions: # echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ) #writeStackTrace() - elif n.kind == nkIdent: - let - prefix = if c.config.m.trackPosAttached: nil else: n - info = n.info - wholeSymTab(filterSym(it, prefix, pm), ideSug) else: let prefix = if c.config.m.trackPosAttached: nil else: n suggestEverything(c, n, prefix, outputs) @@ -690,25 +655,12 @@ proc suggestSentinel*(c: PContext) = inc(c.compilesContextId) var outputs: Suggestions = @[] # suggest everything: - for (it, scopeN, isLocal) in uniqueSyms(c): + for (it, scopeN, isLocal) in allSyms(c): var pm: PrefixMatch if filterSymNoOpr(it, nil, pm): - outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, - newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), it.getQuality, + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, + newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), 0, PrefixMatch.None, false, scopeN)) 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/syntaxes.nim b/compiler/syntaxes.nim index c00fe8b67..e745e28ba 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -13,10 +13,7 @@ import strutils, llstream, ast, idents, lexer, options, msgs, parser, filters, filter_tmpl, renderer, lineinfos, pathutils -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - -export Parser, parseAll, parseTopLevelStmt, checkFirstLineIndentation, closeParser +export Parser, parseAll, parseTopLevelStmt, closeParser type FilterKind = enum @@ -53,7 +50,7 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache if i+1 < line.len and line[i] == '#' and line[i+1] == '?': when defined(nimpretty): # XXX this is a bit hacky, but oh well... - config.quitOrRaise "can't nimpretty a source code filter: " & $filename + quit "can't nimpretty a source code filter" else: inc(i, 2) while i < line.len and line[i] in Whitespace: inc(i) diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim index 83c891ca8..ba553906a 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, cstring(libpath)) + addIncludePath(gTinyC, libpath) when defined(windows): - addSysincludePath(gTinyC, cstring(tinycRoot / "tinyc/win32/include")) - addSysincludePath(gTinyC, cstring(tinycRoot / "tinyc/include")) + addSysincludePath(gTinyC, tinycRoot / "tinyc/win32/include") + addSysincludePath(gTinyC, 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, cstring(gccbin /../ "include")) + addSysincludePath(gTinyC, 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 445bc1df1..461db9e89 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -24,19 +24,13 @@ import lowerings, liftlocals, modulegraphs, lineinfos -when defined(nimPreviewSlimSystem): - import std/assertions - -type - TransformBodyFlag* = enum - dontUseCache, useCache - -proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, flag: TransformBodyFlag, force = false): PNode +proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, cache: bool): PNode import closureiters, lambdalifting type - PTransCon = ref object # part of TContext; stackable + PTransCon = ref TTransCon + TTransCon{.final.} = object # part of TContext; stackable mapping: TIdNodeTable # mapping from symbols to nodes owner: PSym # current owner forStmt: PNode # current for stmt @@ -46,15 +40,16 @@ type # if we encounter the 2nd yield statement next: PTransCon # for stacking - PTransf = ref object + TTransfContext = object module: PSym transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) - isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) + nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly: bool graph: ModuleGraph idgen: IdGenerator + PTransf = ref TTransfContext proc newTransNode(a: PNode): PNode {.inline.} = result = shallowCopy(a) @@ -69,6 +64,7 @@ proc newTransNode(kind: TNodeKind, n: PNode, sons: int): PNode {.inline.} = var x = newNodeIT(kind, n.info, n.typ) newSeq(x.sons, sons) + x.typ = n.typ # x.flags = n.flags result = x @@ -107,18 +103,16 @@ proc transformSons(c: PTransf, n: PNode): PNode = for i in 0.. 0: @@ -189,12 +175,10 @@ proc transformVarSection(c: PTransf, v: PNode): PNode = if it.kind == nkCommentStmt: result[i] = it elif it.kind == nkIdentDefs: - var vn = it[0] - if vn.kind == nkPragmaExpr: vn = vn[0] - if vn.kind == nkSym: + if it[0].kind == nkSym: internalAssert(c.graph.config, it.len == 3) - let x = freshVar(c, vn.sym) - idNodeTablePut(c.transCon.mapping, vn.sym, x) + let x = freshVar(c, it[0].sym) + idNodeTablePut(c.transCon.mapping, it[0].sym, x) var defs = newTransNode(nkIdentDefs, it.info, 3) if importantComments(c.graph.config): # keep documentation information: @@ -370,9 +354,9 @@ proc transformYield(c: PTransf, n: PNode): PNode = case lhs.kind of nkSym: internalAssert c.graph.config, lhs.sym.kind == skForVar - result = newAsgnStmt(c, nkFastAsgn, lhs, rhs, false) + result = newAsgnStmt(c, nkFastAsgn, lhs, rhs) of nkDotExpr: - result = newAsgnStmt(c, nkAsgn, lhs, rhs, false) + result = newAsgnStmt(c, nkAsgn, lhs, rhs) else: internalAssert c.graph.config, false result = newTransNode(nkStmtList, n.info, 0) @@ -382,7 +366,7 @@ proc transformYield(c: PTransf, n: PNode): PNode = if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons if c.transCon.forStmt.len != 3: e = skipConv(e) - if e.kind == nkTupleConstr: + if e.kind in {nkPar, nkTupleConstr}: for i in 0.. 0: + var changeNode = result[0] changeNode.info = c.transCon.forStmt.info for i, child in changeNode: child.info = changeNode.info -proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode = +proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = result = transformSons(c, n) if c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags: return var n = result case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: var m = n[0][0] - if m.kind in kinds: + if m.kind == a or m.kind == b: # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) n[0][0] = m[0] result = n[0] @@ -478,7 +427,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode = result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen) of nkHiddenStdConv, nkHiddenSubConv, nkConv: var m = n[0][1] - if m.kind in kinds: + if m.kind == a or m.kind == b: # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) n[0][1] = m[0] result = n[0] @@ -487,7 +436,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode = elif n.typ.skipTypes(abstractInst).kind in {tyVar}: result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen) else: - if n[0].kind in kinds: + if n[0].kind == a or n[0].kind == b: # addr ( deref ( x )) --> x result = n[0][0] if n.typ.skipTypes(abstractVar).kind != tyOpenArray: @@ -547,17 +496,17 @@ proc transformConv(c: PTransf, n: PNode): PNode = of tyOpenArray, tyVarargs: result = transform(c, n[1]) #result = transformSons(c, n) - result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen) + result.typ = takeType(n.typ, n[1].typ, c.idgen) #echo n.info, " came here and produced ", typeToString(result.typ), # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) - of tyCstring: + of tyCString: if source.kind == tyString: result = newTransNode(nkStringToCString, n, 1) result[0] = transform(c, n[1]) else: result = transformSons(c, n) of tyString: - if source.kind == tyCstring: + if source.kind == tyCString: result = newTransNode(nkCStringToString, n, 1) result[0] = transform(c, n[1]) else: @@ -575,7 +524,6 @@ proc transformConv(c: PTransf, n: PNode): PNode = result[0] = transform(c, n[1]) else: result = transform(c, n[1]) - result.typ = n.typ else: result = transformSons(c, n) of tyObject: @@ -588,7 +536,6 @@ proc transformConv(c: PTransf, n: PNode): PNode = result[0] = transform(c, n[1]) else: result = transform(c, n[1]) - result.typ = n.typ of tyGenericParam, tyOrdinal: result = transform(c, n[1]) # happens sometimes for generated assignments, etc. @@ -647,7 +594,7 @@ proc findWrongOwners(c: PTransf, n: PNode) = else: for i in 0.. 0 and not c.isIntroducingNewLocalVars: + if c.inlining > 0: result = transformYield(c, n) else: result = transformSons(c, n) of nkAsgn: result = transformAsgn(c, n) of nkIdentDefs, nkConstDef: - result = newTransNode(n) - result[0] = transform(c, skipPragmaExpr(n[0])) + result = n + result[0] = transform(c, n[0]) # Skip the second son since it only contains an unsemanticized copy of the # variable type used by docgen - let last = n.len-1 - for i in 1..= 0: assert(t.data[index].key != nil) t.data[index].val = val else: if mustRehash(t.data.len, t.counter): - var n = newSeq[TNodePair](t.data.len * GrowthFactor) + newSeq(n, t.data.len * GrowthFactor) for i in 0..high(t.data): if t.data[i].key != nil: nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val) - t.data = move n + swap(t.data, n) nodeTableRawInsert(t.data, k, key, val) inc(t.counter) proc nodeTableTestOrSet*(t: var TNodeTable, key: PNode, val: int): int = - let k = hashTree(key) - let index = nodeTableRawGet(t, k, key) + var n: TNodePairSeq + var k: Hash = hashTree(key) + var index = nodeTableRawGet(t, k, key) if index >= 0: assert(t.data[index].key != nil) result = t.data[index].val else: if mustRehash(t.data.len, t.counter): - var n = newSeq[TNodePair](t.data.len * GrowthFactor) + newSeq(n, t.data.len * GrowthFactor) for i in 0..high(t.data): if t.data[i].key != nil: nodeTableRawInsert(n, t.data[i].h, t.data[i].key, t.data[i].val) - t.data = move n + swap(t.data, n) nodeTableRawInsert(t.data, k, key, val) result = val inc(t.counter) diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 18cfcf6b2..c81819250 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -8,14 +8,11 @@ # ## This module contains 'typeAllowed' and friends which check -## for invalid types like `openArray[var int]`. +## for invalid types like 'openArray[var int]'. import intsets, ast, renderer, options, semdata, types -when defined(nimPreviewSlimSystem): - import std/assertions - type TTypeAllowedFlag* = enum taField, @@ -60,12 +57,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyVar, tyLent: if kind in {skProc, skFunc, skConst} and (views notin c.features): result = t - elif taIsOpenArray in flags: - result = t - elif t.kind == tyLent and ((kind != skResult and views notin c.features) or - kind == skParam): # lent can't be used as parameters. - result = t - elif isOutParam(t) and kind != skParam: + elif t.kind == tyLent and kind != skResult and (views notin c.features): result = t else: var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc, tySink}) @@ -104,9 +96,6 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = nil of tyUntyped, tyTyped: if kind notin {skParam, skResult} or taNoUntyped in flags: result = t - of tyIterable: - if kind notin {skParam} or taNoUntyped in flags: result = t - # tyIterable is only for templates and macros. of tyStatic: if kind notin {skParam}: result = t of tyVoid: @@ -123,7 +112,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = t of tyNil: if kind != skConst and kind != skParam: result = t - of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCstring, tyPointer: + of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer: result = nil of tyOrdinal: if kind != skParam: result = t @@ -131,7 +120,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = typeAllowedAux(marker, lastSon(t), kind, c, flags) of tyRange: if skipTypes(t[0], abstractInst-{tyTypeDesc}).kind notin - {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64, tyRange}: result = t + {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64}: result = t of tyOpenArray: # you cannot nest openArrays/sinks/etc. if (kind != skParam or taIsOpenArray in flags) and views notin c.features: @@ -198,9 +187,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap}) else: result = t - of tyConcept: - if kind != skParam: result = t - else: result = nil + of tyOptDeprecated: doAssert false proc typeAllowed*(t: PType, kind: TSymKind; c: PContext; flags: TTypeAllowedFlags = {}): PType = # returns 'nil' on success and otherwise the part of the type that is @@ -238,7 +225,7 @@ proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind = case t.kind of tyVar: result = mutableView - of tyLent, tyOpenArray, tyVarargs: + of tyLent, tyOpenArray: result = immutableView of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned, tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic: diff --git a/compiler/types.nim b/compiler/types.nim index 457568e32..a751e3602 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -11,10 +11,7 @@ import intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options, - lineinfos, int128, modulegraphs, astmsgs - -when defined(nimPreviewSlimSystem): - import std/[assertions, formatfloat] + lineinfos, int128 type TPreferedDesc* = enum @@ -29,37 +26,7 @@ type # most useful, shows: symbol + resolved symbols if it differs, e.g.: # tuple[a: MyInt{int}, b: float] - TTypeRelation* = enum # order is important! - isNone, isConvertible, - isIntConv, - isSubtype, - isSubrange, # subrange of the wanted type; no type conversion - # but apart from that counts as ``isSubtype`` - isBothMetaConvertible # generic proc parameter was matched against - # generic type, e.g., map(mySeq, x=>x+1), - # maybe recoverable by rerun if the parameter is - # the proc's return value - isInferred, # generic proc was matched against a concrete type - isInferredConvertible, # same as above, but requiring proc CC conversion - isGeneric, - isFromIntLit, # conversion *from* int literal; proven safe - isEqual - - ProcConvMismatch* = enum - pcmNoSideEffect - pcmNotGcSafe - pcmNotIterator - pcmDifferentCallConv - proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string - -proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) = - if optDeclaredLocs in conf.globalOptions: - result.add typeToString(typ, preferMixed) - result.addDeclaredLoc(conf, typ) - else: - result.add typeToString(typ) - template `$`*(typ: PType): string = typeToString(typ) proc base*(t: PType): PType = @@ -98,12 +65,15 @@ const tyAlias, tyInferred, tySink, tyLent, tyOwned} abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tyInferred, tySink, tyOwned} + # see also ast.abstractVarRange + abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, + tyInferred, tySink, tyOwned} abstractInstOwned* = abstractInst + {tyOwned} skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyTypeDesc, tyAlias, tyInferred, tySink, tyLent, tyOwned} # typedescX is used if we're sure tyTypeDesc should be included (or skipped) typedescPtrs* = abstractPtrs + {tyTypeDesc} - typedescInst* = abstractInst + {tyTypeDesc, tyOwned, tyUserTypeClass} + typedescInst* = abstractInst + {tyTypeDesc, tyOwned} proc invalidGenericInst*(f: PType): bool = result = f.kind == tyGenericInst and lastSon(f) == nil @@ -153,6 +123,23 @@ proc isIntLit*(t: PType): bool {.inline.} = proc isFloatLit*(t: PType): bool {.inline.} = result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit +proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) = + result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)] + +proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; sym: PSym) = + if optDeclaredLocs in conf.globalOptions and sym != nil: + addDeclaredLoc(result, conf, sym) + +proc addDeclaredLoc(result: var string, conf: ConfigRef; typ: PType) = + let typ = typ.skipTypes(abstractInst - {tyRange}) + result.add " [$1" % typ.kind.toHumanStr + if typ.sym != nil: + result.add " declared in " & toFileLineCol(conf, typ.sym.info) + result.add "]" + +proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) = + if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ) + proc addTypeHeader*(result: var string, conf: ConfigRef; typ: PType; prefer: TPreferedDesc = preferMixed; getDeclarationPath = true) = result.add typeToString(typ, prefer) if getDeclarationPath: result.addDeclaredLoc(conf, typ.sym) @@ -392,13 +379,13 @@ proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool = if tfAcyclic in t.flags: return case t.kind of tyTuple, tyObject, tyRef, tySequence, tyArray, tyOpenArray, tyVarargs: - if t.id == startId: - result = true - elif not containsOrIncl(marker, t.id): + if not containsOrIncl(marker, t.id): for i in 0..typedesc: typedesc is declared as such, and is 10x more common. "GenericInvocation", "GenericBody", "GenericInst", "GenericParam", "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", @@ -477,8 +459,8 @@ const "lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type", "BuiltInTypeClass", "UserTypeClass", "UserTypeClassInst", "CompositeTypeClass", "inferred", - "and", "or", "not", "any", "static", "TypeFromExpr", "concept", # xxx bugfix - "void", "iterable"] + "and", "or", "not", "any", "static", "TypeFromExpr", "out ", + "void"] const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, preferGenericArg, preferResolved, preferMixed} @@ -520,7 +502,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = typeToString(t[0]) elif prefer in {preferResolved, preferMixed}: case t.kind - of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}: + of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCString}: result = typeToStr[t.kind] of tyGenericBody: result = typeToString(t.lastSon) @@ -568,7 +550,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result.add(']') of tyTypeDesc: if t[0].kind == tyNone: result = "typedesc" - else: result = "typedesc[" & typeToString(t[0]) & "]" + else: result = "type " & typeToString(t[0]) of tyStatic: if prefer == preferGenericArg and t.n != nil: result = t.n.renderTree @@ -662,11 +644,6 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyDistinct: result = "distinct " & typeToString(t[0], if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName) - of tyIterable: - # xxx factor this pattern - result = "iterable" - if t.len > 0: - result &= "[" & typeToString(t[0]) & ']' of tyTuple: # we iterate over t.sons here, because t.n may be nil if t.n != nil: @@ -687,7 +664,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = elif t.len == 1: result.add(",") result.add(')') of tyPtr, tyRef, tyVar, tyLent: - result = if isOutParam(t): "out " else: typeToStr[t.kind] + result = typeToStr[t.kind] if t.len >= 2: setLen(result, result.len-1) result.add '[' @@ -730,6 +707,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if tfThread in t.flags: addSep(prag) prag.add("gcsafe") + if t.lockLevel.ord != UnspecifiedLockLevel.ord: + addSep(prag) + prag.add("locks: " & $t.lockLevel) if prag.len != 0: result.add("{." & prag & ".}") of tyVarargs: result = typeToStr[t.kind] % typeToString(t[0]) @@ -753,13 +733,8 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = assert(t.n.kind == nkRange) result = getOrdValue(t.n[0]) of tyInt: - if conf != nil: - case conf.target.intSize - of 8: result = toInt128(0x8000000000000000'i64) - of 4: result = toInt128(-2147483648) - of 2: result = toInt128(-32768) - of 1: result = toInt128(-128) - else: discard + if conf != nil and conf.target.intSize == 4: + result = toInt128(-2147483648) else: result = toInt128(0x8000000000000000'i64) of tyInt8: result = toInt128(-128) @@ -781,7 +756,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') - of tyUncheckedArray, tyCstring: + of tyUncheckedArray, tyCString: result = Zero else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') @@ -802,29 +777,6 @@ proc firstFloat*(t: PType): BiggestFloat = internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')') NaN -proc targetSizeSignedToKind*(conf: ConfigRef): TTypeKind = - case conf.target.intSize - of 8: result = tyInt64 - of 4: result = tyInt32 - of 2: result = tyInt16 - else: discard - -proc targetSizeUnsignedToKind*(conf: ConfigRef): TTypeKind = - case conf.target.intSize - of 8: result = tyUInt64 - of 4: result = tyUInt32 - of 2: result = tyUInt16 - else: discard - -proc normalizeKind*(conf: ConfigRef, k: TTypeKind): TTypeKind = - case k - of tyInt: - result = conf.targetSizeSignedToKind() - of tyUInt: - result = conf.targetSizeUnsignedToKind() - else: - result = k - proc lastOrd*(conf: ConfigRef; t: PType): Int128 = case t.kind of tyBool: result = toInt128(1'u) @@ -836,13 +788,7 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = assert(t.n.kind == nkRange) result = getOrdValue(t.n[1]) of tyInt: - if conf != nil: - case conf.target.intSize - of 8: result = toInt128(0x7FFFFFFFFFFFFFFF'u64) - of 4: result = toInt128(0x7FFFFFFF) - of 2: result = toInt128(0x00007FFF) - of 1: result = toInt128(0x0000007F) - else: discard + if conf != nil and conf.target.intSize == 4: result = toInt128(0x7FFFFFFF) else: result = toInt128(0x7FFFFFFFFFFFFFFF'u64) of tyInt8: result = toInt128(0x0000007F) of tyInt16: result = toInt128(0x00007FFF) @@ -932,7 +878,6 @@ type ExactConstraints ExactGcSafety AllowCommonBase - PickyCAliases # be picky about the distinction between 'cint' and 'int32' TTypeCmpFlags* = set[TTypeCmpFlag] @@ -950,6 +895,8 @@ proc initSameTypeClosure: TSameTypeClosure = proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool = result = c.s.len > 0 and c.s.contains((a.id, b.id)) if not result: + when not defined(nimNoNilSeqs): + if isNil(c.s): c.s = @[] c.s.add((a.id, b.id)) proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool @@ -1157,20 +1104,15 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = case c.cmp of dcEq: return false of dcEqIgnoreDistinct: - a = a.skipTypes({tyDistinct, tyGenericInst}) - b = b.skipTypes({tyDistinct, tyGenericInst}) + while a.kind == tyDistinct: a = a[0] + while b.kind == tyDistinct: b = b[0] if a.kind != b.kind: return false of dcEqOrDistinctOf: - a = a.skipTypes({tyDistinct, tyGenericInst}) + while a.kind == tyDistinct: a = a[0] if a.kind != b.kind: return false - #[ - The following code should not run in the case either side is an generic alias, - but it's not presently possible to distinguish the genericinsts from aliases of - objects ie `type A[T] = SomeObject` - ]# # this is required by tunique_type but makes no sense really: - if tyDistinct notin {x.kind, y.kind} and x.kind == tyGenericInst and IgnoreTupleFields notin c.flags: + if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags: let lhs = x.skipGenericAlias rhs = y.skipGenericAlias @@ -1183,17 +1125,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = return true case a.kind - of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCstring, + of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, tyInt..tyUInt64, tyTyped, tyUntyped, tyVoid: result = sameFlags(a, b) - if result and {PickyCAliases, ExactTypeDescValues} <= c.flags: - # additional requirement for the caching of generics for importc'ed types: - # the symbols must be identical too: - let symFlagsA = if a.sym != nil: a.sym.flags else: {} - let symFlagsB = if b.sym != nil: b.sym.flags else: {} - if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}: - result = symFlagsA == symFlagsB - of tyStatic, tyFromExpr: result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) if result and a.len == b.len and a.len == 1: @@ -1245,7 +1179,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameChildrenAux(a, b, c) if result: if IgnoreTupleFields in c.flags: - result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam} + result = a.flags * {tfVarIsPtr} == b.flags * {tfVarIsPtr} else: result = sameFlags(a, b) if result and ExactGcSafety in c.flags: @@ -1258,12 +1192,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameTypeOrNilAux(a[0], b[0], c) and sameValue(a.n[0], b.n[0]) and sameValue(a.n[1], b.n[1]) - of tyGenericInst, tyAlias, tyInferred, tyIterable: + of tyGenericInst, tyAlias, tyInferred: cycleCheck() result = sameTypeAux(a.lastSon, b.lastSon, c) of tyNone: result = false - of tyConcept: - result = exprStructuralEquivalent(a.n, b.n) + of tyOptDeprecated: doAssert false proc sameBackendType*(x, y: PType): bool = var c = initSameTypeClosure() @@ -1339,7 +1272,6 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], a = a[i] result = a.kind == last - include sizealignoffsetimpl proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt = @@ -1375,12 +1307,11 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool = proc containsGenericType*(t: PType): bool = result = iterOverType(t, containsGenericTypeIter, nil) -proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType = +proc baseOfDistinct*(t: PType; idgen: IdGenerator): PType = if t.kind == tyDistinct: result = t[0] else: result = copyType(t, nextTypeId idgen, t.owner) - copyTypeProps(g, idgen.module, result, t) var parent: PType = nil var it = result while it.kind in {tyPtr, tyRef, tyOwned}: @@ -1406,13 +1337,6 @@ proc compatibleEffectsAux(se, re: PNode): bool = return false result = true -proc hasIncompatibleEffect(se, re: PNode): bool = - if re.isNil: return false - for r in items(re): - for s in items(se): - if safeInheritanceDiff(r.typ, s.typ) != high(int): - return true - type EffectsCompat* = enum efCompat @@ -1420,15 +1344,11 @@ type efRaisesUnknown efTagsDiffer efTagsUnknown - efEffectsDelayed - efTagsIllegal + efLockLevelsDiffer proc compatibleEffects*(formal, actual: PType): EffectsCompat = # for proc type compatibility checking: assert formal.kind == tyProc and actual.kind == tyProc - #if tfEffectSystemWorkaround in actual.flags: - # return efCompat - if formal.n[0].kind != nkEffectList or actual.n[0].kind != nkEffectList: return efTagsUnknown @@ -1452,24 +1372,12 @@ proc compatibleEffects*(formal, actual: PType): EffectsCompat = # spec requires some exception or tag, but we don't know anything: if real.len == 0: return efTagsUnknown let res = compatibleEffectsAux(st, real[tagEffects]) - if not res: - #if tfEffectSystemWorkaround notin actual.flags: - return efTagsDiffer - - let sn = spec[forbiddenEffects] - if not isNil(sn) and sn.kind != nkArgList: - if 0 == real.len: - return efTagsUnknown - elif hasIncompatibleEffect(sn, real[tagEffects]): - return efTagsIllegal - - for i in 1 ..< min(formal.n.len, actual.n.len): - if formal.n[i].sym.flags * {sfEffectsDelayed} != actual.n[i].sym.flags * {sfEffectsDelayed}: - result = efEffectsDelayed - break - - result = efCompat - + if not res: return efTagsDiffer + if formal.lockLevel.ord < 0 or + actual.lockLevel.ord <= formal.lockLevel.ord: + result = efCompat + else: + result = efLockLevelsDiffer proc isCompileTimeOnly*(t: PType): bool {.inline.} = result = t.kind in {tyTypeDesc, tyStatic} @@ -1541,7 +1449,7 @@ proc isEmptyContainer*(t: PType): bool = of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon) else: result = false -proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType = +proc takeType*(formal, arg: PType; idgen: IdGenerator): PType = # param: openArray[string] = [] # [] is an array constructor of length 0 of type string! if arg.kind == tyNil: @@ -1550,7 +1458,6 @@ proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType = elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and arg.isEmptyContainer: let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), nextTypeId(idgen), arg.owner) - copyTypeProps(g, idgen.module, a, arg) a[ord(arg.kind == tyArray)] = formal[0] result = a elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind: @@ -1558,14 +1465,14 @@ proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType = else: result = arg -proc skipHiddenSubConv*(n: PNode; g: ModuleGraph; idgen: IdGenerator): PNode = +proc skipHiddenSubConv*(n: PNode; idgen: IdGenerator): PNode = if n.kind == nkHiddenSubConv: # param: openArray[string] = [] # [] is an array constructor of length 0 of type string! let formal = n.typ result = n[1] let arg = result.typ - let dest = takeType(formal, arg, g, idgen) + let dest = takeType(formal, arg, idgen) if dest == arg and formal.kind != tyUntyped: #echo n.info, " came here for ", formal.typeToString result = n @@ -1575,76 +1482,6 @@ proc skipHiddenSubConv*(n: PNode; g: ModuleGraph; idgen: IdGenerator): PNode = else: result = n -proc getProcConvMismatch*(c: ConfigRef, f, a: PType, rel = isNone): (set[ProcConvMismatch], TTypeRelation) = - ## Returns a set of the reason of mismatch, and the relation for conversion. - result[1] = rel - if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: - # Formal is pure, but actual is not - result[0].incl pcmNoSideEffect - result[1] = isNone - - if tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and - optThreadAnalysis in c.globalOptions: - # noSideEffect implies ``tfThread``! - result[0].incl pcmNotGcSafe - result[1] = isNone - - if f.flags * {tfIterator} != a.flags * {tfIterator}: - # One of them is an iterator so not convertible - result[0].incl pcmNotIterator - result[1] = isNone - - if f.callConv != a.callConv: - # valid to pass a 'nimcall' thingie to 'closure': - if f.callConv == ccClosure and a.callConv == ccNimCall: - case result[1] - of isInferred: result[1] = isInferredConvertible - of isBothMetaConvertible: result[1] = isBothMetaConvertible - elif result[1] != isNone: result[1] = isConvertible - else: result[0].incl pcmDifferentCallConv - else: - result[1] = isNone - result[0].incl pcmDifferentCallConv - -proc addPragmaAndCallConvMismatch*(message: var string, formal, actual: PType, conf: ConfigRef) = - assert formal.kind == tyProc and actual.kind == tyProc - let (convMismatch, _) = getProcConvMismatch(conf, formal, actual) - var - gotPragmas = "" - expectedPragmas = "" - for reason in convMismatch: - case reason - of pcmDifferentCallConv: - message.add "\n Calling convention mismatch: got '{.$1.}', but expected '{.$2.}'." % [$actual.callConv, $formal.callConv] - of pcmNoSideEffect: - expectedPragmas.add "noSideEffect, " - of pcmNotGcSafe: - expectedPragmas.add "gcsafe, " - of pcmNotIterator: discard - - if expectedPragmas.len > 0: - gotPragmas.setLen(max(0, gotPragmas.len - 2)) # Remove ", " - expectedPragmas.setLen(max(0, expectedPragmas.len - 2)) # Remove ", " - message.add "\n Pragma mismatch: got '{.$1.}', but expected '{.$2.}'." % [gotPragmas, expectedPragmas] - -proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, conf: ConfigRef) = - if formal.kind == tyProc and actual.kind == tyProc: - msg.addPragmaAndCallConvMismatch(formal, actual, conf) - case compatibleEffects(formal, actual) - of efCompat: discard - of efRaisesDiffer: - msg.add "\n.raise effects differ" - of efRaisesUnknown: - msg.add "\n.raise effect is 'can raise any'" - of efTagsDiffer: - msg.add "\n.tag effects differ" - of efTagsUnknown: - msg.add "\n.tag effect is 'any tag allowed'" - of efEffectsDelayed: - msg.add "\n.effectsOf annotations differ" - of efTagsIllegal: - msg.add "\n.notTag catched an illegal effect" - proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) = if formal.kind != tyError and actual.kind != tyError: let actualStr = typeToString(actual) @@ -1663,18 +1500,20 @@ proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: P msg.add "\n" msg.add " but expected '$1'" % x if verbose: msg.addDeclaredLoc(conf, formal) - var a = formal - var b = actual - if formal.kind == tyArray and actual.kind == tyArray: - a = formal[1] - b = actual[1] - processPragmaAndCallConvMismatch(msg, a, b, conf) - elif formal.kind == tySequence and actual.kind == tySequence: - a = formal[0] - b = actual[0] - processPragmaAndCallConvMismatch(msg, a, b, conf) - else: - processPragmaAndCallConvMismatch(msg, a, b, conf) + + if formal.kind == tyProc and actual.kind == tyProc: + case compatibleEffects(formal, actual) + of efCompat: discard + of efRaisesDiffer: + msg.add "\n.raise effects differ" + of efRaisesUnknown: + msg.add "\n.raise effect is 'can raise any'" + of efTagsDiffer: + msg.add "\n.tag effects differ" + of efTagsUnknown: + msg.add "\n.tag effect is 'any tag allowed'" + of efLockLevelsDiffer: + msg.add "\nlock levels differ" localError(conf, info, msg) proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool = @@ -1721,18 +1560,6 @@ proc isDefectException*(t: PType): bool = t = skipTypes(t[0], abstractPtrs) return false -proc isDefectOrCatchableError*(t: PType): bool = - var t = t.skipTypes(abstractPtrs) - while t.kind == tyObject: - if t.sym != nil and t.sym.owner != nil and - sfSystemModule in t.sym.owner.flags and - (t.sym.name.s == "Defect" or - t.sym.name.s == "CatchableError"): - return true - if t[0] == nil: break - t = skipTypes(t[0], abstractPtrs) - return false - proc isSinkTypeForParam*(t: PType): bool = # a parameter like 'seq[owned T]' must not be used only once, but its # elements must, so we detect this case here: @@ -1743,31 +1570,3 @@ proc isSinkTypeForParam*(t: PType): bool = result = false else: result = true - -proc lookupFieldAgain*(ty: PType; field: PSym): PSym = - var ty = ty - while ty != nil: - ty = ty.skipTypes(skipPtrs) - assert(ty.kind in {tyTuple, tyObject}) - result = lookupInRecord(ty.n, field.name) - if result != nil: break - ty = ty[0] - if result == nil: result = field - -proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool = - let t = t.skipTypes(abstractInst) - if t.kind == tyPtr: - let pointsTo = t[0].skipTypes(abstractInst) - case pointsTo.kind - of tyUncheckedArray: - result = pointsTo[0].kind == tyChar - of tyArray: - result = pointsTo[1].kind == tyChar and firstOrd(nil, pointsTo[0]) == 0 and - skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} - of tyChar: - result = allowPointerToChar - else: - discard - -proc lacksMTypeField*(typ: PType): bool {.inline.} = - (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index f59f63659..0da05d70d 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -9,18 +9,8 @@ import renderer, strutils, ast, types -when defined(nimPreviewSlimSystem): - import std/assertions - - const defaultParamSeparator* = "," -template mayNormalize(s: string): string = - if toNormalize: - s.nimIdentNormalize - else: - s - proc renderPlainSymbolName*(n: PNode): string = ## Returns the first non '*' nkIdent node from the tree. ## @@ -40,26 +30,24 @@ proc renderPlainSymbolName*(n: PNode): string = result = "" #internalError(n.info, "renderPlainSymbolName() with " & $n.kind) -proc renderType(n: PNode, toNormalize: bool): string = +proc renderType(n: PNode): string = ## Returns a string with the node type or the empty string. - ## This proc should be kept in sync with `toLangSymbols` from - ## ``lib/packages/docutils/dochelpers.nim``. case n.kind: - of nkIdent: result = mayNormalize(n.ident.s) - of nkSym: result = mayNormalize(typeToString(n.sym.typ)) + of nkIdent: result = n.ident.s + of nkSym: result = typeToString(n.sym.typ) of nkVarTy: if n.len == 1: - result = renderType(n[0], toNormalize) + result = renderType(n[0]) else: result = "var" of nkRefTy: if n.len == 1: - result = "ref." & renderType(n[0], toNormalize) + result = "ref." & renderType(n[0]) else: result = "ref" of nkPtrTy: if n.len == 1: - result = "ptr." & renderType(n[0], toNormalize) + result = "ptr." & renderType(n[0]) else: result = "ptr" of nkProcTy: @@ -69,53 +57,36 @@ proc renderType(n: PNode, toNormalize: bool): string = assert params.kind == nkFormalParams assert params.len > 0 result = "proc(" - for i in 1..= 3 let typePos = n.len - 2 - let typeStr = renderType(n[typePos], toNormalize) + let typeStr = renderType(n[typePos]) result = typeStr for i in 1..= 2 - result = renderType(n[0], toNormalize) & '[' - for i in 1.. 1: result.add ", " - result.add(renderType(n[i], toNormalize)) + result.add(renderType(n[i])) else: result = "" -proc renderParamNames*(n: PNode, toNormalize=false): seq[string] = - ## Returns parameter names of routine `n`. - doAssert n.kind == nkFormalParams - case n.kind - of nkFormalParams: - for i in 1.. 0 - var typeStr = renderType(n[typePos], toNormalize) + var typeStr = renderType(n[typePos]) if typeStr.len < 1 and n[typePos+1].kind != nkEmpty: # Try with the last node, maybe its a default value. let typ = n[typePos+1].typ @@ -140,8 +111,7 @@ proc renderParamTypes*(found: var seq[string], n: PNode, toNormalize=false) = found.add($n) #internalError(n.info, "renderParamTypes(found,n) with " & $n.kind) -proc renderParamTypes*(n: PNode, sep = defaultParamSeparator, - toNormalize=false): string = +proc renderParamTypes*(n: PNode, sep = defaultParamSeparator): string = ## Returns the types contained in `n` joined by `sep`. ## ## This proc expects to be passed as `n` the parameters of any callable. The @@ -150,10 +120,6 @@ proc renderParamTypes*(n: PNode, sep = defaultParamSeparator, ## other characters may appear too, like ``[]`` or ``|``. result = "" var found: seq[string] = @[] - renderParamTypes(found, n, toNormalize) + renderParamTypes(found, n) if found.len > 0: result = found.join(sep) - -proc renderOutType*(n: PNode, toNormalize=false): string = - assert n.kind == nkFormalParams - result = renderType(n[0], toNormalize) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index c83da3277..be327692f 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -28,13 +28,10 @@ ## See https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm ## for a high-level description of how borrow checking works. -import ast, types, lineinfos, options, msgs, renderer, typeallowed, modulegraphs +import ast, types, lineinfos, options, msgs, renderer, typeallowed from trees import getMagic, isNoSideEffectPragma, stupidStmtListExpr from isolation_check import canAlias -when defined(nimPreviewSlimSystem): - import std/assertions - type AbstractTime = distinct int @@ -54,14 +51,12 @@ type SubgraphFlag = enum isMutated, # graph might be mutated isMutatedDirectly, # graph is mutated directly by a non-var parameter. - isMutatedByVarParam, # graph is mutated by a var parameter. connectsConstParam # graph is connected to a non-var parameter. VarFlag = enum ownsData, preventCursor, isReassigned, - isConditionallyReassigned, viewDoesMutate, viewBorrowsFromConst @@ -103,9 +98,8 @@ type goals: set[Goal] unanalysableMutation: bool inAsgnSource, inConstructor, inNoSideEffectSection: int - inConditional, inLoop: int owner: PSym - g: ModuleGraph + config: ConfigRef proc mutationAfterConnection(g: MutationInfo): bool {.inline.} = #echo g.maxMutation.int, " ", g.minConnection.int, " ", g.param @@ -179,18 +173,12 @@ proc root(v: var Partitions; start: int): int = v.s[it].con = Connection(kind: dependsOn, parent: result) it = next -proc potentialMutation(v: var Partitions; s: PSym; level: int; info: TLineInfo) = +proc potentialMutation(v: var Partitions; s: PSym; info: TLineInfo) = let id = variableId(v, s) if id >= 0: let r = root(v, id) - let flags = if s.kind == skParam: - if isConstParam(s): - {isMutated, isMutatedDirectly} - elif s.typ.kind == tyVar and level <= 1: - # varParam[i] = v is different from varParam[i][] = v - {isMutatedByVarParam} - else: - {isMutated} + let flags = if s.kind == skParam and isConstParam(s): + {isMutated, isMutatedDirectly} else: {isMutated} @@ -349,44 +337,36 @@ proc pathExpr(node: PNode; owner: PSym): PNode = if result == nil and borrowFromConstExpr(n): result = n -const - RootEscapes = 1000 # in 'p(r)' we don't know what p does to our poor root. - # so we assume a high level of indirections - -proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) = +proc allRoots(n: PNode; result: var seq[PSym]; followDotExpr = true) = case n.kind of nkSym: if n.sym.kind in {skParam, skVar, skTemp, skLet, skResult, skForVar}: - result.add((n.sym, level)) + result.add(n.sym) - of nkDerefExpr, nkHiddenDeref: - allRoots(n[0], result, level+1) - of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, nkAddr, nkHiddenAddr: - allRoots(n[0], result, level) + of nkDotExpr, nkDerefExpr, nkBracketExpr, nkHiddenDeref, + nkCheckedFieldExpr, nkAddr, nkHiddenAddr: + if followDotExpr: + allRoots(n[0], result, followDotExpr) of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkCast, nkObjUpConv, nkObjDownConv: if n.len > 0: - allRoots(n.lastSon, result, level) + allRoots(n.lastSon, result, followDotExpr) of nkCaseStmt, nkObjConstr: for i in 1.. 1: - # XXX We really need the unwritten RFC here and distinguish between - # proc `[]`(x: var Container): var T # resizes the container - # and - # proc `[]`(x: Container): var T # only allows for slot mutation - allRoots(n[1], result, RootEscapes) + allRoots(n[1], result, followDotExpr) else: let m = getMagic(n) case m @@ -405,12 +385,12 @@ proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) = let paramType = typ.n[i].typ if not paramType.isCompileTimeOnly and not typ.sons[0].isEmptyType and canAlias(paramType, typ.sons[0]): - allRoots(it, result, RootEscapes) + allRoots(it, result, followDotExpr) else: - allRoots(it, result, RootEscapes) + allRoots(it, result, followDotExpr) of mSlice: - allRoots(n[1], result, level+1) + allRoots(n[1], result, followDotExpr) else: discard "harmless operation" else: @@ -419,6 +399,7 @@ proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) = proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = ## Analyse if 'n' is an expression that owns the data, if so mark 'dest' ## with 'ownsData'. + if n.typ == nil: return case n.kind of nkEmpty, nkCharLit..nkNilLit: # primitive literals including the empty are harmless: @@ -477,33 +458,30 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = destMightOwn(c, dest, n[0]) of nkCallKinds: - if n.typ != nil: - if hasDestructor(n.typ): - # calls do construct, what we construct must be destroyed, - # so dest cannot be a cursor: - dest.flags.incl ownsData - elif n.typ.kind in {tyLent, tyVar} and n.len > 1: - # we know the result is derived from the first argument: - var roots: seq[(PSym, int)] - allRoots(n[1], roots, RootEscapes) - for r in roots: - connect(c, dest.sym, r[0], n[1].info) + if hasDestructor(n.typ): + # calls do construct, what we construct must be destroyed, + # so dest cannot be a cursor: + dest.flags.incl ownsData + elif n.typ.kind in {tyLent, tyVar}: + # we know the result is derived from the first argument: + var roots: seq[PSym] + allRoots(n[1], roots) + for r in roots: + connect(c, dest.sym, r, n[1].info) - else: - let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone - # this list is subtle, we try to answer the question if after 'dest = f(src)' - # there is a connection betwen 'src' and 'dest' so that mutations to 'src' - # also reflect 'dest': - if magic in {mNone, mMove, mSlice, - mAppendStrCh, mAppendStrStr, mAppendSeqElem, - mArrToSeq, mOpenArrayToSeq}: - for i in 1..= 0: @@ -565,13 +539,13 @@ proc borrowingCall(c: var Partitions; destType: PType; n: PNode; i: int) = when false: let isView = directViewType(destType) == immutableView if n[0].kind == nkSym and n[0].sym.name.s == "[]=": - localError(c.g.config, n[i].info, "attempt to mutate an immutable view") + localError(c.config, n[i].info, "attempt to mutate an immutable view") for j in i+1..= 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" - else: - localError(c.g.config, dest.info, "attempt to mutate a borrowed location from an immutable view") - ]# + of immutableView: + if dest.kind == nkBracketExpr and dest[0].kind == nkHiddenDeref and + mutableParameter(dest[0][0]): + discard "remains a mutable location anyhow" else: - discard "nothing to do" + localError(c.config, dest.info, "attempt to mutate a borrowed location from an immutable view") + of noView: discard "nothing to do" proc containsPointer(t: PType): bool = proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} @@ -616,31 +580,30 @@ proc deps(c: var Partitions; dest, src: PNode) = if borrowChecking in c.goals: borrowingAsgn(c, dest, src) - var targets, sources: seq[(PSym, int)] - allRoots(dest, targets, 0) - allRoots(src, sources, 0) + var targets, sources: seq[PSym] + allRoots(dest, targets) + allRoots(src, sources) let destIsComplex = containsPointer(dest.typ) for t in targets: if dest.kind != nkSym and c.inNoSideEffectSection == 0: - potentialMutation(c, t[0], t[1], dest.info) + potentialMutation(c, t, dest.info) if destIsComplex: for s in sources: - connect(c, t[0], s[0], dest.info) + connect(c, t, s, dest.info) - if cursorInference in c.goals and src.kind != nkEmpty: + if cursorInference in c.goals and src.kind != nkEmpty: let d = pathExpr(dest, c.owner) if d != nil and d.kind == nkSym: let vid = variableId(c, d.sym) if vid >= 0: destMightOwn(c, c.s[vid], src) - for source in sources: - let s = source[0] + for s in sources: if s == d.sym: discard "assignments like: it = it.next are fine" - elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(c.g, s.typ): + elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(s.typ): # do not borrow from a global variable or from something with a # disabled assignment operator. c.s[vid].flags.incl preventCursor @@ -661,14 +624,20 @@ proc deps(c: var Partitions; dest, src: PNode) = when explainCursors: echo "D not a cursor ", d.sym, " reassignedTo ", c.s[srcid].reassignedTo c.s[vid].flags.incl preventCursor +const + nodesToIgnoreSet = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit, + nkTypeSection, nkProcDef, nkConverterDef, + nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, + nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr} proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) = if constParameters in c.goals and tfNoSideEffect in callee.flags: discard "we know there are no hidden mutations through an immutable parameter" elif c.inNoSideEffectSection == 0 and containsPointer(n.typ): - var roots: seq[(PSym, int)] - allRoots(n, roots, RootEscapes) - for r in roots: potentialMutation(c, r[0], r[1], n.info) + var roots: seq[PSym] + allRoots(n, roots) + for r in roots: potentialMutation(c, r, n.info) proc traverse(c: var Partitions; n: PNode) = inc c.abstractTime @@ -686,7 +655,7 @@ proc traverse(c: var Partitions; n: PNode) = for i in 0.. 0 and c.inLoop > 0: - # bug #17033: live ranges with loops and conditionals are too - # complex for our current analysis, so we prevent the cursorfication. - c.s[vid].flags.incl isConditionallyReassigned - proc computeLiveRanges(c: var Partitions; n: PNode) = # first pass: Compute live ranges for locals. # **Watch out!** We must traverse the tree like 'traverse' does @@ -811,7 +768,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = registerVariable(c, child[i]) #deps(c, child[i], last) - of nkAsgn, nkFastAsgn, nkSinkAsgn: + of nkAsgn, nkFastAsgn: computeLiveRanges(c, n[0]) computeLiveRanges(c, n[1]) if n[0].kind == nkSym: @@ -820,7 +777,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = if n[1].kind == nkSym and (c.s[vid].reassignedTo == 0 or c.s[vid].reassignedTo == n[1].sym.id): c.s[vid].reassignedTo = n[1].sym.id else: - markAsReassigned(c, vid) + c.s[vid].flags.incl isReassigned of nkSym: dec c.abstractTime @@ -847,7 +804,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = if not paramType.isCompileTimeOnly and paramType.kind == tyVar: let vid = variableId(c, it.sym) if vid >= 0: - markAsReassigned(c, vid) + c.s[vid].flags.incl isReassigned of nkAddr, nkHiddenAddr: computeLiveRanges(c, n[0]) @@ -865,23 +822,12 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = # while cond: # mutate(graph) # connect(graph, cursorVar) - inc c.inLoop for child in n: computeLiveRanges(c, child) - 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) - dec c.inConditional else: for child in n: computeLiveRanges(c, child) -proc computeGraphPartitions*(s: PSym; n: PNode; g: ModuleGraph; goals: set[Goal]): Partitions = - result = Partitions(owner: s, g: g, goals: goals) +proc computeGraphPartitions*(s: PSym; n: PNode; config: ConfigRef; goals: set[Goal]): Partitions = + result = Partitions(owner: s, config: config, goals: goals) if s.kind notin {skModule, skMacro}: let params = s.typ.n for i in 1.. safeLen: - stackTrace(c, tos, pc, formatErrorIndexBound(right, safeLen)) - - case collection.kind - of nkTupleConstr: # slice of a slice - let safeLen = collection[2].intVal - collection[1].intVal - rangeCheck(leftInd, rightInd, safeLen) - let - leftInd = leftInd + collection[1].intVal # Slice is from the start of the old - rightInd = rightInd + collection[1].intVal - - regs[ra].node = newTree( - nkTupleConstr, - collection[0], - newIntNode(nkIntLit, BiggestInt leftInd), - newIntNode(nkIntLit, BiggestInt rightInd) - ) - - else: - let safeLen = safeArrLen(collection) - 1 - rangeCheck(leftInd, rightInd, safeLen) - regs[ra].node = newTree( - nkTupleConstr, - collection, - newIntNode(nkIntLit, BiggestInt leftInd), - newIntNode(nkIntLit, BiggestInt rightInd) - ) - - of opcLdArr: # a = b[c] decodeBC(rkNode) @@ -726,24 +650,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int))) let idx = regs[rc].intVal.int let src = regs[rb].node - case src.kind - of nkTupleConstr: # refer to `of opcSlice` - let - left = src[1].intVal - right = src[2].intVal - realIndex = left + idx - if idx in 0..(right - left): - case src[0].kind - of nkStrKinds: - regs[ra].node = newIntNode(nkCharLit, ord src[0].strVal[int realIndex]) - of nkBracket: - regs[ra].node = src[0][int realIndex] - else: - stackTrace(c, tos, pc, "opcLdArr internal error") - else: - stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right)) - - of nkStrLit..nkTripleStrLit: + if src.kind in {nkStrLit..nkTripleStrLit}: if idx <% src.strVal.len: regs[ra].node = newNodeI(nkCharLit, c.debug[pc]) regs[ra].node.intVal = src.strVal[idx].ord @@ -760,31 +667,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int))) let idx = regs[rc].intVal.int let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[] - case src.kind - of nkTupleConstr: - let - left = src[1].intVal - right = src[2].intVal - realIndex = left + idx - if idx in 0..(right - left): # Refer to `opcSlice` - case src[0].kind - of nkStrKinds: - regs[ra] = takeCharAddress(c, src[0], realIndex, pc) - of nkBracket: - takeAddress regs[ra], src.sons[0].sons[realIndex] - else: - stackTrace(c, tos, pc, "opcLdArrAddr internal error") - else: - stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right)) + if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len: + regs[ra].nodeAddr = addr src.sons[idx] else: - if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len: - takeAddress regs[ra], src.sons[idx] - else: - stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1)) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1)) of opcLdStrIdx: decodeBC(rkInt) let idx = regs[rc].intVal.int - let s {.cursor.} = regs[rb].node.strVal + let s = regs[rb].node.strVal if idx <% s.len: regs[ra].intVal = s[idx].ord else: @@ -797,7 +687,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let idx = regs[rc].intVal.int let s = regs[rb].node.strVal.addr # or `byaddr` if idx <% s[].len: - regs[ra] = takeCharAddress(c, regs[rb].node, idx, pc) + # `makePtrType` not accessible from vm.nim + let typ = newType(tyPtr, nextTypeId c.idgen, c.module.owner) + typ.add getSysType(c.graph, c.debug[pc], tyChar) + let node = newNodeIT(nkIntLit, c.debug[pc], typ) # xxx nkPtrLit + node.intVal = cast[int](s[][idx].addr) + node.flags.incl nfIsPtr + regs[ra].node = node else: stackTrace(c, tos, pc, formatErrorIndexBound(idx, s[].len-1)) of opcWrArr: @@ -805,24 +701,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkNode) let idx = regs[rb].intVal.int let arr = regs[ra].node - case arr.kind - of nkTupleConstr: # refer to `opcSlice` - let - src = arr[0] - left = arr[1].intVal - right = arr[2].intVal - realIndex = left + idx - if idx in 0..(right - left): - case src.kind - of nkStrKinds: - src.strVal[int(realIndex)] = char(regs[rc].intVal) - of nkBracket: - src[int(realIndex)] = regs[rc].node - else: - stackTrace(c, tos, pc, "opcWrArr internal error") - else: - stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right)) - of {nkStrLit..nkTripleStrLit}: + if arr.kind in {nkStrLit..nkTripleStrLit}: if idx <% arr.strVal.len: arr.strVal[idx] = chr(regs[rc].intVal) else: @@ -834,7 +713,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdObj: # a = b.c decodeBC(rkNode) - let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[] + let src = regs[rb].node case src.kind of nkEmpty..nkNilLit: # for nkPtrLit, this could be supported in the future, use something like: @@ -857,11 +736,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of nkObjConstr: let n = src.sons[rc + 1] if n.kind == nkExprColonExpr: - takeAddress regs[ra], n.sons[1] + regs[ra].nodeAddr = addr n.sons[1] else: - takeAddress regs[ra], src.sons[rc + 1] + regs[ra].nodeAddr = addr src.sons[rc + 1] else: - takeAddress regs[ra], src.sons[rc] + regs[ra].nodeAddr = addr src.sons[rc] of opcWrObj: # a.b = c decodeBC(rkNode) @@ -888,7 +767,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNodeAddr) case regs[rb].kind of rkNode: - takeAddress regs[ra], regs[rb].node + regs[ra].nodeAddr = addr(regs[rb].node) of rkNodeAddr: # bug #14339 regs[ra].nodeAddr = regs[rb].nodeAddr else: @@ -924,10 +803,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # vmgen generates opcWrDeref, which means that we must dereference # twice. # TODO: This should likely be handled differently in vmgen. - let nAddr = regs[ra].nodeAddr - if nAddr[] == nil: stackTrace(c, tos, pc, "opcWrDeref internal error") # refs bug #16613 - if (nfIsRef notin nAddr[].flags and nfIsRef notin n.flags): nAddr[][] = n[] - else: nAddr[] = n + if (nfIsRef notin regs[ra].nodeAddr[].flags and + nfIsRef notin n.flags): + regs[ra].nodeAddr[][] = n[] + else: + regs[ra].nodeAddr[] = n of rkRegisterAddr: regs[ra].regAddr[] = regs[rc] of rkNode: # xxx: also check for nkRefTy as in opcLdDeref? @@ -980,29 +860,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLenSeq: decodeBImm(rkInt) #assert regs[rb].kind == nkBracket - let - high = (imm and 1) # discard flags - node = regs[rb].node + let high = (imm and 1) # discard flags if (imm and nimNodeFlag) != 0: # used by mNLen (NimNode.len) regs[ra].intVal = regs[rb].node.safeLen - high else: - case node.kind - of nkTupleConstr: # refer to `of opcSlice` - regs[ra].intVal = node[2].intVal - node[1].intVal + 1 - high - else: - # safeArrLen also return string node len - # used when string is passed as openArray in VM - regs[ra].intVal = node.safeArrLen - high - + # safeArrLen also return string node len + # used when string is passed as openArray in VM + regs[ra].intVal = regs[rb].node.safeArrLen - high of opcLenStr: decodeBImm(rkInt) assert regs[rb].kind == rkNode regs[ra].intVal = regs[rb].node.strVal.len - imm - of opcLenCstring: - decodeBImm(rkInt) - assert regs[rb].kind == rkNode - regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm of opcIncl: decodeB(rkNode) let b = regs[rb].regToNode @@ -1123,12 +992,6 @@ 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) @@ -1142,23 +1005,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = return t2.kind in PtrLikeKinds and n2.intVal == p1 else: return false - let rbReg = skipRegisterAddr(regs[rb]) - let rcReg = skipRegisterAddr(regs[rc]) - - if rbReg.kind == rkNodeAddr: - if rcReg.kind == rkNodeAddr: - ret = rbReg.nodeAddr == rcReg.nodeAddr + if regs[rb].kind == rkNodeAddr: + if regs[rc].kind == rkNodeAddr: + ret = regs[rb].nodeAddr == regs[rc].nodeAddr else: - ret = ptrEquality(rbReg.nodeAddr, rcReg.node) - elif rcReg.kind == rkNodeAddr: - ret = ptrEquality(rcReg.nodeAddr, rbReg.node) + ret = ptrEquality(regs[rb].nodeAddr, regs[rc].node) + elif regs[rc].kind == rkNodeAddr: + ret = ptrEquality(regs[rc].nodeAddr, regs[rb].node) else: - let nb = rbReg.node - let nc = rcReg.node + let nb = regs[rb].node + let nc = regs[rc].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): - ret = true + elif sameConstant(nb, nc): ret = true # this also takes care of procvar's, represented as nkTupleConstr, e.g. (nil, nil) elif nb.kind == nkIntLit and nc.kind == nkIntLit and nb.intVal == nc.intVal: # TODO: nkPtrLit let tb = nb.getTyp @@ -1172,8 +1031,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = strictSymEquality=true)) of opcSameNodeType: decodeBC(rkInt) - regs[ra].intVal = ord(regs[rb].node.typ.sameTypeOrNil(regs[rc].node.typ, {ExactTypeDescValues, ExactGenericParams})) - # The types should exactly match which is why we pass `{ExactTypeDescValues..ExactGcSafety}`. + regs[ra].intVal = ord(regs[rb].node.typ.sameTypeOrNil regs[rc].node.typ) of opcXor: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) @@ -1271,7 +1129,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = a.sym.ast.shallowCopy for i in 0.. 0 and - node[0].kind == nkNilLit and node[1].kind == nkNilLit)) + node.typ.callConv == ccClosure and node[0].kind == nkNilLit and + node[1].kind == nkNilLit)) of opcNBindSym: # cannot use this simple check # if dynamicBindSym notin c.config.features: @@ -1677,7 +1519,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = rb = instr.regB rc = instr.regC idx = int(regs[rb+rc-1].intVal) - callback = c.callbacks[idx] + callback = c.callbacks[idx].value args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[ptr UncheckedArray[TFullReg]](addr regs[0]), currentException: c.currentExceptionA, currentLineInfo: c.debug[pc]) @@ -1855,7 +1697,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[rb].node.kind != nkSym: stackTrace(c, tos, pc, "node is not a symbol") else: - regs[ra].node.strVal = $sigHash(regs[rb].node.sym, c.config) + regs[ra].node.strVal = $sigHash(regs[rb].node.sym) of opcSlurp: decodeB(rkNode) createStr regs[ra] @@ -1889,11 +1731,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = elif instr.opcode == opcNHint: message(c.config, info, hintUser, a.strVal) of opcParseExprToAst: - decodeBC(rkNode) + decodeB(rkNode) + # c.debug[pc].line.int - countLines(regs[rb].strVal) ? var error: string let ast = parseString(regs[rb].node.strVal, c.cache, c.config, - regs[rc].node.strVal, 0, - proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = + toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, + proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) {.nosinks.} = if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) if error.len > 0: @@ -1904,11 +1747,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: regs[ra].node = ast[0] of opcParseStmtToAst: - decodeBC(rkNode) + decodeB(rkNode) var error: string let ast = parseString(regs[rb].node.strVal, c.cache, c.config, - regs[rc].node.strVal, 0, - proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = + toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, + proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) {.nosinks.} = if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) if error.len > 0: @@ -1932,24 +1775,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of 1: # getLine regs[ra].node = newIntNode(nkIntLit, n.info.line.int) of 2: # getColumn - regs[ra].node = newIntNode(nkIntLit, n.info.col.int) + regs[ra].node = newIntNode(nkIntLit, n.info.col) else: internalAssert c.config, false regs[ra].node.info = n.info regs[ra].node.typ = n.typ - of opcNCopyLineInfo: + of opcNSetLineInfo: decodeB(rkNode) regs[ra].node.info = regs[rb].node.info - of opcNSetLineInfoLine: - decodeB(rkNode) - regs[ra].node.info.line = regs[rb].intVal.uint16 - of opcNSetLineInfoColumn: - decodeB(rkNode) - regs[ra].node.info.col = regs[rb].intVal.int16 - of opcNSetLineInfoFile: - decodeB(rkNode) - regs[ra].node.info.fileIndex = - fileInfoIdx(c.config, RelativeFile regs[rb].node.strVal) of opcEqIdent: decodeBC(rkInt) # aliases for shorter and easier to understand code below @@ -2074,6 +1907,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = dest.ident = regs[rb].node.ident else: stackTrace(c, tos, pc, errFieldXNotFound & "ident") + of opcNSetType: + decodeB(rkNode) + let b = regs[rb].node + internalAssert c.config, b.kind == nkSym and b.sym.kind == skType + internalAssert c.config, regs[ra].node != nil + regs[ra].node.typ = b.sym.typ of opcNSetStrVal: decodeB(rkNode) var dest = regs[ra].node @@ -2129,12 +1968,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.flags.incl nfIsRef of opcNccValue: decodeB(rkInt) - let destKey {.cursor.} = regs[rb].node.strVal + let destKey = regs[rb].node.strVal regs[ra].intVal = getOrDefault(c.graph.cacheCounters, destKey) of opcNccInc: let g = c.graph declBC() - let destKey {.cursor.} = regs[rb].node.strVal + let destKey = regs[rb].node.strVal let by = regs[rc].intVal let v = getOrDefault(g.cacheCounters, destKey) g.cacheCounters[destKey] = v+by @@ -2142,7 +1981,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNcsAdd: let g = c.graph declBC() - let destKey {.cursor.} = regs[rb].node.strVal + let destKey = regs[rb].node.strVal let val = regs[rc].node if not contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey] = newTree(nkStmtList, val) @@ -2152,7 +1991,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNcsIncl: let g = c.graph declBC() - let destKey {.cursor.} = regs[rb].node.strVal + let destKey = regs[rb].node.strVal let val = regs[rc].node if not contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey] = newTree(nkStmtList, val) @@ -2166,22 +2005,22 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNcsLen: let g = c.graph decodeB(rkInt) - let destKey {.cursor.} = regs[rb].node.strVal + let destKey = regs[rb].node.strVal regs[ra].intVal = if contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey].len else: 0 of opcNcsAt: let g = c.graph decodeBC(rkNode) let idx = regs[rc].intVal - let destKey {.cursor.} = regs[rb].node.strVal + let destKey = regs[rb].node.strVal if contains(g.cacheSeqs, destKey) and idx <% g.cacheSeqs[destKey].len: regs[ra].node = g.cacheSeqs[destKey][idx.int] else: stackTrace(c, tos, pc, formatErrorIndexBound(idx, g.cacheSeqs[destKey].len-1)) of opcNctPut: let g = c.graph - let destKey {.cursor.} = regs[ra].node.strVal - let key {.cursor.} = regs[instr.regB].node.strVal + let destKey = regs[ra].node.strVal + let key = regs[instr.regB].node.strVal let val = regs[instr.regC].node if not contains(g.cacheTables, destKey): g.cacheTables[destKey] = initBTree[string, PNode]() @@ -2193,14 +2032,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNctLen: let g = c.graph decodeB(rkInt) - let destKey {.cursor.} = regs[rb].node.strVal + let destKey = regs[rb].node.strVal regs[ra].intVal = if contains(g.cacheTables, destKey): g.cacheTables[destKey].len else: 0 of opcNctGet: let g = c.graph decodeBC(rkNode) - let destKey {.cursor.} = regs[rb].node.strVal - let key {.cursor.} = regs[rc].node.strVal + let destKey = regs[rb].node.strVal + let key = regs[rc].node.strVal if contains(g.cacheTables, destKey): if contains(g.cacheTables[destKey], key): regs[ra].node = getOrDefault(g.cacheTables[destKey], key) @@ -2211,7 +2050,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNctHasNext: let g = c.graph decodeBC(rkInt) - let destKey {.cursor.} = regs[rb].node.strVal + let destKey = regs[rb].node.strVal regs[ra].intVal = if g.cacheTables.contains(destKey): ord(btrees.hasNext(g.cacheTables[destKey], regs[rc].intVal.int)) @@ -2220,7 +2059,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNctNext: let g = c.graph decodeBC(rkNode) - let destKey {.cursor.} = regs[rb].node.strVal + let destKey = regs[rb].node.strVal let index = regs[rc].intVal if contains(g.cacheTables, destKey): let (k, v, nextIndex) = btrees.next(g.cacheTables[destKey], index.int) @@ -2238,6 +2077,20 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = while typ.kind == tyTypeDesc and typ.len > 0: typ = typ[0] createStr regs[ra] regs[ra].node.strVal = typ.typeToString(preferExported) + of opcMarshalLoad: + let ra = instr.regA + let rb = instr.regB + inc pc + let typ = c.types[c.code[pc].regBx - wordExcess] + putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config, c.idgen)) + of opcMarshalStore: + decodeB(rkNode) + inc pc + let typ = c.types[c.code[pc].regBx - wordExcess] + createStrKeepNode(regs[ra]) + when not defined(nimNoNilSeqs): + if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) + storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config) c.profiler.leave(c) @@ -2245,7 +2098,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = proc execute(c: PCtx, start: int): PNode = var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil) - newSeq(tos.slots, c.prc.regInfo.len) + newSeq(tos.slots, c.prc.maxSlots) result = rawExecute(c, start, tos).regToNode proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = @@ -2295,11 +2148,6 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode = internalAssert c.config, s.kind in {skLet, skVar} and sfGlobal in s.flags result = c.globals[s.position-1] -proc setGlobalValue*(c: PCtx; s: PSym, val: PNode) = - ## Does not do type checking so ensure the `val` matches the `s.typ` - internalAssert c.config, s.kind in {skLet, skVar} and sfGlobal in s.flags - c.globals[s.position-1] = val - include vmops proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) = @@ -2309,7 +2157,7 @@ proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) = else: refresh(PCtx graph.vm, module, idgen) -proc setupEvalGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = +proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = #var c = newEvalContext(module, emRepl) #c.features = {allowCast, allowInfiniteLoops} #pushStackFrame(c, newStackFrame()) @@ -2318,7 +2166,7 @@ proc setupEvalGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassC setupGlobalCtx(module, graph, idgen) result = PCtx graph.vm -proc interpreterCode*(c: PPassContext, n: PNode): PNode = +proc myProcess(c: PPassContext, n: PNode): PNode = let c = PCtx(c) # don't eval errornous code: if c.oldErrorCount == c.config.errorCounter: @@ -2328,13 +2176,15 @@ proc interpreterCode*(c: PPassContext, n: PNode): PNode = result = n c.oldErrorCount = c.config.errorCounter +proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = + result = myProcess(c, n) + +const evalPass* = makePass(myOpen, myProcess, myClose) + proc evalConstExprAux(module: PSym; idgen: IdGenerator; g: ModuleGraph; prc: PSym, n: PNode, mode: TEvalMode): PNode = - when defined(nimsuggest): - if g.config.expandDone(): - return n - #if g.config.errorCounter > 0: return n + if g.config.errorCounter > 0: return n let n = transformExpr(g, idgen, module, n) setupGlobalCtx(module, g, idgen) var c = PCtx g.vm @@ -2345,8 +2195,8 @@ proc evalConstExprAux(module: PSym; idgen: IdGenerator; assert c.code[start].opcode != opcEof when debugEchoCode: c.echoCode start var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil) - newSeq(tos.slots, c.prc.regInfo.len) - #for i in 0.. 0: return errorNode(idgen, module, n) + if g.config.errorCounter > 0: return errorNode(idgen, module, n) # XXX globalError() is ugly here, but I don't know a better solution for now inc(g.config.evalMacroCounter) diff --git a/compiler/vmconv.nim b/compiler/vmconv.nim index 2353feba8..7db3906c2 100644 --- a/compiler/vmconv.nim +++ b/compiler/vmconv.nim @@ -1,6 +1,4 @@ -import ast, idents, lineinfos, astalgo -import vmdef -import std/times +import ast template elementType*(T: typedesc): typedesc = typeof(block: @@ -30,10 +28,6 @@ proc toLit*[T](a: T): PNode = elif T is tuple: result = newTree(nkTupleConstr) for ai in fields(a): result.add toLit(ai) - elif T is seq: - result = newNode(nkBracket) - for ai in a: - result.add toLit(ai) elif T is object: result = newTree(nkObjConstr) result.add(newNode(nkEmpty)) @@ -45,12 +39,3 @@ proc toLit*[T](a: T): PNode = else: static: doAssert false, "not yet supported: " & $T # add as needed -proc toTimeLit*(a: Time, c: PCtx, obj: PNode, info: TLineInfo): PNode = - # probably refactor it into `toLit` in the future - result = newTree(nkObjConstr) - result.add(newNode(nkEmpty)) # can be changed to a symbol according to PType - for k, ai in fieldPairs(a): - let reti = newNode(nkExprColonExpr) - reti.add newSymNode(lookupInRecord(obj, getIdent(c.cache, k)), info) - reti.add ai.toLit - result.add reti diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index f369908ba..6d6c55250 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -10,8 +10,6 @@ ## This module contains the type definitions for the new evaluation engine. ## An instruction is 1-3 int32s in memory, it is a register based VM. -import std/[tables, strutils] - import ast, idents, options, modulegraphs, lineinfos type TInstrType* = uint64 @@ -81,7 +79,6 @@ type opcWrStrIdx, opcLdStrIdx, # a = b[c] opcLdStrIdxAddr, # a = addr(b[c]) - opcSlice, # toOpenArray(collection, left, right) opcAddInt, opcAddImmInt, @@ -89,7 +86,6 @@ type opcSubImmInt, opcLenSeq, opcLenStr, - opcLenCstring, opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt, opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, @@ -103,7 +99,7 @@ type opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcIsNil, opcOf, opcIs, - opcParseFloat, opcConv, opcCast, + opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcInvalidField, opcNarrowS, opcNarrowU, opcSignExtend, @@ -127,7 +123,7 @@ type opcNGetSize, opcNSetIntVal, - opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetStrVal, + opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal, opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym, opcNccValue, opcNccInc, opcNcsAdd, opcNcsIncl, opcNcsLen, opcNcsAt, @@ -141,8 +137,7 @@ type opcNError, opcNWarning, opcNHint, - opcNGetLineInfo, opcNCopyLineInfo, opcNSetLineInfoLine, - opcNSetLineInfoColumn, opcNSetLineInfoFile + opcNGetLineInfo, opcNSetLineInfo, opcEqIdent, opcStrToIdent, opcGetImpl, @@ -182,6 +177,7 @@ type opcNBindSym, opcNDynBindSym, opcSetType, # dest.typ = types[Bx] opcTypeTrait, + opcMarshalLoad, opcMarshalStore, opcSymOwner, opcSymIsInstantiationOf @@ -232,7 +228,8 @@ type PProc* = ref object blocks*: seq[TBlock] # blocks; temp data structure sym*: PSym - regInfo*: seq[tuple[inUse: bool, kind: TSlotKind]] + slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]] + maxSlots*: int VmArgs* = object ra*, rb*, rc*: Natural @@ -259,8 +256,7 @@ type traceActive*: bool loopIterations*: int comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces - callbacks*: seq[VmCallback] - callbackIndex*: Table[string, int] + callbacks*: seq[tuple[key: string, value: VmCallback]] errorFlag*: string cache*: IdentCache config*: ConfigRef @@ -268,11 +264,9 @@ type oldErrorCount*: int profiler*: Profiler templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst - vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC) - procToCodePos*: Table[int, int] PStackFrame* = ref TStackFrame - TStackFrame* {.acyclic.} = object + TStackFrame* = object prc*: PSym # current prc; proc that is evaluated slots*: seq[TFullReg] # parameters passed to the proc + locals; # parameters come first @@ -293,7 +287,7 @@ proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator PCtx(code: @[], debug: @[], globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM, - comesFromHeuristic: unknownLineInfo, callbacks: @[], callbackIndex: initTable[string, int](), errorFlag: "", + comesFromHeuristic: unknownLineInfo, callbacks: @[], errorFlag: "", cache: cache, config: g.config, graph: g, idgen: idgen) proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) = @@ -302,24 +296,15 @@ proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) = c.loopIterations = c.config.maxLoopIterationsVM c.idgen = idgen -proc reverseName(s: string): string = - result = newStringOfCap(s.len) - let y = s.split('.') - for i in 1..y.len: - result.add y[^i] - if i != y.len: - result.add '.' - proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} = result = c.callbacks.len - c.callbacks.add(callback) - c.callbackIndex[reverseName(name)] = result + c.callbacks.add((name, callback)) const firstABxInstr* = opcTJmp largeInstrs* = { # instructions which use 2 int32s instead of 1: - opcConv, opcCast, opcNewSeq, opcOf - } + opcSubStr, opcConv, opcCast, opcNewSeq, opcOf, + opcMarshalLoad, opcMarshalStore} slotSomeTemp* = slotTempUnknown relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 118a3031e..f8765c4dc 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -10,9 +10,6 @@ import ast, types, msgs, os, options, idents, lineinfos from pathutils import AbsoluteFile -when defined(nimPreviewSlimSystem): - import std/syncio - proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): string = try: var filename = parentDir(toFullPath(conf, info)) / file @@ -260,7 +257,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; result.add t.n[1].copyTree of tyPointer: result = atomicType("pointer", mPointer) of tyString: result = atomicType("string", mString) - of tyCstring: result = atomicType("cstring", mCstring) + of tyCString: result = atomicType("cstring", mCstring) of tyInt: result = atomicType("int", mInt) of tyInt8: result = atomicType("int8", mInt8) of tyInt16: result = atomicType("int16", mInt16) @@ -290,9 +287,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyAnd: result = mapTypeToBracket("and", mAnd, t, info) of tyOr: result = mapTypeToBracket("or", mOr, t, info) of tyNot: result = mapTypeToBracket("not", mNot, t, info) - of tyIterable: result = mapTypeToBracket("iterable", mIterableType, t, info) of tyAnything: result = atomicType("anything", mNone) - of tyInferred: result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion) + of tyInferred: assert false of tyStatic, tyFromExpr: if inst: if t.n != nil: result = t.n.copyTree @@ -303,9 +299,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; if t.n != nil: result.add t.n.copyTree of tyOwned: result = mapTypeToBracket("owned", mBuiltinType, t, info) - of tyConcept: - result = mapTypeToBracket("concept", mNone, t, info) - result.add t.n.copyTree + of tyOptDeprecated: doAssert false proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode = result = mapTypeToAstX(cache, t, info, idgen, inst=false, allowRecursionX=true) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 937d4b095..efb657d17 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -15,10 +15,11 @@ # 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: -# ```nim -# let s = a & b # no matter what, create fresh node -# s = a & b # no matter what, keep the node -# ``` +# +#.. code-block:: 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 @@ -26,19 +27,9 @@ # solves the opcLdConst vs opcAsgnConst issue. Of course whether we need # this copy depends on the involved types. -import tables - -when defined(nimPreviewSlimSystem): - import std/assertions - import - strutils, ast, types, msgs, renderer, vmdef, trees, - intsets, magicsys, options, lowerings, lineinfos, transf, astmsgs - -from modulegraphs import getBody - -when defined(nimCompilerStacktraceHints): - import std/stackframes + strutils, ast, types, msgs, renderer, vmdef, + intsets, magicsys, options, lowerings, lineinfos, transf const debugEchoCode* = defined(nimVMDebug) @@ -101,6 +92,11 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = let idx = x.regBx-wordExcess result.addf("\t$#\tr$#, $# ($#)", opc.toStr, x.regA, c.constants[idx].renderTree, $idx) + elif opc in {opcMarshalLoad, opcMarshalStore}: + let y = c.code[i+1] + result.addf("\t$#\tr$#, r$#, $#", opc.toStr, x.regA, x.regB, + c.types[y.regBx-wordExcess].typeToString) + inc i else: result.addf("\t$#\tr$#, $#", opc.toStr, x.regA, x.regBx-wordExcess) result.add("\t# ") @@ -190,7 +186,7 @@ proc getSlotKind(t: PType): TSlotKind = case t.skipTypes(abstractRange-{tyTypeDesc}).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: slotTempInt - of tyString, tyCstring: + of tyString, tyCString: slotTempStr of tyFloat..tyFloat128: slotTempFloat @@ -211,22 +207,22 @@ proc getFreeRegister(cc: PCtx; k: TSlotKind; start: int): TRegister = # we prefer the same slot kind here for efficiency. Unfortunately for # discardable return types we may not know the desired type. This can happen # for e.g. mNAdd[Multiple]: - for i in start..c.regInfo.len-1: - if c.regInfo[i].kind == k and not c.regInfo[i].inUse: - c.regInfo[i].inUse = true + for i in start..c.maxSlots-1: + if c.slots[i].kind == k and not c.slots[i].inUse: + c.slots[i].inUse = true return TRegister(i) # if register pressure is high, we re-use more aggressively: - if c.regInfo.len >= high(TRegister): - for i in start..c.regInfo.len-1: - if not c.regInfo[i].inUse: - c.regInfo[i] = (inUse: true, kind: k) + if c.maxSlots >= high(TRegister): + for i in start..c.maxSlots-1: + if not c.slots[i].inUse: + c.slots[i] = (inUse: true, kind: k) return TRegister(i) - if c.regInfo.len >= high(TRegister): + if c.maxSlots >= high(TRegister): globalError(cc.config, cc.bestEffort, "VM problem: too many registers required") - result = TRegister(max(c.regInfo.len, start)) - c.regInfo.setLen int(result)+1 - c.regInfo[result] = (inUse: true, kind: k) + result = TRegister(max(c.maxSlots, start)) + c.slots[result] = (inUse: true, kind: k) + c.maxSlots = result + 1 proc getTemp(cc: PCtx; tt: PType): TRegister = let typ = tt.skipTypesOrNil({tyStatic}) @@ -244,29 +240,29 @@ proc getTemp(cc: PCtx; tt: PType): TRegister = proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc - if c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}: + if c.slots[r].kind in {slotSomeTemp..slotTempComplex}: # this seems to cause https://github.com/nim-lang/Nim/issues/10647 - c.regInfo[r].inUse = false + c.slots[r].inUse = false proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister = # if register pressure is high, we re-use more aggressively: let c = cc.prc # we could also customize via the following (with proper caching in ConfigRef): # let highRegisterPressure = cc.config.getConfigVar("vm.highRegisterPressure", "40").parseInt - if c.regInfo.len >= HighRegisterPressure or c.regInfo.len+n >= high(TRegister): - for i in 0..c.regInfo.len-n: - if not c.regInfo[i].inUse: + if c.maxSlots >= HighRegisterPressure or c.maxSlots+n >= high(TRegister): + for i in 0..c.maxSlots-n: + if not c.slots[i].inUse: block search: for j in i+1..i+n-1: - if c.regInfo[j].inUse: break search + if c.slots[j].inUse: break search result = TRegister(i) - for k in result..result+n-1: c.regInfo[k] = (inUse: true, kind: kind) + for k in result..result+n-1: c.slots[k] = (inUse: true, kind: kind) return - if c.regInfo.len+n >= high(TRegister): + if c.maxSlots+n >= high(TRegister): globalError(cc.config, cc.bestEffort, "VM problem: too many registers required") - result = TRegister(c.regInfo.len) - setLen c.regInfo, c.regInfo.len+n - for k in result..result+n-1: c.regInfo[k] = (inUse: true, kind: kind) + result = TRegister(c.maxSlots) + inc c.maxSlots, n + for k in result..result+n-1: c.slots[k] = (inUse: true, kind: kind) proc freeTempRange(c: PCtx; start: TRegister, n: int) = for i in start..start+n-1: c.freeTemp(TRegister(i)) @@ -350,21 +346,21 @@ proc genWhile(c: PCtx; n: PNode) = c.patch(lab2) proc genBlock(c: PCtx; n: PNode; dest: var TDest) = - let oldRegisterCount = c.prc.regInfo.len + let oldRegisterCount = c.prc.maxSlots withBlock(n[0].sym): c.gen(n[1], dest) - for i in oldRegisterCount..= 0 and c.prc.regInfo[dest].kind >= slotTempUnknown + result = dest >= 0 and c.prc.slots[dest].kind >= slotTempUnknown proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = # asgn dest, a @@ -438,11 +434,14 @@ 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 + c.constants.add n.canonValue internalAssert c.config, result < regBxMax proc sameConstant*(a, b: PNode): bool = @@ -504,10 +503,7 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = let it = n[i] if it.len == 1: # else stmt: - if it[0].kind != nkNilLit or it[0].typ != nil: - # an nkNilLit with nil for typ implies there is no else branch, this - # avoids unused related errors as we've already consumed the dest - c.gen(it[0], dest) + c.gen(it[0], dest) else: let b = rawGenLiteral(c, it) c.gABx(it, opcBranch, tmp, b) @@ -580,7 +576,7 @@ proc genLit(c: PCtx; n: PNode; dest: var TDest) = # assignments now: #var opc = opcLdConst if dest < 0: dest = c.getTemp(n.typ) - #elif c.prc.regInfo[dest].kind == slotFixedVar: opc = opcAsgnConst + #elif c.prc.slots[dest].kind == slotFixedVar: opc = opcAsgnConst let lit = genLiteral(c, n) c.gABx(n, opcLdConst, dest, lit) @@ -640,19 +636,9 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: - let - dest = c.genx(le[0], {gfNode}) - idx = c.genIndex(le[1], le[0].typ) - collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}) - - case collTyp.kind - of tyString, tyCstring: - c.gABC(le, opcWrStrIdx, dest, idx, value) - of tyTuple: - c.gABC(le, opcWrObj, dest, int le[1].intVal, value) - else: - c.gABC(le, opcWrArr, dest, idx, value) - + let dest = c.genx(le[0], {gfNode}) + let idx = c.genIndex(le[1], le[0].typ) + c.gABC(le, opcWrArr, dest, idx, value) c.freeTemp(dest) c.freeTemp(idx) of nkCheckedFieldExpr: @@ -753,20 +739,18 @@ proc genNarrow(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: - let size = getSize(c.config, t) - if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(size*8)) - elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8): - c.gABC(n, opcNarrowS, dest, TRegister(size*8)) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) + elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): + c.gABC(n, opcNarrowS, dest, TRegister(t.size*8)) proc genNarrowU(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: - let size = getSize(c.config, t) if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or - (t.kind in {tyUInt, tyInt} and size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(size*8)) + (t.kind in {tyUInt, tyInt} and t.size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) @@ -823,7 +807,7 @@ proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = var r: TRegister = x+i-1 c.gen(n[i], r) c.gABC(n, opc, dest, x, n.len-1) - c.freeTempRange(x, n.len-1) + c.freeTempRange(x, n.len) proc isInt8Lit(n: PNode): bool = if n.kind in {nkCharLit..nkUInt64Lit}: @@ -844,20 +828,10 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.genNarrow(n, dest) proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = - let t2 = n.typ.skipTypes({tyDistinct}) - let targ2 = arg.typ.skipTypes({tyDistinct}) - - proc implicitConv(): bool = - if sameType(t2, targ2): return true - # xxx consider whether to use t2 and targ2 here - if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc: - # don't do anything for lambda lifting conversions: - return true - - if implicitConv(): + if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc: + # don't do anything for lambda lifting conversions: gen(c, arg, dest) return - let tmp = c.genx(arg) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) @@ -991,7 +965,7 @@ proc genBindSym(c: PCtx; n: PNode; dest: var TDest) = proc fitsRegister*(t: PType): bool = assert t != nil - t.skipTypes(abstractInst + {tyStatic} - {tyTypeDesc}).kind in { + t.skipTypes(abstractInst-{tyTypeDesc}).kind in { tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar} proc ldNullOpcode(t: PType): TOpcode = @@ -1034,9 +1008,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.genNarrow(n[1], d) c.genAsgnPatch(n[1], d) c.freeTemp(d) - of mOrd, mChr, mArrToSeq, mUnown: c.gen(n[1], dest) - of generatedMagics: - genCall(c, n, dest) + of mOrd, mChr, mArrToSeq, mUnown, mIsolate: c.gen(n[1], dest) of mNew, mNewFinalize: unused(c, n, dest) c.genNew(n) @@ -1060,22 +1032,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mLengthOpenArray, mLengthArray, mLengthSeq: genUnaryABI(c, n, dest, opcLenSeq) of mLengthStr: - case n[1].typ.skipTypes(abstractVarRange).kind - of tyString: genUnaryABI(c, n, dest, opcLenStr) - of tyCstring: genUnaryABI(c, n, dest, opcLenCstring) - else: doAssert false, $n[1].typ.kind - of mSlice: - var - d = c.genx(n[1]) - left = c.genIndex(n[2], n[1].typ) - right = c.genIndex(n[3], n[1].typ) - if dest < 0: dest = c.getTemp(n.typ) - c.gABC(n, opcNodeToReg, dest, d) - c.gABC(n, opcSlice, dest, left, right) - c.freeTemp(left) - c.freeTemp(right) - c.freeTemp(d) - + genUnaryABI(c, n, dest, opcLenStr) of mIncl, mExcl: unused(c, n, dest) var d = c.genx(n[1]) @@ -1106,11 +1063,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = genBinaryABC(c, n, dest, opcShlInt) # genNarrowU modified let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) - let size = getSize(c.config, t) - if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(size*8)) - elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8): - c.gABC(n, opcSignExtend, dest, TRegister(size*8)) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) + elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): + c.gABC(n, opcSignExtend, dest, TRegister(t.size*8)) of mAshrI: genBinaryABC(c, n, dest, opcAshrInt) of mBitandI: genBinaryABC(c, n, dest, opcBitandInt) of mBitorI: genBinaryABC(c, n, dest, opcBitorInt) @@ -1144,10 +1100,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = genUnaryABC(c, n, dest, opcBitnotInt) #genNarrowU modified, do not narrow signed types let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) - let size = getSize(c.config, t) - if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(size*8)) - of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) + of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, + mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: genConv(c, n, n[1], dest) of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) @@ -1189,8 +1145,11 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = d2 = c.genx(d2AsNode) var tmp1 = c.genx(n[1]) + tmp3 = c.genx(n[3]) c.gABC(n, opcParseFloat, dest, tmp1, d2) + c.gABC(n, opcParseFloat, tmp3) c.freeTemp(tmp1) + c.freeTemp(tmp3) c.genAsgnPatch(d2AsNode, d2) c.freeTemp(d2) of mReset: @@ -1198,9 +1157,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = var d = c.genx(n[1]) # XXX use ldNullOpcode() here? c.gABx(n, opcLdNull, d, c.genType(n[1].typ)) - c.gABC(n, opcNodeToReg, d, d) + c.gABx(n, opcNodeToReg, d, d) c.genAsgnPatch(n[1], d) - of mDefault, mZeroDefault: + of mDefault: if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ)) of mOf, mIs: @@ -1217,9 +1176,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(n.typ) let tmp = c.genx(n[1]) case n[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind: - of tyString: c.gABI(n, opcLenStr, dest, tmp, 1) - of tyCstring: c.gABI(n, opcLenCstring, dest, tmp, 1) - else: c.gABI(n, opcLenSeq, dest, tmp, 1) + of tyString, tyCString: + c.gABI(n, opcLenStr, dest, tmp, 1) + else: + c.gABI(n, opcLenSeq, dest, tmp, 1) c.freeTemp(tmp) of mEcho: unused(c, n, dest) @@ -1242,9 +1202,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = unused(c, n, dest) genBinaryStmtVar(c, n, opcAddSeqElem) of mParseExprToAst: - genBinaryABC(c, n, dest, opcParseExprToAst) + genUnaryABC(c, n, dest, opcParseExprToAst) of mParseStmtToAst: - genBinaryABC(c, n, dest, opcParseStmtToAst) + genUnaryABC(c, n, dest, opcParseStmtToAst) of mTypeTrait: let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n.typ) @@ -1313,6 +1273,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNSetIdent: unused(c, n, dest) genBinaryStmt(c, n, opcNSetIdent) + of mNSetType: + unused(c, n, dest) + genBinaryStmt(c, n, opcNSetType) of mNSetStrVal: unused(c, n, dest) genBinaryStmt(c, n, opcNSetStrVal) @@ -1332,19 +1295,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of "copyLineInfo": internalAssert c.config, n.len == 3 unused(c, n, dest) - genBinaryStmt(c, n, opcNCopyLineInfo) - of "setLine": - internalAssert c.config, n.len == 3 - unused(c, n, dest) - genBinaryStmt(c, n, opcNSetLineInfoLine) - of "setColumn": - internalAssert c.config, n.len == 3 - unused(c, n, dest) - genBinaryStmt(c, n, opcNSetLineInfoColumn) - of "setFile": - internalAssert c.config, n.len == 3 - unused(c, n, dest) - genBinaryStmt(c, n, opcNSetLineInfoFile) + genBinaryStmt(c, n, opcNSetLineInfo) else: internalAssert c.config, false of mNHint: unused(c, n, dest) @@ -1387,17 +1338,16 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = globalError(c.config, n.info, sizeOfLikeMsg("offsetof")) of mRunnableExamples: discard "just ignore any call to runnableExamples" - of mDestroy, mTrace: discard "ignore calls to the default destructor" + of mDestroy: discard "ignore calls to the default destructor" of mMove: let arg = n[1] let a = c.genx(arg) if dest < 0: dest = c.getTemp(arg.typ) gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a) # XXX use ldNullOpcode() here? - # Don't zero out the arg for now #17199 - # c.gABx(n, opcLdNull, a, c.genType(arg.typ)) - # c.gABx(n, opcNodeToReg, a, a) - # c.genAsgnPatch(arg, a) + c.gABx(n, opcLdNull, a, c.genType(arg.typ)) + c.gABx(n, opcNodeToReg, a, a) + c.genAsgnPatch(arg, a) c.freeTemp(a) of mNodeId: c.genUnaryABC(n, dest, opcNodeId) @@ -1405,10 +1355,29 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = # mGCref, mGCunref, globalError(c.config, n.info, "cannot generate code for: " & $m) +proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) = + ## Signature: proc to*[T](data: string): T + if dest < 0: dest = c.getTemp(n.typ) + var tmp = c.genx(n[1]) + c.gABC(n, opcMarshalLoad, dest, tmp) + c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ)) + c.freeTemp(tmp) + +proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) = + ## Signature: proc `$$`*[T](x: T): string + if dest < 0: dest = c.getTemp(n.typ) + var tmp = c.genx(n[1]) + c.gABC(n, opcMarshalStore, dest, tmp) + c.gABx(n, opcMarshalStore, 0, c.genType(n[1].typ)) + c.freeTemp(tmp) + 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] @@ -1440,13 +1409,13 @@ proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) = else: let tmp = c.genx(n[0], newflags) if dest < 0: dest = c.getTemp(n.typ) - if c.prc.regInfo[tmp].kind >= slotTempUnknown: + if c.prc.slots[tmp].kind >= slotTempUnknown: gABC(c, n, opcAddrNode, dest, tmp) # hack ahead; in order to fix bug #1781 we mark the temporary as # permanent, so that it's not used for anything else: - c.prc.regInfo[tmp].kind = slotTempPerm + c.prc.slots[tmp].kind = slotTempPerm # XXX this is still a hack - #message(c.congig, n.info, warnUser, "suspicious opcode used") + #message(n.info, warnUser, "suspicious opcode used") else: gABC(c, n, opcAddrReg, dest, tmp) c.freeTemp(tmp) @@ -1500,7 +1469,7 @@ proc checkCanEval(c: PCtx; n: PNode) = # proc foo() = var x ... let s = n.sym if {sfCompileTime, sfGlobal} <= s.flags: return - if compiletimeFFI in c.config.features and s.importcCondVar: return + if s.importcCondVar: return if s.kind in {skVar, skTemp, skLet, skParam, skResult} and not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl: # little hack ahead for bug #12612: assume gensym'ed variables @@ -1535,16 +1504,12 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode; proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: - let - dest = c.genx(le[0], {gfNode}) - idx = c.genIndex(le[1], le[0].typ) - tmp = c.genx(ri) - collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}) - case collTyp.kind - of tyString, tyCstring: + let dest = c.genx(le[0], {gfNode}) + let idx = c.genIndex(le[1], le[0].typ) + let tmp = c.genx(ri) + if le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { + tyString, tyCString}: c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp) - of tyTuple: - c.preventFalseAlias(le, opcWrObj, dest, int le[1].intVal, tmp) else: c.preventFalseAlias(le, opcWrArr, dest, idx, tmp) c.freeTemp(tmp) @@ -1605,20 +1570,11 @@ proc genTypeLit(c: PCtx; t: PType; dest: var TDest) = n.typ = t genLit(c, n, dest) -proc isEmptyBody(n: PNode): bool = - case n.kind - of nkStmtList: - for i in 0..= slotSomeTemp and + let requiresCopy = c.prc.slots[dest].kind >= slotSomeTemp and gfIsParam notin flags genAsgn(c, dest, n, requiresCopy) else: @@ -1711,13 +1667,15 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) else: - #message(c.config, n.info, warnUser, "argh") + #message(n.info, warnUser, "argh") #echo "FLAGS ", flags, " ", fitsRegister(n.typ), " ", typeToString(n.typ) c.gABC(n, opc, dest, a, b) c.freeTemp(a) c.freeTemp(b) -proc genObjAccessAux(c: PCtx; n: PNode; a, b: int, dest: var TDest; flags: TGenFlags) = +proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = + let a = c.genx(n[0], flags) + let b = genField(c, n[1]) if dest < 0: dest = c.getTemp(n.typ) if {gfNodeAddr} * flags != {}: c.gABC(n, opcLdObjAddr, dest, a, b) @@ -1730,11 +1688,6 @@ proc genObjAccessAux(c: PCtx; n: PNode; a, b: int, dest: var TDest; flags: TGenF c.gABC(n, opcLdObj, dest, a, b) c.freeTemp(a) -proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = - genObjAccessAux(c, n, c.genx(n[0], flags), genField(c, n[1]), dest, flags) - - - proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = internalAssert c.config, n.kind == nkCheckedFieldExpr # nkDotExpr to access the requested field @@ -1765,14 +1718,12 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags let lab1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs) c.freeTemp(rs) let strType = getSysType(c.graph, n.info, tyString) - var msgReg: TDest = c.getTemp(strType) - let fieldName = $accessExpr[1] - let msg = genFieldDefect(c.config, fieldName, disc.sym) - let strLit = newStrNode(msg, accessExpr[1].info) + var fieldNameRegister: TDest = c.getTemp(strType) + let strLit = newStrNode($accessExpr[1], accessExpr[1].info) strLit.typ = strType - c.genLit(strLit, msgReg) - c.gABC(n, opcInvalidField, msgReg, discVal) - c.freeTemp(msgReg) + c.genLit(strLit, fieldNameRegister) + c.gABC(n, opcInvalidField, fieldNameRegister) + c.freeTemp(fieldNameRegister) c.patch(lab1) proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = @@ -1802,13 +1753,10 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind - case arrayType - of tyString, tyCstring: + if arrayType in {tyString, tyCString}: let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx genArrAccessOpcode(c, n, dest, opc, flags) - of tyTuple: - c.genObjAccessAux(n, c.genx(n[0], flags), int n[1].intVal, dest, flags) - of tyTypeDesc: + elif arrayType == tyTypeDesc: c.genTypeLit(n.typ, dest) else: let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr @@ -1828,9 +1776,7 @@ proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currP of nkSym: let field = newNodeI(nkExprColonExpr, result.info) field.add(obj) - let value = getNullValue(obj.sym.typ, result.info, conf) - value.flags.incl nfUseDefaultField - field.add(value) + field.add(getNullValue(obj.sym.typ, result.info, conf)) result.add field doAssert obj.sym.position == currPosition inc currPosition @@ -1845,7 +1791,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIT(nkFloatLit, info, t) - of tyCstring, tyString: + of tyCString, tyString: result = newNodeIT(nkStrLit, info, t) result.strVal = "" of tyVar, tyLent, tyPointer, tyPtr, tyUntyped, @@ -1895,11 +1841,11 @@ proc genVarSection(c: PCtx; n: PNode) = checkCanEval(c, a[0]) if s.isGlobal: if s.position == 0: - if importcCond(c, s): c.importcSym(a.info, s) + if importcCond(s): c.importcSym(a.info, s) else: let sa = getNullValue(s.typ, a.info, c.config) #if s.ast.isNil: getNullValue(s.typ, a.info) - #else: s.ast + #else: canonValue(s.ast) assert sa.kind != nkCall c.globals.add(sa) s.position = c.globals.len @@ -1909,19 +1855,6 @@ proc genVarSection(c: PCtx; n: PNode) = c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) - elif not importcCondVar(s) and not (s.typ.kind == tyProc and s.typ.callConv == ccClosure) and - sfPure notin s.flags: # fixes #10938 - # there is a pre-existing issue with closure types in VM - # if `(var s: proc () = default(proc ()); doAssert s == nil)` works for you; - # you might remove the second condition. - # the problem is that closure types are tuples in VM, but the types of its children - # shouldn't have the same type as closure types. - let tmp = c.genx(a[0], {gfNodeAddr}) - let sa = getNullValue(s.typ, a.info, c.config) - let val = c.genx(sa) - c.genAdditionalCopy(sa, opcWrDeref, tmp, 0, val) - c.freeTemp(val) - c.freeTemp(tmp) else: setSlot(c, s) if a[2].kind == nkEmpty: @@ -2024,33 +1957,26 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = proc genProc*(c: PCtx; s: PSym): int -proc toKey(s: PSym): string = +proc matches(s: PSym; x: string): bool = + let y = x.split('.') var s = s - while s != nil: - result.add s.name.s - if s.owner != nil: - if sfFromGeneric in s.flags: - s = s.owner.owner - else: - s = s.owner - result.add "." - else: - break + for i in 1..y.len: + if s == nil or (y[^i].cmpIgnoreStyle(s.name.s) != 0 and y[^i] != "*"): + return false + s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner + result = true proc procIsCallback(c: PCtx; s: PSym): bool = if s.offset < -1: return true - let key = toKey(s) - if c.callbackIndex.contains(key): - let index = c.callbackIndex[key] - doAssert s.offset == -1 - s.offset = -2 - index - result = true - else: - result = false + var i = -2 + for key, value in items(c.callbacks): + if s.matches(key): + doAssert s.offset == -1 + s.offset = i + return true + dec i proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = - when defined(nimCompilerStacktraceHints): - setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags case n.kind of nkSym: let s = n.sym @@ -2063,14 +1989,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure: globalError(c.config, n.info, "Closure iterators are not supported by VM!") if procIsCallback(c, s): discard - elif importcCond(c, s): c.importcSym(n.info, s) + elif importcCond(s): c.importcSym(n.info, s) genLit(c, n, dest) of skConst: - let constVal = if s.astdef != nil: s.astdef else: s.typ.n - if dontInlineConstant(n, constVal): - genLit(c, constVal, dest) - else: - gen(c, constVal, dest) + let constVal = if s.ast != nil: s.ast else: s.typ.n + gen(c, constVal, dest) of skEnumField: # we never reach this case - as of the time of this comment, # skEnumField is folded to an int in semfold.nim, but this code @@ -2098,6 +2021,12 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = elif s.kind == skMethod: localError(c.config, n.info, "cannot call method " & s.name.s & " at compile time") + elif matches(s, "stdlib.marshal.to"): + # XXX marshal load&store should not be opcodes, but use the + # general callback mechanisms. + genMarshalLoad(c, n, dest) + elif matches(s, "stdlib.marshal.$$"): + genMarshalStore(c, n, dest) else: genCall(c, n, dest) clearDest(c, n, dest) @@ -2114,7 +2043,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkNilLit: if not n.typ.isEmptyType: genLit(c, getNullValue(n.typ, n.info, c.config), dest) else: unused(c, n, dest) - of nkAsgn, nkFastAsgn, nkSinkAsgn: + of nkAsgn, nkFastAsgn: unused(c, n, dest) genAsgn(c, n[0], n[1], n.kind == nkAsgn) of nkDotExpr: genObjAccess(c, n, dest, flags) @@ -2179,8 +2108,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = else: dest = tmp0 of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, - nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, - nkMixinStmt, nkBindStmt: + nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt: unused(c, n, dest) of nkStringToCString, nkCStringToString: gen(c, n[0], dest) @@ -2236,10 +2164,10 @@ proc genExpr*(c: PCtx; n: PNode, requiresValue = true): int = proc genParams(c: PCtx; params: PNode) = # res.sym.position is already 0 - setLen(c.prc.regInfo, max(params.len, 1)) - c.prc.regInfo[0] = (inUse: true, kind: slotFixedVar) + c.prc.slots[0] = (inUse: true, kind: slotFixedVar) for i in 1..= effectListLen and fn.typ.n[0][effectIndex] != nil: + var list = newNodeI(nkBracket, fn.info) for e in fn.typ.n[0][effectIndex]: list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info, c.idgen) - else: - list.add newIdentNode(getIdent(c.cache, "UncomputedEffects"), fn.info) - - setResult(a, list) + setResult(a, list) registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) = 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) @@ -367,40 +290,3 @@ proc registerAdditionalOps*(c: PCtx) = let fn = getNode(a, 0) setResult(a, (fn.typ != nil and tfNoSideEffect in fn.typ.flags) or (fn.kind == nkSym and fn.sym.kind == skFunc)) - - registerCallback c, "stdlib.typetraits.hasClosureImpl", proc (a: VmArgs) = - let fn = getNode(a, 0) - setResult(a, fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure)) - - registerCallback c, "stdlib.formatfloat.addFloatRoundtrip", proc(a: VmArgs) = - let p = a.getVar(0) - let x = a.getFloat(1) - addFloatRoundtrip(p.strVal, x) - - registerCallback c, "stdlib.formatfloat.addFloatSprintf", proc(a: VmArgs) = - let p = a.getVar(0) - let x = a.getFloat(1) - addFloatSprintf(p.strVal, x) - - registerCallback c, "stdlib.strutils.formatBiggestFloat", proc(a: VmArgs) = - setResult(a, formatBiggestFloat(a.getFloat(0), FloatFormatMode(a.getInt(1)), - a.getInt(2), chr(a.getInt(3)))) - - wrapIterator("stdlib.envvars.envPairsImplSeq"): envPairs() - - registerCallback c, "stdlib.marshal.toVM", proc(a: VmArgs) = - let typ = a.getNode(0).typ - case typ.kind - of tyInt..tyInt64, tyUInt..tyUInt64: - setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).intVal) - of tyFloat..tyFloat128: - setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).floatVal) - else: - setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen)) - - registerCallback c, "stdlib.marshal.loadVM", proc(a: VmArgs) = - let typ = a.getNode(0).typ - let p = a.getReg(1) - var res: string - storeAny(res, typ, regToNode(p[]), c.config) - setResult(a, res) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 29a50ac52..170d04df1 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -17,101 +17,95 @@ type TSpecialWord* = enum wInvalid = "", wAddr = "addr", wAnd = "and", wAs = "as", wAsm = "asm", - wBind = "bind", wBlock = "block", wBreak = "break", wCase = "case", wCast = "cast", - wConcept = "concept", wConst = "const", wContinue = "continue", wConverter = "converter", + wBind = "bind", wBlock = "block", wBreak = "break", wCase = "case", wCast = "cast", + wConcept = "concept", wConst = "const", wContinue = "continue", wConverter = "converter", wDefer = "defer", wDiscard = "discard", wDistinct = "distinct", wDiv = "div", wDo = "do", - wElif = "elif", wElse = "else", wEnd = "end", wEnum = "enum", wExcept = "except", - wExport = "export", wFinally = "finally", wFor = "for", wFrom = "from", wFunc = "func", - wIf = "if", wImport = "import", wIn = "in", wInclude = "include", wInterface = "interface", + wElif = "elif", wElse = "else", wEnd = "end", wEnum = "enum", wExcept = "except", + wExport = "export", wFinally = "finally", wFor = "for", wFrom = "from", wFunc = "func", + wIf = "if", wImport = "import", wIn = "in", wInclude = "include", wInterface = "interface", wIs = "is", wIsnot = "isnot", wIterator = "iterator", wLet = "let", wMacro = "macro", - wMethod = "method", wMixin = "mixin", wMod = "mod", wNil = "nil", wNot = "not", wNotin = "notin", - wObject = "object", wOf = "of", wOr = "or", wOut = "out", wProc = "proc", wPtr = "ptr", - wRaise = "raise", wRef = "ref", wReturn = "return", wShl = "shl", wShr = "shr", wStatic = "static", - wTemplate = "template", wTry = "try", wTuple = "tuple", wType = "type", wUsing = "using", + wMethod = "method", wMixin = "mixin", wMod = "mod", wNil = "nil", wNot = "not", wNotin = "notin", + wObject = "object", wOf = "of", wOr = "or", wOut = "out", wProc = "proc", wPtr = "ptr", + wRaise = "raise", wRef = "ref", wReturn = "return", wShl = "shl", wShr = "shr", wStatic = "static", + wTemplate = "template", wTry = "try", wTuple = "tuple", wType = "type", wUsing = "using", wVar = "var", wWhen = "when", wWhile = "while", wXor = "xor", wYield = "yield", wColon = ":", wColonColon = "::", wEquals = "=", wDot = ".", wDotDot = "..", wStar = "*", wMinus = "-", - wMagic = "magic", wThread = "thread", wFinal = "final", wProfiler = "profiler", + wMagic = "magic", wThread = "thread", wFinal = "final", wProfiler = "profiler", wMemTracker = "memtracker", wObjChecks = "objchecks", - wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine", - wCursor = "cursor", wNoalias = "noalias", wEffectsOf = "effectsOf", - wUncheckedAssign = "uncheckedAssign", + wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine", + wCursor = "cursor", wNoalias = "noalias", - wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor", - wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp", - wCppNonPod = "cppNonPod", - wImportObjC = "importobjc", wImportCompilerProc = "importCompilerProc", - wImportc = "importc", wImportJs = "importjs", wExportc = "exportc", wExportCpp = "exportcpp", + wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor", + wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp", + wImportObjC = "importobjc", wImportCompilerProc = "importcompilerproc", + wImportc = "importc", wImportJs = "importjs", wExportc = "exportc", wExportCpp = "exportcpp", wExportNims = "exportnims", wIncompleteStruct = "incompleteStruct", # deprecated wCompleteStruct = "completeStruct", wRequiresInit = "requiresInit", wAlign = "align", wNodecl = "nodecl", wPure = "pure", wSideEffect = "sideEffect", wHeader = "header", wNoSideEffect = "noSideEffect", wGcSafe = "gcsafe", wNoreturn = "noreturn", wNosinks = "nosinks", wMerge = "merge", wLib = "lib", wDynlib = "dynlib", - wCompilerProc = "compilerproc", wCore = "core", wProcVar = "procvar", - wBase = "base", wUsed = "used", wFatal = "fatal", wError = "error", wWarning = "warning", - wHint = "hint", - wWarningAsError = "warningAsError", - wHintAsError = "hintAsError", - wLine = "line", wPush = "push", - wPop = "pop", wDefine = "define", wUndef = "undef", wLineDir = "lineDir", + wCompilerProc = "compilerproc", wCore = "core", wProcVar = "procvar", + wBase = "base", wUsed = "used", wFatal = "fatal", wError = "error", wWarning = "warning", + wHint = "hint", wWarningAsError = "warningAsError", wLine = "line", wPush = "push", + wPop = "pop", wDefine = "define", wUndef = "undef", wLineDir = "lineDir", wStackTrace = "stackTrace", wLineTrace = "lineTrace", wLink = "link", wCompile = "compile", - wLinksys = "linksys", wDeprecated = "deprecated", wVarargs = "varargs", wCallconv = "callconv", - wDebugger = "debugger", wNimcall = "nimcall", wStdcall = "stdcall", wCdecl = "cdecl", + wLinksys = "linksys", wDeprecated = "deprecated", wVarargs = "varargs", wCallconv = "callconv", + wDebugger = "debugger", wNimcall = "nimcall", wStdcall = "stdcall", wCdecl = "cdecl", wSafecall = "safecall", wSyscall = "syscall", wInline = "inline", wNoInline = "noinline", wFastcall = "fastcall", wThiscall = "thiscall", wClosure = "closure", wNoconv = "noconv", - wOn = "on", wOff = "off", wChecks = "checks", wRangeChecks = "rangeChecks", + wOn = "on", wOff = "off", wChecks = "checks", wRangeChecks = "rangeChecks", wBoundChecks = "boundChecks", wOverflowChecks = "overflowChecks", wNilChecks = "nilChecks", - wFloatChecks = "floatChecks", wNanChecks = "nanChecks", wInfChecks = "infChecks", + wFloatChecks = "floatChecks", wNanChecks = "nanChecks", wInfChecks = "infChecks", wStyleChecks = "styleChecks", wStaticBoundchecks = "staticBoundChecks", wNonReloadable = "nonReloadable", wExecuteOnReload = "executeOnReload", - wAssertions = "assertions", wPatterns = "patterns", wTrMacros = "trmacros", + wAssertions = "assertions", wPatterns = "patterns", wTrMacros = "trmacros", wSinkInference = "sinkInference", wWarnings = "warnings", - wHints = "hints", wOptimization = "optimization", wRaises = "raises", + wHints = "hints", wOptimization = "optimization", wRaises = "raises", wWrites = "writes", wReads = "reads", wSize = "size", wEffects = "effects", wTags = "tags", - wForbids = "forbids", wRequires = "requires", wEnsures = "ensures", wInvariant = "invariant", + 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", wNoRewrite = "norewrite", wNoDestroy = "nodestroy", wPragma = "pragma", wCompileTime = "compileTime", wNoInit = "noinit", wPassc = "passc", wPassl = "passl", - wLocalPassc = "localPassC", wBorrow = "borrow", wDiscardable = "discardable", + wLocalPassc = "localPassC", wBorrow = "borrow", wDiscardable = "discardable", wFieldChecks = "fieldChecks", wSubsChar = "subschar", wAcyclic = "acyclic", wShallow = "shallow", wUnroll = "unroll", wLinearScanEnd = "linearScanEnd", - wComputedGoto = "computedGoto", wExperimental = "experimental", wDoctype = "doctype", - wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty", + wComputedGoto = "computedGoto", wInjectStmt = "injectStmt", wExperimental = "experimental", + 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", wSystemRaisesDefect = "systemRaisesDefect", - wRedefine = "redefine", wCallsite = "callsite", + wLiftLocals = "liftlocals", - wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", - wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default", - wDelete = "delete", wDouble = "double", wDynamicCast = "dynamic_cast", + 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", 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", - 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", + 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", + 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 = "wchar_t", + wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual", + wVoid = "void", wVolatile = "volatile", wWchar_t = "wchar_t", - wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", + wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", wNullptr = "nullptr", wNoexcept = "noexcept", - wThreadLocal = "thread_local", wStaticAssert = "static_assert", - wChar16 = "char16_t", wChar32 = "char32_t", + wThread_local = "thread_local", wStatic_assert = "static_assert", + wChar16_t = "char16_t", wChar32_t = "char32_t", wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr", wInOut = "inout", wByCopy = "bycopy", wByRef = "byref", wOneWay = "oneway", - wBitsize = "bitsize", wImportHidden = "all", + wBitsize = "bitsize" TSpecialWords* = set[TSpecialWord] @@ -130,7 +124,20 @@ const wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing} -from std/enumutils import genEnumCaseStmt -from strutils import normalize -proc findStr*[T: enum](a, b: static[T], s: string, default: T): T = - genEnumCaseStmt(T, s, default, ord(a), ord(b), normalize) +const enumUtilsExist = compiles: + import std/enumutils + +when enumUtilsExist: + from std/enumutils import genEnumCaseStmt + from strutils import normalize + proc findStr*[T: enum](a, b: static[T], s: string, default: T): T = + genEnumCaseStmt(T, s, default, ord(a), ord(b), normalize) + +else: + from strutils import cmpIgnoreStyle + proc findStr*[T: enum](a, b: static[T], s: string, default: T): T {.deprecated.} = + # used for compiler bootstrapping only + for i in a..b: + if cmpIgnoreStyle($i, s) == 0: + return i + result = default \ No newline at end of file diff --git a/config/build_config.txt b/config/build_config.txt deleted file mode 100644 index 66390e695..000000000 --- a/config/build_config.txt +++ /dev/null @@ -1,5 +0,0 @@ -nim_comment="key-value pairs for windows/posix bootstrapping build scripts" -nim_csourcesDir=csources_v2 -nim_csourcesUrl=https://github.com/nim-lang/csources_v2.git -nim_csourcesBranch=master -nim_csourcesHash=86742fb02c6606ab01a532a0085784effb2e753e diff --git a/config/config.nims b/config/config.nims index 4f3e2b13f..24c790168 100644 --- a/config/config.nims +++ b/config/config.nims @@ -1,24 +1,5 @@ # this config.nims also needs to exist to prevent future regressions, see #9990 -cppDefine "errno" -cppDefine "unix" - -# mangle the macro names in nimbase.h -cppDefine "NAN_INFINITY" -cppDefine "INF" -cppDefine "NAN" - -when defined(nimStrictMode): - # xxx add more flags here, and use `-d:nimStrictMode` in more contexts in CI. - - # pending bug #14246, enable this: - # when defined(nimHasWarningAsError): - # switch("warningAsError", "UnusedImport") - - when defined(nimHasHintAsError): - # switch("hint", "ConvFromXtoItselfNotNeeded") - switch("hintAsError", "ConvFromXtoItselfNotNeeded") - # future work: XDeclaredButNotUsed - -switch("define", "nimVersion:" & NimVersion) - +when defined(nimHasCppDefine): + cppDefine "errno" + cppDefine "unix" diff --git a/config/nim.cfg b/config/nim.cfg index ef93ec99e..a33a2f0a9 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -2,7 +2,6 @@ # (c) 2017 Andreas Rumpf # Feel free to edit the default values as you need. -# See https://nim-lang.org/docs/nimc.html # You may set environment variables with # @putenv "key" "val" @@ -15,31 +14,14 @@ cc = gcc --parallel_build: "0" # 0 to auto-detect number of processors hint[LineTooLong]=off -@if nimHasAmbiguousEnumHint: - # not needed if hint is a style check - hint[AmbiguousEnum]=off -@end #hint[XDeclaredButNotUsed]=off -threads:on -define:nimPreviewRangeDefault - # Examples of how to setup a cross-compiler: -# Nim can target architectures and OSes different than the local host -# Syntax: ..gcc.exe = "" -# ..gcc.linkerexe = "" -# ARM e.g. Raspberry Pi 2: gcc-arm-linux-gnueabihf package on Debian/Ubuntu +# Cross-compiling for Raspberry Pi. +# (This compiler is available in gcc-arm-linux-gnueabihf package on Ubuntu) arm.linux.gcc.exe = "arm-linux-gnueabihf-gcc" arm.linux.gcc.linkerexe = "arm-linux-gnueabihf-gcc" -# ARM64/aarch64 e.g. Raspberry Pi 3: gcc-aarch64-linux-gnu package on Debian/Ubuntu -arm64.linux.gcc.exe = "aarch64-linux-gnu-gcc" -arm64.linux.gcc.linkerexe = "aarch64-linux-gnu-gcc" -# RISC-V: gcc-riscv64-linux-gnu package on Debian/Ubuntu -riscv32.linux.gcc.exe = "riscv64-linux-gnu-gcc" -riscv32.linux.gcc.linkerexe = "riscv64-linux-gnu-gcc" -riscv64.linux.gcc.exe = "riscv64-linux-gnu-gcc" -riscv64.linux.gcc.linkerexe = "riscv64-linux-gnu-gcc" # For OpenWRT, you will also need to adjust PATH to point to your toolchain. mips.linux.gcc.exe = "mips-openwrt-linux-gcc" @@ -61,16 +43,15 @@ path="$lib/arch" path="$lib/core" path="$lib/pure" -@if not windows: - nimblepath="/opt/nimble/pkgs2/" - nimblepath="/opt/nimble/pkgs/" -@else: - # TODO: +@if nimbabel: + @if not windows: + nimblepath="/opt/nimble/pkgs/" + @else: + # TODO: + @end + nimblepath="$home/.nimble/pkgs/" @end -nimblepath="$home/.nimble/pkgs2/" -nimblepath="$home/.nimble/pkgs/" -# Syncronize with compiler/commands.specialDefine @if danger or quick: obj_checks:off field_checks:off @@ -82,9 +63,12 @@ nimblepath="$home/.nimble/pkgs/" linetrace:off debugger:off line_dir:off + dead_code_elim:on + @if nimHasNilChecks: + nilchecks:off + @end @end -# Syncronize with compiler/commands.specialDefine @if release or danger: stacktrace:off excessiveStackTrace:off @@ -100,11 +84,6 @@ nimblepath="$home/.nimble/pkgs/" gcc.options.always %= "${gcc.options.always} -fsanitize=null -fsanitize-undefined-trap-on-error" @end -# Turn off threads support when compiling for bare-metal targets (--os:any) -@if any: - threads:off -@end - @if unix and mingw: # Cross compile for Windows from Linux/OSX using MinGW i386.windows.gcc.exe = "i686-w64-mingw32-gcc" @@ -132,16 +111,7 @@ nimblepath="$home/.nimble/pkgs/" @end @if unix: - @if bsd: - # BSD got posix_spawn only recently, so we deactivate it for osproc: - define:useFork - @elif haiku: - gcc.options.linker = "-Wl,--as-needed -lnetwork" - gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork" - clang.options.linker = "-Wl,--as-needed -lnetwork" - clang.cpp.options.linker = "-Wl,--as-needed -lnetwork" - tcc.options.linker = "-Wl,--as-needed -lnetwork" - @elif not genode: + @if not bsd or haiku: # -fopenmp gcc.options.linker = "-ldl" gcc.cpp.options.linker = "-ldl" @@ -149,6 +119,19 @@ nimblepath="$home/.nimble/pkgs/" clang.cpp.options.linker = "-ldl" tcc.options.linker = "-ldl" @end + @if bsd: + # BSD got posix_spawn only recently, so we deactivate it for osproc: + define:useFork + # at least NetBSD has problems with thread local storage: + tlsEmulation:on + @end + @if haiku: + gcc.options.linker = "-Wl,--as-needed -lnetwork" + gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork" + clang.options.linker = "-Wl,--as-needed -lnetwork" + clang.cpp.options.linker = "-Wl,--as-needed -lnetwork" + tcc.options.linker = "-Wl,--as-needed -lnetwork" + @end @end @if android: @@ -186,14 +169,9 @@ nimblepath="$home/.nimble/pkgs/" gcc.maxerrorsimpl = "-fmax-errors=3" -@if freebsd or netbsd: - tlsEmulation:off -@elif bsd: - tlsEmulation:on -@end - @if macosx or freebsd or openbsd: cc = clang + tlsEmulation:on gcc.options.always %= "-w ${gcc.maxerrorsimpl}" gcc.cpp.options.always %= "-w ${gcc.maxerrorsimpl} -fpermissive" @elif windows: @@ -256,17 +234,14 @@ clang.objc.options.linker = "-lobjc -lgnustep-base" gcc.options.linker %= "-L $WIND_BASE/target/lib/usr/lib/ppc/PPC32/common -mrtp -fno-strict-aliasing -D_C99 -D_HAS_C9X -std=c99 -fasm -Wall -Wno-write-strings" @end -# -fno-math-errno is default in OSX, iOS, BSD, Musl, Libm, LLVM, Clang, ICC. -# See https://itnext.io/why-standard-c-math-functions-are-slow-d10d02554e33 -# and https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Optimize-Options.html#Optimize-Options -gcc.options.speed = "-O3 -fno-strict-aliasing -fno-ident -fno-math-errno" +gcc.options.speed = "-O3 -fno-strict-aliasing -fno-ident" gcc.options.size = "-Os -fno-ident" @if windows: gcc.options.debug = "-g3 -Og -gdwarf-3" @else: gcc.options.debug = "-g3 -Og" @end -gcc.cpp.options.speed = "-O3 -fno-strict-aliasing -fno-ident -fno-math-errno" +gcc.cpp.options.speed = "-O3 -fno-strict-aliasing -fno-ident" gcc.cpp.options.size = "-Os -fno-ident" gcc.cpp.options.debug = "-g3 -Og" #passl = "-pg" @@ -320,10 +295,39 @@ vcc.cpp.options.size = "/O1" # Configuration for the Tiny C Compiler: tcc.options.always = "-w" +# Configuration for the Genode toolchain +@if genode: + noCppExceptions # avoid std C++ + tlsEmulation:on # no TLS segment register magic + @if i386 or amd64: + gcc.exe = "genode-x86-gcc" + gcc.cpp.exe = "genode-x86-g++" + gcc.cpp.linkerexe = "genode-x86-ld" + @elif arm: + gcc.exe = "genode-arm-gcc" + gcc.cpp.exe = "genode-arm-g++" + gcc.cpp.linkerexe = "genode-arm-ld" + @elif arm64: + gcc.exe = "genode-aarch64-gcc" + gcc.cpp.exe = "genode-aarch64-g++" + gcc.cpp.linkerexe = "genode-aarch64-ld" + @elif riscv64: + gcc.exe = "genode-riscv-gcc" + gcc.cpp.exe = "genode-riscv-g++" + gcc.cpp.linkerexe = "genode-riscv-ld" + @end +@end + @if arm or arm64: --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 abe039738..585b3cc49 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -9,88 +9,65 @@ split.item.toc = "20" doc.section = """
-

$sectionTitle

-
- $content -
-
+

$sectionTitle

+
+$content +
""" -# Just a single item in the TOC (e.g. imports, exports) -doc.section.toc_item = """ -
  • - $sectionTitle -
  • -""" - -# This is a section (e.g. procs, types) in the TOC which gets turned into a drop down doc.section.toc = """
  • -
    - $sectionTitle -
      - $content -
    -
    + $sectionTitle +
      + $content +
  • """ doc.section.toc2 = """ -
      $plainName - $content -
    +
      $plainName + $content +
    """ # Chunk of HTML emitted for each entry in the HTML table of contents. # Available variables are: # * $desc: the actual docstring of the item. # * $header: the full version of name, including types, pragmas, tags, etc. -# * $header_plain: like header but without HTML (and without pragmas, tags, etc.), -# for attribute embedding. +# * $header_plain: like header but without HTML, for attribute embedding. # * $itemID: numerical unique entry of the item in the HTML. # * $itemSym: short symbolic name of the item for easier hyperlinking. # * $itemSymEnc: quoted version for URLs or attributes. # * $itemSymOrID: the symbolic name or the ID if that is not unique. # * $itemSymOrIDEnc: quoted version for URLs or attributes. # * $name: reduced name of the item. -# * $uniqueName: name with parameters for routine types or $name for others. # * $seeSrc: generated HTML from doc.item.seesrc (if some switches are used). doc.item = """ -
    -
    $header
    -
    - $deprecationMsg - $desc - $seeSrc -
    -
    -""" - -# A wrapper of a few overloaded `doc.item`s with the same basic name -# * $header_plain - see above -# * $overloadGroupName - the anchor for this whole group -# * $content - string containing `doc.item`s themselves -doc.item2 = """ -
    - $content -
    + +
    $header
    +
    +$deprecationMsg +$desc +$seeSrc +
    """ # Chunk of HTML emitted for each entry in the HTML table of contents. # See doc.item for available substitution variables. - -# 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
  • +
  • $itemSymOrID
  • """ + + # 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: @@ -100,31 +77,32 @@ 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: @@ -135,37 +113,45 @@ doc.body_toc_groupsection = """ doc.body_toc_group = """
    -
    - - -
    - -
    - Search: -
    - $body_toc_groupsection - $tableofcontents +
    + +     Dark Mode
    + +
    + Search: +
    + $body_toc_groupsection + $tableofcontents +
    + $seeSrc
    - $seeSrc -
    - $deprecationMsg -

    $moduledesc

    - $content +
    + $deprecationMsg +

    $moduledesc

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

    $moduledesc

    - $content +
    + $deprecationMsg +

    $moduledesc

    + $content
    """ @@ -218,46 +206,88 @@ $moduledesc $content """ -# $1 - number of listing in document, $2 - language (e.g. langNim), $3 - anchor -doc.listing_start = "" +doc.listing_start = "
    "
     doc.listing_end = "
    " # * $analytics: Google analytics location, includes + + + -
    -
    -

    $title

    $subtitle - $content +
    +
    +

    $title

    + $content +
    - $analytics - - - +
    +$analytics """ diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 4fb1aec90..6d7ae413d 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -3,42 +3,27 @@ # (c) 2012 Andreas Rumpf # Feel free to edit the templates as you need. -split.item.toc = "20" +split.item.toc = "20" # too long entries in the table of contents wrap around # after this number of characters doc.section = """ -\rsthA[$sectionTitle]{$sectionTitle}\label{$sectionID} - +\chapter{$sectionTitle}\label{$sectionID} +\begin{description} $content +\end{description} """ doc.section.toc = "" # $sectionID $sectionTitleID $sectionTitle $content doc.item = """ - -\vspace{1em} -\phantomsection\addcontentsline{toc}{subsubsection}{$uniqueName} -\label{$itemSymOrID}\hypertarget{$itemSymOrID}{} - -\begin{rstdocitem} -$header -\end{rstdocitem} - -\begin{addmargin}[0.05\linewidth]{0pt} +\item[\texttt{$header}\label{$itemID}]\mbox{~}\\* $desc -\end{addmargin} -""" - -doc.item2 = """ -\phantomsection\addcontentsline{toc}{subsection}{$header_plain} -\label{$overloadGroupName}\hypertarget{$overloadGroupName}{} - -$content """ doc.item.toc = "" +# \item $name\ref{$itemID} doc.toc = r"\tableofcontents \newpage" @@ -53,33 +38,96 @@ $moduledesc $content """ -# $1 - number of listing in document, $2 - language (e.g. langNim), $3 - anchor -doc.listing_start = "\\begin{rstpre}\n" -doc.listing_end = "\n\\end{rstpre}\n\n" - doc.file = """ % This file was generated by Nim. % Generated: $date $time UTC -% -% Compile it by: xelatex (up to 3 times to get labels generated) -% ------- -% For example: -% xelatex file.tex -% xelatex file.tex -% makeindex file -% xelatex file.tex -% -\documentclass{nimdoc} +\documentclass[a4paper]{article} +\usepackage[left=2cm,right=3cm,top=3cm,bottom=3cm]{geometry} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{graphicx} +\usepackage{lmodern} +\usepackage{fancyvrb, courier} +\usepackage{tabularx} +\usepackage{hyperref} +\usepackage{enumitem} + +\usepackage{xcolor} +\usepackage[tikz]{mdframed} +\usetikzlibrary{shadows} +\mdfsetup{% +linewidth=3, +topline=false, +rightline=false, +bottomline=false} \begin{document} -\title{$title $version $subtitle} +\title{$title $version} \author{$author} +\tolerance 1414 +\hbadness 1414 +\emergencystretch 1.5em +\hfuzz 0.3pt +\widowpenalty=10000 +\vfuzz \hfuzz +\raggedbottom + \maketitle +\newenvironment{rstpre}{\VerbatimEnvironment\begingroup\begin{Verbatim}[fontsize=\footnotesize , commandchars=\\\{\}]}{\end{Verbatim}\endgroup} + +% to pack tabularx into a new environment, special syntax is needed :-( +\newenvironment{rsttab}[1]{\tabularx{\linewidth}{#1}}{\endtabularx} + +\newcommand{\rstsub}[1]{\raisebox{-0.5ex}{\scriptsize{#1}}} +\newcommand{\rstsup}[1]{\raisebox{0.5ex}{\scriptsize{#1}}} + +\newcommand{\rsthA}[1]{\section{#1}} +\newcommand{\rsthB}[1]{\subsection{#1}} +\newcommand{\rsthC}[1]{\subsubsection{#1}} +\newcommand{\rsthD}[1]{\paragraph{#1}} +\newcommand{\rsthE}[1]{\paragraph{#1}} + +\newcommand{\rstovA}[1]{\section*{#1}} +\newcommand{\rstovB}[1]{\subsection*{#1}} +\newcommand{\rstovC}[1]{\subsubsection*{#1}} +\newcommand{\rstovD}[1]{\paragraph*{#1}} +\newcommand{\rstovE}[1]{\paragraph*{#1}} + +% Syntax highlighting: +\newcommand{\spanDecNumber}[1]{#1} +\newcommand{\spanBinNumber}[1]{#1} +\newcommand{\spanHexNumber}[1]{#1} +\newcommand{\spanOctNumber}[1]{#1} +\newcommand{\spanFloatNumber}[1]{#1} +\newcommand{\spanIdentifier}[1]{#1} +\newcommand{\spanKeyword}[1]{\textbf{#1}} +\newcommand{\spanStringLit}[1]{#1} +\newcommand{\spanLongStringLit}[1]{#1} +\newcommand{\spanCharLit}[1]{#1} +\newcommand{\spanEscapeSequence}[1]{#1} +\newcommand{\spanOperator}[1]{#1} +\newcommand{\spanPunctuation}[1]{#1} +\newcommand{\spanComment}[1]{\emph{#1}} +\newcommand{\spanLongComment}[1]{\emph{#1}} +\newcommand{\spanRegularExpression}[1]{#1} +\newcommand{\spanTagStart}[1]{#1} +\newcommand{\spanTagEnd}[1]{#1} +\newcommand{\spanKey}[1]{#1} +\newcommand{\spanValue}[1]{#1} +\newcommand{\spanRawData}[1]{#1} +\newcommand{\spanAssembler}[1]{#1} +\newcommand{\spanPreprocessor}[1]{#1} +\newcommand{\spanDirective}[1]{#1} +\newcommand{\spanCommand}[1]{#1} +\newcommand{\spanRule}[1]{#1} +\newcommand{\spanHyperlink}[1]{#1} +\newcommand{\spanLabel}[1]{#1} +\newcommand{\spanReference}[1]{#1} +\newcommand{\spanOther}[1]{#1} +\newcommand{\spantok}[1]{\frame{#1}} + $content - -\printindex - \end{document} """ diff --git a/copying.txt b/copying.txt index ae3a162a7..a498a9525 100644 --- a/copying.txt +++ b/copying.txt @@ -1,7 +1,7 @@ ===================================================== Nim -- a Compiler for Nim. https://nim-lang.org/ -Copyright (C) 2006-2023 Andreas Rumpf. All rights reserved. +Copyright (C) 2006-2021 Andreas Rumpf. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/doc/advopt.txt b/doc/advopt.txt index 4db89062b..d87b00f91 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -4,13 +4,9 @@ Advanced commands: //compileToOC, objc compile project to Objective C code //js compile project to Javascript //e run a Nimscript file - //md2html convert a Markdown file to HTML - use `--docCmd:skip` to skip compiling snippets //rst2html convert a reStructuredText file to HTML use `--docCmd:skip` to skip compiling snippets - //md2tex convert a Markdown file to LaTeX - //rst2tex convert a reStructuredText file to LaTeX - //doc2tex extract the documentation to a LaTeX file + //rst2tex convert a reStructuredText file to TeX //jsondoc extract the documentation to a json file //ctags create a tags file //buildIndex build an index for the whole documentation @@ -18,8 +14,7 @@ Advanced commands: module dependency graph //dump dump all defined conditionals and search paths see also: --dump.format:json (useful with: `| jq`) - //check checks the project for syntax and semantics - (can be combined with --defusages) + //check checks the project for syntax and semantic Runtime checks (see -x): --objChecks:on|off turn obj conversion checks on|off @@ -30,47 +25,28 @@ Runtime checks (see -x): --floatChecks:on|off turn all floating point (NaN/Inf) checks on|off --nanChecks:on|off turn NaN checks on|off --infChecks:on|off turn Inf checks on|off + --refChecks:on|off turn ref checks on|off (only for --newruntime) Advanced options: - --defusages:FILE,LINE,COL - find the definition and all usages of a symbol -o:FILE, --out:FILE set the output filename --outdir:DIR set the path where the output file will be written --usenimcache will use `outdir=$$nimcache`, whichever it resolves to after all options have been processed --stdout:on|off output to stdout --colors:on|off turn compiler messages coloring on|off - --filenames:abs|canonical|legacyRelProj - customize how filenames are rendered in compiler messages, - defaults to `abs` (absolute) - --processing:dots|filenames|off - show files as they're being processed by nim compiler - --unitsep:on|off use the ASCII unit separator (31) between error - messages, useful for IDE-like tooling + --listFullPaths:on|off list full paths in messages --declaredLocs:on|off show declaration locations in messages - --spellSuggest|:num show at most `num >= 0` spelling suggestions on typos. - if `num` is not specified (or `auto`), return - an implementation defined set of suggestions. - --hints:on|off|list. `on|off` enables or disables hints. - `list` reports which hints are selected. - --hint:X:on|off turn specific hint X on|off. `hint:X` means `hint:X:on`, - as with similar flags. `all` is the set of all hints - (only `all:off` is supported). - --hintAsError:X:on|off turn specific hint X into an error on|off -w:on|off|list, --warnings:on|off|list - `on|off` enables or disables warnings. - `list` reports which warnings are selected. - --warning:X:on|off turn specific warning X on|off. `warning:X` means `warning:X:on`, - as with similar flags. `all` is the set of all warning - (only `all:off` is supported). - --warningAsError:X:on|off + turn all warnings on|off or list all available + --warning[X]:on|off turn specific warning X on|off + --hints:on|off|list turn all hints on|off or list all available + --hint[X]:on|off turn specific hint X on|off + --warningAsError[X]:on|off turn specific warning X into an error on|off --styleCheck:off|hint|error produce hints or errors for Nim identifiers that do not adhere to Nim's official style guide https://nim-lang.org/docs/nep1.html - --styleCheck:usages only enforce consistent spellings of identifiers, - do not enforce the style on declarations --showAllMismatches:on|off show all mismatching candidates in overloading resolution @@ -85,8 +61,8 @@ Advanced options: --noMain:on|off do not generate a main procedure --genScript:on|off generate a compile script (in the 'nimcache' subdirectory named 'compile_$$project$$scriptext'), - and a '.deps' file containing the dependencies; implies --compileOnly + --genDeps:on|off generate a '.deps' file containing the dependencies --os:SYMBOL set the target operating system (cross-compilation) --cpu:SYMBOL set the target processor (cross-compilation) --debuginfo:on|off enables debug information @@ -105,8 +81,7 @@ Advanced options: if path == @default (the default and most useful), will use best match among @pkg,@path. if these are nonexistent, will use project path - -b, --backend:c|cpp|js|objc - sets backend to use with commands like `nim doc` or `nim r` + -b, --backend:c|cpp|js|objc sets backend to use with commands like `nim doc` or `nim r` --docCmd:cmd if `cmd == skip`, skips runnableExamples else, runs runnableExamples with given options, e.g.: `--docCmd:"-d:foo --threads:on"` @@ -116,26 +91,31 @@ Advanced options: --lineDir:on|off generation of #line directive on|off --embedsrc:on|off embeds the original source code as comments in the generated output + --threadanalysis:on|off turn thread analysis on|off --tlsEmulation:on|off turn thread local storage emulation on|off + --taintMode:on|off turn taint mode on|off --implicitStatic:on|off turn implicit compile time evaluation on|off --trmacros:on|off turn term rewriting macros on|off --multimethods:on|off turn multi-methods on|off + --memTracker:on|off turn memory tracker on|off --hotCodeReloading:on|off turn support for hot code reloading on|off --excessiveStackTrace:on|off stack traces use full file paths --stackTraceMsgs:on|off enable user defined stack frame msgs via `setFrameMsg` + --nilseqs:on|off allow 'nil' for strings/seqs for + backwards compatibility + --seqsv2:on|off use the new string/seq implementation based on + destructors --skipCfg:on|off do not read the nim installation's configuration file --skipUserCfg:on|off do not read the user's configuration file --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 'orc' - --exceptions:setjmp|cpp|goto|quirky + --gc:refc|arc|orc|markAndSweep|boehm|go|none|regions + select the GC to use; default is 'refc' + --exceptions:setjmp|cpp|goto select the exception handling implementation - --index:on|off|only docgen: turn index file generation? (`only` means - not generate output files like HTML) - --noImportdoc:on|off disable loading documentation ``.idx`` files? + --index:on|off turn index file generation on|off --putenv:key=value set an environment variable --NimblePath:PATH add a path for Nimble support --noNimblePath deactivate the Nimble path @@ -143,8 +123,6 @@ Advanced options: --cppCompileToNamespace:namespace use the provided namespace for the generated C++ code, if no namespace is provided "Nim" will be used - --nimMainPrefix:prefix use `{prefix}NimMain` instead of `NimMain` in the produced - C/C++ code --expandMacro:MACRO dump every generated AST from MACRO --expandArc:PROCNAME show how PROCNAME looks like after diverse optimizations before the final backend phase (mostly ARC/ORC specific) @@ -169,9 +147,12 @@ Advanced options: enable experimental language feature --legacy:$2 enable obsolete/legacy language feature - --useVersion:1.0|1.2|1.6 emulate Nim version X of the Nim compiler, for testing - --benchmarkVM:on|off turn benchmarking of VM code with cpuTime() on|off - --profileVM:on|off turn compile time VM profiler on|off - --sinkInference:on|off turn sink parameter inference on|off (default: off) + --useVersion:1.0 emulate Nim version X of the Nim compiler + --profiler:on|off enable profiling; requires `import nimprof`, and + works better with `--stackTrace:on` + see also https://nim-lang.github.io/Nim/estp.html + --benchmarkVM:on|off enable benchmarking of VM code with cpuTime() + --profileVM:on|off enable compile time VM profiler + --sinkInference:on|off en-/disable sink parameter inference (default: on) --panics:on|off turn panics into process terminations (default: off) - --deepcopy:on|off enable 'system.deepCopy' for ``--mm:arc|orc`` + --deepcopy:on|off enable 'system.deepCopy' for ``--gc:arc|orc`` diff --git a/doc/apis.md b/doc/apis.rst similarity index 68% rename from doc/apis.md rename to doc/apis.rst index f0b8c93e5..03374c2db 100644 --- a/doc/apis.md +++ b/doc/apis.rst @@ -2,9 +2,6 @@ API naming design ================= -.. default-role:: code -.. include:: rstcommon.rst - The API is designed to be **easy to use** and consistent. Ease of use is measured by the number of calls to achieve a concrete high-level action. @@ -18,25 +15,25 @@ 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 - reference type `P` +------------------- ------------ -------------------------------------- +initialize initT ``init`` is used to create a + value type ``T`` +new newP ``new`` is used to create a + reference type ``P`` find find should return the position where something was found; for a bool result - use `contains` -contains contains often short for `find() >= 0` -append add use `add` instead of `append` + use ``contains`` +contains contains often short for ``find() >= 0`` +append add use ``add`` instead of ``append`` compare cmp should return an int with the - `< 0` `== 0` or `> 0` semantics; - for a bool result use `sameXYZ` -put put, `[]=` consider overloading `[]=` for put -get get, `[]` consider overloading `[]` for get; - consider to not use `get` as a - prefix: `len` instead of `getLen` + ``< 0`` ``== 0`` or ``> 0`` semantics; + for a bool result use ``sameXYZ`` +put put, ``[]=`` consider overloading ``[]=`` for put +get get, ``[]`` consider overloading ``[]`` for get; + consider to not use ``get`` as a + prefix: ``len`` instead of ``getLen`` length len also used for *number of elements* size size, len size should refer to a byte size capacity cap @@ -47,7 +44,7 @@ delete delete, del del is supposed to be faster than delete, because it does not keep the order; delete keeps the order remove delete, del inconsistent right now -remove-and-return pop `Table`/`TableRef` alias to `take` +remove-and-return pop ``Table``/``TableRef`` alias to ``take`` include incl exclude excl command cmd @@ -82,4 +79,4 @@ literal lit string str identifier ident indentation indent -=================== ============ ====================================== +------------------- ------------ -------------------------------------- diff --git a/doc/astspec.txt b/doc/astspec.txt index bfaec7155..e7bbcecc2 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -6,7 +6,8 @@ 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: - ```nim +.. code-block:: nim + type NimNodeKind = enum ## kind of a node; only explanatory nnkNone, ## invalid node kind @@ -31,7 +32,6 @@ 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,19 +86,17 @@ Command call Concrete syntax: - ```nim +.. code-block:: nim echo "abc", "xyz" - ``` AST: - ```nim +.. code-block:: nim nnkCommand( nnkIdent("echo"), nnkStrLit("abc"), nnkStrLit("xyz") ) - ``` Call with ``()`` @@ -106,19 +104,17 @@ Call with ``()`` Concrete syntax: - ```nim +.. code-block:: nim echo("abc", "xyz") - ``` AST: - ```nim +.. code-block:: nim nnkCall( nnkIdent("echo"), nnkStrLit("abc"), nnkStrLit("xyz") ) - ``` Infix operator call @@ -126,32 +122,29 @@ Infix operator call Concrete syntax: - ```nim +.. code-block:: nim "abc" & "xyz" - ``` AST: - ```nim +.. code-block:: nim nnkInfix( nnkIdent("&"), nnkStrLit("abc"), nnkStrLit("xyz") ) - ``` Note that with multiple infix operators, the command is parsed by operator precedence. Concrete syntax: - ```nim +.. code-block:: nim 5 + 3 * 4 - ``` AST: - ```nim +.. code-block:: nim nnkInfix( nnkIdent("+"), nnkIntLit(5), @@ -161,7 +154,6 @@ AST: nnkIntLit(4) ) ) - ``` As a side note, if you choose to use infix operators in a prefix form, the AST behaves as a @@ -170,13 +162,12 @@ behaves as a Concrete syntax: - ```nim +.. code-block:: nim `+`(3, 4) - ``` AST: - ```nim +.. code-block:: nim nnkCall( nnkAccQuoted( nnkIdent("+") @@ -184,25 +175,22 @@ AST: nnkIntLit(3), nnkIntLit(4) ) - ``` Prefix operator call -------------------- Concrete syntax: - ```nim +.. code-block:: nim ? "xyz" - ``` AST: - ```nim +.. code-block:: nim nnkPrefix( nnkIdent("?"), nnkStrLit("abc") ) - ``` Postfix operator call @@ -213,18 +201,16 @@ Postfix operator call Concrete syntax: - ```nim +.. code-block:: nim identifier* - ``` AST: - ```nim +.. code-block:: nim nnkPostfix( nnkIdent("*"), nnkIdent("identifier") ) - ``` Call with named arguments @@ -232,13 +218,12 @@ Call with named arguments Concrete syntax: - ```nim +.. code-block:: nim writeLine(file=stdout, "hallo") - ``` AST: - ```nim +.. code-block:: nim nnkCall( nnkIdent("writeLine"), nnkExprEqExpr( @@ -247,7 +232,6 @@ AST: ), nnkStrLit("hallo") ) - ``` Call with raw string literal ---------------------------- @@ -258,33 +242,29 @@ This is used, for example, in the ``bindSym`` examples Concrete syntax: - ```nim +.. code-block:: nim echo"abc" - ``` AST: - ```nim +.. code-block:: nim nnkCallStrLit( nnkIdent("echo"), nnkRStrLit("hello") ) - ``` Dereference operator ``[]`` --------------------------- Concrete syntax: - ```nim +.. code-block:: nim x[] - ``` AST: - ```nim +.. code-block:: nim nnkDerefExpr(nnkIdent("x")) - ``` Addr operator @@ -292,15 +272,13 @@ Addr operator Concrete syntax: - ```nim +.. code-block:: nim addr(x) - ``` AST: - ```nim +.. code-block:: nim nnkAddr(nnkIdent("x")) - ``` Cast operator @@ -308,15 +286,13 @@ Cast operator Concrete syntax: - ```nim +.. code-block:: nim cast[T](x) - ``` AST: - ```nim +.. code-block:: nim nnkCast(nnkIdent("T"), nnkIdent("x")) - ``` Object access operator ``.`` @@ -324,15 +300,13 @@ Object access operator ``.`` Concrete syntax: - ```nim +.. code-block:: nim x.y - ``` AST: - ```nim +.. code-block:: 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``. @@ -343,77 +317,31 @@ Array access operator ``[]`` Concrete syntax: - ```nim +.. code-block:: nim x[y] - ``` AST: - ```nim +.. code-block:: nim nnkBracketExpr(nnkIdent("x"), nnkIdent("y")) - ``` Parentheses ----------- -Parentheses for affecting operator precedence use the ``nnkPar`` node. +Parentheses for affecting operator precedence or tuple construction +are built with the ``nnkPar`` node. Concrete syntax: - ```nim - (a + b) * c - ``` +.. code-block:: nim + (1, 2, (3)) AST: - ```nim - nnkInfix(nnkIdent("*"), - nnkPar( - nnkInfix(nnkIdent("+"), nnkIdent("a"), nnkIdent("b"))), - nnkIdent("c")) - ``` +.. code-block:: nim + nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3))) -Tuple Constructors ------------------- - -Nodes for tuple construction are built with the ``nnkTupleConstr`` node. - -Concrete syntax: - - ```nim - (1, 2, 3) - (a: 1, b: 2, c: 3) - () - ``` - -AST: - - ```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. - - ```nim - (1,) - (a: 1) - ``` - -AST: - - ```nim - nnkTupleConstr(nnkIntLit(1)) - nnkTupleConstr( - nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1))) - ``` Curly braces ------------ @@ -422,32 +350,28 @@ Curly braces are used as the set constructor. Concrete syntax: - ```nim +.. code-block:: nim {1, 2, 3} - ``` AST: - ```nim +.. code-block:: nim nnkCurly(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) - ``` When used as a table constructor, the syntax is different. Concrete syntax: - ```nim +.. code-block:: nim {a: 3, b: 5} - ``` AST: - ```nim +.. code-block:: nim nnkTableConstr( nnkExprColonExpr(nnkIdent("a"), nnkIntLit(3)), nnkExprColonExpr(nnkIdent("b"), nnkIntLit(5)) ) - ``` Brackets @@ -457,15 +381,13 @@ Brackets are used as the array constructor. Concrete syntax: - ```nim +.. code-block:: nim [1, 2, 3] - ``` AST: - ```nim +.. code-block:: nim nnkBracket(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) - ``` Ranges @@ -477,24 +399,22 @@ AST, construction with ``..`` as an infix operator should be used instead. Concrete syntax: - ```nim +.. code-block:: nim 1..3 - ``` AST: - ```nim +.. code-block:: nim nnkInfix( nnkIdent(".."), nnkIntLit(1), nnkIntLit(3) ) - ``` Example code: - ```nim - macro genRepeatEcho() = +.. code-block:: nim + macro genRepeatEcho(): stmt = result = newNimNode(nnkStmtList) var forStmt = newNimNode(nnkForStmt) # generate a for statement @@ -512,7 +432,6 @@ Example code: # 3 # 3 # 3 - ``` If expression @@ -522,19 +441,17 @@ The representation of the ``if`` expression is subtle, but easy to traverse. Concrete syntax: - ```nim +.. code-block:: nim if cond1: expr1 elif cond2: expr2 else: expr3 - ``` AST: - ```nim +.. code-block:: nim nnkIfExpr( nnkElifExpr(cond1, expr1), nnkElifExpr(cond2, expr2), nnkElseExpr(expr3) ) - ``` Documentation Comments ---------------------- @@ -545,21 +462,19 @@ comments are ignored. Concrete syntax: - ```nim +.. code-block:: nim ## This is a comment ## This is part of the first comment stmt1 ## Yet another - ``` AST: - ```nim +.. code-block:: nim nnkCommentStmt() # only appears once for the first two lines! stmt1 nnkCommentStmt() # another nnkCommentStmt because there is another comment # (separate from the first) - ``` Pragmas ------- @@ -570,33 +485,30 @@ objects, but the standalone ``emit`` pragma shows the basics with the AST. Concrete syntax: - ```nim +.. code-block:: nim {.emit: "#include ".} - ``` AST: - ```nim +.. code-block:: 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: - ```nim +.. code-block:: nim {.pragma: cdeclRename, cdecl.} - ``` AST: - ```nim +.. code-block:: nim nnkPragma( nnkExprColonExpr( nnkIdent("pragma"), # this is always first when declaring a new pragma @@ -604,7 +516,6 @@ AST: ), nnkIdent("cdecl") ) - ``` Statements ========== @@ -617,7 +528,7 @@ there is no ``else`` branch, no ``nnkElse`` child exists. Concrete syntax: - ```nim +.. code-block:: nim if cond1: stmt1 elif cond2: @@ -626,18 +537,16 @@ Concrete syntax: stmt3 else: stmt4 - ``` AST: - ```nim +.. code-block:: nim nnkIfStmt( nnkElifBranch(cond1, stmt1), nnkElifBranch(cond2, stmt2), nnkElifBranch(cond3, stmt3), nnkElse(stmt4) ) - ``` When statement @@ -651,15 +560,13 @@ Assignment Concrete syntax: - ```nim +.. code-block:: nim x = 42 - ``` AST: - ```nim +.. code-block:: nim nnkAsgn(nnkIdent("x"), nnkIntLit(42)) - ``` This is not the syntax for assignment when combined with ``var``, ``let``, or ``const``. @@ -669,17 +576,15 @@ Statement list Concrete syntax: - ```nim +.. code-block:: nim stmt1 stmt2 stmt3 - ``` AST: - ```nim +.. code-block:: nim nnkStmtList(stmt1, stmt2, stmt3) - ``` Case statement @@ -687,7 +592,7 @@ Case statement Concrete syntax: - ```nim +.. code-block:: nim case expr1 of expr2, expr3..expr4: stmt1 @@ -697,11 +602,10 @@ Concrete syntax: stmt3 else: stmt4 - ``` AST: - ```nim +.. code-block:: nim nnkCaseStmt( expr1, nnkOfBranch(expr2, nnkRange(expr3, expr4), stmt1), @@ -709,7 +613,6 @@ AST: nnkElifBranch(cond1, stmt3), nnkElse(stmt4) ) - ``` The ``nnkElifBranch`` and ``nnkElse`` parts may be missing. @@ -719,16 +622,14 @@ While statement Concrete syntax: - ```nim +.. code-block:: nim while expr1: stmt1 - ``` AST: - ```nim +.. code-block:: nim nnkWhileStmt(expr1, stmt1) - ``` For statement @@ -736,16 +637,14 @@ For statement Concrete syntax: - ```nim +.. code-block:: nim for ident1, ident2 in expr1: stmt1 - ``` AST: - ```nim +.. code-block:: nim nnkForStmt(ident1, ident2, expr1, stmt1) - ``` Try statement @@ -753,7 +652,7 @@ Try statement Concrete syntax: - ```nim +.. code-block:: nim try: stmt1 except e1, e2: @@ -764,11 +663,10 @@ Concrete syntax: stmt4 finally: stmt5 - ``` AST: - ```nim +.. code-block:: nim nnkTryStmt( stmt1, nnkExceptBranch(e1, e2, stmt2), @@ -776,7 +674,6 @@ AST: nnkExceptBranch(stmt4), nnkFinally(stmt5) ) - ``` Return statement @@ -784,15 +681,13 @@ Return statement Concrete syntax: - ```nim +.. code-block:: nim return expr1 - ``` AST: - ```nim +.. code-block:: nim nnkReturnStmt(expr1) - ``` Yield statement @@ -800,9 +695,8 @@ Yield statement Like ``return``, but with ``nnkYieldStmt`` kind. - ```nim +.. code-block:: nim nnkYieldStmt(expr1) - ``` Discard statement @@ -810,9 +704,8 @@ Discard statement Like ``return``, but with ``nnkDiscardStmt`` kind. - ```nim +.. code-block:: nim nnkDiscardStmt(expr1) - ``` Continue statement @@ -820,30 +713,26 @@ Continue statement Concrete syntax: - ```nim +.. code-block:: nim continue - ``` AST: - ```nim +.. code-block:: nim nnkContinueStmt() - ``` Break statement --------------- Concrete syntax: - ```nim +.. code-block:: nim break otherLocation - ``` AST: - ```nim +.. code-block:: nim nnkBreakStmt(nnkIdent("otherLocation")) - ``` If ``break`` is used without a jump-to location, ``nnkEmpty`` replaces ``nnkIdent``. @@ -852,15 +741,13 @@ Block statement Concrete syntax: - ```nim +.. code-block:: nim block name: - ``` AST: - ```nim +.. code-block:: nim nnkBlockStmt(nnkIdent("name"), nnkStmtList(...)) - ``` A ``block`` doesn't need an name, in which case ``nnkEmpty`` is used. @@ -869,20 +756,18 @@ Asm statement Concrete syntax: - ```nim +.. code-block:: nim asm """ some asm """ - ``` AST: - ```nim +.. code-block:: nim nnkAsmStmt( nnkEmpty(), # for pragmas nnkTripleStrLit("some asm"), ) - ``` Import section -------------- @@ -892,42 +777,37 @@ on what keywords are present. Let's start with the simplest form. Concrete syntax: - ```nim +.. code-block:: nim import math - ``` AST: - ```nim +.. code-block:: nim nnkImportStmt(nnkIdent("math")) - ``` With ``except``, we get ``nnkImportExceptStmt``. Concrete syntax: - ```nim +.. code-block:: nim import math except pow - ``` AST: - ```nim +.. code-block:: 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: - ```nim +.. code-block:: nim import strutils as su - ``` AST: - ```nim +.. code-block:: nim nnkImportStmt( nnkInfix( nnkIdent("as"), @@ -935,7 +815,6 @@ AST: nnkIdent("su") ) ) - ``` From statement -------------- @@ -944,15 +823,13 @@ If we use ``from ... import``, the result is different, too. Concrete syntax: - ```nim +.. code-block:: nim from math import pow - ``` AST: - ```nim +.. code-block:: 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``. @@ -965,30 +842,26 @@ the ``export`` syntax is pretty straightforward. Concrete syntax: - ```nim +.. code-block:: nim export unsigned - ``` AST: - ```nim +.. code-block:: nim nnkExportStmt(nnkIdent("unsigned")) - ``` Similar to the ``import`` statement, the AST is different for ``export ... except``. Concrete syntax: - ```nim +.. code-block:: nim export math except pow # we're going to implement our own exponentiation - ``` AST: - ```nim +.. code-block:: nim nnkExportExceptStmt(nnkIdent("math"),nnkIdent("pow")) - ``` Include statement ----------------- @@ -997,28 +870,25 @@ Like a plain ``import`` statement but with ``nnkIncludeStmt``. Concrete syntax: - ```nim +.. code-block:: nim include blocks - ``` AST: - ```nim +.. code-block:: nim nnkIncludeStmt(nnkIdent("blocks")) - ``` Var section ----------- Concrete syntax: - ```nim +.. code-block:: nim var a = 3 - ``` AST: - ```nim +.. code-block:: nim nnkVarSection( nnkIdentDefs( nnkIdent("a"), @@ -1026,7 +896,6 @@ 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 @@ -1044,13 +913,12 @@ This is equivalent to ``var``, but with ``nnkLetSection`` rather than Concrete syntax: - ```nim +.. code-block:: nim let a = 3 - ``` AST: - ```nim +.. code-block:: nim nnkLetSection( nnkIdentDefs( nnkIdent("a"), @@ -1058,20 +926,18 @@ AST: nnkIntLit(3), ) ) - ``` Const section ------------- Concrete syntax: - ```nim +.. code-block:: nim const a = 3 - ``` AST: - ```nim +.. code-block:: nim nnkConstSection( nnkConstDef( # not nnkConstDefs! nnkIdent("a"), @@ -1079,7 +945,6 @@ AST: nnkIntLit(3), # required in a const declaration! ) ) - ``` Type section ------------ @@ -1089,13 +954,12 @@ and ``const``. Concrete syntax: - ```nim +.. code-block:: nim type A = int - ``` AST: - ```nim +.. code-block:: nim nnkTypeSection( nnkTypeDef( nnkIdent("A"), @@ -1103,20 +967,18 @@ AST: nnkIdent("int") ) ) - ``` Declaring ``distinct`` types is similar, with the last ``nnkIdent`` wrapped in ``nnkDistinctTy``. Concrete syntax: - ```nim +.. code-block:: nim type MyInt = distinct int - ``` AST: - ```nim +.. code-block:: nim # ... nnkTypeDef( nnkIdent("MyInt"), @@ -1125,19 +987,17 @@ AST: nnkIdent("int") ) ) - ``` If a type section uses generic parameters, they are treated here: Concrete syntax: - ```nim +.. code-block:: nim type A[T] = expr1 - ``` AST: - ```nim +.. code-block:: nim nnkTypeSection( nnkTypeDef( nnkIdent("A"), @@ -1152,7 +1012,6 @@ AST: expr1, ) ) - ``` Note that not all ``nnkTypeDef`` utilize ``nnkIdent`` as their parameter. One of the most common uses of type declarations @@ -1160,13 +1019,12 @@ is to work with objects. Concrete syntax: - ```nim +.. code-block:: nim type IO = object of RootObj - ``` AST: - ```nim +.. code-block:: nim # ... nnkTypeDef( nnkIdent("IO"), @@ -1179,39 +1037,29 @@ 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: - ```nim - type Obj[T] {.inheritable.} = object +.. code-block:: nim + type Obj[T] = object {.inheritable.} name: string case isFat: bool of true: m: array[100_000, T] of false: m: array[10, T] - ``` AST: - ```nim +.. code-block:: nim # ... - nnkPragmaExpr( - nnkIdent("Obj"), - nnkPragma(nnkIdent("inheritable")) - ), - nnkGenericParams( - nnkIdentDefs( - nnkIdent("T"), - nnkEmpty(), - nnkEmpty()) - ), nnkObjectTy( - nnkEmpty(), + nnkPragma( + nnkIdent("inheritable") + ), nnkEmpty(), nnkRecList( # list of object parameters nnkIdentDefs( @@ -1255,61 +1103,55 @@ AST: ) ) ) - ``` Using an ``enum`` is similar to using an ``object``. Concrete syntax: - ```nim +.. code-block:: nim type X = enum First - ``` AST: - ```nim +.. code-block:: 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: - ```nim +.. code-block:: nim type Con = concept x,y,z (x & y & z) is string - ``` AST: - ```nim +.. code-block:: nim # ... nnkTypeClassTy( # note this isn't nnkConceptTy! - nnkArgList( + nnkArglist( # ... idents for x, y, z ) # ... ) - ``` Static types, like ``static[int]``, use ``nnkIdent`` wrapped in ``nnkStaticTy``. Concrete syntax: - ```nim +.. code-block:: nim type A[T: static[int]] = object - ``` AST: - ```nim +.. code-block:: nim # ... within nnkGenericParams nnkIdentDefs( nnkIdent("T"), @@ -1319,14 +1161,13 @@ 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`` @@ -1339,7 +1180,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``. @@ -1347,13 +1188,12 @@ Generic parameters are treated in the type, not the ``proc`` itself. Concrete syntax: - ```nim +.. code-block:: nim type MyProc[T] = proc(x: T) - ``` AST: - ```nim +.. code-block:: nim # ... nnkTypeDef( nnkIdent("MyProc"), @@ -1366,7 +1206,6 @@ AST: ) ) ) - ``` The same syntax applies to ``iterator`` (with ``nnkIteratorTy``), but *does not* apply to ``converter`` or ``template``. @@ -1376,30 +1215,26 @@ Mixin statement Concrete syntax: - ```nim +.. code-block:: nim mixin x - ``` AST: - ```nim +.. code-block:: nim nnkMixinStmt(nnkIdent("x")) - ``` Bind statement -------------- Concrete syntax: - ```nim +.. code-block:: nim bind x - ``` AST: - ```nim +.. code-block:: nim nnkBindStmt(nnkIdent("x")) - ``` Procedure declaration --------------------- @@ -1409,13 +1244,12 @@ a feel for how procedure calls are broken down. Concrete syntax: - ```nim +.. code-block:: nim proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard - ``` AST: - ```nim +.. code-block:: nim nnkProcDef( nnkPostfix(nnkIdent("*"), nnkIdent("hello")), # the exported proc name nnkEmpty(), # patterns for term rewriting in templates and macros (not procs) @@ -1443,7 +1277,6 @@ 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)`` @@ -1451,13 +1284,12 @@ are equivalent in the code, the AST is a little different for the latter. Concrete syntax: - ```nim +.. code-block:: nim proc(a, b: int) - ``` AST: - ```nim +.. code-block:: nim # ...AST as above... nnkFormalParams( nnkEmpty(), # no return here @@ -1469,27 +1301,24 @@ AST: ) ), # ... - ``` When a procedure uses the special ``var`` type return variable, the result is different from that of a var section. Concrete syntax: - ```nim +.. code-block:: nim proc hello(): var int - ``` AST: - ```nim +.. code-block:: nim # ... nnkFormalParams( nnkVarTy( nnkIdent("int") ) ) - ``` Iterator declaration -------------------- @@ -1499,19 +1328,17 @@ replacing ``nnkProcDef``. Concrete syntax: - ```nim +.. code-block:: nim iterator nonsense[T](x: seq[T]): float {.closure.} = ... - ``` AST: - ```nim +.. code-block:: nim nnkIteratorDef( nnkIdent("nonsense"), nnkEmpty(), ... ) - ``` Converter declaration --------------------- @@ -1520,18 +1347,16 @@ A converter is similar to a proc. Concrete syntax: - ```nim +.. code-block:: nim converter toBool(x: float): bool - ``` AST: - ```nim +.. code-block:: nim nnkConverterDef( nnkIdent("toBool"), # ... ) - ``` Template declaration -------------------- @@ -1544,13 +1369,12 @@ the ``nnkEmpty()`` as the second argument to ``nnkProcDef`` and Concrete syntax: - ```nim +.. code-block:: nim template optOpt{expr1}(a: int): int - ``` AST: - ```nim +.. code-block:: nim nnkTemplateDef( nnkIdent("optOpt"), nnkStmtList( # instead of nnkEmpty() @@ -1558,7 +1382,6 @@ 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``. @@ -1572,9 +1395,8 @@ Macros behave like templates, but ``nnkTemplateDef`` is replaced with Hidden Standard Conversion -------------------------- - ```nim +.. code-block:: 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.md b/doc/backends.rst similarity index 54% rename from doc/backends.md rename to doc/backends.rst index 5258e9b4d..070cadbfd 100644 --- a/doc/backends.md +++ b/doc/backends.rst @@ -5,33 +5,27 @@ :Author: Puppet Master :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst -.. no syntax highlighting here by default: - .. contents:: - -> "Heresy grows from idleness." -- Unknown. + "Heresy grows from idleness." -- Unknown. Introduction ============ -The [Nim Compiler User Guide](nimc.html) documents the typical -compiler invocation, using the `compile`:option: -or `c`:option: command to transform a +The `Nim Compiler User Guide `_ documents the typical +compiler invocation, using the ``compile`` or ``c`` 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. 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]( -http://nodejs.org). +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 +`_. On top of generating libraries or standalone applications, Nim offers bidirectional interfacing with the backend targets through generic and @@ -46,33 +40,31 @@ The C like targets The commands to compile to either C, C++ or Objective-C are: -//compileToC, cc compile project with C code generator -//compileToCpp, cpp compile project to C++ code -//compileToOC, objc compile project to Objective C code + //compileToC, cc compile project with C code generator + //compileToCpp, cpp compile project to C++ code + //compileToOC, objc compile project to Objective C code The most significant difference between these commands is that if you look into the ``nimcache`` directory you will find ``.c``, ``.cpp`` or ``.m`` files, other than that all of them will produce a native binary for your -project. This allows you to take the generated code and place it directly +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: +line invocations:: - ```cmd - nim c hallo.nim - nim cpp hallo.nim - nim objc hallo.nim - ``` + $ 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]( -nimc.html#crossminuscompilation) to select the target CPU, operative system +`specify additional switches for cross-compilation +`_ to select the target CPU, operative system or compiler/linker commands. The JavaScript target --------------------- -Nim can also generate `JavaScript`:idx: code through the `js`:option: command. +Nim can also generate `JavaScript`:idx: code through the ``js`` command. Nim targets JavaScript 1.5 which is supported by any widely used browser. Since JavaScript does not have a portable means to include another module, @@ -81,32 +73,30 @@ Nim just generates a long ``.js`` file. Features or modules that the JavaScript platform does not support are not available. This includes: -* manual memory management (`alloc`, etc.) -* casting and other unsafe operations (`cast` operator, `zeroMem`, etc.) +* manual memory management (``alloc``, etc.) +* casting and other unsafe operations (``cast`` operator, ``zeroMem``, etc.) * file management * OS-specific operations * threading, coroutines * some modules of the standard library * proper 64-bit integer arithmetic -To compensate, the standard library has modules [catered to the JS backend]( -lib.html#pure-libraries-modules-for-js-backend) +To compensate, the standard library has modules `catered to the 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 +To compile a Nim module into a ``.js`` file use the ``js`` 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: -(http://nodejs.org): +(``_):: - ```cmd nim js -d:nodejs -r examples/hallo.nim - ``` -If you experience errors saying that `globalThis` is not defined, be +If you experience errors saying that ``globalThis`` is not defined, be sure to run a recent version of Node.js (at least 12.0). - + Interfacing =========== @@ -120,86 +110,85 @@ component?). Nim code calling the backend ---------------------------- -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]( -manual.html#implementation-specific-pragmas-importcpp-pragma) and -[ImportObjC](manual.html#implementation-specific-pragmas-importobjc-pragma) +Nim code can interface with the backend through the `Foreign function +interface `_ mainly through the +`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 `_ pragmas to call methods from classes. Whenever you use any of these pragmas you need to integrate native code into your final binary. In the case of JavaScript this is no problem at all, the same HTML file which hosts the generated JavaScript will likely provide other -JavaScript functions which you are importing with `importc`. +JavaScript functions which you are importing with ``importc``. 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]( -manual.html#foreign-function-interface-dynlib-pragma-for-import), though -more specific control can be gained using the [dynlib module](dynlib.html). +through the `dynlib pragma for import +`_, though +more specific control can be gained using the `dynlib module `_. -The [dynlibOverride](nimc.html#dynliboverride) command line switch allows +The `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](manual.html#implementation-specific-pragmas-compile-pragma) if +Nim wrappers designed to statically link source files can use the `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](manual.html#implementation-specific-pragmas-passl-pragma). +the `PassL pragma `_. -To wrap native code, take a look at the [c2nim tool]( -https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst) which helps +To wrap native code, take a look at the `c2nim tool `_ 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: - ```c +.. code-block:: c int addTwoIntegers(int a, int b) { return a + b; } - ``` Create a ``calculator.nim`` file with the following content: - ```nim +.. code-block:: 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 +With these two files in place, you can run ``nim c -r calculator.nim`` and the Nim compiler will compile the ``logic.c`` file in addition to -``calculator.nim`` and link both into an executable, which outputs `10` when +``calculator.nim`` and link both into an executable, which outputs ``10`` when 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: +be to remove the line with the ``compile`` pragma and run the following typical +Unix commands:: - ```cmd - gcc -c logic.c - ar rvs mylib.a logic.o - nim c --passL:mylib.a -r calculator.nim - ``` + $ 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): - ```nim +.. code-block:: 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](dom.html) for specific DOM querying and modification procs -or take a look at [karax](https://github.com/pragmagic/karax) for how to +Compile the Nim code to JavaScript with ``nim js -o:calculator.js +calculator.nim`` 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 develop browser-based applications. Backend code calling Nim ------------------------ -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 +Backend code can interface with Nim code exposed through the `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 +avoid any name collision, so the most significant thing the ``exportc`` pragma does is maintain the Nim symbol name, or if specified, use an alternative symbol for the backend in case the symbol rules don't match. The JavaScript target doesn't have any further interfacing considerations since it also has garbage collection, but the C targets require you to -initialize Nim's internals, which is done calling a `NimMain` function. +initialize Nim's internals, which is done calling a ``NimMain`` function. Also, C code requires you to specify a forward declaration for functions or the compiler will assume certain types for the return value and parameters which will likely make your program crash at runtime. -The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch. -Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`. +The Nim compiler can generate a C interface header through the ``--header`` +command-line switch. The generated header will contain all the exported +symbols and the ``NimMain`` proc which you need to call before any other +Nim code. -### Nim invocation example from C +Nim invocation example from C +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Create a ``fib.nim`` file with the following content: - ```nim +.. code-block:: 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: - ```c - #include +.. code-block:: c - int fib(int a); - void NimMain(); + #include "fib.h" + #include int main(void) { @@ -278,67 +268,64 @@ 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: +program:: - ```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 - ``` + $ nim c --noMain --noLinking --header:fib.h 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 -object files into a final binary. All the generated files are placed into the ``nimcache`` +generating a ``main()`` function in the generated files, avoid linking the +object files into a final binary, and explicitly generate a header file for C +integration. All the generated files are placed into the ``nimcache`` directory. That's why the next command compiles the ``maths.c`` source plus all the ``.c`` files from ``nimcache``. In addition to this path, you also 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: +also ask the Nim compiler to generate a statically linked library:: - ```cmd - nim c --app:staticLib --noMain fib.nim - gcc -o m -Inimcache -Ipath/to/nim/lib maths.c libfib.nim.a - ``` + $ nim c --app:staticLib --noMain --header fib.nim + $ gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c The Nim compiler will handle linking the source files generated in the ``nimcache`` directory into the ``libfib.nim.a`` static library, which you can -then link into your C program. Note that these commands are generic and will +then link into your C program. Note that these commands are generic and will vary for each system. For instance, on Linux systems you will likely need to -use `-ldl`:option: too to link in required dlopen functionality. +use ``-ldl`` 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): - ```nim +.. code-block:: 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 +Compile the Nim code to JavaScript with ``nim js -o:fib.js fib.nim`` and open ``mhost.html`` in a browser. If the browser supports javascript, you should see an alert box displaying the text ``Fib for 9 is 34``. As mentioned -earlier, JavaScript doesn't require an initialization call to `NimMain` or +earlier, JavaScript doesn't require an initialization call to ``NimMain`` or a similar function and you can call the exported Nim proc directly. @@ -348,14 +335,14 @@ 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]( -nimc.html#compiler-usage-commandminusline-switches) to change it. +use the ``--nimcache`` `compiler switch +`_ to change it. Memory management ================= -In the previous sections, the `NimMain()` function reared its head. Since +In the previous sections, the ``NimMain()`` function reared its head. Since JavaScript already provides automatic memory management, you can freely pass objects between the two languages without problems. In C and derivate languages you need to be careful about what you do and how you share memory. The @@ -367,26 +354,32 @@ aware of who controls what to avoid crashing. Strings and C strings --------------------- -The manual mentions that [Nim strings are implicitly convertible to -cstrings](manual.html#types-cstring-type) which makes interaction usually +The manual mentions that `Nim strings are implicitly convertible to +cstrings `_ 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 +``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](system.html#GC_ref,string) and [GC_unref]( -system.html#GC_unref,string). +as a ``cstring``, you will need to manually prevent the string data from being +freed with `GC_ref `_ and `GC_unref +`_. A similar thing happens with C code invoking Nim code which returns a -`cstring`. Consider the following proc: +``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`. +Since Nim's garbage collector is not aware of the C code, once the +``gimme`` proc has finished it can reclaim the memory of the ``cstring``. +However, from a practical standpoint, the C code invoking the ``gimme`` +function directly will be able to use it since Nim's garbage collector has +not had a chance to run *yet*. This gives you enough time to make a copy for +the C side of the program, as calling any further Nim procs *might* trigger +garbage collection making the previously returned string garbage. Or maybe you +are `yourself triggering the collection `_. Custom data types @@ -394,15 +387,43 @@ 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]( -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 +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 required anymore. Again, if you are wrapping a library which *mallocs* and *frees* data structures, you need to expose the appropriate *free* function to Nim so you can clean it up. And of course, once cleaned you should avoid accessing it from Nim (or C for that matter). Typically C data structures have their own -`malloc_structure`:c: and `free_structure`:c: specific functions, so wrapping +``malloc_structure`` and ``free_structure`` specific functions, so wrapping these for the Nim side should be enough. + + +Thread coordination +------------------- + +When the ``NimMain()`` function is called Nim initializes the garbage +collector to the current thread, which is usually the main thread of your +application. If your C code later spawns a different thread and calls Nim +code, the garbage collector will fail to work properly and you will crash. + +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 + + 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 +lived. + +Before the thread exits, you should tear down the thread's GC to prevent memory +leaks by calling + +.. code-block:: nim + + system.tearDownForeignThreadGc() diff --git a/doc/basicopt.txt b/doc/basicopt.txt index e8133d227..a2db1c92e 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -1,9 +1,10 @@ +:: nim command [options] [projectfile] [arguments] 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) @@ -26,11 +27,11 @@ Options: -a, --assertions:on|off turn assertions on|off --opt:none|speed|size optimize not at all or for speed|size Note: use -d:release for a release build! - --debugger:native use native debugger (gdb) + --debugger:native Use native debugger (gdb) --app:console|gui|lib|staticlib generate a console app|GUI app|DLL|static library -r, --run run the compiled program with given arguments - --eval:cmd evaluate nim code directly; e.g.: `nim --eval:"echo 1"` + --eval:cmd evaluates nim code directly; e.g.: `nim --eval:"echo 1"` defaults to `e` (nimscript) but customizable: `nim r --eval:'for a in stdin.lines: echo a'` --fullhelp show all command line switches diff --git a/doc/contributing.md b/doc/contributing.md deleted file mode 100644 index d331c88c1..000000000 --- a/doc/contributing.md +++ /dev/null @@ -1,798 +0,0 @@ -============ -Contributing -============ - -.. default-role:: code -.. include:: rstcommon.rst - -.. contents:: - - -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 -of the issue. - -The PR has to be approved by two core developers or by Araq. - - - -Writing tests -============= - -There are 4 types of tests: - -1. `runnableExamples` documentation comment tests, ran by `nim doc mymod.nim`:cmd: - These end up in documentation and ensure documentation stays in sync with code. - -2. separate test files, e.g.: ``tests/stdlib/tos.nim``. - In nim repo, `testament`:cmd: (see below) runs all - ``$nim/tests/*/t*.nim`` test files; - for nimble packages, see https://github.com/nim-lang/nimble#tests. - -3. (deprecated) tests in `when isMainModule:` block, ran by `nim r mymod.nim`:cmd:. - `nimble test`:cmd: can run those in nimble packages when specified in a - `task "test"`. - -4. (not preferred) ``.. code-block:: nim`` RST snippets; - these should only be used in rst sources, - in nim sources `runnableExamples` should now always be preferred to those for - several reasons (cleaner syntax, syntax highlights, batched testing, and - parameter `rdoccmd` allows customization). - -Not all the tests follow the convention here, feel free to change the ones -that don't. Always leave the code cleaner than you found it. - -Stdlib ------- - -Each stdlib module (anything under ``lib/``, e.g. ``lib/pure/os.nim``) should -preferably have a corresponding separate test file, e.g. ``tests/stdlib/tos.nim``. -The old convention was to add a `when isMainModule:` block in the source file, -which only gets executed when the tester is building the file. - -Each test should be in a separate `block:` statement, such that -each has its own scope. Use boolean conditions and `doAssert` for the -testing by itself, don't rely on echo statements or similar; in particular, avoid -things like `echo "done"`. Don't use `unittest.suite` and `unittest.test`. - -Sample test: - - ```nim - block: # foo - doAssert foo(1) == 10 - - block: # bug #1234 - static: doAssert 1+1 == 2 - - block: # bug #1235 - var seq2D = newSeqWith(4, newSeq[bool](2)) - seq2D[0][0] = true - seq2D[1][0] = true - seq2D[0][1] = true - doAssert seq2D == @[@[true, true], @[true, false], - @[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 -extensions (e.g. https://github.com/sindresorhus/refined-github) will even turn those -in clickable links when it works. - -Rationale for using a separate test file instead of `when isMainModule:` block: -* allows custom compiler flags or testing options (see details below) -* faster CI since they can be joined in ``megatest`` (combined into a single test) -* avoids making the parser do un-necessary work when a source file is merely imported -* avoids mixing source and test code when reporting line of code statistics or code coverage - -Compiler --------- - -The tests for the compiler use a testing tool called `testament`:cmd:. They are all -located in ``tests/`` (e.g.: ``tests/destructor/tdestructor3.nim``). -Each test has its own file. All test files are prefixed with `t`. If you want -to create a file for import into another test only, use the prefix `m`. - -At the beginning of every test is the expected behavior of the test. -Possible keys are: - -- `cmd`: A compilation command template e.g. `nim $target --threads:on $options $file`:cmd: -- `output`: The expected output (stdout + stderr), most likely via `echo` -- `exitcode`: Exit code of the test (via `exit(number)`) -- `errormsg`: The expected compiler error message -- `file`: The file the errormsg was produced at -- `line`: The line the errormsg was produced at - -For a full spec, see here: ``testament/specs.nim`` - -An example of a test: - - ```nim - discard """ - errormsg: "type mismatch: got (PTest)" - """ - - type - PTest = ref object - - proc test(x: PTest, y: int) = nil - - var buf: PTest - buf.test() - ``` - - -Running tests -============= - -You can run the tests with - - ```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 - -```cmd - ./koch tests --failing all -``` - -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. - - ```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: - - ```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 -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: -commands. - - -Comparing tests -=============== - -Test failures can be grepped using ``Failure:``. - -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. - - ```cmd - git checkout devel - DEVEL_COMMIT=$(git rev-parse HEAD) - ./koch tests - ``` - -Then switch over to your changes and run the tester again. - - ```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. - - ```cmd - ./koch tests --print html $DEVEL_COMMIT - ``` - - -Deprecation -=========== - -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 - - # for (const/var/let/fields) the msg is not yet supported: - const Foo {.deprecated.} = 1 - - # for enum types, you can deprecate the type or some elements - # (likewise with object types and their fields): - type Bar {.deprecated.} = enum bar0, bar1 - type Barz = enum baz0, baz1 {.deprecated.}, baz2 - ``` - - -See also [Deprecated](manual.html#pragmas-deprecated-pragma) -pragma in the manual. - - -Documentation -============= - -When contributing new procs, be sure to add documentation, especially if -the proc is public. Even private procs benefit from documentation and can be -viewed using `nim doc --docInternal foo.nim`:cmd:. -Documentation begins on the line -following the `proc` definition, and is prefixed by `##` on each line. - -Runnable code examples are also encouraged, to show typical behavior with a few -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. - - ```nim - proc addBar*(a: string): string = - ## Adds "Bar" to `a`. - runnableExamples: - assert "baz".addBar == "bazBar" - result = a & "Bar" - ``` - -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: - - ````nim - proc someProc*(): string = - ## Returns "something" - ## - ## ``` - ## echo someProc() # "something" - ## ``` - result = "something" # single-hash comments do not produce documentation - ```` - -The \`\`\` followed by a newline and an indentation instructs the -`nim doc`:cmd: command to produce syntax-highlighted example code with the -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. - - ```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: - - ```nim - proc hello*(): string = - ## Returns "hello" - result = "hello" - ``` - -or - - ```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: - - ```nim - ## A higher level `SQLite`:idx: database wrapper. - ``` - -.. _`Nim Index`: https://nim-lang.org/docs/theindex.html - -Inline monospaced text can be input using \`single backticks\` or -\`\`double backticks\`\`. The former are syntactically highlighted, -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 - programming languages, including identifiers, in ``*.nim`` files. - - For languages other than Nim add a role after final backtick, - e.g. for C++ inline highlighting: - - `#include `:cpp: - - For a currently unsupported language add the `:code:` role, - like for SQL in this example: - - `SELECT * FROM ;`:code: - - Highlight shell commands by ``:cmd:`` role; for command line options use - ``:option:`` role, e.g.: \`--docInternal\`:option:. - -* Use double backticks: - - * For file names: \`\`os.nim\`\` - * For fragments of strings **not** enclosed by `"` and `"` and not - related to code, e.g. text of compiler messages - * 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 \[*]: - - .. role:: nim(code) - :language: nim - .. default-role:: nim - - The first 2 lines are for other RST implementations, - including Github one. - - \[*] this is fulfilled when ``doc/rstcommon.rst`` is included. - -Best practices -============== - -Note: these are general guidelines, not hard rules; there are always exceptions. -Code reviews can just point to a specific section here to save time and -propagate best practices. - -.. _define_needs_prefix: -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. - - ```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 - - ```nim - doAssert isValid() == true - doAssert isValid() # preferred - ``` - -.. _design_for_mcs: -Design with method call syntax chaining in mind - - ```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 - - ```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:. - - ```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 -intended to be used as `runnableExamples`, which for brevity use `assert` -instead of `doAssert`. Note that `nim doc -d:danger main`:cmd: won't pass `-d:danger`:option: to the -`runnableExamples`, but `nim doc --doccmd:-d:danger main`:cmd: would, and so would the -second example below: - - ```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.). - - ```nim - proc foo() = echo "bar" # bad - proc foo(): string = "bar" # preferred (usually) - ``` - -.. _use_Option: -(Ongoing debate) Consider using Option instead of return bool + var argument, -unless stack allocation is needed (e.g. for efficiency). - - ```nim - proc foo(a: var Bar): bool - proc foo(): Option[Bar] - ``` - -.. _use_doAssert_not_echo: -Tests (including in testament) should always prefer assertions over `echo`, -except when that's not possible. It's more precise, easier for readers and -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 - - ```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 -==================== - -General commit rules --------------------- - -1. Important, critical bugfixes that have a tiny chance of breaking - somebody's code should be backported to the latest stable release - branch (currently 1.4.x) and maybe also all the way back to the 1.0.x branch. - The commit message should contain the tag ``[backport]`` for "backport to the latest - stable release" and the tag ``[backport:$VERSION]`` for backporting back to the - given $VERSION (and all newer releases). - -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]( - 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. - - If you change something unrelated to the subject parts of the file, because - 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 - carefully your changes with `git add -p`:cmd:. - -4. Changes should not introduce any trailing whitespace. - - Always check your changes for whitespace errors using `git diff --check`:cmd: - or add the following ``pre-commit`` hook: - - ```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), 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 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 - merge can happen) - - e.g.: use `git pull --rebase origin devel`:cmd:. This is to avoid messing up - git history. - Exceptions should be very rare: when rebase gives too many conflicts, simply - squash all commits using the script shown in - https://github.com/nim-lang/Nim/pull/9356 - -8. Do not mix pure formatting changes (e.g. whitespace changes, nimpretty) or - automated changes (e.g. nimfix) with other code changes: these should be in - separate commits (and the merge on GitHub should not squash these into 1). - - -Continuous Integration (CI) ---------------------------- - -1. Continuous Integration is by default run on every push in a PR; this clogs - the CI pipeline and affects other PR's; if you don't need it (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 - (using custom logic, which should complete in < 1mn) as well as our former other pipelines: - [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, 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. - -Debugging CI failures, flaky tests, etc ---------------------------------------- - -1. First check the CI logs and search for `FAIL` to find why CI failed; if the - failure seems related to your PR, try to fix the code instead of restarting CI. - -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. - 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 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 - https://man.sr.ht/tutorials/set-up-account-and-git.md to generate and upload ssh keys. - - -Code reviews ------------- - -1. Whenever possible, use GitHub's new 'Suggested change' in code reviews, which - saves time explaining the change or applying it; see also - https://forum.nim-lang.org/t/4317 - -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]( - https://github.com/nim-lang/Nim/pull/10431#issuecomment-456968196): - - ```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.: - - # put this in ~/.gitconfig: - [core] - pager = "diff-so-fancy | less -R" # or: use: `diff-highlight` - - - -.. include:: docstyle.md - - -Evolving the stdlib -=================== - -As outlined in https://github.com/nim-lang/RFCs/issues/173 there are a couple -of guidelines about what should go into the stdlib, what should be added and -what eventually should be removed. - - -What the compiler itself needs must be part of the stdlib ---------------------------------------------------------- - -Maybe in the future the compiler itself can depend on Nimble packages but for -the time being, we strive to have zero dependencies in the compiler as the -compiler is the root of the bootstrapping process and is also used to build -Nimble. - - -Vocabulary types must be part of the stdlib -------------------------------------------- - -These are types most packages need to agree on for better interoperability, -for example `Option[T]`. This rule also covers the existing collections like -`Table`, `CountTable` etc. "Sorted" containers based on a tree-like data -structure are still missing and should be added. - -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 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. - -This covers modules like `os`, `osproc`, `strscans`, `strutils`, -`strformat`, etc. - - -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, 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 -as external, successful Nimble packages. - - - -Other new stdlib modules do not start as stdlib modules -------------------------------------------------------- - -As we strive for higher quality everywhere, it's easier to adopt existing, -battle-tested modules eventually rather than creating modules from scratch. - - -Little additions are acceptable -------------------------------- - -As long as they are documented and tested well, adding little helpers -to existing modules is acceptable. For two reasons: - -1. It makes Nim easier to learn and use in the long run. - ("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, 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 - that nobody uses. - -Conventions ------------ -1. New stdlib modules should go under ``Nim/lib/std/``. The rationale is to - require users to import via `import std/foo` instead of `import foo`, - which would cause potential conflicts with nimble packages. - Note that this still applies for new modules in existing logical - directories, e.g.: use ``lib/std/collections/foo.nim``, - not ``lib/pure/collections/foo.nim``. - -2. New module names should prefer plural form whenever possible, e.g.: - ``std/sums.nim`` instead of ``std/sum.nim``. In particular, this reduces - chances of conflicts between module name and the symbols it defines. - Furthermore, module names should use `snake_case` and not use capital - letters, which cause issues when going from an OS without case - sensitivity to an OS with it. - - -Breaking Changes -================ - -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 -difficult to make partial upgrades with only benign changes. When one library depends on -a legacy behavior, it can no longer be used together with another library that does not, -breaking all downstream applications - the standard library is unique in that it sits at -the root of **all** dependency trees. - -There is a big difference between compile-time breaking changes and run-time breaking -changes. - - -Run-time breaking changes -------------------------- - -Run-time breaking changes are to be avoided at almost all costs: Nim is used for -mission critical applications which depend on behaviours that -are not covered by the test suite. As such, it's important that changes to the -*stable* parts of the standard library are made avoiding changing the existing -behaviors, even when the test suite continues to pass. - -Examples of run-time breaking changes: - -- Raising exceptions of a new type, compared to what's currently being raised. - -- Adding unconstrained or poorly constrained generic procs or macros - ("hash now works for all `ref T`"): This may cause code to behave differently - depending only on which modules are imported - common examples include `==` and `hash`. - -- Changing behavior of existing functions like: - - * "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 -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 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 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 -Nim compiler developers, not for Nim users.) - -Above all else, additive approaches that don't change existing behaviors -should be preferred. - - -Compile-time breaking changes ------------------------------ - -Compile-time breaking changes are usually easier to handle, but for large code bases -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): - -* 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 - 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` - 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. -* Adding a new proc to an existing stdlib module. However, for aesthetic reasons - this is often preferred over introducing a new module with just a single proc - inside. We use good judgement and our list of "important packages" to see if - it doesn't break too much code out there in practice. The new procs need to - be annotated with a `.since` annotation. - - -Compiler/language spec bugfixes -------------------------------- - -This can even be applied to compiler "bugfixes": If the compiler should have been -"pickier" in its handling of `typedesc`, instead of "fixing typedesc handling bugs", -consider the following solution: - -- Spec out how `typedesc` should really work and also spec out the cases where it - should not be allowed! -- Deprecate `typedesc` and name the new metatype something new like `typeArg`. -- Implement the spec. - - -Non-breaking changes --------------------- - -Examples of changes that are considered non-breaking (or acceptable breaking changes) include: - -* Creating a new module. -* Adding an overload to an already overloaded proc. -* Adding new default parameters to an existing proc. It is assumed that you do not - use Nim's stdlib procs's addresses (that you don't use them as first class entities). -* Changing the calling convention from `nimcall` to `inline` - (but first RFC https://github.com/nim-lang/RFCs/issues/396 needs to be implemented). -* Changing the behavior from "crashing" into some other, well documented result (including - raising a Defect, but not raising an exception that does not inherit from Defect). -* Adding new fields to an existing object. - -Nim's introspection facilities imply that strictly speaking almost every addition can -break somebody's code. It is impractical to care about these cases, a change that only -affects introspection is not considered to be a breaking change. diff --git a/doc/contributing.rst b/doc/contributing.rst new file mode 100644 index 000000000..d1837ac14 --- /dev/null +++ b/doc/contributing.rst @@ -0,0 +1,592 @@ +============ +Contributing +============ + +.. contents:: + + +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 PR has to be approved by two core developers or by Araq. + + + +Writing tests +============= + +There are 4 types of tests: + +1. ``runnableExamples`` documentation comment tests, ran by ``nim doc mymod.nim`` + These end up in documentation and ensure documentation stays in sync with code. + +2. separate test files, e.g.: ``tests/stdlib/tos.nim``. + In nim repo, `testament` (see below) runs all `$nim/tests/*/t*.nim` test files; + for nimble packages, see https://github.com/nim-lang/nimble#tests. + +3. (deprecated) tests in ``when isMainModule:`` block, ran by ``nim r mymod.nim``. + ``nimble test`` can run those in nimble packages when specified in a + `task "test"`. + +4. (not preferred) `.. code-block:: nim` RST snippets; these should only be used in rst sources, + in nim sources `runnableExamples` should now always be preferred to those for + several reasons (cleaner syntax, syntax highlights, batched testing, and + `rdoccmd` allows customization). + +Not all the tests follow the convention here, feel free to change the ones +that don't. Always leave the code cleaner than you found it. + +Stdlib +------ + +Each stdlib module (anything under ``lib/``, e.g. ``lib/pure/os.nim``) should +preferably have a corresponding separate test file, e.g. `tests/stdlib/tos.nim`. +The old convention was to add a ``when isMainModule:`` block in the source file, +which only gets executed when the tester is building the file. + +Each test should be in a separate ``block:`` statement, such that +each has its own scope. Use boolean conditions and ``doAssert`` for the +testing by itself, don't rely on echo statements or similar; in particular, avoid +things like `echo "done"`. Don't use `unittest.suite` and `unittest.test`. + +Sample test: + +.. code-block:: nim + + block: # bug #1234 + static: doAssert 1+1 == 2 + + block: # bug #1235 + var seq2D = newSeqWith(4, newSeq[bool](2)) + seq2D[0][0] = true + seq2D[1][0] = true + seq2D[0][1] = true + doAssert seq2D == @[@[true, true], @[true, false], + @[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 +extensions (e.g. https://github.com/sindresorhus/refined-github) will even turn those +in clickable links when it works. + +Rationale for using a separate test file instead of `when isMainModule:` block: +* allows custom compiler flags or testing options (see details below) +* faster CI since they can be joined in `megatest` (combined into a single test) +* avoids making the parser do un-necessary work when a source file is merely imported +* avoids mixing source and test code when reporting line of code statistics or code coverage + +Compiler +-------- + +The tests for the compiler use a testing tool called ``testament``. They are all +located in ``tests/`` (e.g.: ``tests/destructor/tdestructor3.nim``). +Each test has its own file. All test files are prefixed with ``t``. If you want +to create a file for import into another test only, use the prefix ``m``. + +At the beginning of every test is the expected behavior of the test. +Possible keys are: + +- ``cmd``: A compilation command template e.g. ``nim $target --threads:on $options $file`` +- ``output``: The expected output (stdout + stderr), most likely via ``echo`` +- ``exitcode``: Exit code of the test (via ``exit(number)``) +- ``errormsg``: The expected compiler error message +- ``file``: The file the errormsg was produced at +- ``line``: The line the errormsg was produced at + +For a full spec, see here: ``testament/specs.nim`` + +An example of a test: + +.. code-block:: nim + + discard """ + errormsg: "type mismatch: got (PTest)" + """ + + type + PTest = ref object + + proc test(x: PTest, y: int) = nil + + var buf: PTest + buf.test() + + +Running tests +============= + +You can run the tests with + +:: + + ./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 + +:: + + ./koch tests --failing all + +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. + +:: + + ./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: + +:: + + ./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 travis/appveyor), 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`` before running ``./koch`` +commands. + + +Comparing tests +=============== + +Test failures can be grepped using ``Failure:``. + +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. + +:: + + git checkout devel + DEVEL_COMMIT=$(git rev-parse HEAD) + ./koch tests + +Then switch over to your changes and run the tester again. + +:: + + 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. + +:: + + ./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 + + # 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 + + # for (const/var/let/fields) the msg is not yet supported: + const Foo {.deprecated.} = 1 + + # for enum types, you can deprecate the type or some elements + # (likewise with object types and their fields): + type Bar {.deprecated.} = enum bar0, bar1 + type Barz = enum baz0, baz1 {.deprecated.}, baz2 + + +See also `Deprecated `_ +pragma in the manual. + + +Documentation +============= + +When contributing new procs, be sure to add documentation, especially if +the proc is public. Even private procs benefit from documentation and can be +viewed using ``nim doc --docInternal foo.nim``. +Documentation begins on the line +following the ``proc`` definition, and is prefixed by ``##`` on each line. + +Runnable code examples are also encouraged, to show typical behavior with a few +test cases (typically 1 to 3 ``assert`` statements, depending on complexity). +These ``runnableExamples`` are automatically run by ``nim doc mymodule.nim`` +as well as ``testament`` and guarantee they stay in sync. + +.. code-block:: nim + proc addBar*(a: string): string = + ## Adds "Bar" to `a`. + runnableExamples: + assert "baz".addBar == "bazBar" + result = a & "Bar" + +See `parentDir `_ example. + +The RestructuredText Nim uses has a special syntax for including code snippets +embedded in documentation; these are not run by ``nim doc`` and therefore are +not guaranteed to stay in sync, so ``runnableExamples`` is usually preferred: + +.. code-block:: nim + + proc someproc*(): string = + ## Returns "something" + ## + ## .. code-block:: + ## 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 +``nim doc`` command to produce syntax-highlighted example code with the +documentation (``.. code-block::`` is sufficient from inside a nim module). + +When forward declaration is used, the documentation should be included with the +first appearance of the proc. + +.. code-block:: 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 + + proc hello*(): string = + ## Returns "hello" + result = "hello" + +or + +.. code-block:: nim + + proc hello*(): string = + ## say hello + result = "hello" + +the first is preferred. + + +Best practices +============== + +Note: these are general guidelines, not hard rules; there are always exceptions. +Code reviews can just point to a specific section here to save time and +propagate best practices. + +.. _define_needs_prefix: +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 + + # if in nim sources + when defined(allocStats): discard # bad, can cause conflicts + when defined(nimAllocStats): discard # preferred + # if in a pacakge `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 + + doAssert isValid() == true + doAssert isValid() # preferred + +.. _design_for_mcs: +Design with method call syntax chaining in mind + +.. code-block:: nim + + proc foo(cond: bool, lines: seq[string]) # bad + proc foo(lines: seq[string], cond: bool) # preferred + # can be called as: `getLines().foo(false)` + +.. _avoid_quit: +Use exceptions (including assert / doAssert) instead of ``quit`` +rationale: https://forum.nim-lang.org/t/4089 + +.. code-block:: nim + + quit() # bad in almost all cases + doAssert() # preferred + +.. _tests_use_doAssert: +Use ``doAssert`` (or ``require``, etc), not ``assert`` in all tests so they'll +be enabled even in release mode (except for tests in ``runnableExamples`` blocks +which for which ``nim doc`` ignores ``-d:release``). + +.. code-block:: nim + + when isMainModule: + assert foo() # bad + doAssert foo() # preferred + +.. _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 + + proc foo() = echo "bar" # bad + proc foo(): string = "bar" # preferred (usually) + +.. _use_Option: +[Ongoing debate] Consider using Option instead of return bool + var argument, +unless stack allocation is needed (e.g. for efficiency). + +.. code-block:: nim + + proc foo(a: var Bar): bool + proc foo(): Option[Bar] + +.. _use_doAssert_not_echo: +Tests (including in testament) should always prefer assertions over ``echo``, +except when that's not possible. It's more precise, easier for readers and +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 + + 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 stuff +============= + +General commit rules +-------------------- + +1. Important, critical bugfixes that have a tiny chance of breaking + somebody's code should be backported to the latest stable release + branch (currently 1.4.x) and maybe also all the way back to the 1.0.x branch. + The commit message should contain the tag ``[backport]`` for "backport to all + stable releases" and the tag ``[backport:$VERSION]`` for backporting to the + given $VERSION. + +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 + `_. + +3. All changes introduced by the commit (diff lines) must be related to the + subject of the commit. + + If you change something unrelated to the subject parts of the file, because + 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``, but review + carefully your changes with ``git add -p``. + +4. Changes should not introduce any trailing whitespace. + + Always check your changes for whitespace errors using ``git diff --check`` + or add the following ``pre-commit`` hook: + + .. code-block:: sh + + #!/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 + (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). + +7. Commits should be always be rebased against devel (so a fast forward + merge can happen) + + e.g.: use ``git pull --rebase origin devel``. This is to avoid messing up + git history. + Exceptions should be very rare: when rebase gives too many conflicts, simply + squash all commits using the script shown in + https://github.com/nim-lang/Nim/pull/9356 + +8. Do not mix pure formatting changes (e.g. whitespace changes, nimpretty) or + automated changes (e.g. nimfix) with other code changes: these should be in + separate commits (and the merge on GitHub should not squash these into 1). + + +Continuous Integration (CI) +--------------------------- + +1. Continuous Integration is by default run on every push in a PR; this clogs + the CI pipeline and affects other PR's; if you don't need it (e.g. for WIP or + documentation only changes), add ``[ci skip]`` to your commit message title. + This convention is supported by `Appveyor + `_ + and `Travis `_. + +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 + applies for updates on a PR: you can test commits on a separate private + branch before updating the main PR. + +Debugging CI failures, flaky tests, etc +--------------------------------------- + +1. First check the CI logs and search for `FAIL` to find why CI failed; if the + failure seems related to your PR, try to fix the code instead of restarting CI. + +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, + 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. + 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 + + +Code reviews +------------ + +1. Whenever possible, use GitHub's new 'Suggested change' in code reviews, which + saves time explaining the change or applying it; see also + https://forum.nim-lang.org/t/4317 + +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-block:: sh + + 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` or `diff-so-fancy`, e.g.: + + .. code-block:: sh + + # put this in ~/.gitconfig: + [core] + pager = "diff-so-fancy | less -R" # or: use: `diff-highlight` + + + +.. include:: docstyle.rst + + +Evolving the stdlib +=================== + +As outlined in https://github.com/nim-lang/RFCs/issues/173 there are a couple +of guidelines about what should go into the stdlib, what should be added and +what eventually should be removed. + + +What the compiler itself needs must be part of the stdlib +--------------------------------------------------------- + +Maybe in the future the compiler itself can depend on Nimble packages but for +the time being, we strive to have zero dependencies in the compiler as the +compiler is the root of the bootstrapping process and is also used to build +Nimble. + + +Vocabulary types must be part of the stdlib +------------------------------------------- + +These are types most packages need to agree on for better interoperability, +for example ``Option[T]``. This rule also covers the existing collections like +``Table``, ``CountTable`` etc. "Sorted" containers based on a tree-like data +structure are still missing and should be added. + +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 +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. + +This covers modules like ``os``, ``osproc``, ``strscans``, ``strutils``, +``strformat``, etc. + + +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 +"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 +as external, successful Nimble packages. + + + +Other new stdlib modules do not start as stdlib modules +------------------------------------------------------- + +As we strive for higher quality everywhere, it's easier to adopt existing, +battle-tested modules eventually rather than creating modules from scratch. + + +Little additions are acceptable +------------------------------- + +As long as they are documented and tested well, adding little helpers +to existing modules is acceptable. For two reasons: + +1. It makes Nim easier to learn and use in the long run. + ("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 + 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 + that nobody uses. + +Conventions +----------- +1. New stdlib modules should go under `Nim/lib/std/`. The rationale is to require +users to import via `import std/foo` instead of `import foo`, which would cause +potential conflicts with nimble packages. Note that this still applies for new modules +in existing logical directories, e.g.: +use `lib/std/collections/foo.nim`, not `lib/pure/collections/foo.nim`. + +2. New module names should prefer plural form whenever possible, e.g.: +`std/sums.nim` instead of `std/sum.nim`. In particular, this reduces chances of conflicts +between module name and the symbols it defines. Furthermore, is should use `snake_case` +and not use capital letters, which cause issues when going from an OS without case +sensitivity to an OS without it. diff --git a/doc/destructors.md b/doc/destructors.rst similarity index 53% rename from doc/destructors.md rename to doc/destructors.rst index a96ac35ef..e48e360dd 100644 --- a/doc/destructors.md +++ b/doc/destructors.rst @@ -5,8 +5,6 @@ Nim Destructors and Move Semantics :Authors: Andreas Rumpf :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: @@ -18,7 +16,7 @@ not use classical GC algorithms anymore but is based on destructors and move semantics. The new runtime's advantages are that Nim programs become oblivious to the involved heap sizes and programs are easier to write to make effective use of multi-core machines. As a nice bonus, files and sockets and -the like will not require manual `close` calls anymore. +the like will not require manual ``close`` calls anymore. This document aims to be a precise specification about how move semantics and destructors work in Nim. @@ -30,7 +28,8 @@ Motivating example With the language mechanisms described here, a custom seq could be written as: - ```nim +.. code-block:: nim + type myseq*[T] = object len, cap: int @@ -38,15 +37,9 @@ written as: proc `=destroy`*[T](x: var myseq[T]) = if x.data != nil: - for i in 0..= x.cap: - x.cap = max(x.len + 1, x.cap * 2) - x.data = cast[typeof(x.data)](realloc(x.data, x.cap * sizeof(T))) + if x.len >= x.cap: resize(x) x.data[x.len] = y inc x.len @@ -90,19 +81,17 @@ written as: for i in 0.. destruction pairs, a proc's return -type can be annotated as `lent T`. This is useful for "getter" accessors +type can be annotated as ``lent T``. This is useful for "getter" accessors that seek to allow an immutable view into a container. -The `sink` and `lent` annotations allow us to remove most (if not all) +The ``sink`` and ``lent`` annotations allow us to remove most (if not all) superfluous copies and destructions. -`lent T` is like `var T` a hidden pointer. It is proven by the compiler +``lent T`` is like ``var T`` a hidden pointer. It is proven by the compiler that the pointer does not outlive its origin. No destructor call is injected -for expressions of type `lent T` or of type `var T`. +for expressions of type ``lent T`` or of type ``var T``. - ```nim +.. code-block:: nim + type Tree = object kids: seq[Tree] @@ -542,81 +480,80 @@ 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 -================= +The .cursor annotation +====================== -Under the `--mm:arc|orc`:option: modes Nim's `ref` type is implemented -via the same runtime "hooks" and thus via reference counting. -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: +Under the ``--gc:arc|orc`` modes Nim's `ref` type is implemented via the same runtime +"hooks" and thus via reference counting. This means that cyclic structures cannot be freed +immediately (``--gc:orc`` ships with a cycle collector). With the ``.cursor`` annotation +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. Automatic reference counting also has the disadvantage that it introduces overhead -when iterating over linked structures. The `cursor` pragma can also be used +when iterating over linked structures. The ``.cursor`` annotation can also be used to avoid this overhead: - ```nim +.. code-block:: nim + var it {.cursor.} = listRoot while it != nil: use(it) it = it.next - ``` -In fact, `cursor` more generally prevents object construction/destruction pairs + +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 -use raw pointers (`ptr`) instead which is more cumbersome and also more dangerous -for Nim's evolution: Later on, the compiler can try to prove `cursor` pragmas -to be safe, but for `ptr` the compiler has to remain silent about possible +use raw pointers (``ptr``) instead which is more cumbersome and also more dangerous +for Nim's evolution: Later on, the compiler can try to prove ``.cursor`` annotations +to be safe, but for ``ptr`` the compiler has to remain silent about possible problems. Cursor inference / copy elision =============================== -The current implementation also performs `cursor` inference. Cursor inference is +The current implementation also performs `.cursor` inference. Cursor inference is a form of copy elision. To see how and when we can do that, think about this question: In `dest = src` when do we really have to *materialize* the full copy? - Only if `dest` or `src` are mutated -afterward. If `dest` is a local variable that is simple to analyze. And if `src` is a +afterwards. If `dest` is a local variable that is simple to analyze. And if `src` is a location derived from a formal parameter, we also know it is not mutated! In other words, we do a compile-time copy-on-write analysis. This means that "borrowed" views can be written naturally and without explicit pointer indirections: - ```nim +.. code-block:: nim + proc main(tab: Table[string, string]) = - let v = tab["key"] # inferred as cursor because 'tab' is not mutated. + 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 ============ -The hooks of a tuple type `(A, B, ...)` are generated by lifting the -hooks of the involved types `A`, `B`, ... to the tuple type. In -other words, a copy `x = y` is implemented -as `x[0] = y[0]; x[1] = y[1]; ...`, likewise for `=sink` and `=destroy`. +The hooks of a tuple type ``(A, B, ...)`` are generated by lifting the +hooks of the involved types ``A``, ``B``, ... to the tuple type. In +other words, a copy ``x = y`` is implemented +as ``x[0] = y[0]; x[1] = y[1]; ...``, likewise for ``=sink`` and ``=destroy``. -Other value-based compound types like `object` and `array` are handled -correspondingly. For `object` however, the compiler-generated hooks +Other value-based compound types like ``object`` and ``array`` are handled +correspondingly. For ``object`` however, the compiler-generated hooks can be overridden. This can also be important to use an alternative traversal of the involved data structure that is more efficient or in order to avoid deep recursions. @@ -628,7 +565,8 @@ Hook generation The ability to override a hook leads to a phase ordering problem: - ```nim +.. code-block:: nim + type Foo[T] = object @@ -639,7 +577,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 @@ -647,12 +585,12 @@ hooks for all types in *strategic places* so that an explicitly provided hook that comes too "late" can be detected reliably. These *strategic places* have been derived from the rewrite rules and are as follows: -- In the construct `let/var x = ...` (var/let binding) - hooks are generated for `typeof(x)`. -- In `x = ...` (assignment) hooks are generated for `typeof(x)`. -- In `f(...)` (function call) hooks are generated for `typeof(f(...))`. -- For every sink parameter `x: sink T` the hooks are generated - for `typeof(x)`. +- In the construct ``let/var x = ...`` (var/let binding) + hooks are generated for ``typeof(x)``. +- In ``x = ...`` (assignment) hooks are generated for ``typeof(x)``. +- In ``f(...)`` (function call) hooks are generated for ``typeof(f(...))``. +- For every sink parameter ``x: sink T`` the hooks are generated + for ``typeof(x)``. nodestroy pragma @@ -662,7 +600,8 @@ The experimental `nodestroy`:idx: pragma inhibits hook injections. This can be used to specialize the object traversal in order to avoid deep recursions: - ```nim +.. code-block:: nim + type Node = ref object x, y: int32 left, right: Node @@ -682,38 +621,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 should eventually be replaced by a better solution. - - -Copy on write -============= - -String literals are implemented as "copy on write". -When assigning a string literal to a variable, a copy of the literal won't be created. -Instead the variable simply points to the literal. -The literal is shared between different variables which are pointing to it. -The copy operation is deferred until the first write. - -For example: - - ```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: - - ```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.md b/doc/docgen.md deleted file mode 100644 index 9e0a83bab..000000000 --- a/doc/docgen.md +++ /dev/null @@ -1,869 +0,0 @@ -=================================== - Nim DocGen Tools Guide -=================================== - -:Author: Erik O'Leary -:Version: |nimversion| - -.. default-role:: code -.. include:: rstcommon.rst -.. contents:: - -.. importdoc:: markdown_rst.md, compiler/docgen.nim - -Introduction -============ - -This document describes the `documentation generation tools`:idx: built into -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. - -Quick start ------------ - -Generate HTML documentation for a file: - - ```cmd - nim doc .nim - ``` - -Generate HTML documentation for a whole project: - - ```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` - # See also `--docroot` to specify a relative root. - # to get search (dochacks.js) to work locally, you need a server otherwise - # CORS will prevent opening file:// urls; this works: - python3 -m http.server 7029 --directory htmldocs - # When --outdir is omitted it defaults to $projectPath/htmldocs, - # or `$nimcache/htmldocs` with `--usenimcache` which avoids clobbering your sources; - # and likewise without `--project`. - # Adding `-r` will open in a browser directly. - # Use `--showNonExports` to show non-exported fields of an exported type. - ``` - -Documentation Comments ----------------------- - -Any comments which are preceded by a double-hash (`##`), are interpreted as -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: - - ```nim - type Person* = object - ## This type contains a description of a person - name: string - age: int - ``` - -Outputs: - - Person* = object - name: string - age: int - - This type contains a description of a person - -Field documentation comments can be added to fields like so: - - ```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 -*exported* types/procedures/etc. - -It's recommended to always add exactly **one** space after `##` for readability -of comments — this extra space will be cropped from the parsed comments and -won't influence RST formatting. - -.. note:: Generally, this baseline indentation level inside a documentation - comment may not be 1: it can be any since it is determined by the offset - of the first non-whitespace character in the comment. - After that indentation **must** be consistent on the following lines of - the same comment. - If you still need to add an additional indentation at the very beginning - (for RST block quote syntax) use backslash \\ before it: - - ```nim - ## \ - ## - ## Block quote at the first line. - ## - ## Paragraph. - ``` - -Structuring output directories ------------------------------- - -Basic directory for output is set by `--outdir:OUTDIR`:option: switch, -by default `OUTDIR` is ``htmldocs`` sub-directory in the directory of -the processed file. - -There are 2 basic options as to how generated HTML output files are stored: - -1) complex hierarchy when docgen-compiling with `--project`:option:, - which follows directory structure of the project itself. - So `nim doc`:cmd: replicates project's directory structure - inside `--outdir:OUTDIR`:option: directory. - `--project`:option: is well suited for projects that have 1 main module. - File name clashes are impossible in this case. - -2) flattened structure, where user-provided script goes through all - needed input files and calls commands like `nim doc`:cmd: - with `--outdir:OUTDIR`:option: switch, thus putting all HTML (and - ``.idx``) files into 1 directory. - - .. Important:: Make sure that you don't have files with same base name - like ``x.nim`` and ``x.md`` in the same package, otherwise you'll - have name conflict for ``x.html``. - - .. Tip:: To structure your output directories and avoid file name - clashes you can split your project into - different *packages* -- parts of your repository that are - docgen-compiled with different `--outdir:OUTDIR`:option: options. - - An example of such strategy is Nim repository itself which has: - - * its stdlib ``.nim`` files from different directories and ``.md`` - documentation from ``doc/`` directory are all docgen-compiled - into `--outdir:web/upload//`:option: directory - * its ``.nim`` files from ``compiler/`` directory are docgen-compiled - into `--outdir:web/upload//compiler/`:option: directory. - Interestingly, it's compiled with complex hierarchy using - `--project`:option: switch. - - Contents of ``web/upload/`` are then deployed into Nim's - Web server. - - This output directory structure allows to work correctly with files like - ``compiler/docgen.nim`` (implementation) and ``doc/docgen.md`` (user - documentation) in 1 repository. - - -Index files ------------ - -Index (``.idx``) files are used for 2 different purposes: - -1. easy cross-referencing between different ``.nim`` and/or ``.md`` / ``.rst`` - files described in [Nim external referencing] -2. creating a whole-project index for searching of symbols and keywords, - see [Buildindex command]. - - -Document Types -============== - -Example of Nim file input -------------------------- - -The following examples will generate documentation for this sample -*Nim* module, aptly named ``doc/docgen_sample.nim``: - - ```nim file=docgen_sample.nim - ``` - -All the below commands save their output to ``htmldocs`` directory relative to -the directory of file; -hence the output for this sample will be in ``doc/htmldocs``. - -HTML ----- - -The generation of HTML documents is done via the `doc`:option: command. This command -takes either a single ``.nim`` file, outputting a single ``.html`` file with the same -base filename, or multiple ``.nim`` files, outputting multiple ``.html`` files and, -optionally, an index file. - -The `doc`:option: command: - - ```cmd - nim doc docgen_sample.nim - ``` - -Partial Output: - - ... - proc helloWorld(times: int) {.raises: [], tags: [].} - ... - -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. - -LaTeX ------ - -LaTeX files are intended to be converted to PDF, especially for offline -reading or making hard copies. (LaTeX output is oftentimes better than -HTML -> PDF conversion). - -The `doc2tex`:option: command: - - ```cmd - nim doc2tex docgen_sample.nim - cd htmldocs - xelatex docgen_sample.tex - xelatex docgen_sample.tex - # It is usually necessary to run `xelatex` 2 times (or even 3 times for - # 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``. - -JSON ----- - -The generation of JSON documents is done via the `jsondoc`:option: command. -This command takes in a ``.nim`` file and outputs a ``.json`` file with -the same base filename. -Note that this tool is built off of the `doc`:option: command -(previously `doc2`:option:), and contains the same information. - -The `jsondoc`:option: command: - - ```cmd - nim jsondoc docgen_sample.nim - ``` - -Output: - - { - "orig": "docgen_sample.nim", - "nimble": "", - "moduleDescription": "This module is a sample", - "entries": [ - { - "name": "helloWorld", - "type": "skProc", - "line": 5, - "col": 0, - "description": "Takes an integer and outputs as many "hello world!"s", - "code": "proc helloWorld(times: int) {.raises: [], tags: [].}" - } - ] - } - -Similarly to the old `doc`:option: command, the old `jsondoc`:option: command has been -renamed to `jsondoc0`:option:. - -The `jsondoc0`:option: command: - - ```cmd - nim jsondoc0 docgen_sample.nim - ``` - -Output: - - [ - { - "comment": "This module is a sample." - }, - { - "name": "helloWorld", - "type": "skProc", - "description": "Takes an integer and outputs as many "hello world!"s", - "code": "proc helloWorld*(times: int)" - } - ] - -Note that the `jsondoc`:option: command outputs its JSON without pretty-printing it, -while `jsondoc0`:option: outputs pretty-printed JSON. - - -Simple documentation links -========================== - -It's possible to use normal Markdown/RST syntax to *manually* -reference Nim symbols using HTML anchors, however Nim has an *automatic* -facility that makes referencing inside ``.nim`` and ``.md/.rst`` files and -between them easy and seamless. -The point is that such links will be resolved automatically -by `nim doc`:cmd: (or `md2html`:option:, or `jsondoc`:option:, -or `doc2tex`:option:, ...). And, unlike manual links, such automatic -links **check** that their target exists -- a warning is emitted for -any broken link, so you avoid broken links in your project. - -Nim treats both ``.md/.rst`` files and ``.nim`` modules (their doc comment -part) as *documents* uniformly. -Hence all directions of referencing are equally possible having the same syntax: - -1. ``.md/rst`` -> itself (internal). See [Markup local referencing]. -2. ``.md/rst`` -> external ``.md/rst``. See [Markup external referencing]. - To summarize, referencing in `.md`/`.rst` files was already described in - [Nim-flavored Markdown and reStructuredText] - (particularly it described usage of index files for referencing), - while in this document we focus on Nim-specific details. -3. ``.md/rst`` -> external ``.nim``. See [Nim external referencing]. -4. ``.nim`` -> itself (internal). See [Nim local referencing]. -5. ``.nim`` -> external ``.md/rst``. See [Markup external referencing]. -6. ``.nim`` -> external ``.nim``. See [Nim external referencing]. - -To put it shortly, local referencing always works out of the box, -external referencing requires to use ``.. importdoc:: `` -directive to import `file` and to ensure that the corresponding -``.idx`` file was generated. - - -Nim local referencing ---------------------- - -You can reference Nim identifiers from Nim documentation comments -inside their ``.nim`` file (or inside a ``.rst`` file included from -a ``.nim``). -This pertains to any exported symbol like `proc`, `const`, `iterator`, etc. -Syntax for referencing is basically a normal RST one: addition of -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. - -\[*] anchors' format is described in [HTML anchor generation] section below. - -If you have a constant: - - ```Nim - const pi* = 3.14 - ``` - -then it should be referenced in one of the 2 forms: - -A. non-qualified (no symbol kind specification): - - pi_ - -B. qualified (with symbol kind specification): - - `const pi`_ - -For routine kinds there are more options. Consider this definition: - - ```Nim - proc foo*(a: int, b: float): string - ``` - -Generally following syntax is allowed for referencing `foo`: - -* short (without parameters): - - A. non-qualified: - - foo_ - - B. qualified: - - `proc foo`_ - -* longer variants (with parameters): - - A. non-qualified: - - 1) specifying parameters names: - - `foo(a, b)`_ - - 2) specifying parameters types: - - `foo(int, float)`_ - - 3) specifying both names and types: - - `foo(a: int, b: float)`_ - - 4) output parameter can also be specified if you wish: - - `foo(a: int, b: float): string`_ - - B. qualified: all 4 options above are valid. - Particularly you can use the full format: - - `proc foo(a: int, b: float): string`_ - -.. Tip:: Avoid cluttering your text with extraneous information by using - one of shorter forms: - - binarySearch_ - `binarySearch(a, key, cmp)`_ - - Brevity is better for reading! If you use a short form and have an - ambiguity problem (see below) then just add some additional info. - -Symbol kind like `proc` can also be specified in the postfix form: - - `foo proc`_ - `walkDir(d: string) iterator`_ - -.. Warning:: An ambiguity in resolving documentation links may arise because of: - - 1. clash with other RST anchors - * manually setup anchors - * automatically set up, e.g. section names - 2. collision with other Nim symbols: - - * routines with different parameters can exist e.g. for - `proc` and `template`. In this case they are split between their - corresponding sections in output file. Qualified references are - useful in this case -- just disambiguate by referring to these - sections explicitly: - - See `foo proc`_ and `foo template`_. - - * because in Nim `proc` and `iterator` belong to different namespaces, - so there can be a collision even if parameters are the same. - Use `\`proc foo\`_`:literal: or `\`iterator foo\`_`:literal: then. - - Any ambiguity is always reported with Nim compiler warnings and an anchor - with higher priority is selected. Manual anchors have highest - priority, then go automatic RST anchors; then Nim-generated anchors - (while procs have higher priority than other Nim symbol kinds). - -Generic parameters can also be used. All in all, this long form will be -recognized fine: - - `proc binarySearch*[T; K](a: openArray[T], key: K, cmp: proc(T, K)): int`_ - -**Limitations**: - -1. The parameters of a nested routine type can be specified only with types - (without parameter names, see form A.2 above). - E.g. for this signature: - - ```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: - - `binarySearch(a: openArray[T], key: K, cmp: proc(T, K))`_ - `binarySearch(openArray[T], K, proc(T, K))`_ - `binarySearch(a, key, cmp)`_ -2. Default values in routine parameters are not recognized, one needs to - specify the type and/or name instead. E.g. for referencing `proc f(x = 7)` - use one of the mentioned forms: - - `f(int)`_ or `f(x)`_ or `f(x: int)`_. -3. Generic parameters must be given the same way as in the - definition of referenced symbol. - - * their names should be the same - * parameters list should be given the same way, e.g. without substitutions - between commas (,) and semicolons (;). - -.. Note:: A bit special case is operators - (as their signature is also defined with `\``): - - ```Nim - func `$`(x: MyType): string - func `[]`*[T](x: openArray[T]): T - ``` - - A short form works without additional backticks: - - `$`_ - `[]`_ - - However for fully-qualified reference copy-pasting backticks (`) into other - backticks will not work in our RST parser (because we use Markdown-like - inline markup rules). You need either to delete backticks or keep - them and escape with backslash \\: - - no backticks: `func $`_ - escaped: `func \`$\``_ - no backticks: `func [][T](x: openArray[T]): T`_ - escaped: `func \`[]\`[T](x: openArray[T]): T`_ - -.. Note:: Types that defined as `enum`, or `object`, or `tuple` can also be - referenced with those names directly (instead of `type`): - - type CopyFlag = enum - ... - ## Ref. `CopyFlag enum`_ - -Nim external referencing ------------------------- - -Just like for [Markup external referencing], which saves markup anchors, -the Nim symbols are also saved in ``.idx`` files, so one needs -to generate them beforehand, and they should be loaded by -an ``.. importdoc::`` directive. Arguments to ``.. importdoc::`` is a -comma-separated list of Nim modules or Markdown/RST documents. - -`--index:only`:option: tells Nim to only generate ``.idx`` file and -do **not** attempt to generate HTML/LaTeX output. -For ``.nim`` modules there are 2 alternatives to work with ``.idx`` files: - -1. using [Project switch] implies generation of ``.idx`` files, - however, if ``importdoc`` is called on upper modules as its arguments, - their ``.idx`` are not yet created. Thus one should generate **all** - required ``.idx`` first: - ```cmd - nim doc --project --index:only
    .nim - nim doc --project
    .nim - ``` -2. or run `nim doc --index:only `:cmd: command for **all** (used) - Nim modules in your project. Then run `nim doc ` on them for - output HTML generation. - - .. Warning:: A mere `nim doc --index:on`:cmd: may fail on an attempt to do - ``importdoc`` from another module (for which ``.idx`` was not yet - generated), that's why `--index:only`:option: shall be used instead. - - For ``.md``/``.rst`` markup documents point 2 is the only option. - -Then, you can freely use something like this in ``your_module.nim``: - - ```nim - ## .. importdoc:: user_manual.md, another_module.nim - - ... - ## Ref. [some section from User Manual]. - - ... - ## Ref. [proc f] - ## (assuming you have a proc `f` in ``another_module``). - ``` - -and compile it by `nim doc`:cmd:. Note that link text will -be automatically prefixed by the module name of symbol, -so you will see something like "Ref. [another_module: proc f](#)" -in the generated output. - -It's also possible to reference a whole module by prefixing or -suffixing full canonical module name with "module": - - Ref. [module subdir/name] or [subdir/name module]. - -Markup documents as a whole can be referenced just by their title -(or by their file name if the title was not set) without any prefix. - -.. Tip:: During development process the stage of ``.idx`` files generation - can be done only *once*, after that you use already generated ``.idx`` - files while working with a document *being developed* (unless you do - incompatible changes to *referenced* documents). - -.. Hint:: After changing a *referenced* document file one may need - to regenerate its corresponding ``.idx`` file to get correct results. - Of course, when referencing *internally* inside any given ``.nim`` file, - it's not needed, one can even immediately use any freshly added anchor - (a document's own ``.idx`` file is not used for resolving its internal links). - -If an ``importdoc`` directive fails to find a ``.idx``, then an error -is emitted. - -In case of such compilation failures please note that: - -* **all** relative paths, given to ``importdoc``, relate to insides of - ``OUTDIR``, and **not** project's directory structure. - -* ``importdoc`` searches for ``.idx`` in `--outdir:OUTDIR`:option: directory - (``htmldocs`` by default) and **not** around original modules, so: - - .. Tip:: look into ``OUTDIR`` to understand what's going on. - -* also keep in mind that ``.html`` and ``.idx`` files should always be - output to the same directory, so check this and, if it's not true, check - that both runs *with* and *without* `--index:only`:option: have all - other options the same. - -To summarize, for 2 basic options of [Structuring output directories] -compilation options are different: - -1) complex hierarchy with `--project`:option: switch. - - As the **original** project's directory structure is replicated in - `OUTDIR`, all passed paths are related to this structure also. - - E.g. if a module ``path1/module.nim`` does - ``.. importdoc:: path2/another.nim`` then docgen tries to load file - ``OUTDIR/path1/path2/another.idx``. - - .. Note:: markup documents are just placed into the specified directory - `OUTDIR`:option: by default (i.e. they are **not** affected by - `--project`:option:), so if you have ``PROJECT/doc/manual.md`` - document and want to use complex hirearchy (with ``doc/``), - compile it with `--docroot`:option:\: - ```cmd - # 1st stage - nim md2html --outdir:OUTDIR --docroot:/absolute/path/to/PROJECT \ - --index:only PROJECT/doc/manual.md - ... - # 2nd stage - nim md2html --outdir:OUTDIR --docroot:/absolute/path/to/PROJECT \ - PROJECT/doc/manual.md - ``` - - Then the output file will be placed as ``OUTDIR/doc/manual.idx``. - So if you have ``PROJECT/path1/module.nim``, then ``manual.md`` can - be referenced as ``../doc/manual.md``. - -2) flattened structure. - - E.g. if a module ``path1/module.nim`` does - ``.. importdoc:: path2/another.nim`` then docgen tries to load - ``OUTDIR/path2/another.idx``, so the path ``path1`` - does not matter and providing ``path2`` can be useful only - in the case it contains another package that was placed there - using `--outdir:OUTDIR/path2`:option:. - - The links' text will be prefixed as ``another: ...`` in both cases. - - .. Warning:: Again, the same `--outdir:OUTDIR`:option: option should - be provided to both `doc --index:only`:option: / - `md2html --index:only`:option: and final generation by - `doc`:option:/`md2html`:option: inside 1 package. - -To temporarily disable ``importdoc``, e.g. if you don't need -correct link resolution at the moment, use a `--noImportdoc`:option: switch -(only warnings about unresolved links will be generated for external references). - -Related Options -=============== - -Project switch --------------- - - ```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`` -belongs to. The index files and the corresponding ``theindex.html`` will -also be generated. - - -Index switch ------------- - - ```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. - -.. Note:: `--index`:option: switch only affects creation of ``.idx`` - index files, while user-searchable Index HTML file is created by - `buildIndex`:option: commmand. - -Buildindex command ------------------- - -Once index files have been generated for one or more modules, the Nim -compiler command `nim buildIndex directory`:cmd: can be run to go over all the index -files in the specified directory to generate a [theindex.html](theindex.html) -file: - - ```cmd - nim buildIndex -o:path/to/htmldocs/theindex.html path/to/htmldocs - ``` - -See source switch ------------------ - - ```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 -item on a GitHub repository. -You can click the link to see the implementation of the item. - -The `git.commit`:option: switch overrides the hardcoded `devel` branch in -``config/nimdoc.cfg``. -This is useful to link to a different branch e.g. `--git.commit:master`:option:, -or to a tag e.g. `--git.commit:1.2.3`:option: or a commit. - -Source URLs are generated as ``href="${url}/tree/${commit}/${path}#L${line}"`` -by default and thus compatible with GitHub but not with GitLab. - -Similarly, `git.devel`:option: switch overrides the hardcoded `devel` branch -for the `Edit` link which is also useful if you have a different working -branch than `devel` e.g. `--git.devel:master`:option:. - -Edit URLs are generated as ``href="${url}/tree/${devel}/${path}#L${line}"`` -by default. - -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. - - -Other Input Formats -=================== - -The *Nim compiler* also has support for RST (reStructuredText) files with -the `rst2html`:option: and `rst2tex`:option: commands. Documents like this one are -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](highlite.html). - -See [Markdown and RST markup languages](markdown_rst.html) for -usage of those commands. - -HTML anchor generation -====================== - -When you run the `rst2html`:option: command, all sections in the RST document will -get an anchor you can hyperlink to. Usually, you can guess the anchor lower -casing the section title and replacing spaces with dashes, and in any case, you -can get it from the table of contents. But when you run the `doc`:option: -command to generate API documentation, some symbol get one or two anchors at -the same time: a numerical identifier, or a plain name plus a complex name. - -The numerical identifier is just a random number. The number gets assigned -according to the section and position of the symbol in the file being processed -and you should not rely on it being constant: if you add or remove a symbol the -numbers may shuffle around. - -The plain name of a symbol is a simplified version of its fully exported -signature. Variables or constants have the same plain name symbol as their -complex name. The plain name for procs, templates, and other callable types -will be their unquoted value after removing parameters, return types, and -pragmas. The plain name allows short and nice linking of symbols that works -unless you have a module with collisions due to overloading. - -If you hyperlink a plain name symbol and there are other matches on the same -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**)\* - -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 -represented by their types and will be comma-separated. To the plain symbol a -suffix may be added depending on the type of the callable: - -============== ============== -Callable type Suffix -============== ============== -`proc`, `func` *empty string* -`macro` ``.m`` -`method` ``.e`` -`iterator` ``.i`` -`template` ``.t`` -`converter` ``.c`` -============== ============== - -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](system.html). - -* `type SomeSignedInt = int | int8 | int16 | int32 | int64` **=>** - [#SomeSignedInt](system.html#SomeSignedInt) -* `var globalRaiseHook: proc (e: ref E_Base): bool {.nimcall.}` **=>** - [#globalRaiseHook](system.html#globalRaiseHook) -* `const NimVersion = "0.0.0"` **=>** - [#NimVersion](system.html#NimVersion) -* `proc getTotalMem(): int {.rtl, raises: [], tags: [].}` **=>** - [#getTotalMem](system.html#getTotalMem) -* `proc len[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}` **=>** - [#len,seq[T]](system.html#len,seq[T]) -* `iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}` **=>** - [#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]( - 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] along with commands to generate -documentation from source or text files. You can programmatically generate -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()](rstgen.html#mergeIndexes,string). This section documents -the file format in detail. - -Index files are line-oriented and tab-separated (newline and tab characters -have to be escaped). Each line represents a record with 6 fields. -The content of these columns is: - -0. Discriminator tag denoting type of the index entry, allowed values are: - `markupTitle` - : a title for ``.md``/``.rst`` document - `nimTitle` - : a title of ``.nim`` module - `heading` - : heading of sections, can be both in Nim and markup files - `idx` - : terms marked with :idx: role - `nim` - : a Nim symbol - `nimgrp` - : a Nim group for overloadable symbols like `proc`s -1. Mandatory term being indexed. Terms can include quoting according to - Nim's rules (e.g. \`^\`). -2. Base filename plus anchor hyperlink (e.g. ``algorithm.html#*,int,SortOrder``). -3. Optional human-readable string to display as a hyperlink. If the value is not - present or is the empty string, the hyperlink will be rendered - using the term. Prefix whitespace indicates that this entry is - not for an API symbol but for a TOC entry. -4. Optional title or description of the hyperlink. Browsers usually display - this as a tooltip after hovering a moment over the hyperlink. -5. A line number of file where the entry was defined. - -The index generation tools differentiate between documentation -generated from ``.nim`` files and documentation generated from ``.md`` or -``.rst`` files by tag `nimTitle` or `markupTitle` in the 1st line of -the ``.idx`` file. - -.. TODO Normal symbols are added to the index with surrounding whitespaces removed. An - exception to this are the table of content (TOC) entries. TOC entries are added to - the index file with their third column having as much prefix spaces as their - level is in the TOC (at least 1 character). The prefix whitespace helps to - filter TOC entries from API or text symbols. This is important because the - amount of spaces is used to replicate the hierarchy for document TOCs in the - final index, and TOC entries found in ``.nim`` files are discarded. - - -Additional resources -==================== - -* [Nim Compiler User Guide](nimc.html#compiler-usage-commandminusline-switches) - -* 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 implementation is in [module compiler/docgen]. - -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](rstgen.html) in your -programs if you want to reuse the compiler's documentation generation procs. diff --git a/doc/docgen.rst b/doc/docgen.rst new file mode 100644 index 000000000..bbf0838bb --- /dev/null +++ b/doc/docgen.rst @@ -0,0 +1,392 @@ +=================================== + Nim DocGen Tools Guide +=================================== + +:Author: Erik O'Leary +:Version: |nimversion| + +.. contents:: + + +Introduction +============ + +This document describes the `documentation generation tools`:idx: built into +the `Nim compiler `_, which can generate HTML 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. + +Quick start +----------- + +Generate HTML documentation for a file: + +:: + nim doc .nim + +Generate HTML documentation for a whole project: + +:: + # 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` + # See also `--docroot` to specify a relative root. + # to get search (dochacks.js) to work locally, you need a server otherwise + # CORS will prevent opening file:// urls; this works: + python3 -m http.server 7029 --directory htmldocs + # When --outdir is omitted it defaults to $projectPath/htmldocs, + 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 +Nim module authors the ability to easily generate richly formatted +documentation with only their well-documented code. + +Example: + +.. code-block:: nim + type Person* = object + ## This type contains a description of a person + name: string + age: int + +Outputs:: + Person* = object + name: string + age: int + +This type contains a description of a person + +Field documentation comments can be added to fields like so: + +.. code-block:: 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 +*exported* types/procedures/etc. + + +Nim file input +----------------- + +The following examples will generate documentation for the below contrived +*Nim* module, aptly named 'sample.nim' + +sample.nim: + +.. code-block:: nim + ## This module is a sample. + + import strutils + + proc helloWorld*(times: int) = + ## Takes an integer and outputs + ## as many "hello world!"s + + for i in 0 .. times-1: + echo "hello world!" + + helloWorld(5) + + +Document Types +============== + + +HTML +---- + +The generation of HTML documents is done via the ``doc`` command. This command +takes either a single .nim file, outputting a single .html file with the same +base filename, or multiple .nim files, outputting multiple .html files and, +optionally, an index file. + +The ``doc`` command:: + nim doc sample + +Partial Output:: + ... + proc helloWorld(times: int) {.raises: [], tags: [].} + ... + +The full output can be seen here: `docgen_sample.html `_. +It runs after semantic checking and includes pragmas attached implicitly by the +compiler. + + +JSON +---- + +The generation of JSON documents is done via the ``jsondoc`` command. This command +takes in a .nim file and outputs a .json file with the same base filename. Note +that this tool is built off of the ``doc`` command (previously ``doc2``), and +contains the same information. + +The ``jsondoc`` command:: + nim jsondoc sample + +Output:: + { + "orig": "docgen_sample.nim", + "nimble": "", + "moduleDescription": "This module is a sample", + "entries": [ + { + "name": "helloWorld", + "type": "skProc", + "line": 5, + "col": 0, + "description": "Takes an integer and outputs as many "hello world!"s", + "code": "proc helloWorld(times: int) {.raises: [], tags: [].}" + } + ] + } + +Similarly to the old ``doc`` command, the old ``jsondoc`` command has been +renamed to ``jsondoc0``. + +The ``jsondoc0`` command:: + nim jsondoc0 sample + +Output:: + [ + { + "comment": "This module is a sample." + }, + { + "name": "helloWorld", + "type": "skProc", + "description": "Takes an integer and outputs as many "hello world!"s", + "code": "proc helloWorld*(times: int)" + } + ] + +Note that the ``jsondoc`` command outputs it's JSON without pretty-printing it, +while ``jsondoc0`` outputs pretty-printed JSON. + +Related Options +=============== + +Project switch +-------------- + +:: + 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`` +belongs to. + + +Index switch +------------ + +:: + 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. + +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 `_ +file. + +See source switch +----------------- + +:: + nim doc --git.url: filename.nim + +With the ``git.url`` switch the *See source* hyperlink will appear below each +documented item in your source code pointing to the implementation of that +item on a GitHub repository. +You can click the link to see the implementation of the item. + +The ``git.commit`` switch overrides the hardcoded `devel` branch in config/nimdoc.cfg. +This is useful to link to a different branch e.g. `--git.commit:master`, +or to a tag e.g. `--git.commit:1.2.3` or a commit. + +Source URLs are generated as `href="${url}/tree/${commit}/${path}#L${line}"` by default and this compatible with GitHub but not with GitLab. + +Similarly, ``git.devel`` switch overrides the hardcoded `devel` branch for the `Edit` link which is also useful if you have a different working branch than `devel` e.g. `--git.devel:master`. + +Edit URLs are generated as `href="${url}/tree/${devel}/${path}#L${line}"` by default. + +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. + + +Other Input Formats +=================== + +The *Nim compiler* also has support for RST (reStructuredText) files with +the ``rst2html`` and ``rst2tex`` commands. Documents like this one are +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 C++ and some other c-like languages. + +Usage:: + nim rst2html docgen.txt + +Output:: + You're reading it! + +The ``rst2tex`` command is invoked identically to ``rst2html``, but outputs +a .tex file instead of .html. + + +HTML anchor generation +====================== + +When you run the ``rst2html`` command, all sections in the RST document will +get an anchor you can hyperlink to. Usually, you can guess the anchor lower +casing the section title and replacing spaces with dashes, and in any case, you +can get it from the table of contents. But when you run the ``doc`` +command to generate API documentation, some symbol get one or two anchors at +the same time: a numerical identifier, or a plain name plus a complex name. + +The numerical identifier is just a random number. The number gets assigned +according to the section and position of the symbol in the file being processed +and you should not rely on it being constant: if you add or remove a symbol the +numbers may shuffle around. + +The plain name of a symbol is a simplified version of its fully exported +signature. Variables or constants have the same plain name symbol as their +complex name. The plain name for procs, templates, and other callable types +will be their unquoted value after removing parameters, return types, and +pragmas. The plain name allows short and nice linking of symbols that works +unless you have a module with collisions due to overloading. + +If you hyperlink a plain name symbol and there are other matches on the same +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**)\* + +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 +represented by their types and will be comma-separated. To the plain symbol a +suffix may be added depending on the type of the callable: + +------------- -------------- +Callable type Suffix +------------- -------------- +proc *empty string* +macro ``.m`` +method ``.e`` +iterator ``.i`` +template ``.t`` +converter ``.c`` +------------- -------------- + +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 `_. + +* ``type SomeSignedInt = int | int8 | int16 | int32 | int64`` **=>** + `#SomeSignedInt `_ +* ``var globalRaiseHook: proc (e: ref E_Base): bool {.nimcall.}`` **=>** + `#globalRaiseHook `_ +* ``const NimVersion = "0.0.0"`` **=>** + `#NimVersion `_ +* ``proc getTotalMem(): int {.rtl, raises: [], tags: [].}`` **=>** + `#getTotalMem, `_ +* ``proc len[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}`` **=>** + `#len,seq[T] `_ +* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>** + `#pairs.i,seq[T] `_ +* ``template newException[](exceptn: typedesc; message: string; + parentException: ref Exception = nil): untyped`` **=>** + `#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 +documentation from source or text files. You can programmatically generate +indices with the `setIndexTerm() +`_ +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 +the file format in detail. + +Index files are line-oriented and tab-separated (newline and tab characters +have to be escaped). Each line represents a record with at least two fields +but can have up to four (additional columns are ignored). The content of these +columns is: + +1. Mandatory term being indexed. Terms can include quoting according to + Nim's rules (e.g. \`^\`). +2. Base filename plus anchor hyperlink (e.g. ``algorithm.html#*,int,SortOrder``). +3. Optional human-readable string to display as a hyperlink. If the value is not + present or is the empty string, the hyperlink will be rendered + using the term. Prefix whitespace indicates that this entry is + not for an API symbol but for a TOC entry. +4. Optional title or description of the hyperlink. Browsers usually display + this as a tooltip after hovering a moment over the hyperlink. + +The index generation tools try to differentiate between documentation +generated from ``.nim`` files and documentation generated from ``.txt`` or +``.rst`` files. The former are always closely related to source code and +consist mainly of API entries. The latter are generic documents meant for +human reading. + +To differentiate both types (documents and APIs), the index generator will add +to the index of documents an entry with the title of the document. Since the +title is the topmost element, it will be added with a second field containing +just the filename without any HTML anchor. By convention, this entry without +anchor is the *title entry*, and since entries in the index file are added as +they are scanned, the title entry will be the first line. The title for APIs +is not present because it can be generated concatenating the name of the file +to the word **Module**. + +Normal symbols are added to the index with surrounding whitespaces removed. An +exception to this are the table of content (TOC) entries. TOC entries are added to +the index file with their third column having as much prefix spaces as their +level is in the TOC (at least 1 character). The prefix whitespace helps to +filter TOC entries from API or text symbols. This is important because the +amount of spaces is used to replicate the hierarchy for document TOCs in the +final index, and TOC entries found in ``.nim`` files are discarded. + + +Additional resources +==================== + +`Nim Compiler User Guide `_ + +`RST Quick Reference +`_ + +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 +programs if you want to reuse the compiler's documentation generation procs. diff --git a/doc/docs.md b/doc/docs.rst similarity index 63% rename from doc/docs.md rename to doc/docs.rst index b6ff6d2c7..b34383253 100644 --- a/doc/docs.md +++ b/doc/docs.rst @@ -1,38 +1,38 @@ The documentation consists of several documents: -- | [Tutorial (part I)](tut1.html) +- | `Tutorial (part I) `_ | The Nim tutorial part one deals with the basics. -- | [Tutorial (part II)](tut2.html) +- | `Tutorial (part II) `_ | The Nim tutorial part two deals with the advanced language constructs. -- | [Tutorial (part III)](tut3.html) +- | `Tutorial (part III) `_ | The Nim tutorial part three about Nim's macro system. -- | [Language Manual](manual.html) +- | `Language Manual `_ | The Nim manual is a draft that will evolve into a proper specification. -- | [Library documentation](lib.html) +- | `Library documentation `_ | This document describes Nim's standard library. -- | [Compiler user guide](nimc.html) +- | `Compiler user guide `_ | The user guide lists command line arguments, special features of the compiler, etc. -- | [Tools documentation](tools.html) +- | `Tools documentation `_ | Description of some tools that come with the standard distribution. -- | [Memory management](mm.html) - | Additional documentation about Nim's memory management strategies +- | `GC `_ + | Additional documentation about Nim's multi-paradigm memory management strategies | and how to operate them in a realtime setting. -- | [Source code filters](filters.html) +- | `Source code filters `_ | The Nim compiler supports source code filters as a simple yet powerful builtin templating system. -- | [Internal documentation](intern.html) +- | `Internal documentation `_ | The internal documentation describes how the compiler is implemented. Read this if you want to hack the compiler. -- | [Index](theindex.html) +- | `Index `_ | The generated index. **Index + (Ctrl+F) == Joy** diff --git a/doc/docstyle.md b/doc/docstyle.rst similarity index 73% rename from doc/docstyle.md rename to doc/docstyle.rst index 291a34cf6..50ff81ac3 100644 --- a/doc/docstyle.md +++ b/doc/docstyle.rst @@ -4,52 +4,49 @@ Documentation Style General Guidelines ------------------ -* See also [nep1](nep1.html) which should probably be merged here. +* See also `nep1`_ 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:). + procs can be useful too (visible via ``nim doc --docInternal foo.nim``). * Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation, each sentence after the first should be complete and in present tense. * Documentation is parsed as a custom ReStructuredText (RST) with partial markdown support. * In nim sources, prefer single backticks to double backticks since it's simpler - and `nim doc`:cmd: supports it. Likewise with ``rst`` files: `nim rst2html`:cmd: will render those as monospace, and - adding ``.. default-role:: code`` to an ``rst`` file will also make those render as monospace when rendered directly - in tools such as github. -* (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). + and `nim doc` supports it (even in rst files with `nim rst2html`). +* In nim sources, for links, prefer `[link text](link.html)` to ``` `link text`_ ``` + since the syntax is simpler and markdown is more common (likewise, `nim rst2html` 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 -------------------------- -Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (`##`). -Sometimes `##[ multiline docs containing code ]##` is preferable, see ``lib/pure/times.nim``. +Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (``##``). +Sometimes ``##[ multiline docs containing code ]##`` is preferable, see ``lib/pure/times.nim``. Code samples are encouraged, and should follow the general RST syntax: - ````Nim +.. code-block:: Nim + ## The `universe` module computes the answer to life, the universe, and everything. ## - ## ``` - ## doAssert computeAnswerString() == 42 - ## ``` - ```` + ## .. code-block:: + ## 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. - ```Nim +.. code-block:: 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.). @@ -58,38 +55,38 @@ 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: - ```Nim +.. code-block:: 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. - ````Nim +.. code-block:: Nim + proc addThree*(x, y, z: int8): int = ## Adds three `int8` values, treating them as unsigned and ## truncating the result. ## - ## ``` - ## # things that aren't suitable for a `runnableExamples` go in code block: - ## echo execCmdEx("git pull") - ## drawOnScreen() - ## ``` + ## .. code-block:: + ## # things that aren't suitable for a `runnableExamples` go in code-block: + ## echo execCmdEx("git pull") + ## drawOnScreen() runnableExamples: - # `runnableExamples` is usually preferred to code blocks, when possible. + # `runnableExamples` is usually preferred to `code-block`, when possible. doAssert addThree(3, 125, 6) == -122 result = x +% y +% z - ```` -The command `nim doc`:cmd: will then correctly syntax highlight the Nim code within the documentation. +The command ``nim doc`` will then correctly syntax highlight the Nim code within the documentation. 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. - ```Nim +.. code-block:: Nim + type NamedQueue*[T] = object ## Provides a linked data structure with names ## throughout. It is named for convenience. I'm making @@ -97,12 +94,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: - ```Nim +.. code-block:: Nim + type NamedQueue*[T] = object ## Provides a linked data structure with names @@ -111,54 +108,53 @@ 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. - ```Nim +.. code-block:: Nim + type - ## Bad: this documentation disappears because it annotates the `type` keyword - ## above, not `NamedQueue`. + ## Bad: this documentation disappears because it annotates the ``type`` keyword + ## above, not ``NamedQueue``. NamedQueue*[T] = object name*: string ## This becomes the main documentation for the object, which ## 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. +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* = [ [1,2,3], [2,3,1], [3,1,2], - ] ## Doc comment for `SpreadArray`. - ``` + ] ## Doc comment for ``SpreadArray``. -Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (`#`). +Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (``#``). + +.. code-block:: Nim - ```Nim const 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: - ```Nim +.. code-block:: Nim + const BadMathVals* = [ 3.14, # π 2.72, # e 0.58, # γ ] ## A bunch of badly rounded values (including π!). - ``` diff --git a/doc/drnim.md b/doc/drnim.rst similarity index 57% rename from doc/drnim.md rename to doc/drnim.rst index 1dc2b550f..ee6e0ea17 100644 --- a/doc/drnim.md +++ b/doc/drnim.rst @@ -5,8 +5,6 @@ :Author: Andreas Rumpf :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: @@ -14,28 +12,28 @@ Introduction ============ This document describes the usage of the *DrNim* tool. DrNim combines -the Nim frontend with the [Z3](https://github.com/Z3Prover/z3) proof +the Nim frontend with the `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. DrNim currently only checks the sections of your code that are marked -via `staticBoundChecks: on`: +via ``staticBoundChecks: on``: + +.. code-block:: nim - ```nim {.push staticBoundChecks: on.} # <--- 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 the future. Later versions of the **Nim compiler** will **assume** that the checks inside -the `staticBoundChecks: on` environment have been proven correct and so +the ``staticBoundChecks: on`` environment have been proven correct and so it will **omit** the runtime checks. If you do not want this behavior, use -instead `{.push staticBoundChecks: defined(nimDrNim).}`. This way the +instead ``{.push staticBoundChecks: defined(nimDrNim).}``. This way the Nim compiler remains unaware of the performed proofs but DrNim will prove your code. @@ -43,8 +41,7 @@ your code. Installation ============ -Run `koch drnim`:cmd:, the executable will afterwards be -in ``$nim/bin/drnim``. +Run ``koch drnim``, the executable will afterwards be in ``$nim/bin/drnim``. Motivating Example @@ -53,7 +50,8 @@ Motivating Example The follow example highlights what DrNim can easily do, even without additional annotations: - ```nim +.. code-block:: nim + {.push staticBoundChecks: on.} proc sum(a: openArray[int]): int = @@ -63,14 +61,13 @@ 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: +detects it and produces the following error message:: - cannot prove: i <= len(a) + -1; counter example: i -> 0 a.len -> 0 [IndexCheck] + cannot prove: i <= len(a) + -1; counter example: i -> 0 a.len -> 0 [IndexCheck] -In other words for `i == 0` and `a.len == 0` (for example!) there would be +In other words for ``i == 0`` and ``a.len == 0`` (for example!) there would be an index out of bounds error. @@ -85,37 +82,37 @@ DrNim adds 4 additional annotations (pragmas) to Nim: - `assume`:idx: These pragmas are ignored by the Nim compiler so that they don't have to -be disabled via `when defined(nimDrNim)`. +be disabled via ``when defined(nimDrNim)``. Invariant --------- -An `invariant` is a proposition that must be true after every loop +An ``invariant`` is a proposition that must be true after every loop iteration, it's tied to the loop body it's part of. Requires -------- -A `requires` annotation describes what the function expects to be true -before it's called so that it can perform its operation. A `requires` +A ``requires`` annotation describes what the function expects to be true +before it's called so that it can perform its operation. A ``requires`` annotation is also called a `precondition`:idx:. Ensures ------- -An `ensures` annotation describes what will be true after the function -call. An `ensures` annotation is also called a `postcondition`:idx:. +An ``ensures`` annotation describes what will be true after the function +call. An ``ensures`` annotation is also called a `postcondition`:idx:. Assume ------ -An `assume` annotation describes what DrNim should **assume** to be true +An ``assume`` annotation describes what DrNim should **assume** to be true in this section of the program. It is an unsafe escape mechanism comparable -to Nim's `cast` statement. Use it only when you really know better +to Nim's ``cast`` statement. Use it only when you really know better than DrNim. You should add a comment to a paper that proves the proposition you assume. @@ -125,7 +122,8 @@ Example: insertionSort **Note**: This example does not yet work with DrNim. - ```nim +.. code-block:: nim + import std / logic proc insertionSort(a: var openArray[int]) {. @@ -141,65 +139,64 @@ Example: insertionSort {.invariant: forall(j in 1..k, i in 0..`. -A `prop` is either a comparison or a compound: +The basic syntax is ``ensures|requires|invariant: ``. +A ``prop`` is either a comparison or a compound:: - prop = nim_bool_expression - | prop 'and' prop - | prop 'or' prop - | prop '->' prop # implication - | prop '<->' prop - | 'not' prop - | '(' prop ')' # you can group props via () - | forallProp - | existsProp + prop = nim_bool_expression + | prop 'and' prop + | prop 'or' prop + | prop '->' prop # implication + | prop '<->' prop + | 'not' prop + | '(' prop ')' # you can group props via () + | forallProp + | existsProp - forallProp = 'forall' '(' quantifierList ',' prop ')' - existsProp = 'exists' '(' quantifierList ',' prop ')' + forallProp = 'forall' '(' quantifierList ',' prop ')' + existsProp = 'exists' '(' quantifierList ',' prop ')' - quantifierList = quantifier (',' quantifier)* - quantifier = 'in' nim_iteration_expression + quantifierList = quantifier (',' quantifier)* + quantifier = 'in' nim_iteration_expression -`nim_iteration_expression` here is an ordinary expression of Nim code -that describes an iteration space, for example `1..4` or `1.. a.len`. +``nim_bool_expression`` here is an ordinary expression of Nim code of +type ``bool`` like ``a == 3`` or ``23 > a.len``. The supported subset of Nim code that can be used in these expressions -is currently underspecified but `let` variables, function parameters -and `result` (which represents the function's final result) are amenable +is currently underspecified but ``let`` variables, function parameters +and ``result`` (which represents the function's final result) are amenable for verification. The expressions must not have any side-effects and must terminate. -The operators `forall`, `exists`, `->`, `<->` have to imported -from `std / logic`. +The operators ``forall``, ``exists``, ``->``, ``<->`` have to imported +from ``std / logic``. diff --git a/doc/effects.txt b/doc/effects.txt index df624e8b0..4ed1d09f1 100644 --- a/doc/effects.txt +++ b/doc/effects.txt @@ -11,14 +11,13 @@ Iff a proc is side effect free and all its argument are evaluable at compile time, it can be evaluated by the compiler. However, really difficult is the ``newString`` proc: If it is simply wrapped, it should not be evaluated at compile time! On other occasions it can -and should be evaluated: +and should be evaluted: - ```nim +.. code-block:: nim proc toUpper(s: string): string = result = newString(len(s)) for i in 0..len(s) - 1: result[i] = toUpper(s[i]) - ``` No, it really can always be evaluated. The code generator should transform ``s = "\0\0\0..."`` back into ``s = newString(...)``. diff --git a/doc/estp.md b/doc/estp.md deleted file mode 100644 index d0ef4f1ba..000000000 --- a/doc/estp.md +++ /dev/null @@ -1,206 +0,0 @@ -=================================================== - Embedded Stack Trace Profiler (ESTP) User Guide -=================================================== - -.. default-role:: code -.. include:: rstcommon.rst - -:Author: Andreas Rumpf -:Version: |nimversion| - - -Nim comes with a platform independent profiler - -the Embedded Stack Trace Profiler (ESTP). The profiler -is *embedded* into your executable. To activate the profiler you need to do: - -* compile your program with the `--profiler:on --stackTrace:on`:option: command - line options -* import the `nimprof` module -* run your program as usual. - -You can in fact look at `nimprof`'s source code to see how to implement -your own profiler. - -The setting `--profiler:on`:option: defines the conditional symbol `profiler`. -You can use `when compileOption("profiler")` to make the switch seamless. -If `profiler`:option: is `off`:option:, your program runs normally. -Otherwise your program is profiled. - -```nim -when compileOption("profiler"): - import nimprof -``` - -After your program has finished the profiler will create a -file ``profile_results.txt`` containing the profiling results. - -Since the profiler works by examining stack traces, it's essential that -the option `--stackTrace:on`:option: is active! Unfortunately this means that a -profiling build is much slower than a release build. - - -Memory profiler -=============== - -You can also use ESTP as a memory profiler to see which stack traces allocate -the most memory and thus create the most GC pressure. It may also help to -find memory leaks. To activate the memory profiler you need to do: - -* compile your program with the - `--profiler:off --stackTrace:on -d:memProfiler`:option: - command line options. Yes it's `--profiler:off`:option:. -* import the `nimprof` module -* run your program as usual. - -Define the symbol `ignoreAllocationSize` so that only the number of -allocations is counted and the sizes of the memory allocations do not matter. - - -Example results file -==================== - -The results file lists stack traces ordered by significance. - -The following example file has been generated by profiling the Nim compiler -itself: It shows that in total 5.4% of the runtime has been spent -in `crcFromRope` or its children. - -In general the stack traces show you immediately where the problem is because -the trace acts like an explanation; in traditional profilers you can only find -expensive leaf functions easily but the *reason* why they are invoked -often remains mysterious. - - total executions of each stack trace: - Entry: 0/3391 Calls: 84/4160 = 2.0% [sum: 84; 84/4160 = 2.0%] - newCrcFromRopeAux - crcFromRope - writeRopeIfNotEqual - shouldRecompile - writeModule - myClose - closePasses - processModule - CompileModule - CompileProject - CommandCompileToC - MainCommand - HandleCmdLine - nim - Entry: 1/3391 Calls: 46/4160 = 1.1% [sum: 130; 130/4160 = 3.1%] - updateCrc32 - newCrcFromRopeAux - crcFromRope - writeRopeIfNotEqual - shouldRecompile - writeModule - myClose - closePasses - processModule - CompileModule - CompileProject - CommandCompileToC - MainCommand - HandleCmdLine - nim - Entry: 2/3391 Calls: 41/4160 = 0.99% [sum: 171; 171/4160 = 4.1%] - updateCrc32 - updateCrc32 - newCrcFromRopeAux - crcFromRope - writeRopeIfNotEqual - shouldRecompile - writeModule - myClose - closePasses - processModule - CompileModule - CompileProject - CommandCompileToC - MainCommand - HandleCmdLine - nim - Entry: 3/3391 Calls: 41/4160 = 0.99% [sum: 212; 212/4160 = 5.1%] - crcFromFile - writeRopeIfNotEqual - shouldRecompile - writeModule - myClose - closePasses - processModule - CompileModule - CompileProject - CommandCompileToC - MainCommand - HandleCmdLine - nim - Entry: 4/3391 Calls: 41/4160 = 0.99% [sum: 253; 253/4160 = 6.1%] - updateCrc32 - crcFromFile - writeRopeIfNotEqual - shouldRecompile - writeModule - myClose - closePasses - processModule - CompileModule - CompileProject - CommandCompileToC - MainCommand - HandleCmdLine - nim - Entry: 5/3391 Calls: 32/4160 = 0.77% [sum: 285; 285/4160 = 6.9%] - pop - newCrcFromRopeAux - crcFromRope - writeRopeIfNotEqual - shouldRecompile - writeModule - myClose - closePasses - processModule - CompileModule - CompileProject - CommandCompileToC - MainCommand - HandleCmdLine - nim - Entry: 6/3391 Calls: 17/4160 = 0.41% [sum: 302; 302/4160 = 7.3%] - doOperation - forAllChildrenAux - pop - newCrcFromRopeAux - crcFromRope - writeRopeIfNotEqual - shouldRecompile - writeModule - myClose - closePasses - processModule - CompileModule - CompileProject - CommandCompileToC - MainCommand - HandleCmdLine - ... - nim - Entry: 7/3391 Calls: 14/4160 = 0.34% [sum: 316; 316/4160 = 7.6%] - Contains - isAccessible - interiorAllocatedPtr - gcMark - markStackAndRegisters - collectCTBody - collectCT - rawNewObj - newObj - newNode - copyTree - matchesAux - matches - resolveOverloads - semOverloadedCall - semOverloadedCallAnalyseEffects - ... - CommandCompileToC - MainCommand - HandleCmdLine diff --git a/doc/estp.rst b/doc/estp.rst new file mode 100644 index 000000000..805a84eb7 --- /dev/null +++ b/doc/estp.rst @@ -0,0 +1,195 @@ +=================================================== + Embedded Stack Trace Profiler (ESTP) User Guide +=================================================== + +:Author: Andreas Rumpf +:Version: |nimversion| + + +Nim comes with a platform independent profiler - +the Embedded Stack Trace Profiler (ESTP). The profiler +is *embedded* into your executable. To activate the profiler you need to do: + +* compile your program with the ``--profiler:on --stackTrace:on`` command + line options +* import the ``nimprof`` module +* run your program as usual. + +You can in fact look at ``nimprof``'s source code to see how to implement +your own profiler. + +The setting ``--profiler:on`` defines the conditional symbol ``profiler``. + +After your program has finished the profiler will create a +file ``profile_results.txt`` containing the profiling results. + +Since the profiler works by examining stack traces, it's essential that +the option ``--stackTrace:on`` is active! Unfortunately this means that a +profiling build is much slower than a release build. + + +Memory profiler +=============== + +You can also use ESTP as a memory profiler to see which stack traces allocate +the most memory and thus create the most GC pressure. It may also help to +find memory leaks. To activate the memory profiler you need to do: + +* compile your program with the ``--profiler:off --stackTrace:on -d:memProfiler`` + command line options. Yes it's ``--profiler:off``. +* import the ``nimprof`` module +* run your program as usual. + +Define the symbol ``ignoreAllocationSize`` so that only the number of +allocations is counted and the sizes of the memory allocations do not matter. + + +Example results file +==================== + +The results file lists stack traces ordered by significance. + +The following example file has been generated by profiling the Nim compiler +itself: It shows that in total 5.4% of the runtime has been spent +in ``crcFromRope`` or its children. + +In general the stack traces show you immediately where the problem is because +the trace acts like an explanation; in traditional profilers you can only find +expensive leaf functions easily but the *reason* why they are invoked +often remains mysterious. + +:: + total executions of each stack trace: + Entry: 0/3391 Calls: 84/4160 = 2.0% [sum: 84; 84/4160 = 2.0%] + newCrcFromRopeAux + crcFromRope + writeRopeIfNotEqual + shouldRecompile + writeModule + myClose + closePasses + processModule + CompileModule + CompileProject + CommandCompileToC + MainCommand + HandleCmdLine + nim + Entry: 1/3391 Calls: 46/4160 = 1.1% [sum: 130; 130/4160 = 3.1%] + updateCrc32 + newCrcFromRopeAux + crcFromRope + writeRopeIfNotEqual + shouldRecompile + writeModule + myClose + closePasses + processModule + CompileModule + CompileProject + CommandCompileToC + MainCommand + HandleCmdLine + nim + Entry: 2/3391 Calls: 41/4160 = 0.99% [sum: 171; 171/4160 = 4.1%] + updateCrc32 + updateCrc32 + newCrcFromRopeAux + crcFromRope + writeRopeIfNotEqual + shouldRecompile + writeModule + myClose + closePasses + processModule + CompileModule + CompileProject + CommandCompileToC + MainCommand + HandleCmdLine + nim + Entry: 3/3391 Calls: 41/4160 = 0.99% [sum: 212; 212/4160 = 5.1%] + crcFromFile + writeRopeIfNotEqual + shouldRecompile + writeModule + myClose + closePasses + processModule + CompileModule + CompileProject + CommandCompileToC + MainCommand + HandleCmdLine + nim + Entry: 4/3391 Calls: 41/4160 = 0.99% [sum: 253; 253/4160 = 6.1%] + updateCrc32 + crcFromFile + writeRopeIfNotEqual + shouldRecompile + writeModule + myClose + closePasses + processModule + CompileModule + CompileProject + CommandCompileToC + MainCommand + HandleCmdLine + nim + Entry: 5/3391 Calls: 32/4160 = 0.77% [sum: 285; 285/4160 = 6.9%] + pop + newCrcFromRopeAux + crcFromRope + writeRopeIfNotEqual + shouldRecompile + writeModule + myClose + closePasses + processModule + CompileModule + CompileProject + CommandCompileToC + MainCommand + HandleCmdLine + nim + Entry: 6/3391 Calls: 17/4160 = 0.41% [sum: 302; 302/4160 = 7.3%] + doOperation + forAllChildrenAux + pop + newCrcFromRopeAux + crcFromRope + writeRopeIfNotEqual + shouldRecompile + writeModule + myClose + closePasses + processModule + CompileModule + CompileProject + CommandCompileToC + MainCommand + HandleCmdLine + ... + nim + Entry: 7/3391 Calls: 14/4160 = 0.34% [sum: 316; 316/4160 = 7.6%] + Contains + isAccessible + interiorAllocatedPtr + gcMark + markStackAndRegisters + collectCTBody + collectCT + rawNewObj + newObj + newNode + copyTree + matchesAux + matches + resolveOverloads + semOverloadedCall + semOverloadedCallAnalyseEffects + ... + CommandCompileToC + MainCommand + HandleCmdLine diff --git a/doc/filelist.txt b/doc/filelist.txt index 74995f154..c863ba0cf 100644 --- a/doc/filelist.txt +++ b/doc/filelist.txt @@ -5,7 +5,7 @@ Short description of Nim's modules Module Description ============== ========================================================== nim main module: parses the command line and calls - `main.MainCommand` + ``main.MainCommand`` main implements the top-level command dispatching nimconf implements the config file reader syntaxes dispatcher for the different parsers and filters @@ -36,7 +36,7 @@ evals contains an AST interpreter for compile time evaluation pragmas semantic checking of pragmas idents implements a general mapping from identifiers to an internal - representation (`PIdent`) that is used so that a simple + representation (``PIdent``) that is used so that a simple id-comparison suffices to establish whether two Nim identifiers are equivalent ropes implements long strings represented as trees for diff --git a/doc/filters.md b/doc/filters.md deleted file mode 100644 index 9482b0b47..000000000 --- a/doc/filters.md +++ /dev/null @@ -1,218 +0,0 @@ -=================== -Source Code Filters -=================== - -.. include:: rstcommon.rst -.. default-role:: code -.. contents:: - -A `Source Code Filter (SCF)` transforms the input character stream to an in-memory -output stream before parsing. A filter can be used to provide templating -systems or preprocessors. - -To use a filter for a source file the `#?` notation is used: - - #? stdtmpl(subsChar = '$', metaChar = '#') - #proc generateXML(name, age: string): string = - # result = "" - - $name - $age - - -As the example shows, passing arguments to a filter can be done -just like an ordinary procedure call with named or positional arguments. The -available parameters depend on the invoked filter. Before version 0.12.0 of -the language `#!` was used instead of `#?`. - -**Hint:** With `--hint:codeBegin:on`:option: or `--verbosity:2`:option: -(or higher) while compiling or `nim check`:cmd:, Nim lists the processed code after -each filter application. - -Usage -===== - -First, put your SCF code in a separate file with filters specified in the first line. -**Note:** You can name your SCF file with any file extension you want, but the -conventional extension is `.nimf` -(it used to be `.tmpl` but that was too generic, for example preventing github to -recognize it as Nim source file). - -If we use `generateXML` code shown above and call the SCF file `xmlGen.nimf` -In your `main.nim`: - - ```nim - include "xmlGen.nimf" - - echo generateXML("John Smith","42") - ``` - -Pipe operator -============= - -Filters can be combined with the `|` pipe operator: - - #? strip(startswith="<") | stdtmpl - #proc generateXML(name, age: string): string = - # result = "" - - $name - $age - - - -Available filters -================= - -Replace filter --------------- - -The replace filter replaces substrings in each line. - -Parameters and their defaults: - -* `sub: string = ""` - : the substring that is searched for - -* `by: string = ""` - : the string the substring is replaced with - - -Strip filter ------------- - -The strip filter simply removes leading and trailing whitespace from -each line. - -Parameters and their defaults: - -* `startswith: string = ""` - : strip only the lines that start with *startswith* (ignoring leading - whitespace). If empty every line is stripped. - -* `leading: bool = true` - : strip leading whitespace - -* `trailing: bool = true` - : strip trailing whitespace - - -StdTmpl filter --------------- - -The stdtmpl filter provides a simple templating engine for Nim. The -filter uses a line based parser: Lines prefixed with a *meta character* -(default: `#`) contain Nim code, other lines are verbatim. Because -indentation-based parsing is not suited for a templating engine, control flow -statements need `end X` delimiters. - -Parameters and their defaults: - -* `metaChar: char = '#'` - : prefix for a line that contains Nim code - -* `subsChar: char = '$'` - : prefix for a Nim expression within a template line - -* `conc: string = " & "` - : the operation for concatenation - -* `emit: string = "result.add"` - : the operation to emit a string literal - -* `toString: string = "$"` - : the operation that is applied to each expression - -Example: - - #? stdtmpl | standard - #proc generateHTMLPage(title, currentTab, content: string, - # tabs: openArray[string]): string = - # result = "" - $title - - -
    - $content - A dollar: $$. -
    - - -The filter transforms this into: - - ```nim - proc generateHTMLPage(title, currentTab, content: string, - tabs: openArray[string]): string = - result = "" - result.add("" & $(title) & "\n" & - "\n" & - "
    \n" & - "
      \n") - for tab in items(tabs): - if currentTab == tab: - result.add("
    • " & $(tab) & "
    • \n") - #end - result.add("
    \n" & - "
    \n" & - "
    \n" & - " " & $(content) & "\n" & - " A dollar: $.\n" & - "
    \n" & - "\n") - ``` - - -Each line that does not start with the meta character (ignoring leading -whitespace) is converted to a string literal that is added to `result`. - -The substitution character introduces a Nim expression *e* within the -string literal. *e* is converted to a string with the *toString* operation -which defaults to `$`. For strong type checking, set `toString` to the -empty string. *e* must match this PEG pattern: - - e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}' - x <- '{' x+ '}' / [^}]* - -To produce a single substitution character it has to be doubled: `$$` -produces `$`. - -The template engine is quite flexible. It is easy to produce a procedure that -writes the template code directly to a file: - - #? stdtmpl(emit="f.write") | standard - #proc writeHTMLPage(f: File, title, currentTab, content: string, - # tabs: openArray[string]) = - $title - - -
    - $content - A dollar: $$. -
    - diff --git a/doc/filters.rst b/doc/filters.rst new file mode 100644 index 000000000..40346ecaf --- /dev/null +++ b/doc/filters.rst @@ -0,0 +1,214 @@ +=================== +Source Code Filters +=================== + +.. contents:: + +A `Source Code Filter (SCF)` transforms the input character stream to an in-memory +output stream before parsing. A filter can be used to provide templating +systems or preprocessors. + +To use a filter for a source file the ``#?`` notation is used:: + + #? stdtmpl(subsChar = '$', metaChar = '#') + #proc generateXML(name, age: string): string = + # result = "" + + $name + $age + + +As the example shows, passing arguments to a filter can be done +just like an ordinary procedure call with named or positional arguments. The +available parameters depend on the invoked filter. Before version 0.12.0 of +the language ``#!`` was used instead of ``#?``. + +**Hint:** With ``--hint[codeBegin]:on`` or ``--verbosity:2`` +(or higher) while compiling or `nim check`, Nim lists the processed code after +each filter application. + +Usage +===== + +First, put your SCF code in a separate file with filters specified in the first line. +**Note:** You can name your SCF file with any file extension you want, but the +conventional extension is ``.nimf`` +(it used to be ``.tmpl`` but that was too generic, for example preventing github to +recognize it as Nim source file). + +If we use `generateXML` code shown above and call the SCF file `xmlGen.nimf` +In your `main.nim`: + +.. code-block:: nim + include "xmlGen.nimf" + + echo generateXML("John Smith","42") + +Pipe operator +============= + +Filters can be combined with the ``|`` pipe operator:: + + #? strip(startswith="<") | stdtmpl + #proc generateXML(name, age: string): string = + # result = "" + + $name + $age + + + +Available filters +================= + +Replace filter +-------------- + +The replace filter replaces substrings in each line. + +Parameters and their defaults: + + ``sub: string = ""`` + the substring that is searched for + + ``by: string = ""`` + the string the substring is replaced with + + +Strip filter +------------ + +The strip filter simply removes leading and trailing whitespace from +each line. + +Parameters and their defaults: + + ``startswith: string = ""`` + strip only the lines that start with *startswith* (ignoring leading + whitespace). If empty every line is stripped. + + ``leading: bool = true`` + strip leading whitespace + + ``trailing: bool = true`` + strip trailing whitespace + + +StdTmpl filter +-------------- + +The stdtmpl filter provides a simple templating engine for Nim. The +filter uses a line based parser: Lines prefixed with a *meta character* +(default: ``#``) contain Nim code, other lines are verbatim. Because +indentation-based parsing is not suited for a templating engine, control flow +statements need ``end X`` delimiters. + +Parameters and their defaults: + + ``metaChar: char = '#'`` + prefix for a line that contains Nim code + + ``subsChar: char = '$'`` + prefix for a Nim expression within a template line + + ``conc: string = " & "`` + the operation for concatenation + + ``emit: string = "result.add"`` + the operation to emit a string literal + + ``toString: string = "$"`` + the operation that is applied to each expression + +Example:: + + #? stdtmpl | standard + #proc generateHTMLPage(title, currentTab, content: string, + # tabs: openArray[string]): string = + # result = "" + $title + + +
    + $content + A dollar: $$. +
    + + +The filter transforms this into: + +.. code-block:: nim + proc generateHTMLPage(title, currentTab, content: string, + tabs: openArray[string]): string = + result = "" + result.add("" & $(title) & "\n" & + "\n" & + "
    \n" & + "
      \n") + for tab in items(tabs): + if currentTab == tab: + result.add("
    • " & $(tab) & "
    • \n") + #end + result.add("
    \n" & + "
    \n" & + "
    \n" & + " " & $(content) & "\n" & + " A dollar: $.\n" & + "
    \n" & + "\n") + + +Each line that does not start with the meta character (ignoring leading +whitespace) is converted to a string literal that is added to ``result``. + +The substitution character introduces a Nim expression *e* within the +string literal. *e* is converted to a string with the *toString* operation +which defaults to ``$``. For strong type checking, set ``toString`` to the +empty string. *e* must match this PEG pattern:: + + e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}' + x <- '{' x+ '}' / [^}]* + +To produce a single substitution character it has to be doubled: ``$$`` +produces ``$``. + +The template engine is quite flexible. It is easy to produce a procedure that +writes the template code directly to a file:: + + #? stdtmpl(emit="f.write") | standard + #proc writeHTMLPage(f: File, title, currentTab, content: string, + # tabs: openArray[string]) = + $title + + +
    + $content + A dollar: $$. +
    + diff --git a/doc/gc.rst b/doc/gc.rst new file mode 100644 index 000000000..a19ad0671 --- /dev/null +++ b/doc/gc.rst @@ -0,0 +1,193 @@ +======================= +Nim's Memory Management +======================= + +:Author: Andreas Rumpf +:Version: |nimversion| + +.. + + + "The road to hell is paved with good intentions." + + +Introduction +============ + +A memory-management algorithm optimal for every use-case cannot exist. +Nim provides multiple paradigms for needs ranging from large multi-threaded +applications, to games, hard-realtime systems and small microcontrollers. + +This document describes how the management strategies work; +How to tune the garbage collectors for your needs, like (soft) `realtime systems`:idx:, +and how the memory management strategies other than garbage collectors work. + +.. note:: the default GC is incremental, thread-local and not "stop-the-world" + +Multi-paradigm Memory Management Strategies +=========================================== + +To choose the memory management strategy use the ``--gc:`` switch. + +- ``--gc:refc``. This is the default GC. 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. +- ``--gc:markAndSweep``. Simple Mark-And-Sweep based garbage collector. Heaps are thread-local. +- ``--gc:boehm``. Boehm based garbage collector, it offers a shared heap. +- ``--gc:go``. Go's garbage collector, useful for interoperability with Go. Offers a shared heap. +- ``--gc:arc``. Plain reference counting with + `move semantic optimizations `_, offers a shared heap. + It offers deterministic performance for `hard realtime`:idx: systems. Reference cycles + cause memory leaks, beware. + +- ``--gc:orc``. Same as ``--gc:arc`` but adds a cycle collector based on "trial deletion". + Unfortunately, that makes its performance profile hard to reason about so it is less + useful for hard real-time systems. + +- ``--gc:none``. No memory management strategy nor a garbage collector. Allocated memory is + simply never freed. You should use ``--gc:arc`` instead. + + +================== ======== ================= ============== =================== +Memory Management Heap Reference Cycles Stop-The-World Command line switch +================== ======== ================= ============== =================== +RefC Local Cycle Collector No ``--gc:refc`` +Mark & Sweep Local Cycle Collector No ``--gc:markAndSweep`` +ARC Shared Leak No ``--gc:arc`` +ORC Shared Cycle Collector No ``--gc:orc`` +Boehm Shared Cycle Collector Yes ``--gc:boehm`` +Go Shared Cycle Collector Yes ``--gc:go`` +None Manual Manual Manual ``--gc:none`` +================== ======== ================= ============== =================== + +JavaScript's garbage collector is used for the `JavaScript and NodeJS +`_ compilation targets. +The `NimScript `_ target uses the memory management strategy built into +the Nim compiler. + + +Tweaking the refc GC +==================== + +Cycle collector +--------------- + +The cycle collector can be en-/disabled independently from the other parts of +the garbage collector with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``. + + +Soft real-time support +--------------------- + +To enable real-time support, the symbol `useRealtimeGC`:idx: needs to be +defined via ``--define:useRealtimeGC`` (you can put this into your config +file as well). +With this switch the garbage collector supports the following operations: + +.. code-block:: 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. + +These two procs are the two modus operandi of the real-time garbage collector: + +(1) GC_SetMaxPause Mode + + You can call ``GC_SetMaxPause`` at program startup and then each triggered + garbage collector run tries to not take longer than ``maxPause`` time. However, it is + possible (and common) that the work is nevertheless not evenly distributed + as each call to ``new`` can trigger the garbage collector and thus take ``maxPause`` + time. + +(2) GC_step Mode + + This allows the garbage collector to perform some work for up to ``us`` time. + This is useful to call in the main loop to ensure the garbage collector can do its work. + To bind all garbage collector activity to a ``GC_step`` call, + deactivate the garbage collector with ``GC_disable`` at program startup. + If ``strongAdvice`` is set to ``true``, + then the garbage collector will be forced to perform the collection cycle. + Otherwise, the garbage collector may decide not to do anything, + if there is not much garbage to collect. + You may also specify the current stack size via ``stackSize`` parameter. + It can improve performance when you know that there are no unique Nim references + below a certain point on the stack. Make sure the size you specify is greater + than the potential worst-case size. + + It can improve performance when you know that there are no unique Nim + references below a certain point on the stack. Make sure the size you specify + is greater than the potential worst-case size. + +These procs provide a "best effort" real-time guarantee; in particular the +cycle collector is not aware of deadlines. Deactivate it to get more +predictable real-time behaviour. Tests show that a 1ms max pause +time will be met in almost all cases on modern CPUs (with the cycle collector +disabled). + + +Time measurement with garbage collectors +---------------------------------------- + +The garbage collectors' way of measuring time uses +(see ``lib/system/timers.nim`` for the implementation): + +1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows. +2) ``mach_absolute_time`` on Mac OS X. +3) ``gettimeofday`` on Posix systems. + +As such it supports a resolution of nanoseconds internally; however, the API +uses microseconds for convenience. + +Define the symbol ``reportMissedDeadlines`` to make the +garbage collector output whenever it missed a deadline. +The reporting will be enhanced and supported by the API in later versions of the collector. + + +Tweaking the garbage collector +------------------------------ + +The collector checks whether there is still time left for its work after +every ``workPackage``'th iteration. This is currently set to 100 which means +that up to 100 objects are traversed and freed before it checks again. Thus +``workPackage`` affects the timing granularity and may need to be tweaked in +highly specialized environments or for older hardware. + + +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: + +* ``getTotalMem()`` Returns the amount of total memory managed by the garbage collector. +* ``getOccupiedMem()`` Bytes reserved by the garbage collector and used by objects. +* ``getFreeMem()`` Bytes reserved by the garbage collector and not in use. +* ``GC_getStatistics()`` Garbage collector statistics as a human-readable string. + +These numbers are usually only for the running thread, not for the whole heap, +with the exception of ``--gc:boehm`` and ``--gc:go``. + +In addition to ``GC_ref`` and ``GC_unref`` you can avoid the garbage collector by manually +allocating memory with procs like ``alloc``, ``alloc0``, ``allocShared``, ``allocShared0`` or ``allocCStringArray``. +The garbage collector won't try to free them, you need to call their respective *dealloc* pairs +(``dealloc``, ``deallocShared``, ``deallocCStringArray``, etc) +when you are done with them or they will leak. + + +Heap dump +========= + +The heap dump feature is still in its infancy, but it already proved +useful for us, so it might be useful for you. To get a heap dump, compile +with ``-d:nimTypeNames`` and call ``dumpNumberOfInstances`` at a strategic place in your program. +This produces a list of the used types in your program and for every type +the total amount of object instances for this type as well as the total +amount of bytes these instances take up. + +The numbers count the number of objects in all garbage collector heaps, they refer to +all running threads, not only to the current thread. (The current thread +would be the thread that calls ``dumpNumberOfInstances``.) This might +change in later versions. diff --git a/doc/grammar.txt b/doc/grammar.txt index 88094981c..f5e619ebc 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -22,16 +22,13 @@ ampExpr = plusExpr (OP7 optInd plusExpr)* plusExpr = mulExpr (OP8 optInd mulExpr)* mulExpr = dollarExpr (OP9 optInd dollarExpr)* dollarExpr = primary (OP10 optInd primary)* -operatorB = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 | - 'div' | 'mod' | 'shl' | 'shr' | 'in' | 'notin' | - 'is' | 'isnot' | 'not' | 'of' | 'as' | 'from' | '..' | 'and' | 'or' | 'xor' symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' | IDENT | KEYW exprColonEqExpr = expr (':'|'=' expr)? -exprEqExpr = expr ('=' expr)? exprList = expr ^+ comma -optionalExprList = expr ^* comma exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)? +dotExpr = expr '.' optInd (symbol | '[:' exprList ']') +explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? qualifiedIdent = symbol ('.' optInd symbol)? setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}' castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') / @@ -39,49 +36,50 @@ parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' | 'when' | 'var' | 'mixin' par = '(' optInd - ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';' - | ';' (ifExpr / complexOrSimpleStmt) ^+ ';' + ( &parKeyw (ifExpr \ complexOrSimpleStmt) ^+ ';' + | ';' (ifExpr \ complexOrSimpleStmt) ^+ ';' | pragmaStmt - | simpleExpr ( ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? ) + | simpleExpr ( ('=' expr (';' (ifExpr \ complexOrSimpleStmt) ^+ ';' )? ) | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) optPar ')' literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT | STR_LIT | RSTR_LIT | TRIPLESTR_LIT - | CHAR_LIT | CUSTOM_NUMERIC_LIT + | CHAR_LIT | NIL generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT identOrLiteral = generalizedLit | symbol | literal - | par | arrayConstr | setOrTableConstr | tupleConstr + | par | arrayConstr | setOrTableConstr | castExpr tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' primarySuffix = '(' (exprColonEqExpr comma?)* ')' - | '.' optInd symbol ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit? - | DOTLIKEOP optInd symbol generalizedLit? + | '.' optInd symbol generalizedLit? | '[' optInd exprColonEqExprList optPar ']' | '{' optInd exprColonEqExprList optPar '}' + | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}') identVis = symbol OPR? # postfix position identVisDot = symbol '.' optInd symbol OPR? identWithPragma = identVis pragma? identWithPragmaDot = identVisDot pragma? declColonEquals = identWithPragma (comma identWithPragma)* comma? - (':' optInd typeDescExpr)? ('=' optInd expr)? + (':' optInd typeDesc)? ('=' optInd expr)? identColonEquals = IDENT (comma IDENT)* comma? - (':' optInd typeDescExpr)? ('=' optInd expr)?) -tupleTypeBracket = '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' -tupleType = 'tuple' tupleTypeBracket -tupleDecl = 'tuple' (tupleTypeBracket / - COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?) + (':' optInd typeDesc)? ('=' optInd expr)?) +inlTupleDecl = 'tuple' + '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' +extTupleDecl = 'tuple' + COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? +tupleClass = 'tuple' paramList = '(' declColonEquals ^* (comma/semicolon) ')' paramListArrow = paramList? ('->' optInd typeDesc)? paramListColon = paramList? (':' optInd typeDesc)? doBlock = 'do' paramListArrow pragma? colcom stmt -routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)? -routineType = ('proc' | 'iterator') paramListColon pragma? -forStmt = 'for' ((varTuple / identWithPragma) ^+ comma) 'in' expr colcom stmt +procExpr = 'proc' paramListColon pragma? ('=' COMMENT? stmt)? +distinct = 'distinct' optInd typeDesc +forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt forExpr = forStmt expr = (blockExpr | ifExpr @@ -90,32 +88,24 @@ expr = (blockExpr | forExpr | tryExpr) / simpleExpr -simplePrimary = SIGILLIKEOP? identOrLiteral primarySuffix* -commandStart = &('`'|IDENT|literal|'cast'|'addr'|'type'|'var'|'out'| - 'static'|'enum'|'tuple'|'object'|'proc') -primary = simplePrimary (commandStart expr) - / operatorB primary - / routineExpr - / rawTypeDesc - / prefixOperator primary -rawTypeDesc = (tupleType | routineType | 'enum' | 'object' | - ('var' | 'out' | 'ref' | 'ptr' | 'distinct') typeDesc?) - ('not' expr)? -typeDescExpr = (routineType / simpleExpr) ('not' expr)? -typeDesc = rawTypeDesc / typeDescExpr -typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl | - ('ref' | 'ptr' | 'distinct') (tupleDecl | objectDecl)) - / (simpleExpr (exprEqExpr ^+ comma postExprBlocks)?)) - ('not' expr)? +typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple' + | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' +primary = typeKeyw optInd typeDesc + / prefixOperator* identOrLiteral primarySuffix* + / 'bind' primary +typeDesc = simpleExpr ('not' expr)? +typeDefAux = simpleExpr ('not' expr)? + | 'concept' typeClass postExprBlocks = ':' stmt? ( IND{=} doBlock | IND{=} 'of' exprList ':' stmt | IND{=} 'elif' expr ':' stmt - | IND{=} 'except' optionalExprList ':' stmt - | IND{=} 'finally' ':' stmt + | IND{=} 'except' exprList ':' stmt | IND{=} 'else' ':' stmt )* -exprStmt = simpleExpr postExprBlocks? - / simplePrimary (exprEqExpr ^+ comma) postExprBlocks? - / simpleExpr '=' optInd (expr postExprBlocks?) +exprStmt = simpleExpr + (( '=' optInd expr colonBody? ) + / ( expr ^+ comma + postExprBlocks + ))? importStmt = 'import' optInd expr ((comma expr)* / 'except' optInd (expr ^+ comma)) @@ -129,7 +119,7 @@ raiseStmt = 'raise' optInd expr? yieldStmt = 'yield' optInd expr? discardStmt = 'discard' optInd expr? breakStmt = 'break' optInd expr? -continueStmt = 'continue' optInd expr? +continueStmt = 'break' optInd expr? condStmt = expr colcom stmt COMMENT? (IND{=} 'elif' expr colcom stmt)* (IND{=} 'else' colcom stmt)? @@ -149,11 +139,12 @@ caseStmt = 'case' expr ':'? COMMENT? (IND{>} ofBranches DED | IND{=} ofBranches) tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') - (IND{=}? 'except' optionalExprList colcom stmt)* + (IND{=}? 'except' exprList colcom stmt)* (IND{=}? 'finally' colcom stmt)? tryExpr = 'try' colcom stmt &(optInd 'except'|'finally') - (optInd 'except' optionalExprList colcom stmt)* + (optInd 'except' exprList colcom stmt)* (optInd 'finally' colcom stmt)? +exceptBlock = 'except' colcom stmt blockStmt = 'block' symbol? colcom stmt blockExpr = 'block' symbol? colcom stmt staticStmt = 'static' colcom stmt @@ -168,7 +159,7 @@ routine = optInd identVis pattern? genericParamList? paramListColon pragma? ('=' COMMENT? stmt)? indAndComment commentStmt = COMMENT section(RULE) = COMMENT? RULE / (IND{>} (RULE / COMMENT)^+IND{=} DED) -enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ +enum = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ objectWhen = 'when' expr colcom objectPart COMMENT? ('elif' expr colcom objectPart COMMENT?)* ('else' colcom objectPart COMMENT?)? @@ -176,16 +167,17 @@ objectBranch = 'of' exprList colcom objectPart objectBranches = objectBranch (IND{=} objectBranch)* (IND{=} 'elif' expr colcom objectPart)* (IND{=} 'else' colcom objectPart)? -objectCase = 'case' declColonEquals ':'? COMMENT? +objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT? (IND{>} objectBranches DED | IND{=} objectBranches) objectPart = IND{>} objectPart^+IND{=} DED / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals -objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart -conceptParam = ('var' | 'out')? symbol -conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? +object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart +typeClassParam = ('var' | 'out')? symbol +typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? &IND{>} stmt -typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue +typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux + indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux indAndComment? varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr colonBody = colcom stmt postExprBlocks? diff --git a/doc/hcr.md b/doc/hcr.rst similarity index 84% rename from doc/hcr.md rename to doc/hcr.rst index 285a86282..d2e13e1c7 100644 --- a/doc/hcr.md +++ b/doc/hcr.rst @@ -2,9 +2,6 @@ Hot code reloading =================================== -.. default-role:: code -.. include:: rstcommon.rst - The `hotCodeReloading`:idx: option enables special compilation mode where changes in the code can be applied automatically to a running program. The code reloading happens at the granularity of an individual module. @@ -22,16 +19,17 @@ so we have to use a helper module where the major logic we want to change during development resides. In this example, we use SDL2 to create a window and we reload the logic -code when `F9` is pressed. The important lines are marked with `#***`. -To install SDL2 you can use `nimble install sdl2`:cmd:. +code when ``F9`` is pressed. The important lines are marked with ``#***``. +To install SDL2 you can use ``nimble install sdl2``. - ```nim +.. code-block:: nim + # logic.nim import sdl2 #*** import the hotcodereloading stdlib module *** - import std/hotcodereloading + import hotcodereloading var runGame*: bool = true var window: WindowPtr @@ -78,14 +76,14 @@ To install SDL2 you can use `nimble install sdl2`:cmd:. discard renderer.clear() discard renderer.setDrawColor(255, 128, 128, 0) - var rect: Rect = (x: posX - 25, y: posY - 25, w: 50.cint, h: 50.cint) + var rect = Rect(x: posX - 25, y: posY - 25, w: 50.cint, h: 50.cint) discard renderer.fillRect(rect) delay(16) renderer.present() - ``` - ```nim +.. code-block:: nim + # mymain.nim import logic @@ -96,46 +94,38 @@ To install SDL2 you can use `nimble install sdl2`:cmd:. destroy() main() - ``` -Compile this example via: +Compile this example via:: - ```cmd nim c --hotcodereloading:on mymain.nim - ``` Now start the program and KEEP it running! - ```cmd +:: + # Unix: mymain & # or Windows (click on the .exe) mymain.exe # edit - ``` -For example, change the line: +For example, change the line:: - ```nim discard renderer.setDrawColor(255, 128, 128, 0) - ``` -into: +into:: - ```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 nim c --hotcodereloading:on mymain.nim - ``` -Now give the `mymain` SDL window the focus, press F9, and watch the +Now give the ``mymain`` SDL window the focus, press F9, and watch the updated version of the program. @@ -143,11 +133,11 @@ updated version of the program. Reloading API ============= -One can use the special event handlers `beforeCodeReload` and -`afterCodeReload` to reset the state of a particular variable or to force +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: - ```Nim +.. code-block:: Nim var settings = initTable[string, string]() lastReload: Time @@ -160,7 +150,6 @@ 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 @@ -169,7 +158,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: - ```Nim +.. code-block:: Nim import mydb var myCache = initTable[Key, Value]() @@ -177,7 +166,6 @@ 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 @@ -190,23 +178,24 @@ It's expected that most projects will implement the reloading with a suitable build-system triggered IPC notification mechanism, but a polling solution is also possible through the provided `hasAnyModuleChanged()`:idx: API. -In order to access `beforeCodeReload`, `afterCodeReload`, `hasModuleChanged` -or `hasAnyModuleChanged` one must import the `hotcodereloading`:idx: module. +In order to access ``beforeCodeReload``, ``afterCodeReload``, ``hasModuleChanged`` +or ``hasAnyModuleChanged`` one must import the `hotcodereloading`:idx: module. Native code targets =================== Native projects using the hot code reloading option will be implicitly -compiled with the `-d:useNimRtl`:option: option and they will depend on both -the `nimrtl` library and the `nimhcr` library which implements the -hot code reloading run-time. Both libraries can be found in the `lib` +compiled with the `-d:useNimRtl` option and they will depend on both +the ``nimrtl`` library and the ``nimhcr`` library which implements the +hot code reloading run-time. Both libraries can be found in the ``lib`` folder of Nim and can be compiled into dynamic libraries to satisfy -runtime demands of the example code above. An example of compiling +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. - ```console +:: + # Unix/MacOS # Make sure you are in the directory containing your .nim files $ cd your-source-directory @@ -217,19 +206,18 @@ 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 +libraries placed in the ``nimcache`` directory. Please note that during the execution of the program, the hot code reloading run-time will load only copies of these libraries in order to not interfere with any newly issued build commands. The main module of the program is considered non-reloadable. Please note that procs from reloadable modules should not appear in the call stack of -program while `performCodeReload` is being called. Thus, the main module +program while ``performCodeReload`` is being called. Thus, the main module is a suitable place for implementing a program loop capable of calling -`performCodeReload`. +``performCodeReload``. Please note that reloading won't be possible when any of the type definitions in the program has been changed. When closure iterators are used (directly or diff --git a/doc/idetools.md b/doc/idetools.rst similarity index 65% rename from doc/idetools.md rename to doc/idetools.rst index 0388a76c0..ac3b92947 100644 --- a/doc/idetools.md +++ b/doc/idetools.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ================================ Nim IDE Integration Guide ================================ @@ -10,21 +8,24 @@ .. contents:: -> "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32. +.. raw:: html +

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

    -Note: this is mostly outdated, see instead [nimsuggest](nimsuggest.html) +Note: this is mostly outdated, see instead `nimsuggest `_ 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](nimc.html), any IDE -can query a `.nim` source file and obtain useful information like +``idetools`` command of `the compiler `_, 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](https://github.com/Araq/Nim/wiki/Editor-Support) +at the test files found in the `Test suite`_ or `various editor +integrations `_ already available. @@ -35,48 +36,44 @@ Specifying the location of the query ------------------------------------ All of the available idetools commands require you to specify a -query location through the `--track` or `--trackDirty` switches. -The general idetools invocations are: +query location through the ``--track`` or ``--trackDirty`` switches. +The general idetools invocations are:: - ```cmd - nim idetools --track:FILE,LINE,COL proj.nim - ``` + nim idetools --track:FILE,LINE,COL proj.nim -Or: +Or:: - ```cmd - nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL proj.nim - ``` + nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL proj.nim -`proj.nim` -: This is the main *project* filename. Most of the time you will +``proj.nim`` + 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 - `--def` or `--suggest` explained in the following sections. +```` + 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 +``COL`` + 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 +``LINE`` + 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 +``FILE`` + 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 +``DIRTY_FILE`` + 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 - `--trackDirty` switch. + ``--trackDirty`` switch. Dirty files are likely to contain errors and they are usually compiled partially only to the point needed to service the @@ -94,7 +91,7 @@ Or: Definitions ----------- -The `--def` idetools switch performs a query about the definition +The ``--def`` idetools switch performs a query about the definition of a specific symbol. If available, idetools will answer with the type, source file, line/column information and other accessory data if available like a docstring. With this information an IDE can @@ -115,7 +112,7 @@ can't find any valid symbol matching the position of the query. Suggestions ----------- -The `--suggest` idetools switch performs a query about possible +The ``--suggest`` idetools switch performs a query about possible completion symbols at some point in the file. IDEs can easily provide an autocompletion feature where the IDE scans the current file (and related ones, if it knows about the language being edited and follows @@ -128,8 +125,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](tut2.html#object-oriented-programming-method-call-syntax). +user has typed the dot character for `the object oriented call +syntax `_. Idetools will try to return the suggestions sorted first by scope (from innermost to outermost) and then by item name. @@ -137,7 +134,7 @@ Idetools will try to return the suggestions sorted first by scope Invocation context ------------------ -The `--context` idetools switch is very similar to the suggestions +The ``--context`` idetools switch is very similar to the suggestions switch, but instead of being used after the user has typed a dot character, this one is meant to be used after the user has typed an opening brace to start typing parameters. @@ -146,7 +143,7 @@ an opening brace to start typing parameters. Symbol usages ------------- -The `--usages` idetools switch lists all usages of the symbol at +The ``--usages`` idetools switch lists all usages of the symbol at a position. IDEs can use this to find all the places in the file where the symbol is used and offer the user to rename it in all places at the same time. Again, a pure string based search and @@ -182,18 +179,14 @@ results of the compilation, and subsequent queries should be fast in the millisecond range, thus being responsive enough for IDEs. If you want to start the server using stdin/stdout as communication -you need to type: +you need to type:: - ```cmd - nim serve --server.type:stdin proj.nim - ``` + nim serve --server.type:stdin proj.nim -If you want to start the server using tcp and a port, you need to type: +If you want to start the server using tcp and a port, you need to type:: - ```cmd - nim serve --server.type:tcp --server.port:6000 \ + nim serve --server.type:tcp --server.port:6000 \ --server.address:hostname proj.nim - ``` In both cases the server will start up and await further commands. The syntax of the commands you can now send to the server is @@ -204,7 +197,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 @@ -214,15 +207,15 @@ Idetools outputs is always returned on single lines separated by tab characters (``\t``). The values of each column are: 1. Three characters indicating the type of returned answer (e.g. - def for definition, `sug` for suggestion, etc). -2. Type of the symbol. This can be `skProc`, `skLet`, and just - about any of the enums defined in the module `compiler/ast.nim`. + def for definition, ``sug`` for suggestion, etc). +2. Type of the symbol. This can be ``skProc``, ``skLet``, and just + about any of the enums defined in the module ``compiler/ast.nim``. 3. Full qualified path of the symbol. If you are querying a symbol - defined in the `proj.nim` file, this would have the form - `proj.symbolName`. + defined in the ``proj.nim`` file, this would have the form + ``proj.symbolName``. 4. Type/signature. For variables and enums this will contain the type of the symbol, for procs, methods and templates this will - contain the full unique signature (e.g. `proc (File)`). + contain the full unique signature (e.g. ``proc (File)``). 5. Full path to the file containing the symbol. 6. Line where the symbol is located in the file. Lines start to count at **1**. @@ -247,48 +240,45 @@ 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. - ```nim - const SOME_SEQUENCE = @[1, 2] - --> col 2: $MODULE.SOME_SEQUENCE - col 3: seq[int] - col 7: "" - ``` +.. code-block:: 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. - ```nim - Open(filename, fmWrite) - --> col 2: system.FileMode.fmWrite - col 3: FileMode - col 7: "" - ``` +.. code-block:: 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. - ```nim - proc looper(filename = "tests.nim") = - for letter in filename: - echo letter - --> col 2: $MODULE.looper.letter - col 3: char - col 7: "" - ``` +.. code-block:: nim + proc looper(filename = "tests.nim") = + for letter in filename: + echo letter + --> col 2: $MODULE.looper.letter + col 3: char + col 7: "" skIterator, skClosureIterator @@ -299,51 +289,48 @@ 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. - ```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`." - ``` +.. 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`." 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. - ```nim - proc test(text: string) = - var found = -1 - block loops: - --> col 2: $MODULE.test.loops - col 3: "" - col 7: "" - ``` +.. code-block:: 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. - ```nim - let - text = "some text" - --> col 2: $MODULE.text - col 3: string - col 7: "" - ``` +.. code-block:: nim + let + text = "some text" + --> col 2: $MODULE.text + col 3: TaintedString + col 7: "" skMacro @@ -354,17 +341,16 @@ 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. - ```nim - proc testMacro() = - expect(EArithmetic): - --> col 2: idetools_api.expect - col 3: proc (varargs[expr], stmt): stmt - col 7: "" - ``` +.. code-block:: nim + proc testMacro() = + expect(EArithmetic): + --> col 2: idetools_api.expect + col 3: proc (varargs[expr], stmt): stmt + col 7: "" skMethod @@ -375,8 +361,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]( -tut2.html#object-oriented-programming-dynamic-dispatch) and +Methods imply `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 @@ -388,39 +374,37 @@ While at the language level a method is differentiated from others by the parameters and return value, the signature of the method returned by idetools returns also the pragmas for the method. -Note that at the moment the word `proc` is returned for the -signature of the found method instead of the expected `method`. +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. - ```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: "" - ``` +.. 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: "" 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. - ```nim - proc reader(filename = "tests.nim") = - let text = readFile(filename) - --> col 2: $MODULE.reader.filename - col 3: string - col 7: "" - ``` +.. code-block:: nim + proc reader(filename = "tests.nim") = + let text = readFile(filename) + --> col 2: $MODULE.reader.filename + col 3: string + col 7: "" skProc @@ -435,36 +419,34 @@ 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. - ```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`. +.. 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`. - 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. - ```nim - proc getRandomValue() : int = - return 4 - --> col 2: $MODULE.getRandomValue.result - col 3: int - col 7: "" - ``` +.. code-block:: nim + proc getRandomValue() : int = + return 4 + --> col 2: $MODULE.getRandomValue.result + col 3: int + col 7: "" skTemplate @@ -475,11 +457,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. - `````nim +.. code-block:: nim let text = "some text" letters = toSeq(runes(text)) @@ -490,56 +472,52 @@ posterior instances of the template. Example: - ```nim + .. code-block:: 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. - ```nim - proc writeTempFile() = - var output: File - --> col 2: system.File - col 3: File - col 7: "" - ``` +.. code-block:: 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. - ```nim - proc writeTempFile() = - var output: File - output.open("/tmp/somefile", fmWrite) - output.write("test") - --> col 2: $MODULE.writeTempFile.output - col 3: File - col 7: "" - ``` +.. 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: "" Test suite ========== To verify that idetools is working properly there are files in the -`tests/caas/` directory which provide unit testing. If you find +``tests/caas/`` directory which provide unit testing. If you find odd idetools behaviour and are able to reproduce it, you are welcome to report it as a bug and add a test to the suite to avoid future regressions. @@ -550,59 +528,55 @@ Running the test suite At the moment idetools support is still in development so the test suite is not integrated with the main test suite and you have to -run it manually. First you have to compile the tester: +run it manually. First you have to compile the tester:: - ```cmd - $ cd my/nim/checkout/tests - $ nim c testament/caasdriver.nim - ``` + $ cd my/nim/checkout/tests + $ nim c testament/caasdriver.nim -Running the `caasdriver` without parameters will attempt to process +Running the ``caasdriver`` without parameters will attempt to process all the test cases in all three operation modes. If a test succeeds nothing will be printed and the process will exit with zero. If any test fails, the specific line of the test preceding the failure and the failure itself will be dumped to stdout, along with a final indicator of the success state and operation mode. You can pass the -parameter `verbose` to force all output even on successful tests. +parameter ``verbose`` to force all output even on successful tests. -The normal operation mode is called `ProcRun` and it involves +The normal operation mode is called ``ProcRun`` and it involves starting a process for each command or query, similar to running -manually the Nim compiler from the commandline. The `CaasRun` -mode starts a server process to answer all queries. The `SymbolProcRun` +manually the Nim compiler from the commandline. The ``CaasRun`` +mode starts a server process to answer all queries. The ``SymbolProcRun`` mode is used by compiler developers. This means that running all -tests involves processing all `*.txt` files three times, which +tests involves processing all ``*.txt`` files three times, which can be quite time consuming. If you don't want to run all the test case files you can pass any -substring as a parameter to `caasdriver`. Only files matching the +substring as a parameter to ``caasdriver``. Only files matching the passed substring will be run. The filtering doesn't use any globbing metacharacters, it's a plain match. For example, to run only -`*-compile*.txt` tests in verbose mode: +``*-compile*.txt`` tests in verbose mode:: - ```cmd - ./caasdriver verbose -compile - ``` + ./caasdriver verbose -compile Test case file format --------------------- -All the `tests/caas/*.txt` files encode a session with the compiler: +All the ``tests/caas/*.txt`` files encode a session with the compiler: * The first line indicates the main project file. -* Lines starting with `>` indicate a command to be sent to the +* Lines starting with ``>`` indicate a command to be sent to the compiler and the lines following a command include checks for - expected or forbidden output (`!` for forbidden). + expected or forbidden output (``!`` for forbidden). -* If a line starts with `#` it will be ignored completely, so you +* If a line starts with ``#`` it will be ignored completely, so you can use that for comments. -* Since some cases are specific to either `ProcRun` or `CaasRun` +* Since some cases are specific to either ``ProcRun`` or ``CaasRun`` 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](re.html), +* The rest of the line is treated as a `regular expression `_, so be careful escaping metacharacters like parenthesis. Before the line is processed as a regular expression, some basic diff --git a/doc/intern.md b/doc/intern.md deleted file mode 100644 index e1861ea7d..000000000 --- a/doc/intern.md +++ /dev/null @@ -1,675 +0,0 @@ -========================================= - Internals of the Nim Compiler -========================================= - - -:Author: Andreas Rumpf -:Version: |nimversion| - -.. default-role:: code -.. include:: rstcommon.rst -.. contents:: - -> "Abstraction is layering ignorance on top of reality." -- Richard Gabriel - - -Directory structure -=================== - -The Nim project's directory structure is: - -============ =================================================== -Path Purpose -============ =================================================== -`bin` generated binary files -`build` generated C code for the installation -`compiler` the Nim compiler itself; note that this - code has been translated from a bootstrapping - version written in Pascal, so the code is **not** - a poster child of good Nim code -`config` configuration files for Nim -`dist` additional packages for the distribution -`doc` the documentation; it is a bunch of - reStructuredText files -`lib` the Nim library -============ =================================================== - - -Bootstrapping the compiler -========================== - -**Note**: Add ``.`` to your PATH so that `koch`:cmd: can be used without the ``./``. - -Compiling the compiler is a simple matter of running: - - ```cmd - nim c koch.nim - koch boot -d:release - ``` - -For a debug version use: - - ```cmd - nim c koch.nim - koch boot - ``` - - -And for a debug version compatible with GDB: - - ```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](koch.html) -documentation. - - -Reproducible builds -------------------- - -Set the compilation timestamp with the `SOURCE_DATE_EPOCH` environment variable. - - ```cmd - export SOURCE_DATE_EPOCH=$(git log -n 1 --format=%at) - koch boot # or `./build_all.sh` - ``` - - -Debugging the compiler -====================== - - -Bisecting for regressions -------------------------- - -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. - -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: - - ```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]. - - -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) - - # 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) - - # 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) - - # 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: - - ```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 -=========================== - -Nim uses the classic compiler architecture: A lexer/scanner feeds tokens to a -parser. The parser builds a syntax tree that is used by the code generators. -This syntax tree is the interface between the parser and the code generator. -It is essential to understand most of the compiler's code. - -Semantic analysis is separated from parsing. - -.. include:: filelist.txt - - -The syntax tree ---------------- -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](macros.html) module contains many -examples how the AST represents each syntactic structure. - - -Runtimes -======== - -Nim has two different runtimes, the "old runtime" and the "new runtime". The old -runtime supports the old GCs (markAndSweep, refc, Boehm), the new runtime supports -ARC/ORC. The new runtime is active `when defined(nimV2)`. - - -Coding Guidelines -================= - -* 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 - pointers/references which start with `P`. -* Prefer `import package`:nim: over `from package import symbol`:nim:. - -See also the [API naming design](apis.html) document. - - -Porting to new platforms -======================== - -Porting Nim to a new architecture is pretty easy, since C is the most -portable programming language (within certain limits) and Nim generates -C code, porting the code generator is not necessary. - -POSIX-compliant systems on conventional hardware are usually pretty easy to -port: Add the platform to `platform` (if it is not already listed there), -check that the OS, System modules work and recompile Nim. - -The only case where things aren't as easy is when old runtime's garbage -collectors need some assembler tweaking to work. The default -implementation uses C's `setjmp`:c: function to store all registers -on the hardware stack. It may be necessary that the new platform needs to -replace this generic code by some assembler code. - -Files that may need changed for your platform include: - -* `compiler/platform.nim` - Add os/cpu properties. -* `lib/system.nim` - Add os/cpu to the documentation for `system.hostOS` and `system.hostCPU`. -* `compiler/options.nim` - Add special os/cpu property checks in `isDefined`. -* `compiler/installer.ini` - Add os/cpu to `Project.Platforms` field. -* `lib/system/platforms.nim` - Add os/cpu. -* `std/private/osseps.nim` - Add os specializations. -* `lib/pure/distros.nim` - Add os, package handler. -* `tools/niminst/makefile.nimf` - Add os/cpu compiler/linker flags. -* `tools/niminst/buildsh.nimf` - 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 endianness from `system.cpuEndian`, -`system.hostOS` and `system.hostCPU`. Those values are derived from -`compiler/platform.nim`. - -In order for the new platform to be bootstrapped from the `csources`, it must: - -* have `compiler/platform.nim` updated -* have `compiler/installer.ini` updated -* have `tools/niminst/buildsh.nimf` updated -* have `tools/niminst/makefile.nimf` updated -* be backported to the Nim version used by the `csources` -* the new `csources` must be pushed -* the new `csources` revision must be updated in `config/build_config.txt` - - -Runtime type information -======================== - -**Note**: This section describes the "old runtime". - -*Runtime type information* (RTTI) is needed for several aspects of the Nim -programming language: - -Garbage collection -: 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 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. -Look at the file ``lib/system/hti.nim`` for more information. - - -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`) -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 -semantic checking, a `compilerproc` is a proc that is used by the code -generator. - - -Code generation for closures -============================ - -Code generation for closures is implemented by `lambda lifting`:idx:. - - -Design ------- - -A `closure` proc var can call ordinary procs of the default Nim calling -convention. But not the other way round! A closure is implemented as a -`tuple[prc, env]`. `env` can be nil implying a call without a closure. -This means that a call through a closure generates an `if` but the -interoperability is worth the cost of the `if`. Thunk generation would be -possible too, but it's slightly more effort to implement. - -Tests with GCC on Amd64 showed that it's really beneficial if the -'environment' pointer is passed as the last argument, not as the first argument. - -Proper thunk generation is harder because the proc that is to wrap -could stem from a complex expression: - - ```nim - receivesClosure(returnsDefaultCC[i]) - ``` - -A thunk would need to call `returnsDefaultCC[i]` somehow and that would require -an *additional* closure generation... Ok, not really, but it requires to pass -the function to call. So we'd end up with 2 indirect calls instead of one. -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: - - ```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: - - ```nim - type - Env = ref object - x: int # data - - proc anon(y: int, c: Env): int = - return y + c.x - - proc add(x: int): tuple[prc, data] = - var env: Env - new env - env.x = x - result = (anon, env) - - var add2 = add(2) - let tmp = if add2.data == nil: add2.prc(5) else: add2.prc(5, add2.data) - echo tmp - ``` - - -Beware of nesting: - - ```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 = - return x + y + z - - var add24 = add(2)(4) - echo add24(5) #OUT 11 - ``` - -This should produce roughly this code: - - ```nim - type - EnvX = ref object - x: int # data - - EnvY = ref object - y: int - ex: EnvX - - proc lambdaZ(z: int, ey: EnvY): int = - return ey.ex.x + ey.y + z - - proc lambdaY(y: int, ex: EnvX): tuple[prc, data: EnvY] = - var ey: EnvY - new ey - ey.y = y - ey.ex = ex - result = (lambdaZ, ey) - - proc add(x: int): tuple[prc, data: EnvX] = - var ex: EnvX - ex.x = x - 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. -More useful is escape analysis and stack allocation of the environment, -however. - - -Accumulator ------------ - - ```nim - proc getAccumulator(start: int): proc (): int {.closure} = - var i = start - return lambda: int = - inc i - return i - - proc p = - var delta = 7 - proc accumulator(start: int): proc(): int = - var x = start-1 - result = proc (): int = - x = x + delta - inc delta - return x - - 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 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 -backend somehow. We deal with this by modifying `s.ast[paramPos]` to contain -the formal hidden parameter, but not `s.typ`! - - -Notes on type and AST representation -==================================== - -To be expanded. - - -Integer literals ----------------- - -In Nim, there is a redundant way to specify the type of an -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. The -kind of the `typ` field can be one of the following ones, and it -should be matching the literal kind: - - tyInt, tyInt8, tyInt16, tyInt32, tyInt64, tyUInt, tyUInt8, - tyUInt16, tyUInt32, tyUInt64 - -Then there is also the integer literal type. This is a specific type -that is implicitly convertible into the requested type if the -requested type can hold the value. For this to work, the type needs to -know the concrete value of the literal. For example an expression -`321` will be of type `int literal(321)`. This type is implicitly -convertible to all integer types and ranges that contain the value -`321`. That would be all builtin integer types except `uint8` and -`int8` where `321` would be out of range. When this literal type is -assigned to a new `var` or `let` variable, it's type will be resolved -to just `int`, not `int literal(321)` unlike constants. A constant -keeps the full `int literal(321)` type. Here is an example where that -difference matters. - - - ```nim - proc foo(arg: int8) = - echo "def" - - const tmp1 = 123 - foo(tmp1) # OK - - 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. - - ```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" - - 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 -pointing back to the integer literal node in the ast containing the -integer value. These are the properties that hold true for integer -literal types. - - n.kind == nkIntLit - n.typ.kind == tyInt - n.typ.n == n - -Other literal types, such as `uint literal(123)` that would -automatically convert to other integer types, but prefers to -become a `uint` are not part of the Nim language. - -In an unchecked AST, the `typ` field is nil. The type checker will set -the `typ` field accordingly to the node kind. Nodes of kind `nkIntLit` -will get the integer literal type (e.g. `int literal(123)`). Nodes of -kind `nkUIntLit` will get type `uint` (kind `tyUint`), etc. - -This also means that it is not possible to write a literal in an -unchecked AST that will after sem checking just be of type `int` and -not implicitly convertible to other integer types. This only works for -all integer types that are not `int`. diff --git a/doc/intern.rst b/doc/intern.rst new file mode 100644 index 000000000..5de0c35d5 --- /dev/null +++ b/doc/intern.rst @@ -0,0 +1,794 @@ +========================================= + Internals of the Nim Compiler +========================================= + + +:Author: Andreas Rumpf +:Version: |nimversion| + +.. contents:: + + "Abstraction is layering ignorance on top of reality." -- Richard Gabriel + + +Directory structure +=================== + +The Nim project's directory structure is: + +============ =================================================== +Path Purpose +============ =================================================== +``bin`` generated binary files +``build`` generated C code for the installation +``compiler`` the Nim compiler itself; note that this + code has been translated from a bootstrapping + version written in Pascal, so the code is **not** + a poster child of good Nim code +``config`` configuration files for Nim +``dist`` additional packages for the distribution +``doc`` the documentation; it is a bunch of + reStructuredText files +``lib`` the Nim library +``web`` website of Nim; generated by ``nimweb`` + from the ``*.txt`` and ``*.nimf`` files +============ =================================================== + + +Bootstrapping the compiler +========================== + +Compiling the compiler is a simple matter of running:: + + nim c koch.nim + ./koch boot + +For a release version use:: + + nim c koch.nim + ./koch boot -d:release + +And for a debug version compatible with GDB:: + + nim c koch.nim + ./koch boot --debuginfo --linedir:on + +The ``koch`` 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 `_ +documentation. + + +Coding Guidelines +================= + +* Use CamelCase, not underscored_identifiers. +* Indent with two spaces. +* Max line length is 80 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 + pointers/references which start with ``P``. + +See also the `API naming design `_ document. + + +Porting to new platforms +======================== + +Porting Nim to a new architecture is pretty easy, since C is the most +portable programming language (within certain limits) and Nim generates +C code, porting the code generator is not necessary. + +POSIX-compliant systems on conventional hardware are usually pretty easy to +port: Add the platform to ``platform`` (if it is not already listed there), +check that the OS, System modules work and recompile Nim. + +The only case where things aren't as easy is when the garbage +collector needs some assembler tweaking to work. The standard +version of the GC uses C's ``setjmp`` function to store all registers +on the hardware stack. It may be necessary that the new platform needs to +replace this generic code by some assembler code. + + +Runtime type information +======================== + +*Runtime type information* (RTTI) is needed for several aspects of the Nim +programming language: + +Garbage collection + The most important reason for RTTI. Generating + traversal procedures produces bigger code and is likely to be slower on + modern hardware as dynamic procedure binding is hard to predict. + +Complex assignments + Sequences and strings are implemented as + pointers to resizeable buffers, but Nim requires copying for + assignments. Apart from RTTI the compiler could generate copy procedures + for any type that needs one. However, this would make the code bigger and + the RTTI is likely already there for the GC. + +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. +Look at the file ``lib/system/hti.nim`` for more information. + +Rebuilding the compiler +======================== + +After an initial build via `sh build_all.sh` on posix or `build_all.bat` on windows, +you can rebuild the compiler as follows: + +* `nim c koch` if you need to rebuild koch +* `./koch boot -d:release` this ensures the compiler can rebuild itself + (use `koch` instead of `./koch` on windows), which builds the compiler 3 times. + +A faster approach if you don't need to run the full bootstrapping implied by `koch boot`, +is the following: + +* `pathto/nim c --lib:lib -d:release -o:bin/nim_temp compiler/nim.nim` + +Where `pathto/nim` is any nim binary sufficiently recent (e.g. `bin/nim_cources` +built during bootstrap or `$HOME/.nimble/bin/nim` installed by `choosenim 1.2.0`) + +You can pass any additional options such as `-d:leanCompiler` if you don't need +certain features or `-d:debug --stacktrace:on --excessiveStackTrace --stackTraceMsgs` +for debugging the compiler. See also `Debugging the compiler`_. + +Debugging the compiler +====================== + +You can of course use GDB or Visual Studio to debug the +compiler (via ``--debuginfo --lineDir:on``). However, there +are also lots of procs that aid in debugging: + + +.. code-block:: nim + # pretty prints the Nim AST + echo renderTree(someNode) + # outputs some JSON representation + debug(someNode) + # pretty prints some type + echo typeToString(someType) + debug(someType) + echo symbol.name.s + 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() + +These procs may not be imported by a module. You can import them directly for debugging: + +.. code-block:: nim + from astalgo import debug + from types import typeToString + from renderer import renderTree + from msgs import `??` + +To create a new compiler for each run, use ``koch temp``:: + + ./koch temp c /tmp/test.nim + +``koch temp`` creates a debug build of the compiler, which is useful +to create stacktraces for compiler debugging. See also +`Rebuilding the compiler`_ if you need more control. + +Bisecting for regressions +========================= + +``koch temp`` returns 125 as the exit code in case the compiler +compilation fails. This exit code tells ``git bisect`` to skip the +current commit.:: + + 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` by explicit compilation command, see `Rebuilding the compiler`_. + +The compiler's architecture +=========================== + +Nim uses the classic compiler architecture: A lexer/scanner feds tokens to a +parser. The parser builds a syntax tree that is used by the code generator. +This syntax tree is the interface between the parser and the code generator. +It is essential to understand most of the compiler's code. + +In order to compile Nim correctly, type-checking has to be separated from +parsing. Otherwise generics cannot work. + +.. include:: filelist.txt + + +The syntax tree +--------------- +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 +examples how the AST represents each syntactic structure. + + +How the RTL is compiled +======================= + +The ``system`` module contains the part of the RTL which needs support by +compiler magic (and the stuff that needs to be in it because the spec +says so). 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 CCG. Therefore the module ``magicsys`` contains 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 +semantic checking, a ``compilerproc`` is a proc that is used by the code +generator. + + +Compilation cache +================= + +The implementation of the compilation cache is tricky: There are lots +of issues to be solved for the front- and backend. + + +General approach: AST replay +---------------------------- + +We store a module's AST of a successful semantic check in a SQLite +database. There are plenty of features that require a sub sequence +to be re-applied, for example: + +.. code-block:: nim + {.compile: "foo.c".} # even if the module is loaded from the DB, + # "foo.c" needs to be compiled/linked. + +The solution is to **re-play** the module's top level statements. +This solves the problem without having to special case the logic +that fills the internal seqs which are affected by the pragmas. + +In fact, this describes how the AST should be stored in the database, +as a "shallow" tree. Let's assume we compile module ``m`` with the +following contents: + +.. code-block:: nim + import strutils + + var x*: int = 90 + {.compile: "foo.c".} + proc p = echo "p" + proc q = echo "q" + static: + echo "static" + +Conceptually this is the AST we store for the module: + +.. code-block:: nim + import strutils + + var x* + {.compile: "foo.c".} + proc p + proc q + static: + echo "static" + +The symbol's ``ast`` field is loaded lazily, on demand. This is where most +savings come from, only the shallow outer AST is reconstructed immediately. + +It is also important that the replay involves the ``import`` statement so +that dependencies are resolved properly. + + +Shared global compiletime state +------------------------------- + +Nim allows ``.global, compiletime`` variables that can be filled by macro +invocations across different modules. This feature breaks modularity in a +severe way. Plenty of different solutions have been proposed: + +- Restrict the types of global compiletime variables to ``Set[T]`` or + similar unordered, only-growable collections so that we can track + the module's write effects to these variables and reapply the changes + in a different order. +- In every module compilation, reset the variable to its default value. +- Provide a restrictive API that can load/save the compiletime state to + a file. + +(These solutions are not mutually exclusive.) + +Since we adopt the "replay the top level statements" idea, the natural +solution to this problem is to emit pseudo top level statements that +reflect the mutations done to the global variable. However, this is +MUCH harder than it sounds, for example ``squeaknim`` uses this +snippet: + +.. code-block:: nim + apicall.add(") module: '" & dllName & "'>\C" & + "\t^self externalCallFailed\C!\C\C") + stCode.add(st & "\C\t\"Generated by NimSqueak\"\C\t" & apicall) + +We can "replay" ``stCode.add`` only if the values of ``st`` +and ``apicall`` are known. And even then a hash table's ``add`` with its +hashing mechanism is too hard to replay. + +In practice, things are worse still, consider ``someGlobal[i][j].add arg``. +We only know the root is ``someGlobal`` but the concrete path to the data +is unknown as is the value that is added. We could compute a "diff" between +the global states and use that to compute a symbol patchset, but this is +quite some work, expensive to do at runtime (it would need to run after +every module has been compiled) and would also break for hash tables. + +We need an API that hides the complex aliasing problems by not relying +on Nim's global variables. The obvious solution is to use string keys +instead of global variables: + +.. code-block:: nim + + proc cachePut*(key: string; value: string) + proc cacheGet*(key: string): string + +However, the values being strings/json is quite problematic: Many +lookup tables that are built at compiletime embed *proc vars* and +types which have no obvious string representation... Seems like +AST diffing is still the best idea as it will not require to use +an alien API and works with some existing Nimble packages, at least. + +On the other hand, in Nim's future I would like to replace the VM +by native code. A diff algorithm wouldn't work for that. +Instead the native code would work with an API like ``put``, ``get``: + +.. code-block:: nim + + proc cachePut*(key: string; value: NimNode) + proc cacheGet*(key: string): NimNode + +The API should embrace the AST diffing notion: See the +module ``macrocache`` for the final details. + + + +Methods and type converters +--------------------------- + +In the following +sections *global* means *shared between modules* or *property of the whole +program*. + +Nim contains language features that are *global*. The best example for that +are multi methods: Introducing a new method with the same name and some +compatible object parameter means that the method's dispatcher needs to take +the new method into account. So the dispatching logic is only completely known +after the whole program has been translated! + +Other features that are *implicitly* triggered cause problems for modularity +too. Type converters fall into this category: + +.. code-block:: nim + # module A + converter toBool(x: int): bool = + result = x != 0 + +.. code-block:: nim + # module B + import A + + if 1: + echo "ugly, but should work" + +If in the above example module ``B`` is re-compiled, but ``A`` is not then +``B`` needs to be aware of ``toBool`` even though ``toBool`` is not referenced +in ``B`` *explicitly*. + +Both the multi method and the type converter problems are solved by the +AST replay implementation. + + +Generics +~~~~~~~~ + +We cache generic instantiations and need to ensure this caching works +well with the incremental compilation feature. Since the cache is +attached to the ``PSym`` datastructure, it should work without any +special logic. + + +Backend issues +-------------- + +- Init procs must not be "forgotten" to be called. +- Files must not be "forgotten" to be linked. +- Method dispatchers are global. +- DLL loading via ``dlsym`` is global. +- Emulated thread vars are global. + +However the biggest problem is that dead code elimination breaks modularity! +To see why, consider this scenario: The module ``G`` (for example the huge +Gtk2 module...) is compiled with dead code elimination turned on. So none +of ``G``'s procs is generated at all. + +Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem, +``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``. + +Then module ``A`` (that depends on ``B`` and ``G``) is compiled and ``B`` +and ``G`` are left unchanged. ``A`` requires ``G.P2``. + +So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even +loaded ``P1`` from the symbol file, nor do we want to because we then quickly +would restore large parts of the whole program. + + +Solution +~~~~~~~~ + +The backend must have some logic so that if the currently processed module +is from the compilation cache, the ``ast`` field is not accessed. Instead +the generated C(++) for the symbol's body needs to be cached too and +inserted back into the produced C file. This approach seems to deal with +all the outlined problems above. + + +Debugging Nim's memory management +================================= + +The following paragraphs are mostly a reminder for myself. Things to keep +in mind: + +* If an assertion in Nim's memory manager or GC fails, the stack trace + keeps allocating memory! Thus a stack overflow may happen, hiding the + real issue. +* What seem to be C code generation problems is often a bug resulting from + not producing prototypes, so that some types default to ``cint``. Testing + without the ``-w`` option helps! + + +The Garbage Collector +===================== + +Introduction +------------ + +I use the term *cell* here to refer to everything that is traced +(sequences, refs, strings). +This section describes how the GC works. + +The basic algorithm is *Deferrent Reference Counting* with cycle detection. +References on the stack are not counted for better performance and easier C +code generation. + +Each cell has a header consisting of a RC and a pointer to its type +descriptor. However the program does not know about these, so they are placed at +negative offsets. In the GC code the type ``PCell`` denotes a pointer +decremented by the right offset, so that the header can be accessed easily. It +is extremely important that ``pointer`` is not confused with a ``PCell`` +as this would lead to a memory corruption. + + +The CellSet data structure +-------------------------- + +The GC depends on an extremely efficient datastructure for storing a +set of pointers - this is called a ``TCellSet`` in the source code. +Inserting, deleting and searching are done in constant time. However, +modifying a ``TCellSet`` during traversal leads to undefined behaviour. + +.. code-block:: Nim + type + TCellSet # hidden + + proc cellSetInit(s: var TCellSet) # initialize a new set + proc cellSetDeinit(s: var TCellSet) # empty the set and free its memory + proc incl(s: var TCellSet, elem: PCell) # include an element + proc excl(s: var TCellSet, elem: PCell) # exclude an element + + proc `in`(elem: PCell, s: TCellSet): bool # tests membership + + iterator elements(s: TCellSet): (elem: PCell) + + +All the operations have to perform efficiently. Because a Cellset can +become huge a hash table alone is not suitable for this. + +We use a mixture of bitset and hash table for this. The hash table maps *pages* +to a page descriptor. The page descriptor contains a bit for any possible cell +address within this page. So including a cell is done as follows: + +- Find the page descriptor for the page the cell belongs to. +- Set the appropriate bit in the page descriptor indicating that the + cell points to the start of a memory block. + +Removing a cell is analogous - the bit has to be set to zero. +Single page descriptors are never deleted from the hash table. This is not +needed as the data structures needs to be rebuilt periodically anyway. + +Complete traversal is done in this way:: + + for each page descriptor d: + for each bit in d: + if bit == 1: + traverse the pointer belonging to this bit + + +Further complications +--------------------- + +In Nim the compiler cannot always know if a reference +is stored on the stack or not. This is caused by var parameters. +Consider this example: + +.. code-block:: Nim + proc setRef(r: var ref TNode) = + new(r) + + proc usage = + var + r: ref TNode + setRef(r) # here we should not update the reference counts, because + # r is on the stack + setRef(r.left) # here we should update the refcounts! + +We have to decide at runtime whether the reference is on the stack or not. +The generated code looks roughly like this: + +.. code-block:: C + void setref(TNode** ref) { + unsureAsgnRef(ref, newObj(TNode_TI, sizeof(TNode))) + } + void usage(void) { + setRef(&r) + setRef(&r->left) + } + +Note that for systems with a continuous stack (which most systems have) +the check whether the ref is on the stack is very cheap (only two +comparisons). + + +Code generation for closures +============================ + +Code generation for closures is implemented by `lambda lifting`:idx:. + + +Design +------ + +A ``closure`` proc var can call ordinary procs of the default Nim calling +convention. But not the other way round! A closure is implemented as a +``tuple[prc, env]``. ``env`` can be nil implying a call without a closure. +This means that a call through a closure generates an ``if`` but the +interoperability is worth the cost of the ``if``. Thunk generation would be +possible too, but it's slightly more effort to implement. + +Tests with GCC on Amd64 showed that it's really beneficial if the +'environment' pointer is passed as the last argument, not as the first argument. + +Proper thunk generation is harder because the proc that is to wrap +could stem from a complex expression: + +.. code-block:: nim + receivesClosure(returnsDefaultCC[i]) + +A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require +an *additional* closure generation... Ok, not really, but it requires to pass +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 +to pass a proc pointer around via a generic ``ref`` type. + + +Example code: + +.. code-block:: 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 + type + PEnv = ref object + x: int # data + + proc anon(y: int, c: PEnv): int = + return y + c.x + + proc add(x: int): tuple[prc, data] = + var env: PEnv + new env + env.x = x + result = (anon, env) + + 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 + 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 = + return x + y + z + + var add24 = add(2)(4) + echo add24(5) #OUT 11 + +This should produce roughly this code: + +.. code-block:: nim + type + PEnvX = ref object + x: int # data + + PEnvY = ref object + y: int + ex: PEnvX + + proc lambdaZ(z: int, ey: PEnvY): int = + return ey.ex.x + ey.y + z + + proc lambdaY(y: int, ex: PEnvX): tuple[prc, data: PEnvY] = + var ey: PEnvY + new ey + ey.y = y + ey.ex = ex + result = (lambdaZ, ey) + + proc add(x: int): tuple[prc, data: PEnvX] = + var ex: PEnvX + ex.x = x + result = (labmdaY, 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. +More useful is escape analysis and stack allocation of the environment, +however. + + +Alternative +----------- + +Process the closure of all inner procs in one pass and accumulate the +environments. This is however not always possible. + + +Accumulator +----------- + +.. code-block:: nim + proc getAccumulator(start: int): proc (): int {.closure} = + var i = start + return lambda: int = + inc i + return i + + proc p = + var delta = 7 + proc accumulator(start: int): proc(): int = + var x = start-1 + result = proc (): int = + x = x + delta + inc delta + return x + + 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, +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*`` and not something more +specialized. However the more specialized env type needs to passed to the +backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain +the formal hidden parameter, but not ``s.typ``! + + +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 +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 +kind of the `typ` field can be one of the following ones, and it +should be matching the literal kind: + + tyInt, tyInt8, tyInt16, tyInt32, tyInt64, tyUInt, tyUInt8, + tyUInt16, tyUInt32, tyUInt64 + +Then there is also the integer literal type. This is a specific type +that is implicitly convertible into the requested type if the +requested type can hold the value. For this to work, the type needs to +know the concrete value of the literal. For example an expression +`321` will be of type `int literal(321)`. This type is implicitly +convertible to all integer types and ranges that contain the value +`321`. That would be all builtin integer types except `uint8` and +`int8` where `321` would be out of range. When this literal type is +assigned to a new `var` or `let` variable, it's type will be resolved +to just `int`, not `int literal(321)` unlike constants. A constant +keeps the full `int literal(321)` type. Here is an example where that +difference matters. + + +.. code-block:: nim + + proc foo(arg: int8) = + echo "def" + + const tmp1 = 123 + foo(tmp1) # OK + + 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 + + proc bar(arg: int16) = + echo "abc" + proc bar(arg: int8) = + echo "def" + + 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 +pointing back to the integer literal node in the ast containing the +integer value. These are the properties that hold true for integer +literal types. + + n.kind == nkIntLit + n.typ.kind == tyInt + n.typ.n == n + +Other literal types, such as `uint literal(123)` that would +automatically convert to other integer types, but prefers to +become a `uint` are not part of the Nim language. + +In an unchecked AST, the `typ` field is nil. The type checker will set +the `typ` field accordingly to the node kind. Nodes of kind `nkIntLit` +will get the integer literal type (e.g. `int literal(123)`). Nodes of +kind `nkUIntLit` will get type `uint` (kind `tyUint`), etc. + +This also means that it is not possible to write a literal in an +unchecked AST that will after sem checking just be of type `int` and +not implicitly convertible to other integer types. This only works for +all integer types that are not `int`. diff --git a/doc/koch.md b/doc/koch.rst similarity index 53% rename from doc/koch.md rename to doc/koch.rst index 8fa19ce44..01c690816 100644 --- a/doc/koch.md +++ b/doc/koch.rst @@ -4,11 +4,12 @@ :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: -> "A great chef is an artist that I truly respect" -- Robert Stack. +.. raw:: html +

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

    Introduction @@ -16,7 +17,7 @@ Introduction The `koch`:idx: program is Nim's maintenance script. It is a replacement for make and shell scripting with the advantage that it is much more portable. -The word *koch* means *cook* in German. `koch`:cmd: is used mainly to build the +The word *koch* means *cook* in German. ``koch`` is used mainly to build the Nim compiler, but it can also be used for other tasks. This document describes the supported commands and their options. @@ -30,48 +31,46 @@ boot command The `boot`:idx: command bootstraps the compiler, and it accepts different options: --d:release By default a debug version is created, passing this option will +-d:release + By default a debug version is created, passing this option will force a release build, which is much faster and should be preferred 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. +-d:nimUseLinenoise + Use the linenoise library for interactive mode (not needed on Windows). 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 -your `$PATH` or use the install command to place it where it will be +compiler in the ``bin`` directory. You can add Nim's ``bin`` directory to +your ``$PATH`` or use the install command to place it where it will be found. 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). +(``nim_temp``), 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>`_. test command ------------ -The `test`:idx: command can also be invoked with the alias `tests`:option:. This +The `test`:idx: command can also be invoked with the alias ``tests``. This command will compile and run ``testament/tester.nim``, which is the main -driver of Nim's test suite. You can pass options to the `test`:option: command, +driver of Nim's test suite. You can pass options to the ``test`` command, they will be forwarded to the tester. See its source code for available options. web command ----------- -The `web`:idx: command converts the documentation in the `doc` directory +The `web`:idx: command converts the documentation in the ``doc`` directory from rst to HTML. It also repeats the same operation but places the result in the ``web/upload`` which can be used to update the website at https://nim-lang.org. @@ -79,13 +78,6 @@ https://nim-lang.org. By default, the documentation will be built in parallel using the number of available CPU cores. If any documentation build sub-commands fail, they will be rerun in serial fashion so that meaningful error output can be gathered for -inspection. The `--parallelBuild:n`:option: switch or configuration option can be +inspection. The ``--parallelBuild:n`` switch or configuration option can be used to force a specific number of parallel jobs or run everything serially -from the start (`n == 1`). - -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](https://www.latex-project.org/get) first. +from the start (``n == 1``). diff --git a/doc/lib.md b/doc/lib.rst similarity index 50% rename from doc/lib.md rename to doc/lib.rst index d6ec408ec..3c80fd343 100644 --- a/doc/lib.md +++ b/doc/lib.rst @@ -5,8 +5,6 @@ Nim Standard Library :Author: Andreas Rumpf :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: Nim's library is divided into *pure libraries*, *impure libraries*, and *wrappers*. @@ -15,14 +13,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](apis.html) for a quick overview of the API design. +Read this `document `_ for a quick overview of the API design. Nimble ====== Nim's standard library only covers the basics, check -out https://nimble.directory/ for a list of 3rd party packages. +out ``_ for a list of 3rd party packages. Pure libraries @@ -31,53 +29,52 @@ Pure libraries Automatic imports ----------------- -* [system](system.html) +* `system `_ 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 + implicitly by the compiler. Do not import it directly. It relies on compiler magic to work. +* `threads `_ + Basic Nim thread support. **Note**: This is part of the system module. Do not + import it explicitly. Enabled with ``--threads:on``. + +* `channels `_ + Nim message passing support for threads. **Note**: This is part of the + system module. Do not import it explicitly. Enabled with ``--threads:on``. + + Core ---- -* [atomics](atomics.html) - Types and operations for atomic operations and lockless algorithms. - -* [bitops](bitops.html) +* `bitops `_ Provides a series of low-level methods for bit manipulation. -* [compilesettings](compilesettings.html) - This module allows querying the compiler about diverse configuration settings. - -* [cpuinfo](cpuinfo.html) +* `cpuinfo `_ This module implements procs to determine the number of CPUs / cores. -* [effecttraits](effecttraits.html) - This module provides access to the inferred .raises effects - for Nim's macro system. - -* [endians](endians.html) +* `endians `_ This module contains helpers that deal with different byte orders. -* [locks](locks.html) +* `lenientops `_ + Provides binary operators for mixed integer/float expressions for convenience. + +* `locks `_ Locks and condition variables for Nim. -* [macrocache](macrocache.html) - Provides an API for macros to collect compile-time information across modules. - -* [macros](macros.html) +* `macros `_ Contains the AST API and documentation of Nim for writing macros. -* [rlocks](rlocks.html) +* `rlocks `_ Reentrant locks for Nim. -* [typeinfo](typeinfo.html) +* `typeinfo `_ Provides (unsafe) access to Nim's run-time type information. -* [typetraits](typetraits.html) +* `typetraits `_ This module defines compile-time reflection procs for working with types. -* [volatile](volatile.html) +* `volatile `_ This module contains code for generating volatile loads and stores, which are useful in embedded and systems programming. @@ -85,516 +82,405 @@ Core Algorithms ---------- -* [algorithm](algorithm.html) +* `algorithm `_ This module implements some common generic algorithms like sort or binary search. -* [enumutils](enumutils.html) - This module adds functionality for the built-in `enum` type. - -* [sequtils](sequtils.html) - This module implements operations for the built-in `seq` type +* `sequtils `_ + This module implements operations for the built-in seq type which were inspired by functional programming languages. -* [setutils](setutils.html) - This module adds functionality for the built-in `set` type. Collections ----------- -* [critbits](critbits.html) +* `critbits `_ 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.html) +* `deques `_ Implementation of a double-ended queue. - The underlying implementation uses a `seq`. + The underlying implementation uses a ``seq``. -* [heapqueue](heapqueue.html) - Implementation of a binary heap data structure that can be used as a priority queue. +* `heapqueue `_ + Implementation of a heap data structure that can be used as a priority queue. -* [intsets](intsets.html) +* `intsets `_ Efficient implementation of a set of ints as a sparse bit set. -* [lists](lists.html) +* `lists `_ Nim linked list support. Contains singly and doubly linked lists and circular lists ("rings"). -* [options](options.html) +* `options `_ The option type encapsulates an optional value. -* [packedsets](packedsets.html) - Efficient implementation of a set of ordinals as a sparse bit set. +* `sets `_ + Nim hash and bit set support. -* [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). +* `sharedlist `_ + Nim shared linked list support. Contains a shared singly-linked list. -* [sets](sets.html) - Nim hash set support. +* `sharedtables `_ + Nim shared hash table support. Contains shared tables. -* [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. - -* [tables](tables.html) +* `tables `_ Nim hash table support. Contains tables, ordered tables, and count tables. + String handling --------------- -* [cstrutils](cstrutils.html) - Utilities for `cstring` handling. +* `cstrutils `_ + Utilities for ``cstring`` handling. -* [editdistance](editdistance.html) +* `std/editdistance `_ This module contains an algorithm to compute the edit distance between two Unicode strings. -* [encodings](encodings.html) +* `encodings `_ Converts between different character encodings. On UNIX, this uses - the `iconv` library, on Windows the Windows API. + the ``iconv`` library, on Windows the Windows API. -* [formatfloat](formatfloat.html) - This module implements formatting floats as strings. +* `parseutils `_ + This module contains helpers for parsing tokens, numbers, identifiers, etc. -* [objectdollar](objectdollar.html) - This module implements a generic `$` operator to convert objects to strings. +* `pegs `_ + This module contains procedures and operators for handling PEGs. -* [punycode](punycode.html) +* `punycode `_ Implements a representation of Unicode with the limited ASCII character subset. -* [strbasics](strbasics.html) - This module provides some high performance string operations. +* `ropes `_ + This module contains support for a *rope* data type. + Ropes can represent very long strings efficiently; + especially concatenation is done in O(1) instead of O(n). -* [strformat](strformat.html) +* `strformat `_ Macro based standard string interpolation/formatting. Inspired by - Python's f-strings. + Python's ``f``-strings. -* [strmisc](strmisc.html) +* `strmisc `_ This module contains uncommon string handling operations that do not fit with the commonly used operations in strutils. -* [strscans](strscans.html) - This module contains a `scanf` macro for convenient parsing of mini languages. +* `strscans `_ + This module contains a ``scanf`` macro for convenient parsing of mini languages. -* [strutils](strutils.html) +* `strtabs `_ + 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 `_ 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.html) +* `unicode `_ This module provides support to handle the Unicode UTF-8 encoding. -* [unidecode](unidecode.html) +* `unidecode `_ It provides a single proc that does Unicode to ASCII transliterations. Based on Python's Unidecode module. -* [widestrs](widestrs.html) - Nim support for C/C++'s wide strings. - -* [wordwrap](wordwrap.html) +* `std/wordwrap `_ This module contains an algorithm to wordwrap a Unicode string. Time handling ------------- -* [monotimes](monotimes.html) +* `std/monotimes `_ The `monotimes` module implements monotonic timestamps. -* [times](times.html) - The `times` module contains support for working with time. +* `times `_ + The ``times`` module contains support for working with time. Generic Operating System Services --------------------------------- -* [appdirs](appdirs.html) - This module implements helpers for determining special directories used by apps. - -* [cmdline](cmdline.html) - This module contains system facilities for reading command - line parameters. - -* [dirs](dirs.html) - This module implements directory handling. - -* [distros](distros.html) +* `distros `_ 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](packaging.html) for hints on distributing Nim using OS packages. + See `packaging `_ for hints on distributing Nim using OS packages. -* [dynlib](dynlib.html) +* `dynlib `_ This module implements the ability to access symbols from shared libraries. -* [envvars](envvars.html) - This module implements environment variable handling. +* `marshal `_ + Contains procs for serialization and deserialization of arbitrary Nim + data structures. -* [exitprocs](exitprocs.html) - This module allows adding hooks to program exit. - -* [files](files.html) - This module implements file handling. - -* [memfiles](memfiles.html) - This module provides support for memory-mapped files (Posix's `mmap`) +* `memfiles `_ + This module provides support for memory-mapped files (Posix's ``mmap``) on the different operating systems. -* [os](os.html) +* `os `_ Basic operating system facilities like retrieving environment variables, reading command line arguments, working with directories, running shell commands, etc. -* [oserrors](oserrors.html) - This module implements OS error reporting. +* `osproc `_ + Module for process communication beyond ``os.execShellCmd``. -* [osproc](osproc.html) - Module for process communication beyond `os.execShellCmd`. - -* [paths](paths.html) - This module implements path handling. - -* [reservedmem](reservedmem.html) - This module provides utilities for reserving portions of the - address space of a program without consuming physical memory. - -* [streams](streams.html) +* `streams `_ 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 + 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. -* [symlinks](symlinks.html) - This module implements symlink handling. - -* [syncio](syncio.html) - This module implements various synchronized I/O operations. - -* [terminal](terminal.html) +* `terminal `_ 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.html) +* `complex `_ This module implements complex numbers and relevant mathematical operations. -* [fenv](fenv.html) +* `fenv `_ Floating-point environment. Handling of floating-point rounding and exceptions (overflow, zero-divide, etc.). -* [lenientops](lenientops.html) - Provides binary operators for mixed integer/float expressions for convenience. - -* [math](math.html) +* `math `_ Mathematical operations like cosine, square root. -* [random](random.html) +* `mersenne `_ + Mersenne twister random number generator. + +* `random `_ Fast and tiny random number generator. -* [rationals](rationals.html) +* `rationals `_ This module implements rational numbers and relevant mathematical operations. -* [stats](stats.html) - Statistical analysis. +* `stats `_ + Statistical analysis -* [sysrand](sysrand.html) - Cryptographically secure pseudorandom number generator. +* `std/sums `_ + Fast summation functions. Internet Protocols and Support ------------------------------ -* [async](async.html) - Exports `asyncmacro` and `asyncfutures` for native backends, and `asyncjs` on the JS backend. - -* [asyncdispatch](asyncdispatch.html) +* `asyncdispatch `_ This module implements an asynchronous dispatcher for IO operations. -* [asyncfile](asyncfile.html) +* `asyncfile `_ This module implements asynchronous file reading and writing using - `asyncdispatch`. + ``asyncdispatch``. -* [asyncftpclient](asyncftpclient.html) - This module implements an asynchronous FTP client using the `asyncnet` +* `asyncftpclient `_ + This module implements an asynchronous FTP client using the ``asyncnet`` module. -* [asynchttpserver](asynchttpserver.html) - This module implements an asynchronous HTTP server using the `asyncnet` +* `asynchttpserver `_ + This module implements an asynchronous HTTP server using the ``asyncnet`` module. -* [asyncmacro](asyncmacro.html) - Implements the `async` and `multisync` macros for `asyncdispatch`. - -* [asyncnet](asyncnet.html) - This module implements asynchronous sockets based on the `asyncdispatch` +* `asyncnet `_ + This module implements asynchronous sockets based on the ``asyncdispatch`` module. -* [asyncstreams](asyncstreams.html) +* `asyncstreams `_ This module provides `FutureStream` - a future that acts as a queue. -* [cgi](cgi.html) +* `cgi `_ This module implements helpers for CGI applications. -* [cookies](cookies.html) +* `cookies `_ This module contains helper procs for parsing and generating cookies. -* [httpclient](httpclient.html) +* `httpclient `_ This module implements a simple HTTP client which supports both synchronous and asynchronous retrieval of web pages. -* [mimetypes](mimetypes.html) +* `mimetypes `_ This module implements a mimetypes database. -* [nativesockets](nativesockets.html) +* `nativesockets `_ This module implements a low-level sockets API. -* [net](net.html) +* `net `_ This module implements a high-level sockets API. It replaces the - `sockets` module. + ``sockets`` module. -* [selectors](selectors.html) +* `selectors `_ This module implements a selector API with backends specific to each OS. Currently, epoll on Linux and select on other operating systems. -* [smtp](smtp.html) +* `smtp `_ This module implements a simple SMTP client. -* [socketstreams](socketstreams.html) - This module provides an implementation of the streams interface for sockets. - - -* [uri](uri.html) +* `uri `_ This module provides functions for working with URIs. Threading --------- -* [isolation](isolation.html) - This module implements the `Isolated[T]` type for - safe construction of isolated subgraphs that can be - passed efficiently to different channels and threads. - -* [tasks](tasks.html) - This module provides basic primitives for creating parallel programs. - -* [threadpool](threadpool.html) - Implements Nim's [spawn](manual_experimental.html#parallel-amp-spawn). - -* [typedthreads](typedthreads.html) - Basic Nim thread support. +* `threadpool `_ + Implements Nim's `spawn `_. Parsers ------- -* [htmlparser](htmlparser.html) +* `htmlparser `_ This module parses an HTML document and creates its XML tree representation. -* [json](json.html) +* `json `_ High-performance JSON parser. -* [lexbase](lexbase.html) +* `lexbase `_ 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.html) - The `parsecfg` module implements a high-performance configuration file +* `parsecfg `_ + 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.html) - The `parsecsv` module implements a simple high-performance CSV parser. +* `parsecsv `_ + The ``parsecsv`` module implements a simple high-performance CSV parser. -* [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 `_ + The ``parseopt`` module implements a command line option parser. -* [parseopt](parseopt.html) - The `parseopt` module implements a command line option parser. +* `parsesql `_ + The ``parsesql`` module implements a simple high-performance SQL parser. -* [parsesql](parsesql.html) - The `parsesql` module implements a simple high-performance SQL parser. - -* [parseutils](parseutils.html) - This module contains helpers for parsing tokens, numbers, identifiers, etc. - -* [parsexml](parsexml.html) - The `parsexml` module implements a simple high performance XML/HTML parser. +* `parsexml `_ + 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 web can be parsed with it. -* [pegs](pegs.html) - This module contains procedures and operators for handling PEGs. - Docutils -------- -* [packages/docutils/highlite](highlite.html) - Source highlighter for programming or markup languages. Currently, +* `packages/docutils/highlite `_ + 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](rst.html) - This module implements a reStructuredText parser. A large subset +* `packages/docutils/rst `_ + This module implements a reStructuredText parser. A large subset is implemented. Some features of the markdown wiki syntax are also supported. -* [packages/docutils/rstast](rstast.html) +* `packages/docutils/rstast `_ This module implements an AST for the reStructuredText parser. -* [packages/docutils/rstgen](rstgen.html) +* `packages/docutils/rstgen `_ This module implements a generator of HTML/Latex from reStructuredText. XML Processing -------------- -* [xmltree](xmltree.html) +* `xmltree `_ A simple XML tree. More efficient and simpler than the DOM. It also contains a macro for XML/HTML code generation. -* [xmlparser](xmlparser.html) +* `xmlparser `_ This module parses an XML document and creates its XML tree representation. Generators ---------- -* [genasts](genasts.html) - This module implements AST generation using captured variables for macros. - -* [htmlgen](htmlgen.html) - This module implements a simple XML and HTML code - generator. Each commonly used HTML tag has a corresponding macro +* `htmlgen `_ + 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. + Hashing ------- -* [base64](base64.html) - This module implements a Base64 encoder and decoder. +* `base64 `_ + This module implements a base64 encoder and decoder. -* [hashes](hashes.html) +* `hashes `_ This module implements efficient computations of hash values for diverse Nim types. -* [md5](md5.html) +* `md5 `_ This module implements the MD5 checksum algorithm. -* [oids](oids.html) +* `oids `_ 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. + produce a globally distributed unique ID. This implementation was extracted + from the Mongodb interface and it thus binary compatible with a Mongo OID. -* [sha1](sha1.html) - This module implements the SHA-1 checksum algorithm. +* `std/sha1 `_ + This module implements a sha1 encoder and decoder. -Serialization -------------- - -* [jsonutils](jsonutils.html) - This module implements a hookable (de)serialization for arbitrary types - using JSON. - -* [marshal](marshal.html) - Contains procs for serialization and deserialization of arbitrary Nim - data structures. - Miscellaneous ------------- -* [assertions](assertions.html) - This module implements assertion handling. - -* [browsers](browsers.html) +* `browsers `_ This module implements procs for opening URLs with the user's default browser. -* [colors](colors.html) +* `colors `_ This module implements color handling for Nim. -* [coro](coro.html) +* `coro `_ This module implements experimental coroutines in Nim. -* [decls](decls.html) - This module implements syntax sugar for some declarations. - -* [enumerate](enumerate.html) - This module implements `enumerate` syntactic sugar based on Nim's macro system. - -* [importutils](importutils.html) - Utilities related to import and symbol resolution. - -* [logging](logging.html) +* `logging `_ This module implements a simple logger. -* [segfaults](segfaults.html) - Turns access violations or segfaults into a `NilAccessDefect` exception. +* `segfaults `_ + Turns access violations or segfaults into a ``NilAccessDefect`` exception. -* [sugar](sugar.html) +* `sugar `_ This module implements nice syntactic sugar based on Nim's macro system. -* [unittest](unittest.html) +* `unittest `_ Implements a Unit testing DSL. -* [varints](varints.html) +* `std/varints `_ Decode variable-length integers that are compatible with SQLite. -* [with](with.html) - This module implements the `with` macro for easy function chaining. -* [wrapnils](wrapnils.html) - This module allows evaluating expressions safely against nil dereferences. +Modules for JS backend +---------------------- - -Modules for the JavaScript backend ----------------------------------- - -* [asyncjs](asyncjs.html) +* `asyncjs `_ Types and macros for writing asynchronous procedures in JavaScript. -* [dom](dom.html) +* `dom `_ Declaration of the Document Object Model for the JS backend. -* [jsbigints](jsbigints.html) - Arbitrary precision integers. +* `jsconsole `_ + Wrapper for the ``console`` object. -* [jsconsole](jsconsole.html) - Wrapper for the `console` object. - -* [jscore](jscore.html) +* `jscore `_ The wrapper of core JavaScript functions. For most purposes, you should be using - the `math`, `json`, and `times` stdlib modules instead of this module. + the ``math``, ``json``, and ``times`` stdlib modules instead of this module. -* [jsfetch](jshttp.html) - Wrapper for `fetch`. - -* [jsffi](jsffi.html) +* `jsffi `_ Types and macros for easier interaction with JavaScript. -* [jsre](jsre.html) - Regular Expressions for the JavaScript target. - Impure libraries ================ @@ -602,40 +488,25 @@ Impure libraries Regular expressions ------------------- -* [re](re.html) - This module contains procedures and operators for handling regular +* `re `_ + This module contains procedures and operators for handling regular expressions. The current implementation uses PCRE. -* [nre](nre.html) - - This module contains many help functions for handling regular expressions. - The current implementation uses PCRE. Database support ---------------- -* [db_mysql](db_mysql.html) - A higher level MySQL database wrapper. The same interface is implemented - for other databases too. - -* [db_odbc](db_odbc.html) - A higher level ODBC database wrapper. The same interface is implemented - for other databases too. - -* [db_postgres](db_postgres.html) +* `db_postgres `_ A higher level PostgreSQL database wrapper. The same interface is implemented for other databases too. -* [db_sqlite](db_sqlite.html) - A higher level SQLite database wrapper. The same interface is implemented +* `db_mysql `_ + A higher level MySQL database wrapper. The same interface is implemented for other databases too. - -Generic Operating System Services ---------------------------------- - -* [rdstdin](rdstdin.html) - This module contains code for reading from stdin. +* `db_sqlite `_ + A higher level SQLite database wrapper. The same interface is implemented + for other databases too. Wrappers @@ -648,43 +519,42 @@ not contained in the distribution. You can then find them on the website. Windows-specific ---------------- -* [winlean](winlean.html) +* `winlean `_ Contains a wrapper for a small subset of the Win32 API. -* [registry](registry.html) +* `registry `_ Windows registry support. UNIX specific ------------- -* [posix](posix.html) +* `posix `_ Contains a wrapper for the POSIX standard. -* [posix_utils](posix_utils.html) +* `posix_utils `_ Contains helpers for the POSIX standard or specialized for Linux and BSDs. - Regular expressions ------------------- -* [pcre](pcre.html) +* `pcre `_ Wrapper for the PCRE library. Database support ---------------- -* [mysql](mysql.html) - Contains a wrapper for the mySQL API. -* [odbcsql](odbcsql.html) - interface to the ODBC driver. -* [postgres](postgres.html) +* `postgres `_ Contains a wrapper for the PostgreSQL API. -* [sqlite3](sqlite3.html) - Contains a wrapper for the SQLite 3 API. +* `mysql `_ + Contains a wrapper for the mySQL API. +* `sqlite3 `_ + Contains a wrapper for SQLite 3 API. +* `odbcsql `_ + interface to the ODBC driver. Network Programming and Internet Protocols ------------------------------------------ -* [openssl](openssl.html) +* `openssl `_ Wrapper for OpenSSL. diff --git a/doc/manual.md b/doc/manual.rst similarity index 58% rename from doc/manual.md rename to doc/manual.rst index 31c620ca7..9fabee1e8 100644 --- a/doc/manual.md +++ b/doc/manual.rst @@ -5,14 +5,12 @@ Nim Manual :Authors: Andreas Rumpf, Zahary Karadjov :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. 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,41 +20,41 @@ About this document precise wording. This manual is constantly evolving into a proper specification. **Note**: The experimental features of Nim are -covered [here](manual_experimental.html). +covered `here `_. **Note**: Assignments, moves, and destruction are specified in -the [destructors](destructors.html) document. +the `destructors `_ 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](nimc.html) and the [DocGen Tools Guide](docgen.html). +`Compiler User Guide `_ and `DocGen Tools Guide `_. -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 +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 optional *a*. Parentheses may be used to group elements. -`&` is the lookahead operator; `&a` means that an `a` is expected but +``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but not consumed. It will be consumed in the following rule. -The `|`, `/` symbols are used to mark alternatives and have the lowest -precedence. `/` is the ordered choice that requires the parser to try the -alternatives in the given order. `/` is often used to ensure the grammar +The ``|``, ``/`` symbols are used to mark alternatives and have the lowest +precedence. ``/`` is the ordered choice that requires the parser to try the +alternatives in the given order. ``/`` is often used to ensure the grammar is not ambiguous. Non-terminals start with a lowercase letter, abstract terminal symbols are in UPPERCASE. Verbatim terminal symbols (including keywords) are quoted -with `'`. An example: +with ``'``. An example:: - ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)? + ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)? -The binary `^*` operator is used as a shorthand for 0 or more occurrences -separated by its second argument; likewise `^+` means 1 or more -occurrences: `a ^+ b` is short for `a (b a)*` -and `a ^* b` is short for `(a (b a)*)?`. Example: +The binary ``^*`` operator is used as a shorthand for 0 or more occurrences +separated by its second argument; likewise ``^+`` means 1 or more +occurrences: ``a ^+ b`` is short for ``a (b a)*`` +and ``a ^* b`` is short for ``(a (b a)*)?``. Example:: - arrayConstructor = '[' expr ^* ',' ']' + arrayConstructor = '[' expr ^* ',' ']' Other parts of Nim, like scoping rules or runtime semantics, are described informally. @@ -91,11 +89,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 code may be executed at +However, some of the 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] for +supported at compile-time, but there are some restrictions -- see `Restrictions +on Compile-Time Execution <#restrictions-on-compileminustime-execution>`_ for details. We use the term `runtime`:idx: to cover both compile-time execution and code execution in the executable. @@ -111,26 +109,25 @@ 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. - ```nim +.. code-block:: nim var a: array[0..1, char] let i = 5 try: a[i] = 'N' except IndexDefect: echo "invalid index" - ``` -The current implementation allows switching between these different behaviors -via `--panics:on|off`:option:. When panics are turned on, the program dies with a +The current implementation allows to switch between these different behaviors +via ``--panics:on|off``. 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 +exceptions. The benefit of ``--panics:on`` is that it produces smaller binary code and the compiler has more freedom to optimize the code. An `unchecked runtime error`:idx: is an error that is not guaranteed to be @@ -183,23 +180,23 @@ lookahead. The parser uses a stack of indentation levels: the stack consists of integers counting the spaces. The indentation information is queried at strategic -places in the parser but ignored otherwise: The pseudo-terminal `IND{>}` +places in the parser but ignored otherwise: The pseudo-terminal ``IND{>}`` denotes an indentation that consists of more spaces than the entry at the top -of the stack; `IND{=}` an indentation that has the same number of spaces. `DED` +of the stack; ``IND{=}`` an indentation that has the same number of spaces. ``DED`` is another pseudo terminal that describes the *action* of popping a value -from the stack, `IND{>}` then implies to push onto the stack. +from the stack, ``IND{>}`` then implies to push onto the stack. With this notation we can now easily define the core of the grammar: A block of -statements (simplified example): +statements (simplified example):: - ifStmt = 'if' expr ':' stmt - (IND{=} 'elif' expr ':' stmt)* - (IND{=} 'else' ':' stmt)? + ifStmt = 'if' expr ':' stmt + (IND{=} 'elif' expr ':' stmt)* + (IND{=} 'else' ':' stmt)? - simpleStmt = ifStmt / ... + simpleStmt = ifStmt / ... - stmt = IND{>} stmt ^+ IND{=} DED # list of statements - / simpleStmt # or a simple statement + stmt = IND{>} stmt ^+ IND{=} DED # list of statements + / simpleStmt # or a simple statement @@ -207,24 +204,23 @@ Comments -------- Comments start anywhere outside a string or character literal with the -hash character `#`. +hash character ``#``. Comments consist of a concatenation of `comment pieces`:idx:. A comment piece -starts with `#` and runs until the end of the line. The end of line characters +starts with ``#`` and runs until the end of the line. The end of line characters belong to the piece. If the next line only consists of a comment piece with no other tokens between it and the preceding one, it does not start a new comment: - ```nim +.. code-block:: nim i = 0 # This is a single comment over multiple lines. - # The lexer merges these two pieces. + # The scanner merges these two pieces. # The comment continues here. - ``` -`Documentation comments`:idx: are comments that start with two `##`. +`Documentation comments`:idx: are comments that start with two ``##``. Documentation comments are tokens; they are only allowed at certain places in -the input file as they belong to the syntax tree. +the input file as they belong to the syntax tree! Multiline comments @@ -233,41 +229,26 @@ Multiline comments Starting with version 0.13.0 of the language Nim supports multiline comments. They look like: - ```nim +.. code-block:: nim #[Comment here. Multiple lines are not a problem.]# - ``` Multiline comments support nesting: - ```nim +.. code-block:: nim #[ #[ Multiline comment in already commented out code. ]# proc p[T](x: T) = discard ]# - ``` Multiline documentation comments also exist and support nesting too: - ```nim +.. code-block:: nim proc foo = ##[Long documentation comment - here. + here. ]## - ``` - -You can also use the [discard statement](#statements-and-expressions-discard-statement) together with -[triple quoted string literals](#lexical-analysis-triple-quoted-string-literals) to create multiline comments: - - ```nim - discard """ You can have any Nim code text commented - out inside this with no indentation restrictions. - yes("May I ask a pointless question?") """ - ``` - -This was how multiline comments were done before version 0.13.0, -and it is used to provide specifications to [testament](testament.html#writing-unitests) test framework. Identifiers & Keywords @@ -277,24 +258,22 @@ Identifiers in Nim can be any string of letters, digits and underscores, with the following restrictions: * begins with a letter -* does not end with an underscore `_` -* two immediate following underscores `__` are not allowed: +* does not end with an underscore ``_`` +* two immediate following underscores ``__`` are not allowed:: - ``` 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 +classified as a ``letter`` and may thus be part of an identifier but later versions of the language may assign some Unicode characters to belong to the operator characters instead. The following keywords are reserved and cannot be used as identifiers: - ```nim file="keywords.txt" - ``` +.. code-block:: nim + :file: keywords.txt Some keywords are unused; they are reserved for future developments of the language. @@ -305,11 +284,10 @@ Identifier equality Two identifiers are considered equal if the following algorithm returns true: - ```nim +.. code-block:: 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. @@ -324,35 +302,34 @@ by different programmers cannot use incompatible conventions. A Nim-aware editor or IDE can show the identifiers as preferred. Another advantage is that it frees the programmer from remembering the exact spelling of an identifier. The exception with respect to the first -letter allows common code like `var foo: Foo` to be parsed unambiguously. +letter allows common code like ``var foo: Foo`` to be parsed unambiguously. -Note that this rule also applies to keywords, meaning that `notin` is -the same as `notIn` and `not_in` (all-lowercase version (`notin`, `isnot`) +Note that this rule also applies to keywords, meaning that ``notin`` is +the same as ``notIn`` and ``not_in`` (all-lowercase version (``notin``, ``isnot``) is the preferred way of writing keywords). Historically, Nim was a fully `style-insensitive`:idx: language. This meant that it was not case-sensitive and underscores were ignored and there was not even a -distinction between `foo` and `Foo`. +distinction between ``foo`` and ``Foo``. -Keywords as identifiers ------------------------ +Stropping +--------- If a keyword is enclosed in backticks it loses its keyword property and becomes an ordinary identifier. Examples - ```nim +.. code-block:: nim var `var` = "Hello Stropping" - ``` - ```nim - type Obj = object - `type`: int +.. code-block:: nim + type Type = object + `int`: int - let `object` = Obj(`type`: 9) - assert `object` is Obj - assert `object`.`type` == 9 + let `object` = Type(`int`: 9) + assert `object` is Type + assert `object`.`int` == 9 var `var` = 42 let `let` = 8 @@ -360,13 +337,12 @@ Examples const `assert` = true assert `assert` - ``` String literals --------------- -Terminal symbol in the grammar: `STR_LIT`. +Terminal symbol in the grammar: ``STR_LIT``. String literals can be delimited by matching double quotes, and can contain the following `escape sequences`:idx:\ : @@ -395,93 +371,93 @@ contain the following `escape sequences`:idx:\ : ``\u`` HHHH `unicode codepoint with hex value HHHH`:idx:; exactly four hex digits are allowed ``\u`` {H+} `unicode codepoint`:idx:; - all hex digits enclosed in `{}` are used for + all hex digits enclosed in ``{}`` are used for the codepoint ================== =================================================== -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. Triple quoted string literals ----------------------------- -Terminal symbol in the grammar: `TRIPLESTR_LIT`. +Terminal symbol in the grammar: ``TRIPLESTR_LIT``. -String literals can also be delimited by three double quotes `"""` ... `"""`. -Literals in this form may run for several lines, may contain `"` and do not +String literals can also be delimited by three double quotes +``"""`` ... ``"""``. +Literals in this form may run for several lines, may contain ``"`` and do not interpret any escape sequences. -For convenience, when the opening `"""` is followed by a newline (there may -be whitespace between the opening `"""` and the newline), +For convenience, when the opening ``"""`` is followed by a newline (there may +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: +ending of the string literal is defined by the pattern ``"""[^"]``, so this: - ```nim +.. code-block:: nim """"long string within quotes"""" - ``` -Produces: +Produces:: - "long string within quotes" + "long string within quotes" Raw string literals ------------------- -Terminal symbol in the grammar: `RSTR_LIT`. +Terminal symbol in the grammar: ``RSTR_LIT``. There are also raw string literals that are preceded with the -letter `r` (or `R`) and are delimited by matching double quotes (just +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: - ```nim +.. code-block:: 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: +To produce a single ``"`` within a raw string literal, it has to be doubled: + +.. code-block:: nim - ```nim r"a""b" - ``` -Produces: +Produces:: - a"b + a"b -`r""""` is not possible with this notation, because the three leading -quotes introduce a triple quoted string literal. `r"""` is the same -as `"""` since triple quoted string literals do not interpret escape +``r""""`` is not possible with this notation, because the three leading +quotes introduce a triple quoted string literal. ``r"""`` is the same +as ``"""`` since triple quoted string literals do not interpret escape sequences either. Generalized raw string literals ------------------------------- -Terminal symbols in the grammar: `GENERALIZED_STR_LIT`, -`GENERALIZED_TRIPLESTR_LIT`. +Terminal symbols in the grammar: ``GENERALIZED_STR_LIT``, +``GENERALIZED_TRIPLESTR_LIT``. -The construct `identifier"string literal"` (without whitespace between the +The construct ``identifier"string literal"`` (without whitespace between the identifier and the opening quotation mark) is a generalized raw string literal. It is a shortcut for the construct -`identifier(r"string literal")`, so it denotes a routine call with a +``identifier(r"string literal")``, so it denotes a procedure call with a raw string literal as its only argument. Generalized raw string literals are especially convenient for embedding mini languages directly into Nim (for example regular expressions). -The construct `identifier"""string literal"""` exists too. It is a shortcut -for `identifier("""string literal""")`. +The construct ``identifier"""string literal"""`` exists too. It is a shortcut +for ``identifier("""string literal""")``. Character literals ------------------ -Character literals are enclosed in single quotes `''` and can contain the +Character literals are enclosed in single quotes ``''`` and can contain the same escape sequences as strings - with one exception: the platform dependent `newline`:idx: (``\p``) -is not allowed as it may be wider than one character (it can be the pair -CR/LF). Here are the valid `escape sequences`:idx: for character +is not allowed as it may be wider than one character (often it is the pair +CR/LF for example). Here are the valid `escape sequences`:idx: for character literals: ================== =================================================== @@ -505,188 +481,107 @@ literals: exactly two hex digits are allowed ================== =================================================== -A character is not a Unicode character but a single byte. - -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](unicode.html). - -A character literal that does not end in `'` is interpreted as `'` if there -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]. +A character is not a Unicode character but a single byte. 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 specially designed for +this. Another reason is that Nim can thus support ``array[char, int]`` or +``set[char]`` efficiently as many algorithms rely on this feature. The `Rune` +type is used for Unicode characters, it can represent any Unicode character. +``Rune`` is declared in the `unicode module `_. -Numeric literals ----------------- +Numerical constants +------------------- -Numeric literals have the form: +Numerical constants are of a single type and have the form:: - hexdigit = digit | 'A'..'F' | 'a'..'f' - octdigit = '0'..'7' - bindigit = '0'..'1' - unary_minus = '-' # See the section about unary minus - HEX_LIT = unary_minus? '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* - DEC_LIT = unary_minus? digit ( ['_'] digit )* - OCT_LIT = unary_minus? '0' 'o' octdigit ( ['_'] octdigit )* - BIN_LIT = unary_minus? '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* + hexdigit = digit | 'A'..'F' | 'a'..'f' + octdigit = '0'..'7' + bindigit = '0'..'1' + HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* + DEC_LIT = digit ( ['_'] digit )* + OCT_LIT = '0' 'o' octdigit ( ['_'] octdigit )* + BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* - INT_LIT = HEX_LIT - | DEC_LIT - | OCT_LIT - | BIN_LIT + INT_LIT = HEX_LIT + | DEC_LIT + | OCT_LIT + | BIN_LIT - INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8' - INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16' - INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32' - INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64' + INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8' + INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16' + INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32' + INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64' - UINT_LIT = INT_LIT ['\''] ('u' | 'U') - UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8' - UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16' - UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32' - UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64' + UINT_LIT = INT_LIT ['\''] ('u' | 'U') + UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8' + UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16' + UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32' + UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64' - exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* - FLOAT_LIT = unary_minus? digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent) - FLOAT32_SUFFIX = ('f' | 'F') ['32'] - FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX - | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX - FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D' - FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX - | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX - - CUSTOM_NUMERIC_LIT = (FLOAT_LIT | INT_LIT) '\'' CUSTOM_NUMERIC_SUFFIX - - # CUSTOM_NUMERIC_SUFFIX is any Nim identifier that is not - # a pre-defined type suffix. + exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* + FLOAT_LIT = digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent) + FLOAT32_SUFFIX = ('f' | 'F') ['32'] + FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX + | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX + FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D' + FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX + | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX -As can be seen in the productions, numeric literals can contain underscores +As can be seen in the productions, numerical constants can contain underscores for readability. Integer and floating-point literals may be given in decimal (no -prefix), binary (prefix `0b`), octal (prefix `0o`), and hexadecimal -(prefix `0x`) notation. +prefix), binary (prefix ``0b``), octal (prefix ``0o``), and hexadecimal +(prefix ``0x``) notation. -The fact that the unary minus `-` in a number literal like `-1` is considered -to be part of the literal is a late addition to the language. The rationale is that -an expression `-128'i8` should be valid and without this special case, this would -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 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: - - ```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:): - - ```nim - echo x-1 - echo (int)-1 - echo [a]-1 - "abc"-1 - ``` - - -The suffix starting with an apostrophe ('\'') is called a +There exists a literal for each numerical type that is +defined. The suffix starting with an apostrophe ('\'') is called a `type suffix`:idx:. Literals without a type suffix are of an integer type -unless the literal contains a dot or `E|e` in which case it is of -type `float`. This integer type is `int` if the literal is in the range -`low(int32)..high(int32)`, otherwise it is `int64`. +unless the literal contains a dot or ``E|e`` in which case it is of +type ``float``. This integer type is ``int`` if the literal is in the range +``low(i32)..high(i32)``, otherwise it is ``int64``. For notational convenience, the apostrophe of a type suffix is optional if it is not ambiguous (only hexadecimal floating-point literals with a type suffix can be ambiguous). -The pre-defined type suffixes are: +The type suffixes are: ================= ========================= Type Suffix Resulting type of literal ================= ========================= - `'i8` int8 - `'i16` int16 - `'i32` int32 - `'i64` int64 - `'u` uint - `'u8` uint8 - `'u16` uint16 - `'u32` uint32 - `'u64` uint64 - `'f` float32 - `'d` float64 - `'f32` float32 - `'f64` float64 + ``'i8`` int8 + ``'i16`` int16 + ``'i32`` int32 + ``'i64`` int64 + ``'u`` uint + ``'u8`` uint8 + ``'u16`` uint16 + ``'u32`` uint32 + ``'u64`` uint64 + ``'f`` float32 + ``'d`` float64 + ``'f32`` float32 + ``'f64`` float64 ================= ========================= Floating-point literals may also be in binary, octal or hexadecimal notation: -`0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64` +``0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64`` is approximately 1.72826e35 according to the IEEE floating-point standard. -Literals must match the datatype, for example, `333'i8` is an invalid literal. -Non-base-10 literals are used mainly for flags and bit pattern representations, -therefore the checking is done on bit width and not on value range. +Literals are bounds checked so that they fit the datatype. Non-base-10 +literals are used mainly for flags and bit pattern representations, therefore +bounds checking is done on bit width, not value range. If the literal fits in +the bit width of the datatype, it is accepted. Hence: 0b10000000'u8 == 0x80'u8 == 128, but, 0b10000000'i8 == 0x80'i8 == -1 instead of causing an overflow error. - -### 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: - - ```nim - import strutils - type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble" - proc `'u4`(n: string): u4 = - # The leading ' is required. - 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: - - ```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. - - Operators --------- Nim allows user defined operators. An operator is any combination of the -following characters: +following characters:: = + - * / < > @ $ ~ & % | @@ -696,51 +591,37 @@ following characters: defined here.) These keywords are also operators: -`and or not xor shl shr div mod in notin is isnot of as from`. +``and or not xor shl shr div mod in notin is isnot of as from``. -`.`:tok:, `=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they +`.`:tok: `=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they are used for other notational purposes. -`*:` is as a special case treated as the two tokens `*`:tok: and `:`:tok: -(to support `var v*: T`). +``*:`` is as a special case treated as the two tokens `*`:tok: and `:`:tok: +(to support ``var v*: T``). -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. +The ``not`` keyword is always a unary operator, ``a not b`` is parsed +as ``a(not b)``, not as ``(a) not (b)``. Other tokens ------------ -The following strings denote other tokens: +The following strings denote other tokens:: ` ( ) { } [ ] , ; [. .] {. .} (. .) [: The `slice`:idx: operator `..`:tok: takes precedence over other tokens that -contain a dot: `{..}` are the three tokens `{`:tok:, `..`:tok:, `}`:tok: +contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok: and not the two tokens `{.`:tok:, `.}`:tok:. + 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. @@ -750,92 +631,79 @@ Binary operators have 11 different levels of precedence. Associativity ------------- -Binary operators whose first character is `^` are right-associative, all +Binary operators whose first character is ``^`` are right-associative, all other binary operators are left-associative. - ```nim +.. code-block:: 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 ---------- Unary operators always bind stronger than any binary -operator: `$a + b` is `($a) + b` and not `$(a + b)`. +operator: ``$a + b`` is ``($a) + b`` and not ``$(a + b)``. -If a unary operator's first character is `@` it is a `sigil-like`:idx: -operator which binds stronger than a `primarySuffix`: `@x.abc` is parsed -as `(@x).abc` whereas `$x.abc` is parsed as `$(x.abc)`. +If an unary operator's first character is ``@`` it is a `sigil-like`:idx: +operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed +as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``. For binary operators that are not keywords, the precedence is determined by the following rules: -Operators ending in either `->`, `~>` or `=>` are called +Operators ending in either ``->``, ``~>`` or ``=>`` are called `arrow like`:idx:, and have the lowest precedence of all operators. -If the operator ends with `=` and its first character is none of -`<`, `>`, `!`, `=`, `~`, `?`, it is an *assignment operator* which +If the operator ends with ``=`` and its first character is none of +``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which has the second-lowest precedence. Otherwise, precedence is determined by the first character. - ================ ======================================================= ================== =============== Precedence level Operators First character Terminal symbol ================ ======================================================= ================== =============== - 10 (highest) `$ ^` OP10 - 9 `* / div mod shl shr %` `* % \ /` OP9 - 8 `+ -` `+ - ~ |` OP8 - 7 `&` `&` OP7 - 6 `..` `.` OP6 - 5 `== <= < >= > != in notin is isnot not of as from` `= < > !` OP5 - 4 `and` OP4 - 3 `or xor` OP3 - 2 `@ : ?` OP2 - 1 *assignment operator* (like `+=`, `*=`) OP1 - 0 (lowest) *arrow like operator* (like `->`, `=>`) OP0 + 10 (highest) ``$ ^`` OP10 + 9 ``* / div mod shl shr %`` ``* % \ /`` OP9 + 8 ``+ -`` ``+ - ~ |`` OP8 + 7 ``&`` ``&`` OP7 + 6 ``..`` ``.`` OP6 + 5 ``== <= < >= > != in notin is isnot not of as from`` ``= < > !`` OP5 + 4 ``and`` OP4 + 3 ``or xor`` OP3 + 2 ``@ : ?`` OP2 + 1 *assignment operator* (like ``+=``, ``*=``) OP1 + 0 (lowest) *arrow like operator* (like ``->``, ``=>``) OP0 ================ ======================================================= ================== =============== 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): - ```nim +.. code-block:: nim echo $foo # is parsed as echo($foo) - ``` -Spacing also determines whether `(a, b)` is parsed as an argument list +Spacing also determines whether ``(a, b)`` is parsed as an argument list of a call or whether it is parsed as a tuple constructor: - ```nim +.. code-block:: nim echo(1, 2) # pass 1 and 2 to echo - ``` - ```nim +.. code-block:: nim echo (1, 2) # pass the tuple (1, 2) to echo - ``` - -Dot-like operators ------------------- - -Terminal symbol in the grammar: `DOTLIKEOP`. - -Dot-like operators are operators starting with `.`, but not with `..`, for e.g. `.?`; -they have the same precedence as `.`, so that `a.?b.c` is parsed as `(a.?b).c` instead of `a.?(b.c)`. Grammar ------- -The grammar's start symbol is `module`. +The grammar's start symbol is ``module``. .. include:: grammar.txt :literal: @@ -848,7 +716,9 @@ Order of evaluation Order of evaluation is strictly left-to-right, inside-out as it is typical for most others imperative programming languages: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + var s = "" proc p(arg: int): int = @@ -858,13 +728,14 @@ 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: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + var v = 0 proc getI(): int = result = v @@ -882,18 +753,19 @@ right-hand side: someCopy(b[getI()], getI()) doAssert b == [1, 0, 0] - ``` Rationale: Consistency with overloaded assignment or assignment-like operations, -`a = b` can be read as `performSomeCopy(a, b)`. +``a = b`` can be read as ``performSomeCopy(a, b)``. 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: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + var s = "" proc p(): int = @@ -920,7 +792,6 @@ 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. @@ -955,37 +826,37 @@ constant expressions that may be surprising to those coming from other statically typed languages. For example, the following code echoes the beginning of the Fibonacci series **at compile-time**. (This is a demonstration of flexibility in defining constants, not a recommended style for solving this -problem.) +problem!) - ```nim test = "nim c $1" - import std/strformat +.. code-block:: nim + :test: "nim c $1" + import strformat - var fibN {.compileTime.}: int - var fibPrev {.compileTime.}: int - var fibPrevPrev {.compileTime.}: int + var fib_n {.compileTime.}: int + var fib_prev {.compileTime.}: int + var fib_prev_prev {.compileTime.}: int - proc nextFib(): int = - result = if fibN < 2: - fibN + proc next_fib(): int = + result = if fib_n < 2: + fib_n else: - fibPrevPrev + fibPrev - inc(fibN) - fibPrevPrev = fibPrev - fibPrev = result + fib_prev_prev + fib_prev + inc(fib_n) + fib_prev_prev = fib_prev + fib_prev = result - const f0 = nextFib() - const f1 = nextFib() + const f0 = next_fib() + const f1 = next_fib() - const displayFib = block: - const f2 = nextFib() + const display_fib = block: + const f2 = next_fib() var result = fmt"Fibonacci sequence: {f0}, {f1}, {f2}" for i in 3..12: - add(result, fmt", {nextFib()}") + add(result, fmt", {next_fib()}") result static: - echo displayFib - ``` + echo display_fib Restrictions on Compile-Time Execution @@ -996,11 +867,11 @@ language features: * methods * closure iterators -* the `cast` operator +* the ``cast`` operator * reference (pointer) types * FFI -The use of wrappers that use FFI and/or `cast` is also disallowed. Note that +The use of wrappers that use FFI and/or ``cast`` is also disallowed. Note that these wrappers include the ones in the standard libraries. Some or all of these restrictions are likely to be lifted over time. @@ -1029,15 +900,13 @@ Ordinal types ------------- Ordinal types have the following characteristics: -- Ordinal types are countable and ordered. This property allows the operation - of functions such as `inc`, `ord`, and `dec` on ordinal types to +- Ordinal types are countable and ordered. This property allows + the operation of functions as ``inc``, ``ord``, ``dec`` on ordinal types to be defined. -- Ordinal types have a smallest possible value, accessible with `low(type)`. - Trying to count further down than the smallest value produces a panic or - a static error. -- Ordinal types have a largest possible value, accessible with `high(type)`. - Trying to count further up than the largest value produces a panic or - a static error. +- Ordinal values have the smallest possible value. Trying to count further + down than the smallest value produces a panic or a static error. +- Ordinal values have the largest possible value. Trying to count further + than the largest value produces a panic or a static error. Integers, bool, characters, and enumeration types (and subranges of these types) belong to ordinal types. @@ -1049,74 +918,83 @@ Pre-defined integer types ------------------------- These integer types are pre-defined: -`int` -: the generic signed integer type; its size is platform-dependent and has the +``int`` + 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`. + ``low(int32)..high(int32)`` otherwise the literal's type is ``int64``. -`int`\ XX -: additional signed integer types of XX bits use this naming scheme +intXX + 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`. + 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 - has the same size as a pointer. An integer literal with the type - suffix `'u` is of this type. +``uint`` + 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 +uintXX + 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. + The current implementation supports ``uint8``, ``uint16``, ``uint32``, + ``uint64``. Literals of these types have the suffix 'uXX. Unsigned operations all wrap around; they cannot lead to over- or underflow errors. In addition to the usual arithmetic operators for signed and unsigned integers -(`+ - *` etc.) there are also operators that formally work on *signed* +(``+ - *`` etc.) there are also operators that formally work on *signed* integers but treat their arguments as *unsigned*: They are mostly provided for backwards compatibility with older versions of the language that lacked unsigned integer types. These unsigned operations for signed integers use -the `%` suffix as convention: +the ``%`` suffix as convention: ====================== ====================================================== operation meaning ====================== ====================================================== -`a +% b` unsigned integer addition -`a -% b` unsigned integer subtraction -`a *% b` unsigned integer multiplication -`a /% b` unsigned integer division -`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 +``a +% b`` unsigned integer addition +``a -% b`` unsigned integer subtraction +``a *% b`` unsigned integer multiplication +``a /% b`` unsigned integer division +``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 kinds of integer types are used: the smaller type is converted to the larger. A `narrowing type conversion`:idx: converts a larger to a smaller type (for -example `int32 -> int16`). A `widening type conversion`:idx: converts a -smaller type to a larger type (for example `int16 -> int32`). In Nim only +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*: - ```nim +.. code-block:: nim var myInt16 = 5i16 var myInt: int - myInt16 + 34 # of type `int16` - myInt16 + myInt # of type `int` - myInt16 + 2i32 # of type `int32` - ``` + 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 +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. +expensive than other implicit conversions, so ``myInt16 + 34`` produces +an ``int16`` result. -For further details, see [Convertible relation]. +For further details, see `Convertible relation +<#type-relations-convertible-relation>`_. Subrange types @@ -1125,23 +1003,21 @@ 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: - ```nim +.. code-block:: 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 -to 5. `PositiveFloat` defines a subrange of all positive floating-point values. +``Subrange`` is a subrange of an integer which can only hold the values 0 +to 5. ``PositiveFloat`` defines a subrange of all positive floating-point values. NaN does not belong to any subrange of floating-point types. -Assigning any other value to a variable of type `Subrange` is a +Assigning any other value to a variable of type ``Subrange`` is a panic (or a static error if it can be determined during semantic analysis). Assignments from the base type to one of its subrange types (and vice versa) are allowed. -A subrange type has the same size as its base type (`int` in the +A subrange type has the same size as its base type (``int`` in the Subrange example). @@ -1150,22 +1026,23 @@ 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, - but now it is always mapped to `float64`. +``float`` + 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 - this naming scheme (example: `float64` is a 64-bit wide float). The current - implementation supports `float32` and `float64`. Literals of these types +floatXX + 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: @@ -1189,51 +1066,48 @@ 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: - ```nim +.. code-block:: 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 -`FloatDivByZeroDefect`. +In the current implementation ``FloatDivByZeroDefect`` and ``FloatInexactDefect`` +are never raised. ``FloatOverflowDefect`` is raised instead of +``FloatDivByZeroDefect``. There is also a `floatChecks`:idx: pragma that is a short-cut for the -combination of `nanChecks` and `infChecks` pragmas. `floatChecks` are +combination of ``nanChecks`` and ``infChecks`` pragmas. ``floatChecks`` are turned off as default. -The only operations that are affected by the `floatChecks` pragma are -the `+`, `-`, `*`, `/` operators for floating-point types. +The only operations that are affected by the ``floatChecks`` pragma are +the ``+``, ``-``, ``*``, ``/`` operators for floating-point types. An implementation should always use the maximum precision available to evaluate -floating-point values during semantic analysis; this means expressions like -`0.09'f32 + 0.01'f32 == 0.09'f64 + 0.01'f64` that are evaluating during +floating pointer values during semantic analysis; this means expressions like +``0.09'f32 + 0.01'f32 == 0.09'f64 + 0.01'f64`` that are evaluating during constant folding are true. Boolean type ------------ The boolean type is named `bool`:idx: in Nim and can be one of the two -pre-defined values `true` and `false`. Conditions in `while`, -`if`, `elif`, `when`-statements need to be of type `bool`. +pre-defined values ``true`` and ``false``. Conditions in ``while``, +``if``, ``elif``, ``when``-statements need to be of type ``bool``. -This condition holds: +This condition holds:: - ```nim ord(false) == 0 and ord(true) == 1 - ``` -The operators `not, and, or, xor, <, <=, >, >=, !=, ==` are defined -for the bool type. The `and` and `or` operators perform short-cut +The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined +for the bool type. The ``and`` and ``or`` operators perform short-cut evaluation. Example: - ```nim +.. code-block:: 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. @@ -1241,11 +1115,15 @@ 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. - -The `Rune` type is used for Unicode characters, it can represent any Unicode -character. `Rune` is declared in the [unicode module](unicode.html). +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. +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. +Another reason is that Nim can support ``array[char, int]`` or +``set[char]`` efficiently as many algorithms rely on this feature. The +`Rune` type is used for Unicode characters, it can represent any Unicode +character. ``Rune`` is declared in the `unicode module `_. @@ -1255,16 +1133,15 @@ Enumeration types Enumeration types define a new type whose values consist of the ones specified. The values are ordered. Example: - ```nim +.. code-block:: nim + type Direction = enum north, east, south, west - ``` -Now the following holds: +Now the following holds:: - ```nim ord(north) == 0 ord(east) == 1 ord(south) == 2 @@ -1272,11 +1149,10 @@ Now the following holds: # Also allowed: 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 -be qualified with the enum type that it resides in, `Direction.north`. +Thus, north < east < south < west. The comparison operators can be used +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 types can be assigned an explicit ordinal value. However, the ordinal values @@ -1285,41 +1161,41 @@ explicitly given is assigned the value of the previous field + 1. An explicit ordered enum can have *holes*: - ```nim +.. code-block:: 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` -and `pred` are not available for them either. +However, it is then not ordinal anymore, so it is not possible to use these +enums as an index type for arrays. The procedures ``inc``, ``dec``, ``succ`` +and ``pred`` are not available for them either. -The compiler supports the built-in stringify operator `$` for enumerations. +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: - ```nim +.. code-block:: 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 possible to only specify one of them. -An enum can be marked with the `pure` pragma so that its fields are +An enum can be marked with the ``pure`` pragma so that its fields are added to a special module-specific hidden scope that is only queried 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`: +as ``MyEnum.value``: + +.. code-block:: nim - ```nim type MyEnum {.pure.} = enum valueA, valueB, valueC, valueD, amb @@ -1331,86 +1207,35 @@ as `MyEnum.value`: echo valueA # MyEnum.valueA echo amb # Error: Unclear whether it's MyEnum.amb or OtherEnum.amb echo MyEnum.amb # OK. - ``` - -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 - ``` - -In some cases, ambiguity of enums is resolved depending on the relation -between the current scope and the scope the enums were defined in. - - ```nim - # a.nim - type Foo* = enum abc - - # b.nim - import a - type Bar = enum abc - echo abc is Bar # true - - block: - type Baz = enum abc - echo abc is Baz # true - ``` - -To implement bit fields with enums see [Bit fields]. +To implement bit fields with enums see `Bit fields <#set-type-bit-fields>`_ String type ----------- -All string literals are of the type `string`. A string in Nim is very +All string literals are of the type ``string``. A string in Nim is very similar to a sequence of characters. However, strings in Nim are both zero-terminated and have a length field. One can retrieve the length with the -builtin `len` procedure; the length never counts the terminating zero. +builtin ``len`` procedure; the length never counts the terminating zero. The terminating zero cannot be accessed unless the string is converted -to the `cstring` type first. The terminating zero assures that this +to the ``cstring`` type first. The terminating zero assures that this conversion can be done in O(1) and without any allocations. The assignment operator for strings always copies the string. -The `&` operator concatenates strings. +The ``&`` operator concatenates strings. -Most native Nim types support conversion to strings with the special `$` proc. -When calling the `echo` proc, for example, the built-in stringify operation +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: - ```nim +.. code-block:: nim + echo 3 # calls `$` for `int` - ``` Whenever a user creates a specialized object, implementation of this procedure -provides for `string` representation. +provides for ``string`` representation. - ```nim +.. code-block:: nim type Person = object name: string @@ -1422,84 +1247,78 @@ 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 -a `string` like we can for the `echo` proc. +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 +a ``string`` like we can for the ``echo`` proc. 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: - ```nim +.. code-block:: 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](unicode.html) -can be used for iteration over all Unicode characters. +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. cstring type ------------ -The `cstring` type meaning `compatible string` is the native representation -of a string for the compilation backend. For the C backend the `cstring` type +The ``cstring`` type meaning `compatible string` is the native representation +of a string for the compilation backend. For the C backend the ``cstring`` type represents a pointer to a zero-terminated char array -compatible with the type `char*` in ANSI C. Its primary purpose lies in easy -interfacing with C. The index operation `s[i]` means the i-th *char* of -`s`; however no bounds checking for `cstring` is performed making the +compatible with the type ``char*`` in Ansi C. Its primary purpose lies in easy +interfacing with C. The index operation ``s[i]`` means the i-th *char* of +``s``; however no bounds checking for ``cstring`` is performed making the index operation unsafe. -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: +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: - ```nim +.. code-block:: 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 -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. +does not consider a ``cstring`` to be a root and may collect the underlying +memory. However, in practice, this almost never happens as the GC considers +stack roots conservatively. One can use the builtin procs ``GC_ref`` and +``GC_unref`` to keep the string data alive for the rare cases where it does +not work. -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: - ```nim +.. code-block:: nim var str: string = "Hello!" var cstr: cstring = str var newstr: string = $cstr - ``` -`cstring` literals shouldn't be modified. +``cstring`` literals shouldn't be modified. - ```nim +.. code-block:: nim var x = cstring"literals" x[1] = 'A' # This is wrong!!! - ``` -If the `cstring` originates from a regular memory (not read-only memory), +If the ``cstring`` originates from a regular memory (not read-only memory), it can be modified: - ```nim +.. code-block:: 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 ---------------- @@ -1512,9 +1331,9 @@ Array and sequence types Arrays are a homogeneous type, meaning that each element in the array has the same type. Arrays always have a fixed length specified as a constant expression (except for open arrays). They can be indexed by any ordinal type. -A parameter `A` may be an *open array*, in which case it is indexed by -integers from 0 to `len(A)-1`. An array expression may be constructed by the -array constructor `[]`. The element type of this array expression is +A parameter ``A`` may be an *open array*, in which case it is indexed by +integers from 0 to ``len(A)-1``. An array expression may be constructed by the +array constructor ``[]``. The element type of this array expression is inferred from the type of the first element. All other elements need to be implicitly convertible to this type. @@ -1523,17 +1342,18 @@ An array type can be defined using the `array[size, T]` syntax, or using Sequences are similar to arrays but of dynamic length which may change during runtime (like strings). Sequences are implemented as growable arrays, -allocating pieces of memory as items are added. A sequence `S` is always -indexed by integers from 0 to `len(S)-1` and its bounds are checked. -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. +allocating pieces of memory as items are added. A sequence ``S`` is always +indexed by integers from 0 to ``len(S)-1`` and its bounds are checked. +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 may be passed to a parameter that is of type *open array*. Example: - ```nim +.. code-block:: nim + type IntArray = array[0..5, int] # an array that is indexed with 0..5 IntSeq = seq[int] # a sequence of integers @@ -1544,25 +1364,25 @@ 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 -received by `len()`. `low()` for a sequence or an open array always returns +``low()``, the higher bound by ``high()``. The length may be +received by ``len()``. ``low()`` for a sequence or an open array always returns 0, as this is the first valid index. -One can append elements to a sequence with the `add()` proc or the `&` +One can append elements to a sequence with the ``add()`` proc or the ``&`` operator, and remove (and get) the last element of a sequence with the -`pop()` proc. +``pop()`` proc. -The notation `x[i]` can be used to access the i-th element of `x`. +The notation ``x[i]`` can be used to access the i-th element of ``x``. Arrays are always bounds checked (statically or at runtime). These checks can be disabled via pragmas or invoking the compiler with the -`--boundChecks:off`:option: command-line switch. +``--boundChecks:off`` command-line switch. An array constructor can have explicit indexes for readability: - ```nim +.. code-block:: nim + type Values = enum valA, valB, valC @@ -1573,12 +1393,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 +If an index is left out, ``succ(lastIndex)`` is used as the index value: - ```nim +.. code-block:: nim + type Values = enum valA, valB, valC, valD, valE @@ -1590,7 +1410,6 @@ value: valC: "C", "D", "e" ] - ``` @@ -1599,31 +1418,30 @@ 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. 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 open array parameter, the index -type does not matter. In addition to arrays, sequences can also be passed +allows this; it can only be used for parameters. Openarrays 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 +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 open arrays are not +The openarray type cannot be nested: multidimensional openarrays are not supported because this is seldom needed and cannot be done efficiently. - ```nim +.. code-block:: 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 open array parameter that additionally -allows a variable number of arguments to be passed to a procedure. The compiler +A ``varargs`` parameter is an openarray parameter that additionally +allows to pass a variable number of arguments to a procedure. The compiler converts the list of arguments to an array implicitly: - ```nim +.. code-block:: nim proc myWriteln(f: File, a: varargs[string]) = for s in items(a): write(f, s) @@ -1632,13 +1450,12 @@ 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: - ```nim +.. code-block:: nim proc myWriteln(f: File, a: varargs[string, `$`]) = for s in items(a): write(f, s) @@ -1646,57 +1463,52 @@ type conversions in this context: myWriteln(stdout, 123, "abc", 4.0) # is transformed to: - myWriteln(stdout, [$123, $"abc", $4.0]) - ``` + myWriteln(stdout, [$123, $"def", $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.) +In this example ``$`` is applied to any argument that is passed to the +parameter ``a``. (Note that ``$`` applied to strings is a nop.) -Note that an explicit array constructor passed to a `varargs` parameter is +Note that an explicit array constructor passed to a ``varargs`` parameter is not wrapped in another implicit array construction: - ```nim +.. code-block:: 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 +``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: +so that the builtin ``echo`` proc does what is expected: - ```nim +.. code-block:: nim proc echo*(x: varargs[typed, `$`]) {...} echo @[1, 2, 3] # prints "@[1, 2, 3]" and not "123" - ``` Unchecked arrays ---------------- -The `UncheckedArray[T]` type is a special kind of `array` where its bounds +The ``UncheckedArray[T]`` type is a special kind of ``array`` where its bounds 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: - ```nim +.. code-block:: nim type MySeq = object len, cap: int data: UncheckedArray[int] - ``` Produces roughly this C code: - ```C +.. code-block:: 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. @@ -1713,36 +1525,39 @@ A variable of a tuple or object type is a heterogeneous storage container. A tuple or object defines various named *fields* of a type. A tuple also defines a lexicographic *order* of the fields. Tuples are meant to be -heterogeneous storage types with few abstractions. The `()` syntax +heterogeneous storage types with few abstractions. The ``()`` syntax can be used to construct tuples. The order of the fields in the constructor 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. +order. The *names* of the fields also have to be identical. + +The assignment operator for tuples copies each component. +The default assignment operator for objects copies each component. Overloading +of the assignment operator is described `here +`_. + +.. code-block:: nim - ```nim type Person = tuple[name: string, age: int] # type representing a person: - # it consists of a name and an age. - var person: Person + # a person consists of a name + # and an age + var + person: Person person = (name: "Peter", age: 30) - assert person.name == "Peter" + echo person.name # the same, but less readable: person = ("Peter", 30) - assert person[0] == "Peter" - assert Person is (string, int) - assert (string, int) is Person - assert Person isnot tuple[other: string, age: int] # `other` is a different identifier - ``` + echo person[0] A tuple with one unnamed field can be constructed with the parentheses and a trailing comma: - ```nim +.. code-block:: nim proc echoUnaryTuple(a: (int,)) = echo a[0] echoUnaryTuple (1,) - ``` In fact, a trailing comma is allowed for every tuple construction. @@ -1750,23 +1565,22 @@ In fact, a trailing comma is allowed for every tuple construction. The implementation aligns the fields for the best access performance. The alignment 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 `[]`: +For consistency with ``object`` declarations, tuples in a ``type`` section +can also be defined with indentation instead of ``[]``: - ```nim +.. code-block:: 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 -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. +Objects provide many features that tuples do not. Object provide inheritance and +the ability to hide fields from other modules. Objects with inheritance 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. - ```nim +.. code-block:: nim type Person = object of RootObj name*: string # the * means that `name` is accessible from other modules @@ -1780,16 +1594,15 @@ 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 +Object fields that should be visible from outside the defining module, have to +be marked by ``*``. In contrast to tuples, different object types are never *equivalent*, they are nominal types whereas tuples are structural. -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`. +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``. - ```nim +.. code-block:: nim type Person = object # example of a final object name*: string @@ -1797,36 +1610,20 @@ 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][type -bound operators]. Object construction ------------------- 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: +has the syntax ``T(fieldA: valueA, fieldB: valueB, ...)`` where ``T`` is +an ``object`` type or a ``ref object`` type: - ```nim - type - Student = object - name: string - age: int - PStudent = ref Student - var a1 = Student(name: "Anton", age: 5) - var a2 = PStudent(name: "Anton", age: 5) - # this also works directly: - 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) - ``` +.. code-block:: nim + var student = Student(name: "Anton", age: 5, id: 3) Note that, unlike tuples, objects require the field names along with their values. -For a `ref object` type `system.new` is invoked implicitly. +For a ``ref object`` type ``system.new`` is invoked implicitly. Object variants @@ -1838,8 +1635,9 @@ enumerated type used for runtime type flexibility, mirroring the concepts of An example: - ```nim - # This is an example of how an abstract syntax tree could be modelled in Nim +.. code-block:: nim + + # This is an example how an abstract syntax tree could be modelled in Nim type NodeKind = enum # the different node types nkInt, # a leaf with an integer value @@ -1850,7 +1648,7 @@ An example: nkIf # an if statement Node = ref NodeObj NodeObj = object - case kind: NodeKind # the `kind` field is the discriminator + case kind: NodeKind # the ``kind`` field is the discriminator of nkInt: intVal: int of nkFloat: floatVal: float of nkString: strVal: string @@ -1861,11 +1659,11 @@ An example: # create a new case object: var n = Node(kind: nkIf, condition: nil) - # accessing n.thenPart is valid because the `nkIf` branch is active: + # accessing n.thenPart is valid because the ``nkIf`` branch is active: n.thenPart = Node(kind: nkFloat, floatVal: 2.0) # the following statement raises an `FieldDefect` exception, because - # n.kind's value does not fit and the `nkString` branch is not active: + # n.kind's value does not fit and the ``nkString`` branch is not active: n.strVal = "" # invalid: would change the active object branch: @@ -1875,17 +1673,16 @@ 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 object fields raises an exception. -The syntax of `case` in an object declaration follows closely the syntax of -the `case` statement: The branches in a `case` section may be indented too. +The syntax of ``case`` in an object declaration follows closely the syntax of +the ``case`` statement: The branches in a ``case`` section may be indented too. -In the example, the `kind` field is called the `discriminator`:idx:\: For -safety, its address cannot be taken and assignments to it are restricted: The +In the example, the ``kind`` field is called the `discriminator`:idx:\: For +safety its address cannot be taken and assignments to it are restricted: The new value must not lead to a change of the active object branch. Also, when the fields of a particular branch are specified during object construction, the corresponding discriminator value must be specified as a constant expression. @@ -1893,29 +1690,30 @@ 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: - ```nim +.. code-block:: 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 +Starting with version 0.20 ``system.reset`` cannot be used anymore to support object branch changes as this never was completely memory safe. -As a special rule, the discriminator kind can also be bounded using a `case` +As a special rule, the discriminator kind can also be bounded using a ``case`` statement. If possible values of the discriminator variable in a -`case` statement branch are a subset of discriminator values for the selected +``case`` statement branch are a subset of discriminator values for the selected object branch, the initialization is considered valid. This analysis only works -for immutable discriminators of an ordinal type and disregards `elif` -branches. For discriminator values with a `range` type, the compiler +for immutable discriminators of an ordinal type and disregards ``elif`` +branches. For discriminator values with a ``range`` type, the compiler checks if the entire range of possible values for the discriminator value is valid for the chosen object branch. A small example: - ```nim +.. code-block:: nim + let unknownKind = nkSub # invalid: unsafe initialization because the kind field is not statically known: @@ -1932,92 +1730,6 @@ 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 --------------------- - -Some restrictions for case objects can be disabled via a `{.cast(uncheckedAssign).}` section: - - ```nim test="nim c $1" - type - TokenKind* = enum - strLit, intLit - Token = object - case kind*: TokenKind - of strLit: - s*: string - of intLit: - i*: int64 - - proc passToVar(x: var TokenKind) = discard - - var t = Token(kind: strLit, s: "abc") - - {.cast(uncheckedAssign).}: - # inside the 'cast' section it is allowed to pass 't.kind' to a 'var T' parameter: - passToVar(t.kind) - - # inside the 'cast' section it is allowed to set field 's' even though the - # constructed 'kind' field has an unknown value: - t = Token(kind: t.kind, s: "abc") - - # inside the 'cast' section it is allowed to assign to the 't.kind' field directly: - t.kind = intLit - ``` - -Default values for object fields --------------------------------- - -Object fields are allowed to have a constant default value. The type of field can be omitted if a default value is given. - -```nim test -type - Foo = object - a: int = 2 - b: float = 3.14 - c = "I can have a default value" - - Bar = ref object - a: int = 2 - b: float = 3.14 - c = "I can have a default value" -``` - -The explicit initialization uses these defaults which includes an `object` created with an object construction expression or the procedure `default`; a `ref object` created with an object construction expression or the procedure `new`; an array or a tuple with a subtype which has a default created with the procedure `default`. - - -```nim test -type - Foo = object - a: int = 2 - b = 3.0 - Bar = ref object - a: int = 2 - b = 3.0 - -block: # created with an object construction expression - let x = Foo() - assert x.a == 2 and x.b == 3.0 - - let y = Bar() - assert y.a == 2 and y.b == 3.0 - -block: # created with an object construction expression - let x = default(Foo) - assert x.a == 2 and x.b == 3.0 - - let y = default(array[1, Foo]) - assert y[0].a == 2 and y[0].b == 3.0 - - let z = default(tuple[x: Foo]) - assert z.x.a == 2 and z.x.b == 3.0 - -block: # created with the procedure `new` - let y = new Bar - assert y.a == 2 and y.b == 3.0 -``` Set type -------- @@ -2033,24 +1745,25 @@ 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. Traced references are declared with the **ref** keyword, untraced references -are declared with the **ptr** keyword. In general, a `ptr T` is implicitly +are declared with the **ptr** keyword. In general, a `ptr T` is implicitly 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 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 +The ``.`` (access a tuple/object field operator) +and ``[]`` (array/string/sequence index operator) operators perform implicit dereferencing operations for reference types: - ```nim +.. code-block:: nim + type Node = ref NodeObj NodeObj = object @@ -2062,77 +1775,79 @@ 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 +`_. In order to simplify structural type checking, recursive tuples are not valid: - ```nim +.. code-block:: nim # invalid recursion type MyTuple = tuple[a: ref MyTuple] - ``` -Likewise `T = ref T` is an invalid type. +Likewise ``T = ref T`` is an invalid type. -As a syntactical extension, `object` types can be anonymous if -declared in a type section via the `ref object` or `ptr object` notations. +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: - ```nim +.. code-block:: 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](system.html) module -contains further information. +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 contains +further information. Nil --- -If a reference points to *nothing*, it has the value `nil`. `nil` is the -default value for all `ref` and `ptr` types. The `nil` value can also be +If a reference points to *nothing*, it has the value ``nil``. ``nil`` is the +default value for all ``ref`` and ``ptr`` types. The ``nil`` value can also be used like any other literal value. For example, it can be used in an assignment -like `myRef = nil`. +like ``myRef = nil``. -Dereferencing `nil` is an unrecoverable fatal runtime error (and not a panic). +Dereferencing ``nil`` is an unrecoverable fatal runtime error (and not a panic). -A successful dereferencing operation `p[]` implies that `p` is not nil. This +A successful dereferencing operation ``p[]`` implies that ``p`` is not nil. This can be exploited by the implementation to optimize code like: - ```nim +.. code-block:: 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. + # if p were nil, ``p[]`` would have caused a crash already, + # so we know ``p`` is always not nil here. action() - ``` Into: - ```nim +.. code-block:: nim + p[].field = 3 action() - ``` *Note*: This is not comparable to C's "undefined behavior" for dereferencing NULL pointers. -Mixing GC'ed memory with `ptr` +Mixing GC'ed memory with ``ptr`` -------------------------------- Special care has to be taken if an untraced object contains traced objects like traced references, strings, or sequences: in order to free everything properly, -the built-in procedure `reset` has to be called before freeing the untraced +the built-in procedure ``reset`` has to be called before freeing the untraced memory manually: - ```nim +.. code-block:: nim type Data = tuple[x, y: int, s: string] @@ -2147,19 +1862,17 @@ 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 -low-level programming: the `sizeof` proc returns the size of a type or value -in bytes. The `cast` operator can circumvent the type system: the compiler -is forced to treat the result of the `alloc0` call (which returns an untyped -pointer) as if it would have the type `ptr Data`. Casting should only be +Without the ``reset`` call the memory allocated for the ``d.s`` string would +never be freed. The example also demonstrates two important features for low-level programming: the ``sizeof`` proc returns the size of a type or value +in bytes. The ``cast`` operator can circumvent the type system: the compiler +is forced to treat the result of the ``alloc0`` call (which returns an untyped +pointer) as if it would have the type ``ptr Data``. Casting should only be done if it is unavoidable: it breaks type safety and bugs can lead to mysterious crashes. **Note**: The example only works because the memory is initialized to zero -(`alloc0` instead of `alloc` does this): `d.s` is thus initialized to +(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to binary zero which the string assignment can handle. One needs to know low-level details like this when mixing garbage-collected data with unmanaged memory. @@ -2168,22 +1881,24 @@ details like this when mixing garbage-collected data with unmanaged memory. Procedural type --------------- -A procedural type is internally a pointer to a procedure. `nil` is -an allowed value for a variable of a procedural type. +A procedural type is internally a pointer to a procedure. ``nil`` is +an allowed value for variables of a procedural type. Nim uses procedural +types to achieve `functional`:idx: programming techniques. Examples: - ```nim +.. code-block:: nim + proc printItem(x: int) = ... proc forEach(c: proc (x: int) {.cdecl.}) = ... forEach(printItem) # this will NOT compile because calling conventions differ - ``` - ```nim +.. code-block:: nim + type OnMouseMove = proc (x, y: int) {.closure.} @@ -2196,90 +1911,90 @@ 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 procedure influences the type compatibility: procedural types are only compatible if they have the same calling convention. As a special extension, -a procedure of the calling convention `nimcall` can be passed to a parameter -that expects a proc of the calling convention `closure`. +a procedure of the calling convention ``nimcall`` can be passed to a parameter +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 - same as `fastcall`, but only for C compilers that support `fastcall`. + 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 + 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 - procedure is declared with the `__stdcall` keyword. + 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. + the ``__cdecl`` keyword. `safecall`:idx: -: This is the safecall convention as specified by Microsoft. The generated C - procedure is declared with the `__safecall` keyword. The word *safe* + 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 - it may inline procedures that are not marked as `inline`. + this to the C compiler; it generates ``__inline`` procedures. This is + 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 - the C `__fastcall` means. + 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 - C++ class member functions on the x86 architecture. + This is 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`` 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 + Nim's default calling convention for procedures is ``fastcall`` to improve speed. Most calling conventions exist only for the Windows 32-bit platform. -The default calling convention is `nimcall`, unless it is an inner proc (a +The default calling convention is ``nimcall``, unless it is an inner proc (a proc inside of a proc). For an inner proc an analysis is performed whether it accesses its environment. If it does so, it has the calling convention -`closure`, otherwise it has the calling convention `nimcall`. +``closure``, otherwise it has the calling convention ``nimcall``. Distinct type ------------- -A `distinct` type is a new type derived from a `base type`:idx: that is +A ``distinct`` type is a new type derived from a `base type`:idx: that is incompatible with its base type. In particular, it is an essential property of a distinct type that it **does not** imply a subtype relation between it and its base type. Explicit type conversions from a distinct type to its -base type and vice versa are allowed. See also `distinctBase` to get the +base type and vice versa are allowed. See also ``distinctBase`` to get the 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. @@ -2287,7 +2002,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: - ```nim +.. code-block:: nim type Dollar = distinct int Euro = distinct int @@ -2297,22 +2012,20 @@ types are a perfect tool to model different currencies: e: Euro echo d + 12 - # Error: cannot add a number with no unit and a `Dollar` - ``` + # 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: +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: - ```nim +.. code-block:: 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: - ```nim +.. code-block:: proc `*` (x: Dollar, y: int): Dollar = result = Dollar(int(x) * y) @@ -2320,28 +2033,28 @@ 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 -`+` for dollars should produce the same binary code as `+` for ints. +``+`` for dollars should produce the same binary code as ``+`` for ints. The pragma `borrow`:idx: has been designed to solve this problem; in principle, it generates the above trivial implementations: - ```nim +.. code-block:: 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 ``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]. +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" - ```nim test = "nim c $1" template additive(typ: typedesc) = proc `+` *(x, y: typ): typ {.borrow.} proc `-` *(x, y: typ): typ {.borrow.} @@ -2370,13 +2083,12 @@ 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: - ```nim +.. code-block:: nim type Foo = object a, b: int @@ -2389,19 +2101,19 @@ 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:\: - ```nim - import std/strutils +.. code-block:: nim + import strutils proc query(db: DbHandle, statement: string) = ... @@ -2410,13 +2122,12 @@ 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`: +``SQL`` that is incompatible with ``string``: - ```nim +.. code-block:: nim type SQL = distinct string @@ -2427,15 +2138,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: +conversions from ``string`` to ``SQL`` are allowed: - ```nim - import std/[strutils, sequtils] +.. code-block:: nim + import strutils, sequtils proc properQuote(s: string): SQL = # quotes a string properly for an SQL statement @@ -2443,47 +2153,43 @@ conversions from `string` to `SQL` are allowed: proc `%` (frmt: SQL, values: openarray[string]): SQL = # quote each argument: - let v = values.mapIt(properQuote(it)) + let v = values.mapIt(SQL, properQuote(it)) # we need a temporary type for the type conversion :-( type StrSeq = seq[string] # call strutils.`%`: 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](db_common.html#SqlQuery) of -modules like [db_sqlite](db_sqlite.html). +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 `_. Auto type --------- -The `auto` type can only be used for return types and parameters. For return +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: - ```nim +.. code-block:: nim proc returnsInt(): auto = 1984 - ``` For parameters it currently creates implicitly generic routines: - ```nim +.. code-block:: nim proc foo(a, b: auto) = discard - ``` Is the same as: - ```nim +.. code-block:: 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 -the parameters' types can not be inferred from an empty `discard` statement. +parameters' types from the body". Then the above ``foo`` would be rejected as +the parameters' types can not be inferred from an empty ``discard`` statement. Type relations @@ -2495,36 +2201,121 @@ describe the type checking done by the compiler. Type equality ------------- - Nim uses structural type equivalence for most types. Only for objects, -enumerations and distinct types and for generic types name equivalence is used. +enumerations and distinct types name equivalence is used. The following +algorithm, *in pseudo-code*, determines type equality: + +.. code-block:: nim + proc typeEqualsAux(a, b: PType, + s: var HashSet[(PType, PType)]): bool = + if (a,b) in s: return true + incl(s, (a,b)) + if a.kind == b.kind: + case a.kind + of int, intXX, float, floatXX, char, string, cstring, pointer, + bool, nil, void: + # leaf type: kinds identical; nothing more to check + result = true + of ref, ptr, var, set, seq, openarray: + result = typeEqualsAux(a.baseType, b.baseType, s) + of range: + result = typeEqualsAux(a.baseType, b.baseType, s) and + (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB) + of array: + result = typeEqualsAux(a.baseType, b.baseType, s) and + typeEqualsAux(a.indexType, b.indexType, s) + of tuple: + if a.tupleLen == b.tupleLen: + for i in 0..a.tupleLen-1: + if not typeEqualsAux(a[i], b[i], s): return false + result = true + of object, enum, distinct: + result = a == b + of proc: + result = typeEqualsAux(a.parameterTuple, b.parameterTuple, s) and + typeEqualsAux(a.resultType, b.resultType, s) and + a.callingConvention == b.callingConvention + + proc typeEquals(a, b: PType): bool = + var s: HashSet[(PType, PType)] = {} + result = typeEqualsAux(a, b, s) + +Since types are graphs which can have cycles, the above algorithm needs an +auxiliary set ``s`` to detect this case. + + +Type equality modulo type distinction +------------------------------------- + +The following algorithm (in pseudo-code) determines whether two types +are equal with no respect to ``distinct`` types. For brevity the cycle check +with an auxiliary set ``s`` is omitted: + +.. code-block:: nim + proc typeEqualsOrDistinct(a, b: PType): bool = + if a.kind == b.kind: + case a.kind + of int, intXX, float, floatXX, char, string, cstring, pointer, + bool, nil, void: + # leaf type: kinds identical; nothing more to check + result = true + of ref, ptr, var, set, seq, openarray: + result = typeEqualsOrDistinct(a.baseType, b.baseType) + of range: + result = typeEqualsOrDistinct(a.baseType, b.baseType) and + (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB) + of array: + result = typeEqualsOrDistinct(a.baseType, b.baseType) and + typeEqualsOrDistinct(a.indexType, b.indexType) + of tuple: + if a.tupleLen == b.tupleLen: + for i in 0..a.tupleLen-1: + if not typeEqualsOrDistinct(a[i], b[i]): return false + result = true + of distinct: + result = typeEqualsOrDistinct(a.baseType, b.baseType) + of object, enum: + result = a == b + of proc: + result = typeEqualsOrDistinct(a.parameterTuple, b.parameterTuple) and + typeEqualsOrDistinct(a.resultType, b.resultType) and + a.callingConvention == b.callingConvention + elif a.kind == distinct: + result = typeEqualsOrDistinct(a.baseType, b) + elif b.kind == distinct: + result = typeEqualsOrDistinct(a, b.baseType) Subtype relation ---------------- +If object ``a`` inherits from ``b``, ``a`` is a subtype of ``b``. This subtype +relation is extended to the types ``var``, ``ref``, ``ptr``: -If object `a` inherits from `b`, `a` is a subtype of `b`. +.. code-block:: nim + proc isSubtype(a, b: PType): bool = + if a.kind == b.kind: + case a.kind + of object: + var aa = a.baseType + while aa != nil and aa != b: aa = aa.baseType + result = aa == b + of var, ref, ptr: + result = isSubtype(a.baseType, b.baseType) -This subtype relation is extended to the types `var`, `ref`, `ptr`. -If `A` is a subtype of `B` and `A` and `B` are `object` types then: +.. XXX nil is a special value! -- `var A` is a subtype of `var B` -- `ref A` is a subtype of `ref B` -- `ptr A` is a subtype of `ptr B`. -**Note**: One of the above pointer-indirections is required for assignment from -a subtype to its parent type to prevent "object slicing". Convertible relation -------------------- - -A type `a` is **implicitly** convertible to type `b` iff the following +A type ``a`` is **implicitly** convertible to type ``b`` iff the following algorithm returns true: - ```nim +.. code-block:: nim + proc isImplicitlyConvertible(a, b: PType): bool = - if isSubtype(a, b): + if isSubtype(a, b) or isCovariant(a, b): return true if isIntLiteral(a): return b in {int8, int16, int32, int64, int, uint, uint8, uint16, @@ -2550,46 +2341,37 @@ algorithm returns true: result = b == pointer of string: 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". -`compatibleParametersAndEffects(a, b)` is currently not specified. -Implicit conversions are also performed for Nim's `range` type +Implicit conversions are also performed for Nim's ``range`` type constructor. -Let `a0`, `b0` of type `T`. +Let ``a0``, ``b0`` of type ``T``. -Let `A = range[a0..b0]` be the argument's type, `F` the formal -parameter's type. Then an implicit conversion from `A` to `F` -exists if `a0 >= low(F) and b0 <= high(F)` and both `T` and `F` +Let ``A = range[a0..b0]`` be the argument's type, ``F`` the formal +parameter's type. Then an implicit conversion from ``A`` to ``F`` +exists if ``a0 >= low(F) and b0 <= high(F)`` and both ``T`` and ``F`` are signed integers or if both are unsigned integers. -A type `a` is **explicitly** convertible to type `b` iff the following +A type ``a`` is **explicitly** convertible to type ``b`` iff the following algorithm returns true: - ```nim +.. code-block:: nim proc isIntegralType(t: PType): bool = result = isOrdinal(t) or t.kind in {float, float32, float64} proc isExplicitlyConvertible(a, b: PType): bool = result = false if isImplicitlyConvertible(a, b): return true - if typeEquals(a, b): return true - if a == distinct and typeEquals(a.baseType, b): return true - if b == distinct and typeEquals(b.baseType, a): return true + if typeEqualsOrDistinct(a, b): 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:. - ```nim +.. code-block:: nim converter toInt(x: char): int = result = ord(x) var @@ -2602,55 +2384,53 @@ 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. +The type conversion ``T(a)`` is an L-value if ``a`` is an L-value and +``typeEqualsOrDistinct(T, typeof(a))`` holds. Assignment compatibility ------------------------ -An expression `b` can be assigned to an expression `a` iff `a` is an -`l-value` and `isImplicitlyConvertible(b.typ, a.typ)` holds. +An expression ``b`` can be assigned to an expression ``a`` iff ``a`` is an +`l-value` and ``isImplicitlyConvertible(b.typ, a.typ)`` holds. -Overload resolution -=================== +Overloading resolution +====================== -In a call `p(args)` the routine `p` that matches best is selected. If +In a call ``p(args)`` the routine ``p`` that matches best is selected. If multiple routines match equally well, the ambiguity is reported during semantic analysis. Every arg in args needs to match. There are multiple different categories how an -argument can match. Let `f` be the formal parameter's type and `a` the type +argument can match. Let ``f`` be the formal parameter's type and ``a`` the type of the argument. -1. Exact match: `a` and `f` are of the same type. -2. Literal match: `a` is an integer literal of value `v` - and `f` is a signed or unsigned integer type and `v` is in `f`'s - range. Or: `a` is a floating-point literal of value `v` - and `f` is a floating-point type and `v` is in `f`'s +1. Exact match: ``a`` and ``f`` are of the same type. +2. Literal match: ``a`` is an integer literal of value ``v`` + and ``f`` is a signed or unsigned integer type and ``v`` is in ``f``'s + range. Or: ``a`` is a floating-point literal of value ``v`` + and ``f`` is a floating-point type and ``v`` is in ``f``'s range. -3. Generic match: `f` is a generic type and `a` matches, for - instance `a` is `int` and `f` is a generic (constrained) parameter - type (like in `[T]` or `[T: int|char]`). -4. Subrange or subtype match: `a` is a `range[T]` and `T` - matches `f` exactly. Or: `a` is a subtype of `f`. -5. Integral conversion match: `a` is convertible to `f` and `f` and `a` +3. Generic match: ``f`` is a generic type and ``a`` matches, for + instance ``a`` is ``int`` and ``f`` is a generic (constrained) parameter + type (like in ``[T]`` or ``[T: int|char]``. +4. Subrange or subtype match: ``a`` is a ``range[T]`` and ``T`` + matches ``f`` exactly. Or: ``a`` is a subtype of ``f``. +5. Integral conversion match: ``a`` is convertible to ``f`` and ``f`` and ``a`` is some integer or floating-point type. -6. Conversion match: `a` is convertible to `f`, possibly via a user - defined `converter`. +6. Conversion match: ``a`` is convertible to ``f``, possibly via a user + defined ``converter``. These matching categories have a priority: An exact match is better than a -literal match and that is better than a generic match etc. In the following, -`count(p, m)` counts the number of matches of the matching category `m` -for the routine `p`. +literal match and that is better than a generic match etc. In the following +``count(p, m)`` counts the number of matches of the matching category ``m`` +for the routine ``p``. -A routine `p` matches better than a routine `q` if the following -algorithm returns true: +A routine ``p`` matches better than a routine ``q`` if the following +algorithm returns true:: - ```nim for each matching category m in ["exact match", "literal match", "generic match", "subtype match", "integral match", "conversion match"]: @@ -2660,12 +2440,11 @@ algorithm returns true: else: return false return "ambiguous" - ``` Some examples: - ```nim +.. code-block:: nim proc takesInt(x: int) = echo "int" proc takesInt[T](x: T) = echo "T" proc takesInt(x: int16) = echo "int16" @@ -2677,15 +2456,14 @@ Some examples: takesInt(y) # "int16" var z: range[0..4] = 0 takesInt(z) # "T" - ``` If this algorithm returns "ambiguous" further disambiguation is performed: -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 +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: - ```nim +.. code-block:: nim type A = object of RootObj B = object of A @@ -2707,31 +2485,29 @@ into account: # but this is ambiguous: pp(c, c) - ``` -Likewise, for generic matches, the most specialized generic type (that still +Likewise for generic matches the most specialized generic type (that still matches) is preferred: - ```nim +.. code-block:: 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' -------------------------------------- -If the formal parameter `f` is of type `var T` +If the formal parameter ``f`` is of type ``var T`` 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. +``var T`` matches better than just ``T`` then. - ```nim +.. code-block:: nim proc sayHi(x: int): string = # matches a non-var int result = $x @@ -2746,7 +2522,6 @@ the argument is checked to be an `l-value`:idx:. sayHello(3) # 3 # 13 - ``` Lazy type resolution for untyped @@ -2755,100 +2530,37 @@ Lazy type resolution for untyped **Note**: An `unresolved`:idx: expression is an expression for which no symbol lookups and no type checking have been performed. -Since templates and macros that are not declared as `immediate` participate +Since templates and macros that are not declared as ``immediate`` participate 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` +expressions to a template or macro. This is what the meta-type ``untyped`` accomplishes: - ```nim +.. code-block:: nim template rem(x: untyped) = discard rem unresolvedExpression(undeclaredIdentifier) - ``` -A parameter of type `untyped` always matches any argument (as long as there is +A parameter of type ``untyped`` always matches any argument (as long as there is any argument passed to it). But one has to watch out because other overloads might trigger the argument's resolution: - ```nim +.. code-block:: 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. +``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other +metatypes ``typed`` and ``typedesc`` are not lazy. Varargs matching ---------------- -See [Varargs]. - - -iterable --------- - -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). - - ```nim - iterator iota(n: int): int = - for i in 0..`_. Statements and expressions @@ -2861,7 +2573,7 @@ statements. Statements are separated into `simple statements`:idx: and `complex statements`:idx:. Simple statements are statements that cannot contain other statements like -assignments, calls, or the `return` statement; complex statements can +assignments, calls, or the ``return`` statement; complex statements can contain other statements. To avoid the `dangling else problem`:idx:, complex statements always have to be indented. The details can be found in the grammar. @@ -2870,11 +2582,11 @@ Statement list expression ------------------------- Statements can also occur in an expression context that looks -like `(stmt1; stmt2; ...; ex)`. This is called -a statement list expression or `(;)`. The type -of `(stmt1; stmt2; ...; ex)` is the type of `ex`. All the other statements -must be of type `void`. (One can use `discard` to produce a `void` type.) -`(;)` does not introduce a new scope. +like ``(stmt1; stmt2; ...; ex)``. This is called +a statement list expression or ``(;)``. The type +of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements +must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.) +``(;)`` does not introduce a new scope. Discard statement @@ -2882,14 +2594,13 @@ Discard statement Example: - ```nim +.. code-block:: nim proc p(x, y: int): int = result = x + y discard p(3, 4) # discard the return value of `p` - ``` -The `discard` statement evaluates its expression for side-effects and +The ``discard`` statement evaluates its expression for side-effects and throws the expression's resulting value away, and should only be used when ignoring this value is known not to cause problems. @@ -2899,54 +2610,49 @@ a static error. The return value can be ignored implicitly if the called proc/iterator has been declared with the `discardable`:idx: pragma: - ```nim +.. code-block:: nim proc p(x, y: int): int {.discardable.} = result = x + y p(3, 4) # now valid - ``` however the discardable pragma does not work on templates as templates substitute the AST in place. For example: - ```nim +.. code-block:: nim {.push discardable .} template example(): string = "https://nim-lang.org" {.pop.} example() - ``` This template will resolve into "https://nim-lang.org" which is a string literal and since {.discardable.} doesn't apply to literals, the compiler will error. -An empty `discard` statement is often used as a null statement: +An empty ``discard`` statement is often used as a null statement: - ```nim +.. code-block:: nim proc classify(s: string) = case s[0] of SymChars, '_': echo "an identifier" of '0'..'9': echo "a number" else: discard - ``` Void context ------------ -In a list of statements, every expression except the last one needs to have the -type `void`. In addition to this rule an assignment to the builtin `result` -symbol also triggers a mandatory `void` context for the subsequent expressions: +In a list of statements every expression except the last one needs to have the +type ``void``. In addition to this rule an assignment to the builtin ``result`` +symbol also triggers a mandatory ``void`` context for the subsequent expressions: - ```nim +.. code-block:: nim proc invalid*(): string = result = "foo" "invalid" # Error: value of type 'string' has to be discarded - ``` - ```nim +.. code-block:: nim proc valid*(): string = let x = 317 "valid" - ``` Var statement @@ -2956,13 +2662,13 @@ Var statements declare new local and global variables and initialize them. A comma-separated list of variables can be used to specify variables of the same type: - ```nim +.. code-block:: nim + var a: int = 0 x, y, z: int - ``` -If an initializer is given, the type can be omitted: the variable is then of the +If an initializer is given the type can be omitted: the variable is then of the same type as the initializing expression. Variables are always initialized with a default value if there is no initializing expression. The default value depends on the type and is always a zero in binary. @@ -2976,40 +2682,38 @@ char '\\0' bool false ref or pointer type nil procedural type nil -sequence `@[]` -string `""` -`tuple[x: A, y: B, ...]` (default(A), default(B), ...) +sequence ``@[]`` +string ``""`` +tuple[x: A, y: B, ...] (default(A), default(B), ...) (analogous for objects) -`array[0..., T]` `[default(T), ...]` -`range[T]` default(T); this may be out of the valid range -T = enum `cast[T](0)`; this may be an invalid value +array[0..., T] [default(T), ...] +range[T] default(T); this may be out of the valid range +T = enum cast[T]\(0); this may be an invalid value ============================ ============================================== The implicit initialization can be avoided for optimization reasons with the `noinit`:idx: pragma: - ```nim +.. code-block:: nim var - a {.noinit.}: array[0..1023, char] - ``` + a {.noInit.}: array[0..1023, char] -If a proc is annotated with the `noinit` pragma, this refers to its implicit -`result` variable: +If a proc is annotated with the ``noinit`` pragma this refers to its implicit +``result`` variable: - ```nim +.. code-block:: nim proc returnUndefinedValue: int {.noinit.} = discard - ``` -The implicit initialization can also be prevented by the `requiresInit`:idx: +The implicit initialization can be also prevented by the `requiresInit`:idx: type pragma. The compiler requires an explicit initialization for the object and all of its fields. However, it does a `control flow analysis`:idx: to prove the variable has been initialized and does not rely on syntactic properties: - ```nim +.. code-block:: nim type - MyObject {.requiresInit.} = object + MyObject = object {.requiresInit.} proc p() = # the following is valid: @@ -3019,75 +2723,65 @@ the variable has been initialized and does not rely on syntactic properties: else: x = a() # use x - ``` -`requiresInit` pragma can also be applied to `distinct` types. +`requiresInit` pragma can also be applyied to `distinct` types. Given the following distinct type definitions: - ```nim +.. code-block:: nim type - Foo = object - x: string - - DistinctFoo {.requiresInit, borrow: `.`.} = distinct Foo + DistinctObject {.requiresInit, borrow: `.`.} = distinct MyObject DistinctString {.requiresInit.} = distinct string - ``` The following code blocks will fail to compile: - ```nim +.. code-block:: nim var foo: DistinctFoo foo.x = "test" doAssert foo.x == "test" - ``` - ```nim +.. code-block:: nim var s: DistinctString s = "test" - doAssert string(s) == "test" - ``` + doAssert s == "test" -But these will compile successfully: +But these ones will compile successfully: - ```nim +.. code-block:: nim let foo = DistinctFoo(Foo(x: "test")) doAssert foo.x == "test" - ``` - ```nim - let s = DistinctString("test") - doAssert string(s) == "test" - ``` +.. code-block:: nim + let s = "test" + doAssert s == "test" Let statement ------------- -A `let` statement declares new local and global `single assignment`:idx: -variables and binds a value to them. The syntax is the same as that of the `var` -statement, except that the keyword `var` is replaced by the keyword `let`. -Let variables are not l-values and can thus not be passed to `var` parameters +A ``let`` statement declares new local and global `single assignment`:idx: +variables and binds a value to them. The syntax is the same as that of the ``var`` +statement, except that the keyword ``var`` is replaced by the keyword ``let``. +Let variables are not l-values and can thus not be passed to ``var`` parameters nor can their address be taken. They cannot be assigned new values. For let variables, the same pragmas are available as for ordinary variables. -As `let` statements are immutable after creation they need to define a value -when they are declared. The only exception to this is if the `{.importc.}` -pragma (or any of the other `importX` pragmas) is applied, in this case the -value is expected to come from native code, typically a C/C++ `const`. +As ``let`` statements are immutable after creation they need to define a value +when they are declared. The only exception to this is if the ``{.importc.}`` +pragma (or any of the other ``importX`` pragmas) is applied, in this case the +value is expected to come from native code, typically a C/C++ ``const``. Tuple unpacking --------------- -In a `var` or `let` statement tuple unpacking can be performed. The special -identifier `_` can be used to ignore some parts of the tuple: +In a ``var`` or ``let`` statement tuple unpacking can be performed. The special +identifier ``_`` can be used to ignore some parts of the tuple: - ```nim - proc returnsTuple(): (int, int, int) = (4, 2, 3) +.. code-block:: nim + proc returnsTuple(): (int, int, int) = (4, 2, 3) - let (x, _, z) = returnsTuple() - ``` + let (x, _, z) = returnsTuple() @@ -3096,16 +2790,16 @@ Const section A const section declares constants whose values are constant expressions: - ```nim - import std/[strutils] +.. code-block:: + import strutils const roundPi = 3.1415 constEval = contains("abc", 'b') # computed at compile time! - ``` Once declared, a constant's symbol can be used as a constant expression. -See [Constants and Constant Expressions] for details. +See `Constants and Constant Expressions <#constants-and-constant-expressions>`_ +for details. Static statement/expression --------------------------- @@ -3113,27 +2807,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: - ```nim +.. code-block:: + static: echo "echo at compile time" - ``` - -`static` can also be used like a routine. - - ```nim - proc getNum(a: int): int = a - - # Below calls "echo getNum(123)" at compile time. - static: - echo getNum(123) - - # 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] for details. +see `Restrictions on Compile-Time Execution +<#restrictions-on-compileminustime-execution>`_ for details. It's a static error if the compiler cannot execute the block at compile time. @@ -3143,7 +2824,8 @@ If statement Example: - ```nim +.. code-block:: nim + var name = readLine(stdin) if name == "Andreas": @@ -3152,45 +2834,42 @@ 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 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 -conditions fail, the `else` part is executed. If there is no `else` +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 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 +conditions fail, the ``else`` part is executed. If there is no ``else`` part, execution continues with the next statement. -In `if` statements, new scopes begin immediately after -the `if`/`elif`/`else` keywords and ends after the +In ``if`` statements new scopes begin immediately after +the ``if``/``elif``/``else`` keywords and ends after the corresponding *then* block. For visualization purposes the scopes have been enclosed -in `{| |}` in the following example: +in ``{| |}`` in the following example: - ```nim +.. code-block:: 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: - ```nim - let line = readline(stdin) - case line +.. code-block:: nim + + case readline(stdin) of "delete-everything", "restart-computer": echo "permission denied" of "go-for-a-walk": echo "please yourself" - elif line.len == 0: echo "empty" # optional, must come after `of` branches - else: echo "unknown command" # ditto + else: echo "unknown command" # indentation of the branches is also allowed; and so is an optional colon # after the selecting expression: @@ -3199,39 +2878,31 @@ 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 -a multi-branch selection. The expression after the keyword `case` is +The ``case`` statement is similar to the if statement, but it represents +a multi-branch selection. The expression after the keyword ``case`` is evaluated and if its value is in a *slicelist* the corresponding statements -(after the `of` keyword) are executed. If the value is not in any -given *slicelist*, trailing `elif` and `else` parts are executed using same -semantics as for `if` statement, and `elif` is handled just like `else: if`. -If there are no `else` or `elif` parts and not -all possible values that `expr` can hold occur in a *slicelist*, a static error occurs. -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. +(after the ``of`` keyword) are executed. If the value is not in any +given *slicelist* the ``else`` part is executed. If there is no ``else`` +part and not all possible values that ``expr`` can hold occur in a +``slicelist``, a static error occurs. 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`` part with an +empty ``discard`` statement should be used. 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 -require a trailing `else` or `elif` branch; it's unspecified whether this will -keep working in future versions. +these always require an ``else`` part. Because case statements are checked for exhaustiveness during semantic analysis, -the value in every `of` branch must be a constant expression. +the value in every ``of`` branch must be a constant expression. This restriction also allows the compiler to generate more performant code. -As a special semantic extension, an expression in an `of` branch of a case +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: - ```nim +.. code-block:: nim const SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} @@ -3247,12 +2918,11 @@ 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 +The ``case`` statement doesn't produce an l-value, so the following example won't work: - ```nim +.. code-block:: nim type Foo = ref object x: seq[string] @@ -3267,18 +2937,16 @@ won't work: var foo = Foo(x: @[]) foo.get_x().add("asd") - ``` -This can be fixed by explicitly using `result` or `return`: +This can be fixed by explicitly using ``return``: - ```nim +.. code-block:: nim proc get_x(x: Foo): var seq[string] = case true of true: - result = x.x + return x.x else: - result = x.x - ``` + return x.x When statement @@ -3286,7 +2954,8 @@ When statement Example: - ```nim +.. code-block:: nim + when sizeof(int) == 2: echo "running on a 16 bit system!" elif sizeof(int) == 4: @@ -3295,32 +2964,31 @@ Example: echo "running on a 64 bit system!" else: echo "cannot happen!" - ``` -The `when` statement is almost identical to the `if` statement with some +The ``when`` statement is almost identical to the ``if`` statement with some exceptions: -* Each condition (`expr`) has to be a constant expression (of type `bool`). +* Each condition (``expr``) has to be a constant expression (of type ``bool``). * The statements do not open a new scope. * The statements that belong to the expression that evaluated to true are translated by the compiler, the other statements are not checked for semantics! However, each condition is checked for semantics. -The `when` statement enables conditional compilation techniques. As -a special syntactic extension, the `when` construct is also available -within `object` definitions. +The ``when`` statement enables conditional compilation techniques. As +a special syntactic extension, the ``when`` construct is also available +within ``object`` definitions. When nimvm statement -------------------- -`nimvm` is a special symbol that may be used as the expression of a -`when nimvm` statement to differentiate the execution path between -compile-time and the executable. +``nimvm`` is a special symbol, that may be used as an expression of ``when nimvm`` +statement to differentiate execution path between compile-time and the +executable. Example: - ```nim +.. code-block:: nim proc someProcThatMayRunInCompileTime(): bool = when nimvm: # This branch is taken at compile time. @@ -3332,16 +3000,15 @@ Example: let rtValue = someProcThatMayRunInCompileTime() assert(ctValue == true) assert(rtValue == false) - ``` -A `when nimvm` statement must meet the following requirements: +``when nimvm`` statement must meet the following requirements: -* Its expression must always be `nimvm`. More complex expressions are not +* Its expression must always be ``nimvm``. More complex expressions are not allowed. -* It must not contain `elif` branches. -* It must contain an `else` branch. +* It must not contain ``elif`` branches. +* It must contain ``else`` branch. * Code in branches must not affect semantics of the code that follows the - `when nimvm` statement. E.g. it must not define symbols that are used in + ``when nimvm`` statement. E.g. it must not define symbols that are used in the following code. Return statement @@ -3349,29 +3016,26 @@ Return statement Example: - ```nim - return 40 + 2 - ``` +.. code-block:: 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 +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: - ```nim +.. code-block:: nim result = expr return result - ``` -`return` without an expression is a short notation for `return result` if +``return`` without an expression is a short notation for ``return result`` if 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: +variables, ``result`` is initialized to (binary) zero: - ```nim +.. code-block:: nim proc returnZero(): int = # implicitly returns 0 - ``` Yield statement @@ -3379,15 +3043,14 @@ Yield statement Example: - ```nim +.. code-block:: nim yield (1, 2, 3) - ``` -The `yield` statement is used instead of the `return` statement in +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. @@ -3396,7 +3059,7 @@ Block statement Example: - ```nim +.. code-block:: nim var found = false block myblock: for i in 0..3: @@ -3405,12 +3068,11 @@ 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 -immediately. A `break` statement can contain a name of a surrounding -block to specify which block is to be left. +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 +immediately. A ``break`` statement can contain a name of a surrounding +block to specify which block is to leave. Break statement @@ -3418,12 +3080,11 @@ Break statement Example: - ```nim +.. code-block:: 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 +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 leave. If it is absent, the innermost block is left. @@ -3432,54 +3093,51 @@ While statement Example: - ```nim +.. code-block:: 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. -Endless loops are no error. `while` statements open an `implicit block` -so that they can be left with a `break` statement. +The ``while`` statement is executed until the ``expr`` evaluates to false. +Endless loops are no error. ``while`` statements open an `implicit block`, +so that they can be left with a ``break`` statement. Continue statement ------------------ -A `continue` statement leads to the immediate next iteration of the +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: - ```nim +.. code-block:: nim while expr1: stmt1 continue stmt2 - ``` Is equivalent to: - ```nim +.. code-block:: nim while expr1: block myBlockName: stmt1 break myBlockName stmt2 - ``` Assembler statement ------------------- The direct embedding of assembler code into Nim code is supported -by the unsafe `asm` statement. Identifiers in the assembler code that refer to +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 `'\`'`: +specified in the statement's pragmas. The default special character is ``'`'``: - ```nim +.. code-block:: nim {.push stackTrace:off.} proc addInt(a, b: int): int = # a in eax, and b in edx @@ -3491,11 +3149,10 @@ 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: - ```nim +.. code-block:: nim proc addInt(a, b: int): int = asm """ addl %%ecx, %%eax @@ -3505,11 +3162,10 @@ If the GNU assembler is used, quotes and newlines are inserted automatically: :"=a"(`result`) :"a"(`a`), "c"(`b`) """ - ``` Instead of: - ```nim +.. code-block:: nim proc addInt(a, b: int): int = asm """ "addl %%ecx, %%eax\n" @@ -3519,25 +3175,23 @@ Instead of: :"=a"(`result`) :"a"(`a`), "c"(`b`) """ - ``` Using statement --------------- -The `using` statement provides syntactic convenience in modules where +The using statement provides syntactic convenience in modules where the same parameter names and types are used over and over. Instead of: - ```nim +.. code-block:: 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.: +name ``c`` should default to type ``Context``, ``n`` should default to +``Node`` etc.: - ```nim +.. code-block:: nim using c: Context n: Node @@ -3551,43 +3205,41 @@ 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. +The ``using`` section uses the same indentation based grouping syntax as +a ``var`` or ``let`` section. -Note that `using` is not applied for `template` since the untyped template -parameters default to the type `system.untyped`. +Note that ``using`` is not applied for ``template`` since the untyped template +parameters default to the type ``system.untyped``. -Mixing parameters that should use the `using` declaration with parameters +Mixing parameters that should use the ``using`` declaration with parameters that are explicitly typed is possible and requires a semicolon between them. If expression ------------- -An `if` expression is almost like an if statement, but it is an expression. -This feature is similar to *ternary operators* in other languages. +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: - ```nim +.. code-block:: 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. +An if expression always results in a value, so the ``else`` part is +required. ``Elif`` parts are also allowed. When expression --------------- -Just like an `if` expression, but corresponding to the `when` statement. +Just like an `if expression`, but corresponding to the when statement. Case expression --------------- -The `case` expression is again very similar to the case statement: +The `case expression` is again very similar to the case statement: - ```nim +.. code-block:: nim var favoriteFood = case animal of "dog": "bones" of "cat": "mice" @@ -3595,7 +3247,6 @@ 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 @@ -3604,45 +3255,43 @@ the last expression as the result value. Block expression ---------------- -A `block` expression is almost like a block statement, but it is an expression +A `block expression` is almost like a block statement, but it is an expression 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. - ```nim +.. code-block:: 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: - ```nim +.. code-block:: 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 -which is `{}`) which is thus another way to write the empty array -constructor `[]`. This slightly unusual way of supporting tables +The empty table can be written ``{:}`` (in contrast to the empty set +which is ``{}``) which is thus another way to write as the empty array +constructor ``[]``. This slightly unusual way of supporting tables has lots of advantages: * The order of the (key,value)-pairs is preserved, thus it is easy to - support ordered dicts with for example `{key: val}.newOrderedTable`. -* A table literal can be put into a `const` section and the compiler + support ordered dicts with for example ``{key: val}.newOrderedTable``. +* A table literal can be put into a ``const`` section and the compiler can easily put it into the executable's data section just like it can for arrays and the generated data section requires a minimal amount of memory. * Every table implementation is treated equally syntactically. -* Apart from the minimal syntactic sugar, the language core does not need to +* Apart from the minimal syntactic sugar the language core does not need to know about tables. @@ -3655,26 +3304,26 @@ safe in the sense that a failure to convert a type to another results in an exception (if it cannot be determined statically). Ordinary procs are often preferred over type conversions in Nim: For instance, -`$` is the `toString` operator by convention and `toFloat` and `toInt` +``$`` is the ``toString`` operator by convention and ``toFloat`` and ``toInt`` can be used to convert from floating-point to integer or vice versa. Type conversion can also be used to disambiguate overloaded routines: - ```nim +.. code-block:: 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 +type conversion to unsigned integers and between unsigned integers. The rationale for this is mostly better interoperability with the C Programming language when algorithms are ported from C to Nim. Exception: Values that are converted to an unsigned type at compile time -are checked so that code like `byte(-1)` does not compile. +are checked so that code like ``byte(-1)`` does not compile. **Note**: Historically the operations were unchecked and the conversions were sometimes checked but starting with @@ -3689,36 +3338,34 @@ Type casts as if it would be of another type. Type casts are only needed for low-level programming and are inherently unsafe. - ```nim +.. code-block:: 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: - ```nim +.. code-block:: 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 cast (aside from that the size of the target type +bit pattern of the data being casted (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. +languages or C++'s ``reinterpret_cast`` and ``bit_cast`` features. The addr operator ----------------- -The `addr` operator returns the address of an l-value. If the type of the -location is `T`, the `addr` operator result is of the type `ptr T`. An +The ``addr`` operator returns the address of an l-value. If the type of the +location is ``T``, the `addr` operator result is of the type ``ptr T``. An address is always an untraced reference. Taking the address of an object that resides on the stack is **unsafe**, as the pointer may live longer than the object on the stack and can thus reference a non-existing object. One can get -the address of variables. For easier interoperability with other compiled languages -such as C, retrieving the address of a `let` variable, a parameter, -or a `for` loop variable can be accomplished too: +the address of variables, but one can't use it on variables declared through +``let`` statements: + +.. code-block:: nim - ```nim let t1 = "Hello" var t2 = t1 @@ -3727,19 +3374,23 @@ or a `for` loop variable can be accomplished too: # --> ref 0x7fff6b71b670 --> 0x10bb81050"Hello" echo cast[ptr string](t3)[] # --> Hello - # The following line also works + # The following line doesn't compile: echo repr(addr(t1)) - ``` + # Error: expression has no address + The unsafeAddr operator ----------------------- -The `unsafeAddr` operator is a deprecated alias for the `addr` operator: +For easier interoperability with other compiled languages such as C, retrieving +the address of a ``let`` variable, a parameter or a ``for`` loop variable, the +``unsafeAddr`` operation can be used: + +.. code-block:: nim - ```nim let myArray = [1, 2, 3] foreignProcThatTakesAnAddr(unsafeAddr myArray) - ``` + Procedures ========== @@ -3749,12 +3400,12 @@ called `procedures`:idx: in Nim. A procedure declaration consists of an identifier, zero or more formal parameters, a return value type and a block of code. Formal parameters are declared as a list of identifiers separated by either comma or semicolon. A parameter is given a type -by `: typename`. The type applies to all parameters immediately before it, +by ``: typename``. The type applies to all parameters immediately before it, 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. - ```nim +.. code-block:: nim # Using only commas proc foo(a, b: int, c, d: bool): int @@ -3763,43 +3414,31 @@ 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. +does not provide a value for the argument. - ```nim - # b is optional with 47 as its default value. +.. code-block:: 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. - - ```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`. - ```nim +.. code-block:: 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 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 +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 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: - ```nim +.. code-block:: nim + proc toLower(c: char): char = # toLower for characters if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A'))) @@ -3810,11 +3449,10 @@ 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 ways: +Calling a procedure can be done in many different ways: - ```nim +.. code-block:: nim proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ... # call with positional arguments # parameter bindings: @@ -3825,18 +3463,16 @@ Calling a procedure can be done in many 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: - ```nim +.. code-block:: 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 @@ -3844,16 +3480,15 @@ the operator's position within an expression.) There is no way to declare postfix operators: all postfix operators are built-in and handled by the grammar explicitly. -Any operator can be called like an ordinary proc with the \`opr\` +Any operator can be called like an ordinary proc with the '`opr`' notation. (Thus an operator can have more than two parameters): - ```nim +.. code-block:: nim proc `*+` (a, b, c: int): int = # Multiply and add result = a * b + c assert `*+`(3, 4, 6) == `+`(`*`(a, b), c) - ``` Export marker @@ -3862,7 +3497,8 @@ Export marker If a declared symbol is marked with an `asterisk`:idx: it is exported from the current module: - ```nim +.. code-block:: nim + proc exportedEcho*(s: string) = echo s proc `*`*(a: string; b: int): string = result = newStringOfCap(a.len * b) @@ -3873,38 +3509,38 @@ current module: type ExportedType* = object exportedField*: int - ``` Method call syntax ------------------ -For object-oriented programming, the syntax `obj.methodName(args)` can be used -instead of `methodName(obj, args)`. The parentheses can be omitted if -there are no remaining arguments: `obj.len` (instead of `len(obj)`). +For object-oriented programming, the syntax ``obj.method(args)`` can be used +instead of ``method(obj, args)``. The parentheses can be omitted if 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: - ```nim +.. code-block:: 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. 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]`. +``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]. +See also: `Limitations of the method call syntax +<#templates-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 -`p[T](x, y)`. Note that `[: ]` has no AST representation, the rewrite +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 +``p[T](x, y)``. Note that ``[: ]`` has no AST representation, the rewrite is performed directly in the parsing step. @@ -3914,7 +3550,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: - ```nim +.. code-block:: nim # Module asocket type Socket* = ref object of RootObj @@ -3923,57 +3559,55 @@ different; for this, a special setter syntax is needed: proc `host=`*(s: var Socket, value: int) {.inline.} = ## setter of hostAddr. ## This accesses the 'host' field and is not a recursive call to - ## `host=` because the builtin dot access is preferred if it is + ## ``host=`` because the builtin dot access is preferred if it is ## available: s.host = value proc host*(s: Socket): int {.inline.} = ## getter of hostAddr ## This accesses the 'host' field and is not a recursive call to - ## `host` because the builtin dot access is preferred if it is + ## ``host`` because the builtin dot access is preferred if it is ## available: s.host - ``` - ```nim +.. code-block:: 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 proc defined as ``f=`` (with the trailing ``=``) is called a `setter`:idx:. A setter can be called explicitly via the common backticks notation: - ```nim +.. code-block:: nim + proc `f=`(x: MyObject; value: string) = discard `f=`(myObject, "value") - ``` -`f=` can be called implicitly in the pattern -`x.f = value` if and only if the type of `x` does not have a field -named `f` or if `f` is not visible in the current module. These rules +``f=`` can be called implicitly in the pattern +``x.f = value`` if and only if the type of ``x`` does not have a field +named ``f`` or if ``f`` is not visible in the current module. These rules ensure that object fields and accessors can have the same name. Within the -module `x.f` is then always interpreted as field access and outside the +module ``x.f`` is then always interpreted as field access and outside the module it is interpreted as an accessor proc call. Command invocation syntax ------------------------- -Routines can be invoked without the `()` if the call is syntactically +Routines can be invoked without the ``()`` if the call is syntactically a statement. This command invocation syntax also works for expressions, but then only a single argument may follow. This restriction -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 +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: - ```nim +.. code-block:: nim proc optarg(x: int, y: int = 0): int = x + y proc singlearg(x: int): int = 20*x @@ -3983,11 +3617,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 procedures], `if`, -`case` or `try`. Function calls with no arguments still need () to +For example: (`anonymous procs <#procedures-anonymous-procs>`_), ``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. @@ -4003,166 +3636,71 @@ 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]( -system.html#closureScope.t,untyped) and [capture]( -sugar.html#capture.m,varargs[typed],untyped) for details on how to change this behavior. +behavior inside loop bodies. See `closureScope +`_ and `capture +`_ for details on how to change this behavior. -Anonymous procedures --------------------- +Anonymous Procs +--------------- Unnamed procedures can be used as lambda expressions to pass into other procedures: - ```nim +.. code-block:: nim var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"] - cities.sort(proc (x, y: string): int = - cmp(x.len, y.len)) - ``` + 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](sugar.html) module contains the `=>` macro +executable code. The `sugar `_ module contains the `=>` macro which enables a more succinct syntax for anonymous procedures resembling lambdas as they are in languages like JavaScript, C#, etc. -Do notation ------------ - -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: - - ```nim - var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"] - - sort(cities) do (x, y: string) -> int: - cmp(x.len, y.len) - - # 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 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. - -`do` with a parameter list or pragma list corresponds to an anonymous `proc`, -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. - - ```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. +The ``func`` keyword introduces a shortcut for a `noSideEffect`:idx: proc. - ```nim +.. code-block:: nim func binarySearch[T](a: openArray[T]; elem: T): int - ``` Is short for: - ```nim +.. code-block:: nim proc binarySearch[T](a: openArray[T]; elem: T): int {.noSideEffect.} - ``` -Routines --------- - -A routine is a symbol of kind: `proc`, `func`, `method`, `iterator`, `macro`, `template`, `converter`. - -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 `=`. -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). - - ```nim - # foo.nim: - var witness* = 0 - type Foo[T] = object - proc initFoo*(T: typedesc): Foo[T] = discard - proc `=destroy`[T](x: var Foo[T]) = witness.inc # type bound operator - - # main.nim: - import foo - block: - var a = initFoo(int) - doAssert witness == 0 - doAssert witness == 1 - block: - var a = initFoo(int) - doAssert witness == 1 - `=destroy`(a) # can be called explicitly, even without being in scope - doAssert witness == 2 - # will still be called upon exiting scope - doAssert witness == 3 - ``` - -Type bound operators are: -`=destroy`, `=copy`, `=sink`, `=trace`, `=deepcopy`, `=wasMoved`. - -These operations can be *overridden* instead of *overloaded*. This means that -the implementation is automatically lifted to structured types. For instance, -if the type `T` has an overridden assignment operator `=`, this operator is -also used for assignments of the type `seq[T]`. - -Since these operations are bound to a type, they have to be bound to a -nominal type for reasons of simplicity of implementation; this means an -overridden `deepCopy` for `ref T` is really bound to `T` and not to `ref T`. -This also means that one cannot override `deepCopy` for both `ptr T` and -`ref T` at the same time, instead a distinct or object helper type has to be -used for one pointer type. - -For more details on some of those procs, see -[Lifetime-tracking hooks](destructors.html#lifetimeminustracking-hooks). - Nonoverloadable builtins ------------------------ The following built-in procs cannot be overloaded for reasons of implementation -simplicity (they require specialized semantic checking): +simplicity (they require specialized semantic checking):: - declared, defined, definedInScope, compiles, sizeof, - is, shallowCopy, getAst, astToStr, spawn, procCall + 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](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`: +the ``system`` 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``:: - declared, defined, definedInScope, compiles, getAst, astToStr + declared, defined, definedInScope, compiles, getAst, astToStr Var parameters -------------- -The type of a parameter may be prefixed with the `var` keyword: +The type of a parameter may be prefixed with the ``var`` keyword: - ```nim +.. code-block:: nim proc divmod(a, b: int; res, remainder: var int) = res = a div b remainder = a mod b @@ -4173,15 +3711,14 @@ 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`. +In the example, ``res`` and ``remainder`` are `var parameters`. Var parameters can be modified by the procedure and the changes are 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: - ```nim +.. code-block:: nim proc divmod(a, b: int; res, remainder: ptr int) = res[] = a div b remainder[] = a mod b @@ -4191,12 +3728,11 @@ 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: - ```nim +.. code-block:: nim proc divmod(a, b: int): tuple[res, remainder: int] = (a div b, a mod b) @@ -4204,18 +3740,16 @@ 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: - ```nim +.. code-block:: nim var (x, y) = divmod(8, 5) # tuple unpacking assert x == 1 assert y == 3 - ``` -**Note**: `var` parameters are never necessary for efficient parameter +**Note**: ``var`` parameters are never necessary for efficient parameter passing. Since non-var parameters cannot be modified the compiler is always free to pass arguments by reference if it considers it can speed up execution. @@ -4223,10 +3757,10 @@ free to pass arguments by reference if it considers it can speed up execution. Var return type --------------- -A proc, converter, or iterator may return a `var` type which means that the +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: - ```nim +.. code-block:: nim var g = 0 proc writeAccessToG(): var int = @@ -4234,44 +3768,41 @@ 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: - ```nim +.. code-block:: 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: +For iterators, a component of a tuple return type can have a ``var`` type too: - ```nim +.. code-block:: 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. +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.md +.. include:: manual/var_t_return.rst -### Future directions +Future directions +~~~~~~~~~~~~~~~~~ Later versions of Nim can be more precise about the borrowing rule with a syntax like: - ```nim +.. code-block:: nim proc foo(other: Y; container: var X): var T from container - ``` -Here `var T from container` explicitly exposes that the +Here ``var T from container`` explicitly exposes that the location is derived from the second parameter (called -'container' in this case). The syntax `var T from p` specifies a type -`varTy[T, 2]` which is incompatible with `varTy[T, 1]`. +'container' in this case). The syntax ``var T from p`` specifies a type +``varTy[T, 2]`` which is incompatible with ``varTy[T, 1]``. NRVO @@ -4284,15 +3815,15 @@ See https://github.com/nim-lang/RFCs/issues/230 for more information. The return value is represented inside the body of a routine as the special `result`:idx: variable. This allows for a mechanism much like C++'s "named return value optimization" (`NRVO`:idx:). NRVO means that the stores -to `result` inside `p` directly affect the destination `dest` -in `let/var dest = p(args)` (definition of `dest`) and also in `dest = p(args)` -(assignment to `dest`). This is achieved by rewriting `dest = p(args)` -to `p'(args, dest)` where `p'` is a variation of `p` that returns `void` and -receives a hidden mutable parameter representing `result`. +to ``result`` inside ``p`` directly affect the destination ``dest`` +in ``let/var dest = p(args)`` (definition of ``dest``) and also in ``dest = p(args)`` +(assignment to ``dest``). This is achieved by rewriting ``dest = p(args)`` +to ``p'(args, dest)`` where ``p'`` is a variation of ``p`` that returns ``void`` and +receives a hidden mutable parameter representing ``result``. Informally: - ```nim +.. code-block:: nim proc p(): BigT = ... var x = p() @@ -4304,17 +3835,17 @@ Informally: var x; p(x) p(x) - ``` -Let `T`'s be `p`'s return type. NRVO applies for `T` -if `sizeof(T) >= N` (where `N` is implementation dependent), +Let ``T``'s be ``p``'s return type. NRVO applies for ``T`` +if ``sizeof(T) >= N`` (where ``N`` is implementation dependent), in other words, it applies for "big" structures. -If `p` can raise an exception, NRVO applies regardless. This can produce +If ``p`` can raise an exception, NRVO applies regardless. This can produce observable differences in behavior: - ```nim +.. code-block:: nim + type BigT = array[16, int] @@ -4331,24 +3862,23 @@ observable differences in behavior: doAssert x == [0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0] main() - ``` -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: +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``. - ```nim - {.push warning[ObservableStores]: on.} - main() - {.pop.} - ``` Overloading of the subscript operator ------------------------------------- -The `[]` subscript operator for arrays/openarrays/sequences can be overloaded. +The ``[]`` subscript operator for arrays/openarrays/sequences can be overloaded. Methods @@ -4358,7 +3888,7 @@ Procedures always use static dispatch. Methods use dynamic dispatch. For dynamic dispatch to work on an object it should be a reference type. - ```nim +.. code-block:: nim type Expression = ref object of RootObj ## abstract base class for an expression Literal = ref object of Expression @@ -4386,16 +3916,15 @@ 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 +In the example the constructors ``newLit`` and ``newPlus`` are procs +because they should use static binding, but ``eval`` is a method because it requires dynamic binding. As can be seen in the example, base methods have to be annotated with -the `base`:idx: pragma. The `base` pragma also acts as a reminder for the -programmer that a base method `m` is used as the foundation to determine all -the effects that a call to `m` might cause. +the `base`:idx: pragma. The ``base`` pragma also acts as a reminder for the +programmer that a base method ``m`` is used as the foundation to determine all +the effects that a call to ``m`` might cause. **Note**: Compile-time execution is not (yet) supported for methods. @@ -4406,18 +3935,19 @@ Multi-methods -------------- **Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass -`--multimethods:on`:option: when compiling. +``--multimethods:on`` when compiling. -In a multi-method, all parameters that have an object type are used for the -dispatching: +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) {.base, inline.} = + method collide(a, b: Thing) {.inline.} = quit "to override!" method collide(a: Thing, b: Unit) {.inline.} = @@ -4430,7 +3960,6 @@ dispatching: new a new b collide(a, b) # output: 2 - ``` Inhibit dynamic method resolution via procCall ----------------------------------------------- @@ -4439,7 +3968,9 @@ 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. - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + type Thing = ref object of RootObj Unit = ref object of Thing @@ -4452,30 +3983,29 @@ languages offer. # Call the base method: procCall m(Thing(a)) echo "1" - ``` Iterators and the for statement =============================== The `for`:idx: statement is an abstract mechanism to iterate over the elements -of a container. It relies on an `iterator`:idx: to do so. Like `while` -statements, `for` statements open an `implicit block`:idx: so that they -can be left with a `break` statement. +of a container. It relies on an `iterator`:idx: to do so. Like ``while`` +statements, ``for`` statements open an `implicit block`:idx:, so that they +can be left with a ``break`` statement. -The `for` loop declares iteration variables - their scope reaches until the +The ``for`` loop declares iteration variables - their scope reaches until the end of the loop body. The iteration variables' types are inferred by the return type of the iterator. An iterator is similar to a procedure, except that it can be called in the -context of a `for` loop. Iterators provide a way to specify the iteration over -an abstract type. The `yield` statement in the called iterator plays a key -role in the execution of a `for` loop. Whenever a `yield` statement is -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 +context of a ``for`` loop. Iterators provide a way to specify the iteration over +an abstract type. A key role in the execution of a ``for`` loop plays the +``yield`` statement in the called iterator. Whenever a ``yield`` statement is +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: - ```nim +.. code-block:: nim # this definition exists in the system module iterator items*(a: string): char {.inline.} = var i = 0 @@ -4485,17 +4015,15 @@ 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 had written this: +The compiler generates code as if the programmer would have written this: - ```nim +.. code-block:: 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 @@ -4503,21 +4031,20 @@ the type of the i'th component. In other words, implicit tuple unpacking in a for loop context is supported. 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)`; -i.e. an `items` iterator is implicitly invoked: +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: - ```nim +.. code-block:: nim for x in [1,2,3]: echo x - ``` -If the for loop has exactly 2 variables, a `pairs` iterator is implicitly +If the for loop has exactly 2 variables, a ``pairs`` iterator is implicitly invoked. -Symbol lookup of the identifiers `items`/`pairs` is performed after -the rewriting step, so that all overloads of `items`/`pairs` are taken +Symbol lookup of the identifiers ``items``/``pairs`` is performed after +the rewriting step, so that all overloads of ``items``/``pairs`` are taken into account. @@ -4530,7 +4057,7 @@ leading to zero overhead for the abstraction, but may result in a heavy increase in code size. Caution: the body of a for loop over an inline iterator is inlined into -each `yield` statement appearing in the iterator code, +each ``yield`` statement appearing in the iterator code, so ideally the code should be refactored to contain a single yield when possible to avoid code bloat. @@ -4540,7 +4067,7 @@ templates, macros, and other inline iterators. In contrast to that, a `closure iterator`:idx: can be passed around more freely: - ```nim +.. code-block:: nim iterator count0(): int {.closure.} = yield 0 @@ -4555,26 +4082,28 @@ 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 +2. ``return`` is allowed in a closure iterator but not in an inline iterator (but rarely useful) and ends the iteration. -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. +3. Neither inline nor closure iterators can be (directly)* recursive. +4. Neither inline nor closure iterators have the special ``result`` variable. +5. Closure iterators are not supported by the js backend. -Iterators that are neither marked `{.closure.}` nor `{.inline.}` explicitly +(*) 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. -The `iterator` type is always of the calling convention `closure` +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: - ```nim +.. code-block:: nim # simple tasking: type Task = iterator (ticker: int) @@ -4604,16 +4133,15 @@ a `collaborative tasking`:idx: system: inc ticker runTasks(a1, a2) - ``` -The builtin `system.finished` can be used to determine if an iterator has +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 -`true` one iteration after the iterator has finished: +Note that ``system.finished`` is error prone to use because it only returns +``true`` one iteration after the iterator has finished: - ```nim +.. code-block:: nim iterator mycount(a, b: int): int {.closure.} = var x = a while x <= b: @@ -4629,20 +4157,18 @@ 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: - ```nim +.. code-block:: 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` +pair ``(value, done)`` and ``finished`` is used to access the hidden ``done`` field. @@ -4650,7 +4176,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: - ```nim +.. code-block:: nim proc mycount(a, b: int): iterator (): int = result = iterator (): int = var x = a @@ -4662,12 +4188,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: - ```nim - import std/macros +.. code-block:: nim + import macros macro toItr(x: ForLoopStmt): untyped = let expr = x[0] let call = x[1][1] # Get foo out of toItr(foo) @@ -4680,49 +4205,43 @@ 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 apparatus involvement, closure iterator -invocation is typically higher cost than inline iterators. Adornment by +Because of full backend function call aparatus involvment, 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. -The factory `proc`, as an ordinary procedure, can be recursive. The +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: +would. For example: - ```nim +.. code-block:: nim proc recCountDown(n: int): iterator(): int = result = iterator(): int = if n > 0: yield n for e in toItr(recCountDown(n - 1)): - yield e + yield e for i in toItr(recCountDown(6)): # Emits: 6 5 4 3 2 1 echo i - ``` - - -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`_): - ```nim +.. code-block:: 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. @@ -4731,7 +4250,7 @@ Type sections Example: - ```nim +.. code-block:: 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 @@ -4741,13 +4260,12 @@ 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 +A type section begins with the ``type`` keyword. It contains multiple type definitions. A type definition binds a type to a name. Type definitions can be recursive or even mutually recursive. Mutually recursive types are only -possible within a single `type` section. Nominal types like `objects` -or `enums` can only be defined in a `type` section. +possible within a single ``type`` section. Nominal types like ``objects`` +or ``enums`` can only be defined in a ``type`` section. @@ -4759,7 +4277,7 @@ Try statement Example: - ```nim +.. code-block:: nim # read the first two lines of a text file that should contain numbers # and tries to add them var @@ -4771,143 +4289,141 @@ Example: echo "sum: " & $(parseInt(a) + parseInt(b)) except OverflowDefect: echo "overflow!" - except ValueError, IOError: - echo "catch multiple exceptions!" - except CatchableError: - echo "Catchable exception!" + except ValueError: + echo "could not convert string to integer" + except IOError: + echo "IO error!" + except: + echo "Unknown exception!" finally: close(f) - ``` -The statements after the `try` are executed in sequential order unless -an exception `e` is raised. If the exception type of `e` matches any -listed in an `except` clause, the corresponding statements are executed. -The statements following the `except` clauses are called +The statements after the ``try`` are executed in sequential order unless +an exception ``e`` is raised. If the exception type of ``e`` matches any +listed in an ``except`` clause the corresponding statements are executed. +The statements following the ``except`` clauses are called `exception handlers`:idx:. +The empty `except`:idx: clause is executed if there is an exception that is +not listed otherwise. It is similar to an ``else`` clause in ``if`` statements. + If there is a `finally`:idx: clause, it is always executed after the exception handlers. The exception is *consumed* in an exception handler. However, an exception handler may raise another exception. If the exception is not handled, it is propagated through the call stack. This means that often -the rest of the procedure - that is not within a `finally` clause - +the rest of the procedure - that is not within a ``finally`` clause - is not executed (if an exception occurs). Try expression -------------- -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`: +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``: - ```nim test - from std/strutils import parseInt +.. code-block:: nim + from strutils import parseInt let x = try: parseInt("133a") - except ValueError: -1 + 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: +To prevent confusing code there is a parsing limitation; if the ``try`` +follows a ``(`` it has to be written as a one liner: - ```nim test - from std/strutils import parseInt - let x = (try: parseInt("133a") except ValueError: -1) - ``` +.. code-block:: nim + let x = (try: parseInt("133a") except: -1) Except clauses -------------- -Within an `except` clause it is possible to access the current exception +Within an ``except`` clause it is possible to access the current exception using the following syntax: - ```nim +.. code-block:: 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 +Alternatively, it is possible to use ``getCurrentException`` to retrieve the exception that has been raised: - ```nim +.. code-block:: nim try: # ... except IOError: let e = getCurrentException() # Now use "e" - ``` -Note that `getCurrentException` always returns a `ref Exception` +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: +above, ``IOError``), one must convert it explicitly: - ```nim +.. code-block:: 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`: +error message from ``e``, and for such situations, it is enough to use +``getCurrentExceptionMsg``: - ```nim +.. code-block:: nim try: # ... - except CatchableError: + except: echo getCurrentExceptionMsg() - ``` Custom exceptions ----------------- -It is possible to create custom exceptions. A custom exception is a custom type: +Is it possible to create custom exceptions. A custom exception is a custom type: - ```nim +.. code-block:: nim type LoadError* = object of Exception - ``` -Ending the custom exception's name with `Error` is recommended. +Ending the custom exception's name with ``Error`` is recommended. -Custom exceptions can be raised just like any other exception, e.g.: +Custom exceptions can be raised like any others, e.g.: - ```nim +.. code-block:: nim raise newException(LoadError, "Failed to load data") - ``` Defer statement --------------- -Instead of a `try finally` statement a `defer` statement can be used, which +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` will be considered -to be in an implicit try block in the current block: +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" - ```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: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + proc main = var f = open("numbers.txt") try: @@ -4915,12 +4431,13 @@ 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/macro is called from: +to the block where the template is called from: + +.. code-block:: nim + :test: "nim c $1" - ```nim test = "nim c $1" template safeOpenDefer(f, path) = var f = open(path, fmWrite) defer: close(f) @@ -4941,9 +4458,8 @@ to the block where the template/macro is called from: try: f.write "abc" # adds a lexical scope finally: close(f) - ``` -Top-level `defer` statements are not supported +Top-level ``defer`` statements are not supported since it's unclear what such a statement should refer to. @@ -4952,33 +4468,32 @@ Raise statement Example: - ```nim +.. code-block:: 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. +the ``raise`` statement is the only way to raise an exception. .. XXX document this better! If no exception name is given, the current exception is `re-raised`:idx:. The `ReraiseDefect`:idx: exception is raised if there is no exception to -re-raise. It follows that the `raise` statement *always* raises an +re-raise. It follows that the ``raise`` statement *always* raises an exception. Exception hierarchy ------------------- -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`) +The exception tree is defined in the `system `_ 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 that terminates the whole process. If panics are turned into exceptions, these exceptions inherit from `Defect`. Exceptions that indicate any other runtime error that can be caught inherit from -`system.CatchableError` (which is a subtype of `Exception`). +``system.CatchableError`` (which is a subtype of ``Exception``). Imported exceptions @@ -4988,7 +4503,9 @@ 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: - ```nim test = "nim cpp -r $1" +.. code-block:: nim + :test: "nim cpp -r $1" + type CStdException {.importcpp: "std::exception", header: "", inheritable.} = object ## does not inherit from `RootObj`, so we use `inheritable` instead @@ -5019,7 +4536,6 @@ 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 @@ -5029,10 +4545,6 @@ and rely on functionality of the `x` object to get exception details. Effect system ============= -**Note**: The rules for effect tracking changed with the release of version -1.6 of the Nim compiler. - - Exception tracking ------------------ @@ -5040,28 +4552,31 @@ 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: - ```nim test = "nim c $1" +.. code-block:: 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: +An empty ``raises`` list (``raises: []``) means that no exception may be raised: - ```nim +.. code-block:: nim proc p(): bool {.raises: [].} = try: unsafeCall() result = true - except CatchableError: + except: result = false - ``` -A `raises` list can also be attached to a proc type. This affects type +A ``raises`` list can also be attached to a proc type. This affects type compatibility: - ```nim test = "nim c $1" status = 1 +.. code-block:: nim + :test: "nim c $1" + :status: 1 + type Callback = proc (s: string) {.raises: [IOError].} var @@ -5071,269 +4586,110 @@ compatibility: raise newException(OSError, "OS") c = p # type error - ``` -For a routine `p`, the compiler uses inference rules to determine the set of -possibly raised exceptions; the algorithm operates on `p`'s call graph: +For a routine ``p`` the compiler uses inference rules to determine the set of +possibly raised exceptions; the algorithm operates on ``p``'s call graph: -1. Every indirect call via some proc type `T` is assumed to - raise `system.Exception` (the base type of the exception hierarchy) and - thus any exception unless `T` has an explicit `raises` list. - However, if the call is of the form `f(...)` where `f` is a parameter of - the currently analyzed routine it is ignored that is marked as `.effectsOf: f`. - 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` 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 - raise `system.Exception` unless `q` has an explicit `raises` list. - Procs that are `importc`'ed are assumed to have `.raises: []`, unless explicitly - declared otherwise. -4. Every call to a method `m` is assumed to - raise `system.Exception` unless `m` has an explicit `raises` list. -5. For every other call, the analysis can determine an exact `raises` list. -6. For determining a `raises` list, the `raise` and `try` statements - of `p` are taken into consideration. +1. Every indirect call via some proc type ``T`` is assumed to + raise ``system.Exception`` (the base type of the exception hierarchy) and + thus any exception unless ``T`` has an explicit ``raises`` list. + However, if the call is of the form ``f(...)`` where ``f`` is a parameter of the currently analyzed routine it is ignored. The call is optimistically assumed to have no effect. Rule 2 compensates for this case. +2. Every expression of some proc type within a call that is not a call + itself (and not nil) is assumed to be called indirectly somehow 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 or an ``importc`` pragma) is assumed to + raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list. +4. Every call to a method ``m`` is assumed to + raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list. +5. For every other call the analysis can determine an exact ``raises`` list. +6. For determining a ``raises`` list, the ``raise`` and ``try`` statements + of ``p`` are taken into consideration. +Rules 1-2 ensure the following works: -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: - - ```nim - proc mydiv(a, b): int {.raises: [].} = - a div b # can raise an DivByZeroDefect - ``` - -And so is: - - ```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 -with `--panics:on`:option: Defects become unrecoverable errors. -(Since version 1.4 of the language.) - - -EffectsOf annotation --------------------- - -Rules 1-2 of the exception tracking inference rules (see the previous section) -ensure the following works: - - ```nim - proc weDontRaiseButMaybeTheCallback(callback: proc()) {.raises: [], effectsOf: callback.} = - callback() +.. code-block:: nim + proc noRaise(x: proc()) {.raises: [].} = + # unknown call that might raise anything, but valid: + x() proc doRaise() {.raises: [IOError].} = raise newException(IOError, "IO") 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: -The proc `weDontRaiseButMaybeTheCallback` raises the exceptions -that `callback` raises. + noRaise(doRaise) So in many cases a callback does not cause the compiler to be overly -conservative in its effect analysis: +conservative in its effect analysis. - ```nim test = "nim c $1" status = 1 - {.push warningAsError[Effect]: on.} +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:: - import algorithm +.. code-block:: nim - type - MyInt = distinct int + proc mydiv(a, b): int {.raises: [].} = + a div b # can raise an DivByZeroDefect - var toSort = @[MyInt 1, MyInt 2, MyInt 3] +And so is:: - proc cmpN(a, b: MyInt): int = - cmp(a.int, b.int) +.. code-block:: nim - proc harmless {.raises: [].} = - toSort.sort cmpN + proc mydiv(a, b): int {.raises: [].} = + if b == 0: raise newException(DivByZeroDefect, "division by zero") + else: result = a div b - proc cmpE(a, b: MyInt): int {.raises: [Exception].} = - cmp(a.int, b.int) - - proc harmful {.raises: [].} = - # does not compile, `sort` can now raise Exception - toSort.sort cmpE - ``` +The reason for this is that ``DivByZeroDefect`` inherits from ``Defect`` and +with ``--panics:on`` Defects become unrecoverable errors. +(Since version 1.4 of the language.) Tag tracking ------------ -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: +The 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 $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_effects_please() {.tags: [].} = + proc no_IO_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 +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. 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 ------------- - -The `noSideEffect` pragma is used to mark a proc/iterator that can have only -side effects through parameters. This means that the proc/iterator only changes locations that are -reachable from its parameters and the return value only depends on the -parameters. If none of its parameters have the type `var`, `ref`, `ptr`, `cstring`, or `proc`, -then no locations are modified. - -In other words, a routine has no side effects if it does not access a threadlocal -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]( -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: - - ```nim - func `+` (x, y: int): int - ``` - - -To override the compiler's side effect analysis a `{.noSideEffect.}` -`cast` pragma block can be used: - - ```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.** - - -GC safety effect ----------------- - -We call a proc `p` `GC safe`:idx: when it doesn't access any global variable -that contains GC'ed memory (`string`, `seq`, `ref` or a closure) either -directly or indirectly through a call to a GC unsafe proc. - -**The GC safety property is usually inferred. The inference for GC safety is -analogous to the inference for exception tracking.** - -The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe, -otherwise this property is inferred by the compiler. Note that `noSideEffect` -implies `gcsafe`. - -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: - - ```nim - var - someGlobal: string = "some string here" - perThread {.threadvar.}: string - - proc setPerThread() = - {.cast(gcsafe).}: - deepCopy(perThread, someGlobal) - ``` - - -See also: - -- [Shared heap memory management](mm.html). - Effects pragma -------------- -The `effects` pragma has been designed to assist the programmer with the +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: +effects up to the ``effects``'s position: - ```nim +.. code-block:: 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 +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 appears in. @@ -5344,12 +4700,14 @@ Generics are Nim's means to parametrize procs, iterators or types with `type parameters`:idx:. Depending on the context, the brackets are used either to introduce type parameters or to instantiate a generic proc, iterator, or type. -The following example shows how a generic binary tree can be modeled: +The following example shows 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 parameter `T` + # generic param ``T`` le, ri: BinaryTree[T] # left and right subtrees; may be nil data: T # the data stored in a node @@ -5364,8 +4722,8 @@ The following example shows how a generic binary tree can be modeled: else: var it = root while it != nil: - # compare the data items; uses the generic `cmp` proc - # that works for any type that has a `==` and `<` operator + # compare the data items; uses the generic ``cmp`` proc + # that works for any type that has a ``==`` and ``<`` operator var c = cmp(it.data, n.data) if c < 0: if it.le == nil: @@ -5395,56 +4753,56 @@ The following example shows how a generic binary tree can be modeled: n = n.le # and follow the left pointer var - root: BinaryTree[string] # instantiate a BinaryTree with `string` - add(root, newNode("hello")) # instantiates `newNode` and `add` - add(root, "world") # instantiates the second `add` proc + root: BinaryTree[string] # instantiate a BinaryTree with ``string`` + add(root, newNode("hello")) # instantiates ``newNode`` and ``add`` + 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 +The ``T`` is called a `generic type parameter`:idx: or a `type variable`:idx:. + Is operator ----------- -The `is` operator is evaluated during semantic analysis to check for type +The ``is`` operator is evaluated during semantic analysis to check for type equivalence. It is therefore very useful for type specialization within generic code: - ```nim +.. code-block:: 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 +Type Classes ------------ A type class is a special pseudo-type that can be used to match against -types in the context of overload resolution or the `is` operator. +types in the context of overload resolution or the ``is`` operator. Nim supports the following built-in type classes: ================== =================================================== type class matches ================== =================================================== -`object` any object type -`tuple` any tuple type +``object`` any object type +``tuple`` any tuple type -`enum` any enumeration -`proc` any proc type -`ref` any `ref` type -`ptr` any `ptr` type -`var` any `var` type -`distinct` any distinct type -`array` any array type -`set` any set type -`seq` any seq type -`auto` any type +``enum`` any enumeration +``proc`` any proc type +``ref`` any ``ref`` type +``ptr`` any ``ptr`` type +``var`` any ``var`` type +``distinct`` any distinct type +``array`` any array type +``set`` any set type +``seq`` any seq type +``auto`` any type +``any`` distinct auto (see below) ================== =================================================== Furthermore, every generic type automatically creates a type class of the same @@ -5453,22 +4811,14 @@ 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: - ```nim +.. code-block:: 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: - - ```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 @@ -5479,22 +4829,20 @@ runtime type dynamism, unlike object variants or methods. As an example, the following would not compile: - ```nim +.. code-block:: 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: - ```nim +.. code-block:: 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 @@ -5502,108 +4850,107 @@ Implicit generics A type class can be used directly as the parameter's type. - ```nim +.. code-block:: 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 parameter types used within the program. +combination of param 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: - ```nim +.. code-block:: 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 + ## generic ``==`` operator for tuples that is lifted from the components ## of `x` and `y`. 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 parameter matching the type class to bind to a different type. Such +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 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: - ```nim +.. code-block:: 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: - ```nim +.. code-block:: 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 - ``` - ```nim +.. code-block:: nim + proc p(a: Table, b: Table) # is roughly the same as: proc p[Key, Value](a, b: Table[Key, Value]) - ``` - ```nim +.. code-block:: 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: - ```nim +.. code-block:: nim + proc p(a: typedesc) # is roughly the same as: proc p[T](a: typedesc[T]) - ``` `typedesc` is a "bind many" type class: - ```nim +.. code-block:: 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: - ```nim +.. code-block:: nim + proc p(a: typedesc; b: a) = discard # is roughly the same as: @@ -5612,16 +4959,18 @@ 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 ------------------------------ -The types `var T` and `typedesc[T]` cannot be inferred in a generic +The types ``var T`` and ``typedesc[T]`` cannot be inferred in a generic instantiation. The following is not allowed: - ```nim test = "nim c $1" status = 1 +.. code-block:: nim + :test: "nim c $1" + :status: 1 + proc g[T](f: proc(x: T); x: T) = f(x) @@ -5638,24 +4987,26 @@ 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 -context, an "open" symbol can. Per default, overloaded symbols are open +context, an "open" symbol can. Per default overloaded symbols are open 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: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + type Index = distinct int @@ -5665,11 +5016,10 @@ 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 -the `Index` type is defined *after* the `==` for tuples; yet the example +In the example, the generic ``==`` for tuples (as defined in the system module) +uses the ``==`` operators of the tuple's components. However, the ``==`` for +the ``Index`` type is defined *after* the ``==`` for tuples; yet the example compiles as the instantiation takes the currently defined symbols into account too. @@ -5678,27 +5028,28 @@ Mixin statement A symbol can be forced to be open by a `mixin`:idx: declaration: - ```nim test = "nim c $1" +.. code-block:: 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. +``mixin`` statements only make sense in templates and generics. Bind statement -------------- -The `bind` statement is the counterpart to the `mixin` statement. It +The ``bind`` statement is the counterpart to the ``mixin`` statement. It 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): - ```nim +.. code-block:: nim # Module A var lastId = 0 @@ -5707,63 +5058,17 @@ definition): bind lastId inc(lastId) lastId - ``` - ```nim +.. code-block:: nim # Module B import A echo genId() - ``` -But a `bind` is rarely useful because symbol binding from the definition +But a ``bind`` is rarely useful because symbol binding from the definition scope is the default. -`bind` statements only make sense in templates and generics. - - -Delegating bind statements --------------------------- - -The following example outlines a problem that can arise when generic -instantiations cross multiple different modules: - - ```nim - # module A - proc genericA*[T](x: T) = - mixin init - init(x) - ``` - - - ```nim - import C - - # module B - proc genericB*[T](x: T) = - # Without the `bind init` statement C's init proc is - # not available when `genericB` is instantiated: - bind init - genericA(x) - ``` - - ```nim - # module C - type O = object - proc init*(x: var O) = discard - ``` - - ```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 -instantiation of `genericA`. The solution is to `forward`:idx: these -symbols by a `bind` statement inside `genericB`. +``bind`` statements only make sense in templates and generics. Templates @@ -5777,67 +5082,71 @@ The syntax to *invoke* a template is the same as calling a procedure. Example: - ```nim +.. code-block:: 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 +The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact templates: -| `a > b` is transformed into `b < a`. -| `a in b` is transformed into `contains(b, a)`. -| `notin` and `isnot` have the obvious meanings. +| ``a > b`` is transformed into ``b < a``. +| ``a in b`` is transformed into ``contains(b, a)``. +| ``notin`` and ``isnot`` have the obvious meanings. -The "types" of templates can be the symbols `untyped`, -`typed` or `typedesc`. These are "meta types", they can only be used in certain -contexts. Regular types can be used too; this implies that `typed` expressions +The "types" of templates can be the symbols ``untyped``, +``typed`` or ``typedesc``. These are "meta types", they can only be used in certain +contexts. Regular types can be used too; this implies that ``typed`` expressions are expected. Typed vs untyped parameters --------------------------- -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: +An ``untyped`` parameter means that symbol lookups and type resolution is not +performed before the expression is passed to the template. This means that for +example *undeclared* identifiers 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 - ``` - ```nim test = "nim c $1" status = 1 +.. code-block:: 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 -an `immediate` pragma and then these templates do not take part in +A template where every parameter is ``untyped`` is called an `immediate`:idx: +template. For historical reasons templates can be explicitly annotated with +an ``immediate`` pragma and then these templates do not take part in overloading resolution and the parameters' types are *ignored* by the compiler. Explicit immediate templates are now deprecated. -**Note**: For historical reasons, `stmt` was an alias for `typed` and -`expr` was an alias for `untyped`, but they are removed. +**Note**: For historical reasons ``stmt`` was an alias for ``typed`` and +``expr`` was an alias for ``untyped``, but they are removed. Passing a code block to a template ---------------------------------- One can pass a block of statements as the last argument to a template -following the special `:` syntax: +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): @@ -5851,17 +5160,19 @@ 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` +In the example, the two ``writeLine`` statements are bound to the ``actions`` parameter. -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 +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: - ```nim test = "nim c $1" status = 1 +.. code-block:: nim + :test: "nim c $1" + :status: 1 + template t(body: typed) = proc p = echo "hey" block: @@ -5869,15 +5180,16 @@ 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 -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 +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 +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: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + template t(body: untyped) = proc p = echo "hey" block: @@ -5885,20 +5197,20 @@ type-checked: t: p() # compiles - ``` 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: +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. @@ -5910,7 +5222,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: - ```nim +.. code-block:: nim # Module A var lastId = 0 @@ -5918,16 +5230,14 @@ bound from the definition scope of the template: template genId*: untyped = inc(lastId) lastId - ``` - ```nim +.. code-block:: 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` +As in generics symbol binding can be influenced via ``mixin`` or ``bind`` statements. @@ -5935,9 +5245,11 @@ statements. Identifier construction ----------------------- -In templates, identifiers can be constructed with the backticks notation: +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 @@ -5945,20 +5257,19 @@ 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`. +In the example ``name`` is instantiated with ``myint``, so \`T name\` becomes +``Tmyint``. Lookup rules for template parameters ------------------------------------ -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 +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: - ```nim +.. code-block:: nim # module 'm' type @@ -5972,11 +5283,10 @@ 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: +But the global symbol can properly be captured by a ``bind`` statement: - ```nim +.. code-block:: nim # module 'm' type @@ -5991,16 +5301,17 @@ But the global symbol can properly be captured by a `bind` statement: tstLev(levA) # produces: 'levA levB' - ``` Hygiene in templates -------------------- -Per default, templates are `hygienic`:idx:\: Local identifiers declared in a +Per default templates are `hygienic`:idx:\: Local identifiers declared in a template cannot be accessed in the instantiation context: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + template newException*(exceptn: typedesc, message: string): untyped = var e: ref exceptn # e is implicitly gensym'ed here @@ -6011,51 +5322,50 @@ 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 -scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas: -`gensym`'ed symbols are not exposed but `inject`'ed symbols are. +scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas: gensym'ed +symbols are not exposed but inject'ed are. -The default for symbols of entity `type`, `var`, `let` and `const` -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: +The default for symbols of entity ``type``, ``var``, ``let`` and ``const`` +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: - ```nim +.. code-block:: nim template withFile(f, fn, mode: untyped, actions: untyped): untyped = block: - var f: File # since 'f' is a template parameter, it's injected implicitly + var f: File # since 'f' is a template param, 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 a template definition and cannot be abstracted over: +The ``inject`` and ``gensym`` pragmas are second class annotations; they have +no semantics outside of a template definition and cannot be abstracted over: - ```nim +.. code-block:: 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 -a template. `inject` and `gensym` have no effect in `dirty` templates. +a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates. -`gensym`'ed symbols cannot be used as `field` in the `x.field` syntax. -Nor can they be used in the `ObjectConstruction(field: value)` -and `namedParameterCall(field = value)` syntactic constructs. +``gensym``'ed symbols cannot be used as ``field`` in the ``x.field`` syntax. +Nor can they be used in the ``ObjectConstruction(field: value)`` +and ``namedParameterCall(field = value)`` syntactic constructs. The reason for this is that code like - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + type T = object f: int @@ -6063,15 +5373,17 @@ 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. However, this means that the method call syntax is not available for -`gensym`'ed symbols: +``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 @@ -6079,39 +5391,65 @@ However, this means that the method call syntax is not available for echo x.T # invalid: instead use: 'echo T(x)'. tmp(12) - ``` + + +**Note**: The Nim compiler prior to version 1 was more lenient about this +requirement. Use the ``--useVersion:0.19`` switch for a transition period. + Limitations of the method call syntax ------------------------------------- -The expression `x` in `x.f` needs to be semantically checked (that means +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: - ```nim test = "nim c $1" status = 1 +.. code-block:: nim + :test: "nim c $1" + :status: 1 + template declareVar(name: untyped) = const name {.inject.} = 45 # Doesn't compile: unknownIdentifier.declareVar - ``` +Another common example is this: + +.. code-block:: nim + :test: "nim c $1" + :status: 1 + + from sequtils import toSeq + + iterator something: string = + yield "Hello" + yield "World" + + var info = something().toSeq + +The problem here is that the compiler already decided that ``something()`` as +an iterator is not callable in this context before ``toSeq`` gets its +chance to convert it into a sequence. + 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. - ```nim test = "nim c $1" status = 1 - import std/sequtils +.. code-block:: nim + :test: "nim c $1" + :status: 1 - 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 - ``` + import 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 This means that when for some reason a procedure needs a disambiguation through the module name, the call needs to be @@ -6121,7 +5459,7 @@ Macros ====== A macro is a special function that is executed at compile time. -Normally, the input for a macro is an abstract syntax +Normally the input for a macro is an abstract syntax tree (AST) of the code that is passed to it. The macro can then do transformations on it and return the transformed AST. This can be used to add custom language features and implement `domain-specific languages`:idx:. @@ -6140,24 +5478,18 @@ twice: While macros enable advanced compile-time code transformations, they cannot change Nim's syntax. -**Style note:** For code readability, it is best to use the least powerful -programming construct that remains expressive. So the "check list" is: - -(1) Use an ordinary proc/iterator, if possible. -(2) Else: Use a generic proc/iterator, if possible. -(3) Else: Use a template, if possible. -(4) Else: Use a macro. - -Debug example +Debug Example ------------- -The following example implements a powerful `debug` command that accepts a +The following example implements a powerful ``debug`` command that accepts a variable number of arguments: - ```nim test = "nim c $1" +.. code-block:: 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 + # ``macros`` module: + import macros macro debug(args: varargs[untyped]): untyped = # `args` is a collection of `NimNode` values that each contain the @@ -6182,11 +5514,10 @@ variable number of arguments: a[1] = 45 debug(a[0], a[1], x) - ``` The macro call expands to: - ```nim +.. code-block:: nim write(stdout, "a[0]") write(stdout, ": ") writeLine(stdout, a[0]) @@ -6198,25 +5529,26 @@ 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 -constructor expression. This is why `debug` iterates over all of `args`'s +Arguments that are passed to a ``varargs`` parameter are wrapped in an array +constructor expression. This is why ``debug`` iterates over all of ``n``'s children. -bindSym +BindSym ------- -The above `debug` macro relies on the fact that `write`, `writeLine` and -`stdout` are declared in the system module and are thus visible in the +The above ``debug`` macro relies on the fact that ``write``, ``writeLine`` and +``stdout`` are declared in the system module and thus visible in the instantiating context. There is a way to use bound identifiers -(aka `symbols`:idx:) instead of using unbound identifiers. The `bindSym` +(aka `symbols`:idx:) instead of using unbound identifiers. The ``bindSym`` builtin can be used for that: - ```nim test = "nim c $1" - import std/macros +.. code-block:: nim + :test: "nim c $1" + + import macros macro debug(n: varargs[typed]): untyped = result = newNimNode(nnkStmtList, n) @@ -6233,11 +5565,10 @@ builtin can be used for that: a[1] = 45 debug(a[0], a[1], x) - ``` The macro call expands to: - ```nim +.. code-block:: nim write(stdout, "a[0]") write(stdout, ": ") writeLine(stdout, a[0]) @@ -6249,76 +5580,60 @@ The macro call expands to: write(stdout, "x") write(stdout, ": ") writeLine(stdout, x) - ``` -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. +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. -Note that the symbol names passed to `bindSym` have to be constant. The -experimental feature `dynamicBindSym` ([experimental manual]( -manual_experimental.html#dynamic-arguments-for-bindsym)) -allows this value to be computed dynamically. +Case-Of Macro +------------- -Post-statement blocks ---------------------- +In Nim it is possible to have a macro with the syntax of a *case-of* +expression just with the difference that all of branches are passed to +and processed by the macro implementation. It is then up the macro +implementation to transform the *of-branches* into a valid Nim +statement. The following example should show how this feature could be +used for a lexical analyzer. -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 + import macros - ```nim - macro performWithUndo(task, undo: untyped) = ... + macro case_token(args: varargs[untyped]): untyped = + echo args.treeRepr + # creates a lexical analyzer from regular expressions + # ... (implementation is an exercise for the reader ;-) + discard - performWithUndo do: - # multiple-line block of code - # 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): - of (0, 0): - echo "FizzBuzz" - of (0, _): - echo "Fizz" - of (_, 0): - echo "Buzz" + case_token: # this colon tells the parser it is a macro statement + of r"[A-Za-z_]+[A-Za-z_0-9]*": + return tkIdentifier + of r"0-9+": + return tkInteger + of r"[\+\-\*\?]+": + return tkOperator else: - echo num - ``` + return tkUnknown + + +**Style note**: For code readability, it is the best idea to use the least +powerful programming construct that still suffices. So the "check list" is: + +(1) Use an ordinary proc/iterator, if possible. +(2) Else: Use a generic proc/iterator, if possible. +(3) Else: Use a template, if possible. +(4) Else: Use a macro. For loop macro -------------- A macro that takes as its only input parameter an expression of the special -type `system.ForLoopStmt` can rewrite the entirety of a `for` loop: +type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop: - ```nim test = "nim c $1" - import std/macros +.. code-block:: nim + :test: "nim c $1" - macro example(loop: ForLoopStmt) = - result = newTree(nnkForStmt) # Create a new For loop. - result.add loop[^3] # This is "item". - result.add loop[^2][^1] # This is "[1, 2, 3]". - result.add newCall(bindSym"echo", loop[0]) - - for item in example([1, 2, 3]): discard - ``` - -Expands to: - - ```nim - for item in items([1, 2, 3]): - echo item - ``` - -Another example: - - ```nim test = "nim c $1" - import std/macros + import macros macro enumerate(x: ForLoopStmt): untyped = expectKind x, nnkForStmt @@ -6349,60 +5664,19 @@ 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 ---------------------- - -Macros named `` `case` `` can provide implementations of `case` statements -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.==`): - - ```nim test = "nim c $1" - import std/macros - - macro `case`(n: tuple): untyped = - result = newTree(nnkIfStmt) - let selector = n[0] - for i in 1 ..< n.len: - let it = n[i] - case it.kind - of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr: - result.add it - of nnkOfBranch: - for j in 0..it.len-2: - let cond = newCall("==", selector, it[j]) - result.add newTree(nnkElifBranch, cond, it[^1]) - else: - error "custom 'case' for tuple cannot handle this node", it - - case ("foo", 78) - 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 -of the first argument of the `case` macro. Then the complete `case` -statement is passed in place of the argument and the macro is evaluated. - -In other words, the macro needs to transform the full `case` statement -but only the statement's selector expression is used to determine which -macro to call. - Special Types ============= -static\[T] ----------- +static[T] +--------- As their name suggests, static parameters must be constant expressions: - ```nim +.. code-block:: nim + proc precompiledRegex(pattern: static string): RegEx = var res {.global.} = re(pattern) return res @@ -6412,16 +5686,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 parameters are treated as -generic parameters - the proc will be compiled separately for each unique +For the purposes of code generation, all static params are treated as +generic params - the proc will be compiled separately for each unique supplied value (or combination of values). -Static parameters can also appear in the signatures of generic types: +Static params can also appear in the signatures of generic types: + +.. code-block:: nim - ```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 @@ -6432,74 +5706,74 @@ Static parameters 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 parameter can be omitted to obtain the type +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 class of all constant expressions. A more specific type class can be created by -instantiating `static` with another type class. +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: +expression by coercing it to a corresponding ``static`` type: - ```nim - import std/math +.. code-block:: nim + import 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 -all values must have a type, `typedesc` is considered their special type. +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 parameter is omitted, `typedesc` denotes the type class of all types. -As a syntactic convenience, one can also use `typedesc` as a modifier. +``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. +As a syntactic convenience, one can also use ``typedesc`` as a modifier. -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 parameter will refer to +Procs featuring ``typedesc`` params 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 the bound concrete type: - ```nim +.. code-block:: 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 parameters are present, they will bind freely to different -types. To force a bind-once behavior, one can use an explicit generic parameter: +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: - ```nim +.. code-block:: nim proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U]) - ``` -Once bound, type parameters can appear in the rest of the proc signature: +Once bound, type params can appear in the rest of the proc signature: + +.. code-block:: nim + :test: "nim c $1" - ```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 parameter. This works in practice by +of types that will match the type param. This works in practice by attaching attributes to types via templates. The constraint can be a concrete type or a type class. - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + template maxval(T: typedesc[int]): int = high(int) template maxval(T: typedesc[float]): float = Inf @@ -6515,54 +5789,54 @@ 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 +Passing ``typedesc`` almost identical, just with the differences that the macro is not instantiated generically. The type expression is -simply passed as a `NimNode` to the macro, like everything else. +simply passed as a ``NimNode`` to the macro, like everything else. - ```nim - import std/macros +.. code-block:: nim + + import macros macro forwardType(arg: typedesc): typedesc = - # `arg` is of type `NimNode` + # ``arg`` is of type ``NimNode`` let tmp: NimNode = arg result = tmp var tmp: forwardType(int) - ``` typeof operator --------------- -**Note**: `typeof(x)` can for historical reasons also be written as -`type(x)` but `type(x)` is discouraged. +**Note**: ``typeof(x)`` can for historical reasons also be written as +``type(x)`` but ``type(x)`` is discouraged. -One can obtain the type of a given expression by constructing a `typeof` +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): - ```nim +.. code-block:: 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 -call `c(X)` (where `X` stands for a possibly empty list of arguments), the -interpretation, where `c` is an iterator, is preferred over the +If ``typeof`` is used to determine the result type of a proc/iterator/converter +call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the +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`: +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, this has the type `string`: + # since an iterator is the preferred interpretation, `y` has the type ``string``: assert typeof("a b c".split) is string assert typeof("a b c".split, typeOfProc) is seq[string] - ``` @@ -6571,35 +5845,34 @@ 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 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 +A module may gain access to symbols of another module by the `import`:idx: +statement. `Recursive module dependencies`:idx: are allowed, but 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 filename is ``identifier.nim``). The algorithm for compiling modules is: -- Compile the whole module as usual, following import statements recursively. +- compile the whole module as usual, following import statements recursively -- If there is a cycle, only import the already parsed symbols (that are - exported); if an unknown identifier occurs then abort. +- if there is a cycle only import the already parsed symbols (that are + exported); if an unknown identifier occurs then abort This is best illustrated by an example: - ```nim +.. code-block:: nim # Module A type - T1* = int # Module A exports the type `T1` + T1* = int # Module A exports the type ``T1`` import B # the compiler starts parsing B proc main() = var i = p(3) # works because B has been parsed completely here main() - ``` - ```nim +.. code-block:: nim # Module B import A # A is not parsed here! Only the already known symbols # of A are imported. @@ -6608,182 +5881,170 @@ 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` keyword, a list of module names can follow or a single -module name followed by an `except` list to prevent some symbols from being +After the ``import`` statement a list of module names can follow or a single +module name followed by an ``except`` list to prevent some symbols to be imported: - ```nim test = "nim c $1" status = 1 - import std/strutils except `%`, toUpperAscii +.. code-block:: nim + :test: "nim c $1" + :status: 1 + + import 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 different versions of the module, -even when one version does not export some of these identifiers. +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. -The `import` statement is only allowed at the top level. +The ``import`` statement is only allowed at the top level. Include statement ------------------ - -The `include` statement does something fundamentally different than -importing a module: it merely includes the contents of a file. The `include` +~~~~~~~~~~~~~~~~~ +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: - ```nim +.. code-block:: nim include fileA, fileB, fileC - ``` -The `include` statement can be used outside the top level, as such: +The ``include`` statement can be used outside of the top level, as such: - ```nim +.. code-block:: nim # Module A echo "Hello World!" - ``` - ```nim +.. code-block:: nim # Module B proc main() = include A main() # => Hello World! - ``` Module names in imports ------------------------ +~~~~~~~~~~~~~~~~~~~~~~~ -A module alias can be introduced via the `as` keyword, after which the original module name -is inaccessible: +A module alias can be introduced via the ``as`` keyword: - ```nim - import std/strutils as su, std/sequtils as qu +.. code-block:: nim + import strutils as su, sequtils as qu echo su.format("$1", "lalelu") - ``` -The notations `path/to/module` or `"path/to/module"` can be used to refer to a module +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 in subdirectories: - ```nim +.. code-block:: nim import lib/pure/os, "lib/pure/times" - ``` -Note that the module name is still `strutils` and not `lib/pure/strutils`, -thus one **cannot** do: +Note that the module name is still ``strutils`` and not ``lib/pure/strutils`` +and so one **cannot** do: - ```nim +.. code-block:: nim import lib/pure/strutils echo lib/pure/strutils.toUpperAscii("abc") - ``` -Likewise, the following does not make sense as the name is `strutils` already: +Likewise, the following does not make sense as the name is ``strutils`` already: - ```nim +.. code-block:: nim import lib/pure/strutils as strutils - ``` Collective imports from a directory ------------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The syntax `import dir / [moduleA, moduleB]` can be used to import multiple modules +The syntax ``import dir / [moduleA, moduleB]`` can be used to import multiple modules 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: - ```nim +.. code-block:: nim import "gfx/3d/somemodule" # in quotes because '3d' is not a valid Nim identifier - ``` Pseudo import/include paths ---------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~ A directory can also be a so-called "pseudo directory". They can be used to avoid ambiguity when there are multiple modules with the same path. There are two pseudo directories: -1. `std`: The `std` pseudo directory is the abstract location of Nim's standard - library. For example, the syntax `import std / strutils` is used to unambiguously - refer to the standard library's `strutils` module. -2. `pkg`: The `pkg` pseudo directory is used to unambiguously refer to a Nimble - package. However, for technical details that lie outside the scope of this document, - its semantics are: *Use the search path to look for module name but ignore the standard - library locations*. In other words, it is the opposite of `std`. +1. ``std``: The ``std`` pseudo directory is the abstract location of Nim's standard +library. For example, the syntax ``import std / strutils`` is used to unambiguously +refer to the standard library's ``strutils`` module. +2. ``pkg``: The ``pkg`` pseudo directory is used to unambiguously refer to a Nimble +package. However, for technical details that lie outside of the scope of this document +its semantics are: *Use the search path to look for module name but ignore the standard +library locations*. In other words, it is the opposite of ``std``. -It is recommended and preferred but not currently enforced that all stdlib module imports include the std/ "pseudo directory" as part of the import name. From import statement ---------------------- +~~~~~~~~~~~~~~~~~~~~~ -After the `from` keyword, a module name followed by -an `import` to list the symbols one likes to use without explicit +After the ``from`` statement, a module name follows followed by +an ``import`` to list the symbols one likes to use without explicit full qualification: - ```nim test = "nim c $1" - from std/strutils import `%` +.. code-block:: nim + :test: "nim c $1" + + from 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 +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 -in `module`. +in ``module``. Export statement ----------------- +~~~~~~~~~~~~~~~~ -An `export` statement can be used for symbol forwarding so that client +An ``export`` statement can be used for symbol forwarding so that client modules don't need to import a module's dependencies: - ```nim +.. code-block:: nim # module B type MyObject* = object - ``` - ```nim +.. code-block:: nim # module A import B export B.MyObject proc `$`*(x: MyObject): string = "my object" - ``` - ```nim +.. code-block:: 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. +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: - ```nim +.. code-block:: nim import foo/bar/baz export baz - ``` @@ -6794,8 +6055,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, @@ -6805,8 +6066,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: @@ -6814,8 +6075,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. @@ -6824,17 +6085,15 @@ 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: - ```nim +.. code-block:: nim # Module A var x*: string - ``` - ```nim +.. code-block:: nim # Module B var x*: int - ``` - ```nim +.. code-block:: nim # Module C import A, B write(stdout, x) # error: x is ambiguous @@ -6842,20 +6101,6 @@ 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 @@ -6873,7 +6118,7 @@ Pragmas Pragmas are Nim's method to give the compiler additional information / commands without introducing a massive number of new keywords. Pragmas are processed on the fly during semantic checking. Pragmas are enclosed in the -special `{.` and `.}` curly brackets. Pragmas are also often used as a +special ``{.`` and ``.}`` curly brackets. Pragmas are also often used as a first implementation to play with a language feature before a nicer syntax to access the feature becomes available. @@ -6883,45 +6128,73 @@ deprecated pragma The deprecated pragma is used to mark a symbol as deprecated: - ```nim +.. code-block:: nim proc p() {.deprecated.} var x {.deprecated.}: char - ``` This pragma can also take in an optional warning string to relay to developers. - ```nim +.. code-block:: nim proc thing(x: bool) {.deprecated: "use thong instead".} - ``` +noSideEffect pragma +------------------- + +The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side +effects. This means that the proc/iterator only changes locations that are +reachable from its parameters and the return value only depends on the +arguments. If none of its parameters have the type ``var T`` or ``ref T`` +or ``ptr T`` this means no locations are modified. 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, +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 + func `+` (x, y: int): int + + +To override the compiler's side effect analysis a ``{.noSideEffect.}`` +``cast`` pragma block can be used: + +.. code-block:: nim + + func f() = + {.cast(noSideEffect).}: + echo "test" + compileTime pragma ------------------ -The `compileTime` pragma is used to mark a proc or variable to be used only +The ``compileTime`` pragma is used to mark a proc or variable to be used only during compile-time execution. No code will be generated for it. Compile-time 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`: +proc that uses ``system.NimNode`` within its parameter types is implicitly +declared ``compileTime``: - ```nim +.. code-block:: nim proc astHelper(n: NimNode): NimNode = result = n - ``` Is the same as: - ```nim +.. code-block:: nim proc astHelper(n: NimNode): NimNode {.compileTime.} = result = n - ``` -`compileTime` variables are available at runtime too. This simplifies certain +``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: - ```nim test = "nim c -r $1" - import std/macros +.. code-block:: nim + :test: "nim c -r $1" + + import macros var nameToProc {.compileTime.}: seq[(string, proc (): string {.nimcall.})] @@ -6938,64 +6211,61 @@ but accessed at runtime: proc baz: string {.registerProc.} = "baz" doAssert nameToProc[2][1]() == "baz" - ``` -noreturn pragma +noReturn pragma --------------- -The `noreturn` pragma is used to mark a proc that never returns. +The ``noreturn`` pragma is used to mark a proc that never returns. acyclic pragma -------------- -The `acyclic` pragma can be used for object types to mark them as acyclic +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: - ```nim +.. code-block:: nim type Node = ref NodeObj NodeObj {.acyclic.} = object left, right: Node data: string - ``` Or if we directly use a ref object: - ```nim +.. code-block:: 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 +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 -this type may form a cyclic graph. The `acyclic` pragma passes the +this type may form a cyclic graph. The ``acyclic`` pragma passes the information that this cannot happen to the GC. If the programmer uses the -`acyclic` pragma for data types that are in reality cyclic, this may result -in memory leaks, but memory safety is preserved. +``acyclic`` pragma for data types that are in reality cyclic, the memory leaks +can be the result, but memory safety is preserved. final pragma ------------ -The `final` pragma can be used for an object type to specify that it +The ``final`` pragma can be used for an object type to specify that it cannot be inherited from. Note that inheritance is only available for -objects that inherit from an existing object (via the `object of SuperType` -syntax) or that have been marked as `inheritable`. +objects that inherit from an existing object (via the ``object of SuperType`` +syntax) or that have been marked as ``inheritable``. shallow pragma -------------- -The `shallow` pragma affects the semantics of a type: The compiler is +The ``shallow`` pragma affects the semantics of a type: The compiler is allowed to make a shallow copy. This can cause serious semantic issues and break memory safety! However, it can speed up assignments considerably, 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: - ```nim +.. code-block:: nim type NodeKind = enum nkLeaf, nkInner Node {.shallow.} = object @@ -7004,25 +6274,24 @@ structure: strVal: string of nkInner: children: seq[Node] - ``` pure pragma ----------- -An object type can be marked with the `pure` pragma so that its type field +An object type can be marked with the ``pure`` pragma so that its type field which is used for runtime type identification is omitted. This used to be necessary for binary compatibility with other compiled languages. -An enum type can be marked as `pure`. Then access of its fields always +An enum type can be marked as ``pure``. Then access of its fields always requires full qualification. asmNoStackFrame pragma ---------------------- -A proc can be marked with the `asmNoStackFrame` pragma to tell the compiler +A proc can be marked with the ``asmNoStackFrame`` pragma to tell the compiler it should not generate a stack frame for the proc. There are also no exit -statements like `return result;` generated and the generated C function is -declared as `__declspec(naked)`:c: or `__attribute__((naked))`:c: (depending on +statements like ``return result;`` generated and the generated C function is +declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on the used C compiler). **Note**: This pragma should only be used by procs which consist solely of @@ -7030,67 +6299,65 @@ assembler statements. error pragma ------------ -The `error` pragma is used to make the compiler output an error message +The ``error`` pragma is used to make the compiler output an error message with the given content. The compilation does not necessarily abort after an error though. -The `error` pragma can also be used to +The ``error`` pragma can also be used to 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: - ```nim +.. code-block:: nim ## check that underlying int values are compared and not the pointers: proc `==`(x, y: ptr int): bool {.error.} - ``` fatal pragma ------------ -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 +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: - ```nim +.. code-block:: nim when not defined(objc): {.fatal: "Compile this program with the objc command!".} - ``` warning pragma -------------- -The `warning` pragma is used to make the compiler output a warning message +The ``warning`` pragma is used to make the compiler output a warning message with the given content. Compilation continues after the warning. hint pragma ----------- -The `hint` pragma is used to make the compiler output a hint message with +The ``hint`` pragma is used to make the compiler output a hint message with the given content. Compilation continues after the hint. line pragma ----------- -The `line` pragma can be used to affect line information of the annotated +The ``line`` pragma can be used to affect line information of the annotated statement, as seen in stack backtraces: - ```nim +.. code-block:: nim + template myassert*(cond: untyped, msg = "") = if not cond: # change run-time line information of the 'raise' statement: {.line: instantiationInfo().}: - raise newException(AssertionDefect, msg) - ``` + raise newException(EAssertionFailed, msg) -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. +If the ``line`` pragma is used with a parameter, the parameter needs be a +``tuple[filename: string, line: int]``. If it is used without a parameter, +``system.InstantiationInfo()`` is used. linearScanEnd pragma -------------------- -The `linearScanEnd` pragma can be used to tell the compiler how to +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: - ```nim +.. code-block:: nim case myInt of 0: echo "most common case" @@ -7099,27 +6366,27 @@ 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 +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 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 stall. -The `linearScanEnd` pragma should be put into the last branch that should be +The ``linearScanEnd`` pragma should be put into the last branch that should be tested against via linear scanning. If put into the last branch of the -whole `case` statement, the whole `case` statement uses linear scanning. +whole ``case`` statement, the whole ``case`` statement uses linear scanning. computedGoto pragma ------------------- -The `computedGoto` pragma can be used to tell the compiler how to -compile a Nim `case`:idx: in a `while true` statement. +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: - ```nim +.. code-block:: nim + type MyEnum = enum enumA, enumB, enumC, enumD, enumE @@ -7151,9 +6418,8 @@ 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 +As the example shows ``computedGoto`` is mostly useful for interpreters. If the underlying backend (C compiler) does not support the computed goto extension the pragma is simply ignored. @@ -7161,24 +6427,9 @@ extension the pragma is simply ignored. immediate pragma ---------------- -The immediate pragma is obsolete. See [Typed vs untyped parameters]. +The immediate pragma is obsolete. See `Typed vs untyped parameters +<#templates-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 -------------------------- @@ -7216,10 +6467,9 @@ callconv cdecl|... Specifies the default calling convention for Example: - ```nim +.. code-block:: nim {.checks: off, optimization: speed.} # compile without runtime checks and optimize for speed - ``` push and pop pragmas @@ -7227,17 +6477,16 @@ 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: - ```nim +.. code-block:: 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: - ```nim +.. code-block:: nim {.push inline.} proc thisIsInlined(): int = 42 func willBeInlined(): float = 42.0 @@ -7251,33 +6500,31 @@ 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. register pragma --------------- -The `register` pragma is for variables only. It declares the variable as -`register`, giving the compiler a hint that the variable should be placed +The ``register`` pragma is for variables only. It declares the variable as +``register``, giving the compiler a hint that the variable should be placed in a hardware register for faster access. C compilers usually ignore this though and for good reasons: Often they do a better job without it anyway. -However, in highly specific cases (a dispatch loop of a bytecode interpreter -for example) it may provide benefits. +In highly specific cases (a dispatch loop of a bytecode interpreter for +example) it may provide benefits, though. global pragma ------------- -The `global` pragma can be applied to a variable within a proc to instruct +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. - ```nim +.. code-block:: 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 @@ -7289,13 +6536,11 @@ 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 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: +and warning message contains a symbol in brackets. This is the message's +identifier that can be used to enable or disable it: - ```Nim +.. code-block:: Nim {.hint[LineTooLong]: off.} # turn off the hint about too long lines - ``` This is often better than disabling all warnings at once. @@ -7304,10 +6549,10 @@ used pragma ----------- 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 +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: - ```nim +.. code-block:: nim template implementArithOps(T) = proc echoAdd(a, b: T) {.used.} = echo a + b @@ -7317,34 +6562,32 @@ 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". +``used`` can also be used as a top-level statement to mark a module as "used". This prevents the "Unused import" warning: - ```nim +.. code-block:: 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 ------------------- -The `experimental` pragma enables experimental language features. Depending +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](manual_experimental.html) for more details. +is uncertain (it may be removed at any time). Example: - ```nim - import std/threadpool +.. code-block:: nim + import threadpool {.experimental: "parallel".} proc threadedEcho(s: string, i: int) = @@ -7356,15 +6599,15 @@ Example: spawn threadedEcho("echo in parallel", i) useParallel() - ``` As a top-level statement, the experimental pragma enables a feature for the 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: +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. @@ -7374,13 +6617,12 @@ put into a `.push/pop` environment: echo "echo in parallel" {.pop.} - ``` - ```nim +.. code-block:: nim + import client useParallel(1) - ``` Implementation Specific Pragmas @@ -7392,22 +6634,20 @@ supports but which should not be seen as part of the language specification. Bitsize pragma -------------- -The `bitsize` pragma is for object field members. It declares the field as +The ``bitsize`` pragma is for object field members. It declares the field as a bitfield in C/C++. - ```Nim +.. code-block:: Nim type mybitfield = object flag {.bitsize:1.}: cuint - ``` generates: - ```C +.. code-block:: C struct mybitfield { unsigned int flag:1; }; - ``` Align pragma @@ -7420,121 +6660,101 @@ 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. - ```Nim - type - sseType = object - sseData {.align(16).}: array[4, float32] +.. code-block:: Nim - # every object will be aligned to 128-byte boundary - Data = object - x: char - cacheline {.align(128).}: array[128, char] # over-aligned array of char, + type + sseType = object + sseData {.align(16).}: array[4, float32] - 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 + # every object will be aligned to 128-byte boundary + Data = object + x: char + cacheline {.align(128).}: array[128, char] # over-aligned array of char, - main() - ``` + 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() 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 -the underlying pointer is pointing to a unique location in memory, no other aliases to -this location exist. It is *unchecked* that this alias restriction is followed. If the -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 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 +The ``volatile`` pragma is for variables only. It declares the variable as +``volatile``, 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. -nodecl pragma +NoDecl pragma ------------- -The `nodecl` pragma can be applied to almost any symbol (variable, proc, +The ``noDecl`` pragma can be applied to almost any symbol (variable, proc, 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: - ```Nim +.. code-block:: Nim var - EACCES {.importc, nodecl.}: cint # pretend EACCES was a variable, as + 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. +However, the ``header`` pragma is often the better alternative. **Note**: This will not work for the LLVM backend. Header pragma ------------- -The `header` pragma is very similar to the `nodecl` pragma: It can be +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:\: +and instead, the generated code should contain an ``#include``: - ```Nim +.. code-block:: 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 +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 -in angle brackets: `<>`:c:. If no angle brackets are given, Nim -encloses the header file in `""`:c: in the generated C code. +in angle brackets: ``<>``. If no angle brackets are given, Nim +encloses the header file in ``""`` in the generated C code. **Note**: This will not work for the LLVM backend. IncompleteStruct pragma ----------------------- -The `incompleteStruct` pragma tells the compiler to not use the -underlying C `struct`:c: in a `sizeof` expression: +The ``incompleteStruct`` pragma tells the compiler to not use the +underlying C ``struct`` in a ``sizeof`` expression: - ```Nim +.. code-block:: Nim type DIR* {.importc: "DIR", header: "", pure, incompleteStruct.} = object - ``` Compile pragma -------------- -The `compile` pragma can be used to compile and link a C/C++ source file +The ``compile`` pragma can be used to compile and link a C/C++ source file with the project: - ```Nim +.. code-block:: 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 -the recompilation of the file. +has changed. One can use the ``-f`` command-line option to force the recompilation +of the file. Since 1.4 the `compile` pragma is also available with this syntax: - ```Nim +.. code-block:: 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. @@ -7542,72 +6762,66 @@ that are passed to the C compiler when the file is recompiled. Link pragma ----------- -The `link` pragma can be used to link an additional file with the project: +The ``link`` pragma can be used to link an additional file with the project: - ```Nim +.. code-block:: 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 use the command-line switch `--passc`:option:\: +The ``passc`` pragma can be used to pass additional parameters to the C +compiler like one would using the command-line switch ``--passc``: - ```Nim +.. code-block:: Nim {.passc: "-Wall -Werror".} - ``` -Note that one can use `gorge` from the [system module](system.html) to +Note that one can use ``gorge`` from the `system module `_ to embed parameters from an external command that will be executed during semantic analysis: - ```Nim +.. code-block:: Nim {.passc: gorge("pkg-config --cflags sdl").} - ``` -localPassC pragma +LocalPassc pragma ----------------- -The `localPassC` pragma can be used to pass additional parameters to the C +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: - ```Nim +.. code-block:: Nim # Module A.nim # Produces: A.nim.cpp - {.localPassC: "-Wall -Werror".} # Passed when compiling 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``: - ```Nim - {.passl: "-lSDLmain -lSDL".} - ``` +.. code-block:: Nim + {.passL: "-lSDLmain -lSDL".} -Note that one can use `gorge` from the [system module](system.html) to +Note that one can use ``gorge`` from the `system module `_ to embed parameters from an external command that will be executed during semantic analysis: - ```Nim - {.passl: gorge("pkg-config --libs sdl").} - ``` +.. code-block:: Nim + {.passL: gorge("pkg-config --libs sdl").} Emit pragma ----------- -The `emit` pragma can be used to directly affect the output of the +The ``emit`` pragma can be used to directly affect the output of the compiler's code generator. The code is then unportable to other code generators/backends. Its usage is highly discouraged! However, it can be extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code. Example: - ```Nim +.. code-block:: Nim {.emit: """ static int cvariable = 420; """.} @@ -7620,29 +6834,27 @@ 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.: +``nimbase.h`` defines ``NIM_EXTERNC`` C macro that can be used for +``extern "C"`` code to work with both ``nim c`` and ``nim cpp``, e.g.: - ```Nim +.. code-block:: 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. - This usage is however deprecated. +For backward compatibility, if the argument to the ``emit`` statement +is a single string literal, Nim symbols can be referred to via backticks. +This usage is however deprecated. -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:\: +For a toplevel emit statement the section where in the generated C/C++ file +the code should be emitted can be influenced via the +prefixes ``/*TYPESECTION*/`` or ``/*VARSECTION*/`` or ``/*INCLUDESECTION*/``: - ```Nim +.. code-block:: Nim {.emit: """/*TYPESECTION*/ struct Vector3 { public: @@ -7656,25 +6868,24 @@ 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](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 +**Note**: `c2nim `_ 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, the -`importcpp` pragma can be used to import `C++`:idx: methods or C++ symbols +Similar to the `importc pragma for C +<#foreign-function-interface-importc-pragma>`_, 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` +syntax: ``obj->method(arg)``. In combination with the ``header`` and ``emit`` pragmas this allows *sloppy* interfacing with libraries written in C++: - ```Nim +.. code-block:: Nim # Horrible example of how to interface with a C++ engine ... ;-) {.link: "/usr/lib/libIrrlicht.so".} @@ -7700,147 +6911,138 @@ 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 +The compiler needs to be told to generate C++ (command ``cpp``) 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: +The *sloppy interfacing* example uses ``.emit`` to produce ``using namespace`` declarations. It is usually much better to instead refer to the imported name -via the `namespace::identifier`:cpp: notation: +via the ``namespace::identifier`` notation: - ```nim +.. code-block:: 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: -`((TheCppEnum)(3))`:cpp:. +When ``importcpp`` is applied to an enum type the numerical enum values are +annotated with the C++ enum type, like in this example: ``((TheCppEnum)(3))``. (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 +Note that the ``importcpp`` variant for procs uses a somewhat cryptic pattern language for maximum flexibility: - A hash ``#`` symbol is replaced by the first or next argument. - A dot following the hash ``#.`` indicates that the call should use C++'s dot or arrow notation. -- An at symbol ``@`` is replaced by the remaining arguments, - separated by commas. +- An at symbol ``@`` is replaced by the remaining arguments, separated by commas. For example: - ```nim +.. code-block:: nim proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "#.CppMethod(@)".} var x: ptr CppObj cppMethod(x[], 1, 2, 3) - ``` Produces: - ```C +.. code-block:: 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 +``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: - ```nim +.. code-block:: nim proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "CppMethod".} - ``` Note that the pattern language naturally also covers C++'s operator overloading capabilities: - ```nim +.. code-block:: 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 is replaced by the i'th parameter *type*. The 0th position is the result type. This can be used to pass types to C++ function templates. Between the ``'`` and the digit, an asterisk can be used to get to the base type - of the type. (So it "takes away a star" from the type; `T*`:c: becomes `T`.) + of the type. (So it "takes away a star" from the type; ``T*`` becomes ``T``.) Two stars can be used to get to the element type of the element type etc. For example: - ```nim +.. code-block:: nim + type Input {.importcpp: "System::Input".} = object proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.} let x: ptr Input = getSubsystem[Input]() - ``` Produces: - ```C +.. code-block:: C x = SystemManager::getSubsystem() - ``` -- ``#@`` is a special case to support a `cnew` operation. It is required so +- ``#@`` is a special case to support a ``cnew`` operation. It is required so that the call expression is inlined directly, without going through a temporary location. This is only required to circumvent a limitation of the current code generator. -For example C++'s `new`:cpp: operator can be "imported" like this: +For example C++'s ``new`` operator can be "imported" like this: - ```nim +.. code-block:: 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: - ```C +.. code-block:: C x = new Foo(3, 4) - ``` -However, depending on the use case `new Foo`:cpp: can also be wrapped like this +However, depending on the use case ``new Foo`` can also be wrapped like this instead: - ```nim +.. code-block:: 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 -`Class c(1,2);`:cpp:. +``Class c = Class(1,2);`` must not be generated but instead ``Class c(1,2);``. 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: - ```nim +.. code-block:: 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 @@ -7848,18 +7050,20 @@ 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: - ```nim +.. code-block:: 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 +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: - ```nim test = "nim cpp $1" +.. code-block:: 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) {. @@ -7867,57 +7071,56 @@ language for object types: var x: StdMap[cint, cdouble] x[6] = 91.4 - ``` Produces: - ```C +.. code-block:: C std::map x; x[6] = 91.4; - ``` -- If more precise control is needed, the apostrophe `'` can be used in the +- 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. - ```nim - type - VectorIterator[T] {.importcpp: "std::vector<'0>::iterator".} = object +.. code-block:: nim - var x: VectorIterator[cint] - ``` + type + VectorIterator {.importcpp: "std::vector<'0>::iterator".} [T] = object - Produces: + var x: VectorIterator[cint] - ```C - std::vector::iterator x; - ``` +Produces: + +.. code-block:: C + + std::vector::iterator x; ImportJs pragma --------------- -Similar to the [importcpp pragma] for C++, -the `importjs` pragma can be used to import Javascript methods or +Similar to the `importcpp pragma for C++ <#implementation-specific-pragmas-importcpp-pragma>`_, +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)``. ImportObjC pragma ----------------- -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 +Similar to the `importc pragma for C +<#foreign-function-interface-importc-pragma>`_, 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 +In addition with the ``header`` and ``emit`` pragmas this allows *sloppy* interfacing with libraries written in Objective C: - ```Nim +.. code-block:: Nim # horrible example of how to interface with GNUStep ... - {.passl: "-lobjc".} + {.passL: "-lobjc".} {.emit: """ #include @interface Greeter:Object @@ -7949,9 +7152,8 @@ 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 +The compiler needs to be told to generate Objective C (command ``objc``) for this to work. The conditional symbol ``objc`` is defined when the compiler emits Objective C code. @@ -7959,65 +7161,56 @@ emits Objective C code. CodegenDecl pragma ------------------ -The `codegenDecl` pragma can be used to directly influence Nim's code +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, -$2 is the name of the variable, and each appearance of $# represents $1/$2 -respectively according to its position. +For variables, $1 in the format string represents the type of the variable +and $2 is the name of the variable. The following Nim code: - ```nim +.. code-block:: nim var a {.codegenDecl: "$# progmem $#".}: int - ``` will generate this C code: - ```c +.. code-block:: c int progmem a - ``` -For procedures, $1 is the return type of the procedure, $2 is the name of -the procedure, $3 is the parameter list, and each appearance of $# represents -$1/$2/$3 respectively according to its position. +For procedures $1 is the return type of the procedure, $2 is the name of +the procedure and $3 is the parameter list. The following nim code: - ```nim +.. code-block:: nim proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} = echo "realistic interrupt handler" - ``` will generate this code: - ```c +.. code-block:: c __interrupt void myinterrupt() - ``` -`cppNonPod` pragma ------------------- +InjectStmt pragma +----------------- -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:. +The ``injectStmt`` pragma can be used to inject a statement before every +other statement in the current module. It is only supposed to be used for +debugging: - ```nim - type Foo {.cppNonPod, importcpp, header: "funs.h".} = object - x: cint - proc main()= - var a {.threadvar.}: Foo - ``` +.. code-block:: nim + {.injectStmt: gcInvariants().} + # ... complex code here that produces crashes ... compile-time define pragmas --------------------------- The pragmas listed here can be used to optionally accept values from -the `-d/--define`:option: option at compile time. +the -d/--define option at compile time. The implementation currently provides the following possible options (various others may be added later). @@ -8030,40 +7223,19 @@ pragma description `booldefine`:idx: Reads in a build-time define as a bool ================= ============================================ - ```nim - const FooBar {.intdefine.}: int = 5 - echo FooBar - ``` +.. code-block:: nim + const FooBar {.intdefine.}: int = 5 + echo FooBar - ```cmd - nim c -d:FooBar=42 foobar.nim - ``` +:: + 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 -`-d:FooBar=42`:option: were to be omitted, the default value of 5 would be +In the above example, providing the -d flag causes the symbol +``FooBar`` to be overwritten at compile-time, printing out 42. If the +``-d:FooBar=42`` were to be omitted, the default value of 5 would be used. To see if a value was provided, `defined(FooBar)` can be used. -The syntax `-d:flag`:option: is actually just a shortcut for -`-d:flag=true`:option:. - -These pragmas also accept an optional string argument for qualified -define names. - - ```nim - const FooBar {.intdefine: "package.FooBar".}: int = 5 - echo FooBar - ``` - - ```cmd - nim c -d:package.FooBar=42 foobar.nim - ``` - -This helps disambiguate define names in different packages. - -See also the [generic `define` pragma](manual_experimental.html#generic-define-pragma) -for a version of these pragmas that detects the type of the define based on -the constant value. +The syntax `-d:flag` is actually just a shortcut for `-d:flag=true`. User-defined pragmas ==================== @@ -8072,24 +7244,22 @@ User-defined pragmas pragma pragma ------------- -The `pragma` pragma can be used to declare user-defined pragmas. This is -useful because Nim's templates and macros do not affect pragmas. -User-defined pragmas are in a different module-wide scope than all other symbols. +The ``pragma`` pragma can be used to declare user-defined pragmas. This is +useful because Nim's templates and macros do not affect pragmas. User-defined pragmas are in a different module-wide scope than all other symbols. They cannot be imported from a module. Example: - ```nim +.. code-block:: nim when appType == "lib": {.pragma: rtl, exportc, dynlib, cdecl.} else: {.pragma: rtl, importc, dynlib: "client.dll", cdecl.} proc p*(a, b: int): int {.rtl.} = - result = a + b - ``` + result = a+b -In the example, a new pragma named `rtl` is introduced that either imports +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 generation. @@ -8098,20 +7268,18 @@ Custom annotations ------------------ 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`: +Custom pragmas are defined using templates annotated with pragma ``pragma``: - ```nim +.. code-block:: 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: +Consider stylized example of possible Object Relation Mapping (ORM) implementation: - ```nim +.. code-block:: nim const tblspace {.strdefine.} = "dev" # switch for dev, test and prod environments type @@ -8126,8 +7294,7 @@ implementation: user_id {.dbForeignKey: User.}: int read_access: bool write_access: bool - admin_access: bool - ``` + admin_acess: 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 @@ -8140,78 +7307,74 @@ Custom pragmas can be used in all locations where ordinary pragmas can be specified. It is possible to annotate procs, templates, type and variable definitions, statements, etc. -The macros module includes helpers which can be used to simplify custom pragma -access `hasCustomPragma`, `getCustomPragmaVal`. Please consult the -[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. +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 magic, everything they do can +also be achieved by walking the AST of the object representation. More examples with custom pragmas: - Better serialization/deserialization control: - ```nim - type MyObj = object - a {.dontSerialize.}: int - b {.defaultDeserialize: 5.}: int - c {.serializationKey: "_c".}: string - ``` +.. code-block:: nim + type MyObj = object + a {.dontSerialize.}: int + b {.defaultDeserialize: 5.}: int + c {.serializationKey: "_c".}: string - Adopting type for gui inspector in a game engine: - ```nim - type MyComponent = object - position {.editable, animatable.}: Vector3 - alpha {.editRange: [0.0..1.0], animatable.}: float32 - ``` +.. code-block:: 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.) -declarations or routine type expressions. The compiler will perform the -following simple syntactic transformations: +All macros and templates can also be used as pragmas. They can be attached +to routines (procs, iterators, etc), type names, or type expressions. The +compiler will perform the following simple syntactic transformations: - ```nim +.. code-block:: nim template command(name: string, def: untyped) = discard proc p() {.command("print").} = discard - ``` This is translated to: - ```nim +.. code-block:: nim command("print"): proc p() = discard - ``` ------ - ```nim +.. code-block:: nim type AsyncEventHandler = proc (x: Event) {.async.} - ``` This is translated to: - ```nim +.. code-block:: nim type AsyncEventHandler = async(proc (x: Event)) - ``` ------ -When multiple macro pragmas are applied to the same definition, the first one -from left to right will be evaluated. This macro can then choose to keep -the remaining macro pragmas in its output, and those will be evaluated in -the same way. +.. code-block:: nim + type + MyObject {.schema: "schema.protobuf".} = object + +This is translated to a call to the ``schema`` macro with a `nnkTypeDef` +AST node capturing both the left-hand side and right-hand side of the +definition. The macro can return a potentially modified `nnkTypeDef` tree +which will replace the original row in the type section. + +When multiple macro pragmas are applied to the same definition, the +compiler will apply them consequently from left to right. Each macro +will receive as input the output of the previous one. -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]( -manual_experimental.html#extended-macro-pragmas) instead. Foreign function interface @@ -8224,128 +7387,117 @@ are documented here. Importc pragma -------------- -The `importc` pragma provides a means to import a proc or a variable +The ``importc`` pragma provides a means to import a proc or a variable 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*: - ```nim +.. code-block:: 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:\: +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``: - ```nim +.. code-block:: {.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 +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][importcpp pragma] - * [importobjc][importobjc pragma] - * [importjs][importjs pragma] + * `importcpp `_ + * `importobjc `_ + * `importjs `_ -The string literal passed to `importc` can be a format string: - - ```Nim +.. code-block:: Nim proc p(s: cstring) {.importc: "prefix$1".} - ``` -In the example, the external name of `p` is set to `prefixp`. Only ``$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 ``$$``. Exportc pragma -------------- -The `exportc` pragma provides a means to export a type, a variable, or a +The ``exportc`` pragma provides a means to export a type, a variable, or a 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 +is a string containing the C identifier. If the argument is missing, the C name is the Nim identifier *exactly as spelled*: - ```Nim +.. code-block:: 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: +The string literal passed to ``exportc`` can be a format string: - ```Nim +.. code-block:: Nim proc p(s: string) {.exportc: "prefix$1".} = echo s - ``` -In the example, the external name of `p` is set to `prefixp`. Only ``$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 ``$$``. -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]. +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>`_. Extern pragma ------------- -Like `exportc` or `importc`, the `extern` pragma affects name -mangling. The string literal passed to `extern` can be a format string: +Like ``exportc`` or ``importc``, the ``extern`` pragma affects name +mangling. The string literal passed to ``extern`` can be a format string: - ```Nim +.. code-block:: Nim proc p(s: string) {.extern: "prefix$1".} = echo s - ``` -In the example, the external name of `p` is set to `prefixp`. Only ``$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 ``$$``. Bycopy pragma ------------- -The `bycopy` pragma can be applied to an object or tuple type and +The ``bycopy`` pragma can be applied to an object or tuple type and instructs the compiler to pass the type by value to procs: - ```nim +.. code-block:: 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. Byref pragma ------------ -The `byref` pragma can be applied to an object or tuple type and instructs +The ``byref`` pragma can be applied to an object or tuple type and instructs the compiler to pass the type by reference (hidden pointer) to procs. Varargs pragma -------------- -The `varargs` pragma can be applied to procedures only (and procedure +The ``varargs`` pragma can be applied to procedures only (and procedure 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: - ```Nim +.. code-block:: 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 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 +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`` +instead of a ``struct`` 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. @@ -8355,7 +7507,7 @@ should scan unions conservatively. Packed pragma ------------- -The `packed` pragma can be applied to any `object` type. It ensures +The ``packed`` pragma can be applied to any ``object`` type. It ensures that the fields of an object are packed back-to-back in memory. It is useful to store packets or messages from/to network or hardware drivers, and for interoperability with C. Combining packed pragma with inheritance is not @@ -8367,42 +7519,40 @@ a static error. Usage with inheritance should be defined and documented. Dynlib pragma for import ------------------------ -With the `dynlib` pragma, a procedure or a variable can be imported from +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: - ```Nim +.. code-block:: 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* packages need to be installed. -The `dynlib` import mechanism supports a versioning scheme: +The ``dynlib`` import mechanism supports a versioning scheme: - ```nim +.. code-block:: 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): +At runtime the dynamic library is searched for (in this order):: - libtcl.so.1 - libtcl.so.0 - libtcl8.5.so.1 - libtcl8.5.so.0 - libtcl8.4.so.1 - libtcl8.4.so.0 - libtcl8.3.so.1 - libtcl8.3.so.0 + libtcl.so.1 + libtcl.so.0 + libtcl8.5.so.1 + libtcl8.5.so.0 + libtcl8.4.so.1 + libtcl8.4.so.0 + libtcl8.3.so.1 + libtcl8.3.so.0 -The `dynlib` pragma supports not only constant strings as an argument but also +The ``dynlib`` pragma supports not only constant strings as an argument but also string expressions in general: - ```nim - import std/os +.. code-block:: nim + import os proc getDllName: string = result = "mylib.dll" @@ -8412,71 +7562,113 @@ 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. -**Note**: Passing variables to the `dynlib` pragma will fail at runtime +**Note**: Passing variables to the ``dynlib`` pragma will fail at runtime 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](nimc.html) contains further information. +**Note**: A ``dynlib`` import can be overridden with +the ``--dynlibOverride:name`` command-line option. The Compiler User Guide +contains further information. Dynlib pragma for export ------------------------ -With the `dynlib` pragma, a procedure can also be exported to +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: +conjunction with the ``exportc`` pragma: - ```Nim +.. code-block:: 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. +``--app:lib`` command-line option. + Threads ======= -The `--threads:on`:option: command-line switch is enabled by default. The [typedthreads module](typedthreads.html) module then contains several threading primitives. See [spawn](manual_experimental.html#parallel-amp-spawn) for +To enable thread support the ``--threads:on`` command-line switch needs to +be used. The ``system`` module then contains several threading primitives. +See the `threads `_ and `channels `_ modules +for the low-level thread API. There are also high-level parallelism constructs +available. See `spawn `_ for further details. -The only way to create a thread is via `spawn` or -`createThread`. The invoked proc must not use `var` parameters nor must -any of its parameters contain a `ref` or `closure` type. This enforces -the *no heap sharing restriction*. +Nim's memory model for threads is quite different than that of other common +programming languages (C, Pascal, Java): Each thread has its own (garbage +collected) heap, and sharing of memory is restricted to global variables. This +helps to prevent race conditions. GC efficiency is improved quite a lot, +because the GC never has to stop other threads and see what they reference. + Thread pragma ------------- A proc that is executed as a new thread of execution should be marked by the -`thread` pragma for reasons of readability. The compiler checks for +``thread`` pragma for reasons of readability. The compiler checks for 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 can be passed to `createThread` or `spawn`. +A thread proc is passed to ``createThread`` or ``spawn`` and invoked +indirectly; so the ``thread`` pragma implies ``procvar``. +GC safety +--------- + +We call a proc ``p`` `GC safe`:idx: when it doesn't access any global variable +that contains GC'ed memory (``string``, ``seq``, ``ref`` or a closure) either +directly or indirectly through a call to a GC unsafe proc. + +The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe, +otherwise this property is inferred by the compiler. Note that ``noSideEffect`` +implies ``gcsafe``. The only way to create a thread is via ``spawn`` or +``createThread``. The invoked proc must not use ``var`` parameters nor must +any of its parameters contain a ``ref`` or ``closure`` type. This enforces +the *no heap sharing restriction*. + +Routines that are imported from C are always assumed to be ``gcsafe``. +To disable the GC-safety checking the ``--threadAnalysis:off`` command-line +switch can be used. This is a temporary workaround to ease the porting effort +from old code to the new threading model. + +To override the compiler's gcsafety analysis a ``{.cast(gcsafe).}`` pragma block can +be used: + +.. code-block:: nim + + var + someGlobal: string = "some string here" + perThread {.threadvar.}: string + + proc setPerThread() = + {.cast(gcsafe).}: + deepCopy(perThread, someGlobal) + + +See also: + +- `Shared heap memory management. `_. + Threadvar pragma ---------------- -A variable can be marked with the `threadvar` pragma, which makes it a +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. +of the ``global`` pragma. - ```nim +.. code-block:: 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 +Due to implementation restrictions thread-local variables cannot be +initialized within the ``var`` section. (Every thread-local variable needs to be replicated at thread creation.) @@ -8485,141 +7677,4 @@ Threads and exceptions The interaction between threads and exceptions is simple: A *handled* exception in one thread cannot affect any other thread. However, an *unhandled* exception -in one thread terminates the whole *process*. - - -Guards and locks -================ - -Nim provides common low level concurrency mechanisms like locks, atomic -intrinsics or condition variables. - -Nim significantly improves on the safety of these features via additional -pragmas: - -1) A `guard`:idx: annotation is introduced to prevent data races. -2) Every access of a guarded memory location needs to happen in an - appropriate `locks`:idx: statement. - - -Guards and locks sections -------------------------- - -### Protecting global variables - -Object fields and global variables can be annotated via a `guard` pragma: - - ```nim - import std/locks - - var glock: Lock - var gdata {.guard: glock.}: int - ``` - -The compiler then ensures that every access of `gdata` is within a `locks` -section: - - ```nim - proc invalid = - # invalid: unguarded access: - echo gdata - - proc valid = - # 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 -is executed before any concurrent action happens. - -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: - - ```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: - - ```nim - var dummyLock {.compileTime.}: int - var atomicCounter {.guard: dummyLock.}: int - - template atomicRead(x): untyped = - {.locks: [dummyLock].}: - memoryReadBarrier() - 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](manual_experimental.md#lock-levels) section -of experimental manual. - - -### 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 expressiveness of the language: - - ```nim - import std/locks - - type - ProtectedCounter = object - v {.guard: L.}: int - 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: - - ```nim - proc incCounters(counters: var openArray[ProtectedCounter]) = - for i in 0..counters.high: - pthread_mutex_lock(counters[i].L) - {.locks: [counters[i].L].}: - try: - 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 -`path analysis`:idx: because it deals with paths to locations -like `obj.field[i].fieldB[j]`. - -The path analysis is **currently unsound**, but that doesn't make it useless. -Two paths are considered equivalent if they are syntactically the same. - -This means the following compiles (for now) even though it really should not: - - ```nim - {.locks: [a[i].L].}: - inc i - access a[i].v - ``` +in one thread terminates the whole *process*! diff --git a/doc/manual/var_t_return.md b/doc/manual/var_t_return.rst similarity index 55% rename from doc/manual/var_t_return.md rename to doc/manual/var_t_return.rst index 15d908c74..c6de8cf7c 100644 --- a/doc/manual/var_t_return.md +++ b/doc/manual/var_t_return.rst @@ -1,12 +1,9 @@ -.. default-role:: code -.. include:: ../rstcommon.rst - -Memory safety for returning by `var T` is ensured by a simple borrowing -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) +Memory safety for returning by ``var T`` is ensured by a simple borrowing +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: - ```nim +.. code-block:: nim proc forward[T](x: var T): var T = result = x # ok, derived from the first parameter. @@ -14,11 +11,10 @@ then it has to be derived from the routine's first parameter: var x: int # we know 'forward' provides a view into the location derived from # its first argument 'x'. - result = forward(x) # Error: location is derived from `x` + 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 +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 memory safety at the call site. diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md deleted file mode 100644 index 2a56c1354..000000000 --- a/doc/manual_experimental.md +++ /dev/null @@ -1,2012 +0,0 @@ -========================= -Nim Experimental Features -========================= - -:Authors: Andreas Rumpf -:Version: |nimversion| - -.. default-role:: code -.. include:: rstcommon.rst -.. contents:: - - -About this document -=================== - -This document describes features of Nim that are to be considered experimental. -Some of these are not covered by the `.experimental` pragma or -`--experimental`:option: switch because they are already behind a special syntax and -one may want to use Nim libraries using these features without using them -oneself. - -.. note:: Unless otherwise indicated, these features are not to be removed, - but refined and overhauled. - - -Void type -========= - -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: - - ```nim - proc nothing(x, y: void): void = - echo "ha" - - nothing() # writes "ha" to stdout - ``` - -The `void` type is particularly useful for generic code: - - ```nim - proc callProc[T](p: proc (x: T), x: T) = - when T is void: - p() - else: - p(x) - - proc intProc(x: int) = discard - proc emptyProc() = discard - - callProc[int](intProc, 12) - callProc[void](emptyProc) - ``` - -However, a `void` type cannot be inferred in generic code: - - ```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`. - -Generic `define` pragma -======================= - -Aside the [typed define pragmas for constants](manual.html#implementation-specific-pragmas-compileminustime-define-pragmas), -there is a generic `{.define.}` pragma that interprets the value of the define -based on the type of the constant value. - - ```nim - const foo {.define: "package.foo".} = 123 - const bar {.define: "package.bar".} = false - ``` - - ```cmd - nim c -d:package.foo=456 -d:package.bar foobar.nim - ``` - -The following types are supported: - -* `string` and `cstring` -* Signed and unsigned integer types -* `bool` -* Enums - -Top-down type inference -======================= - -In expressions such as: - -```nim -let a: T = ex -``` - -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. - -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") -``` - -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. - -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. - -The extent of this varies, but there are some notable special cases. - -Sequence literals ------------------ - -Top-down type inference applies to sequence literals. - -```nim -let x: seq[seq[float]] = @[@[1, 2, 3], @[4, 5, 6]] -``` - -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. - -```nim -proc `@`(x: string): string = "@" & x - -# does not compile: -let x: seq[float] = @[1, 2, 3] -# compiles: -let x: seq[float] = system.`@`([1, 2, 3]) -``` - - -Package level objects -===================== - -Every Nim module resides in a (nimble) package. An object type can be attached -to the package it resides in. If that is done, the type can be referenced from -other modules as an `incomplete`:idx: object type. This feature allows to -break up recursive type dependencies across module boundaries. Incomplete -object types are always passed `byref` and can only be used in pointer like -contexts (`var/ref/ptr IncompleteObject`) in general, since the compiler does -not yet know the size of the object. To complete an incomplete object, -the `package` pragma has to be used. `package` implies `byref`. - -As long as a type `T` is incomplete, no runtime type information for `T` is -available. - - -Example: - - ```nim - # module A (in an arbitrary package) - type - Pack.SomeObject = object # declare as incomplete object of package 'Pack' - Triple = object - a, b, c: ref SomeObject # pointers to incomplete objects are allowed - - # Incomplete objects can be used as parameters: - proc myproc(x: SomeObject) = discard - ``` - - - ```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. - - -Importing private symbols -========================= - -In some situations, it may be useful to import all symbols (public or private) -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](importutils.html) module. - - -Code reordering -=============== - -The code reordering feature can implicitly rearrange procedure, template, and -macro definitions along with variable declarations and initializations at the top -level scope so that, to a large extent, a programmer should not have to worry -about ordering definitions correctly or be forced to use forward declarations to -preface definitions inside a module. - -.. - NOTE: The following was documentation for the code reordering precursor, - which was {.noForward.}. - - In this mode, procedure definitions may appear out of order and the compiler - will postpone their semantic analysis and compilation until it actually needs - to generate code using the definitions. In this regard, this mode is similar - to the modus operandi of dynamic scripting languages, where the function - calls are not resolved until the code is executed. Here is the detailed - algorithm taken by the compiler: - - 1. When a callable symbol is first encountered, the compiler will only note - the symbol callable name and it will add it to the appropriate overload set - in the current scope. At this step, it won't try to resolve any of the type - expressions used in the signature of the symbol (so they can refer to other - not yet defined symbols). - - 2. When a top level call is encountered (usually at the very end of the - module), the compiler will try to determine the actual types of all of the - symbols in the matching overload set. This is a potentially recursive process - as the signatures of the symbols may include other call expressions, whose - types will be resolved at this point too. - - 3. Finally, after the best overload is picked, the compiler will start - compiling the body of the respective symbol. This in turn will lead the - compiler to discover more call expressions that need to be resolved and steps - 2 and 3 will be repeated as necessary. - - Please note that if a callable symbol is never used in this scenario, its - body will never be compiled. This is the default behavior leading to best - compilation times, but if exhaustive compilation of all definitions is - required, using `nim check` provides this option as well. - -Example: - - ```nim - {.experimental: "codeReordering".} - - proc foo(x: int) = - bar(x) - - proc bar(x: int) = - echo(x) - - foo(10) - ``` - -Variables can also be reordered as well. Variables that are *initialized* (i.e. -variables that have their declaration and assignment combined in a single -statement) can have their entire initialization statement reordered. Be wary of -what code is executed at the top level: - - ```nim - {.experimental: "codeReordering".} - - proc a() = - echo(foo) - - var foo = 5 - - a() # outputs: "5" - ``` - -.. - TODO: Let's table this for now. This is an *experimental feature* and so the - specific manner in which `declared` operates with it can be decided in - eventuality, because right now it works a bit weirdly. - - The values of expressions involving `declared` are decided *before* the - code reordering process, and not after. As an example, the output of this - code is the same as it would be with code reordering disabled. - - ```nim - {.experimental: "codeReordering".} - - proc x() = - echo(declared(foo)) - - var foo = 4 - - x() # "false" - ``` - -It is important to note that reordering *only* works for symbols at top level -scope. Therefore, the following will *fail to compile:* - - ```nim - {.experimental: "codeReordering".} - - proc a() = - b() - proc b() = - echo("Hello!") - - a() - ``` - -This feature will likely be replaced with a better solution to remove -the need for forward declarations. - -Special Operators -================= - -dot operators -------------- - -.. note:: Dot operators are still experimental and so need to be enabled - via `{.experimental: "dotOperators".}`. - -Nim offers a special family of dot operators that can be used to -intercept and rewrite proc call and field access attempts, referring -to previously undeclared symbol names. They can be used to provide a -fluent interface to objects lying outside the static confines of the -type system such as values from dynamic scripting languages -or dynamic file formats such as JSON or XML. - -When Nim encounters an expression that cannot be resolved by the -standard overload resolution rules, the current scope will be searched -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: - - ```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: - - ```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: - -operator `.` ------------- -This operator will be matched against both field accesses and method calls. - -operator `.()` ---------------- -This operator will be matched exclusively against method calls. It has higher -precedence than the `.` operator and this allows one to handle expressions like -`x.y` and `x.y()` differently if one is interfacing with a scripting language -for example. - -operator `.=` -------------- -This operator will be matched against assignments to missing fields. - - ```nim - a.b = c # becomes `.=`(a, b, c) - ``` - -Call operator -------------- -The call operator, `()`, matches all kinds of unresolved calls and takes -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. - - ```nim - {.experimental: "callOperator".} - - template `()`(a: int, b: float): untyped = $(a, b) - - block: - let a = 1.0 - let b = 2 - doAssert b(a) == `()`(b, a) - doAssert a.b == `()`(b, a) - - block: - let a = 1.0 - proc b(): int = 2 - doAssert not compiles(b(a)) - doAssert not compiles(a.b) # `()` not called - - block: - let a = 1.0 - proc b(x: float): int = int(x + 1) - let c = 3.0 - - 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](manual.html#userminusdefined-pragmas-macro-pragmas) -can also be applied to type, variable and constant declarations. - -For types: - - ```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 -side of the definition. The macro can return either a type section or -another `nnkTypeDef` node, both of which will replace the original row -in the type section. - -In the future, this `nnkTypeDef` argument may be replaced with a unary -type section node containing the type definition, or some other node that may -be more convenient to work with. The ability to return nodes other than type -definitions may also be supported, however currently this is not convenient -when dealing with mutual type recursion. For now, macros can return an unused -type definition where the right-hand node is of kind `nnkStmtListType`. -Declarations in this node will be attached to the same scope as -the parent scope of the type section. - ------- - -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. - - ```nim - var - a = ... - b {.importc, foo, nodecl.} = ... - c = ... - ``` - -Assuming `foo` is a macro or a template, this is roughly equivalent to: - - ```nim - var a = ... - foo: - var b {.importc, nodecl.} = ... - var c = ... - ``` - - -Symbols as template/macro calls -=============================== - -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. - - ```nim - type Foo = object - bar: int - - var foo = Foo(bar: 10) - template bar: untyped = foo.bar - 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 -be introduced to guarantee consistency and circumvent bugs. - - -Not nil annotation -================== - -**Note:** This is an experimental feature. It can be enabled with -`{.experimental: "notnil".}`. - -All types for which `nil` is a valid value can be annotated with the -`not nil` annotation to exclude `nil` as a valid value: - - ```nim - {.experimental: "notnil".} - - type - PObject = ref TObj not nil - TProc = (proc (x, y: int)) not nil - - proc p(x: PObject) = - echo "not nil" - - # compiler catches this: - p(nil) - - # 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.md - - -Aliasing restrictions in parameter passing -========================================== - -.. note:: The aliasing restrictions are currently not enforced by the - implementation and need to be fleshed out further. - -"Aliasing" here means that the underlying storage locations overlap in memory -at runtime. An "output parameter" is a parameter of type `var T`, -an input parameter is any parameter that is not of type `var`. - -1. Two output parameters should never be aliased. -2. An input and an output parameter should not be aliased. -3. An output parameter should never be aliased with a global or thread local - variable referenced by the called proc. -4. An input parameter should not be aliased with a global or thread local - variable updated by the called proc. - -One problem with rules 3 and 4 is that they affect specific global or thread -local variables, but Nim's effect tracking only tracks "uses no global variable" -via `.noSideEffect`. The rules 3 and 4 can also be approximated by a different rule: - -5. A global or thread local variable (or a location derived from such a location) - can only passed to a parameter of a `.noSideEffect` proc. - - -Strict funcs -============ - -Since version 1.4, a stricter definition of "side effect" is available. -In addition to the existing rule that a side effect is calling a function -with side effects, the following rule is also enforced: - -A store to the heap via a `ref` or `ptr` indirection is not allowed. - -For example: - - ```nim - {.experimental: "strictFuncs".} - - type - Node = ref object - le, ri: Node - data: string - - func len(n: Node): int = - # valid: len does not have side effects - var it = n - while it != nil: - inc result - it = it.ri - - func mut(n: Node) = - var it = n - while it != nil: - it.data = "yeah" # forbidden mutation - it = it.ri - - ``` - - -View types -========== - -.. tip:: `--experimental:views`:option: is more effective - with `--experimental:strictFuncs`:option:. - -A view type is a type that is or contains one of the following types: - -- `lent T` (view into `T`) -- `openArray[T]` (pair of (pointer to array of `T`, size)) - -For example: - - ```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: - - ```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 -of the locations it's derived from. More on this later. - -A *view* is a symbol (a let, var, const, etc.) that has a view type. - -Since version 1.4, Nim allows view types to be used as local variables. -This feature needs to be enabled via `{.experimental: "views".}`. - -A local variable of a view type *borrows* from the locations and -it is statically enforced that the view does not outlive the location -it was borrowed from. - -For example: - - ```nim - {.experimental: "views".} - - proc take(a: openArray[int]) = - echo a.len - - proc main(s: seq[int]) = - var x: openArray[int] = s # 'x' is a view into 's' - # it is checked that 'x' does not outlive 's' and - # that 's' is not mutated. - for i in 0 .. high(x): - echo x[i] - take(x) - - take(x.toOpenArray(0, 1)) # slicing remains possible - let y = x # create a view from a view - take y - # it is checked that 'y' does not outlive 'x' and - # that 'x' is not mutated as long as 'y' lives. - - - main(@[11, 22, 33]) - ``` - - -A local variable of a view type can borrow from a location -derived from a parameter, another local variable, a global `const` or `let` -symbol or a thread-local `var` or `let`. - -Let `p` the proc that is analysed for the correctness of the borrow operation. - -Let `source` be one of: - -- A formal parameter of `p`. Note that this does not cover parameters of - inner procs. -- The `result` symbol of `p`. -- A local `var` or `let` or `const` of `p`. Note that this does - not cover locals of inner procs. -- A thread-local `var` or `let`. -- A global `let` or `const`. -- A constant array/seq/object/tuple constructor. - - -Path expressions ----------------- - -A location derived from `source` is then defined as a path expression that -has `source` as the owner. A path expression `e` is defined recursively: - -- `source` itself is a path expression. -- Container access like `e[i]` is a path expression. -- Tuple access `e[0]` is a path expression. -- Object field access `e.field` is a path expression. -- `system.toOpenArray(e, ...)` is a path expression. -- Pointer dereference `e[]` is a path expression. -- An address `addr e` is a path expression. -- A type conversion `T(e)` is a path expression. -- A cast expression `cast[T](e)` is a path expression. -- `f(e, ...)` is a path expression if `f`'s return type is a view type. - Because the view can only have been borrowed from `e`, we then know - that the owner of `f(e, ...)` is `e`. - - -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](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 -from both a mutable or an immutable location. - -If a view borrows from a mutable location, the view can be used to update the -location. Otherwise it cannot be used for mutations. - -The *duration* of a borrow is the span of commands beginning from the assignment -to the view and ending with the last usage of the view. - -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. - - ```nim - {.experimental: "views".} - - type - Obj = object - field: string - - proc dangerous(s: var seq[Obj]) = - 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: - - ```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] -feature. In other words `--experimental:views`:option: works better -with `--experimental:strictFuncs`:option:. - -The analysis is currently control flow insensitive: - - ```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 -will never do that at runtime. - - -Start of a borrow ------------------ - -A borrow starts with one of the following: - -- The assignment of a non-view-type to a view-type. -- The assignment of a location that is derived from a local parameter - to a view-type. - - -End of a borrow ---------------- - -A borrow operation ends with the last usage of the view variable. - - -Reborrows ---------- - -A view `v` can borrow from multiple different locations. However, the borrow -is always the full span of `v`'s lifetime and every location that is borrowed -from is sealed during `v`'s lifetime. - - -Algorithm ---------- - -The following section is an outline of the algorithm that the current implementation -uses. The algorithm performs two traversals over the AST of the procedure or global -section of code that uses a view variable. No fixpoint iterations are performed, the -complexity of the analysis is O(N) where N is the number of nodes of the AST. - -The first pass over the AST computes the lifetime of each local variable based on -a notion of an "abstract time", in the implementation it's a simple integer that is -incremented for every visited node. - -In the second pass, information about the underlying object "graphs" is computed. -Let `v` be a parameter or a local variable. Let `G(v)` be the graph -that `v` belongs to. A graph is defined by the set of variables that belong -to the graph. Initially for all `v`: `G(v) = {v}`. Every variable can only -be part of a single graph. - -Assignments like `a = b` "connect" two variables, both variables end up in the -same graph `{a, b} = G(a) = G(b)`. Unfortunately, the pattern to look for is -much more complex than that and can involve multiple assignment targets -and sources: - - f(x, y) = g(a, b) - -connects `x` and `y` to `a` and `b`: `G(x) = G(y) = G(a) = G(b) = {x, y, a, b}`. -A type based alias analysis rules out some of these combinations, for example -a `string` value cannot possibly be connected to a `seq[int]`. - -A pattern like `v[] = value` or `v.field = value` marks `G(v)` as mutated. -After the second pass a set of disjoint graphs was computed. - -For strict functions it is then enforced that there is no graph that is both mutated -and has an element that is an immutable parameter (that is a parameter that is not -of type `var T`). - -For borrow checking, a different set of checks is performed. Let `v` be the view -and `b` the location that is borrowed from. - -- The lifetime of `v` must not exceed `b`'s lifetime. Note: The lifetime of - a parameter is the complete proc body. -- If `v` is used for a mutation, `b` must be a mutable location too. -- During `v`'s lifetime, `G(b)` can only be modified by `v` (and only if - `v` is a mutable view). -- If `v` is `result` then `b` has to be a location derived from the first - formal parameter or from a constant location. -- A view cannot be used for a read or a write access before it was assigned to. - - -Concepts -======== - -Concepts, also known as "user-defined type classes", are used to specify an -arbitrary set of requirements that the matched type must satisfy. - -Concepts are written in the following form: - - ```nim - type - Comparable = concept x, y - (x < y) is bool - - Stack[T] = concept s, var v - s.pop() is T - v.push(T) - - s.len is Ordinal - - for value in s: - value is T - ``` - -The concept matches if: - -a) all expressions within the body can be compiled for the tested type -b) all statically evaluable boolean expressions in the body are true - -The identifiers following the `concept` keyword represent instances of the -currently matched type. You can apply any of the standard type modifiers such -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: - - ```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: - - ```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. - - ```nim - type - # Let's imagine a user-defined casting framework with operators - # such as `val.to(string)` and `val.to(JSonValue)`. We can test - # for these with the following concept: - MyCastables = concept x - x.to(type string) - x.to(type JSonValue) - - # Let's define a couple of concepts, known from Algebra: - AdditiveMonoid* = concept x, y, type T - x + y is T - T.zero is T # require a proc such as `int.zero` or 'Position.zero' - - AdditiveGroup* = concept x, y, type T - 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 -default parameters are still applied in the concept body, it's also possible -to describe usage protocols that do not reveal implementation details. - -Much like generics, concepts are instantiated exactly once for each tested type -and any static code included within the body is executed only once. - - -Concept diagnostics -------------------- - -By default, the compiler will report the matching errors in concepts only when -no other overload can be selected and a normal compilation error is produced. -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. - - ```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. - - -Generic concepts and type binding rules ---------------------------------------- - -The concept types can be parametric just like the regular generic types: - - ```nim - ### matrixalgo.nim - - import std/typetraits - - type - AnyMatrix*[R, C: static int; T] = concept m, var mvar, type M - M.ValueType is T - M.Rows == R - M.Cols == C - - m[int, int] is T - mvar[int, int] = T - - type TransposedType = stripGenericParams(M)[C, R, T] - - AnySquareMatrix*[N: static int, T] = AnyMatrix[N, N, T] - - AnyTransform3D* = AnyMatrix[4, 4, float] - - proc transposed*(m: AnyMatrix): m.TransposedType = - for r in 0 ..< m.R: - for c in 0 ..< m.C: - result[r, c] = m[c, r] - - proc determinant*(m: AnySquareMatrix): int = - ... - - proc setPerspectiveProjection*(m: AnyTransform3D) = - ... - - -------------- - ### matrix.nim - - type - Matrix*[M, N: static int; T] = object - data: array[M*N, T] - - proc `[]`*(M: Matrix; m, n: int): M.T = - M.data[m * M.N + n] - - proc `[]=`*(M: var Matrix; m, n: int; v: M.T) = - M.data[m * M.N + n] = v - - # Adapt the Matrix type to the concept's requirements - template Rows*(M: typedesc[Matrix]): int = M.M - template Cols*(M: typedesc[Matrix]): int = M.N - template ValueType*(M: typedesc[Matrix]): typedesc = M.T - - ------------- - ### usage.nim - - import matrix, matrixalgo - - var - m: Matrix[3, 3, int] - projectionMatrix: Matrix[4, 4, float] - - 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 -resembles the way generic parameters of callable symbols are inferred on -call sites. - -Unbound types can appear both as params to calls such as `s.push(T)` and -on the right-hand side of the `is` operator in cases such as `x.pop is T` -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: - - ```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. - -Just like in regular type classes, Nim discriminates between `bind once` -and `bind many` types when matching the concept. You can add the `distinct` -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: - - ```nim - type - Enumerable[T] = concept e - for v in e: - v is T - - type - MyConcept = concept o - # this could be inferred to a type such as Enumerable[int] - o.foo is distinct Enumerable - - # this could be inferred to a different type such as Enumerable[float] - o.bar is distinct Enumerable - - # 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: - - ```nim - type - MyConcept = concept x - type T1 = auto - x.foo(T1) - x.bar(T1) # both procs must accept the same type - - type T2 = seq[SomeNumber] - 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, -the concept will be automatically instantiated with the bind once auto type -in the place of each missing generic param. - -Please note that generic concepts such as `Enumerable[T]` can be matched -against concrete types such as `string`. Nim doesn't require the concept -type to have the same number of parameters as the type being matched. -If you wish to express a requirement towards the generic parameters of -the matched type, you can use a type mapping operator such as `genericHead` -or `stripGenericParams` within the body of the concept to obtain the -uninstantiated version of the type, which you can then try to instantiate -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: - - ```nim test = "nim c $1" - import std/[sugar, typetraits] - - type - Functor[A] = concept f - type MatchedGenericType = genericHead(typeof(f)) - # `f` will be a value of a type such as `Option[T]` - # `MatchedGenericType` will become the `Option` type - - f.val is A - # The Functor should provide a way to obtain - # a value stored inside it - - type T = auto - map(f, A -> T) is MatchedGenericType[T] - # And it should provide a way to map one instance of - # the Functor to a instance of a different type, given - # a suitable `map` operation for the enclosed values - - import std/options - echo Option[int] is Functor # prints true - ``` - - -Concept derived values ----------------------- - -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: - - ```nim - type - DateTime = concept t1, t2, type T - const Min = T.MinDate - T.Now is T - - t1 < t2 is bool - - type TimeSpan = typeof(t1 - t2) - TimeSpan * int is TimeSpan - TimeSpan + TimeSpan is TimeSpan - - t1 + TimeSpan is T - - proc eventsJitter(events: Enumerable[DateTime]): float = - var - # this variable will have the inferred TimeSpan type for - # the concrete Date-like value the proc was called with: - averageInterval: DateTime.TimeSpan - - deviation: float - ... - ``` - - -Concept refinement ------------------- - -When the matched type within a concept is directly tested against a different -concept, we say that the outer concept is a refinement of the inner concept and -thus it is more-specific. When both concepts are matched in a call during -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: - - ```nim - type - Graph = concept g, type G of EquallyComparable, Copyable - type - VertexType = G.VertexType - EdgeType = G.EdgeType - - VertexType is Copyable - EdgeType is Copyable - - var - v: VertexType - e: EdgeType - - IncidendeGraph = concept of Graph - # symbols such as variables and types from the refined - # concept are automatically in scope: - - g.source(e) is VertexType - g.target(e) is VertexType - - g.outgoingEdges(v) is Enumerable[EdgeType] - - BidirectionalGraph = concept g, type G - # The following will also turn the concept into a refinement when it - # comes to overload resolution, but it doesn't provide the convenient - # symbol inheritance - g is IncidendeGraph - - g.incomingEdges(G.VertexType) is Enumerable[G.EdgeType] - - 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 - ---------------------- - - Concepts can also be used to convert a whole range of types to a single type or - a small set of simpler types. This is achieved with a `return` statement within - the concept body: - - ```nim - type - Stringable = concept x - $x is string - return $x - - StringRefValue[CharType] = object - base: ptr CharType - len: int - - StringRef = concept x - # the following would be an overloaded proc for cstring, string, seq and - # other user-defined types, returning either a StringRefValue[char] or - # StringRefValue[wchar] - return makeStringRefValue(x) - - # the varargs param will here be converted to an array of StringRefValues - # the proc will have only two instantiations for the two character types - proc log(format: static string, varargs[StringRef]) - - # this proc will allow char and wchar values to be mixed in - # 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]) - ``` - - -.. - VTable types - ------------ - - Concepts allow Nim to define a great number of algorithms, using only - static polymorphism and without erasing any type information or sacrificing - any execution speed. But when polymorphic collections of objects are required, - the user must use one of the provided type erasure techniques - either common - base types or VTable types. - - VTable types are represented as "fat pointers" storing a reference to an - object together with a reference to a table of procs implementing a set of - required operations (the so called vtable). - - In contrast to other programming languages, the vtable in Nim is stored - externally to the object, allowing you to create multiple different vtable - views for the same object. Thus, the polymorphism in Nim is unbounded - - any type can implement an unlimited number of protocols or interfaces not - originally envisioned by the type's author. - - Any concept type can be turned into a VTable type by using the `vtref` - or the `vtptr` compiler magics. Under the hood, these magics generate - a converter type class, which converts the regular instances of the matching - types to the corresponding VTable type. - - ```nim - type - IntEnumerable = vtref Enumerable[int] - - MyObject = object - enumerables: seq[IntEnumerable] - streams: seq[OutputStream.vtref] - - proc addEnumerable(o: var MyObject, e: IntEnumerable) = - o.enumerables.add e - - 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 - concrete types. All such calls should include exactly one param of the type - matched against the concept (not necessarily in the first position), which - will be considered the value bound to the vtable. - - Overloads will be created for all captured procs, accepting the vtable type - in the position of the captured underlying object. - - Under these rules, it's possible to obtain a vtable type for a concept with - unbound type parameters or one instantiated with metatypes (type classes), - but it will include a smaller number of captured procs. A completely empty - vtable will be reported as an error. - - The `vtref` magic produces types which can be bound to `ref` types and - the `vtptr` magic produced types bound to `ptr` types. - - -.. - deepCopy - -------- - `=deepCopy` is a builtin that is invoked whenever data is passed to - a `spawn`'ed proc to ensure memory safety. The programmer can override its - behaviour for a specific `ref` or `ptr` type `T`. (Later versions of the - language may weaken this restriction.) - - The signature has to be: - - ```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][spawn statement] for details. - - -Dynamic arguments for bindSym -============================= - -This experimental feature allows the symbol name argument of `macros.bindSym` -to be computed dynamically. - - ```nim - {.experimental: "dynamicBindSym".} - - import macros - - macro callOp(opName, arg1, arg2): untyped = - result = newCall(bindSym($opName), arg1, arg2) - - echo callOp("+", 1, 2) - echo callOp("-", 5, 4) - ``` - - -Term rewriting macros -===================== - -Term rewriting macros are macros or templates that have not only -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: - - ```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 `*`, `**`, -`|`, `~` have a special meaning in patterns if they are written in infix -notation, so to match verbatim against `*` the ordinary function call syntax -needs to be used. - -Term rewriting macros are applied recursively, up to a limit. This means that -if the result of a term rewriting macro is eligible for another rewriting, -the compiler will try to perform it, and so on, until no more optimizations -are applicable. To avoid putting the compiler into an infinite loop, there is -a hard limit on how many times a single term rewriting macro can be applied. -Once this limit has been passed, the term rewriting macro will be ignored. - -Unfortunately optimizations are hard to get right and even this tiny example -is **wrong**: - - ```nim - template optMul{`*`(a, 2)}(a: int): int = a + a - - proc f(): int = - echo "side effect!" - 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: - - ```nim - template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a + a - - proc f(): int = - echo "side effect!" - 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 -differently. - -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: - - ```nim - template mulIsCommutative{`*`(a, b)}(a, b: int): int = b * a - ``` - -What optimizers really need to do is a *canonicalization*: - - ```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. - - - -Parameter constraints ---------------------- - -The `parameter constraint`:idx: expression can use the operators `|` (or), -`&` (and) and `~` (not) and the following predicates: - -=================== ===================================================== -Predicate Meaning -=================== ===================================================== -`atom` The matching node has no children. -`lit` The matching node is a literal like `"abc"`, `12`. -`sym` The matching node must be a symbol (a bound - identifier). -`ident` The matching node must be an identifier (an unbound - identifier). -`call` The matching AST must be a call/apply expression. -`lvalue` The matching AST must be an lvalue. -`sideeffect` The matching AST must have a side effect. -`nosideeffect` The matching AST must have no side effect. -`param` A symbol which is a parameter. -`genericparam` A symbol which is a generic parameter. -`module` A symbol which is a module. -`type` A symbol which is a type. -`var` A symbol which is a variable. -`let` A symbol which is a `let` variable. -`const` A symbol which is a constant. -`result` The special `result` variable. -`proc` A symbol which is a proc. -`method` A symbol which is a method. -`iterator` A symbol which is an iterator. -`converter` A symbol which is a converter. -`macro` A symbol which is a macro. -`template` A symbol which is a template. -`field` A symbol which is a field in a tuple or an object. -`enumfield` A symbol which is a field in an enumeration. -`forvar` A for loop variable. -`label` A label (used in `block` statements). -`nk*` The matching AST must have the specified kind. - (Example: `nkIfStmt` denotes an `if` statement.) -`alias` States that the marked parameter needs to alias - with *some* other parameter. -`noalias` States that *every* other parameter must not alias - with the marked parameter. -=================== ===================================================== - -Predicates that share their name with a keyword have to be escaped with -backticks. -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: - - ```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: - - ```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" - proc somefunc(s: string{nkTripleStrLit}) = assert s == """triple""" - proc somefunc(s: static[string]) = assert s == "constant" - - # Use parameter constraints to provide overloads based on both the input parameter type and form. - var variable = "variable" - somefunc(variable) - const constant = "constant" - somefunc(constant) - somefunc("literal") - somefunc(r"raw") - somefunc("""triple""") - ``` - - -Pattern operators ------------------ - -The operators `*`, `**`, `|`, `~` have a special meaning in patterns -if they are written in infix notation. - - -### The `|` operator - -The `|` operator if used as infix operator creates an ordered choice: - - ```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: - - ```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 -semantics anyway. In fact, they can be deactivated with the `--patterns:off`:option: -command line option or temporarily with the `patterns` pragma. - - -### The `{}` operator - -A pattern expression can be bound to a pattern parameter via the `expr{param}` -notation: - - ```nim - template t{(0|1|2){x}}(x: untyped): untyped = x + 1 - let a = 1 - # outputs 2: - echo a - ``` - - -### The `~` operator - -The `~` operator is the 'not' operator in patterns: - - ```nim - template t{x = (~x){y} and (~x){z}}(x, y, z: bool) = - x = y - if x: x = z - - var - a = false - b = true - c = false - a = b and c - echo a - ``` - - -### The `*` operator - -The `*` operator can *flatten* a nested binary expression like `a & b & c` -to `&(a, b, c)`: - - ```nim - var - calls = 0 - - proc `&&`(s: varargs[string]): string = - result = s[0] - for i in 1..len(s)-1: result.add s[i] - inc calls - - template optConc{ `&&` * a }(a: string): untyped = &&a - - let space = " " - echo "my" && (space & "awe" && "some " ) && "concat" - - # 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 -arguments. The expression `"my" && (space & "awe" && "some " ) && "concat"` -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: - - ```nim - `&&`("my", space & "awe", "some ", "concat") - ``` - - -### 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: - - ```nim - import std/macros - - type - Matrix = object - dummy: int - - proc `*`(a, b: Matrix): Matrix = discard - proc `+`(a, b: Matrix): Matrix = discard - proc `-`(a, b: Matrix): Matrix = discard - proc `$`(a: Matrix): string = result = $a.dummy - proc mat21(): Matrix = - result.dummy = 21 - - macro optM{ (`+`|`-`|`*`) ** a }(a: Matrix): untyped = - echo treeRepr(a) - result = newCall(bindSym"mat21") - - 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: - - Arglist - Sym "x" - Sym "y" - Sym "z" - Sym "*" - Sym "+" - Sym "x" - Sym "-" - -(This is the reverse polish notation of `x + y * z - x`.) - - -Parameters ----------- - -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: - - ```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 ----------------- - -Term rewriting macros and templates are currently greedy and -they will rewrite as long as there is a match. -There was no way to ensure some rewrite happens only once, -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: - - ```nim - template pwnEcho{echo(x)}(x: untyped) = - {.noRewrite.}: echo("pwned!") - - echo "ab" - ``` - -`noRewrite` pragma can be useful to control term-rewriting macros recursion. - - - -Example: Partial evaluation ---------------------------- - -The following example shows how some simple partial evaluation can be -implemented with term rewriting: - - ```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 ------------------ - -The following example shows how some form of hoisting can be implemented: - - ```nim - import std/pegs - - template optPeg{peg(pattern)}(pattern: string{lit}): Peg = - var gl {.global, gensym.} = peg(pattern) - gl - - 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 -stored in a global `gl` which is then re-used. This optimization is called -hoisting because it is comparable to classical loop hoisting. - - -AST based overloading -===================== - -Parameter constraints can also be used for ordinary routine parameters; these -constraints then affect ordinary overloading resolution: - - ```nim - proc optLit(a: string{lit|`const`}) = - echo "string literal" - proc optLit(a: string) = - echo "no string literal" - - const - constant = "abc" - - var - variable = "xyz" - - optLit("literal") - optLit(constant) - optLit(variable) - ``` - -However, the constraints `alias` and `noalias` are not available in -ordinary routines. - - -Parallel & Spawn -================ - -Nim has two flavors of parallelism: -1) `Structured`:idx: parallelism via the `parallel` statement. -2) `Unstructured`:idx: parallelism via the standalone `spawn` statement. - -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](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 -the form `f(a, ...)`. Let `T` be `f`'s return type. If `T` is `void`, -then `spawn`'s return type is also `void`, otherwise it is `FlowVar[T]`. - -Within a `parallel` section, the `FlowVar[T]` is sometimes eliminated -to `T`. This happens when `T` does not contain any GC'ed memory. -The compiler can ensure the location in `location = spawn f(...)` is not -read prematurely within a `parallel` section and so there is no need for -the overhead of an indirection via `FlowVar[T]` to ensure correctness. - -.. note:: Currently exceptions are not propagated between `spawn`'ed tasks! - -This feature is likely to be removed in the future as external packages -can have better solutions. - - -Spawn statement ---------------- - -The `spawn`:idx: statement can be used to pass a task to the thread pool: - - ```nim - import std/threadpool - - proc processLine(line: string) = - discard "do some heavy lifting here" - - 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: - -* It must be a call expression `f(a, ...)`. -* `f` must be `gcsafe`. -* `f` must not have the calling convention `closure`. -* `f`'s parameters may not be of type `var`. - This means one has to use raw `ptr`'s for data passing reminding the - programmer to be careful. -* `ref` parameters are deeply copied, which is a subtle semantic change and - can cause performance problems, but ensures memory safety. This deep copy - is performed via `system.deepCopy`, so it can be overridden. -* For *safe* data exchange between `f` and the caller, a global `Channel` - needs to be used. However, since spawn can return a result, often no further - communication is required. - - -`spawn` executes the passed expression on the thread pool and returns -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: - - ```nim - import std/threadpool, ... - - # wait until 2 out of 3 servers received the update: - proc main = - var responses = newSeq[FlowVarBase](3) - for i in 0..2: - responses[i] = spawn tellServer(Update, "key", "value") - var index = blockUntilAny(responses) - 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 -a data flow variable: `T` has to be a `ref`, `string`, `seq` -or of a type that doesn't contain any GC'd type. This -restriction is not hard to work-around in practice. - - - -Parallel statement ------------------- - -Example: - - ```nim test = "nim c --threads:on $1" - # Compute pi in an inefficient way - import std/[strutils, math, threadpool] - {.experimental: "parallel".} - - proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1) - - proc pi(n: int): float = - var ch = newSeq[float](n + 1) - parallel: - for k in 0..ch.high: - ch[k] = spawn term(float(k)) - for k in 0..ch.high: - result += ch[k] - - echo formatFloat(pi(5000)) - ``` - - -The parallel statement is the preferred mechanism to introduce parallelism in a -Nim program. Only a subset of the Nim language is valid within a `parallel` -section. This subset is checked during semantic analysis to be free of data -races. A sophisticated `disjoint checker`:idx: ensures that no data races are -possible, even though shared memory is extensively supported! - -The subset is in fact the full language with the following -restrictions / changes: - -* `spawn` within a `parallel` section has special semantics. -* Every location of the form `a[i]`, `a[i..j]` and `dest` where - `dest` is part of the pattern `dest = spawn f(...)` has to be - provably disjoint. This is called the *disjoint check*. -* Every other complex location `loc` that is used in a spawned - proc (`spawn f(loc)`) has to be immutable for the duration of - the `parallel` section. This is called the *immutability check*. Currently - it is not specified what exactly "complex location" means. We need to make - this an optimization! -* Every array access has to be provably within bounds. This is called - the *bounds check*. -* Slices are optimized so that no copy is performed. This optimization is not - yet performed for ordinary slices outside of a `parallel` section. - - -Strict definitions and `out` parameters -======================================= - -With `experimental: "strictDefs"` *every* local variable must be initialized explicitly before it can be used: - - ```nim - {.experimental: "strictDefs".} - - proc test = - var s: seq[string] - s.add "abc" # invalid! - - ``` - -Needs to be written as: - - ```nim - {.experimental: "strictDefs".} - - proc test = - var s: seq[string] = @[] - s.add "abc" # valid! - - ``` - -A control flow analysis is performed in order to prove that a variable has been written to -before it is used. Thus the following is valid: - - ```nim - {.experimental: "strictDefs".} - - proc test(cond: bool) = - var s: seq[string] - if cond: - s = @["y"] - else: - s = @[] - s.add "abc" # valid! - ``` - -In this example every path does set `s` to a value before it is used. - - ```nim - {.experimental: "strictDefs".} - - proc test(cond: bool) = - let s: seq[string] - if cond: - s = @["y"] - else: - s = @[] - ``` - -With `experimental: "strictDefs"`, `let` statements are allowed to not have an initial value, but every path should set `s` to a value before it is used. - - -`out` parameters ----------------- - -An `out` parameter is like a `var` parameter but it must be written to before it can be used: - - ```nim - - proc myopen(f: out File; name: string): bool = - f = default(File) - result = open(f, name) - - ``` - -While it is usually the better style to use the return type in order to return results API and ABI -considerations might make this infeasible. Like for `var T` Nim maps `out T` to a hidden pointer. -For example POSIX's `stat` routine can be wrapped as: - - ```nim - - proc stat*(a1: cstring, a2: out Stat): cint {.importc, header: "".} - - ``` - -When the implementation of a routine with output parameters is analysed, the compiler -checks that every path before the (implicit or explicit) return does set every output -parameter: - - ```nim - - proc p(x: out int; y: out string; cond: bool) = - x = 4 - if cond: - y = "abc" - # error: not every path initializes 'y' - - ``` - - -Out parameters and exception handling -------------------------------------- - -The analysis should take exceptions into account (but currently does not): - - ```nim - - proc p(x: out int; y: out string; cond: bool) = - x = canRaise(45) - y = "abc" # <-- error: not every path initializes 'y' - - ``` - -Once the implementation takes exceptions into account it is easy enough to -use `outParam = default(typeof(outParam))` in the beginning of the proc body. - -Out parameters and inheritance ------------------------------- - -It is not valid to pass an lvalue of a supertype to an `out T` parameter: - - ```nim - - type - Superclass = object of RootObj - a: int - Subclass = object of Superclass - s: string - - proc init(x: out Superclass) = - x = Superclass(a: 8) - - var v: Subclass - init v - use v.s # the 's' field was never initialized! - - ``` - -However, in the future this could be allowed and provide a better way to write object -constructors that take inheritance into account. - - -**Note**: The implementation of "strict definitions" and "out parameters" is experimental but the concept -is solid and it is expected that eventually this mode becomes the default in later versions. - - -Strict case objects -=================== - -With `experimental: "strictCaseObjects"` *every* field access is checked to be valid at compile-time. -The field is within a `case` section of an `object`. - - ```nim - {.experimental: "strictCaseObjects".} - - type - Foo = object - case b: bool - of false: - s: string - of true: - x: int - - var x = Foo(b: true, x: 4) - case x.b - of true: - echo x.x # valid - of false: - echo "no" - - case x.b - of false: - echo x.x # error: field access outside of valid case branch: x.x - of true: - echo "no" - - ``` - -**Note**: The implementation of "strict case objects" is experimental but the concept -is solid and it is expected that eventually this mode becomes the default in later versions. diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst new file mode 100644 index 000000000..772f0d12c --- /dev/null +++ b/doc/manual_experimental.rst @@ -0,0 +1,2066 @@ +========================= +Nim Experimental Features +========================= + +:Authors: Andreas Rumpf +:Version: |nimversion| + +.. contents:: + + +About this document +=================== + +This document describes features of Nim that are to be considered experimental. +Some of these are not covered by the ``.experimental`` pragma or +``--experimental`` switch because they are already behind a special syntax and +one may want to use Nim libraries using these features without using them +oneself. + +**Note**: Unless otherwise indicated, these features are not to be removed, +but refined and overhauled. + + +Package level objects +===================== + +Every Nim module resides in a (nimble) package. An object type can be attached +to the package it resides in. If that is done, the type can be referenced from +other modules as an `incomplete`:idx: object type. This feature allows to +break up recursive type dependencies across module boundaries. Incomplete +object types are always passed ``byref`` and can only be used in pointer like +contexts (``var/ref/ptr IncompleteObject``) in general since the compiler does +not yet know the size of the object. To complete an incomplete object +the ``package`` pragma has to be used. ``package`` implies ``byref``. + +As long as a type ``T`` is incomplete, neither ``sizeof(T)`` nor runtime +type information for ``T`` is available. + + +Example: + +.. code-block:: nim + + # module A (in an arbitrary package) + type + Pack.SomeObject = object ## declare as incomplete object of package 'Pack' + Triple = object + a, b, c: ref SomeObject ## pointers to incomplete objects are allowed + + ## Incomplete objects can be used as parameters: + proc myproc(x: SomeObject) = discard + + +.. code-block:: nim + + # module B (in package "Pack") + type + SomeObject* {.package.} = object ## Use 'package' to complete the object + s, t: string + x, y: int + + +Void type +========= + +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 + 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 + proc callProc[T](p: proc (x: T), x: T) = + when T is void: + p() + else: + p(x) + + proc intProc(x: int) = discard + proc emptyProc() = discard + + callProc[int](intProc, 12) + callProc[void](emptyProc) + +However, a ``void`` type cannot be inferred in generic code: + +.. code-block:: 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``. + + + +Covariance +========== + +Covariance in Nim can be introduced only through pointer-like types such +as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated +with pointer-like types will be considered covariant if and only if they +are also immutable. The introduction of a ``var`` modifier or additional +``ptr`` or ``ref`` indirections would result in invariant treatment of +these types. + +``proc`` types are currently always invariant, but future versions of Nim +may relax this rule. + +User-defined generic types may also be covariant with respect to some of +their parameters. By default, all generic params are considered invariant, +but you may choose the apply the prefix modifier ``in`` to a parameter to +make it contravariant or ``out`` to make it covariant: + +.. code-block:: nim + type + AnnotatedPtr[out T] = + metadata: MyTypeInfo + p: ref T + + RingBuffer[out T] = + startPos: int + data: seq[T] + + Action {.importcpp: "std::function".} [in T] = object + +When the designated generic parameter is used to instantiate a pointer-like +type as in the case of `AnnotatedPtr` above, the resulting generic type will +also have pointer-like covariance: + +.. code-block:: nim + type + GuiWidget = object of RootObj + Button = object of GuiWidget + ComboBox = object of GuiWidget + + var + widgetPtr: AnnotatedPtr[GuiWidget] + buttonPtr: AnnotatedPtr[Button] + + ... + + proc drawWidget[T](x: AnnotatedPtr[GuiWidget]) = ... + + # you can call procs expecting base types by supplying a derived type + drawWidget(buttonPtr) + + # and you can convert more-specific pointer types to more general ones + widgetPtr = buttonPtr + +Just like with regular pointers, covariance will be enabled only for immutable +values: + +.. code-block:: nim + proc makeComboBox[T](x: var AnnotatedPtr[GuiWidget]) = + x.p = new(ComboBox) + + makeComboBox(buttonPtr) # Error, AnnotatedPtr[Button] cannot be modified + # to point to a ComboBox + +On the other hand, in the `RingBuffer` example above, the designated generic +param is used to instantiate the non-pointer ``seq`` type, which means that +the resulting generic type will have covariance that mimics an array or +sequence (i.e. it will be covariant only when instantiated with ``ptr`` and +``ref`` types): + +.. code-block:: nim + + type + Base = object of RootObj + Derived = object of Base + + proc consumeBaseValues(b: RingBuffer[Base]) = ... + + var derivedValues: RingBuffer[Derived] + + consumeBaseValues(derivedValues) # Error, Base and Derived values may differ + # in size + + proc consumeBasePointers(b: RingBuffer[ptr Base]) = ... + + var derivedPointers: RingBuffer[ptr Derived] + + consumeBaseValues(derivedPointers) # This is legal + +Please note that Nim will treat the user-defined pointer-like types as +proper alternatives to the built-in pointer types. That is, types such +as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be +considered covariant and you can create new pointer-like types by instantiating +other user-defined pointer-like types. + +The contravariant parameters introduced with the ``in`` modifier are currently +useful only when interfacing with imported types having such semantics. + + +Automatic dereferencing +======================= + +Automatic dereferencing is performed for the first argument of a routine call. +This feature has to be only enabled via ``{.experimental: "implicitDeref".}``: + +.. code-block:: nim + {.experimental: "implicitDeref".} + + proc depth(x: NodeObj): int = ... + + var + n: Node + new(n) + echo n.depth + # no need to write n[].depth either + +Code reordering +=============== + +The code reordering feature can implicitly rearrange procedure, template, and +macro definitions along with variable declarations and initializations at the top +level scope so that, to a large extent, a programmer should not have to worry +about ordering definitions correctly or be forced to use forward declarations to +preface definitions inside a module. + +.. + NOTE: The following was documentation for the code reordering precursor, + which was {.noForward.}. + + In this mode, procedure definitions may appear out of order and the compiler + will postpone their semantic analysis and compilation until it actually needs + to generate code using the definitions. In this regard, this mode is similar + to the modus operandi of dynamic scripting languages, where the function + calls are not resolved until the code is executed. Here is the detailed + algorithm taken by the compiler: + + 1. When a callable symbol is first encountered, the compiler will only note + the symbol callable name and it will add it to the appropriate overload set + in the current scope. At this step, it won't try to resolve any of the type + expressions used in the signature of the symbol (so they can refer to other + not yet defined symbols). + + 2. When a top level call is encountered (usually at the very end of the + module), the compiler will try to determine the actual types of all of the + symbols in the matching overload set. This is a potentially recursive process + as the signatures of the symbols may include other call expressions, whose + types will be resolved at this point too. + + 3. Finally, after the best overload is picked, the compiler will start + compiling the body of the respective symbol. This in turn will lead the + compiler to discover more call expressions that need to be resolved and steps + 2 and 3 will be repeated as necessary. + + Please note that if a callable symbol is never used in this scenario, its + body will never be compiled. This is the default behavior leading to best + compilation times, but if exhaustive compilation of all definitions is + required, using ``nim check`` provides this option as well. + +Example: + +.. code-block:: nim + + {.experimental: "codeReordering".} + + proc foo(x: int) = + bar(x) + + proc bar(x: int) = + echo(x) + + foo(10) + +Variables can also be reordered as well. Variables that are *initialized* (i.e. +variables that have their declaration and assignment combined in a single +statement) can have their entire initialization statement reordered. Be wary of +what code is executed at the top level: + +.. code-block:: nim + {.experimental: "codeReordering".} + + proc a() = + echo(foo) + + var foo = 5 + + a() # outputs: "5" + +.. + TODO: Let's table this for now. This is an *experimental feature* and so the + specific manner in which ``declared`` operates with it can be decided in + eventuality, because right now it works a bit weirdly. + + The values of expressions involving ``declared`` are decided *before* the + code reordering process, and not after. As an example, the output of this + code is the same as it would be with code reordering disabled. + + .. code-block:: nim + {.experimental: "codeReordering".} + + proc x() = + echo(declared(foo)) + + var foo = 4 + + x() # "false" + +It is important to note that reordering *only* works for symbols at top level +scope. Therefore, the following will *fail to compile:* + +.. code-block:: nim + {.experimental: "codeReordering".} + + proc a() = + b() + proc b() = + echo("Hello!") + + a() + + +Named argument overloading +========================== + +Routines with the same type signature can be called differently if a parameter +has different names. This does not need an ``experimental`` switch, but is an +unstable feature. + +.. code-block::nim + proc foo(x: int) = + echo "Using x: ", x + proc foo(y: int) = + echo "Using y: ", y + + foo(x = 2) + # Using x: 2 + foo(y = 2) + # Using y: 2 + + +Do notation +=========== + +As a special more convenient notation, proc expressions involved in procedure +calls can use the ``do`` keyword: + +.. code-block:: nim + sort(cities) do (x,y: string) -> int: + cmp(x.len, y.len) + + # Less parenthesis using the method plus command syntax: + cities = cities.map do (x:string) -> string: + "City of " & x + + # In macros, the do notation is often used for quasi-quoting + macroResults.add quote do: + if not `ex`: + echo `info`, ": Check failed: ", `expString` + +``do`` is written after the parentheses enclosing the regular proc params. +The proc expression represented by the do block is appended to them. +In calls using the command syntax, the do block will bind to the immediately +preceding expression, transforming it in a call. + +``do`` with parentheses is an anonymous ``proc``; however a ``do`` without +parentheses is just a block of code. The ``do`` notation can be used to +pass multiple blocks to a macro: + +.. code-block:: nim + macro performWithUndo(task, undo: untyped) = ... + + performWithUndo do: + # multiple-line block of code + # to perform the task + do: + # code to undo it + + +Special Operators +================= + +dot operators +------------- + +**Note**: Dot operators are still experimental and so need to be enabled +via ``{.experimental: "dotOperators".}``. + +Nim offers a special family of dot operators that can be used to +intercept and rewrite proc call and field access attempts, referring +to previously undeclared symbol names. They can be used to provide a +fluent interface to objects lying outside the static confines of the +type system such as values from dynamic scripting languages +or dynamic file formats such as JSON or XML. + +When Nim encounters an expression that cannot be resolved by the +standard overload resolution rules, the current scope will be searched +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 + 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 + 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: + +operator `.` +------------ +This operator will be matched against both field accesses and method calls. + +operator `.()` +--------------- +This operator will be matched exclusively against method calls. It has higher +precedence than the `.` operator and this allows one to handle expressions like +`x.y` and `x.y()` differently if one is interfacing with a scripting language +for example. + +operator `.=` +------------- +This operator will be matched against assignments to missing fields. + +.. code-block:: nim + a.b = c # becomes `.=`(a, b, c) + + +Not nil annotation +================== + +**Note:** This is an experimental feature. It can be enabled with +``{.experimental: "notnil"}``. + +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 + {.experimental: "notnil"} + + type + PObject = ref TObj not nil + TProc = (proc (x, y: int)) not nil + + proc p(x: PObject) = + echo "not nil" + + # compiler catches this: + p(nil) + + # 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 + +Concepts +======== + +Concepts, also known as "user-defined type classes", are used to specify an +arbitrary set of requirements that the matched type must satisfy. + +Concepts are written in the following form: + +.. code-block:: nim + type + Comparable = concept x, y + (x < y) is bool + + Stack[T] = concept s, var v + s.pop() is T + v.push(T) + + s.len is Ordinal + + for value in s: + value is T + +The concept is a match if: + +a) all of the expressions within the body can be compiled for the tested type +b) all statically evaluable boolean expressions in the body must be true + +The identifiers following the ``concept`` keyword represent instances of the +currently matched type. You can apply any of the standard type modifiers such +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 + 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 + 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 + type + # Let's imagine a user-defined casting framework with operators + # such as `val.to(string)` and `val.to(JSonValue)`. We can test + # for these with the following concept: + MyCastables = concept x + x.to(type string) + x.to(type JSonValue) + + # Let's define a couple of concepts, known from Algebra: + AdditiveMonoid* = concept x, y, type T + x + y is T + T.zero is T # require a proc such as `int.zero` or 'Position.zero' + + AdditiveGroup* = concept x, y, type T + 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 +default parameters are still applied in the concept body, it's also possible +to describe usage protocols that do not reveal implementation details. + +Much like generics, concepts are instantiated exactly once for each tested type +and any static code included within the body is executed only once. + + +Concept diagnostics +------------------- + +By default, the compiler will report the matching errors in concepts only when +no other overload can be selected and a normal compilation error is produced. +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 + 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. + + +Generic concepts and type binding rules +--------------------------------------- + +The concept types can be parametric just like the regular generic types: + +.. code-block:: nim + ### matrixalgo.nim + + import typetraits + + type + AnyMatrix*[R, C: static int; T] = concept m, var mvar, type M + M.ValueType is T + M.Rows == R + M.Cols == C + + m[int, int] is T + mvar[int, int] = T + + type TransposedType = stripGenericParams(M)[C, R, T] + + AnySquareMatrix*[N: static int, T] = AnyMatrix[N, N, T] + + AnyTransform3D* = AnyMatrix[4, 4, float] + + proc transposed*(m: AnyMatrix): m.TransposedType = + for r in 0 ..< m.R: + for c in 0 ..< m.C: + result[r, c] = m[c, r] + + proc determinant*(m: AnySquareMatrix): int = + ... + + proc setPerspectiveProjection*(m: AnyTransform3D) = + ... + + -------------- + ### matrix.nim + + type + Matrix*[M, N: static int; T] = object + data: array[M*N, T] + + proc `[]`*(M: Matrix; m, n: int): M.T = + M.data[m * M.N + n] + + proc `[]=`*(M: var Matrix; m, n: int; v: M.T) = + M.data[m * M.N + n] = v + + # Adapt the Matrix type to the concept's requirements + template Rows*(M: typedesc[Matrix]): int = M.M + template Cols*(M: typedesc[Matrix]): int = M.N + template ValueType*(M: typedesc[Matrix]): typedesc = M.T + + ------------- + ### usage.nim + + import matrix, matrixalgo + + var + m: Matrix[3, 3, int] + projectionMatrix: Matrix[4, 4, float] + + 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 +resembles the way generic parameters of callable symbols are inferred on +call sites. + +Unbound types can appear both as params to calls such as `s.push(T)` and +on the right-hand side of the ``is`` operator in cases such as `x.pop is T` +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 + 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. + +Just like in regular type classes, Nim discriminates between ``bind once`` +and ``bind many`` types when matching the concept. You can add the ``distinct`` +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 + type + Enumerable[T] = concept e + for v in e: + v is T + + type + MyConcept = concept o + # this could be inferred to a type such as Enumerable[int] + o.foo is distinct Enumerable + + # this could be inferred to a different type such as Enumerable[float] + o.bar is distinct Enumerable + + # 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 + type + MyConcept = concept x + type T1 = auto + x.foo(T1) + x.bar(T1) # both procs must accept the same type + + type T2 = seq[SomeNumber] + 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, +the concept will be automatically instantiated with the bind once auto type +in the place of each missing generic param. + +Please note that generic concepts such as `Enumerable[T]` can be matched +against concrete types such as `string`. Nim doesn't require the concept +type to have the same number of parameters as the type being matched. +If you wish to express a requirement towards the generic parameters of +the matched type, you can use a type mapping operator such as `genericHead` +or `stripGenericParams` within the body of the concept to obtain the +uninstantiated version of the type, which you can then try to instantiate +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" + + import sugar, typetraits + + type + Functor[A] = concept f + type MatchedGenericType = genericHead(typeof(f)) + # `f` will be a value of a type such as `Option[T]` + # `MatchedGenericType` will become the `Option` type + + f.val is A + # The Functor should provide a way to obtain + # a value stored inside it + + type T = auto + map(f, A -> T) is MatchedGenericType[T] + # And it should provide a way to map one instance of + # the Functor to a instance of a different type, given + # a suitable `map` operation for the enclosed values + + import options + echo Option[int] is Functor # prints true + + +Concept derived values +---------------------- + +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 + type + DateTime = concept t1, t2, type T + const Min = T.MinDate + T.Now is T + + t1 < t2 is bool + + type TimeSpan = typeof(t1 - t2) + TimeSpan * int is TimeSpan + TimeSpan + TimeSpan is TimeSpan + + t1 + TimeSpan is T + + proc eventsJitter(events: Enumerable[DateTime]): float = + var + # this variable will have the inferred TimeSpan type for + # the concrete Date-like value the proc was called with: + averageInterval: DateTime.TimeSpan + + deviation: float + ... + + +Concept refinement +------------------ + +When the matched type within a concept is directly tested against a different +concept, we say that the outer concept is a refinement of the inner concept and +thus it is more-specific. When both concepts are matched in a call during +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 + type + Graph = concept g, type G of EquallyComparable, Copyable + type + VertexType = G.VertexType + EdgeType = G.EdgeType + + VertexType is Copyable + EdgeType is Copyable + + var + v: VertexType + e: EdgeType + + IncidendeGraph = concept of Graph + # symbols such as variables and types from the refined + # concept are automatically in scope: + + g.source(e) is VertexType + g.target(e) is VertexType + + g.outgoingEdges(v) is Enumerable[EdgeType] + + BidirectionalGraph = concept g, type G + # The following will also turn the concept into a refinement when it + # comes to overload resolution, but it doesn't provide the convenient + # symbol inheritance + g is IncidendeGraph + + g.incomingEdges(G.VertexType) is Enumerable[G.EdgeType] + + 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 + ---------------------- + + Concepts can also be used to convert a whole range of types to a single type or + a small set of simpler types. This is achieved with a `return` statement within + the concept body: + + .. code-block:: nim + type + Stringable = concept x + $x is string + return $x + + StringRefValue[CharType] = object + base: ptr CharType + len: int + + StringRef = concept x + # the following would be an overloaded proc for cstring, string, seq and + # other user-defined types, returning either a StringRefValue[char] or + # StringRefValue[wchar] + return makeStringRefValue(x) + + # the varargs param will here be converted to an array of StringRefValues + # the proc will have only two instantiations for the two character types + proc log(format: static string, varargs[StringRef]) + + # this proc will allow char and wchar values to be mixed in + # 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]) + + +.. + VTable types + ------------ + + Concepts allow Nim to define a great number of algorithms, using only + static polymorphism and without erasing any type information or sacrificing + any execution speed. But when polymorphic collections of objects are required, + the user must use one of the provided type erasure techniques - either common + base types or VTable types. + + VTable types are represented as "fat pointers" storing a reference to an + object together with a reference to a table of procs implementing a set of + required operations (the so called vtable). + + In contrast to other programming languages, the vtable in Nim is stored + externally to the object, allowing you to create multiple different vtable + views for the same object. Thus, the polymorphism in Nim is unbounded - + any type can implement an unlimited number of protocols or interfaces not + originally envisioned by the type's author. + + Any concept type can be turned into a VTable type by using the ``vtref`` + or the ``vtptr`` compiler magics. Under the hood, these magics generate + a converter type class, which converts the regular instances of the matching + types to the corresponding VTable type. + + .. code-block:: nim + type + IntEnumerable = vtref Enumerable[int] + + MyObject = object + enumerables: seq[IntEnumerable] + streams: seq[OutputStream.vtref] + + proc addEnumerable(o: var MyObject, e: IntEnumerable) = + o.enumerables.add e + + 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 + concrete types. All such calls should include exactly one param of the type + matched against the concept (not necessarily in the first position), which + will be considered the value bound to the vtable. + + Overloads will be created for all captured procs, accepting the vtable type + in the position of the captured underlying object. + + Under these rules, it's possible to obtain a vtable type for a concept with + unbound type parameters or one instantiated with metatypes (type classes), + but it will include a smaller number of captured procs. A completely empty + vtable will be reported as an error. + + The ``vtref`` magic produces types which can be bound to ``ref`` types and + the ``vtptr`` magic produced types bound to ``ptr`` types. + + +Type bound operations +===================== + +There are 4 operations that are bound to a type: + +1. Assignment +2. Moves +3. Destruction +4. Deep copying for communication between threads + +These operations can be *overridden* instead of *overloaded*. This means the +implementation is automatically lifted to structured types. For instance if type +``T`` has an overridden assignment operator ``=`` this operator is also used +for assignments of the type ``seq[T]``. Since these operations are bound to a +type they have to be bound to a nominal type for reasons of simplicity of +implementation: This means an overridden ``deepCopy`` for ``ref T`` is really +bound to ``T`` and not to ``ref T``. This also means that one cannot override +``deepCopy`` for both ``ptr T`` and ``ref T`` at the same time; instead a +helper distinct or object type has to be used for one pointer type. + +Assignments, moves and destruction are specified in +the `destructors `_ document. + + +deepCopy +-------- + +``=deepCopy`` is a builtin that is invoked whenever data is passed to +a ``spawn``'ed proc to ensure memory safety. The programmer can override its +behaviour for a specific ``ref`` or ``ptr`` type ``T``. (Later versions of the +language may weaken this restriction.) + +The signature has to be: + +.. code-block:: 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. + + +Case statement macros +===================== + +A macro that needs to be called `match`:idx: can be used to rewrite +``case`` statements in order to implement `pattern matching`:idx: for +certain types. The following example implements a simplistic form of +pattern matching for tuples, leveraging the existing equality operator +for tuples (as provided in ``system.==``): + +.. code-block:: nim + :test: "nim c $1" + + {.experimental: "caseStmtMacros".} + + import macros + + macro match(n: tuple): untyped = + result = newTree(nnkIfStmt) + let selector = n[0] + for i in 1 ..< n.len: + let it = n[i] + case it.kind + of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr: + result.add it + of nnkOfBranch: + for j in 0..it.len-2: + let cond = newCall("==", selector, it[j]) + result.add newTree(nnkElifBranch, cond, it[^1]) + else: + error "'match' cannot handle this node", it + echo repr result + + case ("foo", 78) + of ("foo", 78): echo "yes" + of ("bar", 88): echo "no" + else: discard + + +Currently case statement macros must be enabled explicitly +via ``{.experimental: "caseStmtMacros".}``. + +``match`` macros are subject to overload resolution. First the +``case``'s selector expression is used to determine which ``match`` +macro to call. To this macro is then passed the complete ``case`` +statement body and the macro is evaluated. + +In other words, the macro needs to transform the full ``case`` statement +but only the statement's selector expression is used to determine which +macro to call. + + +Term rewriting macros +===================== + +Term rewriting macros are macros or templates that have not only +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 + 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 +curlies is the pattern to match against. The operators ``*``, ``**``, +``|``, ``~`` have a special meaning in patterns if they are written in infix +notation, so to match verbatim against ``*`` the ordinary function call syntax +needs to be used. + +Term rewriting macro are applied recursively, up to a limit. This means that +if the result of a term rewriting macro is eligible for another rewriting, +the compiler will try to perform it, and so on, until no more optimizations +are applicable. To avoid putting the compiler into an infinite loop, there is +a hard limit on how many times a single term rewriting macro can be applied. +Once this limit has been passed, the term rewriting macro will be ignored. + +Unfortunately optimizations are hard to get right and even the tiny example +is **wrong**: + +.. code-block:: nim + template optMul{`*`(a, 2)}(a: int): int = a+a + + proc f(): int = + echo "side effect!" + 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 + template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a + + proc f(): int = + echo "side effect!" + 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 +differently. + +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 + template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a + +What optimizers really need to do is a *canonicalization*: + +.. code-block:: 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. + + + +Parameter constraints +--------------------- + +The `parameter constraint`:idx: expression can use the operators ``|`` (or), +``&`` (and) and ``~`` (not) and the following predicates: + +=================== ===================================================== +Predicate Meaning +=================== ===================================================== +``atom`` The matching node has no children. +``lit`` The matching node is a literal like "abc", 12. +``sym`` The matching node must be a symbol (a bound + identifier). +``ident`` The matching node must be an identifier (an unbound + identifier). +``call`` The matching AST must be a call/apply expression. +``lvalue`` The matching AST must be an lvalue. +``sideeffect`` The matching AST must have a side effect. +``nosideeffect`` The matching AST must have no side effect. +``param`` A symbol which is a parameter. +``genericparam`` A symbol which is a generic parameter. +``module`` A symbol which is a module. +``type`` A symbol which is a type. +``var`` A symbol which is a variable. +``let`` A symbol which is a ``let`` variable. +``const`` A symbol which is a constant. +``result`` The special ``result`` variable. +``proc`` A symbol which is a proc. +``method`` A symbol which is a method. +``iterator`` A symbol which is an iterator. +``converter`` A symbol which is a converter. +``macro`` A symbol which is a macro. +``template`` A symbol which is a template. +``field`` A symbol which is a field in a tuple or an object. +``enumfield`` A symbol which is a field in an enumeration. +``forvar`` A for loop variable. +``label`` A label (used in ``block`` statements). +``nk*`` The matching AST must have the specified kind. + (Example: ``nkIfStmt`` denotes an ``if`` statement.) +``alias`` States that the marked parameter needs to alias + with *some* other parameter. +``noalias`` States that *every* other parameter must not alias + with the marked parameter. +=================== ===================================================== + +Predicates that share their name with a keyword have to be escaped with +backticks. +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 + 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 + + +Pattern operators +----------------- + +The operators ``*``, ``**``, ``|``, ``~`` have a special meaning in patterns +if they are written in infix notation. + + +The ``|`` operator +~~~~~~~~~~~~~~~~~~ + +The ``|`` operator if used as infix operator creates an ordered choice: + +.. code-block:: 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 + 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 +semantics anyway. In fact they can be deactivated with the ``--patterns:off`` +command line option or temporarily with the ``patterns`` pragma. + + +The ``{}`` operator +~~~~~~~~~~~~~~~~~~~ + +A pattern expression can be bound to a pattern parameter via the ``expr{param}`` +notation: + +.. code-block:: nim + template t{(0|1|2){x}}(x: untyped): untyped = x+1 + let a = 1 + # outputs 2: + echo a + + +The ``~`` operator +~~~~~~~~~~~~~~~~~~ + +The ``~`` operator is the **not** operator in patterns: + +.. code-block:: nim + template t{x = (~x){y} and (~x){z}}(x, y, z: bool) = + x = y + if x: x = z + + var + a = false + b = true + c = false + a = b and c + echo a + + +The ``*`` operator +~~~~~~~~~~~~~~~~~~ + +The ``*`` operator can *flatten* a nested binary expression like ``a & b & c`` +to ``&(a, b, c)``: + +.. code-block:: nim + var + calls = 0 + + proc `&&`(s: varargs[string]): string = + result = s[0] + for i in 1..len(s)-1: result.add s[i] + inc calls + + template optConc{ `&&` * a }(a: string): untyped = &&a + + let space = " " + echo "my" && (space & "awe" && "some " ) && "concat" + + # 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 +arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"`` +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") + + +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 + import macros + + type + Matrix = object + dummy: int + + proc `*`(a, b: Matrix): Matrix = discard + proc `+`(a, b: Matrix): Matrix = discard + proc `-`(a, b: Matrix): Matrix = discard + proc `$`(a: Matrix): string = result = $a.dummy + proc mat21(): Matrix = + result.dummy = 21 + + macro optM{ (`+`|`-`|`*`) ** a }(a: Matrix): untyped = + echo treeRepr(a) + result = newCall(bindSym"mat21") + + 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:: + + Arglist + Sym "x" + Sym "y" + Sym "z" + Sym "*" + Sym "+" + Sym "x" + Sym "-" + +(Which is the reverse polish notation of ``x + y * z - x``.) + + +Parameters +---------- + +Parameters in a pattern are type checked in the matching process. If a +parameter is of the type ``varargs`` it is treated specially and it can match +0 or more arguments in the AST to be matched against: + +.. code-block:: nim + template optWrite{ + write(f, x) + ((write|writeLine){w})(f, y) + }(x, y: varargs[untyped], f: File, w: untyped) = + w(f, x, y) + + + +Example: Partial evaluation +--------------------------- + +The following example shows how some simple partial evaluation can be +implemented with term rewriting: + +.. code-block:: 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 +----------------- + +The following example shows how some form of hoisting can be implemented: + +.. code-block:: nim + import pegs + + template optPeg{peg(pattern)}(pattern: string{lit}): Peg = + var gl {.global, gensym.} = peg(pattern) + gl + + 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 +stored in a global ``gl`` which is then re-used. This optimization is called +hoisting because it is comparable to classical loop hoisting. + + +AST based overloading +===================== + +Parameter constraints can also be used for ordinary routine parameters; these +constraints affect ordinary overloading resolution then: + +.. code-block:: nim + proc optLit(a: string{lit|`const`}) = + echo "string literal" + proc optLit(a: string) = + echo "no string literal" + + const + constant = "abc" + + var + variable = "xyz" + + optLit("literal") + optLit(constant) + optLit(variable) + +However, the constraints ``alias`` and ``noalias`` are not available in +ordinary routines. + + +Parallel & Spawn +================ + +Nim has two flavors of parallelism: +1) `Structured`:idx: parallelism via the ``parallel`` statement. +2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement. + +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 `_ +module to work. + +Somewhat confusingly, ``spawn`` is also used in the ``parallel`` statement +with slightly different semantics. ``spawn`` always takes a call expression of +the form ``f(a, ...)``. Let ``T`` be ``f``'s return type. If ``T`` is ``void`` +then ``spawn``'s return type is also ``void`` otherwise it is ``FlowVar[T]``. + +Within a ``parallel`` section sometimes the ``FlowVar[T]`` is eliminated +to ``T``. This happens when ``T`` does not contain any GC'ed memory. +The compiler can ensure the location in ``location = spawn f(...)`` is not +read prematurely within a ``parallel`` section and so there is no need for +the overhead of an indirection via ``FlowVar[T]`` to ensure correctness. + +**Note**: Currently exceptions are not propagated between ``spawn``'ed tasks! + + +Spawn statement +--------------- + +`spawn`:idx: can be used to pass a task to the thread pool: + +.. code-block:: nim + import threadpool + + proc processLine(line: string) = + discard "do some heavy lifting here" + + 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: + +* It must be a call expression ``f(a, ...)``. +* ``f`` must be ``gcsafe``. +* ``f`` must not have the calling convention ``closure``. +* ``f``'s parameters may not be of type ``var``. + This means one has to use raw ``ptr``'s for data passing reminding the + programmer to be careful. +* ``ref`` parameters are deeply copied which is a subtle semantic change and + can cause performance problems but ensures memory safety. This deep copy + is performed via ``system.deepCopy`` and so can be overridden. +* For *safe* data exchange between ``f`` and the caller a global ``TChannel`` + needs to be used. However, since spawn can return a result, often no further + communication is required. + + +``spawn`` executes the passed expression on the thread pool and returns +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 + import threadpool, ... + + # wait until 2 out of 3 servers received the update: + proc main = + var responses = newSeq[FlowVarBase](3) + for i in 0..2: + responses[i] = spawn tellServer(Update, "key", "value") + var index = blockUntilAny(responses) + 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 +a data flow variable: ``T`` has to be of the type ``ref``, ``string``, ``seq`` +or of a type that doesn't contain a type that is garbage collected. This +restriction is not hard to work-around in practice. + + + +Parallel statement +------------------ + +Example: + +.. code-block:: nim + :test: "nim c --threads:on $1" + + # Compute PI in an inefficient way + import strutils, math, threadpool + {.experimental: "parallel".} + + proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1) + + proc pi(n: int): float = + var ch = newSeq[float](n+1) + parallel: + for k in 0..ch.high: + ch[k] = spawn term(float(k)) + for k in 0..ch.high: + result += ch[k] + + echo formatFloat(pi(5000)) + + +The parallel statement is the preferred mechanism to introduce parallelism in a +Nim program. A subset of the Nim language is valid within a ``parallel`` +section. This subset is checked during semantic analysis to be free of data +races. A sophisticated `disjoint checker`:idx: ensures that no data races are +possible even though shared memory is extensively supported! + +The subset is in fact the full language with the following +restrictions / changes: + +* ``spawn`` within a ``parallel`` section has special semantics. +* Every location of the form ``a[i]`` and ``a[i..j]`` and ``dest`` where + ``dest`` is part of the pattern ``dest = spawn f(...)`` has to be + provably disjoint. This is called the *disjoint check*. +* Every other complex location ``loc`` that is used in a spawned + proc (``spawn f(loc)``) has to be immutable for the duration of + the ``parallel`` section. This is called the *immutability check*. Currently + it is not specified what exactly "complex location" means. We need to make + this an optimization! +* Every array access has to be provably within bounds. This is called + the *bounds check*. +* Slices are optimized so that no copy is performed. This optimization is not + yet performed for ordinary slices outside of a ``parallel`` section. + + +Guards and locks +================ + +Apart from ``spawn`` and ``parallel`` Nim also provides all the common low level +concurrency mechanisms like locks, atomic intrinsics or condition variables. + +Nim significantly improves on the safety of these features via additional +pragmas: + +1) A `guard`:idx: annotation is introduced to prevent data races. +2) Every access of a guarded memory location needs to happen in an + appropriate `locks`:idx: statement. +3) Locks and routines can be annotated with `lock levels`:idx: to allow + potential deadlocks to be detected during semantic analysis. + + +Guards and the locks section +---------------------------- + +Protecting global variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Object fields and global variables can be annotated via a ``guard`` pragma: + +.. code-block:: nim + var glock: TLock + var gdata {.guard: glock.}: int + +The compiler then ensures that every access of ``gdata`` is within a ``locks`` +section: + +.. code-block:: nim + proc invalid = + # invalid: unguarded access: + echo gdata + + proc valid = + # 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 +is executed before any concurrent action happens. + +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) = + 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 + var dummyLock {.compileTime.}: int + var atomicCounter {.guard: dummyLock.}: int + + template atomicRead(x): untyped = + {.locks: [dummyLock].}: + memoryReadBarrier() + 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. + + +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: + +.. code-block:: nim + type + ProtectedCounter = object + v {.guard: L.}: int + L: TLock + + 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 + proc incCounters(counters: var openArray[ProtectedCounter]) = + for i in 0..counters.high: + pthread_mutex_lock(counters[i].L) + {.locks: [counters[i].L].}: + try: + 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 +`path analysis`:idx: because it deals with paths to locations +like ``obj.field[i].fieldB[j]``. + +The path analysis is **currently unsound**, but that doesn't make it useless. +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 + {.locks: [a[i].L].}: + inc i + access a[i].v + + + +Lock levels +----------- + +Lock levels are used to enforce a global locking order in order to detect +potential deadlocks during semantic analysis. A lock level is an constant +integer in the range 0..1_000. Lock level 0 means that no lock is acquired at +all. + +If a section of code holds a lock of level ``M`` than it can also acquire any +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 + var a, b: TLock[2] + var x: TLock[1] + # invalid locking order: TLock[1] cannot be acquired before TLock[2]: + {.locks: [x].}: + {.locks: [a].}: + ... + # valid locking order: TLock[2] acquired before TLock[1]: + {.locks: [a].}: + {.locks: [x].}: + ... + + # invalid locking order: TLock[2] acquired before TLock[2]: + {.locks: [a].}: + {.locks: [b].}: + ... + + # 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 + template multilock(a, b: ptr TLock; body: untyped) = + if cast[ByteAddress](a) < cast[ByteAddress](b): + pthread_mutex_lock(a) + pthread_mutex_lock(b) + else: + pthread_mutex_lock(b) + pthread_mutex_lock(a) + {.locks: [a, b].}: + try: + body + 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 + 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 +relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}`` +iff (M <= N). + +The ``locks`` pragma can also take the special value ``"unknown"``. This +is useful in the context of dynamic method dispatching. In the following +example, the compiler can infer a lock level of 0 for the ``base`` case. +However, one of the overloaded methods calls a procvar which is +potentially locking. Thus, the lock level of calling ``g.testMethod`` +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 + type SomeBase* = ref object of RootObj + type SomeDerived* = ref object of SomeBase + memberProc*: proc () + + method testMethod(g: SomeBase) {.base, locks: "unknown".} = discard + method testMethod(g: SomeDerived) = + if g.memberProc != nil: + g.memberProc() + + +noRewrite pragma +---------------- + +Term rewriting macros and templates are currently greedy and +they will rewrite as long as there is a match. +There was no way to ensure some rewrite happens only once, +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 + template pwnEcho{echo(x)}(x: expr) = + {.noRewrite.}: echo("pwned!") + + echo "ab" + +``noRewrite`` pragma can be useful to control term-rewriting macros recursion. + + +Taint mode +========== + +The Nim compiler and most parts of the standard library support +a taint mode. Input strings are declared with the `TaintedString`:idx: +string type declared in the ``system`` module. + +If the taint mode is turned on (via the ``--taintMode:on`` command line +option) it is a distinct string type which helps to detect input +validation errors: + +.. code-block:: nim + echo "your name: " + var name: TaintedString = stdin.readline + # it is safe here to output the name without any input validation, so + # we simply convert `name` to string to make the compiler happy: + echo "hi, ", name.string + +If the taint mode is turned off, ``TaintedString`` is simply an alias for +``string``. + + +Aliasing restrictions in parameter passing +========================================== + +**Note**: The aliasing restrictions are currently not enforced by the +implementation and need to be fleshed out further. + +"Aliasing" here means that the underlying storage locations overlap in memory +at runtime. An "output parameter" is a parameter of type ``var T``, +an input parameter is any parameter that is not of type ``var``. + +1. Two output parameters should never be aliased. +2. An input and an output parameter should not be aliased. +3. An output parameter should never be aliased with a global or thread local + variable referenced by the called proc. +4. An input parameter should not be aliased with a global or thread local + variable updated by the called proc. + +One problem with rules 3 and 4 is that they affect specific global or thread +local variables, but Nim's effect tracking only tracks "uses no global variable" +via ``.noSideEffect``. The rules 3 and 4 can also be approximated by a different rule: + +5. A global or thread local variable (or a location derived from such a location) + can only passed to a parameter of a ``.noSideEffect`` proc. + + +Noalias annotation +================== + +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`` keyword and means that +the underlying pointer is pointing to a unique location in memory, no other aliases to +this location exist. It is *unchecked* that this alias restriction is followed, if the +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. (Which is also why the name ``noalias`` was choosen instead of a more +verbose name like ``unsafeAssumeNoAlias``.) + + +Strict funcs +============ + +Since version 1.4 a stricter definition of "side effect" is available. In addition +to the existing rule that a side effect is calling a function with side effects +the following rule is also enforced: + +Any mutation to an object does count as a side effect if that object is reachable +via a parameter that is not declared as a ``var`` parameter. + +For example: + +.. code-block:: nim + + {.experimental: "strictFuncs".} + + type + Node = ref object + le, ri: Node + data: string + + func len(n: Node): int = + # valid: len does not have side effects + var it = n + while it != nil: + inc result + it = it.ri + + func mut(n: Node) = + let m = n # is the statement that connected the mutation to the parameter + 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>`_. + + +View types +========== + +**Note**: ``--experimental:views`` is more effective +with ``--experimental:strictFuncs``. + +A view type is a type that is or contains one of the following types: + +- ``var T`` (mutable view into ``T``) +- ``lent T`` (immutable view into ``T``) +- ``openArray[T]`` (pair of (pointer to array of ``T``, size)) + +For example: + +.. code-block:: nim + + type + View1 = var int + View2 = openArray[byte] + View3 = lent string + View4 = 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 + + type + NotView1 = proc (x: openArray[int]) + NotView2 = ptr openArray[char] + NotView3 = ptr array[4, var int] + + +A *mutable* view type is a type that is or contains a ``var T`` type. +An *immutable* view type is a view type that is not a mutable view type. + +A *view* is a symbol (a let, var, const, etc.) that has a view type. + +Since version 1.4 Nim allows view types to be used as local variables. +This feature needs to be enabled via ``{.experimental: "views".}``. + +A local variable of a view type *borrows* from the locations and +it is statically enforced that the view does not outlive the location +it was borrowed from. + +For example: + +.. code-block:: nim + + {.experimental: "views".} + + proc take(a: openArray[int]) = + echo a.len + + proc main(s: seq[int]) = + var x: openArray[int] = s # 'x' is a view into 's' + # it is checked that 'x' does not outlive 's' and + # that 's' is not mutated. + for i in 0 .. high(x): + echo x[i] + take(x) + + take(x.toOpenArray(0, 1)) # slicing remains possible + let y = x # create a view from a view + take y + # it is checked that 'y' does not outlive 'x' and + # that 'x' is not mutated as long as 'y' lives. + + + main(@[11, 22, 33]) + + +A local variable of a view type can borrow from a location +derived from a parameter, another local variable, a global ``const`` or ``let`` +symbol or a thread-local ``var`` or ``let``. + +Let ``p`` the proc that is analysed for the correctness of the borrow operation. + +Let ``source`` be one of: + +- A formal parameter of ``p``. Note that this does not cover parameters of + inner procs. +- The ``result`` symbol of ``p``. +- A local ``var`` or ``let`` or ``const`` of ``p``. Note that this does + not cover locals of inner procs. +- A thread-local ``var`` or ``let``. +- A global ``let`` or ``const``. +- A constant array/seq/object/tuple constructor. + + +Path expressions +---------------- + +A location derived from ``source`` is then defined as a path expression that +has ``source`` as the owner. A path expression ``e`` is defined recursively: + +- ``source`` itself is a path expression. +- Container access like ``e[i]`` is a path expression. +- Tuple access ``e[0]`` is a path expression. +- Object field access ``e.field`` is a path expression. +- ``system.toOpenArray(e, ...)`` is a path expression. +- Pointer dereference ``e[]`` is a path expression. +- An address ``addr e``, ``unsafeAddr e`` is a path expression. +- A type conversion ``T(e)`` is a path expression. +- A cast expression ``cast[T](e)`` is a path expression. +- ``f(e, ...)`` is a path expression if ``f``'s return type is a view type. + Because the view can only have been borrowed from ``e``, we then know + that owner of ``f(e, ...)`` is ``e``. + + +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 https://nim-lang.org/docs/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 +from both a mutable or an immutable location. + +The *duration* of a borrow is the span of commands beginning from the assignment +to the view and ending with the last usage of the view. + +For the duration of the borrow operation, no mutations to the borrowed locations +may be performed except via the potentially mutable view that borrowed from the +location. The borrowed location is said to be *sealed* during the borrow. + +.. code-block:: nim + + {.experimental: "views".} + + type + Obj = object + field: string + + proc dangerous(s: var seq[Obj]) = + 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 + + 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>`_ +feature. In other words ``--experimental:views`` works better +with ``--experimental:strictFuncs``. + +The analysis is currently control flow insensitive: + +.. code-block:: 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 +will never do that at runtime. + + +Start of a borrow +----------------- + +A borrow starts with one of the following: + +- The assignment of a non-view-type to a view-type. +- The assignment of a location that is derived from a local parameter + to a view-type. + + +End of a borrow +--------------- + +A borrow operation ends with the last usage of the view variable. + + +Reborrows +--------- + +A view ``v`` can borrow from multiple different locations. However, the borrow +is always the full span of ``v``'s lifetime and every location that is borrowed +from is sealed during ``v``'s lifetime. + + +Algorithm +--------- + +The following section is an outline of the algorithm that the current implementation +uses. The algorithm performs two traversals over the AST of the procedure or global +section of code that uses a view variable. No fixpoint iterations are performed, the +complexity of the analysis is O(N) where N is the number of nodes of the AST. + +The first pass over the AST computes the lifetime of each local variable based on +a notion of an "abstract time", in the implementation it's a simple integer that is +incremented for every visited node. + +In the second pass information about the underlying object "graphs" is computed. +Let ``v`` be a parameter or a local variable. Let ``G(v)`` be the graph +that ``v`` belongs to. A graph is defined by the set of variables that belong +to the graph. Initially for all ``v``: ``G(v) = {v}``. Every variable can only +be part of a single graph. + +Assignments like ``a = b`` "connect" two variables, both variables end up in the +same graph ``{a, b} = G(a) = G(b)``. Unfortunately, the pattern to look for is +much more complex than that and can involve multiple assignment targets +and sources:: + + f(x, y) = g(a, b) + +connects ``x`` and ``y`` to ``a`` and ``b``: ``G(x) = G(y) = G(a) = G(b) = {x, y, a, b}``. +A type based alias analysis rules out some of these combinations, for example +a ``string`` value cannot possibly be connected to a ``seq[int]``. + +A pattern like ``v[] = value`` or ``v.field = value`` marks ``G(v)`` as mutated. +After the second pass a set of disjoint graphs was computed. + +For strict functions it is then enforced that there is no graph that is both mutated +and has an element that is an immutable parameter (that is a parameter that is not +of type ``var T``). + +For borrow checking a different set of checks is performed. Let ``v`` be the view +and ``b`` the location that is borrowed from. + +- The lifetime of ``v`` must not exceed ``b``'s lifetime. Note: The lifetime of + a parameter is the complete proc body. +- If ``v`` is a mutable view and ``v`` is used to actually mutate the + borrowed location, then ``b`` has to be a mutable location. + Note: If it is not actually used for mutation, borrowing a mutable view from an + immutable location is allowed! This allows for many important idioms and will be + justified in an upcoming RFC. +- During ``v``'s lifetime, ``G(b)`` can only be modified by ``v`` (and only if + ``v`` is a mutable view). +- If ``v`` is ``result`` then ``b`` has to be a location derived from the first + formal parameter or from a constant location. +- A view cannot be used for a read or a write access before it was assigned to. diff --git a/doc/manual_experimental_strictnotnil.md b/doc/manual_experimental_strictnotnil.md deleted file mode 100644 index a6fa6cda8..000000000 --- a/doc/manual_experimental_strictnotnil.md +++ /dev/null @@ -1,255 +0,0 @@ -Strict not nil checking -========================= - -.. default-role:: code -.. include:: rstcommon.rst - -**Note:** This feature is experimental, you need to enable it with - - ```nim - {.experimental: "strictNotNil".} - ``` - -or - - ```cmd - nim c --experimental:strictNotNil - ``` - -In the second case it would check builtin and imported modules as well. - -It checks the nilability of ref-like types and makes dereferencing safer based on flow typing and `not nil` annotations. - -Its implementation is different than the `notnil` one: defined under `strictNotNil`. Keep in mind the difference in option names, be careful with distinguishing them. - -We check several kinds of types for nilability: - -- ref types -- pointer types -- proc types -- cstrings - -nil -------- - -The default kind of nilability types is the nilable kind: they can have the value `nil`. -If you have a non-nilable type `T`, you can use `T nil` to get a nilable type for it. - - -not nil --------- - -You can annotate a type where nil isn't a valid value with `not nil`. - - ```nim - type - NilableObject = ref object - a: int - Object = NilableObject not nil - - Proc = (proc (x, y: int)) - - proc p(x: Object) = - echo x.a # ensured to dereference without an error - # compiler catches this: - p(nil) - # and also this: - var x: NilableObject - if x.isNil: - 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 dereferenced, this -produces a warning by default, you can turn this into an error using -the compiler options `--warningAsError:strictNotNil`:option:. - -If a type is nilable, you should dereference its values only after a `isNil` or equivalent check. - -local turn on/off ---------------------- - -You can still turn off nil checking on function/module level by using a `{.strictNotNil: off.}` pragma. - -.. - 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). - -- `Safe` means it shouldn't be nil at that point: e.g. after assignment to - a non-nil value or `not a.isNil` check -- `MaybeNil` means it might be nil, but it might not be nil: e.g. an argument, - a call argument or a value after an `if` and `else`. -- `Nil` means it should be nil at that point; e.g. after an assignment to - `nil` or a `.isNil` check. -- `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 -in `MaybeNil` or `Nil` state. - - -type nilability ----------------- - -Types are either nilable or non-nilable. -When you pass a param or a default value, we use the type : for nilable types we return `MaybeNil` -and for non-nilable `Safe`. - -.. - TODO: fix the manual here. (This is not great, as default values for non-nilables and nilables are usually actually `nil` , so we should think a bit more about this section.) - -params rules ------------- - -Param's nilability is detected based on type nilability. We use the type of the argument to detect the nilability. - - -assignment rules ------------------ - -Let's say we have `left = right`. - -When we assign, we pass the right's nilability to the left's expression. There should be special handling of aliasing and compound expressions which we specify in their sections. (Assignment is a possible alias `move` or `move out`). - -call args rules ------------------ - -When we call with arguments, we have two cases when we might change the nilability. - - ```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). - - ```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`. Dependants become `MaybeNil`. - - -branches rules ---------------- - -Branches are the reason we do nil checking like this: with flow checking. -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: - - ```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`. - -`not` reverses the nilability, `and` is similar to "forking" : the right expression is checked in the layer resulting from the left one and `or` is similar to "merging": the right and left expression should be both checked in the original layer. - -`isNil`, `== nil` make expressions `Nil`. If there is a `not` or `!= nil`, they make them `Safe`. -We also reverse the nilability in the opposite branch: e.g. `else`. - -compound expressions: field, index expressions ------------------------------------------------ - -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 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. - -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 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 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 ------------------ - -When we assign an object construction, we should track the fields as well: - - - ```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. - -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 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. - - ```nim - for a in c: - if not a.isNil: - b() - break - code # here a: Nil , because if not, we would have breaked - ``` - - -aliasing ------------- - -We support alias detection for local expressions. - -We track sets of aliased expressions. We start with all nilable local expressions in separate sets. -Assignments and other changes to nilability can move / move out expressions of sets. - -`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. - - ```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. - - - ```nim - var left = b - left = nil # moving out - ``` - -.. - initialization of non nilable and nilable values - ------------------------------------------------- - - TODO - -warnings and errors ---------------------- - -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/manual_experimental_strictnotnil.rst b/doc/manual_experimental_strictnotnil.rst new file mode 100644 index 000000000..d779162f4 --- /dev/null +++ b/doc/manual_experimental_strictnotnil.rst @@ -0,0 +1,235 @@ + +Strict not nil checking +========================= + +**Note:** This feature is experimental, you need to enable it with + +.. code-block:: nim + {.experimental: "strictNotNil".} + +or + +.. code-block:: bash + nim c --experimental:strictNotNil + +In the second case it would check builtin and imported modules as well. + +It checks the nilability of ref-like types and makes dereferencing safer based on flow typing and ``not nil`` annotations. + +Its implementation is different than the ``notnil`` one: defined under ``strictNotNil``. Keep in mind the difference in option names, be careful with distinguishing them. + +We check several kinds of types for nilability: + +- ref types +- pointer types +- proc types +- cstrings + +nil +------- + +The default kind of nilability types is the nilable kind: they can have the value ``nil``. +If you have a non-nilable type ``T``, you can use ``T nil`` to get a nilable type for it. + + +not nil +-------- + +You can annotate a type where nil isn't a valid value with ``not nil``. + +.. code-block:: nim + type + NilableObject = ref object + a: int + Object = NilableObject not nil + + Proc = (proc (x, y: int)) + + proc p(x: Object) = + echo x.a # ensured to dereference without an error + # compiler catches this: + p(nil) + # and also this: + var x: NilableObject + if x.isNil: + 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 produces a warning by default, you can turn this into an error using the compiler options ``--warningAsError:strictNotNil`` + +If a type is nilable, you should dereference its values only after a ``isNil`` or equivalent check. + +local turn on/off +--------------------- + +You can still turn off nil checking on function/module level by using a ``{.strictNotNil: off}.`` pragma. +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). + +``Safe`` means it shouldn't be nil at that point: e.g. after assignment to a non-nil value or ``not a.isNil`` check +``MaybeNil`` means it might be nil, but it might not be nil: e.g. an argument, a call argument or a value after an ``if`` and ``else``. +``Nil`` means it should be nil at that point; e.g. after an assignment to ``nil`` or a ``.isNil`` check. + +``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 +in ``MaybeNil`` or ``Nil`` state. + + +type nilability +---------------- + +Types are either nilable or non-nilable. +When you pass a param or a default value, we use the type : for nilable types we return ``MaybeNil`` +and for non-nilable ``Safe``. + +TODO: fix the manual here. (This is not great, as default values for non-nilables and nilables are usually actually ``nil`` , so we should think a bit more about this section.) + +params rules +------------ + +Param's nilability is detected based on type nilability. We use the type of the argument to detect the nilability. + + +assignment rules +----------------- + +Let's say we have ``left = right``. + +When we assign, we pass the right's nilability to the left's expression. There should be special handling of aliasing and compound expressions which we specify in their sections. (Assignment is a possible alias ``move`` or ``move out``). + +call args rules +----------------- + +When we call with arguments, we have two cases when we might change the nilability. + +.. code-block:: 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 + 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``. + + +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`` + +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 + 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``. + +``not`` reverses the nilability, ``and`` is similar to "forking" : the right expression is checked in the layer resulting from the left one and ``or`` is similar to "merging": the right and left expression should be both checked in the original layer. + +``isNil``, ``== nil`` make expressions ``Nil``. If there is a ``not`` or ``!= nil``, they make them ``Safe``. +We also reverse the nilability in the opposite branch: e.g. ``else``. + +compound expressions: field, index expressions +----------------------------------------------- + +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 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. + +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 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]``. + +element tracking +----------------- + +When we assign an object construction, we should track the fields as well: + + +.. code-block:: 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. + +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. + +.. code-block:: nim + for a in c: + if not a.isNil: + b() + break + code # here a: Nil , because if not, we would have breaked + + +aliasing +------------ + +We support alias detection for local expressions. + +We track sets of aliased expressions. We start with all nilable local expressions in separate sets. +Assignments and other changes to nilability can move / move out expressions of sets. + +``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 + 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 + var left = b + left = nil # moving out + + +initialization of non nilable and nilable values +------------------------------------------------- + +TODO + +warnings and errors +--------------------- + +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 deleted file mode 100644 index a374749cb..000000000 --- a/doc/markdown_rst.md +++ /dev/null @@ -1,341 +0,0 @@ -========================================== -Nim-flavored Markdown and reStructuredText -========================================== - -:Author: Andrey Makarov -:Version: |nimversion| - -.. default-role:: code -.. include:: rstcommon.rst -.. contents:: - -.. importdoc:: docgen.md - -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 --------------------------------- - -* referencing to definitions in external files, see - [Markup external referencing] section -* 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. - -Referencing -=========== - -To be able to copy and share links Nim generates anchors for all -main document elements: - -* headlines (including document title) -* footnotes -* explicitly set anchors: RST internal cross-references and - inline internal targets -* Nim symbols (external referencing), see [Nim DocGen Tools Guide] for details. - -But direct use of those anchors have 2 problems: - -1. the anchors are usually mangled (e.g. spaces substituted to minus - signs, etc). -2. manual usage of anchors is not checked, so it's easy to get broken - links inside your project if e.g. spelling has changed for a heading - or you use a wrong relative path to your document. - -That's why Nim implementation has syntax for using -*original* labels for referencing. -Such referencing can be either local/internal or external: - -* Local referencing (inside any given file) is defined by - RST standard or Pandoc Markdown User guide. -* External (cross-document) referencing is a Nim-specific feature, - though it's not really different from local referencing by its syntax. - -Markup local referencing ------------------------- - -There are 2 syntax option available for referencing to objects -inside any given file, e.g. for headlines: - - Markdown RST - - Some headline Some headline - ============= ============= - - Ref. [Some headline] Ref. `Some headline`_ - - -Markup external referencing ---------------------------- - -The syntax is the same as for local referencing, but the anchors are -saved in ``.idx`` files, so one needs to generate them beforehand, -and they should be loaded by an `.. importdoc::` directive. -E.g. if we want to reference section "Some headline" in ``file1.md`` -from ``file2.md``, then ``file2.md`` may look like: - -``` -.. importdoc:: file1.md - -Ref. [Some headline] -``` - -```cmd -nim md2html --index:only file1.md # creates ``htmldocs/file1.idx`` -nim md2html file2.md # creates ``htmldocs/file2.html`` -``` - -To allow cross-references between any files in any order (especially, if -circular references are present), it's strongly reccommended -to make a run for creating all the indexes first: - -```cmd -nim md2html --index:only file1.md # creates ``htmldocs/file1.idx`` -nim md2html --index:only file2.md # creates ``htmldocs/file2.idx`` -nim md2html file1.md # creates ``htmldocs/file1.html`` -nim md2html file2.md # creates ``htmldocs/file2.html`` -``` - -and then one can freely reference any objects as if these 2 documents -are actually 1 file. - -Other -===== - -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.md b/doc/mm.md deleted file mode 100644 index 9d1fc1955..000000000 --- a/doc/mm.md +++ /dev/null @@ -1,94 +0,0 @@ -======================= -Nim's Memory Management -======================= - -.. default-role:: code -.. include:: rstcommon.rst - -:Author: Andreas Rumpf -:Version: |nimversion| - -.. - - -> "The road to hell is paved with good intentions." - - -Multi-paradigm Memory Management Strategies -=========================================== - -.. default-role:: option - -Nim offers multiple different memory management strategies. -To choose the memory management strategy use the `--mm:` switch. - -**The recommended switch for newly written Nim code is `--mm:orc`.** - - -ARC/ORC -------- - -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](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`. - -`--mm:arc` uses the same mechanism as `--mm:orc`, but it leaves out the cycle collector. -Both ARC and ORC offer deterministic performance for `hard realtime`:idx: systems, but -ARC can be easier to reason about for people coming from Ada/C++/C -- roughly speaking -the memory for a variable is freed when it goes "out of scope". - -We generally advise you to use the `acyclic` annotation in order to optimize away the -cycle collector's overhead -but `--mm:orc` also produces more machine code than `--mm:arc`, so if you're on a target -where code size matters and you know that your code does not produce cycles, you can -use `--mm:arc`. Notice that the default `async`:idx: implementation produces cycles -and leaks memory with `--mm:arc`, in other words, for `async` you need to use `--mm:orc`. - - - -Other MM modes --------------- - -.. note:: The `refc` GC is incremental, thread-local and not "stop-the-world". - ---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. ---mm:go Go's garbage collector, useful for interoperability with Go. - Offers a shared heap. - ---mm:none No memory management strategy nor a garbage collector. Allocated memory is - simply never freed. You should use `--mm:arc` instead. - -Here is a comparison of the different memory management modes: - -================== ======== ================= ============== =================== -Memory Management Heap Reference Cycles Stop-The-World Command line switch -================== ======== ================= ============== =================== -ORC Shared Cycle Collector No `--mm:orc` -ARC Shared Leak No `--mm:arc` -RefC Local Cycle Collector No `--mm:refc` -Mark & Sweep Local Cycle Collector No `--mm:markAndSweep` -Boehm Shared Cycle Collector Yes `--mm:boehm` -Go Shared Cycle Collector Yes `--mm:go` -None Manual Manual Manual `--mm:none` -================== ======== ================= ============== =================== - -.. default-role:: code -.. include:: rstcommon.rst - -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.md b/doc/nep1.rst similarity index 70% rename from doc/nep1.md rename to doc/nep1.rst index 0c62e3f9c..73e4d8433 100644 --- a/doc/nep1.md +++ b/doc/nep1.rst @@ -1,12 +1,9 @@ -============================ -Standard Library Style Guide -============================ - +========================================================== +Nim Enhancement Proposal #1 - Standard Library Style Guide +========================================================== :Author: Clay Sweetser, Dominik Picheta :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: @@ -21,7 +18,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](http://legacy.python.org/dev/peps/pep-0008/) changes +`Python's style guide`_ changes over time, this style guide will too. These rules will only be enforced for contributions to the Nim @@ -49,7 +46,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. - ```nim + .. code-block:: nim # This is bad, as the next time someone comes # to edit this code block, they # must re-align all the assignments again: @@ -60,17 +57,25 @@ Spacing and Whitespace Conventions CalId* = int LongLong* = int64 LongLongPtr* = ptr LongLong - ``` Naming Conventions ------------------ +Note: While the rules outlined below are the *current* naming conventions, +these conventions have not always been in place. Previously, the naming +conventions for identifiers followed the Pascal tradition of prefixes which +indicated the base type of the identifier - PFoo for pointer and reference +types, TFoo for value types, EFoo for exceptions, etc. Though this has since +changed, there are many places in the standard library which still use this +convention. Such style remains in place purely for legacy reasons, and will be +changed in the future. + - Type identifiers should be in PascalCase. All other identifiers should be in camelCase with the exception of constants which **may** use PascalCase but are not required to. - ```nim + .. code-block:: nim # Constants can start with either a lower case or upper case letter. const aConstant = 42 const FooBar = 4.2 @@ -80,7 +85,6 @@ 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!) @@ -91,53 +95,49 @@ Naming Conventions that will be used the most, add the suffixes to the pointer variants only. The same applies to C/C++ wrappers. - ```nim + .. code-block:: 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. - ```nim + .. code-block:: 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. - ```nim + .. code-block:: nim type PathComponent = enum pcDir pcLinkToDir pcFile pcLinkToFile - ``` - Non-pure enum values should use camelCase whereas pure enum values should use PascalCase. - ```nim + .. code-block:: 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 - as what they are: Real words. So it's `parseUrl` rather than - `parseURL`, `checkHttpHeader` instead of `checkHTTPHeader` etc. + as what they are: Real words. So it's ``parseUrl`` rather than + ``parseURL``, ``checkHttpHeader`` instead of ``checkHTTPHeader`` etc. -- Operations like `mitems` or `mpairs` (or the now deprecated `mget`) - that allow a *mutating view* into some data structure should start with an `m`. +- Operations like ``mitems`` or ``mpairs`` (or the now deprecated ``mget``) + that allow a *mutating view* into some data structure should start with an ``m``. - When both in-place mutation and 'returns transformed copy' are available the latter is a past participle of the former: @@ -145,8 +145,8 @@ Naming Conventions - sort and sorted - rotate and rotated -- When the 'returns transformed copy' version already exists like `strutils.replace` - an in-place version should get an ``-In`` suffix (`replaceIn` for this example). +- When the 'returns transformed copy' version already exists like ``strutils.replace`` + an in-place version should get an ``-In`` suffix (``replaceIn`` for this example). - Use `subjectVerb`, not `verbSubject`, e.g.: `fileExists`, not `existsFile`. @@ -159,29 +159,25 @@ 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` - with reference semantics. -this or self self for method like procs, e.g.: - `proc fun(self: Foo, a: int)` - rationale: `self` is more unique in English - than `this`, and `foo` would not be DRY. +------------------- ------------ -------------------------------------- +initialize initT ``init`` is used to create a + value type ``T`` +new newP ``new`` is used to create a + reference type ``P`` find find should return the position where something was found; for a bool result - use `contains` -contains contains often short for `find() >= 0` -append add use `add` instead of `append` + use ``contains`` +contains contains often short for ``find() >= 0`` +append add use ``add`` instead of ``append`` compare cmp should return an int with the - `< 0` `== 0` or `> 0` semantics; - for a bool result use `sameXYZ` -put put, `[]=` consider overloading `[]=` for put -get get, `[]` consider overloading `[]` for get; - consider to not use `get` as a - prefix: `len` instead of `getLen` + ``< 0`` ``== 0`` or ``> 0`` semantics; + for a bool result use ``sameXYZ`` +put put, ``[]=`` consider overloading ``[]=`` for put +get get, ``[]`` consider overloading ``[]`` for get; + consider to not use ``get`` as a + prefix: ``len`` instead of ``getLen`` length len also used for *number of elements* size size, len size should refer to a byte size capacity cap @@ -226,29 +222,28 @@ literal lit string str identifier ident indentation indent -=================== ============ ====================================== +------------------- ------------ -------------------------------------- Coding Conventions ------------------ -- The `return` statement should ideally be used when its control-flow properties - are required. Use a procedure's implicit `result` variable whenever possible. +- The 'return' statement should ideally be used when its control-flow properties + are required. Use a procedure's implicit 'result' variable whenever possible. This improves readability. - ```nim + .. code-block:: nim proc repeat(text: string, x: int): string = result = "" - for i in 0..x: + 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. -- Use the `let` statement (not the `var` statement) when declaring variables that - do not change within their scope. Using the `let` statement ensures that +- Use the ``let`` statement (not the ``var`` statement) when declaring variables that + do not change within their scope. Using the ``let`` statement ensures that variables remain immutable, and gives those who read the code a better idea of the code's purpose. @@ -259,16 +254,15 @@ 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. - ```nim + .. code-block:: 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. - ```nim + .. code-block:: nim type EventCallback = proc (timeReceived: Time, errorCode: int, event: Event, output: var string) @@ -276,50 +270,10 @@ 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). - ```nim + .. code-block:: nim startProcess(nimExecutable, currentDirectory, compilerArguments environment, processOptions) - ``` - -Miscellaneous -------------- - -- Use `a..b` instead of `a .. b`, except when `b` contains an operator, for example `a .. -3`. - Likewise with `a.. "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 +17,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](manual.html)). +programming language (which is covered in the `manual `_). Nim is free software; it is licensed under the -[MIT License](http://www.opensource.org/licenses/mit-license.php). +`MIT License `_. Compiler Usage @@ -34,9 +30,6 @@ Command-line switches --------------------- Basic command-line switches are: -.. no syntax highlighting in the below included files at the moment -.. default-role:: code - Usage: .. include:: basicopt.txt @@ -48,13 +41,12 @@ Advanced command-line switches are: .. include:: advopt.txt -.. include:: rstcommon.rst List of warnings ---------------- -Each warning can be activated individually with `--warning:NAME:on|off`:option: or -in a `push` pragma with `{.warning[NAME]:on|off.}`. +Each warning can be activated individually with ``--warning[NAME]:on|off`` or +in a ``push`` pragma. ========================== ============================================ Name Description @@ -68,16 +60,8 @@ ConfigDeprecated The project makes use of a deprecated config file. SmallLshouldNotBeUsed The letter 'l' should not be used as an identifier. -EachIdentIsTuple The code contains a confusing `var` +EachIdentIsTuple The code contains a confusing ``var`` declaration. -CStringConv Warn about dangerous implicit conversions - to `cstring`. -EnumConv Warn about conversions from enum to enum. -AnyEnumConv Warn about any conversions to an enum type. -HoleEnumConv Warn about conversion to an enum with - holes. These conversions are unsafe. -ResultUsed Warn about the usage of the - built-in `result` variable. User Some user-defined warning. ========================== ============================================ @@ -85,8 +69,8 @@ User Some user-defined warning. List of hints ------------- -Each hint can be activated individually with `--hint:NAME:on|off`:option: or in a -`push` pragma with `{.hint[NAME]:on|off.}`. +Each hint can be activated individually with ``--hint[NAME]:on|off`` or in a +``push`` pragma. ========================== ============================================ Name Description @@ -130,13 +114,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]( - manual.html#implementation-specific-pragmas-compile-pragma). + by other modules or through the `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]( - filters.html) if any filter was used during compilation. + standard output the result of applying `a filter to the source code + `_ if any filter was used during compilation. 3 In addition to the previous levels dumps a debug stack trace for compiler developers. ===== ============================================ @@ -145,42 +129,26 @@ Level Description Compile-time symbols -------------------- -Through the `-d:x`:option: or `--define:x`:option: switch you can define compile-time +Through the ``-d:x`` or ``--define:x`` switch you can define compile-time symbols for conditional compilation. The defined switches can be checked in -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 +source code with the `when statement +`_ and +`defined proc `_. The typical use of this switch is +to enable builds in release mode (``-d:release``) where optimizations are +enabled for better performance. Another common use is the ``-d:ssl`` 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](manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) +Additionally, you may pass a value along with the symbol: ``-d:x=y`` +which may be used in conjunction with the `compile-time define +pragmas`_ to override symbols during build time. Compile-time symbols are completely **case insensitive** and underscores are -ignored too. `--define:FOO`:option: and `--define:foo`:option: are identical. +ignored too. ``--define:FOO`` and ``--define:foo`` are identical. -Compile-time symbols starting with the `nim` prefix are reserved for the +Compile-time symbols starting with the ``nim`` prefix are reserved for the implementation and should not be used elsewhere. -========================== ============================================ -Name Description -========================== ============================================ -nimStdSetjmp Use the standard `setjmp()/longjmp()` library - functions for setjmp-based exceptions. This is - the default on most platforms. -nimSigSetjmp Use `sigsetjmp()/siglongjmp()` for setjmp-based exceptions. -nimRawSetjmp Use `_setjmp()/_longjmp()` on POSIX and `_setjmp()/longjmp()` - on Windows, for setjmp-based exceptions. It's the default on - BSDs and BSD-like platforms, where it's significantly faster - than the standard functions. -nimBuiltinSetjmp Use `__builtin_setjmp()/__builtin_longjmp()` for setjmp-based - exceptions. This will not work if an exception is being thrown - and caught inside the same procedure. Useful for benchmarking. -========================== ============================================ - Configuration files ------------------- @@ -189,45 +157,26 @@ Configuration files passed as a command-line argument to the compiler. -The `nim`:cmd: executable processes configuration files in the following +The ``nim`` executable processes configuration files in the following directories (in this order; later files overwrite previous settings): -1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or - ``\config\nim.cfg`` (Windows). - This file can be skipped with the `--skipCfg`:option: command line option. -2) If environment variable `XDG_CONFIG_HOME` is defined, - ``$XDG_CONFIG_HOME/nim/nim.cfg`` or ``~/.config/nim/nim.cfg`` (POSIX) or - ``%APPDATA%/nim/nim.cfg`` (Windows). - This file can be skipped with the `--skipUserCfg`:option: command line - option. -3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent - directory of the project file's path. - These files can be skipped with the `--skipParentCfg`:option: - command-line option. -4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project - file's path. - This file can be skipped with the `--skipProjCfg`:option: - command-line option. -5) A project can also have a project-specific configuration file named - ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. - This file can be skipped with the `--skipProjCfg`:option: - command-line option. +1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or ``\config\nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option. +2) If environment variable ``XDG_CONFIG_HOME`` is defined, ``$XDG_CONFIG_HOME/nim/nim.cfg`` or ``~/.config/nim/nim.cfg`` (POSIX) or ``%APPDATA%/nim/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option. +3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command-line option. +4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command-line option. +5) A project can also have a project-specific configuration file named ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command-line option. 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: +`release build`:idx: define the ``release`` symbol:: - ```cmd nim c -d:release myproject.nim - ``` -To compile a `dangerous release build`:idx: define the `danger` symbol: + To compile a `dangerous release build`:idx: define the ``danger`` symbol:: - ```cmd nim c -d:danger myproject.nim - ``` Search path handling @@ -237,19 +186,19 @@ Nim has the concept of a global search path (PATH) that is queried to determine where to find imported modules or include files. If multiple files are found an ambiguity error is produced. -`nim dump`:cmd: shows the contents of the PATH. +``nim dump`` shows the contents of the PATH. However before the PATH is used the current directory is checked for the file's existence. So if PATH contains ``$lib`` and ``$lib/bar`` and the -directory structure looks like this: +directory structure looks like this:: - $lib/x.nim - $lib/bar/x.nim - foo/x.nim - foo/main.nim - other.nim + $lib/x.nim + $lib/bar/x.nim + foo/x.nim + foo/main.nim + other.nim -And `main` imports `x`, `foo/x` is imported. If `other` imports `x` +And ``main`` imports ``x``, ``foo/x`` is imported. If ``other`` imports ``x`` then both ``$lib/x.nim`` and ``$lib/bar/x.nim`` match but ``$lib/x.nim`` is used as it is the first match. @@ -261,14 +210,14 @@ The generated files that Nim produces all go into a subdirectory called - ``$XDG_CACHE_HOME/nim/$projectname(_r|_d)`` or ``~/.cache/nim/$projectname(_r|_d)`` on Posix -- ``$HOME\nimcache\$projectname(_r|_d)`` on Windows. +- ``$HOME/nimcache/$projectname(_r|_d)`` on Windows. -The `_r` suffix is used for release builds, `_d` is for debug builds. +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][command-line switches] can be used to +The ``--nimcache`` +`compiler switch <#compiler-usage-commandminusline-switches>`_ can be used to to change the ``nimcache`` directory. However, the generated C code is not platform-independent. C code generated for @@ -279,75 +228,60 @@ C file lists the OS, CPU, and CC the file has been compiled for. Compiler Selection ================== -To change the compiler from the default compiler (at the command line): +To change the compiler from the default compiler (at the command line):: - ```cmd nim c --cc:llvm_gcc --compile_only myfile.nim - ``` -This uses the configuration defined in ``config\nim.cfg`` for `llvm_gcc`:cmd:. +This uses the configuration defined in ``config\nim.cfg`` for ``lvm_gcc``. If nimcache already contains compiled code from a different compiler for the same project, -add the `-f`:option: flag to force all files to be recompiled. +add the ``-f`` flag to force all files to be recompiled. The default compiler is defined at the top of ``config\nim.cfg``. -Changing this setting affects the compiler used by `koch`:cmd: to (re)build Nim. +Changing this setting affects the compiler used by ``koch`` to (re)build Nim. -To use the `CC` environment variable, use `nim c --cc:env myfile.nim`:cmd:. -To use the `CXX` environment variable, use `nim cpp --cc:env myfile.nim`:cmd:. -`--cc:env`:option: is available since Nim version 1.4. +To use the ``CC`` environment variable, use ``nim c --cc:env myfile.nim``. To use the +``CXX`` environment variable, use ``nim cpp --cc:env myfile.nim``. ``--cc:env`` is available +since Nim version 1.4. Cross-compilation ================= -To cross compile, use for example: +To cross compile, use for example:: - ```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 +Then move the C code and the compile script ``compile_myproject.sh`` to your Linux i386 machine and run the script. -Another way is to make Nim invoke a cross compiler toolchain: +Another way is to make Nim invoke a cross compiler toolchain:: - ```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`) with options defined in -`$cpu.$os.$cc.options.always`. The configuration system is used to provide -meaningful defaults. For example, for Linux on a 32-bit ARM CPU, your -configuration file should contain something like: +For cross compilation, the compiler invokes a C compiler named +like ``$cpu.$os.$cc`` (for example arm.linux.gcc) and the configuration +system is used to provide meaningful defaults. For example for ``ARM`` your +configuration file should contain something like:: - arm.linux.gcc.path = "/usr/bin" - arm.linux.gcc.exe = "arm-linux-gcc" - arm.linux.gcc.linkerexe = "arm-linux-gcc" - arm.linux.gcc.options.always = "-w -fmax-errors=3" + arm.linux.gcc.path = "/usr/bin" + arm.linux.gcc.exe = "arm-linux-gcc" + arm.linux.gcc.linkerexe = "arm-linux-gcc" Cross-compilation for Windows ============================= -To cross-compile for Windows from Linux or macOS using the MinGW-w64 toolchain: +To cross-compile for Windows from Linux or macOS using the MinGW-w64 toolchain:: - ```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. +Use ``--cpu:i386`` or ``--cpu:amd64`` to switch the CPU architecture. -The MinGW-w64 toolchain can be installed as follows: +The MinGW-w64 toolchain can be installed as follows:: - ```cmd - apt install mingw-w64 # Ubuntu - yum install mingw32-gcc - yum install mingw64-gcc # CentOS - requires EPEL - brew install mingw-w64 # OSX - ``` + Ubuntu: apt install mingw-w64 + CentOS: yum install mingw32-gcc | mingw64-gcc - requires EPEL + OSX: brew install mingw-w64 Cross-compilation for Android @@ -357,40 +291,36 @@ 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](https://wiki.termux.com) to connect and run the Nim compiler +`Termux `_ 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. -Use regular `nim c`:cmd: inside termux to make Android terminal programs. +Use regular ``nim c`` 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](https://developer.android.com/ndk). You can also use -[native-activity](https://developer.android.com/ndk/samples/sample_na) +Nim using the `NDK `_. You can also use +`native-activity `_ to have the Java stub be auto-generated for you. -Use `nim c -c --cpu:arm --os:android -d:androidNDK --noMain:on`:cmd: to +Use ``nim c -c --cpu:arm --os:android -d:androidNDK --noMain:on`` to generate the C source files you need to include in your Android Studio project. Add the generated C files to CMake build script in your Android project. Then do the final compile with Android Studio which uses Gradle to call CMake to compile the project. -Because Nim is part of a library it can't have its own C-style `main()`:c: -so you would need to define your own `android_main`:c: and init the Java +Because Nim is part of a library it can't have its own c style ``main()`` +so you would need to define your own ``android_main`` and init the Java environment, or use a library like SDL2 or GLFM to do it. After the Android -stuff is done, it's very important to call `NimMain()`:c: in order to +stuff is done, it's very important to call ``NimMain()`` in order to initialize Nim's garbage collector and to run the top level statements of your program. - ```Nim +.. code-block:: 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. -Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`. Cross-compilation for iOS @@ -401,75 +331,68 @@ Normal languages for iOS development are Swift and Objective C. Both of these use LLVM and can be compiled into object files linked together with C, C++ or Objective C code produced by Nim. -Use `nim c -c --os:ios --noMain:on`:cmd: to generate C files and include them in +Use ``nim c -c --os:ios --noMain:on`` to generate C files and include them in your XCode project. Then you can use XCode to compile, link, package and sign everything. -Because Nim is part of a library it can't have its own C-style `main()`:c: so you -would need to define `main` that calls `autoreleasepool` and -`UIApplicationMain` to do it, or use a library like SDL2 or GLFM. After -the iOS setup is done, it's very important to call `NimMain()`:c: to +Because Nim is part of a library it can't have its own c style ``main()`` so you +would need to define `main` that calls ``autoreleasepool`` and +``UIApplicationMain`` to do it, or use a library like SDL2 or GLFM. After +the iOS setup is done, it's very important to call ``NimMain()`` to initialize Nim's garbage collector and to run the top-level statements of your program. - ```Nim +.. code-block:: 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. -The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch. -Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`. - Cross-compilation for Nintendo Switch ===================================== -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: +Simply add --os:nintendoswitch +to your usual ``nim c`` or ``nim cpp`` command and set the ``passC`` +and ``passL`` command line switches to something like: - ```cmd - nim c ... --d:nimAllocPagesViaMalloc --mm:orc --passC="-I$DEVKITPRO/libnx/include" ... +.. code-block:: console + nim c ... --passC="-I$DEVKITPRO/libnx/include" ... --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" - ``` -or setup a ``nim.cfg`` file like so: +or setup a nim.cfg file like so: - #nim.cfg - --mm:orc - --d:nimAllocPagesViaMalloc - --define:nimInheritHandles - --passC="-I$DEVKITPRO/libnx/include" - --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" +.. code-block:: Nim + #nim.cfg + --passC="-I$DEVKITPRO/libnx/include" + --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](https://github.com/devkitPro/pacman/releases) or -[here for Windows](https://github.com/devkitPro/installer/releases). +The DevkitPro setup must be the same as the default with their new installer +`here for Mac/Linux `_ or +`here for Windows `_. -For example, with the above-mentioned config: +For example, with the above-mentioned config:: - ```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](https://github.com/jyapayne/nim-libnx.git). +an nro file with the ``elf2nro`` tool in the DevkitPro release. Examples can be found at +`the nim-libnx github repo `_. -There are a few things that don't work because the devkitPro libraries don't support them. +There are a few things that don't work because the DevkitPro libraries don't support them. They are: 1. Waiting for a subprocess to finish. A subprocess can be started, but right now it can't be waited on, which sort of makes subprocesses a bit hard to use -2. Dynamic calls. Switch OS (Horizon) doesn't support dynamic libraries, so dlopen/dlclose are not available. -3. mqueue. Sadly there are no mqueue headers. -4. ucontext. No headers for these either. No coroutines for now :( -5. nl_types. No headers for this. -6. As mmap is not supported, the nimAllocPagesViaMalloc option has to be used. +2. Dynamic calls. DevkitPro libraries have no dlopen/dlclose functions. +3. Command line parameters. It doesn't make sense to have these for a console + anyways, so no big deal here. +4. mqueue. Sadly there are no mqueue headers. +5. ucontext. No headers for these either. No coroutines for now :( +6. nl_types. No headers for this. DLL generation ============== @@ -477,17 +400,13 @@ DLL generation Nim supports the generation of DLLs. However, there must be only one 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: +on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command:: - ```cmd nim c -d:release lib/nimrtl.nim - ``` -To link against ``nimrtl.dll`` use the command: +To link against ``nimrtl.dll`` use the command:: - ```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! @@ -496,46 +415,46 @@ never been tested and is unlikely to work! Additional compilation switches =============================== -The standard library supports a growing number of `useX` conditional defines +The standard library supports a growing number of ``useX`` conditional defines affecting how some features are implemented. This section tries to give a complete list. ====================== ========================================================= Define Effect ====================== ========================================================= -`release` Turns on the optimizer. +``release`` Turns on the optimizer. More aggressive optimizations are possible, e.g.: - `--passC:-ffast-math`:option: (but see issue #10305) -`danger` Turns off all runtime checks and turns on the optimizer. -`useFork` Makes `osproc` use `fork`:c: instead of `posix_spawn`:c:. -`useNimRtl` Compile and link against ``nimrtl.dll``. -`useMalloc` Makes Nim use C's `malloc`:idx: instead of Nim's + ``--passC:-ffast-math`` (but see issue #10305) +``danger`` Turns off all runtime checks and turns on the optimizer. +``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``. +``useNimRtl`` Compile and link against ``nimrtl.dll``. +``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's own memory manager, albeit prefixing each allocation with its size to support clearing memory on reallocation. - 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](mm.html) + This only works with ``gc:none`` and + with ``--newruntime``. +``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime + systems. See the documentation of the `gc `_ for further information. -`logGC` Enable GC logging to stdout. -`nodejs` The JS target is actually ``node.js``. -`ssl` Enables OpenSSL support for the sockets module. -`memProfiler` Enables memory profiling for the native GC. -`uClibc` Use uClibc instead of libc. (Relevant for Unix-like OSes) -`checkAbi` When using types from C headers, add checks that compare +``logGC`` Enable GC logging to stdout. +``nodejs`` The JS target is actually ``node.js``. +``ssl`` Enables OpenSSL support for the sockets module. +``memProfiler`` Enables memory profiling for the native GC. +``uClibc`` Use uClibc instead of libc. (Relevant for Unix-like OSes) +``checkAbi`` When using types from C headers, add checks that compare what's in the Nim file with what's in the C header. This may become enabled by default in the future. -`tempDir` This symbol takes a string as its value, like - `--define:tempDir:/some/temp/path`:option: to override - the temporary directory returned by `os.getTempDir()`. +``tempDir`` This symbol takes a string as its value, like + ``--define:tempDir:/some/temp/path`` to override the + temporary directory returned by ``os.getTempDir()``. The value **should** end with a directory separator character. (Relevant for the Android platform) -`useShPath` This symbol takes a string as its value, like - `--define:useShPath:/opt/sh/bin/sh`:option: to override - the path for the `sh`:cmd: binary, in cases where it is - not located in the default location ``/bin/sh``. -`noSignalHandler` Disable the crash handler from ``system.nim``. -`globalSymbols` Load all `{.dynlib.}` libraries with the `RTLD_GLOBAL`:c: +``useShPath`` This symbol takes a string as its value, like + ``--define:useShPath:/opt/sh/bin/sh`` to override the + path for the ``sh`` binary, in cases where it is not + located in the default location ``/bin/sh``. +``noSignalHandler`` Disable the crash handler from ``system.nim``. +``globalSymbols`` Load all ``{.dynlib.}`` libraries with the ``RTLD_GLOBAL`` flag on Posix systems to resolve symbols in subsequently loaded libraries. ====================== ========================================================= @@ -552,21 +471,20 @@ generator and are subject to change. LineDir option -------------- -The `--lineDir`:option: option can be turned on or off. If turned on the -generated C code contains `#line`:c: directives. This may be helpful for +The ``lineDir`` option can be turned on or off. If turned on the +generated C code contains ``#line`` directives. This may be helpful for debugging with GDB. StackTrace option ----------------- -If the `--stackTrace`:option: option is turned on, the generated C contains code to +If the ``stackTrace`` option is turned on, the generated C contains code to ensure that proper stack traces are given if the program crashes or some uncaught exception is raised. LineTrace option ---------------- -The `--lineTrace`:option: option implies the `stackTrace`:option: option. -If turned on, +The ``lineTrace`` option implies the ``stackTrace`` option. If turned on, the generated C contains code to ensure that proper stack traces with line number information are given if the program crashes or an uncaught exception is raised. @@ -575,26 +493,24 @@ is raised. DynlibOverride ============== -By default Nim's `dynlib` pragma causes the compiler to generate -`GetProcAddress`:cpp: (or their Unix counterparts) -calls to bind to a DLL. With the `dynlibOverride`:option: command line switch this -can be prevented and then via `--passL`:option: the static library can be linked +By default Nim's ``dynlib`` pragma causes the compiler to generate +``GetProcAddress`` (or their Unix counterparts) +calls to bind to a DLL. With the ``dynlibOverride`` command line switch this +can be prevented and then via ``--passL`` the static library can be linked against. For instance, to link statically against Lua this command might work -on Linux: +on Linux:: - ```cmd nim c --dynlibOverride:lua --passL:liblua.lib program.nim - ``` Backend language options ======================== -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 +The typical compiler usage involves using the ``compile`` or ``c`` 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](backends.html). +can be read in the `Nim Backend Integration document `_. Nim documentation tools @@ -602,13 +518,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](docgen.html). +the output. For more details `see the docgen documentation `_. Nim idetools integration ======================== Nim provides language integration with external IDEs through the -idetools command. See the documentation of [idetools](idetools.html) +idetools command. See the documentation of `idetools `_ for further information. .. @@ -617,15 +533,15 @@ for further information. The Nim compiler supports an interactive mode. This is also known as a `REPL`:idx: (*read eval print loop*). If Nim has been built with the - `-d:nimUseLinenoise` switch, it uses the GNU readline library for terminal + ``-d:nimUseLinenoise`` switch, it uses the GNU readline library for terminal input management. To start Nim in interactive mode use the command - `nim secret`. To quit use the `quit()` command. To determine whether an input + ``nim secret``. To quit use the ``quit()`` command. To determine whether an input line is an incomplete statement to be continued these rules are used: 1. The line ends with ``[-+*/\\<>!\?\|%&$@~,;:=#^]\s*$`` (operator symbol followed by optional whitespace). 2. The line starts with a space (indentation). 3. The line is within a triple quoted string literal. However, the detection - does not work if the line contains more than one `"""`. + does not work if the line contains more than one ``"""``. Nim for embedded systems @@ -636,24 +552,22 @@ modern PC hardware and operating systems with ample memory, it is very well possible to run Nim code and a good part of the Nim standard libraries on small 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: +A good start is to use the ``any`` operating target together with the +``malloc`` memory allocator and the ``arc`` garbage collector. For example: - ```cmd - nim c --os:any --mm:arc -d:useMalloc [...] x.nim - ``` +``nim c --os:any --gc:arc -d:useMalloc [...] x.nim`` -- `--mm:arc`:option: will enable the reference counting memory management instead +- ``--gc:arc`` will enable the reference counting memory management instead of the default garbage collector. This enables Nim to use heap memory which is required for strings and seqs, for example. -- The `--os:any`:option: target makes sure Nim does not depend on any specific +- The ``--os:any`` target makes sure Nim does not depend on any specific operating system primitives. Your platform should support only some basic - ANSI C library `stdlib` and `stdio` functions which should be available + ANSI C library ``stdlib`` and ``stdio`` functions which should be available on almost any platform. -- The `-d:useMalloc`:option: option configures Nim to use only the standard C memory - manage primitives `malloc()`:c:, `free()`:c:, `realloc()`:c:. +- The ``-d:useMalloc`` option configures Nim to use only the standard C memory + manage primitives ``malloc()``, ``free()``, ``realloc()``. If your platform does not provide these functions it should be trivial to provide an implementation for them and link these to your program. @@ -663,65 +577,20 @@ additional flags to both the Nim compiler and the C compiler and/or linker to optimize the build for size. For example, the following flags can be used when targeting a gcc compiler: -`--opt:size --passC:-flto --passL:-flto`:option: +``--opt:size --passC:-flto --passL:-flto`` -The `--opt:size`:option: flag instructs Nim to optimize code generation for small -size (with the help of the C compiler), the `-flto`:option: flags enable link-time +The ``--opt:size`` flag instructs Nim to optimize code generation for small +size (with the help of the C compiler), the ``flto`` 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. - -nimAllocPagesViaMalloc ----------------------- - -Nim's default allocator is based on TLSF, this algorithm was designed for embedded -devices. This allocator gets blocks/pages of memory via a currently undocumented -`osalloc` API which usually uses POSIX's `mmap` call. On many environments `mmap` -is not available but C's `malloc` is. You can use the `nimAllocPagesViaMalloc` -define to use `malloc` instead of `mmap`. `nimAllocPagesViaMalloc` is currently -only supported with `--mm:arc` or `--mm:orc`. (Since version 1.6) - -nimPage256 / nimPage512 / nimPage1k -=================================== - -Adjust the page size for Nim's GC allocator. This enables using -`nimAllocPagesViaMalloc` on devices with less RAM. The default -page size requires too much RAM to work. - -Recommended settings: - -- < 32 kB of RAM use `nimPage256` - -- < 512 kB of RAM use `nimPage512` - -- < 2 MB of RAM use `nimPage1k` - -Initial testing hasn't shown much difference between 512B or 1kB page sizes -in terms of performance or latency. Using `nimPages256` will limit the -total amount of allocatable RAM. - -nimMemAlignTiny -=============== - -Sets `MemAlign` to `4` bytes which reduces the memory alignment -to better match some embedded devices. - -Thread stack size -================= - -Nim's thread API provides a simple wrapper around more advanced -RTOS task features. Customizing the stack size and stack guard size can -be done by setting `-d:nimThreadStackSize=16384` or `-d:nimThreadStackGuard=32`. - -Currently only Zephyr and FreeRTOS support these configurations. - Nim for realtime systems ======================== -See the `--mm:arc` or `--mm:orc` memory management settings in -[MM](mm.html) for further information. +See the documentation of Nim's soft realtime `GC `_ for further +information. Signal handling in Nim @@ -731,7 +600,7 @@ The Nim programming language has no concept of Posix's signal handling mechanisms. However, the standard library offers some rudimentary support for signal handling, in particular, segmentation faults are turned into fatal errors that produce a stack trace. This can be disabled with the -`-d:noSignalHandler`:option: switch. +``-d:noSignalHandler`` switch. Optimizing for Nim @@ -764,28 +633,38 @@ 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: - ```Nim +.. code-block:: Nim var s = procA() # assignment will not copy the string; procA allocates a new # string already - ``` However, it is not efficient to do: - ```Nim +.. code-block:: Nim var s = varA # assignment has to copy the whole string into a new buffer! - ``` -For `let` symbols a copy is not always necessary: +For ``let`` symbols a copy is not always necessary: - ```Nim +.. code-block:: 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 + 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:. + 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: - ```Nim +.. code-block:: Nim case normalize(k.key) of "name": c.name = v of "displayname": c.displayName = v @@ -801,4 +680,3 @@ 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 deleted file mode 100644 index 37039f130..000000000 --- a/doc/nimdoc.cls +++ /dev/null @@ -1,196 +0,0 @@ -\ProvidesClass{nimdoc}[2022/04/17, 2018/01/01 LaTeX2e nonstandard class] - -\LoadClass[a4paper,11pt]{article} - -\usepackage[a4paper,xetex,left=3cm,right=3cm,top=1.5cm,bottom=2cm]{geometry} - -% for 2-sided printing with larger inner "binding" margin -%\usepackage[a4paper,xetex,twoside,left=4cm,right=2cm,top=1.5cm,bottom=2cm]{geometry} -% for e-readers with 1.77:1 aspect ratio (e.g. 1920x1080) -%\usepackage[xetex,paperheight=27.6cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} -% for e-readers with 1.45:1 aspect ratio (e.g. 1200x825) -%\usepackage[xetex,paperheight=22.5cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} -% for e-readers with 1.33:1 aspect ratio (e.g. 1872x1404) -%\usepackage[xetex,paperheight=20.7cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} - -\usepackage{fontspec} -% logic to select default font with some fall-back fonts. -\IfFontExistsTF{Times New Roman}{% - \setmainfont{Times New Roman} % the default font - \typeout{========================================= nim: using Times New Roman} -}{ - \IfFontExistsTF{FreeSerif}{% - \setmainfont{FreeSerif} % fallback #1 - official GNU font, resembles Times - \typeout{========================================= nim: using FreeSerif} - }{ - \IfFontExistsTF{DejaVuSerif}{% - \setmainfont{DejaVuSerif} % fallback #2 - very widespread free font - \typeout{========================================= nim: using DejaVuSerif} - }{ - \typeout{!!!!!!!!!!!!!!!!!!! Fonts not found !!!!!!!!!!!!!!!!!!!!!!!} - } - } -} - -% default monospace font for code: -\usepackage{GoMono} -\usepackage{relsize} -% make this monospace font 2 steps smaller to hold 80-character line -\newcommand{\rstverbblockfont}{\smaller[2]} -\newcommand{\rstverbinlinefont}{\smaller} - -\usepackage{parskip} % paragraphs delimited by vertical space, no indent -\usepackage{graphicx} - -\usepackage{makeidx} -\newcommand{\nimindexterm}[2]{#2\index{#2}\label{#1}} -\makeindex - -\usepackage{dingbat} % for \carriagereturn, etc -\usepackage{fvextra} % for code blocks (works better than original fancyvrb) -\fvset{ - breaklines, - breakafter={=}:|\_\{\}[](){,}.;+-*/'", - breaksymbolleft=\color{red}{\ensuremath{\hookrightarrow}}, - breaksymbolright=\color{red}{\small\carriagereturn} -} -\fvinlineset{% - breaklines, - breakafter={=}:|\_\{\}[](){,}.;+-*/'", - % that does not work at all when we underline inline code by ulem :-( - commandchars=\\\{\} -} - -\usepackage{scrextend} % for the `addmargin` environment - -\usepackage[table]{xcolor} -\usepackage[urlbordercolor=blue,linkbordercolor=cyan, - pdfborderstyle={/S/U/W 1}]{hyperref} -\usepackage{enumitem} % for option list, enumList, and rstfootnote - -\usepackage[most]{tcolorbox} % boxes around admonitions, code blocks, doc.item - -\newtcolorbox{rstadmonition}[1][]{blanker, breakable, - left=3mm, right=0mm, top=1mm, bottom=1mm, - before upper=\indent, parbox=false, #1} - -\newtcolorbox{rstquote}[1][]{blanker, breakable, - left=3mm, right=3mm, top=1mm, bottom=1mm, - parbox=false, - borderline west={0.3em}{0pt}{lightgray}, - borderline north={0.05em}{0pt}{lightgray}, - borderline east={0.05em}{0pt}{lightgray}, - borderline south={0.05em}{0pt}{lightgray}} - -\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}, - before upper=\indent, parbox=false, #1} - -\newenvironment{rstpre}{% -\VerbatimEnvironment\begingroup\begin{rstprebox}% -\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}% -{\end{Verbatim}\end{rstprebox}\endgroup} - -\newtcolorbox{rstdocitembox}[1][]{blanker, breakable, - left=3mm, right=3mm, top=1mm, bottom=1mm, - borderline ={1pt}{0pt}{cyan}, - before upper=\indent, parbox=false, #1} - -% Inline code formatting: grey underline, -% use \Verb from fvextras e.g. to display -- correctly as double - -\usepackage[normalem]{ulem} -\newcommand\rstuline{\bgroup\markoverwith{\textcolor{rstframecolor}{\rule[-0.8ex]{2pt}{1.0pt}}}\ULon} - -\newcommand{\rstcode}[1]{% -{\rstverbinlinefont\Verb{\rstuline{#1}}}% -} - -\newcommand{\rstcodeitem}[1]{\Verb{#1}} - -\newenvironment{rstdocitem}{% -\VerbatimEnvironment\begingroup\begin{rstdocitembox}% -\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}% -{\end{Verbatim}\end{rstdocitembox}\endgroup} - - -\newenvironment{rstfootnote}{\begin{description}[labelindent=1em,leftmargin=1em,labelwidth=2.6em]}{\end{description}} -\ifdim\linewidth<30em - \def\rstoptleftmargin{0.4\linewidth} - \def\rstoptlabelwidth{0.35\linewidth} -\else - \def\rstoptleftmargin{12em} - \def\rstoptlabelwidth{10.5em} -\fi -\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 -\tymax=\maxdimen -% to pack tabulary into a new environment, special syntax is needed :-( -\newenvironment{rsttab}[1]{\tabulary{\linewidth}{#1}}{\endtabulary} - -\newcommand{\rstsub}[1]{\raisebox{-0.5ex}{\scriptsize{#1}}} -\newcommand{\rstsup}[1]{\raisebox{0.5ex}{\scriptsize{#1}}} - -\newcommand{\rsthA}[2][]{\section[#1]{#2}} -\newcommand{\rsthB}[2][]{\subsection[#1]{#2}} -\newcommand{\rsthC}[2][]{\subsubsection[#1]{#2}} -\newcommand{\rsthD}[2][]{\paragraph[#1]{#2}} -\newcommand{\rsthE}[2][]{\paragraph[#1]{#2}} - -\newcommand{\rstovA}[2][]{\section*[#1]{#2}} -\newcommand{\rstovB}[2][]{\subsection*[#1]{#2}} -\newcommand{\rstovC}[2][]{\subsubsection*[#1]{#2}} -\newcommand{\rstovD}[2][]{\paragraph*[#1]{#2}} -\newcommand{\rstovE}[2][]{\paragraph*[#1]{#2}} - -% Syntax highlighting: -\newcommand{\spanDecNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanBinNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanHexNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanOctNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanFloatNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanIdentifier}[1]{#1} -\newcommand{\spanKeyword}[1]{\textbf{#1}} -\newcommand{\spanStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanLongStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanCharLit}[1]{#1} -\newcommand{\spanEscapeSequence}[1]{#1} -\newcommand{\spanOperator}[1]{\textbf{#1}} -\newcommand{\spanPunctuation}[1]{#1} -\newcommand{\spanComment}[1]{\emph{#1}} -\newcommand{\spanLongComment}[1]{\emph{#1}} -\newcommand{\spanRegularExpression}[1]{#1} -\newcommand{\spanTagStart}[1]{#1} -\newcommand{\spanTagEnd}[1]{#1} -\newcommand{\spanKey}[1]{#1} -\newcommand{\spanValue}[1]{#1} -\newcommand{\spanRawData}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanAssembler}[1]{#1} -\newcommand{\spanPreprocessor}[1]{#1} -\newcommand{\spanDirective}[1]{#1} -\newcommand{\spanCommand}[1]{#1} -\newcommand{\spanRule}[1]{#1} -\newcommand{\spanHyperlink}[1]{#1} -\newcommand{\spanLabel}[1]{#1} -\newcommand{\spanReference}[1]{#1} -\newcommand{\spanOther}[1]{#1} -\newcommand{\spantok}[1]{\fbox{#1}} -\newcommand{\spanPrompt}[1]{\textcolor{red}{\textbf{#1}}} -\newcommand{\spanProgramOutput}[1]{\textcolor{darkgray}{\textbf{#1}}} -\newcommand{\spanprogram}[1]{\textbf{\underline{#1}}} -\newcommand{\spanoption}[1]{\textbf{\textcolor{darkgray}{#1}}} - -% Never allow text overflow to margin: -\setlength\emergencystretch{\hsize}\hbadness=10000 diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 1417d9eff..7d4a399e1 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -35,13 +35,7 @@ Modified by Boyd Greenfield and narimiran --escapeSequence: #c4891b; --number: #252dbe; --literal: #a4255b; - --program: #6060c0; - --option: #508000; --raw-data: #a4255b; - - --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' 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: black' 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) } [data-theme="dark"] { @@ -69,56 +63,68 @@ Modified by Boyd Greenfield and narimiran --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); } -@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); - } -} - -.theme-select-wrapper { +.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; +} + +.slider.round:before { + border-radius: 50%; +} + html { font-size: 100%; -webkit-text-size-adjust: 100%; @@ -141,39 +147,24 @@ body { padding: 0; box-sizing: border-box; } -.column, .columns { +.column, +.columns { width: 100%; float: left; box-sizing: border-box; - margin-left: 1%; } - -@media print { - #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby { - display:none; - } - .columns { - width:100% !important; - } + 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: 25.0%; - height: 100vh; - position: sticky; - top: 0px; - overflow-y: auto; - padding: 2px; + width: 22%; } .nine.columns { - width: 75.0%; - padding-left: 1.5em; } + width: 77.0%; } .twelve.columns { width: 100%; @@ -239,12 +230,6 @@ select:focus { } /* Docgen styles */ - -:target { - border: 2px solid #B5651D; - border-style: dotted; -} - /* Links */ a { color: var(--anchor); @@ -260,32 +245,27 @@ a.reference-toplevel { font-weight: bold; } -a.nimdoc { - word-spacing: 0.3em; -} - 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; @@ -312,7 +292,8 @@ img { background: transparent !important; box-shadow: none !important; } - a, a:visited { + a, + a:visited { text-decoration: underline; } a[href]:after { @@ -326,14 +307,16 @@ 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 { @@ -348,18 +331,22 @@ 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%; } @@ -367,7 +354,8 @@ small { strong { font-weight: 600; font-size: 0.95em; - color: var(--strong); } + color: var(--strong); +} em { font-style: italic; } @@ -388,14 +376,14 @@ h1.title { text-align: center; font-weight: 900; margin-top: 0.75em; - margin-bottom: 0em; } + margin-bottom: 0em; +} h2 { font-size: 1.3em; margin-top: 2em; } h2.subtitle { - margin-top: 0em; text-align: center; } h3 { @@ -415,29 +403,36 @@ 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; @@ -446,7 +441,8 @@ 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; @@ -456,10 +452,12 @@ 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 { @@ -499,46 +497,6 @@ hr { border: 0; border-top: 1px solid #aaa; } -hr.footnote { - width: 25%; - border-top: 0.15em solid #999; - margin-bottom: 0.15em; - margin-top: 0.15em; -} -div.footnote-group { - margin-left: 1em; -} -div.footnote-label { - display: inline-block; - min-width: 1.7em; -} - -div.option-list { - border: 0.1em solid var(--border); -} -div.option-list-item { - padding-left: 12em; - padding-right: 0; - padding-bottom: 0.3em; - padding-top: 0.3em; -} -div.odd { - background-color: var(--secondary-background); -} -div.option-list-label { - margin-left: -11.5em; - margin-right: 0em; - min-width: 11.5em; - display: inline-block; - vertical-align: top; -} -div.option-list-description { - width: calc(100% - 1em); - padding-left: 1em; - padding-right: 0; - display: inline-block; -} - blockquote { font-size: 0.9em; font-style: italic; @@ -547,12 +505,7 @@ blockquote { border-left: 5px solid #bbc; } -blockquote.markdown-quote { - font-size: 0.9rem; /* use rem to avoid recursion */ - font-style: normal; -} - -.pre, span.tok { +.pre { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; font-weight: 500; font-size: 0.85em; @@ -563,16 +516,6 @@ blockquote.markdown-quote { border-radius: 4px; } -span.tok { - border: 1px solid #808080; - padding-bottom: 0.1em; - margin-right: 0.2em; -} - -.copyToClipBoard { - position: relative; -} - pre { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; color: var(--text); @@ -591,25 +534,7 @@ pre { border: 1px solid var(--border); -webkit-border-radius: 6px; -moz-border-radius: 6px; - border-radius: 6px; -} - -.copyToClipBoardBtn { - visibility: hidden; - position: absolute; - width: 24px; - border-radius: 4px; - background-image: var(--clipboard-image); - right: 5px; - top: 13px; - background-color: var(--secondary-background); - padding: 11px; - border: 0; -} - -.copyToClipBoard:hover .copyToClipBoardBtn { - visibility: visible; -} + border-radius: 6px; } .pre-scrollable { max-height: 340px; @@ -642,7 +567,6 @@ table.line-nums-table { .line-nums-table td.blob-line-nums pre { color: #b0b0b0; -webkit-filter: opacity(75%); - filter: opacity(75%); text-align: right; border-color: transparent; background-color: transparent; @@ -674,8 +598,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 { @@ -692,31 +616,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 { @@ -750,7 +674,8 @@ div.footer, div.header { font-size: smaller; } div.footer { - padding-top: 5em; } + padding-top: 5em; +} div.line-block { display: block; @@ -769,14 +694,17 @@ 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%; } @@ -874,6 +802,9 @@ span.classifier { span.classifier-delimiter { font-weight: bold; } +span.option { + white-space: nowrap; } + span.problematic { color: #b30000; } @@ -953,29 +884,8 @@ span.Preprocessor { span.Directive { color: #252dbe; } -span.option { - font-weight: bold; - font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; - color: var(--option); } - -span.Prompt { - font-weight: bold; - color: red; } - -span.ProgramOutput { - font-weight: bold; - color: #808080; } - -span.program { - font-weight: bold; - color: var(--program); - text-decoration: underline; - text-decoration-color: var(--hint); - text-decoration-thickness: 0.05em; - 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 */ @@ -996,7 +906,6 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier background-position: 0 0; background-size: 51px 14px; -webkit-filter: opacity(50%); - filter: opacity(50%); background-repeat: no-repeat; background-image: var(--nim-sprite-base64); margin-bottom: 5px; } @@ -1013,14 +922,17 @@ 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.md b/doc/nimfix.rst similarity index 75% rename from doc/nimfix.md rename to doc/nimfix.rst index 128b25a45..62064fe69 100644 --- a/doc/nimfix.md +++ b/doc/nimfix.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ===================== Nimfix User Guide ===================== @@ -16,40 +14,33 @@ It performs 3 different actions: 1. It makes your code case consistent. 2. It renames every symbol that has a deprecation rule. So if a module has a - rule `{.deprecated: [TFoo: Foo].}` then `TFoo` is replaced by `Foo`. + rule ``{.deprecated: [TFoo: Foo].}`` then ``TFoo`` is replaced by ``Foo``. 3. It can also check that your identifiers adhere to the official style guide - and optionally modify them to do so (via `--styleCheck:auto`). + and optionally modify them to do so (via ``--styleCheck:auto``). -Note that `nimfix` defaults to **overwrite** your code unless you -use `--overwriteFiles:off`! But hey, if you do not use a version control +Note that ``nimfix`` defaults to **overwrite** your code unless you +use ``--overwriteFiles:off``! But hey, if you do not use a version control system by this day and age, your project is already in big trouble. Installation ------------ -Nimfix is part of the compiler distribution. Compile via: +Nimfix is part of the compiler distribution. Compile via:: - ```cmd nim c compiler/nimfix/nimfix.nim mv compiler/nimfix/nimfix bin - ``` -Or on windows: +Or on windows:: - ```cmd nim c compiler\nimfix\nimfix.nim move compiler\nimfix\nimfix.exe bin - ``` Usage ----- Usage: - - ```cmd nimfix [options] projectfile.nim - ``` Options: diff --git a/doc/nimgrep.md b/doc/nimgrep.md deleted file mode 100644 index 63f760051..000000000 --- a/doc/nimgrep.md +++ /dev/null @@ -1,128 +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: - - ```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 occurrences 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 new file mode 100644 index 000000000..791ead162 --- /dev/null +++ b/doc/nimgrep.rst @@ -0,0 +1,50 @@ +========================= + nimgrep User's manual +========================= + +:Author: Andreas Rumpf +:Version: 0.9 + + +Nimgrep is a command line tool for search&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*. Apart from that it is a generic text +manipulation tool. + + +Installation +============ + +Compile nimgrep with the command:: + + nim c -d:release tools/nimgrep.nim + +And copy the executable somewhere in your ``$PATH``. + + +Command line switches +===================== + +Usage: + nimgrep [options] [pattern] [replacement] (file/directory)* +Options: + --find, -f find the pattern (default) + --replace, -r replace the pattern + --peg pattern is a peg + --re pattern is a regular expression (default); extended + syntax for the regular expression is always turned on + --recursive process directories recursively + --confirm confirm each occurrence/replacement; there is a chance + to abort any time without touching the file + --stdin read pattern from stdin (to avoid the shell's confusing + quoting rules) + --word, -w the match should have word boundaries (buggy for pegs!) + --ignoreCase, -i be case insensitive + --ignoreStyle, -y be style insensitive + --ext:EX1|EX2|... only search the files with the given extension(s) + --verbose be verbose: list every processed file + --help, -h shows this help + --version, -v shows the version diff --git a/doc/nimgrep_cmdline.txt b/doc/nimgrep_cmdline.txt deleted file mode 100644 index 6f6887bc4..000000000 --- a/doc/nimgrep_cmdline.txt +++ /dev/null @@ -1,136 +0,0 @@ - -Usage: - -* To search: - - nimgrep [options] PATTERN [(FILE/DIRECTORY)*/-] - -* To replace: - - nimgrep [options] PATTERN --replace REPLACEMENT (FILE/DIRECTORY)*/- - -* To list file names: - - nimgrep [options] --filenames [PATTERN] [(FILE/DIRECTORY)*] - -Positional arguments, from left to right: -1) PATTERN is either Regex (default) or Peg if `--peg` is specified. - PATTERN and REPLACEMENT should be skipped when `--stdin` is specified. -2) REPLACEMENT supports `$1`, `$#` notations for captured groups in PATTERN. - - .. DANGER:: `--replace` mode **DOES NOT** ask confirmation - unless `--confirm` is specified! - -3) Final arguments are a list of paths (FILE/DIRECTORY) or a standalone - minus `-` or not specified (empty): - - * empty, current directory `.` is assumed (not with `--replace`) - - .. Note:: so when no FILE/DIRECTORY/`-` is specified nimgrep - does **not** read the pipe, but searches files in the current - dir instead! - * `-`, read buffer once from stdin: pipe or terminal input; - in `--replace` mode the result is directed to stdout; - it's not compatible with `--stdin`, `--filenames`, or `--confirm` - - - For any given DIRECTORY nimgrep searches only its immediate files without - traversing subdirectories unless `--recursive` is specified. - -In replacement mode we require all 3 positional arguments to avoid damaging. - -Options: -* Mode of operation: - --find, -f find the PATTERN (default) - --replace, -! replace the PATTERN to REPLACEMENT, rewriting the files - --confirm confirm each occurrence/replacement; there is a chance - to abort any time without touching the file - --filenames just list filenames. Provide a PATTERN to find it in - the filenames (not in the contents of a file) or run - with empty pattern to just list all files: - - 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 - --ignoreStyle, -y be style insensitive in PATTERN and PAT - .. 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 - --sortTime, -s[:asc|desc] - order files by the last modification time (default: off): - ascending (recent files go last) or descending - -* 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) - --colorTheme:THEME select color THEME from `simple` (default), - `bnw` (black and white), `ack`, or `gnu` (GNU grep) - --count only print counts of matches for files that matched - --context:N, -c:N print N lines of leading context before every match and - N lines of trailing context after it (default N: 0) - --afterContext:N, -a:N - print N lines of trailing context after every match - --beforeContext:N, -b:N - print N lines of leading context before every match - --group, -g group matches by file - --newLine, -l display every matching line starting from a new line - --cols[:N] limit max displayed columns/width of output lines from - files by N characters, cropping overflows (default: off) - --cols:auto, -% calculate columns from terminal width for every line - --onlyAscii, -@ display only printable ASCII Latin characters 0x20-0x7E - substitutions: 0 -> ^@, 1 -> ^A, ... 0x1F -> ^_, - 0x7F -> '7F, ..., 0xFF -> 'FF - -* Miscellaneous: - --threads:N, -j:N speed up search by N additional workers (default: 0, off) - --stdin read PATTERN from stdin (to avoid the shell's confusing - quoting rules) and, if `--replace` given, REPLACEMENT - --verbose be verbose: list every processed file - --help, -h shows this help - --version, -v shows the version diff --git a/doc/niminst.md b/doc/niminst.rst similarity index 61% rename from doc/niminst.md rename to doc/niminst.rst index cc399c57a..adb5e6f1f 100644 --- a/doc/niminst.md +++ b/doc/niminst.rst @@ -5,8 +5,6 @@ :Author: Andreas Rumpf :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: Introduction @@ -14,7 +12,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](http://www.jrsoftware.org/isinfo.php) as well as +via `Inno Setup `_ as well as installation/deinstallation scripts for UNIX. Later versions will support Linux' package management systems. @@ -26,15 +24,15 @@ systems. Configuration file ================== -niminst uses the Nim [parsecfg](parsecfg.html) module to parse the +niminst uses the Nim `parsecfg `_ module to parse the configuration file. Here's an example of how the syntax looks like: .. include:: mytest.cfg :literal: The value of a key-value pair can reference user-defined variables via -the `$variable` notation: They can be defined in the command line with the -`--var:name=value`:option: switch. This is useful to not hard-coding the +the ``$variable`` notation: They can be defined in the command line with the +``--var:name=value`` switch. This is useful to not hard-coding the program's version number into the configuration file, for instance. It follows a description of each possible section and how it affects the @@ -49,96 +47,96 @@ 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 - not specified, this is the same as `Name`. -`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: - `"i386;amd64;powerpc"` -`Authors` the project's authors -`Description` the project's description -`App` the application's type: "Console" or "GUI". 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: + ``"windows;linux;macosx"`` +``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 "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 ==================== ======================================================= -`files` key +``files`` key ------------- -Many sections support the `files` key. Listed filenames -can be separated by semicolon or the `files` key can be repeated. Wildcards +Many sections support the ``files`` key. Listed filenames +can be separated by semicolon or the ``files`` key can be repeated. Wildcards in filenames are supported. If it is a directory name, all files in the -directory are used: +directory are used:: - [Config] - Files: "configDir" - Files: "otherconfig/*.conf;otherconfig/*.cfg" + [Config] + Files: "configDir" + Files: "otherconfig/*.conf;otherconfig/*.cfg" Config section -------------- -The `config` section currently only supports the `files` key. Listed files +The ``config`` section currently only supports the ``files`` key. Listed files will be installed into the OS's configuration directory. Documentation section --------------------- -The `documentation` section supports the `files` key. +The ``documentation`` section supports the ``files`` key. Listed files will be installed into the OS's native documentation directory (which might be ``$appdir/doc``). -There is a `start` key which determines whether the Windows installer +There is a ``start`` key which determines whether the Windows installer generates a link to e.g. the ``index.html`` of your documentation. Other section ------------- -The `other` section currently only supports the `files` key. +The ``other`` section currently only supports the ``files`` key. Listed files will be installed into the application installation directory -(`$appdir`). +(``$appdir``). Lib section ----------- -The `lib` section currently only supports the `files` key. +The ``lib`` section currently only supports the ``files`` key. Listed files will be installed into the OS's native library directory -(which might be `$appdir/lib`). +(which might be ``$appdir/lib``). Windows section --------------- -The `windows` section supports the `files` key for Windows-specific files. +The ``windows`` section supports the ``files`` key for Windows-specific files. Listed files will be installed into the application installation directory -(`$appdir`). +(``$appdir``). Other possible options are: ==================== ======================================================= Key description ==================== ======================================================= -`BinPath` paths to add to the Windows `%PATH%` environment +``BinPath`` paths to add to the Windows ``%PATH%`` environment variable. Example: ``BinPath: r"bin;dist\mingw\bin"`` -`InnoSetup` boolean flag whether an Inno Setup installer should be - generated for Windows. Example: `InnoSetup: "Yes"` +``InnoSetup`` boolean flag whether an Inno Setup installer should be + generated for Windows. Example: ``InnoSetup: "Yes"`` ==================== ======================================================= UnixBin section --------------- -The `UnixBin` section currently only supports the `files` key. +The ``UnixBin`` section currently only supports the ``files`` key. Listed files will be installed into the OS's native bin directory (e.g. ``/usr/local/bin``). The exact location depends on the -installation path the user specifies when running the `install.sh` script. +installation path the user specifies when running the ``install.sh`` script. Unix section @@ -149,11 +147,11 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -`InstallScript` boolean flag whether an installation shell script - should be generated. Example: `InstallScript: "Yes"` -`UninstallScript` boolean flag whether a de-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 should be generated. - Example: `UninstallScript: "Yes"` + Example: ``UninstallScript: "Yes"`` ==================== ======================================================= @@ -165,10 +163,10 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -`path` Path to Inno Setup. +``path`` Path to Inno Setup. Example: ``path = r"c:\inno setup 5\iscc.exe"`` -`flags` Flags to pass to Inno Setup. - Example: `flags = "/Q"` +``flags`` Flags to pass to Inno Setup. + Example: ``flags = "/Q"`` ==================== ======================================================= @@ -180,9 +178,9 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -`path` Path to the C compiler. -`flags` Flags to pass to the C Compiler. - Example: `flags = "-w"` +``path`` Path to the C compiler. +``flags`` Flags to pass to the C Compiler. + Example: ``flags = "-w"`` ==================== ======================================================= diff --git a/doc/nims.md b/doc/nims.rst similarity index 58% rename from doc/nims.md rename to doc/nims.rst index 2409bb577..03afa258a 100644 --- a/doc/nims.md +++ b/doc/nims.rst @@ -2,34 +2,31 @@ NimScript ================================ -.. default-role:: code -.. include:: rstcommon.rst - -Strictly speaking, `NimScript` is the subset of Nim that can be evaluated +Strictly speaking, ``NimScript`` is the subset of Nim that can be evaluated by Nim's builtin virtual machine (VM). This VM is used for Nim's compiletime function evaluation features. -The `nim`:cmd: executable processes the ``.nims`` configuration files in +The ``nim`` executable processes the ``.nims`` configuration files in the following directories (in this order; later files overwrite previous settings): -1) If environment variable `XDG_CONFIG_HOME` is defined, +1) If environment variable ``XDG_CONFIG_HOME`` is defined, ``$XDG_CONFIG_HOME/nim/config.nims`` or ``~/.config/nim/config.nims`` (POSIX) or ``%APPDATA%/nim/config.nims`` (Windows). This file can be skipped - with the `--skipUserCfg`:option: command line option. + with the ``--skipUserCfg`` command line option. 2) ``$parentDir/config.nims`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be - skipped with the `--skipParentCfg`:option: command line option. + skipped with the ``--skipParentCfg`` command line option. 3) ``$projectDir/config.nims`` where ``$projectDir`` stands for the - project's path. This file can be skipped with the `--skipProjCfg`:option: + project's path. This file can be skipped with the ``--skipProjCfg`` command line option. 4) A project can also have a project specific configuration file named ``$project.nims`` that resides in the same directory as ``$project.nim``. This file can be skipped with the same - `--skipProjCfg`:option: command line option. + ``--skipProjCfg`` command line option. -For available procs and implementation details see [nimscript](nimscript.html). +For available procs and implementation details see `nimscript `_. Limitations @@ -39,13 +36,13 @@ NimScript is subject to some limitations caused by the implementation of the VM (virtual machine): * Nim's FFI (foreign function interface) is not available in NimScript. This - means that any stdlib module which relies on `importc` can not be used in + means that any stdlib module which relies on ``importc`` can not be used in the VM. -* `ptr` operations are are hard to emulate with the symbolic representation +* ``ptr`` operations are are hard to emulate with the symbolic representation the VM uses. They are available and tested extensively but there are bugs left. -* `var T` function arguments rely on `ptr` operations internally and might +* ``var T`` function arguments rely on ``ptr`` operations internally and might also be problematic in some cases. * More than one level of `ref` is generally not supported (for example, the type @@ -53,7 +50,7 @@ NimScript is subject to some limitations caused by the implementation of the VM * Multimethods are not available. -* `random.randomize()` requires an `int64` explicitly passed as argument, you *must* pass a Seed integer. +* ``random.randomize()`` requires an ``int64`` explicitly passed as argument, you *must* pass a Seed integer. Standard library modules @@ -61,98 +58,96 @@ Standard library modules At least the following standard library modules are available: -* [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) -* [options](options.html) -* [parseutils](parseutils.html) -* [punycode](punycode.html) -* [random](random.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) -* [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) +* `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](system.html) module), +In addition to the standard Nim syntax (`system `_ module), NimScripts support the procs and templates defined in the -[nimscript](nimscript.html) module too. +`nimscript `_ module too. See also: -* [Check the tests for more information about modules compatible with NimScript]( - https://github.com/nim-lang/Nim/blob/devel/tests/test_nimscript.nims) +* `Check the tests for more information about modules compatible with NimScript. `_ NimScript as a configuration file ================================= -A command-line switch `--FOO`:option: is written as `switch("FOO")` in -NimScript. Similarly, command-line `--FOO:VAL`:option: translates to -`switch("FOO", "VAL")`. +A command-line switch ``--FOO`` is written as ``switch("FOO")`` in +NimScript. Similarly, command-line ``--FOO:VAL`` translates to +``switch("FOO", "VAL")``. -Here are few examples of using the `switch` proc: +Here are few examples of using the ``switch`` proc: - ```nim +.. code-block:: nim # command-line: --opt:size switch("opt", "size") - # command-line: --define:release or -d:release - switch("define", "release") + # command-line: --define:foo or -d:foo + switch("define", "foo") # command-line: --forceBuild switch("forceBuild") - ``` -NimScripts also support `--`:option: templates for convenience, which look +NimScripts also support ``--`` templates for convenience, which look like command-line switches written as-is in the NimScript file. So the above example can be rewritten as: - ```nim +.. code-block:: nim --opt:size - --define:release + --define:foo --forceBuild - ``` **Note**: In general, the *define* switches can also be set in -NimScripts using `switch` or `--`, as shown in above examples. Few -`define` switches such as `-d:strip`:option:, `-d:lto`:option: and -`-d:lto_incremental`:option: cannot be set in NimScripts. +NimScripts using ``switch`` or ``--``, as shown in above +examples. Only the ``release`` define (``-d:release``) cannot be set +in NimScripts. NimScript as a build tool ========================= -The `task` template that the `system` module defines allows a NimScript +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: +task ``build`` that is an alias for the ``c`` command: - ```nim +.. code-block:: nim task build, "builds an example": setCommand "c" - ``` In fact, as a convention the following tasks should be available: @@ -160,22 +155,22 @@ In fact, as a convention the following tasks should be available: ========= =================================================== Task Description ========= =================================================== -`help` List all the available NimScript tasks along with their docstrings. -`build` Build the project with the required - backend (`c`:option:, `cpp`:option: or `js`:option:). -`tests` Runs the tests belonging to the project. -`bench` Runs benchmarks belonging to the project. +``help`` List all the available NimScript tasks along with their docstrings. +``build`` Build the project with the required + backend (``c``, ``cpp`` or ``js``). +``tests`` Runs the tests belonging to the project. +``bench`` Runs benchmarks belonging to the project. ========= =================================================== -Look at the module [distros](distros.html) for some support of the +Look at the module `distros `_ for some support of the OS's native package managers. Nimble integration ================== -See the [Nimble readme](https://github.com/nim-lang/nimble#readme) +See the `Nimble readme `_ for more information. @@ -183,10 +178,11 @@ Standalone NimScript ==================== 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, +Batch files. Use ``nim myscript.nims`` to run ``myscript.nims``. For example, installation of Nimble could be accomplished with this simple script: - ```nim +.. code-block:: nim + mode = ScriptMode.Verbose var id = 0 @@ -199,19 +195,18 @@ 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 +On Unix, you can also use the shebang ``#!/usr/bin/env nim``, as long as your filename ends with ``.nims``: - ```nim +.. code-block:: nim + #!/usr/bin/env nim mode = ScriptMode.Silent echo "hello world" - ``` -Use `#!/usr/bin/env -S nim e --hints:off` to disable hints and relax the file extension constraint. +Use ``#!/usr/bin/env -S nim --hints:off`` to disable hints. Benefits @@ -231,8 +226,9 @@ allowing the same script to support a lot of systems. See the following (incomplete) example: - ```nim - import std/distros +.. code-block:: nim + + import distros # Architectures. if defined(amd64): @@ -257,7 +253,6 @@ See the following (incomplete) example: echo "Distro is ArchLinux" elif detectOs(Debian): echo "Distro is Debian" - ``` Uniform Syntax @@ -273,30 +268,30 @@ Powerful Metaprogramming NimScript can use Nim's templates, macros, types, concepts, effect tracking system, and more, you can create modules that work on compiled Nim and also on interpreted NimScript. -`func` will still check for side effects, `debugEcho` also works as expected, +``func`` will still check for side effects, ``debugEcho`` also works as expected, 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: - ```nim +.. code-block:: nim + import nimterlingua nimterlingua("translations.cfg") echo "cat" # Run with -d:RU becomes "kot", -d:ES becomes "gato", ... - ``` translations.cfg - ```none +.. code-block:: none + [cat] ES = gato - IT = gatto + PT = minino RU = kot FR = chat - ``` -* [Nimterlingua](https://nimble.directory/pkg/nimterlingua) +* `Nimterlingua `_ Graceful Fallback @@ -307,7 +302,8 @@ but often a graceful and seamless fallback degradation is used. See the following NimScript: - ```nim +.. code-block:: nim + if likely(true): discard elif unlikely(false): @@ -317,10 +313,9 @@ See the following NimScript: static: echo CompileDate - ``` -`likely()`, `unlikely()`, `static:` and `{.compiletime.}` +``likely()``, ``unlikely()``, ``static:`` and ``{.compiletime.}`` will produce no code at all when run on NimScript, but still no error nor warning is produced and the code just works. @@ -328,15 +323,14 @@ Evolving Scripting language --------------------------- NimScript evolves together with Nim, -[occasionally new features might become available on NimScript]( -https://github.com/nim-lang/Nim/pulls?utf8=%E2%9C%93&q=nimscript), +`occasionally new features might become available on 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](https://nimble.directory) +and check `Nimble `_ to search for third party modules that may work on NimScript. DevOps Scripting @@ -345,5 +339,4 @@ 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.](https://github.com/kaushalmodi/nim_config#list-available-tasks) +* `An example of a third party NimScript that can be used as a project-agnostic tool. `_ diff --git a/doc/nimsuggest.md b/doc/nimsuggest.rst similarity index 72% rename from doc/nimsuggest.md rename to doc/nimsuggest.rst index 97cb2b1fa..a5d0d65b9 100644 --- a/doc/nimsuggest.md +++ b/doc/nimsuggest.rst @@ -5,70 +5,61 @@ :Author: Unknown :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: 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 -`nimsuggest`:cmd: tool, any IDE +``nimsuggest`` tool, 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 nimsuggest support you can look at the -[various editor integrations](https://github.com/Araq/Nim/wiki/Editor-Support) +`various editor integrations `_ already available. Installation ============ -Nimsuggest is part of Nim's core. Build it via: +Nimsuggest is part of Nim's core. Build it via:: - ```cmd koch nimsuggest - ``` Nimsuggest invocation ===================== -Run it via `nimsuggest --stdin --debug myproject.nim`:cmd:. Nimsuggest is a -server that takes queries that are related to `myproject`. There is some +Run it via ``nimsuggest --stdin --debug myproject.nim``. Nimsuggest is a +server that takes queries that are related to ``myproject``. There is some support so that you can throw random ``.nim`` files which are not part -of `myproject` at Nimsuggest too, but usually the query refer to modules/files -that are part of `myproject`. +of ``myproject`` at Nimsuggest too, but usually the query refer to modules/files +that are part of ``myproject``. -`--stdin`:option: means that Nimsuggest reads the query from `stdin`. This is great +``--stdin`` means that Nimsuggest reads the query from ``stdin``. This is great for testing things out and playing with it but for an editor communication 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](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:. - Specifying the location of the query ------------------------------------ Nimsuggest then waits for queries to process. A query consists of a -cryptic 3 letter "command" `def` or `con` or `sug` or `use` followed by +cryptic 3 letter "command" ``def`` or ``con`` or ``sug`` or ``use`` followed by 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 + 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 @@ -77,18 +68,18 @@ 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**. Definitions ----------- -The `def` Nimsuggest command performs a query about the definition +The ``def`` Nimsuggest command performs a query about the definition of a specific symbol. If available, Nimsuggest will answer with the type, source file, line/column information and other accessory data if available like a docstring. With this information an IDE can @@ -109,12 +100,12 @@ can't find any valid symbol matching the position of the query. Suggestions ----------- -The `sug` Nimsuggest command performs a query about possible +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](tut2.html#object-oriented-programming-method-call-syntax). +user has typed the dot character for `the object oriented call +syntax `_. Nimsuggest will try to return the suggestions sorted first by scope (from innermost to outermost) and then by item name. @@ -122,7 +113,7 @@ Nimsuggest will try to return the suggestions sorted first by scope Invocation context ------------------ -The `con` Nimsuggest command is very similar to the suggestions +The ``con`` Nimsuggest command is very similar to the suggestions command, but instead of being used after the user has typed a dot character, this one is meant to be used after the user has typed an opening brace to start typing parameters. @@ -131,7 +122,7 @@ an opening brace to start typing parameters. Symbol usages ------------- -The `use` Nimsuggest command lists all usages of the symbol at +The ``use`` Nimsuggest command lists all usages of the symbol at a position. IDEs can use this to find all the places in the file where the symbol is used and offer the user to rename it in all places at the same time. @@ -149,15 +140,15 @@ Nimsuggest output is always returned on single lines separated by tab characters (``\t``). The values of each column are: 1. Three characters indicating the type of returned answer (e.g. - `def` for definition, `sug` for suggestion, etc). -2. Type of the symbol. This can be `skProc`, `skLet`, and just + ``def`` for definition, ``sug`` for suggestion, etc). +2. Type of the symbol. This can be ``skProc``, ``skLet``, and just about any of the enums defined in the module ``compiler/ast.nim``. 3. Fully qualified path of the symbol. If you are querying a symbol defined in the ``proj.nim`` file, this would have the form - `proj.symbolName`. + ``proj.symbolName``. 4. Type/signature. For variables and enums this will contain the type of the symbol, for procs, methods and templates this will - contain the full unique signature (e.g. `proc (File)`). + contain the full unique signature (e.g. ``proc (File)``). 5. Full path to the file containing the symbol. 6. Line where the symbol is located in the file. Lines start to count at **1**. diff --git a/doc/overview.md b/doc/overview.rst similarity index 86% rename from doc/overview.md rename to doc/overview.rst index b21eb1e68..e01520d7c 100644 --- a/doc/overview.md +++ b/doc/overview.rst @@ -5,5 +5,5 @@ Nim Documentation Overview :Author: Andreas Rumpf :Version: |nimversion| -.. include:: docs.md +.. include:: docs.rst diff --git a/doc/packaging.md b/doc/packaging.rst similarity index 53% rename from doc/packaging.md rename to doc/packaging.rst index b742bef28..ecde73f61 100644 --- a/doc/packaging.md +++ b/doc/packaging.rst @@ -4,10 +4,7 @@ Packaging Nim This page provide hints on distributing Nim using OS packages. -See [distros](distros.html) for tools to detect Linux distribution at runtime. - -See [here](intern.html#bootstrapping-the-compiler-reproducible-builds) for how to -compile reproducible builds. +See `distros `_ for tools to detect Linux distribution at runtime. Supported architectures ----------------------- @@ -48,33 +45,28 @@ The Debian package ships bash and ksh completion and manpages that can be reused Hints on the build process: - ```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 +:: - # optionally generate docs into doc/html - ./koch docs + # build from C sources and then using koch + ./build.sh --os $os_type --cpu $cpu_arch + ./bin/nim c koch + ./koch boot -d:release - ./koch tools + # optionally generate docs into doc/html + ./koch docs - # extract files to be really installed - ./install.sh + ./koch tools -d:release - # also include the tools - for fn in nimble nimsuggest nimgrep; do cp ./bin/$fn /nim/bin/; done - ``` + # 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 What to install: -- The expected stdlib location is `/usr/lib/nim/lib`, previously it was just `/usr/lib/nim` -- `nimdoc.css` and `nimdoc.cls` from the `doc` folder should go into `/usr/lib/nim/doc/` -- `tools/debug/nim-gdb.py` should go into `/usr/lib/nim/tools/` -- `tools/dochack/dochack.js` should be installed to `/usr/lib/nim/tools/dochack/` -- Global configuration files under `/etc/nim` +- The expected stdlib location is /usr/lib/nim +- Global configuration files under /etc/nim - Optionally: manpages, documentation, shell completion - When installing documentation, .idx files are not required - The "compiler" directory contains compiler sources and should not be part of the compiler binary package - diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt index 0a8fd8187..4c557aed8 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)``. @@ -27,10 +27,7 @@ notation meaning ``{E}`` Capture: Apply expression `E` and store the substring that matched `E` into a *capture* that can be accessed after the matching process. -``{}`` Empty capture: Delete the last capture. No character - is consumed. -``$i`` Back reference to the ``i``th capture. ``i`` counts forwards - from 1 or backwards (last capture to first) from ^1. +``$i`` Back reference to the ``i``th capture. ``i`` counts from 1. ``$`` Anchor: Matches at the end of the input. No character is consumed. Same as ``!.``. ``^`` Anchor: Matches at the start of the input. No character @@ -44,20 +41,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 +63,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 a UTF-8 character - ahead, consume it and indicate success. Otherwise, indicate +``_`` Any Unicode character: If there is an 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 excluding + loop for the pattern `E`.) Everything until and exluding `E` is captured. ``@@ E`` Same as ``{@} E``. ``A <- E`` Rule: Bind the expression `E` to the *nonterminal symbol* @@ -82,7 +79,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. =============== ============================================================ @@ -131,53 +128,51 @@ notation meaning Supported PEG grammar --------------------- -The PEG parser implements this grammar (written in PEG syntax): +The PEG parser implements this grammar (written in PEG syntax):: - # Example grammar of PEG in PEG syntax. - # Comments start with '#'. - # First symbol is the start symbol. + # Example grammar of PEG in PEG syntax. + # Comments start with '#'. + # First symbol is the start symbol. - grammar <- rule* / expr + grammar <- rule* / expr - identifier <- [A-Za-z][A-Za-z0-9_]* - charsetchar <- "\\" . / [^\]] - charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]" - stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" / - "'" ("\\" . / [^'])* "'") - builtin <- "\\" identifier / [^\13\10] + identifier <- [A-Za-z][A-Za-z0-9_]* + charsetchar <- "\\" . / [^\]] + charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]" + stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" / + "'" ("\\" . / [^'])* "'") + builtin <- "\\" identifier / [^\13\10] - comment <- '#' @ \n - ig <- (\s / comment)* # things to ignore + comment <- '#' @ \n + ig <- (\s / comment)* # things to ignore - rule <- identifier \s* "<-" expr ig - identNoArrow <- identifier !(\s* "<-") - prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@' - literal <- ig identifier? '$' '^'? [0-9]+ / '$' / '^' / - ig identNoArrow / - ig charset / - ig stringlit / - ig builtin / - ig '.' / - ig '_' / - (ig "(" expr ig ")") / - (ig "{" expr? ig "}") - postfixOpr <- ig '?' / ig '*' / ig '+' - primary <- prefixOpr* (literal postfixOpr*) + rule <- identifier \s* "<-" expr ig + identNoArrow <- identifier !(\s* "<-") + prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@' + literal <- ig identifier? '$' [0-9]+ / '$' / '^' / + ig identNoArrow / + ig charset / + ig stringlit / + ig builtin / + ig '.' / + ig '_' / + (ig "(" expr ig ")") + postfixOpr <- ig '?' / ig '*' / ig '+' + primary <- prefixOpr* (literal postfixOpr*) - # Concatenation has higher priority than choice: - # ``a b / c`` means ``(a b) / c`` + # Concatenation has higher priority than choice: + # ``a b / c`` means ``(a b) / c`` - seqExpr <- primary+ - expr <- seqExpr (ig "/" expr)* + seqExpr <- primary+ + expr <- seqExpr (ig "/" expr)* **Note**: As a special syntactic extension if the whole PEG is only a single expression, identifiers are not interpreted as non-terminals, but are interpreted as verbatim string: - ```nim +.. code-block:: nim abc =~ peg"abc" # is true - ``` So it is not necessary to write ``peg" 'abc' "`` in the above example. @@ -187,25 +182,22 @@ Examples Check if `s` matches Nim's "while" keyword: - ```nim +.. code-block:: nim s =~ peg" y'while'" - ``` Exchange (key, val)-pairs: - ```nim +.. code-block:: nim "key: val; key2: val2".replacef(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1") - ``` Determine the ``#include``'ed files of a C file: - ```nim +.. code-block:: 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/prelude.rst b/doc/prelude.rst new file mode 100644 index 000000000..47fa70b70 --- /dev/null +++ b/doc/prelude.rst @@ -0,0 +1,29 @@ +Prelude +======= + +This is an include file that simply imports common modules for your convenience: + +.. code-block:: nim + include prelude + +Same as: + +.. code-block:: nim + import os, strutils, times, parseutils, parseopt, hashes, tables, sets + + +Examples +======== + +Get the basic most common imports ready to start coding using ``prelude``: + +.. code-block:: nim + include prelude + + echo now() + echo getCurrentDir() + echo "Hello $1".format("World") + + +See also: +- `Sugar `_ diff --git a/doc/readme.txt b/doc/readme.txt index 7b1a445b5..6f4cece87 100644 --- a/doc/readme.txt +++ b/doc/readme.txt @@ -1,5 +1,5 @@ ============================ -Nim's documentation system +Nim's documenation system ============================ This folder contains Nim's documentation. The documentation diff --git a/doc/refc.md b/doc/refc.md deleted file mode 100644 index 4023748e6..000000000 --- a/doc/refc.md +++ /dev/null @@ -1,156 +0,0 @@ -Tweaking the refc GC -==================== - -Cycle collector ---------------- - -The cycle collector can be en-/disabled independently from the other parts of -the garbage collector with `GC_enableMarkAndSweep` and `GC_disableMarkAndSweep`. - - -Soft real-time support ----------------------- - -To enable real-time support, the symbol `useRealtimeGC`:idx: needs to be -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: - - ```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. - -These two procs are the two modus operandi of the real-time garbage collector: - -(1) GC_SetMaxPause Mode - - You can call `GC_SetMaxPause` at program startup and then each triggered - garbage collector run tries to not take longer than `maxPause` time. However, it is - possible (and common) that the work is nevertheless not evenly distributed - as each call to `new` can trigger the garbage collector and thus take `maxPause` - time. - -(2) GC_step Mode - - This allows the garbage collector to perform some work for up to `us` time. - This is useful to call in the main loop to ensure the garbage collector can do its work. - To bind all garbage collector activity to a `GC_step` call, - deactivate the garbage collector with `GC_disable` at program startup. - If `strongAdvice` is set to `true`, - then the garbage collector will be forced to perform the collection cycle. - Otherwise, the garbage collector may decide not to do anything, - if there is not much garbage to collect. - You may also specify the current stack size via `stackSize` parameter. - It can improve performance when you know that there are no unique Nim references - below a certain point on the stack. Make sure the size you specify is greater - than the potential worst-case size. - - It can improve performance when you know that there are no unique Nim - references below a certain point on the stack. Make sure the size you specify - is greater than the potential worst-case size. - -These procs provide a "best effort" real-time guarantee; in particular the -cycle collector is not aware of deadlines. Deactivate it to get more -predictable real-time behaviour. Tests show that a 1ms max pause -time will be met in almost all cases on modern CPUs (with the cycle collector -disabled). - - -Time measurement with garbage collectors ----------------------------------------- - -The garbage collectors' way of measuring time uses -(see ``lib/system/timers.nim`` for the implementation): - -1) `QueryPerformanceCounter` and `QueryPerformanceFrequency` on Windows. -2) `mach_absolute_time` on Mac OS X. -3) `gettimeofday` on Posix systems. - -As such it supports a resolution of nanoseconds internally; however, the API -uses microseconds for convenience. - -Define the symbol `reportMissedDeadlines` to make the -garbage collector output whenever it missed a deadline. -The reporting will be enhanced and supported by the API in later versions of the collector. - - -Tweaking the garbage collector ------------------------------- - -The collector checks whether there is still time left for its work after -every `workPackage`'th iteration. This is currently set to 100 which means -that up to 100 objects are traversed and freed before it checks again. Thus -`workPackage` affects the timing granularity and may need to be tweaked in -highly specialized environments or for older hardware. - - -Thread coordination -------------------- - -When the `NimMain()` function is called Nim initializes the garbage -collector to the current thread, which is usually the main thread of your -application. If your C code later spawns a different thread and calls Nim -code, the garbage collector will fail to work properly and you will crash. - -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 - - ```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 -lived. - -Before the thread exits, you should tear down the thread's GC to prevent memory -leaks by calling - - ```nim - system.tearDownForeignThreadGc() - ``` - - -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](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. -* `getFreeMem()` Bytes reserved by the garbage collector and not in use. -* `GC_getStatistics()` Garbage collector statistics as a human-readable string. - -These numbers are usually only for the running thread, not for the whole heap, -with the exception of `--mm:boehm`:option: and `--mm:go`:option:. - -In addition to `GC_ref` and `GC_unref` you can avoid the garbage collector by manually -allocating memory with procs like `alloc`, `alloc0`, `allocShared`, `allocShared0` or `allocCStringArray`. -The garbage collector won't try to free them, you need to call their respective *dealloc* pairs -(`dealloc`, `deallocShared`, `deallocCStringArray`, etc) -when you are done with them or they will leak. - - - -Heap dump -========= - -The heap dump feature is still in its infancy, but it already proved -useful for us, so it might be useful for you. To get a heap dump, compile -with `-d:nimTypeNames`:option: and call `dumpNumberOfInstances` -at a strategic place in your program. -This produces a list of the used types in your program and for every type -the total amount of object instances for this type as well as the total -amount of bytes these instances take up. - -The numbers count the number of objects in all garbage collector heaps, they refer to -all running threads, not only to the current thread. (The current thread -would be the thread that calls `dumpNumberOfInstances`.) This might -change in later versions. diff --git a/doc/regexprs.txt b/doc/regexprs.txt index fa7f9d24a..b7370d858 100644 --- a/doc/regexprs.txt +++ b/doc/regexprs.txt @@ -47,18 +47,16 @@ full documentation of Perl's regular expressions. Because the backslash ``\`` is a meta character both in the Nim programming language and in regular expressions, it is strongly recommended that one uses the *raw* strings of Nim, so that -backslashes are interpreted by the regular expression engine: +backslashes are interpreted by the regular expression engine:: -```nim r"\S" # matches any character that is not whitespace -``` A regular expression is a pattern that is matched against a subject string from left to right. Most characters stand for themselves in a pattern, and match the corresponding characters in the subject. As a trivial example, -the pattern: +the pattern:: - The quick brown fox + The quick brown fox matches a portion of a subject string that is identical to itself. The power of regular expressions comes from the ability to include @@ -130,7 +128,7 @@ in patterns in a visible manner. There is no restriction on the appearance of non-printing characters, apart from the binary zero that terminates a pattern, but when a pattern is being prepared by text editing, it is usually easier to use one of the following escape sequences than the binary character it -represents: +represents:: ============== ============================================================ character meaning @@ -148,7 +146,7 @@ character meaning After ``\x``, from zero to two hexadecimal digits are read (letters can be in upper or lower case). In UTF-8 mode, any number of hexadecimal digits may appear between ``\x{`` and ``}``, but the value of the character code must be -less than 2^31 (that is, the maximum hexadecimal value is 7FFFFFFF). If +less than 2**31 (that is, the maximum hexadecimal value is 7FFFFFFF). If characters other than hexadecimal digits appear between ``\x{`` and ``}``, or if there is no terminating ``}``, this form of escape is not recognized. Instead, the initial ``\x`` will be interpreted as a basic hexadecimal escape, @@ -246,7 +244,7 @@ The fourth use of backslash is for certain `simple assertions`:idx:. An assertion specifies a condition that has to be met at a particular point in a match, without consuming any characters from the subject string. The use of subpatterns for more complicated assertions is described below. The -backslashed assertions are: +backslashed assertions are:: ============== ============================================================ assertion meaning diff --git a/doc/rstcommon.rst b/doc/rstcommon.rst deleted file mode 100644 index e9cc615db..000000000 --- a/doc/rstcommon.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. - Usage of this file: - Add this in the beginning of *.rst file: - - .. default-role:: code - .. include:: rstcommon.rst - - It's the current trick for brevity and compatibility with both Github and - rst2html.py, considering that Github cannot highlight Nim in - RST files anyway and it does not include files. - This way interpreted text is displayed with monospaced font in Github - and it's displayed an Nim code in both rst2html.py - (note ".. default-role:: Nim" above) and `nim rst2html`. - - For files that are user manual and consist of stuff like cmdline - option description, use 'code' as a **real** default role: - - .. include:: rstcommon.rst - .. default-role:: code - -.. define language roles explicitly (for compatibility with rst2html.py): - -.. role:: nim(code) - :language: nim - -.. default-role:: nim - -.. role:: c(code) - :language: c - -.. role:: cpp(code) - :language: cpp - -.. role:: yaml(code) - :language: yaml - -.. role:: python(code) - :language: python - -.. role:: java(code) - :language: java - -.. role:: csharp(code) - :language: csharp - -.. role:: cmd(code) - -.. role:: program(code) - -.. role:: option(code) - diff --git a/doc/sets_fragment.txt b/doc/sets_fragment.txt index fba98db2d..8436190a0 100644 --- a/doc/sets_fragment.txt +++ b/doc/sets_fragment.txt @@ -1,34 +1,27 @@ The set type models the mathematical notion of a set. The set's basetype can only be an ordinal type of a certain size, namely: -* `int8`-`int16` -* `uint8`/`byte`-`uint16` -* `char` -* `enum` +* ``int8``-``int16`` +* ``uint8``/``byte``-``uint16`` +* ``char`` +* ``enum`` or equivalent. For signed integers the set's base type is defined to be in the -range `0 .. MaxSetElements-1` where `MaxSetElements` is currently always +range ``0 .. MaxSetElements-1`` where ``MaxSetElements`` is currently always 2^16. 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: -```nim +.. code-block:: nim - var s: set[int64] # Error: set is too large; use `std/sets` for ordinal types - # with more than 2^16 elements + var s: set[int64] # Error: set is too large -``` - - -**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 +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): - ```nim +.. code-block:: nim type CharSet = set[char] var @@ -36,44 +29,37 @@ 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' - ``` - -The module [`std/setutils`](https://nim-lang.org/docs/setutils.html) provides a way to initialize a set from an iterable: - -```nim -import std/setutils - -let uniqueChars = myString.toSet -``` These operations are supported by sets: ================== ======================================================== operation meaning ================== ======================================================== -`A + B` union of two sets -`A * B` intersection of two sets -`A - B` difference of two sets (A without B's elements) -`A == B` set equality -`A <= B` subset relation (A is subset of B or equal to B) -`A < B` strict subset relation (A is a proper subset of B) -`e in A` set membership (A contains element e) -`e notin A` A does not contain element e -`contains(A, e)` A contains element e -`card(A)` the cardinality of A (number of elements in A) -`incl(A, elem)` same as `A = A + {elem}` -`excl(A, elem)` same as `A = A - {elem}` +``A + B`` union of two sets +``A * B`` intersection of two sets +``A - B`` difference of two sets (A without B's elements) +``A == B`` set equality +``A <= B`` subset relation (A is subset of B or equal to B) +``A < B`` strict subset relation (A is a proper subset of B) +``e in A`` set membership (A contains element e) +``e notin A`` A does not contain element e +``contains(A, e)`` A contains element e +``card(A)`` the cardinality of A (number of elements in A) +``incl(A, elem)`` same as ``A = A + {elem}`` +``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 -constants that have to be `or`'ed together. +constants that have to be ``or``'ed together. Enum, sets and casting can be used together as in: - ```nim +.. code-block:: nim + type MyFlag* {.size: sizeof(cint).} = enum A @@ -91,11 +77,10 @@ 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](manual.html#implementation-specific-pragmas-bitsize-pragma). +`bitsize pragma `_. diff --git a/doc/spawn.txt b/doc/spawn.txt index ed25ad5fd..ab667ff48 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](threadpool.html) module to work. +Both need the `threadpool `_ 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,8 +28,8 @@ 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: - ```nim - import std/threadpool, ... +.. code-block:: nim + import threadpool, ... # wait until 2 out of 3 servers received the update: proc main = @@ -40,7 +40,6 @@ 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 @@ -55,9 +54,9 @@ Parallel statement Example: - ```nim +.. code-block:: nim # Compute PI in an inefficient way - import std/[strutils, math, threadpool] + import strutils, math, threadpool proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1) @@ -70,7 +69,6 @@ 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 1bfd60213..62ddd1ec8 100644 --- a/doc/subexes.txt +++ b/doc/subexes.txt @@ -47,7 +47,8 @@ Notation meaning Examples ======== - ```nim +.. code-block:: nim + subex"$1($', '{2..})" % ["f", "a", "b", "c"] == "f(a, b, c)" subex"$1 $[files|file|files]{1} copied" % ["1"] == "1 file copied" @@ -56,5 +57,5 @@ Examples subex("type\n TEnum = enum\n $', '40c'\n '{..}") % [ "fieldNameA", "fieldNameB", "fieldNameC", "fieldNameD"] - ``` + diff --git a/doc/testament.md b/doc/testament.md deleted file mode 100644 index 08c3cd835..000000000 --- a/doc/testament.md +++ /dev/null @@ -1,341 +0,0 @@ -=========== - Testament -=========== - -.. default-role:: code -.. include:: rstcommon.rst -.. contents:: - -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](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. - - -Test files location -=================== - -By default, Testament looks for test files on ``"./tests/category/*.nim"``. -You can overwrite this pattern glob using `pattern `:option:. -The default working directory path can be changed using -`--directory:"folder/subfolder/"`:option:. - -Testament uses the `nim`:cmd: compiler on `PATH`. -You can change that using `--nim:"folder/subfolder/nim"`:option:. -Running JavaScript tests with `--targets:"js"`:option: requires -a working NodeJS on `PATH`. - -Commands -======== - -p|pat|pattern run all the tests matching the given pattern -all run all tests inside of category folders -c|cat|category run all the tests of a certain category -r|run run single test file -html generate testresults.html from the database - - -Options -======= - ---print print results to the console ---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: 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. ---megatest:on|off Enable or disable megatest. Default is on. ---valgrind:on|off Enable or disable valgrind support. Default is on. ---skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored - - -Running a single test -===================== - -This is a minimal example to understand the basics, -not very useful for production, but easy to understand: - - ```console - $ mkdir -p tests/category - $ echo "assert 42 == 42" > tests/category/test0.nim - $ testament run tests/category/test0.nim - PASS: tests/category/test0.nim c ( 0.2 sec) - $ testament r tests/category/test0 - PASS: tests/category/test0.nim C ( 0.2 sec) - ``` - - -Running all tests from a directory -================================== - - This will run all tests in the top level tests/ directory. NOTE: these - tests are skipped by `testament all`. - - ```console - $ testament pattern "tests/*.nim" - ``` - -To search for tests deeper in a directory, use - - ```console - $ testament pattern "tests/**/*.nim" # one level deeper - $ testament pattern "tests/**/**/*.nim" # two levels deeper - ``` - -HTML Reports -============ - -Generate HTML Reports ``testresults.html`` from unittests, -you have to run at least 1 test *before* generating a report: - - ```console - $ testament html - ``` - - -Writing Unit tests -================== - -Example "template" **to edit** and write a Testament unittest: - - ```nim - discard """ - - # What actions to expect completion on. - # Options: - # "compile": expect successful compilation - # "run": expect successful compilation and execution - # "reject": expect failed compilation. The "reject" action can catch - # {.error.} pragmas but not {.fatal.} pragmas because - # {.fatal.} pragmas guarantee that compilation will be aborted. - action: "run" - - # The exit code that the test is expected to return. Typically, the default - # value of 0 is fine. Note that if the test will be run by valgrind, then - # the test will exit with either a code of 0 on success or 1 on failure. - exitcode: 0 - - # Provide an `output` string to assert that the test prints to standard out - # 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 (the output includes both the compiler and test execution output). - output: "" - outputsub: "" - - # Whether to sort the output lines before comparing them to the desired - # output. - sortoutput: true - - # Each line in the string given here appears in the same order in the - # compiler output, but there may be more lines that appear before, after, or - # in between them. - nimout: ''' - a very long, - multi-line - string''' - - # This is the Standard Input the test should take, if any. - input: "" - - # Error message the test should print, if any. - errormsg: "" - - # Can be run in batch mode, or not. - batchable: true - - # 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 - # accesses or memory leaks. On other architectures, the test will be run - # as-is, without Valgrind. - # Options: - # true: run the test with Valgrind - # false: run the without Valgrind - # "leaks": run the test with Valgrind, but do not check for memory leaks - valgrind: false # Can use Valgrind to check for memory leaks, or not (Linux 64Bit only). - - # 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" - # Subject to variable interpolation. - cmd: "nim c -r $file" - - # Maximum generated temporary intermediate code file size for the test. - maxcodesize: 666 - - # Timeout seconds to run the test. Fractional values are supported. - timeout: 1.5 - - # 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 `;` - matrix: "; -d:release; -d:caseFoo -d:release" - - # Conditions that will skip this test. Use of multiple "disabled" clauses - # is permitted. - disabled: "bsd" # Can disable OSes... - disabled: "win" - disabled: "32bit" # ...or architectures - disabled: "i386" - disabled: "azure" # ...or pipeline runners - disabled: true # ...or can disable the test entirely - - """ - 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()](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. - - -Inline hints, warnings and errors (notes) ------------------------------------------ - -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: - - ```nim - discard """ - exitcode: 1 - """ - quit "Non-Zero exit code", 1 - ``` - -Standard output checking: - - ```nim - discard """ - - output: ''' - 0 - 1 - 2 - 3 - 4 - 5 - ''' - - """ - for i in 0..5: echo i - ``` - -JavaScript tests: - - ```nim - discard """ - targets: "js" - """ - when defined(js): - import std/jsconsole - console.log("My Frontend Project") - ``` - -Compile-time tests: - - ```nim - discard """ - action: "compile" - """ - static: assert 9 == 9, "Compile time assert" - ``` - -Tests without Spec: - - ```nim - assert 1 == 1 - ``` - - -See also: -* [Unittest](unittest.html) diff --git a/doc/testament.rst b/doc/testament.rst new file mode 100644 index 000000000..253afb33d --- /dev/null +++ b/doc/testament.rst @@ -0,0 +1,250 @@ +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 `_, +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. + + +Test files location +=================== + +By default Testament looks for test files on ``"./tests/*.nim"``. +You can overwrite this pattern glob using ``pattern ``. +The default working directory path can be changed using +``--directory:"folder/subfolder/"``. + +Testament uses the ``nim`` compiler on ``PATH``. +You can change that using ``--nim:"folder/subfolder/nim"``. +Running JavaScript tests with ``--targets:"js"`` requires a working NodeJS on +``PATH``. + + +Options +======= + +* ``--print`` Also print results to the console +* ``--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) +* ``--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. +* ``--skipFrom:file`` Read tests to skip from ``file`` - one test per line, # comments ignored + + +Running a single test +===================== + +This is a minimal example to understand the basics, +not very useful for production, but easy to understand: + +.. code:: + + $ 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:: + + $ testament pattern "tests/*.nim" + +To search for tests deeper in a directory, use + +.. code:: + + $ testament pattern "tests/**/*.nim" # one level deeper + $ testament pattern "tests/**/**/*.nim" # two levels deeper + +HTML Reports +============ + +Generate HTML Reports ``testresults.html`` from unittests, +you have to run at least 1 test *before* generating a report: + +.. code:: + + $ testament html + + +Writing Unitests +================ + +Example "template" **to edit** and write a Testament unittest: + +.. code-block:: nim + + discard """ + + # What actions to expect completion on. + # Options: + # "compile": expect successful compilation + # "run": expect successful compilation and execution + # "reject": expect failed compilation. The "reject" action can catch + # {.error.} pragmas but not {.fatal.} pragmas because + # {.fatal.} pragmas guarantee that compilation will be aborted. + action: "run" + + # The exit code that the test is expected to return. Typically, the default + # value of 0 is fine. Note that if the test will be run by valgrind, then + # the test will exit with either a code of 0 on success or 1 on failure. + 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 + # the string given here is a substring of the standard out output of the + # test. + output: "" + outputsub: "" + + # Whether to sort the output lines before comparing them to the desired + # output. + sortoutput: true + + # Each line in the string given here appears in the same order in the + # compiler output, but there may be more lines that appear before, after, or + # in between them. + nimout: ''' + a very long, + multi-line + string''' + + # This is the Standard Input the test should take, if any. + input: "" + + # Error message the test should print, if any. + errormsg: "" + + # Can be run in batch mode, or not. + batchable: true + + # Can be run Joined with other tests to run all togheter, or not. + joinable: true + + # On Linux 64-bit machines, whether to use Valgrind to check for bad memory + # accesses or memory leaks. On other architectures, the test will be run + # as-is, without Valgrind. + # Options: + # true: run the test with Valgrind + # false: run the without Valgrind + # "leaks": run the test with Valgrind, but do not check for memory leaks + valgrind: false # Can use Valgrind to check for memory leaks, or not (Linux 64Bit only). + + # 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:tests/deps $options $file" + # You can use the $target, $options, and $file placeholders in your own + # command, too. + cmd: "nim c -r $file" + + # Maximum generated temporary intermediate code file size for the test. + maxcodesize: 666 + + # Timeout seconds to run the test. Fractional values are supported. + timeout: 1.5 + + # Targets to run the test into (c, cpp, objc, js). + targets: "c js" + + # flags with which to run the test, delimited by `;` + matrix: "; -d:release; -d:caseFoo -d:release" + + # Conditions that will skip this test. Use of multiple "disabled" clauses + # is permitted. + disabled: "bsd" # Can disable OSes... + disabled: "win" + disabled: "32bit" # ...or architectures + disabled: "i386" + disabled: "azure" # ...or pipeline runners + disabled: true # ...or can disable the test entirely + + """ + 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. `_ +* 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 +================= + +Expected to fail: + +.. code-block:: nim + + discard """ + errormsg: "undeclared identifier: 'not_defined'" + """ + assert not_defined == "not_defined", "not_defined is not defined" + +Non-Zero exit code: + +.. code-block:: nim + + discard """ + exitcode: 1 + """ + quit "Non-Zero exit code", 1 + +Standard output checking: + +.. code-block:: nim + + discard """ + + output: ''' + 0 + 1 + 2 + 3 + 4 + 5 + ''' + + """ + for i in 0..5: echo i + +JavaScript tests: + +.. code-block:: nim + + discard """ + targets: "js" + """ + when defined(js): + import jsconsole + console.log("My Frontend Project") + +Compile-time tests: + +.. code-block:: nim + + discard """ + action: "compile" + """ + static: assert 9 == 9, "Compile time assert" + +Tests without Spec: + +.. code-block:: nim + + assert 1 == 1 + + +See also: +* `Unittest `_ diff --git a/doc/tools.md b/doc/tools.rst similarity index 56% rename from doc/tools.md rename to doc/tools.rst index 6849103f9..f231cdc7d 100644 --- a/doc/tools.md +++ b/doc/tools.rst @@ -2,41 +2,38 @@ Tools available with Nim ======================== -.. default-role:: code -.. include:: rstcommon.rst - The standard distribution ships with the following tools: -- | [Hot code reloading](hcr.html) +- | `Hot code reloading `_ | The "Hot code reloading" feature is built into the compiler but has its own document explaining how it works. -- | [Documentation generator](docgen.html) - | The builtin document generator `nim doc`:cmd: generates HTML documentation +- | `Documentation generator `_ + | The builtin document generator ``nim doc`` generates HTML documentation from ``.nim`` source files. -- | [Nimsuggest for IDE support](nimsuggest.html) - | Through the `nimsuggest`:cmd: tool, any IDE can query a ``.nim`` source file +- | `Nimsuggest for IDE support `_ + | Through the ``nimsuggest`` tool, any IDE can query a ``.nim`` source file and obtain useful information like the definition of symbols or suggestions for completion. -- | [C2nim](https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst) +- | `C2nim `_ | C to Nim source converter. Translates C header files to Nim. -- | [niminst](niminst.html) +- | `niminst `_ | niminst is a tool to generate an installer for a Nim program. -- | [nimgrep](nimgrep.html) +- | `nimgrep `_ | Nim search and replace utility. - | nimpretty - | `nimpretty`:cmd: is a Nim source code beautifier, + | ``nimpretty`` is a Nim source code beautifier, to format code according to the official style guide. -- | [testament](https://nim-lang.github.io/Nim/testament.html) - | `testament`:cmd: is an advanced automatic *unittests runner* for Nim tests, +- | `testament `_ + | ``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, JS, etc), - [simulated Dry-Runs](https://en.wikipedia.org/wiki/Dry_run_(testing)), + `simulated Dry-Runs `_, 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.md b/doc/tut1.rst similarity index 61% rename from doc/tut1.md rename to doc/tut1.rst index 2e83effa3..397782330 100644 --- a/doc/tut1.md +++ b/doc/tut1.rst @@ -5,31 +5,23 @@ Nim Tutorial (Part I) :Author: Andreas Rumpf :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: Introduction ============ -> "Der Mensch ist doch ein Augentier -- Schöne Dinge wünsch' ich mir." +.. raw:: html +

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

    This document is a tutorial for the programming language *Nim*. - This tutorial assumes that you are familiar with basic programming concepts -like variables, types, or statements. - -Here are several other resources for learning Nim: - -* [Nim Basics tutorial](https://narimiran.github.io/nim-basics/) - a gentle - introduction of the concepts mentioned above -* [Learn Nim in 5 minutes](https://learnxinyminutes.com/docs/nim/) - quick, - five-minute introduction to Nim -* [The Nim manual](manual.html) - many more examples of the advanced language features - +like variables, types, or statements but is kept very basic. The `manual +`_ contains 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](nep1.html). +Nim's documentation, follow the `Nim style guide `_. The first program @@ -37,69 +29,59 @@ The first program We start the tour with a modified "hello world" program: - ```Nim test = "nim c $1" +.. code-block:: 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: - ```cmd +Save this code to the file "greetings.nim". Now compile and run it:: + nim compile --run greetings.nim - ``` -With the ``--run`` [switch](nimc.html#compiler-usage-commandminusline-switches) Nim +With the ``--run`` `switch `_ Nim executes the file automatically after compilation. You can give your program -command-line arguments by appending them after the filename: - ```cmd +command-line arguments by appending them after the filename:: + nim compile --run greetings.nim arg1 arg2 - ``` -Commonly used commands and switches have abbreviations, so you can also use: - ```cmd +Commonly used commands and switches have abbreviations, so you can also use:: + nim c -r greetings.nim - ``` -This is a **debug version**. -To compile a release version use: - ```cmd +To compile a release version use:: + nim c -d:release greetings.nim - ``` 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]( -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 -by checks that are **not even available** for C. +`turned off and optimizations are turned on +`_. Though it should be pretty obvious what the program does, I will explain the syntax: statements which are not indented are executed when the program starts. Indentation is Nim's way of grouping statements. Indentation is 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](syncio.html#readLine,File) procedure. Since the -compiler knows that [readLine](syncio.html#readLine,File) returns a string, +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, you can leave out the type in the declaration (this is called `local type inference`:idx:). So this will work too: - ```Nim test = "nim c $1" +.. code-block:: 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](syncio.html#readLine,File), etc. -These built-ins are declared in the [system](system.html) module which is implicitly +to the compiler: ``echo``, `readLine `_, etc. +These built-ins are declared in the system_ module which is implicitly imported by any other module. @@ -118,14 +100,13 @@ 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: - ```Nim +.. code-block:: Nim r"C:\program files\nim" - ``` In raw literals, the backslash is not an escape character. The third and last way to write string literals is *long-string literals*. -They are written with three quotes: `""" ... """`; they can span over +They are written with three quotes: ``""" ... """``; they can span over multiple lines and the ``\`` is not an escape character either. They are very useful for embedding HTML code templates for example. @@ -134,23 +115,24 @@ Comments -------- Comments start anywhere outside a string or character literal with the -hash character `#`. Documentation comments start with `##`: +hash character ``#``. Documentation comments start with ``##``: - ```nim test = "nim c $1" +.. code-block:: 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 the input file as they belong to the syntax tree! This feature enables simpler documentation generators. -Multiline comments are started with `#[` and terminated with `]#`. Multiline +Multiline comments are started with ``#[`` and terminated with ``]#``. Multiline comments can also be nested. - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" #[ You can have any Nim code text commented out inside this with no indentation restrictions. @@ -159,17 +141,16 @@ comments can also be nested. Note: these can be nested!! ]# ]# - ``` Numbers ------- Numerical literals are written as in most other languages. As a special twist, -underscores are allowed for better readability: `1_000_000` (one million). +underscores are allowed for better readability: ``1_000_000`` (one million). A number that contains a dot (or 'e' or 'E') is a floating-point literal: -`1.0e9` (one billion). Hexadecimal literals are prefixed with `0x`, -binary literals with `0b` and octal literals with `0o`. A leading zero +``1.0e9`` (one billion). Hexadecimal literals are prefixed with ``0x``, +binary literals with ``0b`` and octal literals with ``0o``. A leading zero alone does not produce an octal. @@ -177,19 +158,49 @@ The var statement ================= The var statement declares a new local or global variable: - ```nim - var x, y: int # declares x and y to have the type `int` - ``` +.. code-block:: + 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 +Indentation can be used after the ``var`` keyword to list a whole section of variables: - ```nim test = "nim c $1" +.. code-block:: + :test: "nim c $1" var x, y: int # a comment can occur here too a, b, c: string - ``` + + +The assignment statement +======================== + +The assignment statement assigns a new value to a variable or more generally +to a storage location: + +.. code-block:: + 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" + 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" + +Note that declaring multiple variables with a single assignment that calls a +procedure can have unexpected results: the compiler will *unroll* the +assignments and end up calling the procedure several times. If the result of +the procedure depends on side effects, your variables may end up having +different values! For safety use side-effect-free procedures if making multiple +assignments. Constants @@ -199,69 +210,42 @@ 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: - ```nim test = "nim c $1" +.. code-block:: 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 +Indentation can be used after the ``const`` keyword to list a whole section of constants: - ```nim test = "nim c $1" +.. code-block:: + :test: "nim c $1" const x = 1 # a comment can occur here too y = 2 z = y + 5 # computations are possible - ``` The let statement ================= -The `let` statement works like the `var` statement but the declared +The ``let`` statement works like the ``var`` statement but the declared symbols are *single assignment* variables: After the initialization their value cannot change: - ```nim +.. code-block:: 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 +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": - ```nim +.. code-block:: const input = readLine(stdin) # Error: constant expression expected - ``` - ```nim test = "nim c $1" +.. code-block:: + :test: "nim c $1" let input = readLine(stdin) # works - ``` - - -The assignment statement -======================== - -The assignment statement assigns a new value to a variable or more generally -to a storage location: - - ```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: - - ```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 @@ -277,7 +261,8 @@ If statement The if statement is one way to branch the control flow: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" let name = readLine(stdin) if name == "": echo "Poor soul, you lost your name?" @@ -285,21 +270,21 @@ 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 -excessive indentation. (The `""` is the empty string. It contains no +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 +excessive indentation. (The ``""`` is the empty string. It contains no characters.) Case statement -------------- -Another way to branch is provided by the case statement. A case statement allows -for multiple branches: +Another way to branch is provided by the case statement. A case statement is +a multi-branch: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" let name = readLine(stdin) case name of "": @@ -310,44 +295,41 @@ for multiple branches: echo "Cool name!" else: echo "Hi, ", name, "!" - ``` -As it can be seen, for an `of` branch a comma-separated list of values is also +As it can be seen, for an ``of`` branch a comma-separated list of values is also allowed. 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: - ```nim +.. code-block:: nim # this statement will be explained later: - from std/strutils import parseInt + from strutils import parseInt echo "A number please: " let n = parseInt(readLine(stdin)) 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 -`0..8`. Since it is not very practical to list every other possible integer +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 +``0..8``. Since it is not very practical to list every other possible integer (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: - ```nim +.. code-block:: 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] is a *do +The empty `discard statement <#procedures-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` +all possible string values: that is why string cases always need an ``else`` branch. In general, the case statement is used for subrange types or enumerations where @@ -360,13 +342,15 @@ While statement The while statement is a simple looping construct: - ```nim test = "nim c $1" +.. code-block:: 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 - ``` + 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). @@ -375,118 +359,113 @@ as the user types in nothing (only presses RETURN). For statement ------------- -The `for` statement is a construct to loop over any element an *iterator* -provides. The example uses the built-in [countup]( -system.html#countup.i,T,T,Positive) iterator: +The ``for`` statement is a construct to loop over any element an *iterator* +provides. The example uses the built-in `countup +`_ iterator: - ```nim test = "nim c $1" +.. code-block:: 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]( -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: +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 +1, 2, .., 10. Each value is ``echo``-ed. This code does the same: - ```nim +.. code-block:: nim echo "Counting to 10: " var i = 1 while i <= 10: echo i - inc i # increment i by 1 + 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 [..]( -system.html#...i,T,T) iterator that does the same: - - ```nim - for i in 1 .. 10: - ... - ``` Counting down can be achieved as easily (but is less often needed): - ```nim +.. code-block:: 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](system.html#^.t%2Cint)) to simplify +Since counting up occurs so often in programs, Nim also has a `.. +`_ iterator that does the same: + +.. code-block:: nim + for i in 1 .. 10: + ... + +Zero-indexed counting has two shortcuts ``..<`` and ``.. ^1`` +(`backward index operator `_) to simplify counting to one less than the higher index: - ```nim +.. code-block:: nim for i in 0 ..< 10: - ... # the same as 0 .. 9 - ``` + ... # 0 .. 9 or - ```nim +.. code-block:: nim var s = "some string" for i in 0 ..< s.len: ... - ``` or - ```nim +.. code-block:: 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) +* ``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) - ```nim test = "nim c $1" +.. code-block:: 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 ------------------------------ - 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 +new scope. This means that in the following example, ``x`` is not accessible outside the loop: - ```nim test = "nim c $1" status = 1 +.. code-block:: 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` +are only visible within the block they have been declared. The ``block`` statement can be used to open a new block explicitly: - ```nim test = "nim c $1" status = 1 +.. code-block:: 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. +The block's *label* (``myblock`` in the example) is optional. Break statement --------------- - -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 +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: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" block myblock: echo "entering block" while true: @@ -502,20 +481,19 @@ 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 ------------------ - -Like in many other programming languages, a `continue` statement starts +Like in many other programming languages, a ``continue`` statement starts the next iteration immediately: - ```nim test = "nim c $1" - for i in 1 .. 5: - if i <= 3: continue - echo i # will only print 4 and 5 - ``` +.. code-block:: nim + :test: "nim c $1" + while true: + let x = readLine(stdin) + if x == "": continue + echo x When statement @@ -523,7 +501,9 @@ When statement Example: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + when system.hostOS == "windows": echo "running on Windows!" elif system.hostOS == "linux": @@ -532,19 +512,18 @@ 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 +The ``when`` statement is almost identical to the ``if`` statement, but with these differences: * Each condition must be a constant expression since it is evaluated by the compiler. * The statements within a branch do not open a new scope. * The compiler checks the semantics and produces code *only* for the statements - that belong to the first condition that evaluates to `true`. + that belong to the first condition that evaluates to ``true``. -The `when` statement is useful for writing platform-specific code, similar to -the `#ifdef`:c: construct in the C programming language. +The ``when`` statement is useful for writing platform-specific code, similar to +the ``#ifdef`` construct in the C programming language. Statements and indentation @@ -555,12 +534,12 @@ indentation rules. In Nim, there is a distinction between *simple statements* and *complex statements*. *Simple statements* cannot contain other statements: -Assignment, procedure calls, or the `return` statement are all simple -statements. *Complex statements* like `if`, `when`, `for`, `while` can +Assignment, procedure calls, or the ``return`` statement are all simple +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: - ```nim +.. code-block:: nim # no indentation needed for single-assignment statement: if x: x = false @@ -575,43 +554,41 @@ 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: - ```nim +.. code-block:: 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. -With parenthesis and semicolons `(;)` you can use statements where only +With parenthesis and semicolons ``(;)`` you can use statements where only an expression is allowed: - ```nim test = "nim c $1" +.. code-block:: 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](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](tut1.html#procedures-funcs-and-methods). In -Nim, new procedures are defined with the `proc` keyword: +To define new commands like `echo `_ +and `readLine `_ in the examples, the concept of a +`procedure` is needed. (Some languages call them *methods* or *functions*.) +In Nim new procedures are defined with the ``proc`` keyword: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" proc yes(question: string): bool = echo question, " (y/n)" while true: @@ -624,81 +601,79 @@ 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` +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 -false if they answered "no" (or something similar). A `return` statement +false if they answered "no" (or something similar). A ``return`` statement leaves the procedure (and therefore the while loop) immediately. The -`(question: string): bool` syntax describes that the procedure expects a -parameter named `question` of type `string` and returns a value of type -`bool`. The `bool` type is built-in: the only valid values for `bool` are -`true` and `false`. -The conditions in if or while statements must be of type `bool`. +``(question: string): bool`` syntax describes that the procedure expects a +parameter named ``question`` of type ``string`` and returns a value of type +``bool``. The ``bool`` type is built-in: the only valid values for ``bool`` are +``true`` and ``false``. +The conditions in if or while statements must be of type ``bool``. -Some terminology: in the example `question` is called a (formal) *parameter*, -`"Should I..."` is called an *argument* that is passed to this parameter. +Some terminology: in the example ``question`` is called a (formal) *parameter*, +``"Should I..."`` is called an *argument* that is passed to this parameter. Result variable --------------- - -A procedure that returns a value has an implicit `result` variable declared -that represents the return value. A `return` statement with no expression is -shorthand for `return result`. The `result` value is always returned -automatically at the end of a procedure if there is no `return` statement at +A procedure that returns a value has an implicit ``result`` variable declared +that represents the return value. A ``return`` statement with no expression is +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. - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" proc sumTillNegative(x: varargs[int]): int = for i in x: if i < 0: return result = result + i - echo sumTillNegative() # echoes 0 - echo sumTillNegative(3, 4, 5) # echoes 12 - echo sumTillNegative(3, 4 , -1 , 6) # echoes 7 - ``` + echo sumTillNegative() # echos 0 + echo sumTillNegative(3, 4, 5) # echos 12 + echo sumTillNegative(3, 4 , -1 , 6) # echos 7 -The `result` variable is already implicitly declared at the start of the +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 with a normal variable of the same name. The result variable is also already initialized with the type's default value. Note that referential data types will -be `nil` at the start of the procedure, and thus may require manual +be ``nil`` at the start of the procedure, and thus may require manual initialization. -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, +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 - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" proc helloWorld(): string = - "Hello, World!" - ``` + "Hello, World!" returns the string "Hello, World!". Parameters ---------- - Parameters are immutable in the procedure body. By default, their value cannot be changed because this allows the compiler to implement parameter passing in the 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 +to be declared with ``var`` in the procedure body. Shadowing the parameter name is possible, and actually an idiom: - ```nim test = "nim c $1" +.. code-block:: 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: +caller, a ``var`` parameter can be used: - ```nim test = "nim c $1" +.. code-block:: 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 @@ -708,9 +683,8 @@ 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`. +In the example, ``res`` and ``remainder`` are `var parameters`. Var parameters can be modified by the procedure and the changes are visible to the caller. Note that the above example would better make use of a tuple as a return value instead of using var parameters. @@ -718,25 +692,23 @@ a tuple as a return value instead of using var parameters. Discard statement ----------------- - 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 +its return value, a ``discard`` statement **must** be used. Nim does not allow silently throwing away a return value: - ```nim +.. code-block:: 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: +been declared with the ``discardable`` pragma: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" proc p(x, y: int): int {.discardable.} = return x + y p(3, 4) # now valid - ``` Named arguments @@ -744,59 +716,54 @@ 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: - ```nim +.. code-block:: 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 +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: - ```nim +.. code-block:: nim var w = createWindow(0, 0, title = "My Application", height = 600, width = 800, true) - ``` The compiler checks that each parameter receives exactly one argument. Default values -------------- - -To make the `createWindow` proc easier to use it should provide `default +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: - ```nim +.. code-block:: 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 +Now the call to ``createWindow`` only needs to set the values that differ from the defaults. Note that type inference works for parameters with default values; there is -no need to write `title: string = "unknown"`, for example. +no need to write ``title: string = "unknown"``, for example. Overloaded procedures --------------------- - Nim provides the ability to overload procedures similar to C++: - ```nim +.. code-block:: nim proc toString(x: int): string = result = if x < 0: "negative" @@ -810,49 +777,48 @@ 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 [$](dollars.html) operator in -Nim.) The compiler chooses the most appropriate proc for the `toString` +(Note that ``toString`` is usually the `$ `_ 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. +discussed here (it will be specified in the manual soon). However, it does +not lead to nasty surprises and is based on a quite simple unification +algorithm. Ambiguous calls are reported as errors. Operators --------- - -The Nim standard library makes heavy use of overloading - one reason for this is that -each operator like `+` is just an overloaded proc. The parser lets you -use operators in *infix notation* (`a + b`) or *prefix notation* (`+ a`). +The Nim library makes heavy use of overloading - one reason for this is that +each operator like ``+`` is just an overloaded proc. The parser lets you +use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``). An infix operator always receives two arguments, a prefix operator always one. (Postfix operators are not possible, because this would be ambiguous: does -`a @ @ b` mean `(a) @ (@b)` or `(a@) @ (b)`? It always means -`(a) @ (@b)`, because there are no postfix operators in Nim.) +``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means +``(a) @ (@b)``, because there are no postfix operators in Nim.) -Apart from a few built-in keyword operators such as `and`, `or`, `not`, +Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``, operators always consist of these characters: -`+ - * \ / < > = @ $ ~ & % ! ? ^ . |` +``+ - * \ / < > = @ $ ~ & % ! ? ^ . |`` User-defined operators are allowed. Nothing stops you from defining your own -`@!?+~` operator, but doing so may reduce readability. +``@!?+~`` 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](manual.html#syntax-precedence). +can be found in the manual. -To define a new operator enclose the operator in backticks "`": +To define a new operator enclose the operator in backticks "``": - ```nim +.. code-block:: 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 +The "``" notation can also be used to call an operator just like any other procedure: - ```nim test = "nim c $1" - if `==`( `+`(3, 4), 7): echo "true" - ``` +.. code-block:: nim + :test: "nim c $1" + if `==`( `+`(3, 4), 7): echo "True" Forward declarations @@ -863,12 +829,11 @@ 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: - ```nim +.. code-block:: nim # forward declaration: proc even(n: int): bool - ``` - ```nim +.. code-block:: nim proc odd(n: int): bool = assert(n >= 0) # makes sure we don't run into negative recursion if n == 0: false @@ -880,13 +845,12 @@ 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 +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. +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. Later versions of the language will weaken the requirements for forward declarations. @@ -895,86 +859,59 @@ The example also shows that a proc's body can consist of a single expression whose value is then returned implicitly. -Funcs and methods ------------------ - -As mentioned in the introduction, Nim differentiates between procedures, -functions, and methods, defined by the `proc`, `func`, and `method` keywords -respectively. In some ways, Nim is a bit more pedantic in its definitions than -other languages. - -Functions are closer to the concept of a pure mathematical -function, which might be familiar to you if you've ever done functional -programming. Essentially they are procedures with additional limitations set on -them: they can't access global state (except `const`) and can't produce -side-effects. The `func` keyword is basically an alias for `proc` tagged -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 -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](tut2.html#object-oriented-programming-dynamic-dispatch). - - Iterators ========= Let's return to the simple counting example: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" echo "Counting to ten: " for i in countup(1, 10): echo i - ``` -Can a [countup](system.html#countup.i,T,T,Positive) proc be written that -supports this loop? Let's try: +Can a `countup `_ proc be written that +supports this loop? Lets try: - ```nim +.. code-block:: 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 +only ``return``, but return and **continue** after an iteration has 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: +the only thing left to do is to replace the ``proc`` keyword by ``iterator`` +and here it is - our first iterator: - ```nim test = "nim c $1" +.. code-block:: 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: * Iterators can only be called from for loops. -* Iterators cannot contain a `return` statement (and procs cannot contain a - `yield` statement). -* Iterators have no implicit `result` variable. +* Iterators cannot contain a ``return`` statement (and procs cannot contain a + ``yield`` statement). +* Iterators have no implicit ``result`` variable. * Iterators do not support recursion. -* Iterators cannot be forward declared, because the compiler must be able to inline an iterator. - (This restriction will be gone in a future version of the compiler.) +* Iterators cannot be forward declared, because the compiler must be able to inline an iterator. (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]( -manual.html#iterators-and-the-for-statement-firstminusclass-iterators) +However, you can also use a ``closure`` iterator to get a different set of +restrictions. See `first-class 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 to +essentially they have their own namespaces. Therefore it is common practice 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]( -strutils.html). +iterator and return it as a sequence, like ``split`` from the `strutils module +`_. Basic types @@ -986,85 +923,82 @@ that are available for them in detail. 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. +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. -The operators `not, and, or, xor, <, <=, >, >=, !=, ==` are defined -for the bool type. The `and` and `or` operators perform short-circuit +The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined +for the bool type. The ``and`` and ``or`` operators perform short-circuit evaluation. For example: - ```nim +.. code-block:: nim + while p != nil and p.name != "xyz": # p.name is not evaluated if p == nil p = p.next - ``` Characters ---------- - -The *character type* is called `char`. Its size is always one byte, so +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 multibyte UTF-8 character. +that makes up a multi-byte 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. Character literals are enclosed in single quotes. -Chars can be compared with the `==`, `<`, `<=`, `>`, `>=` operators. -The `$` operator converts a `char` to a `string`. Chars cannot be mixed -with integers; to get the ordinal value of a `char` use the `ord` proc. -Converting from an integer to a `char` is done with the `chr` proc. +Chars can be compared with the ``==``, ``<``, ``<=``, ``>``, ``>=`` operators. +The ``$`` operator converts a ``char`` to a ``string``. Chars cannot be mixed +with integers; to get the ordinal value of a ``char`` use the ``ord`` proc. +Converting from an integer to a ``char`` is done with the ``chr`` proc. Strings ------- - String variables are **mutable**, so appending to a string is possible, and quite efficient. Strings in Nim are both zero-terminated and have a -length field. A string's length can be retrieved with the builtin `len` +length field. A string's length can be retrieved with the builtin ``len`` procedure; the length never counts the terminating zero. Accessing the terminating zero is an error, it only exists so that a Nim string can be converted -to a `cstring` without doing a copy. +to a ``cstring`` without doing a copy. -The assignment operator for strings copies the string. You can use the `&` -operator to concatenate strings and `add` to append to a string. +The assignment operator for strings copies the string. You can use the ``&`` +operator to concatenate strings and ``add`` to append to a string. Strings are compared using their lexicographical order. All the comparison operators are supported. By convention, all strings are UTF-8 encoded, 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*. +a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of +``s``, not the i-th *unichar*. -A string variable is initialized with the empty string `""`. +A string variable is initialized with the empty string ``""``. Integers -------- - Nim has these integer types built-in: -`int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64`. +``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``. -The default integer type is `int`. Integer literals can have a *type suffix* +The default integer type is ``int``. Integer literals can have a *type suffix* to specify a non-default integer type: - ```nim test = "nim c $1" +.. code-block:: 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` - ``` + x = 0 # x is of type ``int`` + y = 0'i8 # y is of type ``int8`` + z = 0'i64 # z is of type ``int64`` + 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. +``int`` has the same size as a pointer. -The common operators `+ - * div mod < <= == != > >=` are defined for -integers. The `and or xor not` operators are also defined for integers and -provide *bitwise* operations. Left bit shifting is done with the `shl`, right -shifting with the `shr` operator. Bit shifting operators always treat their +The common operators ``+ - * div mod < <= == != > >=`` are defined for +integers. The ``and or xor not`` operators are also defined for integers and +provide *bitwise* operations. Left bit shifting is done with the ``shl``, right +shifting with the ``shr`` operator. Bit shifting operators always treat their arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary multiplication or division can be used. @@ -1073,66 +1007,65 @@ errors. Lossless `Automatic type conversion`:idx: is performed in expressions where different kinds of integer types are used. However, if the type conversion -would cause loss of information, the `RangeDefect`:idx: is raised (if the error +would cause loss of information, the `EOutOfRange`:idx: exception is raised (if the error cannot be detected at compile time). Floats ------ +Nim has these floating-point types built-in: ``float float32 float64``. -Nim has these floating-point types built-in: `float float32 float64`. - -The default float type is `float`. In the current implementation, -`float` is always 64-bits. +The default float type is ``float``. In the current implementation, +``float`` is always 64-bits. Float literals can have a *type suffix* to specify a non-default float type: - ```nim test = "nim c $1" +.. code-block:: 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` - ``` + 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 +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](system.html#toInt,float) and -[toFloat](system.html#toFloat,int) procs for these conversions. +versa. Use the `toInt `_ and +`toFloat `_ procs for these conversions. Type Conversion --------------- - Conversion between numerical types is performed by using the type as a function: - ```nim test = "nim c $1" +.. code-block:: 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 [$](dollars.html) (stringify) operator +As mentioned earlier, the built-in `$ `_ (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. +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]( -system.html#repr,T) proc which works with any type and even complex data +having to write its ``$`` operator. You can use then the `repr +`_ 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: +there is a difference between the ``$`` and ``repr`` outputs: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" var myBool = true myCharacter = 'n' @@ -1148,48 +1081,47 @@ there is a difference between the `$` and `repr` outputs: echo myInteger, ":", repr(myInteger) # --> 42:42 echo myFloat, ":", repr(myFloat) - # --> 3.14:3.14 - ``` + # --> 3.1400000000000001e+00:3.1400000000000001e+00 Advanced types ============== -In Nim new types can be defined within a `type` statement: +In Nim new types can be defined within a ``type`` statement: - ```nim test = "nim c $1" +.. code-block:: 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. +``type`` statement. Enumerations ------------ - A variable of an enumeration type can only be assigned one of the enumeration's specified values. 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: - ```nim test = "nim c $1" +.. code-block:: 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" - ``` + echo x # writes "south" to `stdout` All the comparison operators can be used with enumeration types. An enumeration's symbol can be qualified to avoid ambiguities: -`Direction.south`. +``Direction.south``. -The `$` operator can convert any enumeration value to its name, and the `ord` +The ``$`` operator can convert any enumeration value to its name, and the ``ord`` proc can convert it to its underlying integer value. For better interfacing to other programming languages, the symbols of enum @@ -1199,57 +1131,55 @@ must be in ascending order. Ordinal types ------------- - -Enumerations, integer types, `char` and `bool` (and +Enumerations, integer types, ``char`` and ``bool`` (and 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 +----------------- -------------------------------------------------------- +``ord(x)`` returns the integer value that is used to represent `x`'s value -`inc(x)` increments `x` by one -`inc(x, n)` increments `x` by `n`; `n` is an integer -`dec(x)` decrements `x` by one -`dec(x, n)` decrements `x` by `n`; `n` is an integer -`succ(x)` returns the successor of `x` -`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` -================= ======================================================== +``inc(x)`` increments `x` by one +``inc(x, n)`` increments `x` by `n`; `n` is an integer +``dec(x)`` decrements `x` by one +``dec(x, n)`` decrements `x` by `n`; `n` is an integer +``succ(x)`` returns the successor of `x` +``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](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 +The `inc `_, `dec `_, `succ +`_ and `pred `_ operations can +fail by raising an `EOutOfRange` or `EOverflow` exception. (If the code has been compiled with the proper runtime checks turned on.) Subranges --------- - A subrange type is a range of values from an integer or enumeration type (the base type). Example: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" type MySubrange = range[0..5] - ``` -`MySubrange` is a subrange of `int` which can only hold the values 0 -to 5. Assigning any other value to a variable of type `MySubrange` is a +``MySubrange`` is a subrange of ``int`` which can only hold the values 0 +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](system.html#Natural) -type as `range[0..high(int)]` ([high](system.html#high,typedesc[T]) returns +The ``system`` module defines the important `Natural `_ +type as ``range[0..high(int)]`` (`high `_ 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. -Nim's `Natural` type helps to avoid this common programming error. +Nim's ``Natural`` type helps to avoid this common programming error. Sets @@ -1259,23 +1189,23 @@ Sets Arrays ------ - An array is a simple fixed-length container. Each element in an array has the same type. The array's index type can be any ordinal type. -Arrays can be constructed using `[]`: +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 x: IntArray x = [1, 2, 3, 4, 5, 6] - for i in low(x) .. high(x): + for i in low(x)..high(x): echo x[i] - ``` -The notation `x[i]` is used to access the i-th element of `x`. +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 checks can be disabled via pragmas or invoking the compiler with the ``--bound_checks:off`` command line switch. @@ -1283,12 +1213,13 @@ 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](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 +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 valid index. - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" type Direction = enum north, east, south, west @@ -1300,11 +1231,10 @@ valid index. level[north] = on level[south] = slowBlink level[east] = fastBlink - echo level # --> [on, fastBlink, slowBlink, off] + echo repr(level) # --> [on, fastBlink, slowBlink, off] 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 @@ -1314,7 +1244,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: - ```nim +.. code-block:: nim type LightTower = array[1..10, LevelSetting] var @@ -1323,26 +1253,25 @@ subdivided into height levels accessed through their integer index: tower[1][east] = mediumBlink echo len(tower) # --> 10 echo len(tower[1]) # --> 4 - echo tower # --> [[slowBlink, mediumBlink, ...more output.. + echo repr(tower) # --> [[slowBlink, mediumBlink, ...more output.. # 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` +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: - ```nim +.. code-block:: 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: - ```nim test = "nim c $1" +.. code-block:: 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 @@ -1351,50 +1280,50 @@ to specify a range from zero to the specified index minus one: y: QuickArray x = [1, 2, 3, 4, 5, 6] y = x - for i in low(x) .. high(x): + for i in low(x)..high(x): echo x[i], y[i] - ``` Sequences --------- - 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]( -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 are always indexed with an ``int`` starting at position 0. The `len +`_, `low `_ and `high +`_ 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](system.html#newSeq) procedure. +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 may be passed to an openarray parameter. Example: - ```nim test = "nim c $1" +.. code-block:: 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 `@[]`. +Sequence variables are initialized with ``@[]``. -The `for` statement can be used with one or two variables when used with a +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()](iterators.html#items.i,seq[T]) iterator from the [system]( -system.html) module. But if you use the two-variable form, the first +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 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()](iterators.html#pairs.i,seq[T]) iterator from the [system]( -system.html) module. Examples: +value. Here the ``for`` statement is looping over the results from the +`pairs() `_ iterator from the `system +`_ module. Examples: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" for value in @[3, 4, 5]: echo value # --> 3 @@ -1406,23 +1335,22 @@ system.html) module. Examples: # --> index: 0, value:3 # --> index: 1, value:4 # --> index: 2, value:5 - ``` Open arrays ----------- - **Note**: Openarrays can only be used for parameters. 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](system.html#len,TOpenArray), [low](system.html#low,openArray[T]) -and [high](system.html#high,openArray[T]) operations are available for open +this. Openarrays 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 type does not matter. - ```nim test = "nim c $1" +.. code-block:: 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 @@ -1436,7 +1364,6 @@ 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. @@ -1445,12 +1372,13 @@ supported because this is seldom needed and cannot be done efficiently. Varargs ------- -A `varargs` parameter is like an openarray parameter. However, it is +A ``varargs`` parameter is like an openarray parameter. However, it is 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: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" proc myWriteln(f: File, a: varargs[string]) = for s in items(a): write(f, s) @@ -1459,13 +1387,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: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" proc myWriteln(f: File, a: varargs[string, `$`]) = for s in items(a): write(f, s) @@ -1474,10 +1402,9 @@ 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 [$](dollars.html) is applied to any argument that is passed -to the parameter `a`. Note that [$](dollars.html) applied to strings is a +In this example `$ `_ is applied to any argument that is passed +to the parameter ``a``. Note that `$ `_ applied to strings is a nop. @@ -1489,7 +1416,9 @@ 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. - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + var a = "Nim is a programming language" b = "Slices are useless." @@ -1497,43 +1426,42 @@ 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 the different ways of specifying the indices of +To understand some of 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 +So the string ``b`` is of length 19, and two different ways of specifying the indices are - ```nim +.. code-block:: 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 shorthand way of specifying the `b.len-1`. See -the [backwards index operator](system.html#^.t%2Cint). +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 `_. 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". -`b[11 .. ^2]` is the portion "useless", and `b[11 .. ^2] = "useful"` replaces the +``b[11 .. ^2]`` is the portion "useless", and ``b[11 .. ^2] = "useful"`` replaces the "useless" portion with "useful", giving the result "Slices are 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 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](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]`. +Note 2: As the ``^`` template returns a `distinct int `_ +of type ``BackwardsIndex``, we can have a ``lastIndex`` constant defined as ``const lastIndex = ^1``, +and later used as ``b[0 .. lastIndex]``. Objects ------- @@ -1543,11 +1471,11 @@ structure with a name is the object type. An object is a value type, which means that when an object is assigned to a new variable all its components are copied as well. -Each object type `Foo` has a constructor `Foo(field: value, ...)` +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. - ```nim +.. code-block:: nim type Person = object name: string @@ -1574,18 +1502,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 `*`. +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 ------ @@ -1596,18 +1524,19 @@ Unlike object types though, tuple types are structurally typed, meaning different tuple-types are *equivalent* if they specify fields of the same type and of the same name in the same order. -The constructor `()` can be used to construct tuples. The order of the +The constructor ``()`` can be used to construct tuples. The order of the fields in the constructor must match the order in the tuple's definition. But unlike objects, a name for the tuple type may not be used here. -Like the object type the notation `t.field` is used to access a +Like the object type the notation ``t.field`` is used to access a 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 +``t[i]`` to access the ``i``'th field. Here ``i`` must be a constant integer. - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" type # type representing a person: # A person consists of a name and an age. @@ -1656,60 +1585,43 @@ 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 having the same field types. -Tuples can be *unpacked* during variable assignment. This can +Tuples can be *unpacked* during variable assignment (and only then!). This can be handy to assign directly the fields of the tuples to individually named -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 +variables. An example of this is the `splitFile `_ +proc from the `os module `_ 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: - ```nim test = "nim c $1" - import std/os +.. code-block:: nim + :test: "nim c $1" + + import os let path = "usr/local/nimc.html" (dir, name, ext) = splitFile(path) baddir, badname, badext = splitFile(path) - echo dir # outputs "usr/local" - echo name # outputs "nimc" - echo ext # outputs ".html" + echo dir # outputs `usr/local` + echo name # outputs `nimc` + echo ext # outputs `.html` # All the following output the same line: - # "(dir: usr/local, name: nimc, ext: .html)" + # `(dir: usr/local, name: nimc, ext: .html)` echo baddir echo badname echo badext - ``` -Tuple unpacking is also supported in for-loops: - - ```nim test = "nim c $1" - let a = [(10, 'a'), (20, 'b'), (30, 'c')] - - for (x, c) in a: - echo x - # This will output: 10; 20; 30 - - # Accessing the index is also possible: - 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 explicitly +Fields of tuples are always public, they don't need to be explicity marked to be exported, unlike for example fields in an object type. - Reference and pointer types --------------------------- - References (similar to pointers in other programming languages) are a way to introduce many-to-one relationships. This means different references can point to and modify the same location in memory. @@ -1717,92 +1629,83 @@ 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. +(e.g., accessing the hardware), untraced references are necessary. Traced references are declared with the **ref** keyword; untraced references are declared with the **ptr** keyword. -The empty `[]` subscript notation can be used to *de-refer* a reference, -meaning to retrieve the item the reference points to. The `.` (access a -tuple/object field operator) and `[]` (array/string/sequence index operator) +The empty ``[]`` subscript notation can be used to *de-refer* a reference, +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: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" + type Node = ref object le, ri: Node data: int - - 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: - - ```nim - var n: Node + var + n: Node new(n) - ``` + n.data = 9 + # no need to write n[].data; in fact n[].data is highly discouraged! -To deal with untraced memory, the procedures `alloc`, `dealloc` and -`realloc` can be used. The [system](system.html) +To allocate a new traced object, the built-in procedure ``new`` must be used. +To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and +``realloc`` can be used. The `system `_ module's documentation contains further details. -If a reference points to *nothing*, it has the value `nil`. +If a reference points to *nothing*, it has the value ``nil``. Procedural type --------------- - A procedural type is a (somewhat abstract) pointer to a procedure. -`nil` is an allowed value for a variable of a procedural type. +``nil`` is an allowed value for a variable of a procedural type. Nim uses procedural types to achieve `functional`:idx: programming techniques. Example: - ```nim test = "nim c $1" - proc greet(name: string): string = - "Hello, " & name & "!" +.. code-block:: nim + :test: "nim c $1" + proc echoItem(x: int) = echo x - proc bye(name: string): string = - "Goodbye, " & name & "." + proc forEach(action: proc (x: int)) = + const + data = [2, 3, 5, 7, 11] + for d in items(data): + action(d) - proc communicate(greeting: proc (x: string): string, name: string) = - echo greeting(name) - - communicate(greet, "John") - communicate(bye, "Mary") - ``` + forEach(echoItem) 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](manual.html#types-procedural-type). +listed in the `manual `_. Distinct type ------------- - A Distinct type allows for the creation of a new type that "does not imply a 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](manual.html#types-distinct-type). +Examples are provided in the `manual `_. Modules ======= - -Nim supports splitting a program into pieces with a *module* concept. +Nim supports splitting a program into pieces with a module concept. Each module is in its own file. Modules enable `information hiding`:idx: and `separate compilation`:idx:. A module may gain access to the symbols of another module by using the `import`:idx: statement. Only top-level symbols that are marked -with an asterisk (`*`) are exported: +with an asterisk (``*``) are exported: - ```nim +.. code-block:: nim # Module A var x*, y: int @@ -1811,38 +1714,35 @@ with an asterisk (`*`) are exported: # allocate a new sequence: newSeq(result, len(a)) # multiply two int sequences: - for i in 0 ..< len(a): result[i] = a[i] * b[i] + for i in 0..len(a)-1: result[i] = a[i] * b[i] when isMainModule: - # test the new `*` operator for sequences: + # 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`. +The above module exports ``x`` and ``*``, but not ``y``. A module's top-level statements are executed at the start of the program. This can be used to initialize complex data structures for example. -Each module has a special magic constant `isMainModule` that is true if the +Each module has a special magic constant ``isMainModule`` that is true if the module is compiled as the main file. This is very useful to embed tests within the module as shown by the above example. -A symbol of a module *can* be *qualified* with the `module.symbol` syntax. And if +A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if 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: - ```nim +.. code-block:: nim # Module A var x*: string - ``` - ```nim +.. code-block:: nim # Module B var x*: int - ``` - ```nim +.. code-block:: nim # Module C import A, B write(stdout, x) # error: x is ambiguous @@ -1850,23 +1750,20 @@ 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: - ```nim +.. code-block:: nim # Module A proc x*(a: int): string = $a - ``` - ```nim +.. code-block:: nim # Module B proc x*(a: string): string = $a - ``` - ```nim +.. code-block:: nim # Module C import A, B write(stdout, x(3)) # no error: A.x is called @@ -1874,70 +1771,62 @@ rules apply: proc x*(a: int): string = discard write(stdout, x(3)) # ambiguous: which `x` is to call? - ``` Excluding symbols ----------------- -The normal `import` statement will bring in all exported symbols. +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. +the ``except`` qualifier. - ```nim +.. code-block:: nim import mymodule except y - ``` From statement -------------- -We have already seen the simple `import` statement that just imports all +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: +``from import`` statement: - ```nim +.. code-block:: nim from mymodule import x, y, z - ``` -The `from` statement can also force namespace qualification on +The ``from`` statement can also force namespace qualification on symbols, thereby making symbols available, but needing to be qualified in order to be used. - ```nim +.. code-block:: nim from mymodule import x, y, z x() # use x without any qualification - ``` - ```nim +.. code-block:: 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. - ```nim +.. code-block:: nim from mymodule as m import nil m.x() # m is aliasing mymodule - ``` Include statement ----------------- - -The `include` statement does something fundamentally different than -importing a module: it merely includes the contents of a file. The `include` +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: - ```nim +.. code-block:: nim include fileA, fileB, fileC - ``` @@ -1945,7 +1834,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](tut2.html) +from a nice syntax for procedural programming: `Part II `_ .. _strutils: strutils.html diff --git a/doc/tut2.md b/doc/tut2.rst similarity index 69% rename from doc/tut2.md rename to doc/tut2.rst index 4b9049082..e3b00555d 100644 --- a/doc/tut2.md +++ b/doc/tut2.rst @@ -5,19 +5,17 @@ Nim Tutorial (Part II) :Author: Andreas Rumpf :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: 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](manual.html) **contains many more examples of the advanced language +`manual `_ **contains many more examples of the advanced language features.** @@ -26,9 +24,9 @@ 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](manual.html#pragmas) or [user guide]( -nimc.html#additional-features) for a description of the available +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 pragmas. @@ -47,13 +45,14 @@ Inheritance Inheritance in Nim is entirely optional. To enable inheritance with runtime type information the object needs to inherit from -`RootObj`. This can be done directly, or indirectly by -inheriting from an object that inherits from `RootObj`. Usually -types with inheritance are also marked as `ref` types even though +``RootObj``. This can be done directly, or indirectly by +inheriting from an object that inherits from ``RootObj``. Usually +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. +type, the ``of`` operator can be used. - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" type Person = ref object of RootObj name*: string # the * means that `name` is accessible from other modules @@ -69,18 +68,17 @@ 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` +Inheritance is done with the ``object of`` syntax. Multiple inheritance is +currently not supported. If an object type has no suitable ancestor, ``RootObj`` can be used as its ancestor, but this is only a convention. Objects that have -no ancestor are implicitly `final`. You can use the `inheritable` pragma -to introduce new object roots apart from `system.RootObj`. (This is used +no ancestor are implicitly ``final``. You can use the ``inheritable`` pragma +to introduce new object roots apart from ``system.RootObj``. (This is used in the GTK wrapper for instance.) Ref objects should be used whenever inheritance is used. It isn't strictly -necessary, but with non-ref objects, assignments such as `let person: Person = -Student(id: 123)` will truncate subclass fields. +necessary, but with non-ref objects assignments such as ``let person: Person = +Student(id: 123)`` will truncate subclass fields. **Note**: Composition (*has-a* relation) is often preferable to inheritance (*is-a* relation) for simple code reuse. Since objects are value types in @@ -97,7 +95,8 @@ would require arbitrary symbol lookahead which slows down compilation.) Example: - ```nim test = "nim c $1" +.. code-block:: 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 @@ -107,13 +106,12 @@ 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 ---------------- Nim distinguishes between `type casts`:idx: and `type conversions`:idx:. -Casts are done with the `cast` operator and force the compiler to +Casts are done with the ``cast`` operator and force the compiler to interpret a bit pattern to be of another type. Type conversions are a much more polite way to convert a type into another: @@ -121,16 +119,15 @@ They preserve the abstract *value*, not necessarily the *bit-pattern*. If a type conversion is not possible, the compiler complains or an exception is raised. -The syntax for type conversions is `destination_type(expression_to_convert)` +The syntax for type conversions is ``destination_type(expression_to_convert)`` (like an ordinary call): - ```nim +.. code-block:: nim proc getID(x: Person): int = Student(x).id - ``` -The `InvalidObjectConversionDefect` exception is raised if `x` is not a -`Student`. +The ``InvalidObjectConversionDefect`` exception is raised if ``x`` is not a +``Student``. Object variants @@ -140,7 +137,9 @@ variant types are needed. An example: - ```nim test = "nim c $1" +.. code-block:: 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 @@ -151,7 +150,7 @@ An example: nkSub, # a subtraction nkIf # an if statement Node = ref object - case kind: NodeKind # the `kind` field is the discriminator + case kind: NodeKind # the ``kind`` field is the discriminator of nkInt: intVal: int of nkFloat: floatVal: float of nkString: strVal: string @@ -164,7 +163,6 @@ 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 @@ -175,35 +173,34 @@ Method call syntax ------------------ There is a syntactic sugar for calling routines: -The syntax `obj.methodName(args)` can be used -instead of `methodName(obj, args)`. +The syntax ``obj.method(args)`` can be used instead of ``method(obj, args)``. If there are no remaining arguments, the parentheses can be omitted: -`obj.len` (instead of `len(obj)`). +``obj.len`` (instead of ``len(obj)``). This method call syntax is not restricted to objects, it can be used for any type: - ```nim test = "nim c $1" - import std/strutils +.. code-block:: nim + :test: "nim c $1" + import 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: - ```nim test = "nim c $1" - import std/[strutils, sequtils] +.. code-block:: nim + :test: "nim c $1" + import 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,7 +210,9 @@ 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: - ```nim test = "nim c $1" +.. code-block:: 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 @@ -229,15 +228,15 @@ is needed: var s: Socket new s s.host = 34 # same as `host=`(s, 34) - ``` -(The example also shows `inline` procedures.) +(The example also shows ``inline`` procedures.) -The `[]` array access operator can be overloaded to provide +The ``[]`` array access operator can be overloaded to provide `array properties`:idx:\ : - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" type Vector* = object x, y, z: float @@ -257,19 +256,19 @@ 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. +already provides ``v[]`` access. Dynamic dispatch ---------------- Procedures always use static dispatch. For dynamic dispatch replace the -`proc` keyword by `method`: +``proc`` keyword by ``method``: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" type Expression = ref object of RootObj ## abstract base class for an expression Literal = ref object of Expression @@ -289,10 +288,9 @@ 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 +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 method because it requires dynamic binding. **Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass @@ -301,7 +299,9 @@ method because it requires dynamic binding. In a multi-method all parameters that have an object type are used for the dispatching: - ```nim test = "nim c --multiMethods:on $1" +.. code-block:: nim + :test: "nim c --multiMethods:on $1" + type Thing = ref object of RootObj Unit = ref object of Thing @@ -320,12 +320,11 @@ dispatching: new a new b collide(a, b) # output: 2 - ``` As the example demonstrates, invocation of a multi-method cannot be ambiguous: Collide 2 is preferred over collide 1 because the resolution works from left to -right. Thus `Unit, Thing` is preferred over `Thing, Unit`. +right. Thus ``Unit, Thing`` is preferred over ``Thing, Unit``. **Performance note**: Nim does not produce a virtual method table, but generates dispatch trees. This avoids the expensive indirect branch for method @@ -337,46 +336,46 @@ Exceptions ========== In Nim exceptions are objects. By convention, exception types are -suffixed with 'Error'. The [system](system.html) module defines an +suffixed with 'Error'. The `system `_ module defines an exception hierarchy that you might want to stick to. Exceptions derive from -`system.Exception`, which provides the common interface. +``system.Exception``, which provides the common interface. Exceptions have to be allocated on the heap because their lifetime is unknown. The compiler will prevent you from raising an exception created on the stack. All raised exceptions should at least specify the reason for being raised in -the `msg` field. +the ``msg`` field. A convention is that exceptions should be raised in *exceptional* cases, they should not be used as an alternative method of control flow. Raise statement --------------- -Raising an exception is done with the `raise` statement: +Raising an exception is done with the ``raise`` statement: - ```nim test = "nim c $1" +.. code-block:: 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 +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: +the template ``newException`` in the ``system`` module can be used: - ```nim +.. code-block:: nim raise newException(OSError, "the request to the OS failed") - ``` Try statement ------------- -The `try` statement handles exceptions: +The ``try`` statement handles exceptions: - ```nim test = "nim c $1" - from std/strutils import parseInt +.. code-block:: nim + :test: "nim c $1" + from strutils import parseInt # read the first two lines of a text file that should contain numbers # and tries to add them @@ -393,63 +392,60 @@ The `try` statement handles exceptions: echo "could not convert string to integer" except IOError: echo "IO error!" - except CatchableError: + except: echo "Unknown exception!" # reraise the unknown exception: raise finally: close(f) - ``` -The statements after the `try` are executed unless an exception is -raised. Then the appropriate `except` part is executed. +The statements after the ``try`` are executed unless an exception is +raised. Then the appropriate ``except`` part is executed. -The empty `except` part is executed if there is an exception that is -not explicitly listed. It is similar to an `else` part in `if` +The empty ``except`` part is executed if there is an exception that is +not explicitly listed. It is similar to an ``else`` part in ``if`` statements. -If there is a `finally` part, it is always executed after the +If there is a ``finally`` part, it is always executed after the exception handlers. -The exception is *consumed* in an `except` part. If an exception is not +The exception is *consumed* in an ``except`` part. If an exception is not handled, it is propagated through the call stack. This means that often -the rest of the procedure - that is not within a `finally` clause - +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()]( -system.html#getCurrentException) and [getCurrentExceptionMsg()]( -system.html#getCurrentExceptionMsg) procs from the [system](system.html) +``except`` branch you can use the `getCurrentException() +`_ and `getCurrentExceptionMsg() +`_ procs from the `system `_ module. Example: - ```nim +.. code-block:: nim try: doSomethingHere() - except CatchableError: + except: let e = getCurrentException() msg = getCurrentExceptionMsg() echo "Got exception ", repr(e), " with message ", msg - ``` Annotating procs with raised exceptions --------------------------------------- -Through the use of the optional `{.raises.}` pragma you can specify that a +Through the use of the optional ``{.raises.}`` pragma you can specify that a proc is meant to raise a specific set of exceptions, or none at all. If the -`{.raises.}` pragma is used, the compiler will verify that this is true. For -instance, if you specify that a proc raises `IOError`, and at some point it +``{.raises.}`` pragma is used, the compiler will verify that this is true. For +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: - ```nim +.. code-block:: 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 @@ -457,14 +453,14 @@ stopped validating the pragma and the raised exception not being caught, along with the file and line where the uncaught exception is being raised, which may help you locate the offending code which has changed. -If you want to add the `{.raises.}` pragma to existing code, the compiler can -also help you. You can add the `{.effects.}` pragma statement to your proc and +If you want to add the ``{.raises.}`` pragma to existing code, the compiler can +also help you. You can add the ``{.effects.}`` pragma statement to your proc and the compiler will output all inferred effects up to that point (exception 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](manual.html#effect-system). +`effect system and related pragmas in the manual `_. Generics @@ -472,13 +468,14 @@ Generics Generics are Nim's means to parametrize procs, iterators or types with `type parameters`:idx:. Generic parameters are written within square -brackets, for example `Foo[T]`. They are most useful for efficient type safe +brackets, for example ``Foo[T]``. They are most useful for efficient type safe containers: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" type BinaryTree*[T] = ref object # BinaryTree is a generic type with - # generic param `T` + # generic param ``T`` le, ri: BinaryTree[T] # left and right subtrees; may be nil data: T # the data stored in a node @@ -494,8 +491,8 @@ containers: else: var it = root while it != nil: - # compare the data items; uses the generic `cmp` proc - # that works for any type that has a `==` and `<` operator + # compare the data items; uses the generic ``cmp`` proc + # that works for any type that has a ``==`` and ``<`` operator var c = cmp(it.data, n.data) if c < 0: if it.le == nil: @@ -525,22 +522,22 @@ containers: n = n.le # and follow the left pointer var - root: BinaryTree[string] # instantiate a BinaryTree with `string` - add(root, newNode("hello")) # instantiates `newNode` and `add` - add(root, "world") # instantiates the second `add` proc + root: BinaryTree[string] # instantiate a BinaryTree with ``string`` + add(root, newNode("hello")) # instantiates ``newNode`` and ``add`` + 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, iterator or type. As the example shows, generics work with overloading: the -best match of `add` is used. The built-in `add` procedure for sequences -is not hidden and is used in the `preorder` iterator. +best match of ``add`` is used. The built-in ``add`` procedure for sequences +is not hidden and is used in the ``preorder`` iterator. -There is a special `[:T]` syntax when using generics with the method call syntax: +There is a special ``[:T]`` syntax when using generics with the method call syntax: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" proc foo[T](i: T) = discard @@ -549,7 +546,6 @@ 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 @@ -564,27 +560,27 @@ To *invoke* a template, call it like a procedure. Example: - ```nim +.. code-block:: 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, -the `!=` operator is available automatically and does the right thing. (Except +The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact +templates: this has the benefit that if you overload the ``==`` operator, +the ``!=`` operator is available automatically and does the right thing. (Except for IEEE floating point numbers - NaN breaks basic boolean logic.) -`a > b` is transformed into `b < a`. -`a in b` is transformed into `contains(b, a)`. -`notin` and `isnot` have the obvious meanings. +``a > b`` is transformed into ``b < a``. +``a in b`` is transformed into ``contains(b, a)``. +``notin`` and ``isnot`` have the obvious meanings. Templates are especially useful for lazy evaluation purposes. Consider a simple proc for logging: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" const debug = true @@ -594,15 +590,15 @@ 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 +This code has a shortcoming: if ``debug`` is set to false someday, the quite +expensive ``$`` and ``&`` operations are still performed! (The argument evaluation for procedures is *eager*). -Turning the `log` proc into a template solves this problem: +Turning the ``log`` proc into a template solves this problem: - ```nim test = "nim c $1" +.. code-block:: nim + :test: "nim c $1" const debug = true @@ -612,19 +608,20 @@ 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 -as an argument, and `untyped` means symbol lookups and type resolution is not +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 +as an argument, and ``untyped`` means symbol lookups and type resolution is not performed before the expression is passed to the template. If the template has no explicit return type, -`void` is used for consistency with procs and methods. +``void`` is used for consistency with procs and methods. -To pass a block of statements to a template, use `untyped` for the last parameter: +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,30 +637,29 @@ 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 +In the example the two ``writeLine`` statements are bound to the ``body`` +parameter. The ``withFile`` template contains boilerplate code and helps to avoid a common bug: to forget to close the file. Note how the -`let fn = filename` statement ensures that `filename` is evaluated only +``let fn = filename`` statement ensures that ``filename`` is evaluated only once. Example: Lifting Procs ---------------------- - `````nim test = "nim c $1" - import std/math +.. code-block:: nim + :test: "nim c $1" + import math template liftScalarProc(fname) = ## Lift a proc taking one scalar parameter and returning a - ## scalar value (eg `proc sssss[T](x: T): float`), + ## scalar value (eg ``proc sssss[T](x: T): float``), ## to provide templated procs that can handle a single ## parameter of seq[T] or nested seq[seq[]] or the same type ## - ## ```Nim - ## liftScalarProc(abs) - ## # now abs(@[@[1,-2], @[-2,-3]]) == @[@[1,2], @[2,3]] - ## ``` + ## .. code-block:: 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)) @@ -673,25 +669,24 @@ 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 ========================= Nim code can be compiled to JavaScript. However in order to write JavaScript-compatible code you should remember the following: -- `addr` and `ptr` have slightly different semantic meaning in JavaScript. +- ``addr`` and ``ptr`` have slightly different semantic meaning in JavaScript. It is recommended to avoid those if you're not sure how they are translated to JavaScript. -- `cast[T](x)` in JavaScript is translated to `(x)`, except for casting +- ``cast[T](x)`` in JavaScript is translated to ``(x)``, except for casting between signed/unsigned ints, in which case it behaves as static cast in C language. -- `cstring` in JavaScript means JavaScript string. It is a good practice to - use `cstring` only when it is semantically appropriate. E.g. don't use - `cstring` as a binary data buffer. +- ``cstring`` in JavaScript means JavaScript string. It is a good practice to + use ``cstring`` only when it is semantically appropriate. E.g. don't use + ``cstring`` as a binary data buffer. Part 3 ====== -The next part is entirely about metaprogramming via macros: [Part III](tut3.html). +The next part is entirely about metaprogramming via macros: `Part III `_ diff --git a/doc/tut3.md b/doc/tut3.rst similarity index 69% rename from doc/tut3.md rename to doc/tut3.rst index ccb2eb809..a159f0d82 100644 --- a/doc/tut3.md +++ b/doc/tut3.rst @@ -5,15 +5,13 @@ Nim Tutorial (Part III) :Author: Arne Döring :Version: |nimversion| -.. default-role:: code -.. include:: rstcommon.rst .. contents:: 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 @@ -22,15 +20,15 @@ a Nim syntax tree into a different tree. Examples of things that can be implemented in macros: * An assert macro that prints both sides of a comparison operator, if - the assertion fails. `myAssert(a == b)` is converted to - `if a != b: quit($a " != " $b)` + the assertion fails. ``myAssert(a == b)`` is converted to + ``if a != b: quit($a " != " $b)`` * A debug macro that prints the value and the name of the symbol. - `myDebugEcho(a)` is converted to `echo "a: ", a` + ``myDebugEcho(a)`` is converted to ``echo "a: ", a`` * Symbolic differentiation of an expression. - `diff(a*pow(x,3) + b*pow(x,2) + c*x + d, x)` is converted to - `3*a*pow(x,2) + 2*b*x + c` + ``diff(a*pow(x,3) + b*pow(x,2) + c*x + d, x)`` is converted to + ``3*a*pow(x,2) + 2*b*x + c`` Macro Arguments @@ -38,14 +36,14 @@ Macro Arguments The types of macro arguments have two faces. One face is used for the overload resolution and the other face is used within the macro -body. For example, if `macro foo(arg: int)` is called in an -expression `foo(x)`, `x` has to be of a type compatible to int, but -*within* the macro's body `arg` has the type `NimNode`, not `int`! +body. For example, if ``macro foo(arg: int)`` is called in an +expression ``foo(x)``, ``x`` has to be of a type compatible to int, but +*within* the macro's body ``arg`` has the type ``NimNode``, not ``int``! Why it is done this way will become obvious later, when we have seen concrete examples. There are two ways to pass arguments to a macro, an argument can be -either `typed` or `untyped`. +either ``typed`` or ``untyped``. Untyped Arguments @@ -60,11 +58,11 @@ result somehow. The result of a macro expansion is always checked by the compiler, so apart from weird error messages, nothing bad can happen. -The downside for an `untyped` argument is that these do not play +The downside for an ``untyped`` argument is that these do not play well with Nim's overloading resolution. The upside for untyped arguments is that the syntax tree is -quite predictable and less complex compared to its `typed` +quite predictable and less complex compared to its ``typed`` counterpart. @@ -76,8 +74,8 @@ does transformations on it, before it is passed to the macro. Here identifier nodes are resolved as symbols, implicit type conversions are visible in the tree as calls, templates are expanded, and probably most importantly, nodes have type information. -Typed arguments can have the type `typed` in the arguments list. -But all other types, such as `int`, `float` or `MyObjectType` +Typed arguments can have the type ``typed`` in the arguments list. +But all other types, such as ``int``, ``float`` or ``MyObjectType`` are typed arguments as well, and they are passed to the macro as a syntax tree. @@ -86,19 +84,19 @@ Static Arguments ---------------- Static arguments are a way to pass values as values and not as syntax -tree nodes to a macro. For example for `macro foo(arg: static[int])` -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`. +tree nodes to a macro. For example for ``macro foo(arg: static[int])`` +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``. - ```nim - import std/macros +.. code-block:: nim + + import macros macro myMacro(arg: static[int]): untyped = - echo arg # just an int (7), not `NimNode` + echo arg # just an int (7), not ``NimNode`` myMacro(1 + 2 * 3) - ``` Code Blocks as Arguments @@ -106,14 +104,14 @@ Code Blocks as Arguments 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`: +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,16 +123,17 @@ 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](macros.html) module. +Nim syntax tree are documented in the `macros `_ module. But a more interactive way to explore the Nim -syntax tree is with `macros.treeRepr`, it converts a syntax tree +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 to explore how the argument expressions are represented in tree form -and for debug printing of generated syntax tree. `dumpTree` is a +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: - ```nim +.. code-block:: nim + dumpTree: var mt: MyType = MyType(a:123.456, b:"abcdef") @@ -152,80 +151,49 @@ but does nothing else. Here is an example of such a tree representation: # ExprColonExpr # Ident "b" # StrLit "abcdef" - ``` Custom Semantic Checking ------------------------- +----------------------- The first thing that a macro should do with its arguments is to check if the argument is in the correct form. Not every type of wrong input needs to be caught here, but anything that could cause a crash during macro evaluation should be caught and create a nice error message. -`macros.expectKind` and `macros.expectLen` are a good start. If +``macros.expectKind`` and ``macros.expectLen`` are a good start. If the checks need to be more complex, arbitrary error messages can -be created with the `macros.error` proc. +be created with the ``macros.error`` proc. + +.. code-block:: nim - ```nim macro myAssert(arg: untyped): untyped = arg.expectKind nnkInfix - ``` Generating Code --------------- There are two ways to generate the code. Either by creating the syntax -tree with expressions that contain a lot of calls to `newTree` and -`newLit`, or with `quote do:` expressions. The first option offers +tree with expressions that contain a lot of calls to ``newTree`` and +``newLit``, or with ``quote do:`` expressions. The first option offers the best low-level control for the syntax tree generation, but the second option is much less verbose. If you choose to create the syntax -tree with calls to `newTree` and `newLit` the macro -`macros.dumpAstGen` can help you with the verbosity. - -`quote do:` allows you to write the code that you want to generate literally. -Backticks are used to insert code from `NimNode` symbols into the -generated expression. - - ```nim test = "nim c $1" - import std/macros - macro a(i) = quote do: - let `i` = 0 - - a b - doAssert b == 0 - ``` - -A custom prefix operator can be defined whenever backticks are needed. - - ```nim test = "nim c $1" - import std/macros - macro a(i) = quote("@") do: - assert @i == 0 - - let b = 0 - a b - ``` - -The injected symbol needs accent quoted when it resolves to a symbol. - - ```nim test = "nim c $1" - import std/macros - macro a(i) = quote("@") do: - let `@i` = 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 -expressions trees of type `NimNode` so that it is safe to inject +tree with calls to ``newTree`` and ``newLit`` the macro +``macros.dumpAstGen`` can help you with the verbosity. ``quote do:`` +allows you to write the code that you want to generate literally, +backticks are used to insert code from ``NimNode`` symbols into the +generated expression. This means that you can't use backticks within +``quote do:`` for anything else than injecting symbols. Make sure to +inject only symbols of type ``NimNode`` into the generated syntax +tree. You can use ``newLit`` to convert arbitrary values into +expressions trees of type ``NimNode`` so that it is safe to inject them into the tree. - ```nim test = "nim c $1" - import std/macros +.. code-block:: nim + :test: "nim c $1" + + import macros type MyType = object @@ -244,27 +212,27 @@ them into the tree. echo `mtLit` myMacro("Hallo") - ``` -The call to `myMacro` will generate the following code: +The call to ``myMacro`` will generate the following code: - ```nim +.. code-block:: nim echo "Hallo" echo MyType(a: 123.456'f64, b: "abcdef") - ``` Building Your First Macro ------------------------- To give a starting point to writing macros we will show now how to -implement the `myAssert` macro mentioned earlier. The first thing to +implement the ``myDebug`` macro mentioned earlier. The first thing to 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. - ```nim test = "nim c $1" - import std/macros +.. code-block:: nim + :test: "nim c $1" + + import macros macro myAssert(arg: untyped): untyped = echo arg.treeRepr @@ -273,14 +241,13 @@ 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 @@ -288,8 +255,10 @@ 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. - ```nim test = "nim c $1" - import std/macros +.. code-block:: nim + :test: "nim c $1" + + import macros macro myAssert(arg: untyped): untyped = # all node kind identifiers are prefixed with "nnk" @@ -309,18 +278,16 @@ written. myAssert(a != b) myAssert(a == b) - ``` This is the code that will be generated. To debug what the macro -actually generated, the statement `echo result.repr` can be used, in +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. - ```nim +.. code-block:: nim if not (a != b): raise newException(AssertionDefect, $a & " != " & $b) - ``` With Power Comes Responsibility ------------------------------- @@ -357,16 +324,16 @@ possible with it. Strformat --------- -In the Nim standard library, the `strformat` library provides a +In the Nim standard library, the ``strformat`` library provides a macro that parses a string literal at compile time. Parsing a string in a macro like here is generally not recommended. The parsed AST cannot have type information, and parsing implemented on the VM is generally not very fast. Working on AST nodes is almost always the -recommended way. But still `strformat` is a good example for a +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. +``assert`` macro. -[Strformat](https://github.com/nim-lang/Nim/blob/5845716df8c96157a047c2bd6bcdd795a7a2b9b1/lib/pure/strformat.nim#L280) +`Strformat `_ Ast Pattern Matching -------------------- @@ -375,7 +342,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](https://github.com/nim-lang/ast-pattern-matching) +`Ast Pattern Matching `_ OpenGL Sandbox -------------- @@ -384,4 +351,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](https://github.com/krux02/opengl-sandbox) +`OpenGL Sandbox `_ diff --git a/drnim/drnim.nim b/drnim/drnim.nim index eb0d89aa2..f2a20fa62 100644 --- a/drnim/drnim.nim +++ b/drnim/drnim.nim @@ -68,7 +68,6 @@ type DrnimContext = ref object z3: Z3_context graph: ModuleGraph - idgen: IdGenerator facts: seq[(PNode, VersionScope)] varVersions: seq[int] # this maps variable IDs to their current version. varSyms: seq[PSym] # mirrors 'varVersions' @@ -81,7 +80,6 @@ type DrCon = object graph: ModuleGraph - idgen: IdGenerator mapping: Table[string, Z3_ast] canonParameterNames: bool assumeUniqueness: bool @@ -303,12 +301,12 @@ proc nodeToZ3(c: var DrCon; n: PNode; scope: VersionScope; vars: var seq[PNode]) proc nodeToDomain(c: var DrCon; n, q: PNode; opAnd: PSym): PNode = assert n.kind == nkInfix - let opLe = createMagic(c.graph, c.idgen, "<=", mLeI) + let opLe = createMagic(c.graph, "<=", mLeI) case $n[0] of "..": result = buildCall(opAnd, buildCall(opLe, n[1], q), buildCall(opLe, q, n[2])) of "..<": - let opLt = createMagic(c.graph, c.idgen, "<", mLtI) + let opLt = createMagic(c.graph, "<", mLtI) result = buildCall(opAnd, buildCall(opLe, n[1], q), buildCall(opLt, q, n[2])) else: notImplemented(n) @@ -317,7 +315,7 @@ template quantorToZ3(fn) {.dirty.} = template ctx: untyped = c.up.z3 var bound = newSeq[Z3_app](n.len-2) - let opAnd = createMagic(c.graph, c.idgen, "and", mAnd) + let opAnd = createMagic(c.graph, "and", mAnd) var known: PNode for i in 1..n.len-2: let it = n[i] @@ -335,7 +333,7 @@ template quantorToZ3(fn) {.dirty.} = var dummy: seq[PNode] assert known != nil - let x = nodeToZ3(c, buildCall(createMagic(c.graph, c.idgen, "->", mImplies), + let x = nodeToZ3(c, buildCall(createMagic(c.graph, "->", mImplies), known, n[^1]), scope, dummy) result = fn(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x) @@ -397,8 +395,8 @@ proc nodeToZ3(c: var DrCon; n: PNode; scope: VersionScope; vars: var seq[PNode]) else: notImplemented(n) of mHigh: - let addOpr = createMagic(c.graph, c.idgen, "+", mAddI) - let lenOpr = createMagic(c.graph, c.idgen, "len", mLengthOpenArray) + let addOpr = createMagic(c.graph, "+", mAddI) + let lenOpr = createMagic(c.graph, "len", mLengthOpenArray) let asLenExpr = addOpr.buildCall(lenOpr.buildCall(n[1]), nkIntLit.newIntNode(-1)) result = rec asLenExpr of mLow: @@ -579,8 +577,8 @@ proc addRangeInfo(c: var DrCon, n: PNode; scope: VersionScope, res: var seq[Z3_a res.add nodeToZ3(c, translateEnsures(ensures, n), scope, dummy) return - let x = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), lowBound, n) - let y = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), n, highBound) + let x = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), lowBound, n) + let y = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), n, highBound) var dummy: seq[PNode] res.add nodeToZ3(c, x, scope, dummy) @@ -679,7 +677,6 @@ proc proofEngine(ctx: DrnimContext; assumptions: seq[(PNode, VersionScope)]; toProve: (PNode, VersionScope)): (bool, string) = var c: DrCon c.graph = ctx.graph - c.idgen = ctx.idgen c.assumeUniqueness = assumeUniqueness c.up = ctx result = proofEngineAux(c, assumptions, toProve) @@ -741,11 +738,10 @@ proc compatibleProps(graph: ModuleGraph; formal, actual: PType): bool {.nimcall. var c: DrCon c.graph = graph - c.idgen = graph.idgen c.canonParameterNames = true try: c.up = DrnimContext(z3: setupZ3(), o: initOperators(graph), graph: graph, owner: nil, - opImplies: createMagic(graph, c.idgen, "->", mImplies)) + opImplies: createMagic(graph, "->", mImplies)) template zero: untyped = VersionScope(0) if not frequires.isEmpty: result = not arequires.isEmpty and proofEngineAux(c, @[(frequires, zero)], (arequires, zero))[0] @@ -851,7 +847,7 @@ proc checkLe(c: DrnimContext, a, b: PNode) = of tyChar, tyUInt..tyUInt64: cmpOp = mLeU else: discard - let cmp = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), a, b) + let cmp = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), a, b) cmp.info = a.info discard prove(c, cmp) @@ -1030,7 +1026,7 @@ proc traverseAsgn(c: DrnimContext; n: PNode) = proc replaceByOldParams(fact, le: PNode): PNode = if guards.sameTree(fact, le): result = newNodeIT(nkCall, fact.info, fact.typ) - result.add newSymNode createMagic(c.graph, c.idgen, "old", mOld) + result.add newSymNode createMagic(c.graph, "old", mOld) result.add fact else: result = shallowCopy(fact) @@ -1180,9 +1176,8 @@ proc strongSemCheck(graph: ModuleGraph; owner: PSym; n: PNode) = c.z3 = setupZ3() c.o = initOperators(graph) c.graph = graph - c.idgen = graph.idgen c.owner = owner - c.opImplies = createMagic(c.graph, c.idgen, "->", mImplies) + c.opImplies = createMagic(c.graph, "->", mImplies) try: traverse(c, n) ensuresCheck(c, owner) @@ -1205,7 +1200,23 @@ proc mainCommand(graph: ModuleGraph) = registerPass graph, semPass compileProject(graph) if conf.errorCounter == 0: - genSuccessX(graph.config) + let mem = + when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" + else: formatSize(getTotalMem()) & " totmem" + let loc = $conf.linesCompiled + let build = if isDefined(conf, "danger"): "Dangerous Release" + elif isDefined(conf, "release"): "Release" + else: "Debug" + let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) + let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName + rawMessage(conf, hintSuccessX, [ + "loc", loc, + "sec", sec, + "mem", mem, + "build", build, + "project", project, + "output", "" + ]) proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = var p = parseopt.initOptParser(cmd) @@ -1260,7 +1271,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = mainCommand(graph) if conf.hasHint(hintGCStats): echo(GC_getStatistics()) -when compileOption("gc", "refc"): +when compileOption("gc", "v2") or compileOption("gc", "refc"): # the new correct mark&sweep collector is too slow :-/ GC_disableMarkAndSweep() diff --git a/drnim/tests/config.nims b/drnim/tests/config.nims index 346b0b4e6..a6b0e7d88 100644 --- a/drnim/tests/config.nims +++ b/drnim/tests/config.nims @@ -3,7 +3,7 @@ switch("path", "$nim/testament/lib") # so we can `import stdtest/foo` in this di ## prevent common user config settings to interfere with testament expectations ## Indifidual tests can override this if needed to test for these options. switch("colors", "off") -switch("filenames", "canonical") +switch("listFullPaths", "off") switch("excessiveStackTrace", "off") # we only want to check the marked parts in the tests: diff --git a/koch.nim b/koch.nim index 52378e43f..5da262884 100644 --- a/koch.nim +++ b/koch.nim @@ -10,10 +10,9 @@ # const - NimbleStableCommit = "7efb226ef908297e8791cade20d991784b4e8bfc" # master - # examples of possible values: #head, #ea82b54, 1.2.3 - FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014" - HeadHash = "#head" + NimbleStableCommit = "8f7af860c5ce9634af880a7081c6435e1f2a5148" # master + FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" + when not defined(windows): const Z3StableCommit = "65de3f748a6812eecd7db7c478d5fc54424d368b" # the version of Z3 that DrNim uses @@ -34,9 +33,6 @@ import std/[os, strutils, parseopt, osproc] # If this fails with: `Error: cannot open file: std/os`, see # https://github.com/nim-lang/Nim/pull/14291 for explanation + how to fix. -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - import tools / kochdocs import tools / deps @@ -70,8 +66,7 @@ Possible Commands: e.g. nimble) doesn't require network connectivity nimble builds the Nimble tool - fusion installs fusion via Nimble - + fusion clone fusion into the working tree Boot options: -d:release produce a release version of the compiler -d:nimUseLinenoise use the linenoise library for interactive mode @@ -81,7 +76,7 @@ Boot options: for bootstrapping Commands for core developers: - runCI runs continuous integration (CI), e.g. from Github Actions + runCI runs continuous integration (CI), e.g. from travis docs [options] generates the full documentation csource -d:danger builds the C sources for installation pdf builds the PDF documentation @@ -92,6 +87,10 @@ Commands for core developers: tests [options] run the testsuite (run a subset of tests by specifying a category, e.g. `tests cat async`) temp options creates a temporary compiler for testing + pushcsource push generated C sources to its repo +Web options: + --googleAnalytics:UA-... add the given google analytics code to the docs. To + build the official docs, use UA-48159761-1 """ let kochExe* = when isMainModule: os.getAppFilename() # always correct when koch is main program, even if `koch` exe renamed e.g.: `nim c -o:koch_debug koch.nim` @@ -150,11 +149,11 @@ 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 --mm:refc --noNimblePath " & args) + options = "-d:release --noNimblePath " & args) proc bundleNimsuggest(args: string) = nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim", - options = "-d:danger " & args) + options = "-d:release -d:danger " & args) proc buildVccTool(args: string) = let input = "tools/vccexe/vccexe.nim" @@ -181,7 +180,14 @@ proc bundleWinTools(args: string) = nimCompile(r"tools\downloader.nim", options = r"--cc:vcc --app:gui -d:ssl --noNimblePath --path:..\ui " & args) +proc bundleFusion(latest: bool) = + let commit = if latest: "HEAD" else: FusionStableCommit + cloneDependency(distDir, "https://github.com/nim-lang/fusion.git", commit, + allowBundled = true) + copyDir(distDir / "fusion" / "src" / "fusion", "lib" / "fusion") + proc zip(latest: bool; args: string) = + bundleFusion(latest) bundleNimbleExe(latest, args) bundleNimsuggest(args) bundleNimpretty(args) @@ -215,22 +221,13 @@ proc buildTools(args: string = "") = options = "-d:release " & args) when defined(windows): buildVccTool(args) bundleNimpretty(args) - nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release " & args) - - # pre-packages a debug version of nim which can help in many cases investigate issuses - # withouth having to rebuild compiler. - # `-d:nimDebugUtils` only makes sense when temporarily editing/debugging compiler - # `-d:debug` should be changed to a flag that doesn't require re-compiling nim - # `--opt:speed` is a sensible default even for a debug build, it doesn't affect nim stacktraces - nimCompileFold("Compile nim_dbg", "compiler/nim.nim", options = - "--opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints " & args, - outputName = "nim_dbg") - - nimCompileFold("Compile atlas", "tools/atlas/atlas.nim", options = "-d:release " & args, - outputName = "atlas") - + nimCompileFold("Compile nimfind", "tools/nimfind.nim", + options = "-d:release " & args) + nimCompileFold("Compile testament", "testament/testament.nim", + options = "-d:release " & args) proc nsis(latest: bool; args: string) = + bundleFusion(latest) bundleNimbleExe(latest, args) bundleNimsuggest(args) bundleWinTools(args) @@ -251,6 +248,20 @@ 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 = @@ -261,7 +272,7 @@ proc findStartNim: string = # If these fail, we try to build nim with the "build.(sh|bat)" script. let (nim, ok) = findNimImpl() if ok: return nim - when defined(posix): + when defined(Posix): const buildScript = "build.sh" if fileExists(buildScript): if tryExec("./" & buildScript): return "bin" / nim @@ -279,13 +290,6 @@ proc thVersion(i: int): string = template doUseCpp(): bool = getEnv("NIM_COMPILE_TO_CPP", "false") == "true" proc boot(args: string) = - ## bootstrapping is a process that involves 3 steps: - ## 1. use csourcesAny to produce nim1.exe. This nim1.exe is buggy but - ## rock solid for building a Nim compiler. It shouldn't be used for anything else. - ## 2. use nim1.exe to produce nim2.exe. nim2.exe is the one you really need. - ## 3. We use nim2.exe to build nim3.exe. nim3.exe is equal to nim2.exe except for timestamps. - ## This step ensures a minimum amount of quality. We know that nim2.exe can be used - ## for Nim compiler development. var output = "compiler" / "nim".exe var finalDest = "bin" / "nim".exe # default to use the 'c' command: @@ -295,28 +299,37 @@ proc boot(args: string) = let nimStart = findStartNim().quoteShell() for i in 0..2: - let defaultCommand = if useCpp: "cpp" else: "c" + # Nim versions < (1, 1) expect Nim's exception type to have a 'raiseId' field for + # C++ interop. Later Nim versions do this differently and removed the 'raiseId' field. + # Thus we always bootstrap the first iteration with "c" and not with "cpp" as + # a workaround. + let defaultCommand = if useCpp and i > 0: "cpp" else: "c" let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: "" echo "iteration: ", i+1 var extraOption = "" var nimi = i.thVersion if i == 0: nimi = nimStart - extraOption.add " --skipUserCfg --skipParentCfg -d:nimKochBootstrap" + extraOption.add " --skipUserCfg --skipParentCfg" # The configs are skipped for bootstrap # (1st iteration) to prevent newer flags from breaking bootstrap phase. let ret = execCmdEx(nimStart & " --version") doAssert ret.exitCode == 0 let version = ret.output.splitLines[0] - if version.startsWith "Nim Compiler Version 0.20.0": + # remove these when csources get updated + template addLib() = extraOption.add " --lib:lib" # see https://github.com/nim-lang/Nim/pull/14291 + if version.startsWith "Nim Compiler Version 0.19.0": + extraOption.add " -d:nimBoostrapCsources0_19_0" + addLib() + elif version.startsWith "Nim Compiler Version 0.20.0": addLib() # 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:$# $# --noNimblePath --compileOnly compiler" / "nim.nim" % + exec "$# $# $# --nimcache:$# $# --compileOnly compiler" / "nim.nim" % [nimi, bootOptions, extraOption, smartNimcache, args] - exec "$# jsonscript --noNimblePath --nimcache:$# $# compiler" / "nim.nim" % + exec "$# jsonscript --nimcache:$# $# compiler" / "nim.nim" % [nimi, smartNimcache, args] if sameFileContent(output, i.thVersion): @@ -444,14 +457,14 @@ proc temp(args: string) = inc i let d = getAppDir() - let output = d / "compiler" / "nim".exe - let finalDest = d / "bin" / "nim_temp".exe + var output = d / "compiler" / "nim".exe + var finalDest = d / "bin" / "nim_temp".exe # 125 is the magic number to tell git bisect to skip the current commit. var (bootArgs, programArgs) = splitArgs(args) if "doc" notin programArgs and "threads" notin programArgs and "js" notin programArgs and "rst2html" notin programArgs: - bootArgs = " -d:leanCompiler" & bootArgs + bootArgs.add " -d:leanCompiler" let nimexec = findNim().quoteShell() exec(nimexec & " c -d:debug --debugger:native -d:nimBetterRun " & bootArgs & " " & (d / "compiler" / "nim"), 125) copyExe(output, finalDest) @@ -469,22 +482,6 @@ proc xtemp(cmd: string) = finally: copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe) -proc icTest(args: string) = - temp("") - let inp = os.parseCmdLine(args)[0] - let content = readFile(inp) - let nimExe = getAppDir() / "bin" / "nim_temp".exe - var i = 0 - for fragment in content.split("#!EDIT!#"): - let file = inp.replace(".nim", "_temp.nim") - writeFile(file, fragment) - var cmd = nimExe & " cpp --ic:on -d:nimIcIntegrityChecks --listcmd " - if i == 0: - cmd.add "-f " - cmd.add quoteShell(file) - exec(cmd) - inc i - proc buildDrNim(args: string) = if not dirExists("dist/nimz3"): exec("git clone https://github.com/zevv/nimz3.git dist/nimz3") @@ -528,66 +525,68 @@ proc runCI(cmd: string) = echo "runCI: ", cmd echo hostInfo() # boot without -d:nimHasLibFFI to make sure this still works - # `--lib:lib` is needed for bootstrap on openbsd, for reasons described in - # https://github.com/nim-lang/Nim/pull/14291 (`getAppFilename` bugsfor older nim on openbsd). - kochExecFold("Boot Nim ORC", "boot -d:release -d:nimStrictMode --lib:lib") - - when false: # debugging: when you need to run only 1 test in CI, use something like this: - execFold("debugging test", "nim r tests/stdlib/tosproc.nim") - doAssert false, "debugging only" + kochExecFold("Boot in release mode", "boot -d:release") ## build nimble early on to enable remainder to depend on it if needed kochExecFold("Build Nimble", "nimble") - execFold("Install smtp", "nimble install smtp -y") - - let batchParam = "--batch:$1" % "NIM_TESTAMENT_BATCH".getEnv("_") if getEnv("NIM_TEST_PACKAGES", "0") == "1": - nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release") - execFold("Test selected Nimble packages", "testament $# pcat nimble-packages" % batchParam) + execFold("Test selected Nimble packages (1)", "nim c -r testament/testament cat nimble-packages-1") + elif getEnv("NIM_TEST_PACKAGES", "0") == "2": + execFold("Test selected Nimble packages (2)", "nim c -r testament/testament cat nimble-packages-2") else: buildTools() - for a in "zip opengl sdl1 jester@#head".split: - let buildDeps = "build"/"deps" # xxx factor pending https://github.com/timotheecour/Nim/issues/616 - # if this gives `Additional info: "build/deps" [OSError]`, make sure nimble is >= v0.12.0, - # otherwise `absolutePath` is needed, refs https://github.com/nim-lang/nimble/issues/901 - execFold("", "nimble install -y --nimbleDir:$# $#" % [buildDeps.quoteShell, a]) - ## run tests execFold("Test nimscript", "nim e tests/test_nimscript.nims") when defined(windows): - execFold("Compile tester", "nim c --usenimcache -d:nimCoroutines --os:genode -d:posix --compileOnly testament/testament") + # note: will be over-written below + execFold("Compile tester", "nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/testament") # main bottleneck here - # xxx: even though this is the main bottleneck, we could speedup the rest via batching with `--batch`. - # BUG: with initOptParser, `--batch:'' all` interprets `all` as the argument of --batch, pending bug #14343 - execFold("Run tester", "nim c -r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:nimStrictMode testament/testament $# all -d:nimCoroutines" % batchParam) + # xxx: even though this is the main bottlneck, we could use same code to batch the other tests + #[ + BUG: with initOptParser, `--batch:'' all` interprets `all` as the argument of --batch + ]# + execFold("Run tester", "nim c -r -d:nimCoroutines testament/testament --pedantic --batch:$1 all -d:nimCoroutines" % ["NIM_TESTAMENT_BATCH".getEnv("_")]) - block: # nimHasLibFFI: + block CT_FFI: when defined(posix): # windows can be handled in future PR's execFold("nimble install -y libffi", "nimble install -y libffi") - const nimFFI = "bin/nim.ctffi" + const nimFFI = "./bin/nim.ctffi" # no need to bootstrap with koch boot (would be slower) let backend = if doUseCpp(): "cpp" else: "c" execFold("build with -d:nimHasLibFFI", "nim $1 -d:release -d:nimHasLibFFI -o:$2 compiler/nim.nim" % [backend, nimFFI]) execFold("test with -d:nimHasLibFFI", "$1 $2 -r testament/testament --nim:$1 r tests/misc/trunner.nim -d:nimTrunnerFfi" % [nimFFI, backend]) - execFold("Run nimdoc tests", "nim r nimdoc/tester") - execFold("Run rst2html tests", "nim r nimdoc/rsttester") - execFold("Run nimpretty tests", "nim r nimpretty/tester.nim") + execFold("Run nimdoc tests", "nim c -r nimdoc/tester") + execFold("Run rst2html tests", "nim c -r nimdoc/rsttester") + execFold("Run nimpretty tests", "nim c -r nimpretty/tester.nim") when defined(posix): - # refs #18385, build with -d:release instead of -d:danger for testing - # We could also skip building nimsuggest in buildTools, or build it with -d:release - # in bundleNimsuggest depending on some environment variable when we are in CI. One advantage - # of rebuilding is this won't affect bin/nimsuggest when running runCI locally - execFold("build nimsuggest_testing", "nim c -o:bin/nimsuggest_testing -d:release nimsuggest/nimsuggest") - execFold("Run nimsuggest tests", "nim r nimsuggest/tester") + execFold("Run nimsuggest tests", "nim c -r nimsuggest/tester") - execFold("Run atlas tests", "nim c -r -d:atlasTests tools/atlas/atlas.nim clone https://github.com/disruptek/balls") - - kochExecFold("Testing booting in refc", "boot -d:release --mm:refc -d:nimStrictMode --lib:lib") +proc pushCsources() = + if not dirExists("../csources/.git"): + quit "[Error] no csources git repository found" + csource("-d:danger") + let cwd = getCurrentDir() + try: + copyDir("build/c_code", "../csources/c_code") + copyFile("build/build.sh", "../csources/build.sh") + copyFile("build/build.bat", "../csources/build.bat") + copyFile("build/build64.bat", "../csources/build64.bat") + copyFile("build/makefile", "../csources/makefile") + setCurrentDir("../csources") + for kind, path in walkDir("c_code"): + if kind == pcDir: + exec("git add " & path / "*.c") + exec("git commit -am \"updated csources to version " & NimVersion & "\"") + exec("git push origin master") + exec("git tag -am \"Version $1\" v$1" % NimVersion) + exec("git push origin v$1" % NimVersion) + finally: + setCurrentDir(cwd) proc testUnixInstall(cmdLineRest: string) = csource("-d:danger" & cmdLineRest) @@ -613,7 +612,7 @@ proc testUnixInstall(cmdLineRest: string) = execCleanPath("./koch tools") # check the tests work: putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH") - execCleanPath("./koch tests --nim:bin/nim cat megatest", destDir / "bin") + execCleanPath("./koch tests --nim:./bin/nim cat megatest", destDir / "bin") else: echo "Version check: failure" finally: @@ -641,16 +640,9 @@ proc valgrind(cmd: string) = let supp = getAppDir() / "tools" / "nimgrind.supp" exec("valgrind --suppressions=" & supp & valcmd) -proc showHelp(success: bool) = +proc showHelp() = quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)), - CompileDate, CompileTime], if success: QuitSuccess else: QuitFailure) - -proc branchDone() = - let thisBranch = execProcess("git symbolic-ref --short HEAD").strip() - if thisBranch != "devel" and thisBranch != "": - exec("git checkout devel") - exec("git branch -D " & thisBranch) - exec("git pull --rebase") + CompileDate, CompileTime], QuitSuccess) when isMainModule: var op = initOptParser() @@ -663,7 +655,6 @@ when isMainModule: case op.kind of cmdLongOption, cmdShortOption: case normalize(op.key) - of "help", "h": showHelp(success = true) of "latest": latest = true of "stable": latest = false of "nim": nimExe = op.val.absolutePath # absolute so still works with changeDir @@ -671,12 +662,12 @@ when isMainModule: localDocsOnly = true if op.val.len > 0: localDocsOut = op.val.absolutePath - else: showHelp(success = false) + else: showHelp() of cmdArgument: case normalize(op.key) of "boot": boot(op.cmdLineRest) of "clean": clean(op.cmdLineRest) - of "doc", "docs": buildDocs(op.cmdLineRest & " --d:nimPreviewSlimSystem " & paCode, localDocsOnly, localDocsOut) + of "doc", "docs": buildDocs(op.cmdLineRest, localDocsOnly, localDocsOut) of "doc0", "docs0": # undocumented command for Araq-the-merciful: buildDocs(op.cmdLineRest & gaCode) @@ -703,17 +694,12 @@ when isMainModule: of "tools": buildTools(op.cmdLineRest) bundleNimbleExe(latest, op.cmdLineRest) - of "pushcsource": - quit "use this instead: https://github.com/nim-lang/csources_v1/blob/master/push_c_code.nim" + bundleFusion(latest) + of "pushcsource", "pushcsources": pushCsources() of "valgrind": valgrind(op.cmdLineRest) of "c2nim": bundleC2nim(op.cmdLineRest) of "drnim": buildDrNim(op.cmdLineRest) - of "fusion": - let suffix = if latest: HeadHash else: FusionStableHash - exec("nimble install -y fusion@$#" % suffix) - of "ic": icTest(op.cmdLineRest) - of "branchdone": branchDone() - else: showHelp(success = false) + of "fusion": bundleFusion(latest) + else: showHelp() break - of cmdEnd: - showHelp(success = false) + of cmdEnd: break diff --git a/lib/core/locks.nim b/lib/core/locks.nim index ad0bff44d..bddd6d864 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -18,7 +18,8 @@ when not compileOption("threads") and not defined(nimdoc): when false: # fix #12330 {.error: "Locks requires --threads:on option.".} -import std/private/syslocks +const insideRLocksModule = false +include "system/syslocks" type Lock* = SysLock ## Nim lock; whether this is re-entrant @@ -27,11 +28,6 @@ type {.push stackTrace: off.} - -proc `$`*(lock: Lock): string = - # workaround bug #14873 - result = "()" - proc initLock*(lock: var Lock) {.inline.} = ## Initializes the given lock. when not defined(js): @@ -41,16 +37,16 @@ proc deinitLock*(lock: var Lock) {.inline.} = ## Frees the resources associated with the lock. deinitSys(lock) -proc tryAcquire*(lock: var Lock): bool {.inline.} = +proc tryAcquire*(lock: var Lock): bool = ## Tries to acquire the given lock. Returns `true` on success. result = tryAcquireSys(lock) -proc acquire*(lock: var Lock) {.inline.} = +proc acquire*(lock: var Lock) = ## Acquires the given lock. when not defined(js): acquireSys(lock) -proc release*(lock: var Lock) {.inline.} = +proc release*(lock: var Lock) = ## Releases the given lock. when not defined(js): releaseSys(lock) @@ -65,21 +61,17 @@ proc deinitCond*(cond: var Cond) {.inline.} = deinitSysCond(cond) proc wait*(cond: var Cond, lock: var Lock) {.inline.} = - ## Waits on the condition variable `cond`. + ## waits on the condition variable `cond`. waitSysCond(cond, lock) proc signal*(cond: var Cond) {.inline.} = - ## Sends a signal to the condition variable `cond`. + ## sends a signal to the condition variable `cond`. signalSysCond(cond) -proc broadcast*(cond: var Cond) {.inline.} = - ## Unblocks all threads currently blocked on the - ## specified condition variable `cond`. - broadcastSysCond(cond) - template withLock*(a: Lock, body: untyped) = ## Acquires the given lock, executes the statements in body and ## releases the lock after the statements finish executing. + mixin acquire, release acquire(a) {.locks: [a].}: try: diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index ffd07abb1..8fe1fa603 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -7,225 +7,38 @@ # distribution, for details about the copyright. # -## This module provides an API for macros to collect compile-time information -## across module boundaries. It should be used instead of global `{.compileTime.}` -## variables as those break incremental compilation. -## -## The main feature of this module is that if you create `CacheTable`s or -## any other `Cache` types with the same name in different modules, their -## content will be shared, meaning that you can fill a `CacheTable` in -## one module, and iterate over its contents in another. - -runnableExamples: - import std/macros - - const mcTable = CacheTable"myTable" - const mcSeq = CacheSeq"mySeq" - const mcCounter = CacheCounter"myCounter" - - static: - # add new key "val" with the value `myval` - let myval = newLit("hello ic") - mcTable["val"] = myval - assert mcTable["val"].kind == nnkStrLit - - # Can access the same cache from different static contexts - # All the information is retained - static: - # get value from `mcTable` and add it to `mcSeq` - mcSeq.add(mcTable["val"]) - assert mcSeq.len == 1 - - static: - assert mcSeq[0].strVal == "hello ic" - - # increase `mcCounter` by 3 - mcCounter.inc(3) - assert mcCounter.value == 3 - +## This module provides an API for macros that need to collect compile +## time information across module boundaries in global variables. +## Starting with version 0.19 of Nim this is not directly supported anymore +## as it breaks incremental compilations. +## Instead the API here needs to be used. type CacheSeq* = distinct string - ## Compile-time sequence of `NimNode`s. CacheTable* = distinct string - ## Compile-time table of key-value pairs. - ## - ## Keys are `string`s and values are `NimNode`s. CacheCounter* = distinct string - ## Compile-time counter, uses `int` for storing the count. -proc value*(c: CacheCounter): int {.magic: "NccValue".} = - ## Returns the value of a counter `c`. - runnableExamples: - static: - let counter = CacheCounter"valTest" - # default value is 0 - assert counter.value == 0 +proc value*(c: CacheCounter): int {.magic: "NccValue".} +proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} - inc counter - assert counter.value == 1 - -proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} = - ## Increments the counter `c` with the value `by`. - runnableExamples: - static: - let counter = CacheCounter"incTest" - inc counter - inc counter, 5 - - assert counter.value == 6 - -proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} = - ## Adds `value` to `s`. - runnableExamples: - import std/macros - const mySeq = CacheSeq"addTest" - - static: - mySeq.add(newLit(5)) - mySeq.add(newLit("hello ic")) - - assert mySeq.len == 2 - assert mySeq[1].strVal == "hello ic" - -proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} = - ## Adds `value` to `s`. - ## - ## .. hint:: This doesn't do anything if `value` is already in `s`. - runnableExamples: - import std/macros - const mySeq = CacheSeq"inclTest" - - static: - mySeq.incl(newLit(5)) - mySeq.incl(newLit(5)) - - # still one element - assert mySeq.len == 1 - -proc len*(s: CacheSeq): int {.magic: "NcsLen".} = - ## Returns the length of `s`. - runnableExamples: - import std/macros - - const mySeq = CacheSeq"lenTest" - static: - let val = newLit("helper") - mySeq.add(val) - assert mySeq.len == 1 - - mySeq.add(val) - assert mySeq.len == 2 - -proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} = - ## Returns the `i`th value from `s`. - runnableExamples: - import std/macros - - const mySeq = CacheSeq"subTest" - static: - mySeq.add(newLit(42)) - assert mySeq[0].intVal == 42 +proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} +proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} +proc len*(s: CacheSeq): int {.magic: "NcsLen".} +proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} iterator items*(s: CacheSeq): NimNode = - ## Iterates over each item in `s`. - runnableExamples: - import std/macros - const myseq = CacheSeq"itemsTest" - - static: - myseq.add(newLit(5)) - myseq.add(newLit(42)) - - for val in myseq: - # check that all values in `myseq` are int literals - assert val.kind == nnkIntLit - for i in 0 ..< len(s): yield s[i] -proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} = - ## Inserts a `(key, value)` pair into `t`. - ## - ## .. warning:: `key` has to be unique! Assigning `value` to a `key` that is already - ## in the table will result in a compiler error. - runnableExamples: - import std/macros +proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} + ## 'key' has to be unique! - const mcTable = CacheTable"subTest" - static: - # assign newLit(5) to the key "value" - mcTable["value"] = newLit(5) - - # check that we can get the value back - assert mcTable["value"].kind == nnkIntLit - -proc len*(t: CacheTable): int {.magic: "NctLen".} = - ## Returns the number of elements in `t`. - runnableExamples: - import std/macros - - const dataTable = CacheTable"lenTest" - static: - dataTable["key"] = newLit(5) - assert dataTable.len == 1 - -proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} = - ## Retrieves the `NimNode` value at `t[key]`. - runnableExamples: - import std/macros - - const mcTable = CacheTable"subTest" - static: - mcTable["toAdd"] = newStmtList() - - # get the NimNode back - assert mcTable["toAdd"].kind == nnkStmtList - -proc hasKey*(t: CacheTable; key: string): bool = - ## Returns true if `key` is in the table `t`. - ## - ## See also: - ## * [contains proc][contains(CacheTable, string)] for use with the `in` operator - runnableExamples: - import std/macros - const mcTable = CacheTable"hasKeyEx" - static: - assert not mcTable.hasKey("foo") - mcTable["foo"] = newEmptyNode() - # Will now be true since we inserted a value - assert mcTable.hasKey("foo") - discard "Implemented in vmops" - -proc contains*(t: CacheTable; key: string): bool {.inline.} = - ## Alias of [hasKey][hasKey(CacheTable, string)] for use with the `in` operator. - runnableExamples: - import std/macros - const mcTable = CacheTable"containsEx" - static: - mcTable["foo"] = newEmptyNode() - # Will be true since we gave it a value before - assert "foo" in mcTable - t.hasKey(key) +proc len*(t: CacheTable): int {.magic: "NctLen".} +proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".} proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".} iterator pairs*(t: CacheTable): (string, NimNode) = - ## Iterates over all `(key, value)` pairs in `t`. - runnableExamples: - import std/macros - const mytabl = CacheTable"values" - - static: - mytabl["intVal"] = newLit(5) - mytabl["otherVal"] = newLit(6) - for key, val in mytabl: - # make sure that we actually get the same keys - assert key in ["intVal", "otherVal"] - - # all vals are int literals - assert val.kind == nnkIntLit - var h = 0 while hasNext(t, h): let (a, b, h2) = next(t, h) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 9cb694bd8..204123f41 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -10,10 +10,6 @@ include "system/inclrtl" import std/private/since -when defined(nimPreviewSlimSystem): - import std/[assertions, formatfloat] - - ## This module contains the interface to the compiler's abstract syntax ## tree (`AST`:idx:). Macros operate on this tree. ## @@ -23,8 +19,6 @@ when defined(nimPreviewSlimSystem): ## .. include:: ../../doc/astspec.txt -## .. importdoc:: system.nim - # If you look for the implementation of the magic symbol # ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`. @@ -77,22 +71,21 @@ type nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkRefTy, nnkPtrTy, nnkVarTy, - nnkConstTy, nnkOutTy, + nnkConstTy, nnkMutableTy, nnkDistinctTy, nnkProcTy, nnkIteratorTy, # iterator type - nnkSinkAsgn, + nnkSharedTy, # 'shared T' nnkEnumTy, nnkEnumFieldDef, - nnkArgList, nnkPattern + nnkArglist, nnkPattern nnkHiddenTryStmt, nnkClosure, nnkGotoState, nnkState, nnkBreakState, nnkFuncDef, - nnkTupleConstr, - nnkError, ## erroneous AST node + nnkTupleConstr NimNodeKinds* = set[NimNodeKind] NimTypeKind* = enum # some types are no longer used, see ast.nim @@ -127,15 +120,11 @@ type TNimSymKinds* {.deprecated.} = set[NimSymKind] -const - nnkMutableTy* {.deprecated.} = nnkOutTy - nnkSharedTy* {.deprecated.} = nnkSinkAsgn - type NimIdent* {.deprecated.} = object of RootObj ## Represents a Nim identifier in the AST. **Note**: This is only ## rarely useful, for identifier construction from a string - ## use `ident"abc"`. + ## use ``ident"abc"``. NimSymObj = object # hidden NimSym* {.deprecated.} = ref NimSymObj @@ -186,9 +175,9 @@ proc `[]`*(n: NimNode, i: BackwardsIndex): NimNode = n[n.len - i.int] template `^^`(n: NimNode, i: untyped): untyped = (when i is BackwardsIndex: n.len - int(i) else: int(i)) -proc `[]`*[T, U: Ordinal](n: NimNode, x: HSlice[T, U]): seq[NimNode] = +proc `[]`*[T, U](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) @@ -204,12 +193,13 @@ proc `[]=`*(n: NimNode, i: BackwardsIndex, child: NimNode) = n[n.len - i.int] = child 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 + ## 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: @@ -218,12 +208,12 @@ template `or`*(x, y: NimNode): NimNode = y proc add*(father, child: NimNode): NimNode {.magic: "NAdd", discardable, - noSideEffect.} + noSideEffect, locks: 0.} ## Adds the `child` to the `father` node. Returns the ## father node so that calls can be nested. proc add*(father: NimNode, children: varargs[NimNode]): NimNode {. - magic: "NAddMultiple", discardable, noSideEffect.} + magic: "NAddMultiple", discardable, noSideEffect, locks: 0.} ## Adds each child of `children` to the `father` node. ## Returns the `father` node so that calls can be nested. @@ -239,17 +229,7 @@ proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.} proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.} ## Returns a float from any floating point literal. - -proc symKind*(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect.} -proc getImpl*(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect.} - ## Returns a copy of the declaration of a symbol or `nil`. -proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.} - ## Returns the string value of an identifier, symbol, comment, or string literal. - ## - ## See also: - ## * `strVal= proc<#strVal=,NimNode,string>`_ for setting the string value. - -{.push warnings: off.} # silence `deprecated` +{.push warnings: off.} proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect, deprecated: "Deprecated since version 0.18.1; All functionality is defined on 'NimNode'.".} @@ -259,13 +239,41 @@ proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect, deprecated: proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect, deprecated: "use `getImpl: NimNode -> NimNode` instead".} -proc `$`*(i: NimIdent): string {.magic: "NStrVal", noSideEffect, deprecated: - "Deprecated since version 0.18.1; Use 'strVal' instead.".} - ## Converts a Nim identifier to a string. +when defined(nimSymKind): + proc symKind*(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect.} + proc getImpl*(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect.} + ## Returns a copy of the declaration of a symbol or `nil`. + proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.} + ## Returns the string value of an identifier, symbol, comment, or string literal. + ## + ## See also: + ## * `strVal= proc<#strVal=,NimNode,string>`_ for setting the string value. -proc `$`*(s: NimSym): string {.magic: "NStrVal", noSideEffect, deprecated: - "Deprecated since version 0.18.1; Use 'strVal' instead.".} - ## Converts a Nim symbol to a string. + proc `$`*(i: NimIdent): string {.magic: "NStrVal", noSideEffect, deprecated: + "Deprecated since version 0.18.1; Use 'strVal' instead.".} + ## Converts a Nim identifier to a string. + + proc `$`*(s: NimSym): string {.magic: "NStrVal", noSideEffect, deprecated: + "Deprecated since version 0.18.1; Use 'strVal' instead.".} + ## Converts a Nim symbol to a string. + +else: # bootstrapping substitute + proc getImpl*(symbol: NimNode): NimNode = + symbol.symbol.getImpl + + proc strValOld(n: NimNode): string {.magic: "NStrVal", noSideEffect.} + + proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.} + + proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.} + + proc strVal*(n: NimNode): string = + if n.kind == nnkIdent: + $n.ident + elif n.kind == nnkSym: + $n.symbol + else: + n.strValOld {.pop.} @@ -276,21 +284,23 @@ when (NimMajor, NimMinor, NimPatch) >= (1, 3, 5) or defined(nimSymImplTransform) ## note that code transformations are implementation dependent and subject to change. ## See an example in `tests/macros/tmacros_various.nim`. -proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.} - ## Accepts a node of kind `nnkSym` and returns its owner's symbol. - ## The meaning of 'owner' depends on `sym`'s `NimSymKind` and declaration - ## context. For top level declarations this is an `nskModule` symbol, - ## for proc local variables an `nskProc` symbol, for enum/object fields an - ## `nskType` symbol, etc. For symbols without an owner, `nil` is returned. - ## - ## See also: - ## * `symKind proc<#symKind,NimNode>`_ to get the kind of a symbol - ## * `getImpl proc<#getImpl,NimNode>`_ to get the declaration of a symbol +when defined(nimHasSymOwnerInMacro): + proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.} + ## Accepts a node of kind `nnkSym` and returns its owner's symbol. + ## The meaning of 'owner' depends on `sym`'s `NimSymKind` and declaration + ## context. For top level declarations this is an `nskModule` symbol, + ## for proc local variables an `nskProc` symbol, for enum/object fields an + ## `nskType` symbol, etc. For symbols without an owner, `nil` is returned. + ## + ## See also: + ## * `symKind proc<#symKind,NimNode>`_ to get the kind of a symbol + ## * `getImpl proc<#getImpl,NimNode>`_ to get the declaration of a symbol -proc isInstantiationOf*(instanceProcSym, genProcSym: NimNode): bool {.magic: "SymIsInstantiationOf", noSideEffect.} - ## Checks if a proc symbol is an instance of the generic proc symbol. - ## Useful to check proc symbols against generic symbols - ## returned by `bindSym`. +when defined(nimHasInstantiationOfInMacro): + proc isInstantiationOf*(instanceProcSym, genProcSym: NimNode): bool {.magic: "SymIsInstantiationOf", noSideEffect.} + ## Checks if a proc symbol is an instance of the generic proc symbol. + ## Useful to check proc symbols against generic symbols + ## returned by `bindSym`. proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} ## With 'getType' you can access the node's `type`:idx:. A Nim type is @@ -301,11 +311,11 @@ proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} ## kind of type it is, call `typeKind` on getType's result. proc getType*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.} - ## Version of `getType` which takes a `typedesc`. + ## Version of ``getType`` which takes a ``typedesc``. proc typeKind*(n: NimNode): NimTypeKind {.magic: "NGetType", noSideEffect.} ## Returns the type kind of the node 'n' that should represent a type, that - ## means the node should have been obtained via `getType`. + ## means the node should have been obtained via ``getType``. proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} = ## Returns the `type`:idx: of a node in a form matching the way the @@ -326,12 +336,12 @@ proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} = doAssert(dumpTypeInst(c) == "Vec[4, float32]") proc getTypeInst*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.} - ## Version of `getTypeInst` which takes a `typedesc`. + ## Version of ``getTypeInst`` which takes a ``typedesc``. proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} = ## Returns the `type`:idx: of a node in a form matching the implementation ## of the type. Any intermediate aliases are expanded to arrive at the final - ## type implementation. You can instead use `getImpl` on a symbol if you + ## type implementation. You can instead use ``getImpl`` on a symbol if you ## want to find the intermediate aliases. runnableExamples: type @@ -352,11 +362,12 @@ object doAssert(dumpTypeImpl(b) == t) doAssert(dumpTypeImpl(c) == t) -proc signatureHash*(n: NimNode): string {.magic: "NSigHash", noSideEffect.} - ## Returns a stable identifier derived from the signature of a symbol. - ## The signature combines many factors such as the type of the symbol, - ## the owning module of the symbol and others. The same identifier is - ## used in the back-end to produce the mangled symbol name. +when defined(nimHasSignatureHashInMacro): + proc signatureHash*(n: NimNode): string {.magic: "NSigHash", noSideEffect.} + ## Returns a stable identifier derived from the signature of a symbol. + ## The signature combines many factors such as the type of the symbol, + ## the owning module of the symbol and others. The same identifier is + ## used in the back-end to produce the mangled symbol name. proc symBodyHash*(s: NimNode): string {.noSideEffect.} = ## Returns a stable digest for symbols derived not only from type signature @@ -366,7 +377,7 @@ proc symBodyHash*(s: NimNode): string {.noSideEffect.} = discard proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.} - ## Version of `getTypeImpl` which takes a `typedesc`. + ## Version of ``getTypeImpl`` which takes a ``typedesc``. proc `intVal=`*(n: NimNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.} proc `floatVal=`*(n: NimNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.} @@ -396,38 +407,15 @@ proc newNimNode*(kind: NimNodeKind, {.magic: "NNewNimNode", noSideEffect.} ## Creates a new AST node of the specified kind. ## - ## The `lineInfoFrom` parameter is used for line information when the + ## The ``lineInfoFrom`` parameter is used for line information when the ## 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.} = - ## 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 copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.} +proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.} - ## Writes an error message at compile time. The optional `n: NimNode` + ## Writes an error message at compile time. The optional ``n: NimNode`` ## parameter is used as the source for file and line number information in ## the compilation error message. @@ -437,29 +425,29 @@ proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.} proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.} ## Writes a hint message at compile time. -proc newStrLitNode*(s: string): NimNode {.noSideEffect.} = +proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} = ## Creates a string literal node from `s`. result = newNimNode(nnkStrLit) result.strVal = s -proc newCommentStmtNode*(s: string): NimNode {.noSideEffect.} = +proc newCommentStmtNode*(s: string): NimNode {.compileTime, noSideEffect.} = ## Creates a comment statement node. result = newNimNode(nnkCommentStmt) result.strVal = s -proc newIntLitNode*(i: BiggestInt): NimNode = +proc newIntLitNode*(i: BiggestInt): NimNode {.compileTime.} = ## Creates an int literal node from `i`. result = newNimNode(nnkIntLit) result.intVal = i -proc newFloatLitNode*(f: BiggestFloat): NimNode = +proc newFloatLitNode*(f: BiggestFloat): NimNode {.compileTime.} = ## Creates a float literal node from `f`. result = newNimNode(nnkFloatLit) result.floatVal = f {.push warnings: off.} -proc newIdentNode*(i: NimIdent): NimNode {.deprecated: "use ident(string)".} = +proc newIdentNode*(i: NimIdent): NimNode {.compileTime, deprecated.} = ## Creates an identifier node from `i`. result = newNimNode(nnkIdent) result.ident = i @@ -468,17 +456,15 @@ proc newIdentNode*(i: NimIdent): NimNode {.deprecated: "use ident(string)".} = proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect.} ## Creates an identifier node from `i`. It is simply an alias for - ## `ident(string)`. Use that, it's shorter. + ## ``ident(string)``. Use that, it's shorter. proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.} ## Create a new ident node from a string. type - BindSymRule* = enum ## Specifies how `bindSym` behaves. The difference - ## between open and closed symbols can be found in - ## ``_ + BindSymRule* = enum ## specifies how ``bindSym`` behaves brClosed, ## only the symbols in current scope are bound - brOpen, ## open for overloaded symbols, but may be a single + brOpen, ## open wrt overloaded symbols, but may be a single ## symbol if not ambiguous (the rules match that of ## binding in generics) brForceOpen ## same as brOpen, but it will always be open even @@ -489,15 +475,20 @@ proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {. magic: "NBindSym", noSideEffect.} ## Creates a node that binds `ident` to a symbol node. The bound symbol ## may be an overloaded symbol. - ## if `ident` is a NimNode, it must have `nnkIdent` kind. - ## If `rule == brClosed` either an `nnkClosedSymChoice` tree is - ## returned or `nnkSym` if the symbol is not ambiguous. - ## If `rule == brOpen` either an `nnkOpenSymChoice` tree is - ## returned or `nnkSym` if the symbol is not ambiguous. - ## If `rule == brForceOpen` always an `nnkOpenSymChoice` tree is + ## if `ident` is a NimNode, it must have ``nnkIdent`` kind. + ## If ``rule == brClosed`` either an ``nnkClosedSymChoice`` tree is + ## returned or ``nnkSym`` if the symbol is not ambiguous. + ## If ``rule == brOpen`` either an ``nnkOpenSymChoice`` tree is + ## returned or ``nnkSym`` if the symbol is not ambiguous. + ## If ``rule == brForceOpen`` always an ``nnkOpenSymChoice`` tree is ## returned even if the symbol is not ambiguous. ## - ## See the `manual `_ for more details. + ## Experimental feature: + ## use {.experimental: "dynamicBindSym".} to activate it. + ## If called from template / regular code, `ident` and `rule` must be + ## constant expression / literal value. + ## If called from macros / compile time procs / static blocks, + ## `ident` and `rule` can be VM computed value. proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {. magic: "NGenSym", noSideEffect.} @@ -505,11 +496,10 @@ proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {. ## needs to occur in a declaration context. proc callsite*(): NimNode {.magic: "NCallSite", benign, deprecated: - "Deprecated since v0.18.1; use `varargs[untyped]` in the macro prototype instead".} + "Deprecated since v0.18.1; use varargs[untyped] in the macro prototype instead".} ## Returns the AST of the invocation expression that invoked this macro. - # see https://github.com/nim-lang/RFCs/issues/387 as candidate replacement. -proc toStrLit*(n: NimNode): NimNode = +proc toStrLit*(n: NimNode): NimNode {.compileTime.} = ## Converts the AST `n` to the concrete Nim code and wraps that ## in a string literal node. return newStrLitNode(repr(n)) @@ -533,66 +523,48 @@ proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.} - ## Copy lineinfo from `info`. + ## Copy lineinfo from ``info``. -proc setLine(arg: NimNode, line: uint16) {.magic: "NLineInfo", noSideEffect.} -proc setColumn(arg: NimNode, column: int16) {.magic: "NLineInfo", noSideEffect.} -proc setFile(arg: NimNode, file: string) {.magic: "NLineInfo", noSideEffect.} - -proc setLineInfo*(arg: NimNode, file: string, line: int, column: int) = - ## Sets the line info on the NimNode. The file needs to exists, but can be a - ## relative path. If you want to attach line info to a block using `quote` - ## you'll need to add the line information after the quote block. - arg.setFile(file) - arg.setLine(line.uint16) - arg.setColumn(column.int16) - -proc setLineInfo*(arg: NimNode, lineInfo: LineInfo) = - ## See `setLineInfo proc<#setLineInfo,NimNode,string,int,int>`_ - setLineInfo(arg, lineInfo.filename, lineInfo.line, lineInfo.column) - -proc lineInfoObj*(n: NimNode): LineInfo = - ## Returns `LineInfo` of `n`, using absolute path for `filename`. +proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = + ## Returns ``LineInfo`` of ``n``, using absolute path for ``filename``. result = LineInfo(filename: n.getFile, line: n.getLine, column: n.getColumn) -proc lineInfo*(arg: NimNode): string = +proc lineInfo*(arg: NimNode): string {.compileTime.} = ## Return line info in the form `filepath(line, column)`. $arg.lineInfoObj -proc internalParseExpr(s, filename: string): NimNode {. +proc internalParseExpr(s: string): NimNode {. magic: "ParseExprToAst", noSideEffect.} -proc internalParseStmt(s, filename: string): NimNode {. +proc internalParseStmt(s: string): NimNode {. magic: "ParseStmtToAst", noSideEffect.} proc internalErrorFlag*(): string {.magic: "NError", noSideEffect.} ## Some builtins set an error flag. This is then turned into a proper ## exception. **Note**: Ordinary application code should not call this. -proc parseExpr*(s: string; filename: string = ""): NimNode {.noSideEffect.} = +proc parseExpr*(s: string): NimNode {.noSideEffect, compileTime.} = ## Compiles the passed string to its AST representation. - ## Expects a single expression. Raises `ValueError` for parsing errors. - ## A filename can be given for more informative errors. - result = internalParseExpr(s, filename) + ## Expects a single expression. Raises ``ValueError`` for parsing errors. + result = internalParseExpr(s) let x = internalErrorFlag() if x.len > 0: raise newException(ValueError, x) -proc parseStmt*(s: string; filename: string = ""): NimNode {.noSideEffect.} = +proc parseStmt*(s: string): NimNode {.noSideEffect, compileTime.} = ## Compiles the passed string to its AST representation. - ## Expects one or more statements. Raises `ValueError` for parsing errors. - ## A filename can be given for more informative errors. - result = internalParseStmt(s, filename) + ## Expects one or more statements. Raises ``ValueError`` for parsing errors. + result = internalParseStmt(s) let x = internalErrorFlag() if x.len > 0: raise newException(ValueError, x) proc getAst*(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst", noSideEffect.} ## 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. @@ -604,11 +576,6 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} = ## operator may be obtained by escaping it (by prefixing it with itself) when used ## as a unary operator: ## e.g. `@` is escaped as `@@`, `&%` is escaped as `&%&%` and so on; see examples. - ## - ## A custom operator interpolation needs accent quoted (``) whenever it resolves - ## to a symbol. - ## - ## See also `genasts `_ which avoids some issues with `quote`. runnableExamples: macro check(ex: untyped) = # this is a simplified version of the check macro from the @@ -647,11 +614,10 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} = runnableExamples: # custom `op` var destroyCalled = false - macro bar(ident) = + macro bar() = var x = 1.5 result = quote("@") do: type Foo = object - let `@ident` = 0 # custom op interpolated symbols need quoted (``) proc `=destroy`(a: var Foo) = doAssert @x == 1.5 doAssert compiles(@x == 1.5) @@ -662,7 +628,7 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} = destroyCalled = true block: let a = Foo() - bar(someident) + bar() doAssert destroyCalled proc `&%`(x: int): int = 1 @@ -677,50 +643,51 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} = doAssert y == 3 bar2() -proc expectKind*(n: NimNode, k: NimNodeKind) = +proc expectKind*(n: NimNode, k: NimNodeKind) {.compileTime.} = ## Checks that `n` is of kind `k`. If this is not the case, ## compilation aborts with an error message. This is useful for writing ## macros that check the AST that is passed to them. if n.kind != k: error("Expected a node of kind " & $k & ", got " & $n.kind, n) -proc expectMinLen*(n: NimNode, min: int) = +proc expectMinLen*(n: NimNode, min: int) {.compileTime.} = ## Checks that `n` has at least `min` children. If this is not the case, ## compilation aborts with an error message. This is useful for writing ## macros that check its number of arguments. if n.len < min: error("Expected a node with at least " & $min & " children, got " & $n.len, n) -proc expectLen*(n: NimNode, len: int) = +proc expectLen*(n: NimNode, len: int) {.compileTime.} = ## Checks that `n` has exactly `len` children. If this is not the case, ## compilation aborts with an error message. This is useful for writing ## macros that check its number of arguments. if n.len != len: error("Expected a node with " & $len & " children, got " & $n.len, n) -proc expectLen*(n: NimNode, min, max: int) = - ## Checks that `n` has a number of children in the range `min..max`. +proc expectLen*(n: NimNode, min, max: int) {.compileTime.} = + ## Checks that `n` has a number of children in the range ``min..max``. ## If this is not the case, compilation aborts with an error message. ## This is useful for writing macros that check its number of arguments. if n.len < min or n.len > max: error("Expected a node with " & $min & ".." & $max & " children, got " & $n.len, n) proc newTree*(kind: NimNodeKind, - children: varargs[NimNode]): NimNode = + children: varargs[NimNode]): NimNode {.compileTime.} = ## Produces a new node with children. result = newNimNode(kind) result.add(children) -proc newCall*(theProc: NimNode, args: varargs[NimNode]): NimNode = +proc newCall*(theProc: NimNode, + args: varargs[NimNode]): NimNode {.compileTime.} = ## Produces a new call node. `theProc` is the proc that is called with - ## the arguments `args[0..]`. + ## the arguments ``args[0..]``. result = newNimNode(nnkCall) result.add(theProc) result.add(args) {.push warnings: off.} -proc newCall*(theProc: NimIdent, args: varargs[NimNode]): NimNode {.deprecated: +proc newCall*(theProc: NimIdent, args: varargs[NimNode]): NimNode {.compileTime, deprecated: "Deprecated since v0.18.1; use 'newCall(string, ...)' or 'newCall(NimNode, ...)' instead".} = ## Produces a new call node. `theProc` is the proc that is called with - ## the arguments `args[0..]`. + ## the arguments ``args[0..]``. result = newNimNode(nnkCall) result.add(newIdentNode(theProc)) result.add(args) @@ -728,128 +695,128 @@ proc newCall*(theProc: NimIdent, args: varargs[NimNode]): NimNode {.deprecated: {.pop.} proc newCall*(theProc: string, - args: varargs[NimNode]): NimNode = + args: varargs[NimNode]): NimNode {.compileTime.} = ## Produces a new call node. `theProc` is the proc that is called with - ## the arguments `args[0..]`. + ## the arguments ``args[0..]``. result = newNimNode(nnkCall) result.add(newIdentNode(theProc)) result.add(args) -proc newLit*(c: char): NimNode = +proc newLit*(c: char): NimNode {.compileTime.} = ## Produces a new character literal node. result = newNimNode(nnkCharLit) result.intVal = ord(c) -proc newLit*(i: int): NimNode = +proc newLit*(i: int): NimNode {.compileTime.} = ## Produces a new integer literal node. result = newNimNode(nnkIntLit) result.intVal = i -proc newLit*(i: int8): NimNode = +proc newLit*(i: int8): NimNode {.compileTime.} = ## Produces a new integer literal node. result = newNimNode(nnkInt8Lit) result.intVal = i -proc newLit*(i: int16): NimNode = +proc newLit*(i: int16): NimNode {.compileTime.} = ## Produces a new integer literal node. result = newNimNode(nnkInt16Lit) result.intVal = i -proc newLit*(i: int32): NimNode = +proc newLit*(i: int32): NimNode {.compileTime.} = ## Produces a new integer literal node. result = newNimNode(nnkInt32Lit) result.intVal = i -proc newLit*(i: int64): NimNode = +proc newLit*(i: int64): NimNode {.compileTime.} = ## Produces a new integer literal node. result = newNimNode(nnkInt64Lit) result.intVal = i -proc newLit*(i: uint): NimNode = +proc newLit*(i: uint): NimNode {.compileTime.} = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUIntLit) result.intVal = BiggestInt(i) -proc newLit*(i: uint8): NimNode = +proc newLit*(i: uint8): NimNode {.compileTime.} = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUInt8Lit) result.intVal = BiggestInt(i) -proc newLit*(i: uint16): NimNode = +proc newLit*(i: uint16): NimNode {.compileTime.} = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUInt16Lit) result.intVal = BiggestInt(i) -proc newLit*(i: uint32): NimNode = +proc newLit*(i: uint32): NimNode {.compileTime.} = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUInt32Lit) result.intVal = BiggestInt(i) -proc newLit*(i: uint64): NimNode = +proc newLit*(i: uint64): NimNode {.compileTime.} = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUInt64Lit) result.intVal = BiggestInt(i) -proc newLit*(b: bool): NimNode = +proc newLit*(b: bool): NimNode {.compileTime.} = ## Produces a new boolean literal node. result = if b: bindSym"true" else: bindSym"false" -proc newLit*(s: string): NimNode = +proc newLit*(s: string): NimNode {.compileTime.} = ## Produces a new string literal node. result = newNimNode(nnkStrLit) result.strVal = s when false: # the float type is not really a distinct type as described in https://github.com/nim-lang/Nim/issues/5875 - proc newLit*(f: float): NimNode = + proc newLit*(f: float): NimNode {.compileTime.} = ## Produces a new float literal node. result = newNimNode(nnkFloatLit) result.floatVal = f -proc newLit*(f: float32): NimNode = +proc newLit*(f: float32): NimNode {.compileTime.} = ## Produces a new float literal node. result = newNimNode(nnkFloat32Lit) result.floatVal = f -proc newLit*(f: float64): NimNode = +proc newLit*(f: float64): NimNode {.compileTime.} = ## Produces a new float literal node. result = newNimNode(nnkFloat64Lit) result.floatVal = f when declared(float128): - proc newLit*(f: float128): NimNode = + proc newLit*(f: float128): NimNode {.compileTime.} = ## Produces a new float literal node. result = newNimNode(nnkFloat128Lit) result.floatVal = f -proc newLit*(arg: enum): NimNode = +proc newLit*(arg: enum): NimNode {.compileTime.} = result = newCall( - arg.typeof.getTypeInst[1], + arg.type.getTypeInst[1], newLit(int(arg)) ) -proc newLit*[N,T](arg: array[N,T]): NimNode -proc newLit*[T](arg: seq[T]): NimNode -proc newLit*[T](s: set[T]): NimNode -proc newLit*[T: tuple](arg: T): NimNode +proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} +proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} +proc newLit*[T](s: set[T]): NimNode {.compileTime.} +proc newLit*[T: tuple](arg: T): NimNode {.compileTime.} -proc newLit*(arg: object): NimNode = - result = nnkObjConstr.newTree(arg.typeof.getTypeInst[1]) +proc newLit*(arg: object): NimNode {.compileTime.} = + result = nnkObjConstr.newTree(arg.type.getTypeInst[1]) for a, b in arg.fieldPairs: result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) ) -proc newLit*(arg: ref object): NimNode = +proc newLit*(arg: ref object): NimNode {.compileTime.} = ## produces a new ref type literal node. - result = nnkObjConstr.newTree(arg.typeof.getTypeInst[1]) + result = nnkObjConstr.newTree(arg.type.getTypeInst[1]) for a, b in fieldPairs(arg[]): result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b)) -proc newLit*[N,T](arg: array[N,T]): NimNode = +proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} = result = nnkBracket.newTree for x in arg: result.add newLit(x) -proc newLit*[T](arg: seq[T]): NimNode = +proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} = let bracket = nnkBracket.newTree for x in arg: bracket.add newLit(x) @@ -862,7 +829,7 @@ proc newLit*[T](arg: seq[T]): NimNode = var typ = getTypeInst(typeof(arg))[1] result = newCall(typ,result) -proc newLit*[T](s: set[T]): NimNode = +proc newLit*[T](s: set[T]): NimNode {.compileTime.} = result = nnkCurly.newTree for x in s: result.add newLit(x) @@ -874,7 +841,7 @@ proc newLit*[T](s: set[T]): NimNode = proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} ## See `typetraits.isNamedTuple` -proc newLit*[T: tuple](arg: T): NimNode = +proc newLit*[T: tuple](arg: T): NimNode {.compileTime.} = ## use -d:nimHasWorkaround14720 to restore behavior prior to PR, forcing ## a named tuple even when `arg` is unnamed. result = nnkTupleConstr.newTree @@ -885,9 +852,9 @@ proc newLit*[T: tuple](arg: T): NimNode = for b in arg.fields: result.add newLit(b) -proc nestList*(op: NimNode; pack: NimNode): NimNode = +proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} = ## Nests the list `pack` into a tree of call expressions: - ## `[a, b, c]` is transformed into `op(a, op(c, d))`. + ## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``. ## This is also known as fold expression. if pack.len < 1: error("`nestList` expects a node with at least 1 child") @@ -895,37 +862,14 @@ proc nestList*(op: NimNode; pack: NimNode): NimNode = for i in countdown(pack.len - 2, 0): result = newCall(op, pack[i], result) -proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode = +proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime.} = ## Nests the list `pack` into a tree of call expressions: - ## `[a, b, c]` is transformed into `op(a, op(c, d))`. + ## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``. ## This is also known as fold expression. result = init for i in countdown(pack.len - 1, 0): result = newCall(op, pack[i], result) -proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.} - ## Style insensitive comparison. - -proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.} - ## Style insensitive comparison. `a` can be an identifier or a - ## symbol. `a` may be wrapped in an export marker - ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), - ## these nodes will be unwrapped. - -proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} - ## Style insensitive comparison. `b` can be an identifier or a - ## symbol. `b` may be wrapped in an export marker - ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), - ## these nodes will be unwrapped. - -proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} - ## Style insensitive comparison. `a` and `b` can be an - ## identifier or a symbol. Both may be wrapped in an export marker - ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), - ## these nodes will be unwrapped. - -const collapseSymChoice = not defined(nimLegacyMacrosCollapseSymChoice) - proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} = if level > 0: if indented: @@ -953,44 +897,31 @@ proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indent res.add(" " & $n.strVal.newLit.repr) of nnkNone: assert false - elif n.kind in {nnkOpenSymChoice, nnkClosedSymChoice} and collapseSymChoice: - res.add(" " & $n.len) - if n.len > 0: - var allSameSymName = true - for i in 0.. 0: - res.add(" " & n[0].strVal.newLit.repr) else: res.add(".newTree(") for j in 0.. `a.b` return newNimNode(nnkDotExpr).add(a, b) -proc newColonExpr*(a, b: NimNode): NimNode = +proc newColonExpr*(a, b: NimNode): NimNode {.compileTime.} = ## Create new colon expression. ## newColonExpr(a, b) -> `a: b` newNimNode(nnkExprColonExpr).add(a, b) proc newIdentDefs*(name, kind: NimNode; - default = newEmptyNode()): NimNode = - ## Creates a new `nnkIdentDefs` node of a specific kind and value. + default = newEmptyNode()): NimNode {.compileTime.} = + ## Creates a new ``nnkIdentDefs`` node of a specific kind and value. ## - ## `nnkIdentDefs` need to have at least three children, but they can have + ## ``nnkIdentDefs`` need to have at least three children, but they can have ## more: first comes a list of identifiers followed by a type and value ## nodes. This helper proc creates a three node subtree, the first subnode - ## being a single identifier name. Both the `kind` node and `default` - ## (value) nodes may be empty depending on where the `nnkIdentDefs` - ## appears: tuple or object definitions will have an empty `default` node, - ## `let` or `var` blocks may have an empty `kind` node if the + ## being a single identifier name. Both the ``kind`` node and ``default`` + ## (value) nodes may be empty depending on where the ``nnkIdentDefs`` + ## appears: tuple or object definitions will have an empty ``default`` node, + ## ``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`: - ## ``` + ## ``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 = +proc newNilLit*(): NimNode {.compileTime.} = ## New nil literal shortcut. result = newNimNode(nnkNilLit) -proc last*(node: NimNode): NimNode = node[node.len-1] +proc last*(node: NimNode): NimNode {.compileTime.} = node[node.len-1] ## Return the last item in nodes children. Same as `node[^1]`. @@ -1209,7 +1132,7 @@ const CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit, nnkHiddenCallConv} -proc expectKind*(n: NimNode; k: set[NimNodeKind]) = +proc expectKind*(n: NimNode; k: set[NimNodeKind]) {.compileTime.} = ## Checks that `n` is of kind `k`. If this is not the case, ## compilation aborts with an error message. This is useful for writing ## macros that check the AST that is passed to them. @@ -1219,10 +1142,10 @@ proc newProc*(name = newEmptyNode(); params: openArray[NimNode] = [newEmptyNode()]; body: NimNode = newStmtList(); procType = nnkProcDef; - pragmas: NimNode = newEmptyNode()): NimNode = + pragmas: NimNode = newEmptyNode()): NimNode {.compileTime.} = ## Shortcut for creating a new proc. ## - ## The `params` array must start with the return type of the proc, + ## The ``params`` array must start with the return type of the proc, ## followed by a list of IdentDefs which specify the params. if procType notin RoutineNodes: error("Expected one of " & $RoutineNodes & ", got " & $procType) @@ -1236,14 +1159,17 @@ proc newProc*(name = newEmptyNode(); newEmptyNode(), body) -proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): NimNode = - ## Constructor for `if` statements. - ## ``` - ## newIfStmt( - ## (Ident, StmtList), - ## ... - ## ) - ## ``` +proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): + NimNode {.compileTime.} = + ## Constructor for ``if`` statements. + ## + ## .. code-block:: nim + ## + ## newIfStmt( + ## (Ident, StmtList), + ## ... + ## ) + ## result = newNimNode(nnkIfStmt) if len(branches) < 1: error("If statement must have at least one branch") @@ -1251,18 +1177,20 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): NimNode = result.add(newTree(nnkElifBranch, i.cond, i.body)) proc newEnum*(name: NimNode, fields: openArray[NimNode], - public, pure: bool): NimNode = + public, pure: bool): NimNode {.compileTime.} = ## Creates a new enum. `name` must be an ident. Fields are allowed to be - ## either idents or EnumFieldDef: - ## ``` - ## newEnum( - ## name = ident("Colors"), - ## fields = [ident("Blue"), ident("Red")], - ## public = true, pure = false) + ## either idents or EnumFieldDef + ## + ## .. code-block:: nim + ## + ## newEnum( + ## name = ident("Colors"), + ## fields = [ident("Blue"), ident("Red")], + ## public = true, pure = false) + ## + ## # type Colors* = Blue Red ## - ## # type Colors* = Blue Red - ## ``` expectKind name, nnkIdent if len(fields) < 1: @@ -1292,7 +1220,7 @@ proc newEnum*(name: NimNode, fields: openArray[NimNode], return typeSect -proc copyChildrenTo*(src, dest: NimNode) = +proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}= ## Copy all children from `src` to `dest`. for i in 0 ..< src.len: dest.add src[i].copyNimTree @@ -1300,7 +1228,7 @@ proc copyChildrenTo*(src, dest: NimNode) = template expectRoutine(node: NimNode) = expectKind(node, RoutineNodes) -proc name*(someProc: NimNode): NimNode = +proc name*(someProc: NimNode): NimNode {.compileTime.} = someProc.expectRoutine result = someProc[0] if result.kind == nnkPostfix: @@ -1311,28 +1239,21 @@ proc name*(someProc: NimNode): NimNode = elif result.kind == nnkAccQuoted: result = result[0] -proc `name=`*(someProc: NimNode; val: NimNode) = +proc `name=`*(someProc: NimNode; val: NimNode) {.compileTime.} = someProc.expectRoutine if someProc[0].kind == nnkPostfix: someProc[0][1] = val else: someProc[0] = val -proc params*(someProc: NimNode): NimNode = - if someProc.kind == nnkProcTy: - someProc[0] - else: - someProc.expectRoutine - someProc[3] - -proc `params=`* (someProc: NimNode; params: NimNode) = +proc params*(someProc: NimNode): NimNode {.compileTime.} = + someProc.expectRoutine + result = someProc[3] +proc `params=`* (someProc: NimNode; params: NimNode) {.compileTime.}= + someProc.expectRoutine expectKind(params, nnkFormalParams) - if someProc.kind == nnkProcTy: - someProc[0] = params - else: - someProc.expectRoutine - someProc[3] = params + someProc[3] = params -proc pragma*(someProc: NimNode): NimNode = +proc pragma*(someProc: NimNode): NimNode {.compileTime.} = ## Get the pragma of a proc type. ## These will be expanded. if someProc.kind == nnkProcTy: @@ -1340,7 +1261,7 @@ proc pragma*(someProc: NimNode): NimNode = else: someProc.expectRoutine result = someProc[4] -proc `pragma=`*(someProc: NimNode; val: NimNode) = +proc `pragma=`*(someProc: NimNode; val: NimNode) {.compileTime.}= ## Set the pragma of a proc type. expectKind(val, {nnkEmpty, nnkPragma}) if someProc.kind == nnkProcTy: @@ -1349,7 +1270,7 @@ proc `pragma=`*(someProc: NimNode; val: NimNode) = someProc.expectRoutine someProc[4] = val -proc addPragma*(someProc, pragma: NimNode) = +proc addPragma*(someProc, pragma: NimNode) {.compileTime.} = ## Adds pragma to routine definition. someProc.expectKind(RoutineNodes + {nnkProcTy}) var pragmaNode = someProc.pragma @@ -1361,7 +1282,7 @@ proc addPragma*(someProc, pragma: NimNode) = template badNodeKind(n, f) = error("Invalid node kind " & $n.kind & " for macros.`" & $f & "`", n) -proc body*(someProc: NimNode): NimNode = +proc body*(someProc: NimNode): NimNode {.compileTime.} = case someProc.kind: of RoutineNodes: return someProc[6] @@ -1372,7 +1293,7 @@ proc body*(someProc: NimNode): NimNode = else: badNodeKind someProc, "body" -proc `body=`*(someProc: NimNode, val: NimNode) = +proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} = case someProc.kind of RoutineNodes: someProc[6] = val @@ -1383,17 +1304,9 @@ proc `body=`*(someProc: NimNode, val: NimNode) = else: badNodeKind someProc, "body=" -proc basename*(a: NimNode): NimNode = - ## Pull an identifier from prefix/postfix expressions. - case a.kind - of nnkIdent: result = a - of nnkPostfix, nnkPrefix: result = a[1] - of nnkPragmaExpr: result = basename(a[0]) - else: - error("Do not know how to get basename of (" & treeRepr(a) & ")\n" & - repr(a), a) +proc basename*(a: NimNode): NimNode {.compileTime, benign.} -proc `$`*(node: NimNode): string = +proc `$`*(node: NimNode): string {.compileTime.} = ## Get the string of an identifier node. case node.kind of nnkPostfix: @@ -1403,33 +1316,31 @@ proc `$`*(node: NimNode): string = of nnkOpenSymChoice, nnkClosedSymChoice: result = $node[0] of nnkAccQuoted: - result = "" - for i in 0 ..< node.len: - result.add(repr(node[i])) + result = $node[0] else: badNodeKind node, "$" iterator items*(n: NimNode): NimNode {.inline.} = - ## Iterates over the children of the NimNode `n`. + ## Iterates over the children of the NimNode ``n``. for i in 0 ..< n.len: yield n[i] iterator pairs*(n: NimNode): (int, NimNode) {.inline.} = - ## Iterates over the children of the NimNode `n` and its indices. + ## Iterates over the children of the NimNode ``n`` and its indices. for i in 0 ..< n.len: yield (i, n[i]) iterator children*(n: NimNode): NimNode {.inline.} = - ## Iterates over the children of the NimNode `n`. + ## Iterates over the children of the NimNode ``n``. for i in 0 ..< n.len: yield n[i] 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") - ## ``` + ## it.basename.ident == toNimIdent"foo") block: var res: NimNode for it in n.children: @@ -1438,8 +1349,8 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} = break res -proc insert*(a: NimNode; pos: int; b: NimNode) = - ## Insert node `b` into node `a` at `pos`. +proc insert*(a: NimNode; pos: int; b: NimNode) {.compileTime.} = + ## Insert node ``b`` into node ``a`` at ``pos``. if len(a)-1 < pos: # add some empty nodes first for i in len(a)-1..pos-2: @@ -1453,7 +1364,17 @@ proc insert*(a: NimNode; pos: int; b: NimNode) = a[i + 1] = a[i] a[pos] = b -proc `basename=`*(a: NimNode; val: string) = +proc basename*(a: NimNode): NimNode = + ## Pull an identifier from prefix/postfix expressions. + case a.kind + of nnkIdent: result = a + of nnkPostfix, nnkPrefix: result = a[1] + of nnkPragmaExpr: result = basename(a[0]) + else: + error("Do not know how to get basename of (" & treeRepr(a) & ")\n" & + repr(a), a) + +proc `basename=`*(a: NimNode; val: string) {.compileTime.}= case a.kind of nnkIdent: a.strVal = val @@ -1464,49 +1385,95 @@ proc `basename=`*(a: NimNode; val: string) = error("Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a), a) -proc postfix*(node: NimNode; op: string): NimNode = +proc postfix*(node: NimNode; op: string): NimNode {.compileTime.} = newNimNode(nnkPostfix).add(ident(op), node) -proc prefix*(node: NimNode; op: string): NimNode = +proc prefix*(node: NimNode; op: string): NimNode {.compileTime.} = newNimNode(nnkPrefix).add(ident(op), node) proc infix*(a: NimNode; op: string; - b: NimNode): NimNode = + b: NimNode): NimNode {.compileTime.} = newNimNode(nnkInfix).add(ident(op), a, b) -proc unpackPostfix*(node: NimNode): tuple[node: NimNode; op: string] = +proc unpackPostfix*(node: NimNode): tuple[node: NimNode; op: string] {. + compileTime.} = node.expectKind nnkPostfix result = (node[1], $node[0]) -proc unpackPrefix*(node: NimNode): tuple[node: NimNode; op: string] = +proc unpackPrefix*(node: NimNode): tuple[node: NimNode; op: string] {. + compileTime.} = node.expectKind nnkPrefix result = (node[1], $node[0]) -proc unpackInfix*(node: NimNode): tuple[left: NimNode; op: string; right: NimNode] = +proc unpackInfix*(node: NimNode): tuple[left: NimNode; op: string; + right: NimNode] {.compileTime.} = expectKind(node, nnkInfix) result = (node[1], $node[0], node[2]) -proc copy*(node: NimNode): NimNode = +proc copy*(node: NimNode): NimNode {.compileTime.} = ## An alias for `copyNimTree<#copyNimTree,NimNode>`_. return node.copyNimTree() -proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} = - ## Check that `eqIdent(n,name)` holds true. If this is not the +when defined(nimVmEqIdent): + proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. + + proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. ``a`` can be an identifier or a + ## symbol. ``a`` may be wrapped in an export marker + ## (``nnkPostfix``) or quoted with backticks (``nnkAccQuoted``), + ## these nodes will be unwrapped. + + proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. ``b`` can be an identifier or a + ## symbol. ``b`` may be wrapped in an export marker + ## (``nnkPostfix``) or quoted with backticks (``nnkAccQuoted``), + ## these nodes will be unwrapped. + + proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. ``a`` and ``b`` can be an + ## identifier or a symbol. Both may be wrapped in an export marker + ## (``nnkPostfix``) or quoted with backticks (``nnkAccQuoted``), + ## these nodes will be unwrapped. + +else: + from std/private/strimpl import cmpIgnoreStyleImpl + # this procedure is optimized for native code, it should not be compiled to nimVM bytecode. + proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} = + cmpIgnoreStyleImpl(a, b, true) + + proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0 + ## Check if two idents are equal. + + proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} = + ## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.) + ## is the same as ``s``. Note that this is the preferred way to check! Most + ## other ways like ``node.ident`` are much more error-prone, unfortunately. + case node.kind + of nnkSym, nnkIdent: + result = eqIdent(node.strVal, s) + of nnkOpenSymChoice, nnkClosedSymChoice: + result = eqIdent($node[0], s) + else: + result = false + +proc expectIdent*(n: NimNode, name: string) {.compileTime, since: (1,1).} = + ## Check that ``eqIdent(n,name)`` holds true. If this is not the ## case, compilation aborts with an error message. This is useful ## for writing macros that check the AST that is passed to them. if not eqIdent(n, name): error("Expected identifier to be `" & name & "` here", n) -proc hasArgOfName*(params: NimNode; name: string): bool = - ## Search `nnkFormalParams` for an argument. +proc hasArgOfName*(params: NimNode; name: string): bool {.compileTime.}= + ## Search ``nnkFormalParams`` for an argument. expectKind(params, nnkFormalParams) for i in 1.. 1 and typ[1].kind == nnkProcTy: return typ[1][1] elif typ.typeKind == ntyTypeDesc: - let impl = getImpl( - if kind(typ[1]) == nnkBracketExpr: typ[1][0] - else: typ[1] - ) - if impl.kind == nnkNilLit: - return impl - elif impl[0].kind == nnkPragmaExpr: + let impl = typ[1].getImpl() + if impl[0].kind == nnkPragmaExpr: return impl[0][1] else: return impl[0] # handle types which don't have macro at all @@ -1588,10 +1534,10 @@ proc customPragmaNode(n: NimNode): NimNode = let impl = n.getImpl() if impl.kind in RoutineNodes: return impl.pragma - elif impl.kind in {nnkIdentDefs, nnkConstDef} and impl[0].kind == nnkPragmaExpr: + elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr: return impl[0][1] else: - let timpl = getImpl(if typ.kind == nnkBracketExpr: typ[0] else: typ) + let timpl = typ.getImpl() if timpl.len>0 and timpl[0].len>1: return timpl[0][1] else: @@ -1599,13 +1545,12 @@ proc customPragmaNode(n: NimNode): NimNode = if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}: let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1]) - var typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]) - while typInst.kind in {nnkVarTy, nnkBracketExpr}: typInst = typInst[0] - var typDef = getImpl(typInst) + let typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]) + var typDef = getImpl(if typInst.kind == nnkVarTy: typInst[0] else: typInst) while typDef != nil: typDef.expectKind(nnkTypeDef) - let typ = typDef[2].extractTypeImpl() - if typ.kind notin {nnkRefTy, nnkPtrTy, nnkObjectTy}: break + let typ = typDef[2] + typ.expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy}) 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]) @@ -1616,17 +1561,18 @@ proc customPragmaNode(n: NimNode): NimNode = for i in 0.. 0: var identDefs = identDefsStack.pop() - - case identDefs.kind - of nnkRecList: - for child in identDefs.children: - identDefsStack.add(child) - of nnkRecCase: - # Add condition definition + if identDefs.kind == nnkRecCase: identDefsStack.add(identDefs[0]) - # Add branches - for i in 1 ..< identDefs.len: - identDefsStack.add(identDefs[i].last) + for i in 1.. 0 and p[0].kind == nnkSym and p[0] == cp: - if p.len == 2 or (p.len == 3 and p[1].kind == nnkSym and p[1].symKind == nskType): + if p.len == 2: result = p[1] else: let def = p[0].getImpl[3] @@ -1702,21 +1646,6 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error, macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = - ## Calls `callee` with `args` unpacked as individual arguments. - ## This is useful in 2 cases: - ## * when forwarding `varargs[T]` for some typed `T` - ## * when forwarding `varargs[untyped]` when `args` can potentially be empty, - ## due to a compiler limitation - runnableExamples: - template call1(fun: typed; args: varargs[untyped]): untyped = - unpackVarargs(fun, args) - # when varargsLen(args) > 0: fun(args) else: fun() # this would also work - template call2(fun: typed; args: varargs[typed]): untyped = - unpackVarargs(fun, args) - proc fn1(a = 0, b = 1) = discard (a, b) - call1(fn1, 10, 11) - call1(fn1) # `args` is empty in this case - if false: call2(echo, 10, 11) # would print 1011 result = newCall(callee) for i in 0 ..< args.len: result.add args[i] @@ -1728,17 +1657,17 @@ proc getProjectPath*(): string = discard ## which returns the path of the source file containing that template ## call. ## - ## For example, assume a `dir1/foo.nim` that imports a `dir2/bar.nim`, - ## have the `bar.nim` print out both `getProjectPath` and - ## `currentSourcePath` outputs. + ## For example, assume a ``dir1/foo.nim`` that imports a ``dir2/bar.nim``, + ## have the ``bar.nim`` print out both ``getProjectPath`` and + ## ``currentSourcePath`` outputs. ## - ## Now when `foo.nim` is compiled, the `getProjectPath` from - ## `bar.nim` will return the `dir1/` path, while the `currentSourcePath` - ## will return the path to the `bar.nim` source file. + ## Now when ``foo.nim`` is compiled, the ``getProjectPath`` from + ## ``bar.nim`` will return the ``dir1/`` path, while the ``currentSourcePath`` + ## will return the path to the ``bar.nim`` source file. ## - ## Now when `bar.nim` is compiled directly, the `getProjectPath` - ## will now return the `dir2/` path, and the `currentSourcePath` - ## will still return the same path, the path to the `bar.nim` source + ## Now when ``bar.nim`` is compiled directly, the ``getProjectPath`` + ## will now return the ``dir2/`` path, and the ``currentSourcePath`` + ## will still return the same path, the path to the ``bar.nim`` source ## file. ## ## The path returned by this proc is set at compile time. @@ -1746,21 +1675,22 @@ proc getProjectPath*(): string = discard ## See also: ## * `getCurrentDir proc `_ -proc getSize*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} = - ## Returns the same result as `system.sizeof` if the size is - ## known by the Nim compiler. Returns a negative value if the Nim - ## compiler does not know the size. -proc getAlign*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} = - ## Returns the same result as `system.alignof` if the alignment - ## is known by the Nim compiler. It works on `NimNode` for use - ## in macro context. Returns a negative value if the Nim compiler - ## does not know the alignment. -proc getOffset*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} = - ## Returns the same result as `system.offsetof` if the offset is - ## known by the Nim compiler. It expects a resolved symbol node - ## from a field of a type. Therefore it only requires one argument - ## instead of two. Returns a negative value if the Nim compiler - ## does not know the offset. +when defined(nimMacrosSizealignof): + proc getSize*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} = + ## Returns the same result as ``system.sizeof`` if the size is + ## known by the Nim compiler. Returns a negative value if the Nim + ## compiler does not know the size. + proc getAlign*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} = + ## Returns the same result as ``system.alignof`` if the alignment + ## is known by the Nim compiler. It works on ``NimNode`` for use + ## in macro context. Returns a negative value if the Nim compiler + ## does not know the alignment. + proc getOffset*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} = + ## Returns the same result as ``system.offsetof`` if the offset is + ## known by the Nim compiler. It expects a resolved symbol node + ## from a field of a type. Therefore it only requires one argument + ## instead of two. Returns a negative value if the Nim compiler + ## does not know the offset. proc isExported*(n: NimNode): bool {.noSideEffect.} = ## Returns whether the symbol is exported or not. @@ -1770,34 +1700,33 @@ proc extractDocCommentsAndRunnables*(n: NimNode): NimNode = ## runnableExamples in `a`, stopping at the first child that is neither. ## Example: ## - ## ``` - ## 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) + ## .. code-block:: nim + ## import 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 - ## ``` + ## 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: case ni.kind of nnkCommentStmt: result.add ni - of nnkCall, nnkCommand: - if ni[0].kind == nnkIdent and ni[0].eqIdent "runnableExamples": + of nnkCall: + if ni[0].kind == nnkIdent and ni[0].strVal == "runnableExamples": result.add ni else: break else: break diff --git a/lib/core/rlocks.nim b/lib/core/rlocks.nim index bee5c1655..27a9c5e60 100644 --- a/lib/core/rlocks.nim +++ b/lib/core/rlocks.nim @@ -11,12 +11,10 @@ when not compileOption("threads") and not defined(nimdoc): - when false: - # make rlocks modlue consistent with locks module, - # so they can replace each other seamlessly. - {.error: "Rlocks requires --threads:on option.".} + {.error: "Rlocks requires --threads:on option.".} -import std/private/syslocks +const insideRLocksModule = true +include "system/syslocks" type RLock* = SysLock ## Nim lock, re-entrant @@ -35,23 +33,23 @@ proc deinitRLock*(lock: var RLock) {.inline.} = ## Frees the resources associated with the lock. deinitSys(lock) -proc tryAcquire*(lock: var RLock): bool {.inline.} = +proc tryAcquire*(lock: var RLock): bool = ## Tries to acquire the given lock. Returns `true` on success. result = tryAcquireSys(lock) -proc acquire*(lock: var RLock) {.inline.} = +proc acquire*(lock: var RLock) = ## Acquires the given lock. acquireSys(lock) -proc release*(lock: var RLock) {.inline.} = +proc release*(lock: var RLock) = ## Releases the given lock. releaseSys(lock) -template withRLock*(lock: RLock, code: untyped) = +template withRLock*(lock: var RLock, code: untyped): untyped = ## Acquires the given lock and then executes the code. - acquire(lock) - {.locks: [lock].}: - try: - code - finally: + block: + acquire(lock) + defer: release(lock) + {.locks: [lock].}: + code diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 5075c8458..9b7e32466 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -9,29 +9,16 @@ ## This module implements an interface to Nim's `runtime type information`:idx: ## (`RTTI`:idx:). See the `marshal `_ module for an example of -## what this allows you to do. +## what this module allows you to do. ## -## .. note:: Even though `Any` and its operations hide the nasty low level -## details from its users, it remains inherently unsafe! Also, Nim's -## runtime type information will evolve and may eventually be deprecated. -## As an alternative approach to programmatically understanding and -## manipulating types, consider using the `macros `_ module to -## work with the types' AST representation at compile time. See for example -## the `getTypeImpl proc `_. As an alternative -## approach to storing arbitrary types at runtime, consider using generics. - -runnableExamples: - var x: Any - - var i = 42 - x = i.toAny - assert x.kind == akInt - assert x.getInt == 42 - - var s = @[1, 2, 3] - x = s.toAny - assert x.kind == akSequence - assert x.len == 3 +## Note that even though ``Any`` and its operations hide the nasty low level +## details from its clients, it remains inherently unsafe! Also, Nim's +## runtime type information will evolve and may eventually be deprecated. +## As an alternative approach to programmatically understanding and +## manipulating types, consider using the `macros `_ package to +## work with the types' AST representation at compile time. See, for example, +## the `getTypeImpl proc`_. As an alternative +## approach to storing arbitrary types at runtime, consider using generics. {.push hints: off.} @@ -40,50 +27,45 @@ include "system/hti.nim" {.pop.} -when defined(nimPreviewSlimSystem): - import std/assertions - - type - AnyKind* = enum ## The kind of `Any`. - akNone = 0, ## invalid - akBool = 1, ## bool - akChar = 2, ## char - akEnum = 14, ## enum - akArray = 16, ## array - akObject = 17, ## object - akTuple = 18, ## tuple - akSet = 19, ## set - akRange = 20, ## range - akPtr = 21, ## ptr - akRef = 22, ## ref - akSequence = 24, ## sequence - akProc = 25, ## proc - akPointer = 26, ## pointer - akString = 28, ## string - akCString = 29, ## cstring - akInt = 31, ## int - akInt8 = 32, ## int8 - akInt16 = 33, ## int16 - akInt32 = 34, ## int32 - akInt64 = 35, ## int64 - akFloat = 36, ## float - akFloat32 = 37, ## float32 - akFloat64 = 38, ## float64 - akFloat128 = 39, ## float128 - akUInt = 40, ## uint - akUInt8 = 41, ## uint8 - akUInt16 = 42, ## uin16 - akUInt32 = 43, ## uint32 - akUInt64 = 44, ## uint64 + AnyKind* = enum ## what kind of ``any`` it is + akNone = 0, ## invalid any + akBool = 1, ## any represents a ``bool`` + akChar = 2, ## any represents a ``char`` + akEnum = 14, ## any represents an enum + akArray = 16, ## any represents an array + akObject = 17, ## any represents an object + akTuple = 18, ## any represents a tuple + akSet = 19, ## any represents a set + akRange = 20, ## any represents a range + akPtr = 21, ## any represents a ptr + akRef = 22, ## any represents a ref + akSequence = 24, ## any represents a sequence + akProc = 25, ## any represents a proc + akPointer = 26, ## any represents a pointer + akString = 28, ## any represents a string + akCString = 29, ## any represents a cstring + akInt = 31, ## any represents an int + akInt8 = 32, ## any represents an int8 + akInt16 = 33, ## any represents an int16 + akInt32 = 34, ## any represents an int32 + akInt64 = 35, ## any represents an int64 + akFloat = 36, ## any represents a float + akFloat32 = 37, ## any represents a float32 + akFloat64 = 38, ## any represents a float64 + akFloat128 = 39, ## any represents a float128 + akUInt = 40, ## any represents an unsigned int + akUInt8 = 41, ## any represents an unsigned int8 + akUInt16 = 42, ## any represents an unsigned in16 + akUInt32 = 43, ## any represents an unsigned int32 + akUInt64 = 44, ## any represents an unsigned int64 # akOpt = 44+18 ## the builtin 'opt' type. - Any* = object - ## A type that can represent any nim value. - ## - ## .. danger:: The wrapped value can be modified with its wrapper! This means - ## that `Any` keeps a non-traced pointer to its wrapped value and - ## **must not** live longer than its wrapped value. + Any* = object ## can represent any nim value; NOTE: the wrapped + ## value can be modified with its wrapper! This means + ## that ``Any`` keeps a non-traced pointer to its + ## wrapped value and **must not** live longer than + ## its wrapped value. value: pointer when defined(js): rawType: PNimType @@ -91,7 +73,7 @@ type rawTypePtr: pointer ppointer = ptr pointer - pbyteArray = ptr array[0xffff, uint8] + pbyteArray = ptr array[0xffff, int8] when not defined(gcDestructors): type @@ -102,14 +84,14 @@ when not defined(gcDestructors): PGenSeq = ptr TGenericSeq when defined(gogc): - const GenericSeqSize = 3 * sizeof(int) + const GenericSeqSize = (3 * sizeof(int)) else: - const GenericSeqSize = 2 * sizeof(int) + const GenericSeqSize = (2 * sizeof(int)) else: include system/seqs_v2_reimpl -from std/private/strimpl import cmpNimIdentifier +from std/private/strimpl import cmpIgnoreStyleImpl when not defined(js): template rawType(x: Any): PNimType = @@ -121,7 +103,8 @@ when not defined(js): proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.} when not defined(gcDestructors): - proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.} + proc genericShallowAssign(dest, src: pointer, mt: PNimType) {. + importCompilerProc.} proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc.} proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.} proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.} @@ -132,26 +115,26 @@ else: proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. importCompilerProc.} -template `+!!`(a, b): untyped = cast[pointer](cast[int](a) + b) +template `+!!`(a, b): untyped = cast[pointer](cast[ByteAddress](a) + b) proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = assert(n.kind == nkCase) var d: int - let a = cast[int](aa) + var a = cast[ByteAddress](aa) case n.typ.size - 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)[]) + of 1: d = ze(cast[ptr int8](a +% n.offset)[]) + of 2: d = ze(cast[ptr int16](a +% n.offset)[]) + of 4: d = int(cast[ptr int32](a +% n.offset)[]) + of 8: d = int(cast[ptr int64](a +% n.offset)[]) else: assert(false) return d proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode = - let discr = getDiscriminant(aa, n) + var discr = getDiscriminant(aa, n) if discr <% n.len: result = n.sons[discr] if result == nil: result = n.sons[n.len] - # n.sons[n.len] contains the `else` part (but may be nil) + # n.sons[n.len] contains the ``else`` part (but may be nil) else: result = n.sons[n.len] @@ -161,17 +144,17 @@ proc newAny(value: pointer, rawType: PNimType): Any {.inline.} = when declared(system.VarSlot): proc toAny*(x: VarSlot): Any {.inline.} = - ## Constructs an `Any` object from a variable slot `x`. + ## Constructs a ``Any`` object from a variable slot ``x``. ## This captures `x`'s address, so `x` can be modified with its - ## `Any` wrapper! The caller needs to ensure that the wrapper + ## ``Any`` wrapper! The client needs to ensure that the wrapper ## **does not** live longer than `x`! ## This is provided for easier reflection capabilities of a debugger. result.value = x.address result.rawType = x.typ proc toAny*[T](x: var T): Any {.inline.} = - ## Constructs an `Any` object from `x`. This captures `x`'s address, so - ## `x` can be modified with its `Any` wrapper! The caller needs to ensure + ## Constructs a ``Any`` object from `x`. This captures `x`'s address, so + ## `x` can be modified with its ``Any`` wrapper! The client needs to ensure ## that the wrapper **does not** live longer than `x`! newAny(addr(x), cast[PNimType](getTypeInfo(x))) @@ -184,17 +167,17 @@ proc size*(x: Any): int {.inline.} = result = x.rawType.size proc baseTypeKind*(x: Any): AnyKind {.inline.} = - ## Gets the base type's kind. If `x` has no base type, `akNone` is returned. + ## Gets the base type's kind; ``akNone`` is returned if `x` has no base type. if x.rawType.base != nil: result = AnyKind(ord(x.rawType.base.kind)) proc baseTypeSize*(x: Any): int {.inline.} = - ## Returns the size of `x`'s base type. If `x` has no base type, 0 is returned. + ## Returns the size of `x`'s basetype. if x.rawType.base != nil: result = x.rawType.base.size proc invokeNew*(x: Any) = - ## Performs `new(x)`. `x` needs to represent a `ref`. + ## Performs ``new(x)``. `x` needs to represent a ``ref``. assert x.rawType.kind == tyRef when defined(gcDestructors): cast[ppointer](x.value)[] = nimNewObj(x.rawType.base.size, x.rawType.base.align) @@ -203,7 +186,7 @@ proc invokeNew*(x: Any) = genericAssign(x.value, addr(z), x.rawType) proc invokeNewSeq*(x: Any, len: int) = - ## Performs `newSeq(x, len)`. `x` needs to represent a `seq`. + ## Performs ``newSeq(x, len)``. `x` needs to represent a ``seq``. assert x.rawType.kind == tySequence when defined(gcDestructors): var s = cast[ptr NimSeqV2Reimpl](x.value) @@ -215,13 +198,12 @@ proc invokeNewSeq*(x: Any, len: int) = genericShallowAssign(x.value, addr(z), x.rawType) proc extendSeq*(x: Any) = - ## Performs `setLen(x, x.len+1)`. `x` needs to represent a `seq`. + ## Performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``. assert x.rawType.kind == tySequence when defined(gcDestructors): var s = cast[ptr NimSeqV2Reimpl](x.value) let elem = x.rawType.base - if s.p == nil or s.p.cap < s.len+1: - s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align)) + s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align)) inc s.len else: var y = cast[ptr PGenSeq](x.value)[] @@ -319,32 +301,33 @@ proc len*(x: Any): int = proc base*(x: Any): Any = - ## Returns the base type of `x` (useful for inherited object types). + ## Returns base Any (useful for inherited object types). result.rawType = x.rawType.base result.value = x.value proc isNil*(x: Any): bool = - ## `isNil` for an `x` that represents a cstring, proc or + ## `isNil` for an any `x` that represents a cstring, proc or ## some pointer type. - assert x.rawType.kind in {tyCstring, tyRef, tyPtr, tyPointer, tyProc} + assert x.rawType.kind in {tyCString, tyRef, tyPtr, tyPointer, tyProc} result = isNil(cast[ppointer](x.value)[]) -const pointerLike = - when defined(gcDestructors): {tyCstring, tyRef, tyPtr, tyPointer, tyProc} - else: {tyString, tyCstring, tyRef, tyPtr, tyPointer, tySequence, tyProc} +const + pointerLike = when defined(gcDestructors): {tyCString, tyRef, tyPtr, tyPointer, tyProc} + else: {tyString, tyCString, tyRef, tyPtr, tyPointer, + tySequence, tyProc} proc getPointer*(x: Any): pointer = - ## Retrieves the pointer value out of `x`. `x` needs to be of kind - ## `akString`, `akCString`, `akProc`, `akRef`, `akPtr`, - ## `akPointer` or `akSequence`. + ## Retrieves the pointer value out of `x`. ``x`` needs to be of kind + ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, + ## ``akPointer``, ``akSequence``. assert x.rawType.kind in pointerLike result = cast[ppointer](x.value)[] proc setPointer*(x: Any, y: pointer) = - ## Sets the pointer value of `x`. `x` needs to be of kind - ## `akString`, `akCString`, `akProc`, `akRef`, `akPtr`, - ## `akPointer` or `akSequence`. + ## Sets the pointer value of `x`. ``x`` needs to be of kind + ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, + ## ``akPointer``, ``akSequence``. assert x.rawType.kind in pointerLike if y != nil and x.rawType.kind != tyPointer: genericAssign(x.value, y, x.rawType) @@ -366,15 +349,15 @@ proc fieldsAux(p: pointer, n: ptr TNimNode, if m != nil: fieldsAux(p, m, ret) iterator fields*(x: Any): tuple[name: string, any: Any] = - ## Iterates over every active field of `x`. `x` needs to represent an object + ## Iterates over every active field of the any `x` that represents an object ## or a tuple. assert x.rawType.kind in {tyTuple, tyObject} - let p = x.value + var p = x.value var t = x.rawType # XXX BUG: does not work yet, however is questionable anyway when false: if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] - var ret: seq[tuple[name: cstring, any: Any]] + var ret: seq[tuple[name: cstring, any: Any]] = @[] if t.kind == tyObject: while true: fieldsAux(p, t.node, ret) @@ -385,7 +368,11 @@ iterator fields*(x: Any): tuple[name: string, any: Any] = for name, any in items(ret): yield ($name, any) -proc getFieldNode(p: pointer, n: ptr TNimNode, name: cstring): ptr TNimNode = +proc cmpNimIdentifier(a, b: cstring): int {.noSideEffect.} = + cmpIgnoreStyleImpl(a, b, true) + +proc getFieldNode(p: pointer, n: ptr TNimNode, + name: cstring): ptr TNimNode = case n.kind of nkNone: assert(false) of nkSlot: @@ -399,17 +386,17 @@ proc getFieldNode(p: pointer, n: ptr TNimNode, name: cstring): ptr TNimNode = if cmpNimIdentifier(n.name, name) == 0: result = n else: - let m = selectBranch(p, n) + var m = selectBranch(p, n) if m != nil: result = getFieldNode(p, m, name) proc `[]=`*(x: Any, fieldName: string, value: Any) = - ## Sets a field of `x`. `x` needs to represent an object or a tuple. + ## Sets a field of `x`; `x` represents an object or a tuple. var t = x.rawType # XXX BUG: does not work yet, however is questionable anyway when false: if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] assert x.rawType.kind in {tyTuple, tyObject} - let n = getFieldNode(x.value, t.node, fieldName) + var n = getFieldNode(x.value, t.node, fieldName) if n != nil: assert n.typ == value.rawType genericAssign(x.value +!! n.offset, value.value, value.rawType) @@ -417,13 +404,13 @@ proc `[]=`*(x: Any, fieldName: string, value: Any) = raise newException(ValueError, "invalid field name: " & fieldName) proc `[]`*(x: Any, fieldName: string): Any = - ## Gets a field of `x`. `x` needs to represent an object or a tuple. + ## Gets a field of `x`; `x` represents an object or a tuple. var t = x.rawType # XXX BUG: does not work yet, however is questionable anyway when false: if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] assert x.rawType.kind in {tyTuple, tyObject} - let n = getFieldNode(x.value, t.node, fieldName) + var n = getFieldNode(x.value, t.node, fieldName) if n != nil: result.value = x.value +!! n.offset result.rawType = n.typ @@ -433,47 +420,47 @@ proc `[]`*(x: Any, fieldName: string): Any = raise newException(ValueError, "invalid field name: " & fieldName) proc `[]`*(x: Any): Any = - ## Dereference operator for `Any`. `x` needs to represent a ptr or a ref. + ## Dereference operation for the any `x` that represents a ptr or a ref. assert x.rawType.kind in {tyRef, tyPtr} result.value = cast[ppointer](x.value)[] result.rawType = x.rawType.base proc `[]=`*(x, y: Any) = - ## Dereference operator for `Any`. `x` needs to represent a ptr or a ref. + ## Dereference operation for the any `x` that represents a ptr or a ref. assert x.rawType.kind in {tyRef, tyPtr} assert y.rawType == x.rawType.base genericAssign(cast[ppointer](x.value)[], y.value, y.rawType) proc getInt*(x: Any): int = - ## Retrieves the `int` value out of `x`. `x` needs to represent an `int`. + ## Retrieves the int value out of `x`. `x` needs to represent an int. assert skipRange(x.rawType).kind == tyInt result = cast[ptr int](x.value)[] proc getInt8*(x: Any): int8 = - ## Retrieves the `int8` value out of `x`. `x` needs to represent an `int8`. + ## Retrieves the int8 value out of `x`. `x` needs to represent an int8. assert skipRange(x.rawType).kind == tyInt8 result = cast[ptr int8](x.value)[] proc getInt16*(x: Any): int16 = - ## Retrieves the `int16` value out of `x`. `x` needs to represent an `int16`. + ## Retrieves the int16 value out of `x`. `x` needs to represent an int16. assert skipRange(x.rawType).kind == tyInt16 result = cast[ptr int16](x.value)[] proc getInt32*(x: Any): int32 = - ## Retrieves the `int32` value out of `x`. `x` needs to represent an `int32`. + ## Retrieves the int32 value out of `x`. `x` needs to represent an int32. assert skipRange(x.rawType).kind == tyInt32 result = cast[ptr int32](x.value)[] proc getInt64*(x: Any): int64 = - ## Retrieves the `int64` value out of `x`. `x` needs to represent an `int64`. + ## Retrieves the int64 value out of `x`. `x` needs to represent an int64. assert skipRange(x.rawType).kind == tyInt64 result = cast[ptr int64](x.value)[] proc getBiggestInt*(x: Any): BiggestInt = ## Retrieves the integer value out of `x`. `x` needs to represent ## some integer, a bool, a char, an enum or a small enough bit set. - ## The value might be sign-extended to `BiggestInt`. - let t = skipRange(x.rawType) + ## The value might be sign-extended to ``BiggestInt``. + var t = skipRange(x.rawType) case t.kind of tyInt: result = BiggestInt(cast[ptr int](x.value)[]) of tyInt8: result = BiggestInt(cast[ptr int8](x.value)[]) @@ -484,8 +471,8 @@ proc getBiggestInt*(x: Any): BiggestInt = of tyChar: result = BiggestInt(cast[ptr char](x.value)[]) of tyEnum, tySet: case t.size - of 1: result = int64(cast[ptr uint8](x.value)[]) - of 2: result = int64(cast[ptr uint16](x.value)[]) + of 1: result = ze64(cast[ptr int8](x.value)[]) + of 2: result = ze64(cast[ptr int16](x.value)[]) of 4: result = BiggestInt(cast[ptr int32](x.value)[]) of 8: result = BiggestInt(cast[ptr int64](x.value)[]) else: assert false @@ -498,7 +485,7 @@ proc getBiggestInt*(x: Any): BiggestInt = proc setBiggestInt*(x: Any, y: BiggestInt) = ## Sets the integer value of `x`. `x` needs to represent ## some integer, a bool, a char, an enum or a small enough bit set. - let t = skipRange(x.rawType) + var t = skipRange(x.rawType) case t.kind of tyInt: cast[ptr int](x.value)[] = int(y) of tyInt8: cast[ptr int8](x.value)[] = int8(y) @@ -509,8 +496,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 uint8](x.value)[] = uint8(y.int) - of 2: cast[ptr uint16](x.value)[] = uint16(y.int) + of 1: cast[ptr int8](x.value)[] = toU8(y.int) + of 2: cast[ptr int16](x.value)[] = toU16(y.int) of 4: cast[ptr int32](x.value)[] = int32(y) of 8: cast[ptr int64](x.value)[] = y else: assert false @@ -521,34 +508,38 @@ proc setBiggestInt*(x: Any, y: BiggestInt) = else: assert false proc getUInt*(x: Any): uint = - ## Retrieves the `uint` value out of `x`. `x` needs to represent a `uint`. + ## Retrieves the uint value out of `x`, `x` needs to represent an uint. assert skipRange(x.rawType).kind == tyUInt result = cast[ptr uint](x.value)[] proc getUInt8*(x: Any): uint8 = - ## Retrieves the `uint8` value out of `x`. `x` needs to represent a `uint8`. + ## Retrieves the uint8 value out of `x`, `x` needs to represent an + ## uint8. assert skipRange(x.rawType).kind == tyUInt8 result = cast[ptr uint8](x.value)[] proc getUInt16*(x: Any): uint16 = - ## Retrieves the `uint16` value out of `x`. `x` needs to represent a `uint16`. + ## Retrieves the uint16 value out of `x`, `x` needs to represent an + ## uint16. assert skipRange(x.rawType).kind == tyUInt16 result = cast[ptr uint16](x.value)[] proc getUInt32*(x: Any): uint32 = - ## Retrieves the `uint32` value out of `x`. `x` needs to represent a `uint32`. + ## Retrieves the uint32 value out of `x`, `x` needs to represent an + ## uint32. assert skipRange(x.rawType).kind == tyUInt32 result = cast[ptr uint32](x.value)[] proc getUInt64*(x: Any): uint64 = - ## Retrieves the `uint64` value out of `x`. `x` needs to represent a `uint64`. + ## Retrieves the uint64 value out of `x`, `x` needs to represent an + ## uint64. assert skipRange(x.rawType).kind == tyUInt64 result = cast[ptr uint64](x.value)[] proc getBiggestUint*(x: Any): uint64 = ## Retrieves the unsigned integer value out of `x`. `x` needs to ## represent an unsigned integer. - let t = skipRange(x.rawType) + var t = skipRange(x.rawType) case t.kind of tyUInt: result = uint64(cast[ptr uint](x.value)[]) of tyUInt8: result = uint64(cast[ptr uint8](x.value)[]) @@ -558,9 +549,9 @@ proc getBiggestUint*(x: Any): uint64 = else: assert false proc setBiggestUint*(x: Any; y: uint64) = - ## Sets the unsigned integer value of `x`. `x` needs to represent an + ## Sets the unsigned integer value of `c`. `c` needs to represent an ## unsigned integer. - let t = skipRange(x.rawType) + var t = skipRange(x.rawType) case t.kind: of tyUInt: cast[ptr uint](x.value)[] = uint(y) of tyUInt8: cast[ptr uint8](x.value)[] = uint8(y) @@ -570,14 +561,14 @@ proc setBiggestUint*(x: Any; y: uint64) = else: assert false proc getChar*(x: Any): char = - ## Retrieves the `char` value out of `x`. `x` needs to represent a `char`. - let t = skipRange(x.rawType) + ## Retrieves the char value out of `x`. `x` needs to represent a char. + var t = skipRange(x.rawType) assert t.kind == tyChar result = cast[ptr char](x.value)[] proc getBool*(x: Any): bool = - ## Retrieves the `bool` value out of `x`. `x` needs to represent a `bool`. - let t = skipRange(x.rawType) + ## Retrieves the bool value out of `x`. `x` needs to represent a bool. + var t = skipRange(x.rawType) assert t.kind == tyBool result = cast[ptr bool](x.value)[] @@ -590,11 +581,11 @@ proc skipRange*(x: Any): Any = proc getEnumOrdinal*(x: Any, name: string): int = ## Gets the enum field ordinal from `name`. `x` needs to represent an enum ## but is only used to access the type information. In case of an error - ## `low(int)` is returned. - let typ = skipRange(x.rawType) + ## ``low(int)`` is returned. + var typ = skipRange(x.rawType) assert typ.kind == tyEnum - let n = typ.node - let s = n.sons + var n = typ.node + var s = n.sons for i in 0 .. n.len-1: if cmpNimIdentifier($s[i].name, name) == 0: if ntfEnumHole notin typ.flags: @@ -607,16 +598,16 @@ proc getEnumField*(x: Any, ordinalValue: int): string = ## Gets the enum field name as a string. `x` needs to represent an enum ## but is only used to access the type information. The field name of ## `ordinalValue` is returned. - let typ = skipRange(x.rawType) + var typ = skipRange(x.rawType) assert typ.kind == tyEnum - let e = ordinalValue + var e = ordinalValue if ntfEnumHole notin typ.flags: if e <% typ.node.len: return $typ.node.sons[e].name else: # ugh we need a slow linear search: - let n = typ.node - let s = n.sons + var n = typ.node + var s = n.sons for i in 0 .. n.len-1: if s[i].offset == e: return $s[i].name result = $e @@ -626,23 +617,23 @@ proc getEnumField*(x: Any): string = result = getEnumField(x, getBiggestInt(x).int) proc getFloat*(x: Any): float = - ## Retrieves the `float` value out of `x`. `x` needs to represent a `float`. + ## Retrieves the float value out of `x`. `x` needs to represent an float. assert skipRange(x.rawType).kind == tyFloat result = cast[ptr float](x.value)[] proc getFloat32*(x: Any): float32 = - ## Retrieves the `float32` value out of `x`. `x` needs to represent a `float32`. + ## Retrieves the float32 value out of `x`. `x` needs to represent an float32. assert skipRange(x.rawType).kind == tyFloat32 result = cast[ptr float32](x.value)[] proc getFloat64*(x: Any): float64 = - ## Retrieves the `float64` value out of `x`. `x` needs to represent a `float64`. + ## Retrieves the float64 value out of `x`. `x` needs to represent an float64. assert skipRange(x.rawType).kind == tyFloat64 result = cast[ptr float64](x.value)[] proc getBiggestFloat*(x: Any): BiggestFloat = ## Retrieves the float value out of `x`. `x` needs to represent - ## some float. The value is extended to `BiggestFloat`. + ## some float. The value is extended to ``BiggestFloat``. case skipRange(x.rawType).kind of tyFloat: result = BiggestFloat(cast[ptr float](x.value)[]) of tyFloat32: result = BiggestFloat(cast[ptr float32](x.value)[]) @@ -659,7 +650,7 @@ proc setBiggestFloat*(x: Any, y: BiggestFloat) = else: assert false proc getString*(x: Any): string = - ## Retrieves the `string` value out of `x`. `x` needs to represent a `string`. + ## Retrieves the string value out of `x`. `x` needs to represent a string. assert x.rawType.kind == tyString when defined(gcDestructors): result = cast[ptr string](x.value)[] @@ -668,50 +659,50 @@ proc getString*(x: Any): string = result = cast[ptr string](x.value)[] proc setString*(x: Any, y: string) = - ## Sets the `string` value of `x`. `x` needs to represent a `string`. + ## Sets the string value of `x`. `x` needs to represent a string. assert x.rawType.kind == tyString cast[ptr string](x.value)[] = y # also correct for gcDestructors proc getCString*(x: Any): cstring = - ## Retrieves the `cstring` value out of `x`. `x` needs to represent a `cstring`. - assert x.rawType.kind == tyCstring + ## Retrieves the cstring value out of `x`. `x` needs to represent a cstring. + assert x.rawType.kind == tyCString result = cast[ptr cstring](x.value)[] proc assign*(x, y: Any) = - ## Copies the value of `y` to `x`. The assignment operator for `Any` + ## Copies the value of `y` to `x`. The assignment operator for ``Any`` ## does NOT do this; it performs a shallow copy instead! assert y.rawType == x.rawType genericAssign(x.value, y.value, y.rawType) iterator elements*(x: Any): int = - ## Iterates over every element of `x`. `x` needs to represent a `set`. + ## Iterates over every element of `x` that represents a Nim bitset. assert x.rawType.kind == tySet - let typ = x.rawType - let p = x.value + var typ = x.rawType + var p = x.value # "typ.slots.len" field is for sets the "first" field var u: int64 case typ.size - 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 1: u = ze64(cast[ptr int8](p)[]) + of 2: u = ze64(cast[ptr int16](p)[]) + of 4: u = ze64(cast[ptr int32](p)[]) of 8: u = cast[ptr int64](p)[] else: - let a = cast[pbyteArray](p) + var a = cast[pbyteArray](p) for i in 0 .. typ.size*8-1: - if (int(a[i div 8]) and (1 shl (i mod 8))) != 0: - yield i + typ.node.len + if (ze(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: if (u and (1'i64 shl int64(i))) != 0'i64: - yield i + typ.node.len + yield i+typ.node.len proc inclSetElement*(x: Any, elem: int) = ## Includes an element `elem` in `x`. `x` needs to represent a Nim bitset. assert x.rawType.kind == tySet - let typ = x.rawType - let p = x.value + var typ = x.rawType + var p = x.value # "typ.slots.len" field is for sets the "first" field - let e = elem - typ.node.len + var e = elem - typ.node.len case typ.size of 1: var a = cast[ptr int8](p) @@ -727,4 +718,4 @@ proc inclSetElement*(x: Any, elem: int) = a[] = a[] or (1'i64 shl e) else: var a = cast[pbyteArray](p) - a[e shr 3] = a[e shr 3] or uint8(1 shl (e and 7)) + a[e shr 3] = toU8(a[e shr 3] or (1 shl (e and 7))) diff --git a/lib/deprecated/pure/LockFreeHash.nim b/lib/deprecated/pure/LockFreeHash.nim new file mode 100644 index 000000000..97e1ef6ad --- /dev/null +++ b/lib/deprecated/pure/LockFreeHash.nim @@ -0,0 +1,609 @@ +#nim c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim + +import math, hashes + +#------------------------------------------------------------------------------ +## Memory Utility Functions + +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 new file mode 100644 index 000000000..d2a8dd085 --- /dev/null +++ b/lib/deprecated/pure/events.nim @@ -0,0 +1,102 @@ +# +# +# 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) + +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 deleted file mode 100644 index 3f7b63a30..000000000 --- a/lib/deprecated/pure/future.nim +++ /dev/null @@ -1,6 +0,0 @@ -## 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 b57839d1a..34d452fd2 100644 --- a/lib/deprecated/pure/ospaths.nim +++ b/lib/deprecated/pure/ospaths.nim @@ -7,9 +7,8 @@ # distribution, for details about the copyright. # -## This module is deprecated since 0.20.0, `import std/os` instead. - -{.deprecated: "use `std/os` instead".} +## This module is deprecated, ``import os`` instead. +{.deprecated: "import os.nim instead".} import os export ReadEnvEffect, WriteEnvEffect, ReadDirEffect, WriteDirEffect, OSErrorCode, diff --git a/lib/deprecated/pure/parseopt2.nim b/lib/deprecated/pure/parseopt2.nim new file mode 100644 index 000000000..e57b7e2c9 --- /dev/null +++ b/lib/deprecated/pure/parseopt2.nim @@ -0,0 +1,155 @@ +# +# +# 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*: TaintedString ## 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 = TaintedString(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): TaintedString {.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: TaintedString] + +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 b4749ad75..c6cde599a 100644 --- a/lib/deprecated/pure/securehash.nim +++ b/lib/deprecated/pure/securehash.nim @@ -1,6 +1,6 @@ -## This module is a deprecated alias for the `sha1` module. Deprecated since 0.18.1. -{.deprecated: "use `std/sha1` instead".} -import "../std/sha1" -export sha1 +## This module is a deprecated alias for the ``sha1`` module. +{.deprecated.} + +include "../std/sha1" diff --git a/lib/deprecated/pure/sharedstrings.nim b/lib/deprecated/pure/sharedstrings.nim new file mode 100644 index 000000000..29cb0fcc2 --- /dev/null +++ b/lib/deprecated/pure/sharedstrings.nim @@ -0,0 +1,152 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Shared string support for Nim. + +type + UncheckedCharArray = UncheckedArray[char] + +type + Buffer = ptr object + refcount: int + capacity, realLen: int + data: UncheckedCharArray + + SharedString* = object ## A string that can be shared. Slicing is O(1). + buffer: Buffer + first, len: int + +proc decRef(b: Buffer) {.inline.} = + if atomicDec(b.refcount) <= 0: + deallocShared(b) + +proc incRef(b: Buffer) {.inline.} = + atomicInc(b.refcount) + +{.experimental.} + +proc `=destroy`*(s: SharedString) = + #echo "destroyed" + if not s.buffer.isNil: + decRef(s.buffer) + +when false: + proc `=`*(dest: var SharedString; src: SharedString) = + incRef(src.buffer) + if not dest.buffer.isNil: + decRef(dest.buffer) + dest.buffer = src.buffer + dest.first = src.first + dest.len = src.len + +proc len*(s: SharedString): int = s.len + +proc `[]`*(s: SharedString; i: Natural): char = + if i < s.len: result = s.buffer.data[i+s.first] + else: raise newException(IndexDefect, formatErrorIndexBound(i, s.len-1)) + +proc `[]=`*(s: var SharedString; i: Natural; value: char) = + if i < s.len: s.buffer.data[i+s.first] = value + else: raise newException(IndexDefect, formatErrorIndexBound(i, s.len-1)) + +proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString = + #incRef(src.buffer) + if ab.a < s.len: + result.buffer = s.buffer + result.first = ab.a + result.len = min(s.len, ab.b - ab.a + 1) + # else: produce empty string ;-) + +proc newBuffer(cap, len: int): Buffer = + assert cap >= len + result = cast[Buffer](allocShared0(sizeof(int)*3 + cap)) + result.refcount = 0 + result.capacity = cap + result.realLen = len + +proc newSharedString*(len: Natural): SharedString = + if len != 0: + # optimization: Don't have an underlying buffer when 'len == 0' + result.buffer = newBuffer(len, len) + result.first = 0 + result.len = len + +proc newSharedString*(s: string): SharedString = + let len = s.len + if len != 0: + # optimization: Don't have an underlying buffer when 'len == 0' + result.buffer = newBuffer(len, len) + copyMem(addr result.buffer.data[0], cstring(s), s.len) + result.first = 0 + result.len = len + +when declared(atomicLoadN): + template load(x): untyped = atomicLoadN(addr x, ATOMIC_SEQ_CST) +else: + # XXX Fixme + template load(x): untyped = x + +proc add*(s: var SharedString; t: cstring; len: Natural) = + if len == 0: return + let newLen = s.len + len + if s.buffer.isNil: + s.buffer = newBuffer(len, len) + copyMem(addr s.buffer.data[0], t, len) + s.len = len + elif newLen >= s.buffer.capacity or s.first != 0 or + s.len != s.buffer.realLen or load(s.buffer.refcount) > 1: + let oldBuf = s.buffer + s.buffer = newBuffer(max(s.buffer.capacity * 3 div 2, newLen), newLen) + copyMem(addr s.buffer.data[0], addr oldBuf.data[s.first], s.len) + copyMem(addr s.buffer.data[s.len], t, len) + decRef(oldBuf) + else: + copyMem(addr s.buffer.data[s.len], t, len) + s.buffer.realLen += len + s.len += len + +proc add*(s: var SharedString; t: string) = + s.add(t.cstring, t.len) + +proc rawData*(s: var SharedString): pointer = + if s.buffer.isNil: result = nil + else: result = addr s.buffer.data[s.first] + +proc add*(s: var SharedString; t: SharedString) = + if t.buffer.isNil: return + s.add(cast[cstring](addr s.buffer.data[s.first]), t.len) + +proc `$`*(s: SharedString): string = + result = newString(s.len) + if s.len > 0: + copyMem(addr result[0], addr s.buffer.data[s.first], s.len) + +proc `==`*(s: SharedString; t: string): bool = + if s.buffer.isNil: result = t.len == 0 + else: result = t.len == s.len and equalMem(addr s.buffer.data[s.first], + cstring(t), t.len) + +proc `==`*(s, t: SharedString): bool = + if s.buffer.isNil: result = t.len == 0 + else: result = t.len == s.len and equalMem(addr s.buffer.data[s.first], + addr t.buffer.data[t.first], t.len) + +iterator items*(s: SharedString): char = + let buf = s.buffer.data + let x = s.first + if buf != nil: + for i in 0..`_ -runnableExamples: - assert diffInt( - [0, 1, 2, 3, 4, 5, 6, 7, 8], - [-1, 1, 2, 3, 4, 5, 666, 7, 42]) == - @[Item(startA: 0, startB: 0, deletedA: 1, insertedB: 1), - Item(startA: 6, startB: 6, deletedA: 1, insertedB: 1), - Item(startA: 8, startB: 8, deletedA: 1, insertedB: 1)] - -runnableExamples: - # 2 samples of text (from "The Call of Cthulhu" by Lovecraft) - let txt0 = """ -abc -def ghi -jkl2""" - let txt1 = """ -bacx -abc -def ghi -jkl""" - assert diffText(txt0, txt1) == - @[Item(startA: 0, startB: 0, deletedA: 0, insertedB: 1), - Item(startA: 2, startB: 3, deletedA: 1, insertedB: 1)] - # code owner: Arne Döring # # This is based on C# code written by Matthias Hertel, http://www.mathertel.de @@ -45,9 +42,6 @@ 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. @@ -65,7 +59,7 @@ type Smsrd = object x, y: int -# template to avoid a seq copy. Required until `sink` parameters are ready. +# template to avoid a seq copy. Required until ``sink`` parameters are ready. template newDiffData(initData: seq[int]; L: int): DiffData = DiffData( data: initData, @@ -77,9 +71,9 @@ proc len(d: DiffData): int {.inline.} = d.data.len proc diffCodes(aText: string; h: var Table[string, int]): DiffData = ## This function converts all textlines of the text into unique numbers for every unique textline ## so further work can work only with simple numbers. - ## `aText` the input text - ## `h` This extern initialized hashtable is used for storing all ever used textlines. - ## `trimSpace` ignore leading and trailing space characters + ## ``aText`` the input text + ## ``h`` This extern initialized hashtable is used for storing all ever used textlines. + ## ``trimSpace`` ignore leading and trailing space characters ## Returns a array of integers. var lastUsedCode = h.len result.data = newSeq[int]() @@ -114,14 +108,14 @@ proc optimize(data: var DiffData) = proc sms(dataA: var DiffData; lowerA, upperA: int; dataB: DiffData; lowerB, upperB: int; downVector, upVector: var openArray[int]): Smsrd = ## This is the algorithm to find the Shortest Middle Snake (sms). - ## `dataA` sequence A - ## `lowerA` lower bound of the actual range in dataA - ## `upperA` upper bound of the actual range in dataA (exclusive) - ## `dataB` sequence B - ## `lowerB` lower bound of the actual range in dataB - ## `upperB` upper bound of the actual range in dataB (exclusive) - ## `downVector` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons. - ## `upVector` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons. + ## ``dataA`` sequence A + ## ``lowerA`` lower bound of the actual range in dataA + ## ``upperA`` upper bound of the actual range in dataA (exclusive) + ## ``dataB`` sequence B + ## ``lowerB`` lower bound of the actual range in dataB + ## ``upperB`` upper bound of the actual range in dataB (exclusive) + ## ``downVector`` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons. + ## ``upVector`` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons. ## Returns a MiddleSnakeData record containing x,y and u,v. let max = dataA.len + dataB.len + 1 @@ -201,14 +195,14 @@ proc lcs(dataA: var DiffData; lowerA, upperA: int; dataB: var DiffData; lowerB, ## algorithm. ## The published algorithm passes recursively parts of the A and B sequences. ## To avoid copying these arrays the lower and upper bounds are passed while the sequences stay constant. - ## `dataA` sequence A - ## `lowerA` lower bound of the actual range in dataA - ## `upperA` upper bound of the actual range in dataA (exclusive) - ## `dataB` sequence B - ## `lowerB` lower bound of the actual range in dataB - ## `upperB` upper bound of the actual range in dataB (exclusive) - ## `downVector` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons. - ## `upVector` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons. + ## ``dataA`` sequence A + ## ``lowerA`` lower bound of the actual range in dataA + ## ``upperA`` upper bound of the actual range in dataA (exclusive) + ## ``dataB`` sequence B + ## ``lowerB`` lower bound of the actual range in dataB + ## ``upperB`` upper bound of the actual range in dataB (exclusive) + ## ``downVector`` a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons. + ## ``upVector`` a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons. # make mutable copy: var lowerA = lowerA @@ -281,9 +275,9 @@ proc createDiffs(dataA, dataB: DiffData): seq[Item] = proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] = ## Find the difference in 2 arrays of integers. ## - ## `arrayA` A-version of the numbers (usually the old one) + ## ``arrayA`` A-version of the numbers (usually the old one) ## - ## `arrayB` B-version of the numbers (usually the new one) + ## ``arrayB`` B-version of the numbers (usually the new one) ## ## Returns a sequence of Items that describe the differences. @@ -294,9 +288,9 @@ proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] = var dataB = newDiffData(@arrayB, arrayB.len) let max = dataA.len + dataB.len + 1 - # vector for the (0,0) to (x,y) search + ## vector for the (0,0) to (x,y) search var downVector = newSeq[int](2 * max + 2) - # vector for the (u,v) to (N,M) search + ## vector for the (u,v) to (N,M) search var upVector = newSeq[int](2 * max + 2) lcs(dataA, 0, dataA.len, dataB, 0, dataB.len, downVector, upVector) @@ -310,12 +304,12 @@ proc diffText*(textA, textB: string): seq[Item] = ## textlines into a common hashtable so i can find duplicates in there, and generating a ## new number each time a new textline is inserted. ## - ## `textA` A-version of the text (usually the old one) + ## ``textA`` A-version of the text (usually the old one) ## - ## `textB` B-version of the text (usually the new one) + ## ``textB`` B-version of the text (usually the new one) ## ## Returns a seq of Items that describe the differences. - # See also `gitutils.diffStrings`. + # prepare the input-text and convert to comparable numbers. var h = initTable[string, int]() # TextA.len + TextB.len <- probably wrong initial size # The A-Version of the data (original data) to be compared. @@ -327,9 +321,9 @@ proc diffText*(textA, textB: string): seq[Item] = h.clear # free up hashtable memory (maybe) let max = dataA.len + dataB.len + 1 - # vector for the (0,0) to (x,y) search + ## vector for the (0,0) to (x,y) search var downVector = newSeq[int](2 * max + 2) - # vector for the (u,v) to (N,M) search + ## vector for the (u,v) to (N,M) search var upVector = newSeq[int](2 * max + 2) lcs(dataA, 0, dataA.len, dataB, 0, dataB.len, downVector, upVector) diff --git a/lib/genode/alloc.nim b/lib/genode/alloc.nim index 24fb9954e..a21a3ad7b 100644 --- a/lib/genode/alloc.nim +++ b/lib/genode/alloc.nim @@ -15,20 +15,20 @@ when not defined(genode): {.error: "Genode only module".} when not declared(GenodeEnv): - import genode/env + include genode/env -type RamDataspaceCapability {. - importcpp: "Genode::Ram_dataspace_capability", pure.} = object +type DataspaceCapability {. + importcpp: "Genode::Dataspace_capability", pure.} = object type Map = object attachment: pointer size: int - ds: RamDataspaceCapability + ds: DataspaceCapability SlabMeta = object next: ptr MapSlab - ds: RamDataspaceCapability + ds: DataspaceCapability MapSlab = object meta: SlabMeta @@ -45,11 +45,11 @@ proc capsAvail(env: GenodeEnv): int {. ## Return the number of available capabilities. ## Each dataspace allocation consumes a capability. -proc allocDataspace(env: GenodeEnv; size: int): RamDataspaceCapability {. +proc allocDataspace(env: GenodeEnv; size: int): DataspaceCapability {. importcpp: "#->pd().alloc(@)".} ## Allocate a dataspace and its capability. -proc attachDataspace(env: GenodeEnv; ds: RamDataspaceCapability): pointer {. +proc attachDataspace(env: GenodeEnv; ds: DataspaceCapability): pointer {. importcpp: "#->rm().attach(@)".} ## Attach a dataspace into the component address-space. @@ -57,7 +57,7 @@ proc detachAddress(env: GenodeEnv; p: pointer) {. importcpp: "#->rm().detach(@)".} ## Detach a dataspace from the component address-space. -proc freeDataspace(env: GenodeEnv; ds: RamDataspaceCapability) {. +proc freeDataspace(env: GenodeEnv; ds: DataspaceCapability) {. importcpp: "#->pd().free(@)".} ## Free a dataspace. @@ -111,7 +111,7 @@ proc osDeallocPages(p: pointer; size: int) = if m.attachment == p: if m.size != size: echo "cannot partially detach dataspace" - rawQuit -1 + quit -1 runtimeEnv.detachAddress m.attachment runtimeEnv.freeDataspace m.ds m[] = Map() diff --git a/lib/genode/constructibles.nim b/lib/genode/constructibles.nim deleted file mode 100644 index 3a4a646e0..000000000 --- a/lib/genode/constructibles.nim +++ /dev/null @@ -1,21 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2022 Emery Hemingway -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -type Constructible*[T] {. - importcpp: "Genode::Constructible", - header: "", byref, pure.} = object - -proc construct*[T](x: Constructible[T]) {.importcpp.} - ## Construct a constructible C++ object. - -proc destruct*[T](x: Constructible[T]) {.importcpp.} - ## Destruct a constructible C++ object. - -proc constructed*[T](x: Constructible[T]): bool {.importcpp.} - ## Test if an object is constructed. diff --git a/lib/genode/entrypoints.nim b/lib/genode/entrypoints.nim deleted file mode 100644 index 0bf5e0e0e..000000000 --- a/lib/genode/entrypoints.nim +++ /dev/null @@ -1,22 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2022 Emery Hemingway -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## See `Genode Foundations - Entrypoint ` -## for a description of Entrypoints. - -type - EntrypointObj {. - importcpp: "Genode::Entrypoint", - header: "", - pure.} = object - Entrypoint* = ptr EntrypointObj - ## Opaque Entrypoint object. - -proc ep*(env: GenodeEnv): Entrypoint {.importcpp: "(&#->ep())".} - ## Access the entrypoint associated with `env`. diff --git a/lib/genode/env.nim b/lib/genode/env.nim index babe2a8a0..2b180d1b3 100644 --- a/lib/genode/env.nim +++ b/lib/genode/env.nim @@ -11,19 +11,19 @@ # This file contains the minimum required definitions # for interacting with the initial Genode environment. # It is reserved for use only within the standard -# library. See `componentConstructHook` in the system +# library. See ``componentConstructHook`` in the system # module for accessing the Genode environment after the # standard library has finished initializating. # when not defined(genode): - {.error: "Genode only module".} + {.error: "Genode only include".} type - GenodeEnvObj* {.importcpp: "Genode::Env", header: "", pure.} = object - GenodeEnvPtr* = ptr GenodeEnvObj + GenodeEnvObj {.importcpp: "Genode::Env", header: "", pure.} = object + GenodeEnvPtr = ptr GenodeEnvObj -const runtimeEnvSym* = "nim_runtime_env" +const runtimeEnvSym = "nim_runtime_env" when not defined(nimscript): - var runtimeEnv* {.importcpp: runtimeEnvSym.}: GenodeEnvPtr + var runtimeEnv {.importcpp: runtimeEnvSym.}: GenodeEnvPtr diff --git a/lib/genode/signals.nim b/lib/genode/signals.nim deleted file mode 100644 index 7d1875730..000000000 --- a/lib/genode/signals.nim +++ /dev/null @@ -1,77 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2022 Emery Hemingway -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## See `Genode Foundations - Asynchronous notifications ` -## for a description of Genode signals. - -when not defined(genode) or defined(nimdoc): - {.error: "Genode only module".} - -import ./entrypoints, ./constructibles - -export ep # Entrypoint accessor on GenodeEnv - -type - SignalContextCapability* {. - importcpp: "Genode::Signal_context_capability", - header: "", pure.} = object - ## Capability to an asynchronous signal context. - -proc isValid*(cap: SignalContextCapability): bool {.importcpp: "#.valid()".} - ## Call the Genode core to check if this `SignalContextCapability` is valid. - # TODO: RpcEffect - -type - HandlerProc = proc () {.closure, gcsafe.} - - SignalHandlerBase {. - importcpp: "Nim::SignalHandler", - header: "genode_cpp/signals.h", - pure.} = object - - SignalHandlerCpp = Constructible[SignalHandlerBase] - - SignalHandlerObj = object - cpp: SignalHandlerCpp - cb: HandlerProc - ## Signal handling procedure called during dispatch. - - SignalHandler* = ref SignalHandlerObj - ## Nim object enclosing a Genode signal handler. - -proc construct(cpp: SignalHandlerCpp; ep: Entrypoint; sh: SignalHandler) {.importcpp.} - -proc cap(cpp: SignalHandlerCpp): SignalContextCapability {.importcpp: "#->cap()".} - -proc newSignalHandler*(ep: Entrypoint; cb: HandlerProc): SignalHandler = - ## Create a new signal handler. A label is recommended for - ## debugging purposes. A signal handler will not be garbage - ## collected until after it has been dissolved. - result = SignalHandler(cb: cb) - result.cpp.construct(ep, result) - GCref result - -proc dissolve*(sig: SignalHandler) = - ## Dissolve signal dispatcher from entrypoint. - # TODO: =destroy? - destruct sig.cpp - sig.cb = nil # lose the callback - GCunref sig - -proc cap*(sig: SignalHandler): SignalContextCapability = - ## Signal context capability. Can be delegated to external components. - sig.cpp.cap - -proc submit*(cap: SignalContextCapability) {. - importcpp: "Genode::Signal_transmitter(#).submit()".} - ## Submit a signal to a context capability. - -proc nimHandleSignal(p: pointer) {.exportc.} = - ## C symbol invoked by entrypoint during signal dispatch. - cast[SignalHandler](p).cb() diff --git a/lib/genode_cpp/signals.h b/lib/genode_cpp/signals.h deleted file mode 100644 index fa3975d38..000000000 --- a/lib/genode_cpp/signals.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Nim's Runtime Library - * (c) Copyright 2022 Emery Hemingway - * - * See the file "copying.txt", included in this - * distribution, for details about the copyright. - * - */ - -#ifndef _NIM_SIGNALS_H_ -#define _NIM_SIGNALS_H_ - -#include -#include -#include - -// Symbol for calling back into Nim -extern "C" void nimHandleSignal(void *arg); - -namespace Nim { struct SignalHandler; } - -struct Nim::SignalHandler -{ - // Pointer to the Nim handler object. - void *arg; - - void handle_signal() { - Libc::with_libc([this] () { nimHandleSignal(arg); }); } - - Genode::Signal_handler handler; - - SignalHandler(Genode::Entrypoint *ep, void *arg) - : arg(arg), handler(*ep, *this, &SignalHandler::handle_signal) { } - - Genode::Signal_context_capability cap() { return handler; } -}; - -#endif diff --git a/lib/genode_cpp/syslocks.h b/lib/genode_cpp/syslocks.h index b5d5ae694..8ba39abc2 100644 --- a/lib/genode_cpp/syslocks.h +++ b/lib/genode_cpp/syslocks.h @@ -13,7 +13,7 @@ /* Genode includes */ #include -#include +#include namespace Nim { struct SysLock; @@ -22,14 +22,15 @@ namespace Nim { struct Nim::SysLock { - Genode::Mutex _mutex_a, _mutex_b; + Genode::Lock _lock_a, _lock_b; bool _locked; void acquireSys() { - Genode::Mutex::Guard guard(_mutex_a); + _lock_a.lock(); _locked = true; - _mutex_b.acquire(); + _lock_a.unlock(); + _lock_b.lock(); } bool tryAcquireSys() @@ -37,22 +38,23 @@ struct Nim::SysLock if (_locked) return false; - Genode::Mutex::Guard guard(_mutex_a); - + _lock_a.lock(); if (_locked) { + _lock_a.unlock(); return false; } else { _locked = true; - _mutex_b.acquire(); + _lock_b.lock(); + _lock_a.unlock(); return true; } } void releaseSys() { - Genode::Mutex::Guard guard(_mutex_a); _locked = false; - _mutex_b.release(); + _lock_a.unlock(); + _lock_b.unlock(); } }; @@ -71,11 +73,6 @@ struct Nim::SysCond { _semaphore.up(); } - - void broadcastSysCond() - { - _semaphore.up(); - } }; #endif diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim new file mode 100644 index 000000000..f0d9d8540 --- /dev/null +++ b/lib/impure/db_mysql.nim @@ -0,0 +1,424 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## A higher level `mySQL`:idx: database wrapper. The same interface is +## implemented for other databases too. +## +## See also: `db_odbc `_, `db_sqlite `_, +## `db_postgres `_. +## +## Parameter substitution +## ====================== +## +## 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 (?, ?, ?)" +## +## +## Examples +## ======== +## +## Opening a connection to a database +## ---------------------------------- +## +## .. code-block:: Nim +## import 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)""")) +## +## Inserting data +## -------------- +## +## .. code-block:: Nim +## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)", +## "Dominik") +## +## Larger example +## -------------- +## +## .. code-block:: Nim +## +## import db_mysql, math +## +## 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"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 +## +## 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() + + +import strutils, mysql + +import db_common +export db_common + +import std/private/since + +type + DbConn* = distinct PMySQL ## encapsulates a database connection + Row* = seq[string] ## a row of a dataset. NULL database values will be + ## converted to nil. + InstantRow* = object ## a handle that can be used to get a row's + ## column text on demand + row: cstringArray + len: int + +proc dbError*(db: DbConn) {.noreturn.} = + ## raises a DbError exception. + var e: ref DbError + new(e) + e.msg = $mysql.error(PMySQL db) + raise e + +when false: + proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) = + var stmt = mysql_stmt_init(db) + if stmt == nil: dbError(db) + if mysql_stmt_prepare(stmt, query, len(query)) != 0: + dbError(db) + var + binding: seq[MYSQL_BIND] + discard mysql_stmt_close(stmt) + +proc dbQuote*(s: string): string = + ## DB quotes the string. + result = newStringOfCap(s.len + 2) + result.add "'" + for c in items(s): + # see https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html#mysql-escaping + case c + of '\0': result.add "\\0" + of '\b': result.add "\\b" + of '\t': result.add "\\t" + of '\l': result.add "\\n" + of '\r': result.add "\\r" + of '\x1a': result.add "\\Z" + 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) + +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.realQuery(PMySQL db, q, q.len) == 0'i32 + +proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) = + var q = dbFormat(query, args) + if mysql.realQuery(PMySQL db, q, 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.realQuery(PMySQL db, q, q.len) != 0'i32: dbError(db) + +proc newRow(L: int): Row = + newSeq(result, L) + for i in 0..L-1: result[i] = "" + +proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) = + if row != nil: + while mysql.fetchRow(sqlres) != nil: discard + mysql.freeResult(sqlres) + +iterator fastRows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + ## executes the query and iterates over the result dataset. + ## + ## This is very fast, but potentially dangerous. Use this iterator only + ## 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``. + rawExec(db, query, args) + var sqlres = mysql.useResult(PMySQL db) + if sqlres != nil: + var + L = int(mysql.numFields(sqlres)) + row: cstringArray + result: Row + backup: Row + newSeq(result, L) + while true: + row = mysql.fetchRow(sqlres) + if row == nil: break + for i in 0..L-1: + setLen(result[i], 0) + result[i].add row[i] + yield result + properFreeResult(sqlres, row) + +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. + rawExec(db, query, args) + var sqlres = mysql.useResult(PMySQL db) + if sqlres != nil: + let L = int(mysql.numFields(sqlres)) + var row: cstringArray + while true: + row = mysql.fetchRow(sqlres) + if row == nil: break + yield InstantRow(row: row, len: L) + properFreeResult(sqlres, row) + +proc setTypeName(t: var DbType; f: PFIELD) = + t.name = $f.name + t.maxReprLen = Natural(f.max_length) + if (NOT_NULL_FLAG and f.flags) != 0: t.notNull = true + case f.ftype + of TYPE_DECIMAL: + t.kind = dbDecimal + of TYPE_TINY: + t.kind = dbInt + t.size = 1 + of TYPE_SHORT: + t.kind = dbInt + t.size = 2 + of TYPE_LONG: + t.kind = dbInt + t.size = 4 + of TYPE_FLOAT: + t.kind = dbFloat + t.size = 4 + of TYPE_DOUBLE: + t.kind = dbFloat + t.size = 8 + of TYPE_NULL: + t.kind = dbNull + of TYPE_TIMESTAMP: + t.kind = dbTimestamp + of TYPE_LONGLONG: + t.kind = dbInt + t.size = 8 + of TYPE_INT24: + t.kind = dbInt + t.size = 3 + of TYPE_DATE: + t.kind = dbDate + of TYPE_TIME: + t.kind = dbTime + of TYPE_DATETIME: + t.kind = dbDatetime + of TYPE_YEAR: + t.kind = dbDate + of TYPE_NEWDATE: + t.kind = dbDate + of TYPE_VARCHAR, TYPE_VAR_STRING, TYPE_STRING: + t.kind = dbVarchar + of TYPE_BIT: + t.kind = dbBit + of TYPE_NEWDECIMAL: + t.kind = dbDecimal + of TYPE_ENUM: t.kind = dbEnum + of TYPE_SET: t.kind = dbSet + of TYPE_TINY_BLOB, TYPE_MEDIUM_BLOB, TYPE_LONG_BLOB, + TYPE_BLOB: t.kind = dbBlob + of TYPE_GEOMETRY: + t.kind = dbGeometry + +proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) = + setLen(columns, L) + for i in 0..`_, `db_sqlite `_, +## `db_mysql `_. +## +## Parameter substitution +## ====================== +## +## 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 (?, ?, ?)" +## +## +## Examples +## ======== +## +## Opening a connection to a database +## ---------------------------------- +## +## .. code-block:: Nim +## import db_odbc +## var 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)""")) +## +## Inserting data +## -------------- +## +## .. code-block:: Nim +## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)", +## "Andreas") +## +## Large example +## ------------- +## +## .. code-block:: Nim +## +## import db_odbc, math +## +## var 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"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 +## +## 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() + +import strutils, odbcsql +import db_common +export db_common + +import std/private/since + +type + OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt] + DbConn* = OdbcConnTyp ## encapsulates a database connection + Row* = seq[string] ## a row of a dataset. NULL database values will be + ## converted to nil. + InstantRow* = tuple[row: seq[string], len: int] ## a handle that can be + ## used to get a row's + ## column text on demand + +var + buf: array[0..4096, char] + +proc properFreeResult(hType: int, sqlres: var SqlHandle) {. + tags: [WriteDbEffect], raises: [].} = + try: + discard SQLFreeHandle(hType.TSqlSmallInt, sqlres) + sqlres = nil + except: discard + +proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {. + tags: [ReadDbEffect], raises: [].} = + ## Returns ODBC error information + var + sqlState: array[0..512, char] + nativeErr: array[0..512, char] + errMsg: array[0..512, char] + retSz: TSqlSmallInt = 0 + res: TSqlSmallInt = 0 + try: + sqlState[0] = '\0' + nativeErr[0] = '\0' + errMsg[0] = '\0' + res = SQLErr(db.env, db.hDb, db.stmt, + cast[PSQLCHAR](sqlState.addr), + cast[PSQLCHAR](nativeErr.addr), + cast[PSQLCHAR](errMsg.addr), + 511.TSqlSmallInt, retSz.addr) + except: + discard + return (res.int, $(addr sqlState), $(addr nativeErr), $(addr errMsg)) + +proc dbError*(db: var DbConn) {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} = + ## Raises an `[DbError]` exception with ODBC error information + var + e: ref DbError + ss, ne, msg: string = "" + isAnError = false + res: int = 0 + prevSs = "" + while true: + prevSs = ss + (res, ss, ne, msg) = db.getErrInfo() + if prevSs == ss: + break + # sqlState of 00000 is not an error + elif ss == "00000": + break + elif ss == "01000": + echo "\nWarning: ", ss, " ", msg + continue + else: + isAnError = true + echo "\nError: ", ss, " ", msg + if isAnError: + new(e) + e.msg = "ODBC Error" + if db.stmt != nil: + properFreeResult(SQL_HANDLE_STMT, db.stmt) + properFreeResult(SQL_HANDLE_DBC, db.hDb) + properFreeResult(SQL_HANDLE_ENV, db.env) + 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 + if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db) + +proc sqlGetDBMS(db: var DbConn): string {. + tags: [ReadDbEffect, WriteDbEffect], raises: [] .} = + ## Returns the ODBC SQL_DBMS_NAME string + const + SQL_DBMS_NAME = 17.SqlUSmallInt + var + sz: TSqlSmallInt = 0 + buf[0] = '\0' + try: + db.sqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr), + 4095.TSqlSmallInt, sz.addr)) + except: discard + return $(addr buf) + +proc dbQuote*(s: string): string {.noSideEffect.} = + ## DB quotes the string. + result = "'" + for c in items(s): + if c == '\'': add(result, "''") + else: add(result, c) + add(result, '\'') + +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) + +proc prepareFetch(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]): TSqlSmallInt {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + # Prepare a statement, execute it and fetch the data to the driver + # ready for retrieval of the data + # Used internally by iterators and retrieval procs + # requires calling + # properFreeResult(SQL_HANDLE_STMT, db.stmt) + # when finished + db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) + var q = dbFormat(query, args) + db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) + db.sqlCheck(SQLExecute(db.stmt)) + result = SQLFetch(db.stmt) + db.sqlCheck(result) + +proc prepareFetchDirect(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]) {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + # Prepare a statement, execute it and fetch the data to the driver + # ready for retrieval of the data + # Used internally by iterators and retrieval procs + # requires calling + # properFreeResult(SQL_HANDLE_STMT, db.stmt) + # when finished + db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt)) + var q = dbFormat(query, args) + db.sqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) + db.sqlCheck(SQLFetch(db.stmt)) + +proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {. + tags: [ReadDbEffect, WriteDbEffect], raises: [].} = + ## Tries to execute the query and returns true if successful, false otherwise. + var + res:TSqlSmallInt = -1 + try: + db.prepareFetchDirect(query, args) + var + rCnt:TSqlLen = -1 + res = SQLRowCount(db.stmt, rCnt) + properFreeResult(SQL_HANDLE_STMT, db.stmt) + if res != SQL_SUCCESS: dbError(db) + except: discard + return res == SQL_SUCCESS + +proc rawExec(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + db.prepareFetchDirect(query, args) + +proc exec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + ## Executes the query and raises EDB if not successful. + db.prepareFetchDirect(query, args) + properFreeResult(SQL_HANDLE_STMT, db.stmt) + +proc newRow(L: int): Row {.noSideEFfect.} = + newSeq(result, L) + for i in 0..L-1: result[i] = "" + +iterator fastRows*(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + ## Executes the query and iterates over the result dataset. + ## + ## This is very fast, but potentially dangerous. Use this iterator only + ## if you require **ALL** the rows. + ## + ## Breaking the fastRows() iterator during a loop may cause a driver error + ## for subsequent queries + ## + ## Rows are retrieved from the server at each iteration. + var + rowRes: Row + sz: TSqlLen = 0 + cCnt: TSqlSmallInt = 0 + res: TSqlSmallInt = 0 + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + discard + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + while res == SQL_SUCCESS: + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095, sz.addr)) + rowRes[colId-1] = $(addr buf) + yield rowRes + res = SQLFetch(db.stmt) + properFreeResult(SQL_HANDLE_STMT, db.stmt) + db.sqlCheck(res) + +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. + var + rowRes: Row = @[] + sz: TSqlLen = 0 + cCnt: TSqlSmallInt = 0 + res: TSqlSmallInt = 0 + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + discard + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + while res == SQL_SUCCESS: + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095, sz.addr)) + rowRes[colId-1] = $(addr buf) + yield (row: rowRes, len: cCnt.int) + res = SQLFetch(db.stmt) + properFreeResult(SQL_HANDLE_STMT, db.stmt) + db.sqlCheck(res) + +proc `[]`*(row: InstantRow, col: int): string {.inline.} = + ## Returns text for given column of the row + $row.row[col] + +proc unsafeColumnAt*(row: InstantRow, index: int): cstring {.inline.} = + ## Return cstring of given column of the row + row.row[index] + +proc len*(row: InstantRow): int {.inline.} = + ## Returns number of columns in the row + row.len + +proc getRow*(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + ## Retrieves a single row. If the query doesn't return any rows, this proc + ## will return a Row with empty strings for each column. + var + rowRes: Row + sz: TSqlLen = 0 + cCnt: TSqlSmallInt = 0 + res: TSqlSmallInt = 0 + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + result = @[] + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095, sz.addr)) + rowRes[colId-1] = $(addr buf) + res = SQLFetch(db.stmt) + result = rowRes + properFreeResult(SQL_HANDLE_STMT, db.stmt) + db.sqlCheck(res) + +proc getAllRows*(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]): seq[Row] {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} = + ## Executes the query and returns the whole result dataset. + var + rows: seq[Row] = @[] + rowRes: Row + sz: TSqlLen = 0 + cCnt: TSqlSmallInt = 0 + res: TSqlSmallInt = 0 + res = db.prepareFetch(query, args) + if res == SQL_NO_DATA: + result = @[] + elif res == SQL_SUCCESS: + res = SQLNumResultCols(db.stmt, cCnt) + rowRes = newRow(cCnt) + rowRes.setLen(max(cCnt,0)) + while res == SQL_SUCCESS: + for colId in 1..cCnt: + buf[0] = '\0' + db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, + cast[cstring](buf.addr), 4095, sz.addr)) + rowRes[colId-1] = $(addr buf) + rows.add(rowRes) + res = SQLFetch(db.stmt) + result = rows + properFreeResult(SQL_HANDLE_STMT, db.stmt) + db.sqlCheck(res) + +iterator rows*(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + ## Same as `fastRows`, but slower and safe. + ## + ## This retrieves ALL rows into memory before + ## iterating through the rows. + ## Large dataset queries will impact on memory usage. + for r in items(getAllRows(db, query, args)): yield r + +proc getValue*(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]): string {. + tags: [ReadDbEffect, WriteDbEffect], raises: [].} = + ## Executes the query and returns the first column of the first row of the + ## result dataset. Returns "" if the dataset contains no rows or the database + ## value is NULL. + result = "" + try: + result = getRow(db, query, args)[0] + except: discard + +proc tryInsertId*(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]): int64 {. + tags: [ReadDbEffect, WriteDbEffect], raises: [].} = + ## Executes the query (typically "INSERT") and returns the + ## generated ID for the row or -1 in case of an error. + if not tryExec(db, query, args): + result = -1'i64 + else: + result = -1'i64 + try: + case sqlGetDBMS(db).toLower(): + of "postgresql": + result = getValue(db, sql"SELECT LASTVAL();", []).parseInt + of "mysql": + result = getValue(db, sql"SELECT LAST_INSERT_ID();", []).parseInt + of "sqlite": + result = getValue(db, sql"SELECT LAST_INSERT_ROWID();", []).parseInt + of "microsoft sql server": + result = getValue(db, sql"SELECT SCOPE_IDENTITY();", []).parseInt + of "oracle": + result = getValue(db, sql"SELECT id.currval FROM DUAL;", []).parseInt + else: result = -1'i64 + except: discard + +proc insertId*(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]): int64 {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + ## Executes the query (typically "INSERT") and returns the + ## generated ID for the row. + result = tryInsertID(db, query, args) + if result < 0: dbError(db) + +proc tryInsert*(db: var DbConn, query: SqlQuery,pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [ReadDbEffect, WriteDbEffect], raises: [], since: (1, 3).} = + ## same as tryInsertID + tryInsertID(db, query, args) + +proc insert*(db: var DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [ReadDbEffect, WriteDbEffect], since: (1, 3).} = + ## same as insertId + result = tryInsert(db, query,pkName, args) + if result < 0: dbError(db) + +proc execAffectedRows*(db: var DbConn, query: SqlQuery, + args: varargs[string, `$`]): int64 {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + ## Runs the query (typically "UPDATE") and returns the + ## number of affected rows + result = -1 + db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle)) + var q = dbFormat(query, args) + db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt)) + rawExec(db, query, args) + var rCnt:TSqlLen = -1 + db.sqlCheck(SQLRowCount(db.hDb, rCnt)) + properFreeResult(SQL_HANDLE_STMT, db.stmt) + result = rCnt.int64 + +proc close*(db: var DbConn) {. + tags: [WriteDbEffect], raises: [].} = + ## Closes the database connection. + if db.hDb != nil: + try: + var res = SQLDisconnect(db.hDb) + if db.stmt != nil: + res = SQLFreeHandle(SQL_HANDLE_STMT, db.stmt) + res = SQLFreeHandle(SQL_HANDLE_DBC, db.hDb) + res = SQLFreeHandle(SQL_HANDLE_ENV, db.env) + db = (hDb: nil, env: nil, stmt: nil) + except: + discard + +proc open*(connection, user, password, database: string): DbConn {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + ## Opens a database connection. + ## + ## Raises `EDb` if the connection could not be established. + ## + ## Currently the database parameter is ignored, + ## but included to match ``open()`` in the other db_xxxxx library modules. + var + val = SQL_OV_ODBC3 + resLen = 0 + result = (hDb: nil, env: nil, stmt: nil) + # allocate environment handle + var res = SQLAllocHandle(SQL_HANDLE_ENV, result.env, result.env) + if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.") + res = SQLSetEnvAttr(result.env, + SQL_ATTR_ODBC_VERSION.TSqlInteger, + cast[SqlPointer](val), resLen.TSqlInteger) + if res != SQL_SUCCESS: dbError("Error: unable to set ODBC driver version.") + # allocate hDb handle + res = SQLAllocHandle(SQL_HANDLE_DBC, result.env, result.hDb) + if res != SQL_SUCCESS: dbError("Error: unable to allocate connection handle.") + + # Connect: connection = dsn str, + res = SQLConnect(result.hDb, + connection.PSQLCHAR , connection.len.TSqlSmallInt, + user.PSQLCHAR, user.len.TSqlSmallInt, + password.PSQLCHAR, password.len.TSqlSmallInt) + if res != SQL_SUCCESS: + result.dbError() + +proc setEncoding*(connection: DbConn, encoding: string): bool {. + tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = + ## Currently not implemented for ODBC. + ## + ## Sets the encoding of a database connection, returns true for + ## success, false for failure. + ##result = set_character_set(connection, encoding) == 0 + dbError("setEncoding() is currently not implemented by the db_odbc module") diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim new file mode 100644 index 000000000..f9f584663 --- /dev/null +++ b/lib/impure/db_postgres.nim @@ -0,0 +1,590 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## A higher level `PostgreSQL`:idx: database wrapper. This interface +## is implemented for other databases also. +## +## See also: `db_odbc `_, `db_sqlite `_, +## `db_mysql `_. +## +## Parameter substitution +## ====================== +## +## 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 (?, ?, ?)" +## +## **Note**: There are two approaches to parameter substitution support by +## this module. +## +## 1. ``SqlQuery`` using ``?, ?, ?, ...`` (same as all the ``db_*`` modules) +## +## 2. ``SqlPrepared`` using ``$1, $2, $3, ...`` +## +## .. code-block:: Nim +## prepare(db, "myExampleInsert", +## sql"""INSERT INTO myTable +## (colA, colB, colC) +## VALUES ($1, $2, $3)""", +## 3) +## +## +## Unix Socket +## =========== +## +## Using Unix sockets instead of TCP connection can +## `improve performance up to 30% ~ 175% for some operations `_. +## +## To use Unix sockets with `db_postgres`, change the server address to the socket file path: +## +## .. code-block:: Nim +## import db_postgres ## Change "localhost" or "127.0.0.1" to the socket file path +## let db = db_postgres.open("/run/postgresql", "user", "password", "database") +## echo db.getAllRows(sql"SELECT version();") +## db.close() +## +## The socket file path is operating system specific and distribution specific, +## additional configuration may or may not be needed on your `postgresql.conf`. +## The Postgres server must be on the same computer and only works for Unix-like operating systems. +## +## +## Examples +## ======== +## +## Opening a connection to a database +## ---------------------------------- +## +## .. code-block:: Nim +## import db_postgres +## 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)""")) +## +## Inserting data +## -------------- +## +## .. code-block:: Nim +## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)", +## "Dominik") +import strutils, postgres + +import db_common +export db_common + +import std/private/since + +type + DbConn* = PPGconn ## encapsulates a database connection + Row* = seq[string] ## a row of a dataset. NULL database values will be + ## converted to nil. + InstantRow* = object ## a handle that can be + res: PPGresult ## used to get a row's + line: int ## column text on demand + SqlPrepared* = distinct string ## a identifier for the prepared queries + +proc dbError*(db: DbConn) {.noreturn.} = + ## raises a DbError exception. + var e: ref DbError + new(e) + e.msg = $pqErrorMessage(db) + raise e + +proc dbQuote*(s: string): string = + ## DB quotes the string. + result = "'" + for c in items(s): + if c == '\'': add(result, "''") + else: add(result, c) + 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) + +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, + nil, nil, 0) + result = pqresultStatus(res) == PGRES_COMMAND_OK + pqclear(res) + +proc tryExec*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): bool {.tags: [ + 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, + nil, nil, 0) + deallocCStringArray(arr) + result = pqresultStatus(res) == PGRES_COMMAND_OK + pqclear(res) + +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, + nil, nil, 0) + if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db) + pqclear(res) + +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, + nil, nil, 0) + deallocCStringArray(arr) + if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db) + pqclear(res) + +proc newRow(L: int): Row = + newSeq(result, L) + for i in 0..L-1: result[i] = "" + +proc setupQuery(db: DbConn, query: SqlQuery, + args: varargs[string]): PPGresult = + result = pqexec(db, dbFormat(query, args)) + 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, + nil, nil, 0) + deallocCStringArray(arr) + if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db) + +proc prepare*(db: DbConn; stmtName: string, query: SqlQuery; + nParams: int): SqlPrepared = + ## Creates a new ``SqlPrepared`` statement. Parameter substitution is done + ## 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) + if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db) + return SqlPrepared(stmtName) + +proc setRow(res: PPGresult, r: var Row, line, cols: int32) = + for col in 0'i32..cols-1: + setLen(r[col], 0) + let x = pqgetvalue(res, line, col) + if x.isNil: + r[col] = "" + else: + add(r[col], x) + +iterator fastRows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + ## executes the query and iterates over the result dataset. This is very + ## fast, but potentially dangerous: If the for-loop-body executes another + ## query, the results can be undefined. For Postgres it is safe though. + var res = setupQuery(db, query, args) + var L = pqnfields(res) + var result = newRow(L) + for i in 0'i32..pqntuples(res)-1: + setRow(res, result, i, L) + yield result + pqclear(res) + +iterator fastRows*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + ## executes the prepared query and iterates over the result dataset. + var res = setupQuery(db, stmtName, args) + var L = pqNfields(res) + var result = newRow(L) + for i in 0'i32..pqNtuples(res)-1: + setRow(res, result, i, L) + yield result + pqClear(res) + +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. + var res = setupQuery(db, query, args) + for i in 0'i32..pqNtuples(res)-1: + yield InstantRow(res: res, line: i) + pqClear(res) + +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. + var res = setupQuery(db, stmtName, args) + for i in 0'i32..pqNtuples(res)-1: + yield InstantRow(res: res, line: i) + pqClear(res) + +proc getColumnType(res: PPGresult, col: int) : DbType = + ## returns DbType for given column in the row + ## defined in pg_type.h file in the postgres source code + ## Wire representation for types: http://www.npgsql.org/dev/types.html + var oid = pqftype(res, int32(col)) + ## The integer returned is the internal OID number of the type + case oid + of 16: return DbType(kind: DbTypeKind.dbBool, name: "bool") + of 17: return DbType(kind: DbTypeKind.dbBlob, name: "bytea") + + of 21: return DbType(kind: DbTypeKind.dbInt, name: "int2", size: 2) + of 23: return DbType(kind: DbTypeKind.dbInt, name: "int4", size: 4) + of 20: return DbType(kind: DbTypeKind.dbInt, name: "int8", size: 8) + of 1560: return DbType(kind: DbTypeKind.dbBit, name: "bit") + of 1562: return DbType(kind: DbTypeKind.dbInt, name: "varbit") + + of 18: return DbType(kind: DbTypeKind.dbFixedChar, name: "char") + of 19: return DbType(kind: DbTypeKind.dbFixedChar, name: "name") + of 1042: return DbType(kind: DbTypeKind.dbFixedChar, name: "bpchar") + + of 25: return DbType(kind: DbTypeKind.dbVarchar, name: "text") + of 1043: return DbType(kind: DbTypeKind.dbVarChar, name: "varchar") + of 2275: return DbType(kind: DbTypeKind.dbVarchar, name: "cstring") + + of 700: return DbType(kind: DbTypeKind.dbFloat, name: "float4") + of 701: return DbType(kind: DbTypeKind.dbFloat, name: "float8") + + of 790: return DbType(kind: DbTypeKind.dbDecimal, name: "money") + of 1700: return DbType(kind: DbTypeKind.dbDecimal, name: "numeric") + + of 704: return DbType(kind: DbTypeKind.dbTimeInterval, name: "tinterval") + of 702: return DbType(kind: DbTypeKind.dbTimestamp, name: "abstime") + of 703: return DbType(kind: DbTypeKind.dbTimeInterval, name: "reltime") + of 1082: return DbType(kind: DbTypeKind.dbDate, name: "date") + of 1083: return DbType(kind: DbTypeKind.dbTime, name: "time") + of 1114: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamp") + of 1184: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamptz") + of 1186: return DbType(kind: DbTypeKind.dbTimeInterval, name: "interval") + of 1266: return DbType(kind: DbTypeKind.dbTime, name: "timetz") + + of 114: return DbType(kind: DbTypeKind.dbJson, name: "json") + of 142: return DbType(kind: DbTypeKind.dbXml, name: "xml") + of 3802: return DbType(kind: DbTypeKind.dbJson, name: "jsonb") + + of 600: return DbType(kind: DbTypeKind.dbPoint, name: "point") + of 601: return DbType(kind: DbTypeKind.dbLseg, name: "lseg") + of 602: return DbType(kind: DbTypeKind.dbPath, name: "path") + of 603: return DbType(kind: DbTypeKind.dbBox, name: "box") + of 604: return DbType(kind: DbTypeKind.dbPolygon, name: "polygon") + of 628: return DbType(kind: DbTypeKind.dbLine, name: "line") + of 718: return DbType(kind: DbTypeKind.dbCircle, name: "circle") + + of 650: return DbType(kind: DbTypeKind.dbInet, name: "cidr") + of 829: return DbType(kind: DbTypeKind.dbMacAddress, name: "macaddr") + of 869: return DbType(kind: DbTypeKind.dbInet, name: "inet") + + of 2950: return DbType(kind: DbTypeKind.dbVarchar, name: "uuid") + of 3614: return DbType(kind: DbTypeKind.dbVarchar, name: "tsvector") + of 3615: return DbType(kind: DbTypeKind.dbVarchar, name: "tsquery") + of 2970: return DbType(kind: DbTypeKind.dbVarchar, name: "txid_snapshot") + + of 27: return DbType(kind: DbTypeKind.dbComposite, name: "tid") + of 1790: return DbType(kind: DbTypeKind.dbComposite, name: "refcursor") + of 2249: return DbType(kind: DbTypeKind.dbComposite, name: "record") + of 3904: return DbType(kind: DbTypeKind.dbComposite, name: "int4range") + of 3906: return DbType(kind: DbTypeKind.dbComposite, name: "numrange") + of 3908: return DbType(kind: DbTypeKind.dbComposite, name: "tsrange") + of 3910: return DbType(kind: DbTypeKind.dbComposite, name: "tstzrange") + of 3912: return DbType(kind: DbTypeKind.dbComposite, name: "daterange") + of 3926: return DbType(kind: DbTypeKind.dbComposite, name: "int8range") + + of 22: return DbType(kind: DbTypeKind.dbArray, name: "int2vector") + of 30: return DbType(kind: DbTypeKind.dbArray, name: "oidvector") + of 143: return DbType(kind: DbTypeKind.dbArray, name: "xml[]") + of 199: return DbType(kind: DbTypeKind.dbArray, name: "json[]") + of 629: return DbType(kind: DbTypeKind.dbArray, name: "line[]") + of 651: return DbType(kind: DbTypeKind.dbArray, name: "cidr[]") + of 719: return DbType(kind: DbTypeKind.dbArray, name: "circle[]") + of 791: return DbType(kind: DbTypeKind.dbArray, name: "money[]") + of 1000: return DbType(kind: DbTypeKind.dbArray, name: "bool[]") + of 1001: return DbType(kind: DbTypeKind.dbArray, name: "bytea[]") + of 1002: return DbType(kind: DbTypeKind.dbArray, name: "char[]") + of 1003: return DbType(kind: DbTypeKind.dbArray, name: "name[]") + of 1005: return DbType(kind: DbTypeKind.dbArray, name: "int2[]") + of 1006: return DbType(kind: DbTypeKind.dbArray, name: "int2vector[]") + of 1007: return DbType(kind: DbTypeKind.dbArray, name: "int4[]") + of 1008: return DbType(kind: DbTypeKind.dbArray, name: "regproc[]") + of 1009: return DbType(kind: DbTypeKind.dbArray, name: "text[]") + of 1028: return DbType(kind: DbTypeKind.dbArray, name: "oid[]") + of 1010: return DbType(kind: DbTypeKind.dbArray, name: "tid[]") + of 1011: return DbType(kind: DbTypeKind.dbArray, name: "xid[]") + of 1012: return DbType(kind: DbTypeKind.dbArray, name: "cid[]") + of 1013: return DbType(kind: DbTypeKind.dbArray, name: "oidvector[]") + of 1014: return DbType(kind: DbTypeKind.dbArray, name: "bpchar[]") + of 1015: return DbType(kind: DbTypeKind.dbArray, name: "varchar[]") + of 1016: return DbType(kind: DbTypeKind.dbArray, name: "int8[]") + of 1017: return DbType(kind: DbTypeKind.dbArray, name: "point[]") + of 1018: return DbType(kind: DbTypeKind.dbArray, name: "lseg[]") + of 1019: return DbType(kind: DbTypeKind.dbArray, name: "path[]") + of 1020: return DbType(kind: DbTypeKind.dbArray, name: "box[]") + of 1021: return DbType(kind: DbTypeKind.dbArray, name: "float4[]") + of 1022: return DbType(kind: DbTypeKind.dbArray, name: "float8[]") + of 1023: return DbType(kind: DbTypeKind.dbArray, name: "abstime[]") + of 1024: return DbType(kind: DbTypeKind.dbArray, name: "reltime[]") + of 1025: return DbType(kind: DbTypeKind.dbArray, name: "tinterval[]") + of 1027: return DbType(kind: DbTypeKind.dbArray, name: "polygon[]") + of 1040: return DbType(kind: DbTypeKind.dbArray, name: "macaddr[]") + of 1041: return DbType(kind: DbTypeKind.dbArray, name: "inet[]") + of 1263: return DbType(kind: DbTypeKind.dbArray, name: "cstring[]") + of 1115: return DbType(kind: DbTypeKind.dbArray, name: "timestamp[]") + of 1182: return DbType(kind: DbTypeKind.dbArray, name: "date[]") + of 1183: return DbType(kind: DbTypeKind.dbArray, name: "time[]") + of 1185: return DbType(kind: DbTypeKind.dbArray, name: "timestamptz[]") + of 1187: return DbType(kind: DbTypeKind.dbArray, name: "interval[]") + of 1231: return DbType(kind: DbTypeKind.dbArray, name: "numeric[]") + of 1270: return DbType(kind: DbTypeKind.dbArray, name: "timetz[]") + of 1561: return DbType(kind: DbTypeKind.dbArray, name: "bit[]") + of 1563: return DbType(kind: DbTypeKind.dbArray, name: "varbit[]") + of 2201: return DbType(kind: DbTypeKind.dbArray, name: "refcursor[]") + of 2951: return DbType(kind: DbTypeKind.dbArray, name: "uuid[]") + of 3643: return DbType(kind: DbTypeKind.dbArray, name: "tsvector[]") + of 3645: return DbType(kind: DbTypeKind.dbArray, name: "tsquery[]") + of 3807: return DbType(kind: DbTypeKind.dbArray, name: "jsonb[]") + of 2949: return DbType(kind: DbTypeKind.dbArray, name: "txid_snapshot[]") + of 3905: return DbType(kind: DbTypeKind.dbArray, name: "int4range[]") + of 3907: return DbType(kind: DbTypeKind.dbArray, name: "numrange[]") + of 3909: return DbType(kind: DbTypeKind.dbArray, name: "tsrange[]") + of 3911: return DbType(kind: DbTypeKind.dbArray, name: "tstzrange[]") + of 3913: return DbType(kind: DbTypeKind.dbArray, name: "daterange[]") + of 3927: return DbType(kind: DbTypeKind.dbArray, name: "int8range[]") + of 2287: return DbType(kind: DbTypeKind.dbArray, name: "record[]") + + of 705: return DbType(kind: DbTypeKind.dbUnknown, name: "unknown") + else: return DbType(kind: DbTypeKind.dbUnknown, name: $oid) ## Query the system table pg_type to determine exactly which type is referenced. + +proc setColumnInfo(columns: var DbColumns; res: PPGresult; L: int32) = + setLen(columns, L) + for i in 0'i32.. 0: + setRow(res, result, 0, L) + pqclear(res) + +proc getRow*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + var res = setupQuery(db, stmtName, args) + var L = pqNfields(res) + result = newRow(L) + if pqntuples(res) > 0: + setRow(res, result, 0, L) + pqClear(res) + +proc getAllRows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): seq[Row] {. + tags: [ReadDbEffect].} = + ## executes the query and returns the whole result dataset. + result = @[] + for r in fastRows(db, query, args): + result.add(r) + +proc getAllRows*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): seq[Row] {.tags: + [ReadDbEffect].} = + ## executes the prepared query and returns the whole result dataset. + result = @[] + for r in fastRows(db, stmtName, args): + result.add(r) + +iterator rows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + ## same as `fastRows`, but slower and safe. + for r in items(getAllRows(db, query, args)): yield r + +iterator rows*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + ## same as `fastRows`, but slower and safe. + for r in items(getAllRows(db, stmtName, args)): yield r + +proc getValue*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): string {. + tags: [ReadDbEffect].} = + ## executes the query and returns the first column of the first row of the + ## result dataset. Returns "" if the dataset contains no rows or the database + ## value is NULL. + var res = setupQuery(db, query, args) + if pqntuples(res) > 0: + var x = pqgetvalue(res, 0, 0) + result = if isNil(x): "" else: $x + else: + result = "" + +proc getValue*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): string {. + tags: [ReadDbEffect].} = + ## executes the query and returns the first column of the first row of the + ## result dataset. Returns "" if the dataset contains no rows or the database + ## value is NULL. + var res = setupQuery(db, stmtName, args) + if pqntuples(res) > 0: + var x = pqgetvalue(res, 0, 0) + result = if isNil(x): "" else: $x + else: + result = "" + +proc tryInsertID*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): int64 {. + tags: [WriteDbEffect].}= + ## executes the query (typically "INSERT") and returns the + ## generated ID for the row or -1 in case of an error. For Postgre this adds + ## ``RETURNING id`` to the query, so it only works if your primary key is + ## named ``id``. + var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"), + args), 0, 0) + if not isNil(x): + result = parseBiggestInt($x) + else: + result = -1 + +proc insertID*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): int64 {. + tags: [WriteDbEffect].} = + ## executes the query (typically "INSERT") and returns the + ## generated ID for the row. For Postgre this adds + ## ``RETURNING id`` to the query, so it only works if your primary key is + ## named ``id``. + result = tryInsertID(db, query, args) + if result < 0: dbError(db) + +proc tryInsert*(db: DbConn, query: SqlQuery,pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], since: (1, 3).}= + ## executes the query (typically "INSERT") and returns the + ## generated ID for the row or -1 in case of an error. + var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING " & pkName), + args), 0, 0) + if not isNil(x): + result = parseBiggestInt($x) + else: + result = -1 + +proc insert*(db: DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], since: (1, 3).} = + ## executes the query (typically "INSERT") and returns the + ## generated ID + result = tryInsertID(db, query, args) + if result < 0: dbError(db) + +proc execAffectedRows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): int64 {.tags: [ + ReadDbEffect, WriteDbEffect].} = + ## executes the query (typically "UPDATE") and returns the + ## number of affected rows. + var q = dbFormat(query, args) + var res = pqExec(db, q) + if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db) + result = parseBiggestInt($pqcmdTuples(res)) + pqclear(res) + +proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): int64 {.tags: [ + ReadDbEffect, WriteDbEffect].} = + ## 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, + nil, nil, 0) + deallocCStringArray(arr) + if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db) + result = parseBiggestInt($pqcmdTuples(res)) + pqclear(res) + +proc close*(db: DbConn) {.tags: [DbEffect].} = + ## closes the database connection. + if db != nil: pqfinish(db) + +proc open*(connection, user, password, database: string): DbConn {. + tags: [DbEffect].} = + ## opens a database connection. Raises `EDb` if the connection could not + ## be established. + ## + ## Clients can also use Postgres keyword/value connection strings to + ## connect. + ## + ## Example: + ## + ## .. code-block:: nim + ## + ## con = open("", "", "", "host=localhost port=5432 dbname=mydb") + ## + ## See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING + ## for more information. + let + colonPos = connection.find(':') + host = if colonPos < 0: connection + else: substr(connection, 0, colonPos-1) + port = if colonPos < 0: "" + else: substr(connection, colonPos+1) + result = pqsetdbLogin(host, port, nil, nil, database, user, password) + if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil + +proc setEncoding*(connection: DbConn, encoding: string): bool {. + tags: [DbEffect].} = + ## sets the encoding of a database connection, returns true for + ## success, false for failure. + return pqsetClientEncoding(connection, encoding) == 0 + + +# Tests are in ../../tests/untestable/tpostgres. diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim new file mode 100644 index 000000000..5c79b8c2a --- /dev/null +++ b/lib/impure/db_sqlite.nim @@ -0,0 +1,950 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## A higher level `SQLite`:idx: database wrapper. This interface +## is implemented for other databases too. +## +## Basic usage +## =========== +## +## The basic flow of using this module is: +## +## 1. Open database connection +## 2. Execute SQL query +## 3. Close database connection +## +## Parameter substitution +## ---------------------- +## +## 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 my_table (colA, colB, colC) VALUES (?, ?, ?)" +## +## Opening a connection to a database +## ---------------------------------- +## +## .. code-block:: Nim +## +## import db_sqlite +## +## # user, password, database name can be empty. +## # These params are not used on db_sqlite module. +## let db = open("mytest.db", "", "", "") +## db.close() +## +## Creating a table +## ---------------- +## +## .. code-block:: Nim +## +## db.exec(sql"DROP TABLE IF EXISTS my_table") +## db.exec(sql"""CREATE TABLE my_table ( +## id INTEGER, +## name VARCHAR(50) NOT NULL +## )""") +## +## Inserting data +## -------------- +## +## .. code-block:: Nim +## +## db.exec(sql"INSERT INTO my_table (id, name) VALUES (0, ?)", +## "Jack") +## +## Larger example +## -------------- +## +## .. code-block:: nim +## +## import db_sqlite, math +## +## let db = open("mytest.db", "", "", "") +## +## db.exec(sql"DROP TABLE IF EXISTS my_table") +## db.exec(sql"""CREATE TABLE my_table ( +## id INTEGER PRIMARY KEY, +## name VARCHAR(50) NOT NULL, +## i INT(11), +## f DECIMAL(18, 10) +## )""") +## +## db.exec(sql"BEGIN") +## for i in 1..1000: +## db.exec(sql"INSERT INTO my_table (name, i, f) VALUES (?, ?, ?)", +## "Item#" & $i, i, sqrt(i.float)) +## db.exec(sql"COMMIT") +## +## for x in db.fastRows(sql"SELECT * FROM my_table"): +## echo x +## +## let id = db.tryInsertId(sql"""INSERT INTO my_table (name, i, f) +## VALUES (?, ?, ?)""", +## "Item#1001", 1001, sqrt(1001.0)) +## echo "Inserted item: ", db.getValue(sql"SELECT name FROM my_table WHERE id=?", id) +## +## db.close() +## +## Storing binary data example +##---------------------------- +## +## .. code-block:: nim +## +## import random +## +## ## Generate random float datas +## var orig = newSeq[float64](150) +## randomize() +## for x in orig.mitems: +## x = rand(1.0)/10.0 +## +## let db = open("mysqlite.db", "", "", "") +## block: ## Create database +## ## Binary datas needs to be of type BLOB in SQLite +## let createTableStr = sql"""CREATE TABLE test( +## id INTEGER NOT NULL PRIMARY KEY, +## data BLOB +## ) +## """ +## db.exec(createTableStr) +## +## block: ## Insert data +## var id = 1 +## ## Data needs to be converted to seq[byte] to be interpreted as binary by bindParams +## var dbuf = newSeq[byte](orig.len*sizeof(float64)) +## copyMem(unsafeAddr(dbuf[0]), unsafeAddr(orig[0]), dbuf.len) +## +## ## Use prepared statement to insert binary data into database +## var insertStmt = db.prepare("INSERT INTO test (id, data) VALUES (?, ?)") +## insertStmt.bindParams(id, dbuf) +## let bres = db.tryExec(insertStmt) +## ## Check insert +## doAssert(bres) +## # Destroy statement +## finalize(insertStmt) +## +## block: ## Use getValue to select data +## var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1) +## ## Calculate sequence size from buffer size +## let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64)) +## ## Copy binary string data in dataTest into a seq +## var res: seq[float64] = newSeq[float64](seqSize) +## copyMem(unsafeAddr(res[0]), addr(dataTest[0]), dataTest.len) +## +## ## Check datas obtained is identical +## doAssert res == orig +## +## db.close() +## +## +## Note +## ==== +## This module does not implement any ORM features such as mapping the types from the schema. +## Instead, a ``seq[string]`` is returned for each row. +## +## The reasoning is as follows: +## 1. it's close to what many DBs offer natively (char**) +## 2. it hides the number of types that the DB supports +## (int? int64? decimal up to 10 places? geo coords?) +## 3. it's convenient when all you do is to forward the data to somewhere else (echo, log, put the data into a new query) +## +## See also +## ======== +## +## * `db_odbc module `_ for ODBC database wrapper +## * `db_mysql module `_ for MySQL database wrapper +## * `db_postgres module `_ for PostgreSQL database wrapper + +{.experimental: "codeReordering".} + +import sqlite3, macros + +import db_common +export db_common + +import std/private/since + +type + DbConn* = PSqlite3 ## Encapsulates a database connection. + Row* = seq[string] ## A row of a dataset. `NULL` database values will be + ## converted to an empty string. + InstantRow* = PStmt ## A handle that can be used to get a row's column + ## text on demand. + SqlPrepared* = distinct PStmt ## a identifier for the prepared queries + +proc dbError*(db: DbConn) {.noreturn.} = + ## Raises a `DbError` exception. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## if not db.tryExec(sql"SELECT * FROM not_exist_table"): + ## dbError(db) + ## db.close() + var e: ref DbError + new(e) + e.msg = $sqlite3.errmsg(db) + raise e + +proc dbQuote*(s: string): string = + ## Escapes the `'` (single quote) char to `''`. + ## Because single quote is used for defining `VARCHAR` in SQL. + runnableExamples: + doAssert dbQuote("'") == "''''" + doAssert dbQuote("A Foobar's pen.") == "'A Foobar''s pen.'" + + result = "'" + for c in items(s): + if c == '\'': add(result, "''") + else: add(result, 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) + +proc prepare*(db: DbConn; q: string): SqlPrepared {.since: (1, 3).} = + ## Creates a new ``SqlPrepared`` statement. + if prepare_v2(db, q, q.len.cint,result.PStmt, nil) != SQLITE_OK: + discard finalize(result.PStmt) + dbError(db) + +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. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## if not db.tryExec(sql"SELECT * FROM my_table"): + ## dbError(db) + ## db.close() + 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: + let x = step(stmt) + if x in {SQLITE_DONE, SQLITE_ROW}: + result = finalize(stmt) == SQLITE_OK + else: + discard finalize(stmt) + result = false + +proc tryExec*(db: DbConn, stmtName: SqlPrepared): bool {. + tags: [ReadDbEffect, WriteDbEffect].} = + let x = step(stmtName.PStmt) + if x in {SQLITE_DONE, SQLITE_ROW}: + result = true + else: + discard finalize(stmtName.PStmt) + result = false + +proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {. + tags: [ReadDbEffect, WriteDbEffect].} = + ## Executes the query and raises a `DbError` exception if not successful. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## try: + ## db.exec(sql"INSERT INTO my_table (id, name) VALUES (?, ?)", + ## 1, "item#1") + ## except: + ## stderr.writeLine(getCurrentExceptionMsg()) + ## finally: + ## db.close() + if not tryExec(db, query, args): dbError(db) + +proc newRow(L: int): Row = + newSeq(result, L) + for i in 0..L-1: result[i] = "" + +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) + +proc setupQuery(db: DbConn, stmtName: SqlPrepared): SqlPrepared {.since: (1, 3).} = + assert(not db.isNil, "Database not connected.") + result = stmtName + +proc setRow(stmt: PStmt, r: var Row, cols: cint) = + for col in 0'i32..cols-1: + let cb = column_bytes(stmt, col) + setLen(r[col], cb) # set capacity + if column_type(stmt, col) == SQLITE_BLOB: + copyMem(addr(r[col][0]), column_blob(stmt, col), cb) + else: + setLen(r[col], 0) + let x = column_text(stmt, col) + if not isNil(x): add(r[col], x) + +iterator fastRows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + ## Executes the query and iterates over the result dataset. + ## + ## This is very fast, but potentially dangerous. Use this iterator only + ## if you require **ALL** the rows. + ## + ## **Note:** Breaking the `fastRows()` iterator during a loop will cause the + ## next database query to raise a `DbError` exception ``unable to close due + ## to ...``. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## + ## # Records of my_table: + ## # | id | name | + ## # |----|----------| + ## # | 1 | item#1 | + ## # | 2 | item#2 | + ## + ## for row in db.fastRows(sql"SELECT id, name FROM my_table"): + ## echo row + ## + ## # Output: + ## # @["1", "item#1"] + ## # @["2", "item#2"] + ## + ## db.close() + var stmt = setupQuery(db, query, args) + var L = (column_count(stmt)) + var result = newRow(L) + try: + while step(stmt) == SQLITE_ROW: + setRow(stmt, result, L) + yield result + finally: + if finalize(stmt) != SQLITE_OK: dbError(db) + +iterator fastRows*(db: DbConn, stmtName: SqlPrepared): Row + {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} = + discard setupQuery(db, stmtName) + var L = (column_count(stmtName.PStmt)) + var result = newRow(L) + try: + while step(stmtName.PStmt) == SQLITE_ROW: + setRow(stmtName.PStmt, result, L) + yield result + except: + dbError(db) + +iterator instantRows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): InstantRow + {.tags: [ReadDbEffect].} = + ## Similar to `fastRows iterator <#fastRows.i,DbConn,SqlQuery,varargs[string,]>`_ + ## but returns a handle that can be used to get column text + ## on demand using `[]`. Returned handle is valid only within the iterator body. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## + ## # Records of my_table: + ## # | id | name | + ## # |----|----------| + ## # | 1 | item#1 | + ## # | 2 | item#2 | + ## + ## for row in db.instantRows(sql"SELECT * FROM my_table"): + ## echo "id:" & row[0] + ## echo "name:" & row[1] + ## echo "length:" & $len(row) + ## + ## # Output: + ## # id:1 + ## # name:item#1 + ## # length:2 + ## # id:2 + ## # name:item#2 + ## # length:2 + ## + ## db.close() + var stmt = setupQuery(db, query, args) + try: + while step(stmt) == SQLITE_ROW: + yield stmt + finally: + if finalize(stmt) != SQLITE_OK: dbError(db) + +iterator instantRows*(db: DbConn, stmtName: SqlPrepared): InstantRow + {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} = + var stmt = setupQuery(db, stmtName).PStmt + try: + while step(stmt) == SQLITE_ROW: + yield stmt + except: + dbError(db) + +proc toTypeKind(t: var DbType; x: int32) = + case x + of SQLITE_INTEGER: + t.kind = dbInt + t.size = 8 + of SQLITE_FLOAT: + t.kind = dbFloat + t.size = 8 + of SQLITE_BLOB: t.kind = dbBlob + of SQLITE_NULL: t.kind = dbNull + of SQLITE_TEXT: t.kind = dbVarchar + else: t.kind = dbUnknown + +proc setColumns(columns: var DbColumns; x: PStmt) = + let L = column_count(x) + setLen(columns, L) + for i in 0'i32 ..< L: + columns[i].name = $column_name(x, i) + columns[i].typ.name = $column_decltype(x, i) + toTypeKind(columns[i].typ, column_type(x, i)) + columns[i].tableName = $column_table_name(x, i) + +iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery, + args: varargs[string, `$`]): InstantRow + {.tags: [ReadDbEffect].} = + ## Similar to `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_, + ## but sets information about columns to `columns`. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## + ## # Records of my_table: + ## # | id | name | + ## # |----|----------| + ## # | 1 | item#1 | + ## # | 2 | item#2 | + ## + ## var columns: DbColumns + ## for row in db.instantRows(columns, sql"SELECT * FROM my_table"): + ## discard + ## echo columns[0] + ## + ## # Output: + ## # (name: "id", tableName: "my_table", typ: (kind: dbNull, + ## # notNull: false, name: "INTEGER", size: 0, maxReprLen: 0, precision: 0, + ## # scale: 0, min: 0, max: 0, validValues: @[]), primaryKey: false, + ## # foreignKey: false) + ## + ## db.close() + var stmt = setupQuery(db, query, args) + setColumns(columns, stmt) + try: + while step(stmt) == SQLITE_ROW: + yield stmt + finally: + if finalize(stmt) != SQLITE_OK: dbError(db) + +proc `[]`*(row: InstantRow, col: int32): string {.inline.} = + ## Returns text for given column of the row. + ## + ## See also: + ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_ + ## example code + $column_text(row, col) + +proc unsafeColumnAt*(row: InstantRow, index: int32): cstring {.inline.} = + ## Returns cstring for given column of the row. + ## + ## See also: + ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_ + ## example code + column_text(row, index) + +proc len*(row: InstantRow): int32 {.inline.} = + ## Returns number of columns in a row. + ## + ## See also: + ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_ + ## example code + column_count(row) + +proc getRow*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + ## Retrieves a single row. If the query doesn't return any rows, this proc + ## will return a `Row` with empty strings for each column. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## + ## # Records of my_table: + ## # | id | name | + ## # |----|----------| + ## # | 1 | item#1 | + ## # | 2 | item#2 | + ## + ## doAssert db.getRow(sql"SELECT id, name FROM my_table" + ## ) == Row(@["1", "item#1"]) + ## doAssert db.getRow(sql"SELECT id, name FROM my_table WHERE id = ?", + ## 2) == Row(@["2", "item#2"]) + ## + ## # Returns empty. + ## doAssert db.getRow(sql"INSERT INTO my_table (id, name) VALUES (?, ?)", + ## 3, "item#3") == @[] + ## doAssert db.getRow(sql"DELETE FROM my_table WHERE id = ?", 3) == @[] + ## doAssert db.getRow(sql"UPDATE my_table SET name = 'ITEM#1' WHERE id = ?", + ## 1) == @[] + ## db.close() + var stmt = setupQuery(db, query, args) + var L = (column_count(stmt)) + result = newRow(L) + if step(stmt) == SQLITE_ROW: + setRow(stmt, result, L) + if finalize(stmt) != SQLITE_OK: dbError(db) + +proc getAllRows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} = + ## Executes the query and returns the whole result dataset. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## + ## # Records of my_table: + ## # | id | name | + ## # |----|----------| + ## # | 1 | item#1 | + ## # | 2 | item#2 | + ## + ## doAssert db.getAllRows(sql"SELECT id, name FROM my_table") == @[Row(@["1", "item#1"]), Row(@["2", "item#2"])] + ## db.close() + result = @[] + for r in fastRows(db, query, args): + result.add(r) + +proc getAllRows*(db: DbConn, stmtName: SqlPrepared): seq[Row] + {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} = + result = @[] + for r in fastRows(db, stmtName): + result.add(r) + +iterator rows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + ## Similar to `fastRows iterator <#fastRows.i,DbConn,SqlQuery,varargs[string,]>`_, + ## but slower and safe. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## + ## # Records of my_table: + ## # | id | name | + ## # |----|----------| + ## # | 1 | item#1 | + ## # | 2 | item#2 | + ## + ## for row in db.rows(sql"SELECT id, name FROM my_table"): + ## echo row + ## + ## ## Output: + ## ## @["1", "item#1"] + ## ## @["2", "item#2"] + ## + ## db.close() + for r in fastRows(db, query, args): yield r + +iterator rows*(db: DbConn, stmtName: SqlPrepared): Row + {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} = + for r in fastRows(db, stmtName): yield r + +proc getValue*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} = + ## Executes the query and returns the first column of the first row of the + ## result dataset. Returns `""` if the dataset contains no rows or the database + ## value is `NULL`. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## + ## # Records of my_table: + ## # | id | name | + ## # |----|----------| + ## # | 1 | item#1 | + ## # | 2 | item#2 | + ## + ## doAssert db.getValue(sql"SELECT name FROM my_table WHERE id = ?", + ## 2) == "item#2" + ## doAssert db.getValue(sql"SELECT id, name FROM my_table") == "1" + ## doAssert db.getValue(sql"SELECT name, id FROM my_table") == "item#1" + ## + ## db.close() + var stmt = setupQuery(db, query, args) + if step(stmt) == SQLITE_ROW: + let cb = column_bytes(stmt, 0) + if cb == 0: + result = "" + else: + if column_type(stmt, 0) == SQLITE_BLOB: + result.setLen(cb) + copyMem(addr(result[0]), column_blob(stmt, 0), cb) + else: + result = newStringOfCap(cb) + add(result, column_text(stmt, 0)) + else: + result = "" + if finalize(stmt) != SQLITE_OK: dbError(db) + +proc getValue*(db: DbConn, stmtName: SqlPrepared): string + {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} = + var stmt = setupQuery(db, stmtName).PStmt + if step(stmt) == SQLITE_ROW: + let cb = column_bytes(stmt, 0) + if cb == 0: + result = "" + else: + if column_type(stmt, 0) == SQLITE_BLOB: + result.setLen(cb) + copyMem(addr(result[0]), column_blob(stmt, 0), cb) + else: + result = newStringOfCap(cb) + add(result, column_text(stmt, 0)) + else: + result = "" + +proc tryInsertID*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], raises: [].} = + ## Executes the query (typically "INSERT") and returns the + ## generated ID for the row or -1 in case of an error. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## db.exec(sql"CREATE TABLE my_table (id INTEGER, name VARCHAR(50) NOT NULL)") + ## + ## doAssert db.tryInsertID(sql"INSERT INTO not_exist_table (id, name) VALUES (?, ?)", + ## 1, "item#1") == -1 + ## db.close() + assert(not db.isNil, "Database not connected.") + var q = dbFormat(query, args) + var stmt: sqlite3.PStmt + result = -1 + if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: + if step(stmt) == SQLITE_DONE: + result = last_insert_rowid(db) + if finalize(stmt) != SQLITE_OK: + result = -1 + else: + discard finalize(stmt) + +proc insertID*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} = + ## Executes the query (typically "INSERT") and returns the + ## generated ID for the row. + ## + ## Raises a `DbError` exception when failed to insert row. + ## For Postgre this adds ``RETURNING id`` to the query, so it only works + ## if your primary key is named ``id``. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## db.exec(sql"CREATE TABLE my_table (id INTEGER, name VARCHAR(50) NOT NULL)") + ## + ## for i in 0..2: + ## let id = db.insertID(sql"INSERT INTO my_table (id, name) VALUES (?, ?)", i, "item#" & $i) + ## echo "LoopIndex = ", i, ", InsertID = ", id + ## + ## # Output: + ## # LoopIndex = 0, InsertID = 1 + ## # LoopIndex = 1, InsertID = 2 + ## # LoopIndex = 2, InsertID = 3 + ## + ## db.close() + result = tryInsertID(db, query, args) + if result < 0: dbError(db) + +proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], raises: [], since: (1, 3).} = + ## same as tryInsertID + tryInsertID(db, query, args) + +proc insert*(db: DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], since: (1, 3).} = + ## same as insertId + result = tryInsert(db, query,pkName, args) + if result < 0: dbError(db) + +proc execAffectedRows*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): int64 {. + tags: [ReadDbEffect, WriteDbEffect].} = + ## Executes the query (typically "UPDATE") and returns the + ## number of affected rows. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## + ## # Records of my_table: + ## # | id | name | + ## # |----|----------| + ## # | 1 | item#1 | + ## # | 2 | item#2 | + ## + ## doAssert db.execAffectedRows(sql"UPDATE my_table SET name = 'TEST'") == 2 + ## + ## db.close() + exec(db, query, args) + result = changes(db) + +proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared): int64 + {.tags: [ReadDbEffect, WriteDbEffect],since: (1, 3).} = + exec(db, stmtName) + result = changes(db) + +proc close*(db: DbConn) {.tags: [DbEffect].} = + ## Closes the database connection. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## db.close() + if sqlite3.close(db) != SQLITE_OK: dbError(db) + +proc open*(connection, user, password, database: string): DbConn {. + tags: [DbEffect].} = + ## Opens a database connection. Raises a `DbError` exception if the connection + ## could not be established. + ## + ## **Note:** Only the ``connection`` parameter is used for ``sqlite``. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## try: + ## let db = open("mytest.db", "", "", "") + ## ## do something... + ## ## db.getAllRows(sql"SELECT * FROM my_table") + ## db.close() + ## except: + ## stderr.writeLine(getCurrentExceptionMsg()) + var db: DbConn + if sqlite3.open(connection, db) == SQLITE_OK: + result = db + else: + dbError(db) + +proc setEncoding*(connection: DbConn, encoding: string): bool {. + tags: [DbEffect].} = + ## Sets the encoding of a database connection, returns `true` for + ## success, `false` for failure. + ## + ## **Note:** The encoding cannot be changed once it's been set. + ## According to SQLite3 documentation, any attempt to change + ## the encoding after the database is created will be silently + ## ignored. + exec(connection, sql"PRAGMA encoding = ?", [encoding]) + result = connection.getValue(sql"PRAGMA encoding") == encoding + +proc finalize*(sqlPrepared:SqlPrepared) {.discardable, since: (1, 3).} = + discard finalize(sqlPrepared.PStmt) + +template dbBindParamError*(paramIdx: int, val: varargs[untyped]) = + ## Raises a `DbError` exception. + var e: ref DbError + new(e) + e.msg = "error binding param in position " & $paramIdx + raise e + +proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int32) {.since: (1, 3).} = + ## Binds a int32 to the specified paramIndex. + if bind_int(ps.PStmt, paramIdx.int32, val) != SQLITE_OK: + dbBindParamError(paramIdx, val) + +proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int64) {.since: (1, 3).} = + ## Binds a int64 to the specified paramIndex. + if bind_int64(ps.PStmt, paramIdx.int32, val) != SQLITE_OK: + dbBindParamError(paramIdx, val) + +proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int) {.since: (1, 3).} = + ## Binds a int to the specified paramIndex. + when sizeof(int) == 8: + bindParam(ps, paramIdx, val.int64) + else: + bindParam(ps, paramIdx, val.int32) + +proc bindParam*(ps: SqlPrepared, paramIdx: int, val: float64) {.since: (1, 3).} = + ## Binds a 64bit float to the specified paramIndex. + if bind_double(ps.PStmt, paramIdx.int32, val) != SQLITE_OK: + dbBindParamError(paramIdx, val) + +proc bindNull*(ps: SqlPrepared, paramIdx: int) {.since: (1, 3).} = + ## Sets the bindparam at the specified paramIndex to null + ## (default behaviour by sqlite). + if bind_null(ps.PStmt, paramIdx.int32) != SQLITE_OK: + dbBindParamError(paramIdx) + +proc bindParam*(ps: SqlPrepared, paramIdx: int, val: string, copy = true) {.since: (1, 3).} = + ## Binds a string to the specified paramIndex. + ## if copy is true then SQLite makes its own private copy of the data immediately + if bind_text(ps.PStmt, paramIdx.int32, val.cstring, val.len.int32, if copy: SQLITE_TRANSIENT else: SQLITE_STATIC) != SQLITE_OK: + dbBindParamError(paramIdx, val) + +proc bindParam*(ps: SqlPrepared, paramIdx: int,val: openArray[byte], copy = true) {.since: (1, 3).} = + ## binds a blob to the specified paramIndex. + ## if copy is true then SQLite makes its own private copy of the data immediately + let len = val.len + if bind_blob(ps.PStmt, paramIdx.int32, val[0].unsafeAddr, len.int32, if copy: SQLITE_TRANSIENT else: SQLITE_STATIC) != SQLITE_OK: + dbBindParamError(paramIdx, val) + +macro bindParams*(ps: SqlPrepared, params: varargs[untyped]): untyped {.since: (1, 3).} = + let bindParam = bindSym("bindParam", brOpen) + let bindNull = bindSym("bindNull") + let preparedStatement = genSym() + result = newStmtList() + # Store `ps` in a temporary variable. This prevents `ps` from being evaluated every call. + result.add newNimNode(nnkLetSection).add(newIdentDefs(preparedStatement, newEmptyNode(), ps)) + for idx, param in params: + if param.kind != nnkNilLit: + result.add newCall(bindParam, preparedStatement, newIntLitNode idx + 1, param) + else: + result.add newCall(bindNull, preparedStatement, newIntLitNode idx + 1) + +macro untypedLen(args: varargs[untyped]): int = + newLit(args.len) + +template exec*(db: DbConn, stmtName: SqlPrepared, + args: varargs[typed]): untyped = + when untypedLen(args) > 0: + if reset(stmtName.PStmt) != SQLITE_OK: + dbError(db) + if clear_bindings(stmtName.PStmt) != SQLITE_OK: + dbError(db) + stmtName.bindParams(args) + if not tryExec(db, stmtName): dbError(db) + +when not defined(testing) and isMainModule: + var db = open(":memory:", "", "", "") + exec(db, sql"create table tbl1(one varchar(10), two smallint)", []) + exec(db, sql"insert into tbl1 values('hello!',10)", []) + exec(db, sql"insert into tbl1 values('goodbye', 20)", []) + var p1 = db.prepare "create table tbl2(one varchar(10), two smallint)" + exec(db, p1) + finalize(p1) + var p2 = db.prepare "insert into tbl2 values('hello!',10)" + exec(db, p2) + finalize(p2) + var p3 = db.prepare "insert into tbl2 values('goodbye', 20)" + exec(db, p3) + finalize(p3) + #db.query("create table tbl1(one varchar(10), two smallint)") + #db.query("insert into tbl1 values('hello!',10)") + #db.query("insert into tbl1 values('goodbye', 20)") + for r in db.rows(sql"select * from tbl1", []): + echo(r[0], r[1]) + for r in db.instantRows(sql"select * from tbl1", []): + echo(r[0], r[1]) + var p4 = db.prepare "select * from tbl2" + for r in db.rows(p4): + echo(r[0], r[1]) + finalize(p4) + var i5 = 0 + var p5 = db.prepare "select * from tbl2" + for r in db.instantRows(p5): + inc i5 + echo(r[0], r[1]) + assert i5 == 2 + finalize(p5) + + for r in db.rows(sql"select * from tbl2", []): + echo(r[0], r[1]) + for r in db.instantRows(sql"select * from tbl2", []): + echo(r[0], r[1]) + var p6 = db.prepare "select * from tbl2 where one = ? " + p6.bindParams("goodbye") + var rowsP3 = 0 + for r in db.rows(p6): + rowsP3 = 1 + echo(r[0], r[1]) + assert rowsP3 == 1 + finalize(p6) + + var p7 = db.prepare "select * from tbl2 where two=?" + p7.bindParams(20'i32) + when sizeof(int) == 4: + p7.bindParams(20) + var rowsP = 0 + for r in db.rows(p7): + rowsP = 1 + echo(r[0], r[1]) + assert rowsP == 1 + finalize(p7) + + exec(db, sql"CREATE TABLE photos(ID INTEGER PRIMARY KEY AUTOINCREMENT, photo BLOB)") + var p8 = db.prepare "INSERT INTO photos (ID,PHOTO) VALUES (?,?)" + var d = "abcdefghijklmnopqrstuvwxyz" + p8.bindParams(1'i32, "abcdefghijklmnopqrstuvwxyz") + exec(db, p8) + finalize(p8) + var p10 = db.prepare "INSERT INTO photos (ID,PHOTO) VALUES (?,?)" + p10.bindParams(2'i32,nil) + exec(db, p10) + exec( db, p10, 3, nil) + finalize(p10) + for r in db.rows(sql"select * from photos where ID = 1", []): + assert r[1].len == d.len + assert r[1] == d + var i6 = 0 + for r in db.rows(sql"select * from photos where ID = 3", []): + i6 = 1 + assert i6 == 1 + var p9 = db.prepare("select * from photos where PHOTO is ?") + p9.bindParams(nil) + var rowsP2 = 0 + for r in db.rows(p9): + rowsP2 = 1 + echo(r[0], repr r[1]) + assert rowsP2 == 1 + finalize(p9) + + db_sqlite.close(db) diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 2c1b1deae..bd17c713e 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -20,17 +20,15 @@ when defined(js): ## search the internet for a wide variety of third-party documentation and ## tools. ## -## .. warning:: If you love `sequtils.toSeq` we have bad news for you. This -## library doesn't work with it due to documented compiler limitations. As -## a workaround, use this: -runnableExamples: - # either `import std/nre except toSeq` or fully qualify `sequtils.toSeq`: - import std/sequtils - iterator iota(n: int): int = - for i in 0..`_ ## - ## `captureCount: int` - ## : the number of captures that the pattern has. + ## ``captureCount: int`` + ## the number of captures that the pattern has. ## - ## `captureNameId: Table[string, int]` - ## : a table from the capture names to their numeric id. + ## ``captureNameId: Table[string, int]`` + ## a table from the capture names to their numeric id. ## ## ## Options @@ -98,30 +98,30 @@ type ## The following options may appear anywhere in the pattern, and they affect ## the rest of it. ## - ## - `(?i)` - case insensitive - ## - `(?m)` - multi-line: `^` and `$` match the beginning and end of + ## - ``(?i)`` - case insensitive + ## - ``(?m)`` - multi-line: ``^`` and ``$`` match the beginning and end of ## lines, not of the subject string - ## - `(?s)` - `.` also matches newline (*dotall*) - ## - `(?U)` - expressions are not greedy by default. `?` can be added + ## - ``(?s)`` - ``.`` also matches newline (*dotall*) + ## - ``(?U)`` - expressions are not greedy by default. ``?`` can be added ## to a qualifier to make it greedy - ## - `(?x)` - whitespace and comments (`#`) are ignored (*extended*) - ## - `(?X)` - character escapes without special meaning (`\w` vs. - ## `\a`) are errors (*extra*) + ## - ``(?x)`` - whitespace and comments (``#``) are ignored (*extended*) + ## - ``(?X)`` - character escapes without special meaning (``\w`` vs. + ## ``\a``) are errors (*extra*) ## ## One or a combination of these options may appear only at the beginning ## of the pattern: ## - ## - `(*UTF8)` - treat both the pattern and subject as UTF-8 - ## - `(*UCP)` - Unicode character properties; `\w` matches `я` - ## - `(*U)` - a combination of the two options above - ## - `(*FIRSTLINE*)` - fails if there is not a match on the first line - ## - `(*NO_AUTO_CAPTURE)` - turn off auto-capture for groups; - ## `(?...)` can be used to capture - ## - `(*CR)` - newlines are separated by `\r` - ## - `(*LF)` - newlines are separated by `\n` (UNIX default) - ## - `(*CRLF)` - newlines are separated by `\r\n` (Windows default) - ## - `(*ANYCRLF)` - newlines are separated by any of the above - ## - `(*ANY)` - newlines are separated by any of the above and Unicode + ## - ``(*UTF8)`` - treat both the pattern and subject as UTF-8 + ## - ``(*UCP)`` - Unicode character properties; ``\w`` matches ``я`` + ## - ``(*U)`` - a combination of the two options above + ## - ``(*FIRSTLINE*)`` - fails if there is not a match on the first line + ## - ``(*NO_AUTO_CAPTURE)`` - turn off auto-capture for groups; + ## ``(?...)`` can be used to capture + ## - ``(*CR)`` - newlines are separated by ``\r`` + ## - ``(*LF)`` - newlines are separated by ``\n`` (UNIX default) + ## - ``(*CRLF)`` - newlines are separated by ``\r\n`` (Windows default) + ## - ``(*ANYCRLF)`` - newlines are separated by any of the above + ## - ``(*ANY)`` - newlines are separated by any of the above and Unicode ## newlines: ## ## single characters VT (vertical tab, U+000B), FF (form feed, U+000C), @@ -130,8 +130,8 @@ type ## are recognized only in UTF-8 mode. ## — man pcre ## - ## - `(*JAVASCRIPT_COMPAT)` - JavaScript compatibility - ## - `(*NO_STUDY)` - turn off studying; study is enabled by default + ## - ``(*JAVASCRIPT_COMPAT)`` - JavaScript compatibility + ## - ``(*NO_STUDY)`` - turn off studying; study is enabled by default ## ## For more details on the leading option groups, see the `Option ## Setting `_ @@ -141,11 +141,11 @@ type ## manual `_. ## ## Some of these options are not part of PCRE and are converted by nre - ## into PCRE flags. These include `NEVER_UTF`, `ANCHORED`, - ## `DOLLAR_ENDONLY`, `FIRSTLINE`, `NO_AUTO_CAPTURE`, - ## `JAVASCRIPT_COMPAT`, `U`, `NO_STUDY`. In other PCRE wrappers, you + ## into PCRE flags. These include ``NEVER_UTF``, ``ANCHORED``, + ## ``DOLLAR_ENDONLY``, ``FIRSTLINE``, ``NO_AUTO_CAPTURE``, + ## ``JAVASCRIPT_COMPAT``, ``U``, ``NO_STUDY``. In other PCRE wrappers, you ## will need to pass these as separate flags to PCRE. - pattern*: string + pattern*: string ## not nil pcreObj: ptr pcre.Pcre ## not nil pcreExtra: ptr pcre.ExtraData ## nil @@ -155,40 +155,50 @@ type ## Usually seen as Option[RegexMatch], it represents the result of an ## execution. On failure, it is none, on success, it is some. ## - ## `pattern: Regex` - ## : the pattern that is being matched + ## ``pattern: Regex`` + ## the pattern that is being matched ## - ## `str: string` - ## : the string that was matched against + ## ``str: string`` + ## the string that was matched against ## - ## `captures[]: string` - ## : 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 + ## ``captures[]: string`` + ## 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`. + ## ``nil`` is returned. ## - ## `captureBounds[]: HSlice[int, int]` - ## : 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`. + ## - ``"abc".match(re"(\w)").get.captures[0] == "a"`` + ## - ``"abc".match(re"(?\w)").get.captures["letter"] == "a"`` + ## - ``"abc".match(re"(\w)\w").get.captures[-1] == "ab"`` ## - ## `match: string` - ## : the full text of the match. + ## ``captureBounds[]: HSlice[int, int]`` + ## 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. ## - ## `matchBounds: HSlice[int, int]` - ## : the bounds of the match, as in `captureBounds[]` + ## - ``"abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0`` + ## - ``0 in "abc".match(re"(\w)").get.captureBounds == true`` + ## - ``"abc".match(re"").get.captureBounds[-1] == 0 .. -1`` + ## - ``"abc".match(re"abc").get.captureBounds[-1] == 0 .. 2`` ## - ## `(captureBounds|captures).toTable` - ## : returns a table with each named capture as a key. + ## ``match: string`` + ## the full text of the match. ## - ## `(captureBounds|captures).toSeq` - ## : returns all the captures by their number. + ## ``matchBounds: HSlice[int, int]`` + ## the bounds of the match, as in ``captureBounds[]`` ## - ## `$: string` - ## : same as `match` + ## ``(captureBounds|captures).toTable`` + ## returns a table with each named capture as a key. + ## + ## ``(captureBounds|captures).toSeq`` + ## returns all the captures by their number. + ## + ## ``$: string`` + ## same as ``match`` pattern*: Regex ## The regex doing the matching. ## Not nil. str*: string ## The string that was matched against. + ## Not nil. pcreMatchBounds: seq[HSlice[cint, cint]] ## First item is the bounds of the match ## Other items are the captures ## `a` is inclusive start, `b` is exclusive end @@ -216,6 +226,18 @@ type ## for whatever reason. The message contains the error ## code. +runnableExamples: + # This MUST be kept in sync with the examples in RegexMatch + doAssert "abc".match(re"(\w)").get.captures[0] == "a" + doAssert "abc".match(re"(?\w)").get.captures["letter"] == "a" + doAssert "abc".match(re"(\w)\w").get.captures[-1] == "ab" + + doAssert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0 + doAssert 0 in "abc".match(re"(\w)").get.captureBounds == true + doAssert "abc".match(re"").get.captureBounds[-1] == 0 .. -1 + doAssert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2 + + proc destroyRegex(pattern: Regex) = pcre.free_substring(cast[cstring](pattern.pcreObj)) if pattern.pcreExtra != nil: @@ -286,7 +308,7 @@ proc matchesCrLf(pattern: Regex): bool = let newlineFlags = flags and (pcre.NEWLINE_CRLF or pcre.NEWLINE_ANY or pcre.NEWLINE_ANYCRLF) - if newlineFlags > 0u32: + if newLineFlags > 0u32: return true # get flags from build config @@ -518,33 +540,24 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = ## Like `find(...)<#find,string,Regex,int>`_, but anchored to the start of the ## string. + ## runnableExamples: - assert "foo".match(re"f").isSome - assert "foo".match(re"o").isNone + doAssert "foo".match(re"f").isSome + doAssert "foo".match(re"o").isNone - assert "abc".match(re"(\w)").get.captures[0] == "a" - assert "abc".match(re"(?\w)").get.captures["letter"] == "a" - assert "abc".match(re"(\w)\w").get.captures[-1] == "ab" - - assert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0 - assert 0 in "abc".match(re"(\w)").get.captureBounds - assert "abc".match(re"").get.captureBounds[-1] == 0 .. -1 - assert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2 return str.matchImpl(pattern, start, endpos, pcre.ANCHORED) iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch = ## Works the same as `find(...)<#find,string,Regex,int>`_, but finds every - ## non-overlapping match: - runnableExamples: - import std/sugar - assert collect(for a in "2222".findIter(re"22"): a.match) == @["22", "22"] - # not @["22", "22", "22"] + ## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not + ## ``"22", "22", "22"``. + ## ## Arguments are the same as `find(...)<#find,string,Regex,int>`_ ## ## Variants: ## - ## - `proc findAll(...)` returns a `seq[string]` - # see pcredemo for explanation => https://www.pcre.org/original/doc/html/pcredemo.html + ## - ``proc findAll(...)`` returns a ``seq[string]`` + # see pcredemo for explanation let matchesCrLf = pattern.matchesCrLf() let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and pcre.UTF8) > 0u32 @@ -565,7 +578,7 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R # either the end of the input or the string # cannot be split here - we also need to bail # if we've never matched and we've already tried to... - if flags == 0 or offset >= strlen or neverMatched: # All matches found + if offset >= strlen or neverMatched: break if matchesCrLf and offset < (str.len - 1) and @@ -581,18 +594,19 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R else: neverMatched = false offset = match.get.matchBounds.b + 1 + yield match.get proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = ## Finds the given pattern in the string between the end and start ## positions. ## - ## `start` - ## : The start point at which to start matching. `|abc` is `0`; - ## `a|bc` is `1` + ## ``start`` + ## 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 + ## ``endpos`` + ## 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) @@ -604,11 +618,12 @@ proc findAll*(str: string, pattern: Regex, start = 0, endpos = int.high): seq[st proc contains*(str: string, pattern: Regex, start = 0, endpos = int.high): bool = ## Determine if the string contains the given pattern between the end and ## start positions: - ## This function is equivalent to `isSome(str.find(pattern, start, endpos))`. + ## This function is equivalent to ``isSome(str.find(pattern, start, endpos))``. + ## runnableExamples: - assert "abc".contains(re"bc") - assert not "abc".contains(re"cd") - assert not "abc".contains(re"a", start = 1) + doAssert "abc".contains(re"bc") + doAssert not "abc".contains(re"cd") + doAssert not "abc".contains(re"a", start = 1) return isSome(str.find(pattern, start, endpos)) @@ -616,20 +631,20 @@ proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string] ## Splits the string with the given regex. This works according to the ## rules that Perl and Javascript use. ## - ## `start` behaves the same as in `find(...)<#find,string,Regex,int>`_. + ## ``start`` behaves the same as in `find(...)<#find,string,Regex,int>`_. ## runnableExamples: # - If the match is zero-width, then the string is still split: - assert "123".split(re"") == @["1", "2", "3"] + doAssert "123".split(re"") == @["1", "2", "3"] # - If the pattern has a capture in it, it is added after the string # split: - assert "12".split(re"(\d)") == @["", "1", "", "2", ""] + doAssert "12".split(re"(\d)") == @["", "1", "", "2", ""] - # - If `maxsplit != -1`, then the string will only be split - # `maxsplit - 1` times. This means that there will be `maxsplit` + # - If ``maxsplit != -1``, then the string will only be split + # ``maxsplit - 1`` times. This means that there will be ``maxsplit`` # strings in the output seq. - assert "1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"] + doAssert "1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"] result = @[] var lastIdx = start @@ -693,27 +708,28 @@ template replaceImpl(str: string, pattern: Regex, proc replace*(str: string, pattern: Regex, subproc: proc (match: RegexMatch): string): string = - ## Replaces each match of Regex in the string with `subproc`, which should - ## never be or return `nil`. + ## Replaces each match of Regex in the string with ``subproc``, which should + ## never be or return ``nil``. ## - ## If `subproc` is a `proc (RegexMatch): string`, then it is executed with + ## If ``subproc`` is a ``proc (RegexMatch): string``, then it is executed with ## 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 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. ## - ## If `subproc` is a string, the syntax is as follows: + ## If ``subproc`` is a string, the syntax is as follows: ## - ## - `$$` - literal `$` - ## - `$123` - capture number `123` - ## - `$foo` - named capture `foo` - ## - `${foo}` - same as above - ## - `$1$#` - first and second captures - ## - `$#` - first capture - ## - `$0` - full match + ## - ``$$`` - literal ``$`` + ## - ``$123`` - capture number ``123`` + ## - ``$foo`` - named capture ``foo`` + ## - ``${foo}`` - same as above + ## - ``$1$#`` - first and second captures + ## - ``$#`` - first capture + ## - ``$0`` - full match ## - ## If a given capture is missing, `IndexDefect` thrown for un-named captures - ## and `KeyError` for named captures. + ## If a given capture is missing, ``IndexDefect`` thrown for un-named captures + ## and ``KeyError`` for named captures. replaceImpl(str, pattern, subproc(match)) proc replace*(str: string, pattern: Regex, @@ -727,13 +743,13 @@ proc replace*(str: string, pattern: Regex, sub: string): string = proc escapeRe*(str: string): string {.gcsafe.} = ## Escapes the string so it doesn't match any special characters. - ## Incompatible with the Extra flag (`X`). + ## Incompatible with the Extra flag (``X``). ## ## Escaped char: `\ + * ? [ ^ ] $ ( ) { } = ! < > | : -` runnableExamples: - assert escapeRe("fly+wind") == "fly\\+wind" - assert escapeRe("!") == "\\!" - assert escapeRe("nim*") == "nim\\*" + doAssert escapeRe("fly+wind") == "fly\\+wind" + doAssert escapeRe("!") == "\\!" + doAssert escapeRe("nim*") == "nim\\*" #([\\+*?[^\]$(){}=!<>|:-]) const SpecialCharMatcher = {'\\', '+', '*', '?', '[', '^', ']', '$', '(', diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index b0c648373..9dbbd97ae 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -9,66 +9,66 @@ ## This module contains code for reading from `stdin`:idx:. On UNIX the ## linenoise library is wrapped and set up to provide default key bindings -## (e.g. you can navigate with the arrow keys). On Windows `system.readLine` +## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine`` ## is used. This suffices because Windows' console already provides the ## wanted functionality. +## +## **Examples:** +## +## .. code-block:: nim +## echo readLineFromStdin("Is Nim awesome? (Y/n):") +## var userResponse: string +## doAssert readLineFromStdin("How are you?:", line = userResponse) +## echo userResponse -runnableExamples("-r:off"): - echo readLineFromStdin("Is Nim awesome? (Y/n): ") - var line: string - while true: - let ok = readLineFromStdin("How are you? ", line) - if not ok: break # ctrl-C or ctrl-D will cause a break - if line.len > 0: echo line - echo "exiting" - - -when defined(windows): - when defined(nimPreviewSlimSystem): - import std/syncio - - proc readLineFromStdin*(prompt: string): string {. +when defined(Windows): + proc readLineFromStdin*(prompt: string): TaintedString {. tags: [ReadIOEffect, WriteIOEffect].} = ## Reads a line from stdin. stdout.write(prompt) - stdout.flushFile() result = readLine(stdin) - proc readLineFromStdin*(prompt: string, line: var string): bool {. + proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {. tags: [ReadIOEffect, WriteIOEffect].} = ## Reads a `line` from stdin. `line` must not be - ## `nil`! May throw an IO exception. - ## A line of text may be delimited by `CR`, `LF` or - ## `CRLF`. The newline character(s) are not part of the returned string. - ## Returns `false` if the end of the file has been reached, `true` - ## otherwise. If `false` is returned `line` contains no new data. + ## ``nil``! May throw an IO exception. + ## A line of text may be delimited by ``CR``, ``LF`` or + ## ``CRLF``. The newline character(s) are not part of the returned string. + ## Returns ``false`` if the end of the file has been reached, ``true`` + ## otherwise. If ``false`` is returned `line` contains no new data. stdout.write(prompt) result = readLine(stdin, line) elif defined(genode): - proc readLineFromStdin*(prompt: string): string {. + proc readLineFromStdin*(prompt: string): TaintedString {. tags: [ReadIOEffect, WriteIOEffect].} = stdin.readLine() - proc readLineFromStdin*(prompt: string, line: var string): bool {. + proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {. tags: [ReadIOEffect, WriteIOEffect].} = stdin.readLine(line) else: import linenoise - proc readLineFromStdin*(prompt: string, line: var string): bool {. + proc readLineFromStdin*(prompt: string): TaintedString {. tags: [ReadIOEffect, WriteIOEffect].} = var buffer = linenoise.readLine(prompt) if isNil(buffer): - line.setLen(0) + raise newException(IOError, "Linenoise returned nil") + result = TaintedString($buffer) + if result.string.len > 0: + historyAdd(buffer) + linenoise.free(buffer) + + proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {. + tags: [ReadIOEffect, WriteIOEffect].} = + var buffer = linenoise.readLine(prompt) + if isNil(buffer): + line.string.setLen(0) return false - line = $buffer - if line.len > 0: + line = TaintedString($buffer) + if line.string.len > 0: historyAdd(buffer) linenoise.free(buffer) result = true - - proc readLineFromStdin*(prompt: string): string {.inline.} = - if not readLineFromStdin(prompt, result): - raise newException(IOError, "Linenoise returned nil") diff --git a/lib/impure/re.nim b/lib/impure/re.nim index f125f9bcd..fe3ea96ff 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -17,10 +17,6 @@ when defined(js): ## C library. This means that your application will depend on the PCRE ## library's licence when using this module, which should not be a problem ## though. -## -## .. note:: There are also alternative nimble packages such as [tinyre](https://github.com/khchen/tinyre) -## and [regex](https://github.com/nitely/nim-regex). -## ## PCRE's licence follows: ## ## .. include:: ../../doc/regexprs.txt @@ -38,22 +34,19 @@ runnableExamples: import pcre, strutils, rtarrays -when defined(nimPreviewSlimSystem): - import std/syncio - const MaxSubpatterns* = 20 ## defines the maximum number of subpatterns that can be captured. - ## This limit still exists for `replacef` and `parallelReplace`. + ## This limit still exists for ``replacef`` and ``parallelReplace``. type RegexFlag* = enum ## options for regular expressions - reIgnoreCase = 0, ## do caseless matching - reMultiLine = 1, ## `^` and `$` match newlines within data - reDotAll = 2, ## `.` matches anything including NL - reExtended = 3, ## ignore whitespace and `#` comments - reStudy = 4 ## study the expression (may be omitted if the - ## expression will be used only once) + reIgnoreCase = 0, ## do caseless matching + reMultiLine = 1, ## ``^`` and ``$`` match newlines within data + reDotAll = 2, ## ``.`` matches anything including NL + reExtended = 3, ## ignore whitespace and ``#`` comments + reStudy = 4 ## study the expression (may be omitted if the + ## expression will be used only once) RegexDesc = object h: ptr Pcre @@ -86,7 +79,7 @@ proc rawCompile(pattern: string, flags: cint): ptr Pcre = proc finalizeRegEx(x: Regex) = # XXX This is a hack, but PCRE does not export its "free" function properly. - # Sigh. The hack relies on PCRE's implementation (see `pcre_get.c`). + # Sigh. The hack relies on PCRE's implementation (see ``pcre_get.c``). # Fortunately the implementation is unlikely to change. pcre.free_substring(cast[cstring](x.h)) if not isNil(x.e): @@ -96,8 +89,8 @@ proc re*(s: string, flags = {reStudy}): Regex = ## Constructor of regular expressions. ## ## Note that Nim's - ## extended raw string literals support the syntax `re"[abc]"` as - ## a short form for `re(r"[abc]")`. Also note that since this + ## extended raw string literals support the syntax ``re"[abc]"`` as + ## a short form for ``re(r"[abc]")``. Also note that since this ## compiles the regular expression, which is expensive, you should ## avoid putting it directly in the arguments of the functions like ## the examples show below if you plan to use it a lot of times, as @@ -148,20 +141,13 @@ proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string], else: matches[i-1] = "" return rawMatches[1] - rawMatches[0] -const MaxReBufSize* = high(cint) - ## Maximum PCRE (API 1) buffer start/size equal to `high(cint)`, which even - ## for 64-bit systems can be either 2`31`:sup:-1 or 2`63`:sup:-1. - proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string], start = 0, bufSize: int): tuple[first, last: int] = - ## returns the starting position and end position of `pattern` in `buf` - ## (where `buf` has length `bufSize` and is not necessarily `'\0'` terminated), + ## returns the starting position and end position of ``pattern`` in ``buf`` + ## (where ``buf`` has length ``bufSize`` and is not necessarily ``'\0'`` terminated), ## and the captured - ## substrings in the array `matches`. If it does not match, nothing - ## is written into `matches` and `(-1,0)` is returned. - ## - ## Note: The memory for `matches` needs to be allocated before this function is - ## called, otherwise it will just remain empty. + ## substrings in the array ``matches``. If it does not match, nothing + ## is written into ``matches`` and ``(-1,0)`` is returned. var rtarray = initRtArray[cint]((matches.len+1)*3) rawMatches = rtarray.getRawData @@ -177,31 +163,20 @@ proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string], proc findBounds*(s: string, pattern: Regex, matches: var openArray[string], start = 0): tuple[first, last: int] {.inline.} = - ## returns the starting position and end position of `pattern` in `s` - ## and the captured substrings in the array `matches`. + ## returns the starting position and end position of ``pattern`` in ``s`` + ## and the captured substrings in the array ``matches``. ## If it does not match, nothing - ## is written into `matches` and `(-1,0)` is returned. - ## - ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. - runnableExamples: - var matches = newSeq[string](1) - let (first, last) = findBounds("Hello World", re"(W\w+)", matches) - doAssert first == 6 - doAssert last == 10 - doAssert matches[0] == "World" - result = findBounds(cstring(s), pattern, matches, - min(start, MaxReBufSize), min(s.len, MaxReBufSize)) + ## is written into ``matches`` and ``(-1,0)`` is returned. + result = findBounds(cstring(s), pattern, matches, start, s.len) proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[tuple[first, last: int]], - start = 0, bufSize: int): tuple[first, last: int] = - ## returns the starting position and end position of `pattern` in `buf` - ## (where `buf` has length `bufSize` and is not necessarily `'\0'` terminated), - ## and the captured substrings in the array `matches`. - ## If it does not match, nothing is written into `matches` and - ## `(-1,0)` is returned. - ## - ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. + start = 0, bufSize = 0): tuple[first, last: int] = + ## returns the starting position and end position of ``pattern`` in ``buf`` + ## (where ``buf`` has length ``bufSize`` and is not necessarily ``'\0'`` terminated), + ## and the captured substrings in the array ``matches``. + ## If it does not match, nothing is written into ``matches`` and + ## ``(-1,0)`` is returned. var rtarray = initRtArray[cint]((matches.len+1)*3) rawMatches = rtarray.getRawData @@ -218,38 +193,17 @@ proc findBounds*(buf: cstring, pattern: Regex, proc findBounds*(s: string, pattern: Regex, matches: var openArray[tuple[first, last: int]], start = 0): tuple[first, last: int] {.inline.} = - ## returns the starting position and end position of `pattern` in `s` - ## and the captured substrings in the array `matches`. - ## If it does not match, nothing is written into `matches` and - ## `(-1,0)` is returned. - ## - ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. - runnableExamples: - var matches = newSeq[tuple[first, last: int]](1) - let (first, last) = findBounds("Hello World", re"(\w+)", matches) - doAssert first == 0 - doAssert last == 4 - doAssert matches[0] == (0, 4) - result = findBounds(cstring(s), pattern, matches, - min(start, MaxReBufSize), min(s.len, MaxReBufSize)) - -proc findBoundsImpl(buf: cstring, pattern: Regex, - start = 0, bufSize = 0, flags = 0): tuple[first, last: int] = - var rtarray = initRtArray[cint](3) - let rawMatches = rtarray.getRawData - let res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, flags.int32, - cast[ptr cint](rawMatches), 3) - - if res < 0'i32: - result = (-1, 0) - else: - result = (int(rawMatches[0]), int(rawMatches[1]-1)) + ## returns the starting position and end position of ``pattern`` in ``s`` + ## and the captured substrings in the array ``matches``. + ## If it does not match, nothing is written into ``matches`` and + ## ``(-1,0)`` is returned. + result = findBounds(cstring(s), pattern, matches, start, s.len) proc findBounds*(buf: cstring, pattern: Regex, start = 0, bufSize: int): tuple[first, last: int] = - ## returns the `first` and `last` position of `pattern` in `buf`, - ## where `buf` has length `bufSize` (not necessarily `'\0'` terminated). - ## If it does not match, `(-1,0)` is returned. + ## returns the ``first`` and ``last`` position of ``pattern`` in ``buf``, + ## where ``buf`` has length ``bufSize`` (not necessarily ``'\0'`` terminated). + ## If it does not match, ``(-1,0)`` is returned. var rtarray = initRtArray[cint](3) rawMatches = rtarray.getRawData @@ -260,14 +214,13 @@ proc findBounds*(buf: cstring, pattern: Regex, proc findBounds*(s: string, pattern: Regex, start = 0): tuple[first, last: int] {.inline.} = - ## returns the `first` and `last` position of `pattern` in `s`. - ## If it does not match, `(-1,0)` is returned. + ## returns the ``first`` and ``last`` position of ``pattern`` in ``s``. + ## If it does not match, ``(-1,0)`` is returned. ## ## Note: there is a speed improvement if the matches do not need to be captured. runnableExamples: assert findBounds("01234abc89", re"abc") == (5,7) - result = findBounds(cstring(s), pattern, - min(start, MaxReBufSize), min(s.len, MaxReBufSize)) + result = findBounds(cstring(s), pattern, start, s.len) proc matchOrFind(buf: cstring, pattern: Regex, start, bufSize: int, flags: cint): cint = var @@ -280,25 +233,21 @@ proc matchOrFind(buf: cstring, pattern: Regex, start, bufSize: int, flags: cint) proc matchLen*(s: string, pattern: Regex, matches: var openArray[string], start = 0): int {.inline.} = - ## the same as `match`, but it returns the length of the match, - ## if there is no match, `-1` is returned. Note that a match length + ## the same as ``match``, but it returns the length of the match, + ## if there is no match, ``-1`` is returned. Note that a match length ## of zero can happen. - ## - ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. result = matchOrFind(cstring(s), pattern, matches, start.cint, s.len.cint, pcre.ANCHORED) proc matchLen*(buf: cstring, pattern: Regex, matches: var openArray[string], start = 0, bufSize: int): int {.inline.} = - ## the same as `match`, but it returns the length of the match, - ## if there is no match, `-1` is returned. Note that a match length + ## the same as ``match``, but it returns the length of the match, + ## if there is no match, ``-1`` is returned. Note that a match length ## of zero can happen. - ## - ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. return matchOrFind(buf, pattern, matches, start.cint, bufSize.cint, pcre.ANCHORED) proc matchLen*(s: string, pattern: Regex, start = 0): int {.inline.} = - ## the same as `match`, but it returns the length of the match, - ## if there is no match, `-1` is returned. Note that a match length + ## the same as ``match``, but it returns the length of the match, + ## if there is no match, ``-1`` is returned. Note that a match length ## of zero can happen. ## runnableExamples: @@ -308,25 +257,24 @@ proc matchLen*(s: string, pattern: Regex, start = 0): int {.inline.} = result = matchOrFind(cstring(s), pattern, start.cint, s.len.cint, pcre.ANCHORED) proc matchLen*(buf: cstring, pattern: Regex, start = 0, bufSize: int): int {.inline.} = - ## the same as `match`, but it returns the length of the match, - ## if there is no match, `-1` is returned. Note that a match length + ## the same as ``match``, but it returns the length of the match, + ## if there is no match, ``-1`` is returned. Note that a match length ## of zero can happen. result = matchOrFind(buf, pattern, start.cint, bufSize, pcre.ANCHORED) proc match*(s: string, pattern: Regex, start = 0): bool {.inline.} = - ## returns `true` if `s[start..]` matches the `pattern`. + ## returns ``true`` if ``s[start..]`` matches the ``pattern``. result = matchLen(cstring(s), pattern, start, s.len) != -1 proc match*(s: string, pattern: Regex, matches: var openArray[string], start = 0): bool {.inline.} = - ## returns `true` if `s[start..]` matches the `pattern` and - ## the captured substrings in the array `matches`. If it does not - ## match, nothing is written into `matches` and `false` is + ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and + ## the captured substrings in the array ``matches``. If it does not + ## match, nothing is written into ``matches`` and ``false`` is ## returned. ## - ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. runnableExamples: - import std/sequtils + import sequtils var matches: array[2, string] if match("abcdefg", re"c(d)ef(g)", matches, 2): doAssert toSeq(matches) == @["d", "g"] @@ -334,23 +282,19 @@ proc match*(s: string, pattern: Regex, matches: var openArray[string], proc match*(buf: cstring, pattern: Regex, matches: var openArray[string], start = 0, bufSize: int): bool {.inline.} = - ## returns `true` if `buf[start..= 0` + ## same as ``find(s, pattern, start) >= 0`` return find(s, pattern, start) >= 0 proc contains*(s: string, pattern: Regex, matches: var openArray[string], start = 0): bool {.inline.} = - ## same as `find(s, pattern, matches, start) >= 0` - ## - ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. + ## same as ``find(s, pattern, matches, start) >= 0`` return find(s, pattern, matches, start) >= 0 proc startsWith*(s: string, prefix: Regex): bool {.inline.} = @@ -486,29 +429,25 @@ proc endsWith*(s: string, suffix: Regex): bool {.inline.} = if matchLen(s, suffix, i) == s.len - i: return true proc replace*(s: string, sub: Regex, by = ""): string = - ## Replaces `sub` in `s` by the string `by`. Captures cannot be - ## accessed in `by`. + ## Replaces ``sub`` in ``s`` by the string ``by``. Captures cannot be + ## accessed in ``by``. runnableExamples: doAssert "var1=key; var2=key2".replace(re"(\w+)=(\w+)") == "; " doAssert "var1=key; var2=key2".replace(re"(\w+)=(\w+)", "?") == "?; ?" result = "" var prev = 0 - var flags = int32(0) while prev < s.len: - var match = findBoundsImpl(s.cstring, sub, prev, s.len, flags) - flags = 0 + var match = findBounds(s, sub, prev) if match.first < 0: break add(result, substr(s, prev, match.first-1)) add(result, by) - if match.first > match.last: - # 0-len match - flags = pcre.NOTEMPTY_ATSTART + if match.last + 1 == prev: break prev = match.last + 1 add(result, substr(s, prev)) proc replacef*(s: string, sub: Regex, by: string): string = - ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` - ## with the notation `$i` and `$#` (see strutils.\`%\`). + ## Replaces ``sub`` in ``s`` by the string ``by``. Captures can be accessed in ``by`` + ## with the notation ``$i`` and ``$#`` (see strutils.\`%\`). runnableExamples: doAssert "var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") == "var1<-keykey; var2<-key2key2" @@ -526,7 +465,7 @@ proc replacef*(s: string, sub: Regex, by: string): string = proc multiReplace*(s: string, subs: openArray[ tuple[pattern: Regex, repl: string]]): string = - ## Returns a modified copy of `s` with the substitutions in `subs` + ## Returns a modified copy of ``s`` with the substitutions in ``subs`` ## applied in parallel. result = "" var i = 0 @@ -546,39 +485,36 @@ proc multiReplace*(s: string, subs: openArray[ proc transformFile*(infile, outfile: string, subs: openArray[tuple[pattern: Regex, repl: string]]) = - ## reads in the file `infile`, performs a parallel replacement (calls - ## `parallelReplace`) and writes back to `outfile`. Raises `IOError` if an + ## reads in the file ``infile``, performs a parallel replacement (calls + ## ``parallelReplace``) and writes back to ``outfile``. Raises ``IOError`` if an ## error occurs. This is supposed to be used for quick scripting. - var x = readFile(infile) + var x = readFile(infile).string writeFile(outfile, x.multiReplace(subs)) iterator split*(s: string, sep: Regex; maxsplit = -1): string = - ## Splits the string `s` into substrings. + ## Splits the string ``s`` into substrings. ## - ## Substrings are separated by the regular expression `sep` - ## (and the portion matched by `sep` is not returned). + ## Substrings are separated by the regular expression ``sep`` + ## (and the portion matched by ``sep`` is not returned). runnableExamples: - import std/sequtils + import sequtils doAssert toSeq(split("00232this02939is39an22example111", re"\d+")) == @["", "this", "is", "an", "example", ""] var last = 0 var splits = maxsplit - var x = -1 - if len(s) == 0: - last = 1 - if matchLen(s, sep, 0) == 0: - x = 0 + var x: int while last <= len(s): var first = last var sepLen = 1 - if x == 0: - inc(last) while last < len(s): x = matchLen(s, sep, last) if x >= 0: sepLen = x break inc(last) + if x == 0: + if last >= len(s): break + inc last if splits == 0: last = len(s) yield substr(s, first, last-1) if splits == 0: break @@ -586,14 +522,14 @@ iterator split*(s: string, sep: Regex; maxsplit = -1): string = inc(last, sepLen) proc split*(s: string, sep: Regex, maxsplit = -1): seq[string] {.inline.} = - ## Splits the string `s` into a seq of substrings. + ## Splits the string ``s`` into a seq of substrings. ## - ## The portion matched by `sep` is not returned. + ## The portion matched by ``sep`` is not returned. result = @[] for x in split(s, sep, maxsplit): result.add x proc escapeRe*(s: string): string = - ## escapes `s` so that it is matched verbatim when used as a regular + ## escapes ``s`` so that it is matched verbatim when used as a regular ## expression. result = "" for c in items(s): diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim index 8e2f85156..76b948e6a 100644 --- a/lib/js/asyncjs.nim +++ b/lib/js/asyncjs.nim @@ -17,42 +17,37 @@ ## ## This is roughly equivalent to the `async` keyword in JavaScript code. ## -## ```nim -## proc loadGame(name: string): Future[Game] {.async.} = -## # code -## ``` +## .. code-block:: nim +## proc loadGame(name: string): Future[Game] {.async.} = +## # code ## ## should be equivalent to ## -## ```javascript +## .. code-block:: javascript ## async function loadGame(name) { ## // code ## } -## ``` ## ## A call to an asynchronous procedure usually needs `await` to wait for ## the completion of the `Future`. ## -## ```nim +## .. code-block:: nim ## var game = await loadGame(name) -## ``` ## ## Often, you might work with callback-based API-s. You can wrap them with ## asynchronous procedures using promises and `newPromise`: ## -## ```nim +## .. code-block:: nim ## proc loadGame(name: string): Future[Game] = ## var promise = newPromise() do (resolve: proc(response: Game)): ## cbBasedLoadGame(name) do (game: Game): ## resolve(game) ## return promise -## ``` ## ## Forward definitions work properly, you just need to always add the `{.async.}` pragma: ## -## ```nim +## .. code-block:: nim ## proc loadGame(name: string): Future[Game] {.async.} -## ``` ## ## JavaScript compatibility ## ======================== @@ -62,21 +57,18 @@ ## If you need to use this module with older versions of JavaScript, you can ## use a tool that backports the resulting JavaScript code, as babel. -# xxx code: javascript above gives `LanguageXNotSupported` warning. +import std/jsffi +import std/macros when not defined(js) and not defined(nimsuggest): {.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".} -import std/jsffi -import std/macros -import std/private/since - type Future*[T] = ref object future*: T ## Wraps the return type of an asynchronous procedure. - PromiseJs* {.importjs: "Promise".} = ref object + PromiseJs* {.importcpp: "Promise".} = ref object ## A JavaScript Promise. @@ -100,18 +92,10 @@ proc isFutureVoid(node: NimNode): bool = node[1].kind == nnkIdent and $node[1] == "void" proc generateJsasync(arg: NimNode): NimNode = - if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo, nnkProcTy}: + if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}: error("Cannot transform this node kind into an async proc." & " proc/method definition or lambda node expected.") - # Transform type X = proc (): something {.async.} - # into type X = proc (): Future[something] - if arg.kind == nnkProcTy: - result = arg - if arg[0][0].kind == nnkEmpty: - result[0][0] = quote do: Future[void] - return result - result = arg var isVoid = false let jsResolve = ident("jsResolve") @@ -127,17 +111,16 @@ proc generateJsasync(arg: NimNode): NimNode = if len(code) > 0: var awaitFunction = quote: - proc await[T](f: Future[T]): T {.importjs: "(await #)", used.} + proc await[T](f: Future[T]): T {.importcpp: "(await #)", used.} result.body.add(awaitFunction) var resolve: NimNode if isVoid: resolve = quote: - var `jsResolve` {.importjs: "undefined".}: Future[void] + var `jsResolve` {.importcpp: "undefined".}: Future[void] else: resolve = quote: - proc jsResolve[T](a: T): Future[T] {.importjs: "#", used.} - proc jsResolve[T](a: Future[T]): Future[T] {.importjs: "#", used.} + proc jsResolve[T](a: T): Future[T] {.importcpp: "#", used.} result.body.add(resolve) else: result.body = newEmptyNode() @@ -164,104 +147,10 @@ macro async*(arg: untyped): untyped = else: result = generateJsasync(arg) -proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importjs: "(new Promise(#))".} +proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importcpp: "(new Promise(#))".} ## A helper for wrapping callback-based functions ## into promises and async procedures. -proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importjs: "(new Promise(#))".} +proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importcpp: "(new Promise(#))".} ## A helper for wrapping callback-based functions ## into promises and async procedures. - -template maybeFuture(T): untyped = - # avoids `Future[Future[T]]` - when T is Future: T - else: Future[T] - - -since (1, 5, 1): - #[ - TODO: - * map `Promise.all()` - * proc toString*(a: Error): cstring {.importjs: "#.toString()".} - - Note: - We probably can't have a `waitFor` in js in browser (single threaded), but maybe it would be possible - in in nodejs, see https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options - and https://stackoverflow.com/questions/61377358/javascript-wait-for-async-call-to-finish-before-returning-from-function-witho - ]# - - type Error* {.importjs: "Error".} = ref object of JsRoot - ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error - message*: cstring - name*: cstring - - type OnReject* = proc(reason: Error) - - proc then*[T](future: Future[T], onSuccess: proc, onReject: OnReject = nil): auto = - ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then - ## Returns a `Future` from the return type of `onSuccess(T.default)`. - runnableExamples("-r:off"): - from std/sugar import `=>` - - proc fn(n: int): Future[int] {.async.} = - if n >= 7: raise newException(ValueError, "foobar: " & $n) - else: result = n * 2 - - proc asyncFact(n: int): Future[int] {.async.} = - if n > 0: result = n * await asyncFact(n-1) - else: result = 1 - - proc main() {.async.} = - block: # then - assert asyncFact(3).await == 3*2 - assert asyncFact(3).then(asyncFact).await == 6*5*4*3*2 - let x1 = await fn(3) - assert x1 == 3 * 2 - let x2 = await fn(4) - .then((a: int) => a.float) - .then((a: float) => $a) - assert x2 == "8.0" - - block: # then with `onReject` callback - var witness = 1 - await fn(6).then((a: int) => (witness = 2), (r: Error) => (witness = 3)) - assert witness == 2 - await fn(7).then((a: int) => (witness = 2), (r: Error) => (witness = 3)) - assert witness == 3 - - template impl(call): untyped = - # see D20210421T014713 - when typeof(block: call) is void: - var ret: Future[void] - else: - var ret = default(maybeFuture(typeof(call))) - typeof(ret) - when T is void: - type A = impl(onSuccess()) - else: - type A = impl(onSuccess(default(T))) - var ret: A - asm "`ret` = `future`.then(`onSuccess`, `onReject`)" - return ret - - proc catch*[T](future: Future[T], onReject: OnReject): Future[void] = - ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch - runnableExamples("-r:off"): - from std/sugar import `=>` - from std/strutils import contains - - proc fn(n: int): Future[int] {.async.} = - if n >= 7: raise newException(ValueError, "foobar: " & $n) - else: result = n * 2 - - proc main() {.async.} = - var reason: Error - await fn(6).catch((r: Error) => (reason = r)) # note: `()` are needed, `=> reason = r` would not work - assert reason == nil - await fn(7).catch((r: Error) => (reason = r)) - assert reason != nil - assert "foobar: 7" in $reason.message - - discard main() - - asm "`result` = `future`.catch(`onReject`)" diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 36434846a..59d1381c7 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -9,37 +9,6 @@ ## Declaration of the Document Object Model for the `JavaScript backend ## `_. -## -## -## Document Ready -## -------------- -## -## * Basic example of a document ready: -runnableExamples"-b:js -r:off": - proc example(e: Event) = echo "Document is ready" - document.addEventListener("DOMContentLoaded", example) # You can also use "load" event. -## * This example runs 5 seconds after the document ready: -runnableExamples"-b:js -r:off": - proc example() = echo "5 seconds after document ready" - proc domReady(e: Event) = discard setTimeout(example, 5_000) # Document is ready. - document.addEventListener("DOMContentLoaded", domReady) -## Document onUnload -## ----------------- -## -## * Simple example of how to implement code that runs when the page unloads: -runnableExamples"-b:js -r:off": - proc example(e: Event) = echo "Document is unloaded" - document.addEventListener("unload", example) # You can also use "beforeunload". -## Document Autorefresh -## -------------------- -## -## * Minimal example of a document autorefresh: -runnableExamples"-b:js -r:off": - proc example() = window.location.reload() - discard setTimeout(example, 5_000) -## - For more examples, see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener - - import std/private/since when not defined(js): {.error: "This module only works on the JavaScript platform".} @@ -48,7 +17,8 @@ const DomApiVersion* = 3 ## the version of DOM API we try to follow. No guarantees though. type - EventTarget* {.importc.} = ref object of RootObj + EventTarget* = ref EventTargetObj + EventTargetObj {.importc.} = object of RootObj onabort*: proc (event: Event) {.closure.} onblur*: proc (event: Event) {.closure.} onchange*: proc (event: Event) {.closure.} @@ -67,7 +37,6 @@ type onmouseup*: proc (event: Event) {.closure.} onreset*: proc (event: Event) {.closure.} onselect*: proc (event: Event) {.closure.} - onstorage*: proc (event: Event) {.closure.} onsubmit*: proc (event: Event) {.closure.} onunload*: proc (event: Event) {.closure.} onloadstart*: proc (event: Event) {.closure.} @@ -103,7 +72,6 @@ type Resize = "resize", Scroll = "scroll", Select = "select", - Storage = "storage", Unload = "unload", Wheel = "wheel" @@ -157,9 +125,10 @@ type rangeCount*: int `type`*: cstring - Storage* {.importc.} = ref object + LocalStorage* {.importc.} = ref object - Window* {.importc.} = ref object of EventTarget + Window* = ref WindowObj + WindowObj {.importc.} = object of EventTargetObj document*: Document event*: Event history*: History @@ -184,13 +153,14 @@ type screen*: Screen performance*: Performance onpopstate*: proc (event: Event) - localStorage*: Storage - sessionStorage*: Storage + localStorage*: LocalStorage parent*: Window - Frame* {.importc.} = ref object of Window + Frame* = ref FrameObj + FrameObj {.importc.} = object of WindowObj - ClassList* {.importc.} = ref object of RootObj + ClassList* = ref ClassListObj + ClassListObj {.importc.} = object of RootObj NodeType* = enum ElementNode = 1, @@ -206,7 +176,8 @@ type DocumentFragmentNode, NotationNode - Node* {.importc.} = ref object of EventTarget + Node* = ref NodeObj + NodeObj {.importc.} = object of EventTargetObj attributes*: seq[Node] childNodes*: seq[Node] children*: seq[Node] @@ -230,7 +201,8 @@ type parentElement*: Element isConnected*: bool - Document* {.importc.} = ref object of Node + Document* = ref DocumentObj + DocumentObj {.importc.} = object of NodeObj activeElement*: Element documentElement*: Element alinkColor*: cstring @@ -241,13 +213,11 @@ 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] @@ -257,7 +227,8 @@ type links*: seq[LinkElement] fonts*: FontFaceSet - Element* {.importc.} = ref object of Node + Element* = ref ElementObj + ElementObj {.importc.} = object of NodeObj className*: cstring classList*: ClassList checked*: bool @@ -278,7 +249,8 @@ type offsetLeft*: int offsetTop*: int - ValidityState* {.importc.} = ref object ## see `docs`_ + ValidityState* = ref ValidityStateObj ## see `docs`_ + ValidityStateObj {.importc.} = object badInput*: bool customError*: bool patternMismatch*: bool @@ -291,21 +263,25 @@ type valid*: bool valueMissing*: bool - Blob* {.importc.} = ref object of RootObj ## see `docs`_ + Blob* = ref BlobObj ## see `docs`_ + BlobObj {.importc.} = object of RootObj size*: int `type`*: cstring - File* {.importc.} = ref object of Blob ## see `docs`_ + File* = ref FileObj ## see `docs`_ + FileObj {.importc.} = object of Blob lastModified*: int name*: cstring - TextAreaElement* {.importc.} = ref object of Element ## see `docs`_ + TextAreaElement* = ref TextAreaElementObj ## see `docs`_ + TextAreaElementObj {.importc.} = object of Element value*: cstring selectionStart*, selectionEnd*: int selectionDirection*: cstring rows*, cols*: int - InputElement* {.importc.} = ref object of Element ## see `docs`_ + InputElement* = ref InputElementObj ## see `docs`_ + InputElementObj {.importc.} = object of Element # Properties related to the parent form formAction*: cstring formEncType*: cstring @@ -357,13 +333,15 @@ type valueAsDate*: cstring valueAsNumber*: float - LinkElement* {.importc.} = ref object of Element + LinkElement* = ref LinkObj + LinkObj {.importc.} = object of ElementObj target*: cstring text*: cstring x*: int y*: int - EmbedElement* {.importc.} = ref object of Element + EmbedElement* = ref EmbedObj + EmbedObj {.importc.} = object of ElementObj height*: int hspace*: int src*: cstring @@ -371,18 +349,21 @@ type `type`*: cstring vspace*: int - AnchorElement* {.importc.} = ref object of Element + AnchorElement* = ref AnchorObj + AnchorObj {.importc.} = object of ElementObj text*: cstring x*, y*: int - OptionElement* {.importc.} = ref object of Element + OptionElement* = ref OptionObj + OptionObj {.importc.} = object of ElementObj defaultSelected*: bool selected*: bool selectedIndex*: int text*: cstring value*: cstring - FormElement* {.importc.} = ref object of Element ## see `docs`_ + FormElement* = ref FormObj ## see `docs`_ + FormObj {.importc.} = object of ElementObj acceptCharset*: cstring action*: cstring autocomplete*: cstring @@ -394,7 +375,8 @@ type noValidate*: bool target*: cstring - ImageElement* {.importc.} = ref object of Element + ImageElement* = ref ImageObj + ImageObj {.importc.} = object of ElementObj border*: int complete*: bool height*: int @@ -404,7 +386,8 @@ type vspace*: int width*: int - Style* {.importc.} = ref object of RootObj + Style* = ref StyleObj + StyleObj {.importc.} = object of RootObj alignContent*: cstring alignItems*: cstring alignSelf*: cstring @@ -780,7 +763,8 @@ type AtTarget, BubblingPhase - Event* {.importc.} = ref object of RootObj ## see `docs`_ + Event* = ref EventObj ## see `docs`_ + EventObj {.importc.} = object of RootObj bubbles*: bool cancelBubble*: bool cancelable*: bool @@ -792,11 +776,13 @@ type `type`*: cstring isTrusted*: bool - UIEvent* {.importc.} = ref object of Event ## see `docs`_ + UIEvent* = ref UIEventObj ## see `docs`_ + UIEventObj {.importc.} = object of Event detail*: int64 view*: Window - KeyboardEvent* {.importc.} = ref object of UIEvent ## see `docs`_ + KeyboardEvent* = ref KeyboardEventObj ## see `docs`_ + KeyboardEventObj {.importc.} = object of UIEvent altKey*, ctrlKey*, metaKey*, shiftKey*: bool code*: cstring isComposing*: bool @@ -1158,7 +1144,8 @@ type FourthButton = 8, FifthButton = 16 - MouseEvent* {.importc.} = ref object of UIEvent ## see `docs`_ + MouseEvent* = ref MouseEventObj ## see `docs`_ + MouseEventObj {.importc.} = object of UIEvent altKey*, ctrlKey*, metaKey*, shiftKey*: bool button*: int buttons*: int @@ -1175,11 +1162,13 @@ type File = "file", String = "string" - DataTransferItem* {.importc.} = ref object of RootObj ## see `docs`_ + DataTransferItem* = ref DataTransferItemObj ## see `docs`_ + DataTransferItemObj {.importc.} = object of RootObj kind*: cstring `type`*: cstring - DataTransfer* {.importc.} = ref object of RootObj ## see `docs`_ + DataTransfer* = ref DataTransferObj ## see `docs`_ + DataTransferObj {.importc.} = object of RootObj dropEffect*: cstring effectAllowed*: cstring files*: seq[Element] @@ -1221,16 +1210,11 @@ type ## see `docs`_ clipboardData*: DataTransfer - StorageEvent* {.importc.} = ref object of Event ## see `docs`_ - key*: cstring - newValue*, oldValue*: cstring - storageArea*: Storage - url*: cstring - TouchList* {.importc.} = ref object of RootObj length*: int - Touch* {.importc.} = ref object of RootObj + Touch* = ref TouchObj + TouchObj {.importc.} = object of RootObj identifier*: int screenX*, screenY*, clientX*, clientY*, pageX*, pageY*: int target*: Element @@ -1238,10 +1222,12 @@ type rotationAngle*: int force*: float - TouchEvent* {.importc.} = ref object of UIEvent + TouchEvent* = ref TouchEventObj + TouchEventObj {.importc.} = object of UIEvent changedTouches*, targetTouches*, touches*: seq[Touch] - Location* {.importc.} = ref object of RootObj + Location* = ref LocationObj + LocationObj {.importc.} = object of RootObj hash*: cstring host*: cstring hostname*: cstring @@ -1252,10 +1238,12 @@ type search*: cstring origin*: cstring - History* {.importc.} = ref object of RootObj + History* = ref HistoryObj + HistoryObj {.importc.} = object of RootObj length*: int - Navigator* {.importc.} = ref object of RootObj + Navigator* = ref NavigatorObj + NavigatorObj {.importc.} = object of RootObj appCodeName*: cstring appName*: cstring appVersion*: cstring @@ -1293,7 +1281,8 @@ type ToolBar* = LocationBar StatusBar* = LocationBar - Screen* {.importc.} = ref object of RootObj + Screen = ref ScreenObj + ScreenObj {.importc.} = object of RootObj availHeight*: int availWidth*: int colorDepth*: int @@ -1302,7 +1291,7 @@ type width*: int TimeOut* {.importc.} = ref object of RootObj - Interval* {.importc.} = ref object of RootObj + Interval* {.importc.} = object of RootObj AddEventListenerOptions* = object capture*: bool @@ -1318,46 +1307,41 @@ type ready*: FontFaceSetReady onloadingdone*: proc(event: Event) - ScrollIntoViewOptions* = object - behavior*: cstring - `block`*: cstring - inline*: cstring - - MediaQueryList* {.importc.} = ref object of EventTarget - matches*: bool - media*: cstring - since (1, 3): type DomParser* = ref object ## DOM Parser object (defined on browser only, may not be on NodeJS). ## * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser ## - ## ```nim + ## .. code-block:: nim ## let prsr = newDomParser() ## discard prsr.parseFromString("Hello World".cstring, "text/html".cstring) - ## ``` - DomException* {.importc.} = ref object + DomException* = ref DOMExceptionObj ## The DOMException interface represents an abnormal event (called an exception) ## which occurs as a result of calling a method or accessing a property of a web API. ## Each exception has a name, which is a short "CamelCase" style string identifying ## the error or abnormal condition. ## https://developer.mozilla.org/en-US/docs/Web/API/DOMException - FileReader* {.importc.} = ref object of EventTarget + DOMExceptionObj {.importc.} = object + + FileReader* = ref FileReaderObj ## The FileReader object lets web applications asynchronously read the contents of files ## (or raw data buffers) stored on the user's computer, using File or Blob objects to specify ## the file or data to read. ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader + FileReaderObj {.importc.} = object of EventTargetObj + FileReaderState* = distinct range[0'u16..2'u16] RootNodeOptions* = object of RootObj composed*: bool DocumentOrShadowRoot* {.importc.} = object of RootObj activeElement*: Element # styleSheets*: StyleSheetList - ShadowRoot* {.importc.} = ref object of DocumentOrShadowRoot + ShadowRoot* = ref ShadowRootObj + ShadowRootObj {.importc.} = object of DocumentOrShadowRoot delegatesFocus*: bool host*: Element innerHTML*: cstring @@ -1366,7 +1350,8 @@ since (1, 3): mode*: cstring delegatesFocus*: bool - HTMLSlotElement* {.importc.} = ref object of RootObj + HTMLSlotElement* = ref HTMLSlotElementObj + HTMLSlotElementObj {.importc.} = object of RootObj name*: cstring SlotOptions* = object of RootObj flatten*: bool @@ -1384,9 +1369,6 @@ proc `class=`*(n: Node; v: cstring) {.importcpp: "#.className = #", nodecl.} proc value*(n: Node): cstring {.importcpp: "#.value", nodecl.} proc `value=`*(n: Node; v: cstring) {.importcpp: "#.value = #", nodecl.} -proc checked*(n: Node): bool {.importcpp: "#.checked", nodecl.} -proc `checked=`*(n: Node; v: bool) {.importcpp: "#.checked = #", nodecl.} - proc `disabled=`*(n: Node; v: bool) {.importcpp: "#.disabled = #", nodecl.} when defined(nodejs): @@ -1475,14 +1457,11 @@ else: proc insertBefore*(n, newNode, before: Node) {.importcpp.} proc getElementById*(d: Document, id: cstring): Element {.importcpp.} proc createElement*(d: Document, identifier: cstring): Element {.importcpp.} - proc createElementNS*(d: Document, namespaceURI, qualifiedIdentifier: cstring): Element {.importcpp.} proc createTextNode*(d: Document, identifier: cstring): Node {.importcpp.} proc createComment*(d: Document, data: cstring): Node {.importcpp.} proc setTimeout*(action: proc(); ms: int): TimeOut {.importc, nodecl.} proc clearTimeout*(t: TimeOut) {.importc, nodecl.} -proc setInterval*(action: proc(); ms: int): Interval {.importc, nodecl.} -proc clearInterval*(i: Interval) {.importc, nodecl.} {.push importcpp.} @@ -1496,8 +1475,8 @@ proc removeEventListener*(et: EventTarget; ev: cstring; cb: proc(ev: Event)) proc alert*(w: Window, msg: cstring) proc back*(w: Window) proc blur*(w: Window) -proc clearInterval*(w: Window, interval: Interval) -proc clearTimeout*(w: Window, timeout: TimeOut) +proc clearInterval*(w: Window, interval: ref Interval) +proc clearTimeout*(w: Window, timeout: ref TimeOut) proc close*(w: Window) proc confirm*(w: Window, msg: cstring): bool proc disableExternalCapture*(w: Window) @@ -1506,9 +1485,7 @@ proc find*(w: Window, text: cstring, caseSensitive = false, backwards = false) proc focus*(w: Window) proc forward*(w: Window) -proc getComputedStyle*(w: Window, e: Node, pe: Node = nil): Style - ## .. warning:: The returned Style may or may not be read-only at run-time in the browser. getComputedStyle is performance costly. - +proc getComputedStyle*(w: Window, e: Node, pe:Node = nil): Style proc handleEvent*(w: Window, e: Event) proc home*(w: Window) proc moveBy*(w: Window, x, y: int) @@ -1522,14 +1499,13 @@ proc resizeTo*(w: Window, x, y: int) proc routeEvent*(w: Window, event: Event) proc scrollBy*(w: Window, x, y: int) proc scrollTo*(w: Window, x, y: int) -proc setInterval*(w: Window, code: cstring, pause: int): Interval -proc setInterval*(w: Window, function: proc (), pause: int): Interval -proc setTimeout*(w: Window, code: cstring, pause: int): TimeOut -proc setTimeout*(w: Window, function: proc (), pause: int): Interval +proc setInterval*(w: Window, code: cstring, pause: int): ref Interval +proc setInterval*(w: Window, function: proc (), pause: int): ref Interval +proc setTimeout*(w: Window, code: cstring, pause: int): ref TimeOut +proc setTimeout*(w: Window, function: proc (), pause: int): ref Interval proc stop*(w: Window) proc requestAnimationFrame*(w: Window, function: proc (time: float)): int proc cancelAnimationFrame*(w: Window, id: int) -proc matchMedia*(w: Window, mediaQueryString: cstring): MediaQueryList # Node "methods" proc appendData*(n: Node, data: cstring) @@ -1546,7 +1522,6 @@ proc removeAttribute*(n: Node, attr: cstring) proc removeAttributeNode*(n, attr: Node) proc replaceData*(n: Node, start, len: int, text: cstring) proc scrollIntoView*(n: Node) -proc scrollIntoView*(n: Node, options: ScrollIntoViewOptions) proc setAttribute*(n: Node, name, value: cstring) proc setAttributeNode*(n: Node, attr: Node) proc querySelector*(n: Node, selectors: cstring): Element @@ -1679,12 +1654,12 @@ proc getRangeAt*(s: Selection, index: int): Range converter toString*(s: Selection): cstring proc `$`*(s: Selection): string = $(s.toString()) -# Storage "methods" -proc getItem*(s: Storage, key: cstring): cstring -proc setItem*(s: Storage, key, value: cstring) -proc hasItem*(s: Storage, key: cstring): bool -proc clear*(s: Storage) -proc removeItem*(s: Storage, key: cstring) +# LocalStorage "methods" +proc getItem*(ls: LocalStorage, key: cstring): cstring +proc setItem*(ls: LocalStorage, key, value: cstring) +proc hasItem*(ls: LocalStorage, key: cstring): bool +proc clear*(ls: LocalStorage) +proc removeItem*(ls: LocalStorage, key: cstring) {.pop.} @@ -1770,65 +1745,5 @@ since (1, 3): ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString proc readAsDataURL*(f: FileReader, b: Blob) {.importcpp: "#.readAsDataURL(#)".} ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL - proc readAsText*(f: FileReader, b: Blob|File, encoding = cstring"UTF-8") {.importcpp: "#.readAsText(#, #)".} + proc readAsText*(f: FileReader, b: Blob, encoding = cstring"UTF-8") {.importcpp: "#.readAsText(#, #)".} ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText - -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/dom_extensions.nim b/lib/js/dom_extensions.nim new file mode 100644 index 000000000..f7d37f4bf --- /dev/null +++ b/lib/js/dom_extensions.nim @@ -0,0 +1,5 @@ +import std/dom + +{.push importcpp.} +proc elementsFromPoint*(n: DocumentOrShadowRoot; x, y: float): seq[Element] +{.pop.} \ No newline at end of file diff --git a/lib/js/jsconsole.nim b/lib/js/jsconsole.nim index e74127334..87dd9eeec 100644 --- a/lib/js/jsconsole.nim +++ b/lib/js/jsconsole.nim @@ -9,18 +9,6 @@ ## Wrapper for the `console` object for the `JavaScript backend ## `_. -## -## Styled Messages -## =============== -## -## CSS-styled messages in the browser are useful for debugging purposes. -## To use them, prefix the message with one or more `%c`, -## and provide the CSS style as the last argument. -## The amount of `%c`'s must match the amount of CSS-styled strings. -## -runnableExamples("-r:off"): - console.log "%c My Debug Message", "color: red" # Notice the "%c" - console.log "%c My Debug %c Message", "color: red", "font-size: 2em" import std/private/since, std/private/miscdollars # toLocation @@ -87,7 +75,7 @@ since (1, 5): func getMsg(info: InstantiationInfo; msg: string): string = var temp = "" temp.toLocation(info.filename, info.line, info.column + 1) - result.addQuoted("[jsAssert] " & temp) + result.addQuoted(temp) result.add ',' result.addQuoted(msg) @@ -110,16 +98,5 @@ since (1, 5): {.line: loc.}: {.emit: ["console.assert(", assertion, ", ", msg, ");"].} - func dir*(console: Console; obj: auto) {.importcpp.} - ## https://developer.mozilla.org/en-US/docs/Web/API/Console/dir - - func dirxml*(console: Console; obj: auto) {.importcpp.} - ## https://developer.mozilla.org/en-US/docs/Web/API/Console/dirxml - - func timeStamp*(console: Console; label: cstring) {.importcpp.} - ## https://developer.mozilla.org/en-US/docs/Web/API/Console/timeStamp - ## - ## ..warning:: non-standard - var console* {.importc, nodecl.}: Console diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim index 781e8fd57..3af33b535 100644 --- a/lib/js/jscore.nim +++ b/lib/js/jscore.nim @@ -13,7 +13,6 @@ ## specific requirements and solely targets JavaScript, you should be using ## the relevant functions in the `math`, `json`, and `times` stdlib ## modules instead. -import std/private/since when not defined(js): {.error: "This module only works on the JavaScript platform".} @@ -105,35 +104,3 @@ proc setFullYear*(d: DateTime, year: int) {.importcpp.} #JSON library proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.} proc parse*(l: JsonLib, s: cstring): JsRoot {.importcpp.} - - -since (1, 5): - func debugger*() {.importjs: "debugger@".} - ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger - - func copyWithin*[T](self: openArray[T]; target: int): seq[T] {.importjs: "#.copyWithin(#)".} - func copyWithin*[T](self: openArray[T]; target, start: int): seq[T] {.importjs: "#.copyWithin(#, #)".} - func copyWithin*[T](self: openArray[T]; target, start, ends: int): seq[T] {.importjs: "#.copyWithin(#, #, #)".} = - ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin - ## `copyWithin` uses shallow copy. - runnableExamples: - assert ['a', 'b', 'c', 'd', 'e'].copyWithin(0, 3, 4) == @['d', 'b', 'c', 'd', 'e'] - assert ['a', 'b', 'c', 'd', 'e'].copyWithin(1, 3) == @['a', 'd', 'e', 'd', 'e'] - assert [1, 2, 3, 4, 5].copyWithin(-2) == @[1, 2, 3, 1, 2] - 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 db40e7515..8220013c1 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -21,7 +21,7 @@ runnableExamples: var document {.importc, nodecl.}: JsObject var console {.importc, nodecl.}: JsObject # import the "$" function - proc jq(selector: JsObject): JsObject {.importjs: "$$(#)".} + proc jq(selector: JsObject): JsObject {.importcpp: "$$(#)".} # Use jQuery to make the following code run, after the document is ready. # This uses an experimental `.()` operator for `JsObject`, to emit @@ -64,13 +64,13 @@ proc validJsName(name: string): bool = if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}: return false -template mangleJsName(name: string): string = +template mangleJsName(name: cstring): cstring = inc nameCounter "mangledName" & $nameCounter # only values that can be mapped 1 to 1 with cstring should be keys: they have an injective function with cstring -proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importjs: "parseInt(#)".} +proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importcpp: "parseInt(#)".} proc toJsKey*[T: enum](text: cstring, t: type T): T = T(text.toJsKey(int)) @@ -78,7 +78,7 @@ proc toJsKey*[T: enum](text: cstring, t: type T): T = proc toJsKey*(text: cstring, t: type cstring): cstring = text -proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importjs: "parseFloat(#)".} +proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importcpp: "parseFloat(#)".} type JsKey* = concept a, type T @@ -103,10 +103,10 @@ var jsFilename* {.importc: "__filename", nodecl.}: cstring ## JavaScript's __filename pseudo-variable. -proc isNull*[T](x: T): bool {.noSideEffect, importjs: "(# === null)".} +proc isNull*[T](x: T): bool {.noSideEffect, importcpp: "(# === null)".} ## Checks if a value is exactly null. -proc isUndefined*[T](x: T): bool {.noSideEffect, importjs: "(# === undefined)".} +proc isUndefined*[T](x: T): bool {.noSideEffect, importcpp: "(# === undefined)".} ## Checks if a value is exactly undefined. # Exceptions @@ -121,35 +121,35 @@ type JsURIError* {.importc: "URIError".} = object of JsError # New -proc newJsObject*: JsObject {.importjs: "{@}".} +proc newJsObject*: JsObject {.importcpp: "{@}".} ## Creates a new empty JsObject. -proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {.importjs: "{@}".} +proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {.importcpp: "{@}".} ## Creates a new empty JsAssoc with key type `K` and value type `V`. # Checks proc hasOwnProperty*(x: JsObject, prop: cstring): bool - {.importjs: "#.hasOwnProperty(#)".} + {.importcpp: "#.hasOwnProperty(#)".} ## Checks, whether `x` has a property of name `prop`. -proc jsTypeOf*(x: JsObject): cstring {.importjs: "typeof(#)".} +proc jsTypeOf*(x: JsObject): cstring {.importcpp: "typeof(#)".} ## Returns the name of the JsObject's JavaScript type as a cstring. -proc jsNew*(x: auto): JsObject {.importjs: "(new #)".} +proc jsNew*(x: auto): JsObject {.importcpp: "(new #)".} ## Turns a regular function call into an invocation of the ## JavaScript's `new` operator. -proc jsDelete*(x: auto): JsObject {.importjs: "(delete #)".} +proc jsDelete*(x: auto): JsObject {.importcpp: "(delete #)".} ## JavaScript's `delete` operator. proc require*(module: cstring): JsObject {.importc.} ## JavaScript's `require` function. # Conversion to and from JsObject -proc to*(x: JsObject, T: typedesc): T {.importjs: "(#)".} +proc to*(x: JsObject, T: typedesc): T {.importcpp: "(#)".} ## Converts a JsObject `x` to type `T`. -proc toJs*[T](val: T): JsObject {.importjs: "(#)".} +proc toJs*[T](val: T): JsObject {.importcpp: "(#)".} ## Converts a value of any type to type JsObject. template toJs*(s: string): JsObject = cstring(s).toJs @@ -160,50 +160,49 @@ macro jsFromAst*(n: untyped): untyped = result = newProc(procType = nnkDo, body = result) return quote: toJs(`result`) -proc `&`*(a, b: cstring): cstring {.importjs: "(# + #)".} +proc `&`*(a, b: cstring): cstring {.importcpp: "(# + #)".} ## Concatenation operator for JavaScript strings. -proc `+`*(x, y: JsObject): JsObject {.importjs: "(# + #)".} -proc `-`*(x, y: JsObject): JsObject {.importjs: "(# - #)".} -proc `*`*(x, y: JsObject): JsObject {.importjs: "(# * #)".} -proc `/`*(x, y: JsObject): JsObject {.importjs: "(# / #)".} -proc `%`*(x, y: JsObject): JsObject {.importjs: "(# % #)".} -proc `+=`*(x, y: JsObject): JsObject {.importjs: "(# += #)", discardable.} -proc `-=`*(x, y: JsObject): JsObject {.importjs: "(# -= #)", discardable.} -proc `*=`*(x, y: JsObject): JsObject {.importjs: "(# *= #)", discardable.} -proc `/=`*(x, y: JsObject): JsObject {.importjs: "(# /= #)", discardable.} -proc `%=`*(x, y: JsObject): JsObject {.importjs: "(# %= #)", discardable.} -proc `++`*(x: JsObject): JsObject {.importjs: "(++#)".} -proc `--`*(x: JsObject): JsObject {.importjs: "(--#)".} -proc `>`*(x, y: JsObject): JsObject {.importjs: "(# > #)".} -proc `<`*(x, y: JsObject): JsObject {.importjs: "(# < #)".} -proc `>=`*(x, y: JsObject): JsObject {.importjs: "(# >= #)".} -proc `<=`*(x, y: JsObject): JsObject {.importjs: "(# <= #)".} -proc `**`*(x, y: JsObject): JsObject {.importjs: "((#) ** #)".} - # (#) needed, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812 -proc `and`*(x, y: JsObject): JsObject {.importjs: "(# && #)".} -proc `or`*(x, y: JsObject): JsObject {.importjs: "(# || #)".} -proc `not`*(x: JsObject): JsObject {.importjs: "(!#)".} -proc `in`*(x, y: JsObject): JsObject {.importjs: "(# in #)".} +proc `+` *(x, y: JsObject): JsObject {.importcpp: "(# + #)".} +proc `-` *(x, y: JsObject): JsObject {.importcpp: "(# - #)".} +proc `*` *(x, y: JsObject): JsObject {.importcpp: "(# * #)".} +proc `/` *(x, y: JsObject): JsObject {.importcpp: "(# / #)".} +proc `%` *(x, y: JsObject): JsObject {.importcpp: "(# % #)".} +proc `+=` *(x, y: JsObject): JsObject {.importcpp: "(# += #)", discardable.} +proc `-=` *(x, y: JsObject): JsObject {.importcpp: "(# -= #)", discardable.} +proc `*=` *(x, y: JsObject): JsObject {.importcpp: "(# *= #)", discardable.} +proc `/=` *(x, y: JsObject): JsObject {.importcpp: "(# /= #)", discardable.} +proc `%=` *(x, y: JsObject): JsObject {.importcpp: "(# %= #)", discardable.} +proc `++` *(x: JsObject): JsObject {.importcpp: "(++#)".} +proc `--` *(x: JsObject): JsObject {.importcpp: "(--#)".} +proc `>` *(x, y: JsObject): JsObject {.importcpp: "(# > #)".} +proc `<` *(x, y: JsObject): JsObject {.importcpp: "(# < #)".} +proc `>=` *(x, y: JsObject): JsObject {.importcpp: "(# >= #)".} +proc `<=` *(x, y: JsObject): JsObject {.importcpp: "(# <= #)".} +proc `**` *(x, y: JsObject): JsObject {.importcpp: "((#) ** #)".} +proc `and`*(x, y: JsObject): JsObject {.importcpp: "(# && #)".} +proc `or` *(x, y: JsObject): JsObject {.importcpp: "(# || #)".} +proc `not`*(x: JsObject): JsObject {.importcpp: "(!#)".} +proc `in` *(x, y: JsObject): JsObject {.importcpp: "(# in #)".} -proc `[]`*(obj: JsObject, field: cstring): JsObject {.importjs: getImpl.} +proc `[]`*(obj: JsObject, field: cstring): JsObject {.importcpp: getImpl.} ## Returns the value of a property of name `field` from a JsObject `obj`. -proc `[]`*(obj: JsObject, field: int): JsObject {.importjs: getImpl.} +proc `[]`*(obj: JsObject, field: int): JsObject {.importcpp: getImpl.} ## Returns the value of a property of name `field` from a JsObject `obj`. -proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {.importjs: setImpl.} +proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {.importcpp: setImpl.} ## Sets the value of a property of name `field` in a JsObject `obj` to `v`. -proc `[]=`*[T](obj: JsObject, field: int, val: T) {.importjs: setImpl.} +proc `[]=`*[T](obj: JsObject, field: int, val: T) {.importcpp: setImpl.} ## Sets the value of a property of name `field` in a JsObject `obj` to `v`. proc `[]`*[K: JsKey, V](obj: JsAssoc[K, V], field: K): V - {.importjs: getImpl.} + {.importcpp: getImpl.} ## Returns the value of a property of name `field` from a JsAssoc `obj`. proc `[]=`*[K: JsKey, V](obj: JsAssoc[K, V], field: K, val: V) - {.importjs: setImpl.} + {.importcpp: setImpl.} ## Sets the value of a property of name `field` in a JsAssoc `obj` to `v`. proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V = @@ -212,7 +211,7 @@ proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V = proc `[]=`*[V](obj: JsAssoc[cstring, V], field: string, val: V) = obj[cstring(field)] = val -proc `==`*(x, y: JsRoot): bool {.importjs: "(# === #)".} +proc `==`*(x, y: JsRoot): bool {.importcpp: "(# === #)".} ## Compares two JsObjects or JsAssocs. Be careful though, as this is comparison ## like in JavaScript, so if your JsObjects are in fact JavaScript Objects, ## and not strings or numbers, this is a *comparison of references*. @@ -229,15 +228,15 @@ macro `.`*(obj: JsObject, field: untyped): JsObject = let importString = "#." & $field result = quote do: proc helper(o: JsObject): JsObject - {.importjs: `importString`, gensym.} + {.importcpp: `importString`, gensym.} 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 - {.importjs: `importString`, gensym.} + {.importcpp: `importString`, gensym.} helper(`obj`) macro `.=`*(obj: JsObject, field, value: untyped): untyped = @@ -247,15 +246,15 @@ macro `.=`*(obj: JsObject, field, value: untyped): untyped = let importString = "#." & $field & " = #" result = quote do: proc helper(o: JsObject, v: auto) - {.importjs: `importString`, gensym.} + {.importcpp: `importString`, gensym.} 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) - {.importjs: `importString`, gensym.} + {.importcpp: `importString`, gensym.} helper(`obj`, `value`) macro `.()`*(obj: JsObject, @@ -268,24 +267,25 @@ macro `.()`*(obj: JsObject, ## so be careful when using this.) ## ## Example: - ## ```nim - ## # Let's get back to the console example: - ## var console {.importc, nodecl.}: JsObject - ## let res = console.log("I return undefined!") - ## console.log(res) # This prints undefined, as console.log always returns - ## # undefined. Thus one has to be careful, when using - ## # JsObject calls. - ## ``` + ## + ## .. code-block:: nim + ## + ## # Let's get back to the console example: + ## var console {.importc, nodecl.}: JsObject + ## let res = console.log("I return undefined!") + ## console.log(res) # This prints undefined, as console.log always returns + ## # undefined. Thus one has to be careful, when using + ## # JsObject calls. var importString: string if validJsName($field): 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 - {.importjs: `importString`, gensym, discardable.} + {.importcpp: `importString`, gensym, discardable.} helper(`obj`) for idx in 0 ..< args.len: let paramName = newIdentNode("param" & $idx) @@ -301,11 +301,11 @@ 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 - {.importjs: `importString`, gensym.} + {.importcpp: `importString`, gensym.} helper(`obj`) macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], @@ -318,11 +318,11 @@ 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) - {.importjs: `importString`, gensym.} + {.importcpp: `importString`, gensym.} helper(`obj`, `value`) macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V], @@ -347,8 +347,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: "}".} @@ -356,8 +356,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: "}".} @@ -365,7 +365,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: "}".} @@ -375,8 +375,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: "}".} @@ -384,8 +384,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: "}".} @@ -393,7 +393,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: "}".} @@ -406,20 +406,21 @@ macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto = ## ## Example: ## - ## ```nim - ## # Let's say we have a type with a ton of fields, where some fields do not - ## # need to be set, and we do not want those fields to be set to `nil`: - ## type - ## ExtremelyHugeType = ref object - ## a, b, c, d, e, f, g: int - ## h, i, j, k, l: cstring - ## # And even more fields ... + ## .. code-block:: nim ## - ## let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 } + ## # Let's say we have a type with a ton of fields, where some fields do not + ## # need to be set, and we do not want those fields to be set to `nil`: + ## type + ## ExtremelyHugeType = ref object + ## a, b, c, d, e, f, g: int + ## h, i, j, k, l: cstring + ## # And even more fields ... + ## + ## let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 } + ## + ## # This generates roughly the same JavaScript as: + ## {.emit: "var obj = {a: 1, k: "foo", d: 42};".} ## - ## # This generates roughly the same JavaScript as: - ## {.emit: "var obj = {a: 1, k: "foo", d: 42};".} - ## ``` let a = ident"a" var body = quote do: var `a` {.noinit.}: `typ` @@ -469,25 +470,24 @@ macro bindMethod*(procedure: typed): auto = ## Example: ## ## We want to generate roughly this JavaScript: - ## ```js - ## var obj = {a: 10}; - ## obj.someMethod = function() { - ## return this.a + 42; - ## }; - ## ``` + ## + ## .. code-block:: js + ## var obj = {a: 10}; + ## obj.someMethod = function() { + ## return this.a + 42; + ## }; ## ## We can achieve this using the `bindMethod` macro: ## - ## ```nim - ## let obj = JsObject{ a: 10 } - ## proc someMethodImpl(that: JsObject): int = - ## that.a.to(int) + 42 - ## obj.someMethod = bindMethod someMethodImpl + ## .. code-block:: nim + ## let obj = JsObject{ a: 10 } + ## proc someMethodImpl(that: JsObject): int = + ## that.a.to(int) + 42 + ## obj.someMethod = bindMethod someMethodImpl ## - ## # Alternatively: - ## obj.someMethod = bindMethod - ## proc(that: JsObject): int = that.a.to(int) + 42 - ## ``` + ## # Alternatively: + ## obj.someMethod = bindMethod + ## proc(that: JsObject): int = that.a.to(int) + 42 if not (procedure.kind == nnkSym or procedure.kind == nnkLambda): error("Argument has to be a proc or a symbol corresponding to a proc.") var diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index 69bd75c3b..7be7221bc 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -1,97 +1,46 @@ ## Regular Expressions for the JavaScript target. ## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions -when not defined(js): - {.error: "This module only works on the JavaScript platform".} - -type RegExp* = ref object of JsRoot - ## Regular Expressions for JavaScript target. - ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp - flags*: cstring ## cstring that contains the flags of the RegExp object. - dotAll*: bool ## Whether `.` matches newlines or not. - global*: bool ## Whether to test against all possible matches in a string, or only against the first. - ignoreCase*: bool ## Whether to ignore case while attempting a match in a string. - multiline*: bool ## Whether to search in strings across multiple lines. - source*: cstring ## The text of the pattern. - sticky*: bool ## Whether the search is sticky. - unicode*: bool ## Whether Unicode features are enabled. - lastIndex*: cint ## Index at which to start the next match (read/write property). - input*: cstring ## Read-only and modified on successful match. - lastMatch*: cstring ## Ditto. - 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(@)".} - ## Creates a new RegExp object. - -func newRegExp*(pattern: cstring): RegExp {.importjs: "new RegExp(@)".} - -func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.compile(@)".} - ## Recompiles a regular expression during execution of a script. - -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 replace*(pattern: cstring, self: RegExp, cb: proc (args: varargs[cstring]): cstring): cstring {.importcpp.} - ## Returns a new string with some or all matches of a pattern replaced by given callback function - -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(#) || [])".} - ## Returns an array of matches of a RegExp against given string - -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()".} - ## Returns a string representing the RegExp object. - -func `$`*(self: RegExp): string = $toCstring(self) - -func contains*(pattern: cstring; self: RegExp): bool = - ## Tests for a substring match in its string parameter. - runnableExamples: - let jsregex: RegExp = newRegExp(r"bc$", r"i") - assert jsregex in r"abc" - assert jsregex notin r"abcd" - assert "xabc".contains jsregex - asm "`result` = `self`.test(`pattern`);" - -func startsWith*(pattern: cstring; self: RegExp): bool = - ## Tests if string starts with given RegExp - runnableExamples: - let jsregex: RegExp = newRegExp(r"abc", r"i") - assert "abcd".startsWith jsregex - pattern.contains(newRegExp(("^" & $(self.source)).cstring, self.flags)) - -func endsWith*(pattern: cstring; self: RegExp): bool = - ## Tests if string ends with given RegExp - runnableExamples: - let jsregex: RegExp = newRegExp(r"bcd", r"i") - assert "abcd".endsWith jsregex - pattern.contains(newRegExp(($(self.source) & "$").cstring, self.flags)) - runnableExamples: let jsregex: RegExp = newRegExp(r"\s+", r"i") jsregex.compile(r"\w+", r"i") - assert "nim javascript".contains jsregex - assert jsregex.exec(r"nim javascript") == @["nim".cstring] - assert jsregex.toCstring() == r"/\w+/i" + doAssert jsregex.test(r"nim javascript") + doAssert jsregex.exec(r"nim javascript") == @["nim".cstring] + doAssert jsregex.toString() == r"/\w+/i" jsregex.compile(r"[0-9]", r"i") - assert "0123456789abcd".contains jsregex - assert $jsregex == "/[0-9]/i" - jsregex.compile(r"abc", r"i") - assert "abcd".startsWith jsregex - assert "dabc".endsWith jsregex - jsregex.compile(r"\d", r"i") - assert "do1ne".split(jsregex) == @["do".cstring, "ne".cstring] - jsregex.compile(r"[lw]", r"i") - assert "hello world".replace(jsregex,"X") == "heXlo world" - jsregex.compile(r"([a-z])\1*", r"g") - assert "abbcccdddd".replace(jsregex, proc (m: varargs[cstring]): cstring = ($m[0] & $(m.len)).cstring) == "a1b2c3d4" - let digitsRegex: RegExp = newRegExp(r"\d") - assert "foo".match(digitsRegex) == @[] + doAssert jsregex.test(r"0123456789abcd") + + +when not defined(js): + {.error: "This module only works on the JavaScript platform".} + +type RegExp* {.importjs.} = object ## Regular Expressions for JavaScript target. + flags* {.importjs.}: cstring ## cstring that contains the flags of the RegExp object. + dotAll* {.importjs.}: bool ## Whether `.` matches newlines or not. + global* {.importjs.}: bool ## Whether to test against all possible matches in a string, or only against the first. + ignoreCase* {.importjs.}: bool ## Whether to ignore case while attempting a match in a string. + multiline* {.importjs.}: bool ## Whether to search in strings across multiple lines. + source* {.importjs.}: cstring ## The text of the pattern. + sticky* {.importjs.}: bool ## Whether the search is sticky. + unicode* {.importjs.}: bool ## Whether Unicode features are enabled. + lastIndex* {.importjs.}: cint ## Index at which to start the next match (read/write property). + input* {.importjs.}: cstring ## Read-only and modified on successful match. + lastMatch* {.importjs.}: cstring ## Ditto. + lastParen* {.importjs.}: cstring ## Ditto. + leftContext* {.importjs.}: cstring ## Ditto. + rightContext* {.importjs.}: cstring ## Ditto. + +func newRegExp*(pattern: cstring; flags: cstring): RegExp {.importjs: "new RegExp(@)".} + ## Creates a new RegExp object. + +func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.compile(@)".} + ## Recompiles a regular expression during execution of a script. + +func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "#.exec(#)".} + ## Executes a search for a match in its string parameter. + +func test*(self: RegExp; pattern: cstring): bool {.importjs: "#.test(#)".} + ## Tests for a match in its string parameter. + +func toString*(self: RegExp): cstring {.importjs: "#.toString()".} + ## Returns a string representing the RegExp object. diff --git a/lib/nimbase.h b/lib/nimbase.h index b9b2695c9..daae47d65 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -75,8 +75,7 @@ __AVR__ #endif /* ------------------------------------------------------------------------- */ -#if defined(__GNUC__) && !defined(__ZEPHYR__) -/* Zephyr does some magic in it's headers that override the GCC stdlib. This breaks that. */ +#if defined(__GNUC__) # define _GNU_SOURCE 1 #endif @@ -144,10 +143,6 @@ __AVR__ # error "Cannot define NIM_THREADVAR" #endif -#if defined(__cplusplus) - #define NIM_THREAD_LOCAL thread_local -#endif - /* --------------- how int64 constants should be declared: ----------- */ #if defined(__GNUC__) || defined(__LCC__) || \ defined(__POCC__) || defined(__DMC__) || defined(_MSC_VER) @@ -326,9 +321,6 @@ typedef unsigned char NIM_BOOL; // best effort #endif NIM_STATIC_ASSERT(sizeof(NIM_BOOL) == 1, ""); // check whether really needed -NIM_STATIC_ASSERT(CHAR_BIT == 8, ""); - // fail fast for (rare) environments where this doesn't hold, as some implicit - // assumptions would need revisiting (e.g. `uint8` or https://github.com/nim-lang/Nim/pull/18505) #define NIM_TRUE true #define NIM_FALSE false @@ -478,9 +470,7 @@ typedef char* NCSTRING; } name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str} /* declared size of a sequence/variable length array: */ -#if defined(__cplusplus) && defined(__clang__) -# define SEQ_DECL_SIZE 1 -#elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) +#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) # define SEQ_DECL_SIZE /* empty is correct! */ #else # define SEQ_DECL_SIZE 1000000 @@ -549,7 +539,7 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); } #endif // Test to see if Nim and the C compiler agree on the size of a pointer. -NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "Pointer size mismatch between Nim and C/C++ backend. You probably need to setup the backend compiler for target CPU."); +NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, ""); #ifdef USE_NIM_NAMESPACE } diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim index 8bccfc22e..fcc0ffdbe 100644 --- a/lib/nimhcr.nim +++ b/lib/nimhcr.nim @@ -20,7 +20,7 @@ batchable: false # by storing them on the heap. For procs, we produce on the fly simple # trampolines that can be dynamically overwritten to jump to a different # target. In the host program, all globals and procs are first registered -# here with `hcrRegisterGlobal` and `hcrRegisterProc` and then the +# here with ``hcrRegisterGlobal`` and ``hcrRegisterProc`` and then the # returned permanent locations are used in every reference to these symbols # onwards. # @@ -200,9 +200,6 @@ batchable: false # block. Perhaps something can be done about this - some way of re-allocating # the state and transferring the old... -when defined(nimPreviewSlimSystem): - import std/assertions - when not defined(js) and (defined(hotcodereloading) or defined(createNimHcr) or defined(testNimHcr)): @@ -212,7 +209,7 @@ when not defined(js) and (defined(hotcodereloading) or else: "so" type HcrProcGetter* = proc (libHandle: pointer, procName: cstring): pointer {.nimcall.} - HcrGcMarkerProc = proc () {.nimcall, raises: [].} + HcrGcMarkerProc = proc () {.nimcall.} HcrModuleInitializer* = proc () {.nimcall.} when defined(createNimHcr): @@ -426,7 +423,7 @@ when defined(createNimHcr): if modules.contains(name): unloadDll(name) else: - modules[name] = newModuleDesc() + modules.add(name, newModuleDesc()) let copiedName = name & ".copy." & dllExt copyFileWithPermissions(name, copiedName) @@ -490,7 +487,7 @@ when defined(createNimHcr): recursiveDiscovery(modules[curr].imports) allModulesOrderedByDFS.add(curr) continue - loadDll(curr.cstring) + loadDll(curr) # first load all dependencies of the current module and init it after that recursiveDiscovery(modules[curr].imports) @@ -500,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.cstring) - initTypeInfoGlobals(curr.cstring) + initHcrData(curr) + initTypeInfoGlobals(curr) # for now system always gets fully inited before any other module (including when reloading) - initPointerData(system.cstring) - initGlobalScope(system.cstring) + initPointerData(system) + initGlobalScope(system) # proceed with the DatInit calls - for all modules - including the main one! for curr in allModulesOrderedByDFS: if curr != system: - initPointerData(curr.cstring) + initPointerData(curr) mainDatInit() # execute top-level code (in global scope) for curr in modulesToInit: if curr != system: - initGlobalScope(curr.cstring) + initGlobalScope(curr) # cleanup old symbols which are gone now for curr in modulesToInit: cleanupSymbols(curr) @@ -604,12 +601,12 @@ when defined(createNimHcr): proc hcrAddModule*(module: cstring) {.nimhcr.} = if not modules.contains($module): - modules[$module] = newModuleDesc() + modules.add($module, newModuleDesc()) proc hcrGeneration*(): int {.nimhcr.} = generation - proc hcrMarkGlobals*() {.compilerproc, exportc, dynlib, nimcall, gcsafe.} = + proc hcrMarkGlobals*() {.nimhcr, nimcall, gcsafe.} = # This is gcsafe, because it will be registered # only in the GC of the main thread. {.gcsafe.}: @@ -651,10 +648,10 @@ elif defined(hotcodereloading) or defined(testNimHcr): proc hcrAddEventHandler*(isBefore: bool, cb: proc ()) {.nimhcr.} - proc hcrMarkGlobals*() {.raises: [], nimhcr, nimcall, gcsafe.} + proc hcrMarkGlobals*() {.nimhcr, nimcall, gcsafe.} when declared(nimRegisterGlobalMarker): - nimRegisterGlobalMarker(cast[GlobalMarkerProc](hcrMarkGlobals)) + nimRegisterGlobalMarker(hcrMarkGlobals) else: proc hcrHasModuleChanged*(moduleHash: string): bool = diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim index 93349b287..a2a10c6bd 100644 --- a/lib/nimrtl.nim +++ b/lib/nimrtl.nim @@ -12,7 +12,7 @@ batchable: false # ## Main file to generate a DLL from the standard library. -## The default Nimrtl does not only contain the `system` module, but these +## The default Nimrtl does not only contain the ``system`` module, but these ## too: ## ## * parseutils @@ -26,7 +26,6 @@ batchable: false ## * unicode ## * pegs ## * ropes -## * cstrutils ## when system.appType != "lib": diff --git a/lib/nintendoswitch/switch_memory.nim b/lib/nintendoswitch/switch_memory.nim new file mode 100644 index 000000000..f34bd363a --- /dev/null +++ b/lib/nintendoswitch/switch_memory.nim @@ -0,0 +1,36 @@ +## All of these library headers and source can be found in the github repo +## https://github.com/switchbrew/libnx. + +const virtMemHeader = "" +const svcHeader = "" +const mallocHeader = "" + +## Aligns a block of memory with request `size` to `bytes` size. For +## example, a request of memalign(0x1000, 0x1001) == 0x2000 bytes allocated +proc memalign*(bytes: csize, size: csize): pointer {.importc: "memalign", + header: mallocHeader.} + +# Should be required, but not needed now because of how +# svcUnmapMemory frees all memory +#proc free*(address: pointer) {.importc: "free", +# header: mallocHeader.} + +## Maps a memaligned block of memory from `src_addr` to `dst_addr`. The +## Nintendo Switch requires this call in order to make use of memory, otherwise +## an invalid memory access occurs. +proc svcMapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): uint32 {. + importc: "svcMapMemory", header: svcHeader.} + +## Unmaps (frees) all memory from both `dst_addr` and `src_addr`. **Must** be called +## whenever svcMapMemory is used. The Switch will expect all memory to be allocated +## before gfxExit() calls () +proc svcUnmapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): uint32 {. + importc: "svcUnmapMemory", header: svcHeader.} + +proc virtmemReserveMap*(size: csize): pointer {.importc: "virtmemReserveMap", + header: virtMemHeader.} + +# Should be required, but not needed now because of how +# svcUnmapMemory frees all memory +#proc virtmemFreeMap*(address: pointer; size: csize) {.importc: "virtmemFreeMap", +# header: virtMemHeader.} diff --git a/lib/packages/docutils/dochelpers.nim b/lib/packages/docutils/dochelpers.nim deleted file mode 100644 index b7cdd16d2..000000000 --- a/lib/packages/docutils/dochelpers.nim +++ /dev/null @@ -1,297 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2021 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Integration helpers between ``docgen.nim`` and ``rst.nim``. -## -## Function `toLangSymbol(linkText)`_ produces a signature `docLink` of -## `type LangSymbol`_ in ``rst.nim``, while `match(generated, docLink)`_ -## matches it with `generated`, produced from `PNode` by ``docgen.rst``. - -import rstast, strutils - -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - - -type - LangSymbol* = object ## symbol signature in Nim - symKind*: string ## "proc", "const", "type", etc - symTypeKind*: string ## ""|enum|object|tuple - - ## valid only when `symKind == "type"` - name*: string ## plain symbol name without any parameters - generics*: string ## generic parameters (without brackets) - isGroup*: bool ## is LangSymbol a group with overloads? - # the following fields are valid iff `isGroup` == false - # (always false when parsed by `toLangSymbol` because link like foo_ - # can point to just a single symbol foo, e.g. proc). - parametersProvided*: bool ## to disambiguate `proc f`_ and `proc f()`_ - parameters*: seq[tuple[name: string, `type`: string]] - ## name-type seq, e.g. for proc - outType*: string ## result type, e.g. for proc - -proc `$`*(s: LangSymbol): string = # for debug - ("(symkind=$1, symTypeKind=$2, name=$3, generics=$4, isGroup=$5, " & - "parametersProvided=$6, parameters=$7, outType=$8)") % [ - s.symKind, s.symTypeKind , s.name, s.generics, $s.isGroup, - $s.parametersProvided, $s.parameters, s.outType] - -func nimIdentBackticksNormalize*(s: string): string = - ## Normalizes the string `s` as a Nim identifier. - ## - ## Unlike `nimIdentNormalize` removes spaces and backticks. - ## - ## .. Warning:: No checking (e.g. that identifiers cannot start from - ## digits or '_', or that number of backticks is even) is performed. - runnableExamples: - doAssert nimIdentBackticksNormalize("Foo_bar") == "Foobar" - doAssert nimIdentBackticksNormalize("FoO BAr") == "Foobar" - doAssert nimIdentBackticksNormalize("`Foo BAR`") == "Foobar" - doAssert nimIdentBackticksNormalize("` Foo BAR `") == "Foobar" - # not a valid identifier: - doAssert nimIdentBackticksNormalize("`_x_y`") == "_xy" - result = newString(s.len) - var firstChar = true - var j = 0 - for i in 0..len(s) - 1: - if s[i] in {'A'..'Z'}: - if not firstChar: # to lowercase - result[j] = chr(ord(s[i]) + (ord('a') - ord('A'))) - else: - result[j] = s[i] - firstChar = false - inc j - elif s[i] notin {'_', ' ', '`'}: - result[j] = s[i] - inc j - firstChar = false - elif s[i] == '_' and firstChar: - result[j] = '_' - inc j - firstChar = false - else: discard # just omit '`' or ' ' - if j != s.len: setLen(result, j) - -proc langSymbolGroup*(kind: string, name: string): LangSymbol = - if kind notin ["proc", "func", "macro", "method", "iterator", - "template", "converter"]: - raise newException(ValueError, "unknown symbol kind $1" % [kind]) - result = LangSymbol(symKind: kind, name: name, isGroup: true) - -proc toLangSymbol*(linkText: PRstNode): LangSymbol = - ## Parses `linkText` into a more structured form using a state machine. - ## - ## This proc is designed to allow link syntax with operators even - ## without escaped backticks inside: - ## - ## `proc *`_ - ## `proc []`_ - ## - ## This proc should be kept in sync with the `renderTypes` proc from - ## ``compiler/typesrenderer.nim``. - template fail(msg: string) = - raise newException(ValueError, msg) - if linkText.kind notin {rnRstRef, rnInner}: - fail("toLangSymbol: wrong input kind " & $linkText.kind) - - const NimDefs = ["proc", "func", "macro", "method", "iterator", - "template", "converter", "const", "type", "var", - "enum", "object", "tuple", "module"] - template resolveSymKind(x: string) = - if x in ["enum", "object", "tuple"]: - result.symKind = "type" - result.symTypeKind = x - else: - result.symKind = x - type - State = enum - inBeginning - afterSymKind - beforeSymbolName # auxiliary state to catch situations like `proc []`_ after space - atSymbolName - afterSymbolName - genericsPar - parameterName - parameterType - outType - var state = inBeginning - var curIdent = "" - template flushIdent() = - if curIdent != "": - case state - of inBeginning: fail("incorrect state inBeginning") - of afterSymKind: resolveSymKind curIdent - of beforeSymbolName: fail("incorrect state beforeSymbolName") - of atSymbolName: result.name = curIdent.nimIdentBackticksNormalize - of afterSymbolName: fail("incorrect state afterSymbolName") - of genericsPar: result.generics = curIdent - of parameterName: result.parameters.add (curIdent, "") - of parameterType: - for a in countdown(result.parameters.len - 1, 0): - if result.parameters[a].`type` == "": - result.parameters[a].`type` = curIdent - of outType: result.outType = curIdent - curIdent = "" - var parens = 0 - let L = linkText.sons.len - template s(i: int): string = linkText.sons[i].text - var i = 0 - template nextState = - case s(i) - of " ": - if state == afterSymKind: - flushIdent - state = beforeSymbolName - of "`": - curIdent.add "`" - inc i - while i < L: # add contents between ` ` as a whole - curIdent.add s(i) - if s(i) == "`": - break - inc i - curIdent = curIdent.nimIdentBackticksNormalize - if state in {inBeginning, afterSymKind, beforeSymbolName}: - state = atSymbolName - flushIdent - state = afterSymbolName - of "[": - if state notin {inBeginning, afterSymKind, beforeSymbolName}: - inc parens - if state in {inBeginning, afterSymKind, beforeSymbolName}: - state = atSymbolName - curIdent.add s(i) - elif state in {atSymbolName, afterSymbolName} and parens == 1: - flushIdent - state = genericsPar - curIdent.add s(i) - else: curIdent.add s(i) - of "]": - if state notin {inBeginning, afterSymKind, beforeSymbolName, atSymbolName}: - dec parens - if state == genericsPar and parens == 0: - curIdent.add s(i) - flushIdent - else: curIdent.add s(i) - of "(": - inc parens - if state in {inBeginning, afterSymKind, beforeSymbolName}: - result.parametersProvided = true - state = atSymbolName - flushIdent - state = parameterName - elif state in {atSymbolName, afterSymbolName, genericsPar} and parens == 1: - result.parametersProvided = true - flushIdent - state = parameterName - else: curIdent.add s(i) - of ")": - dec parens - if state in {parameterName, parameterType} and parens == 0: - flushIdent - state = outType - else: curIdent.add s(i) - of "{": # remove pragmas - while i < L: - if s(i) == "}": - break - inc i - of ",", ";": - if state in {parameterName, parameterType} and parens == 1: - flushIdent - state = parameterName - else: curIdent.add s(i) - of "*": # skip export symbol - if state == atSymbolName: - flushIdent - state = afterSymbolName - elif state == afterSymbolName: - discard - else: curIdent.add "*" - of ":": - if state == outType: discard - elif state == parameterName: - flushIdent - state = parameterType - else: curIdent.add ":" - else: - let isPostfixSymKind = i > 0 and i == L - 1 and - result.symKind == "" and s(i) in NimDefs - if isPostfixSymKind: # for links like `foo proc`_ - resolveSymKind s(i) - else: - case state - of inBeginning: - if s(i) in NimDefs: - state = afterSymKind - else: - state = atSymbolName - curIdent.add s(i) - of afterSymKind, beforeSymbolName: - state = atSymbolName - curIdent.add s(i) - of parameterType: - case s(i) - of "ref": curIdent.add "ref." - of "ptr": curIdent.add "ptr." - of "var": discard - else: curIdent.add s(i).nimIdentBackticksNormalize - of atSymbolName: - curIdent.add s(i) - else: - curIdent.add s(i).nimIdentBackticksNormalize - while i < L: - nextState - inc i - if state == afterSymKind: # treat `type`_ as link to symbol `type` - state = atSymbolName - flushIdent - result.isGroup = false - -proc match*(generated: LangSymbol, docLink: LangSymbol): bool = - ## Returns true if `generated` can be a target for `docLink`. - ## If `generated` is an overload group then only `symKind` and `name` - ## are compared for success. - result = true - if docLink.symKind != "": - if generated.symKind == "proc": - result = docLink.symKind in ["proc", "func"] - else: - result = generated.symKind == docLink.symKind - if result and docLink.symKind == "type" and docLink.symTypeKind != "": - result = generated.symTypeKind == docLink.symTypeKind - if not result: return - result = generated.name == docLink.name - if not result: return - if generated.isGroup: - # if `()` were added then it's not a reference to the whole group: - return not docLink.parametersProvided - if docLink.generics != "": - result = generated.generics == docLink.generics - if not result: return - if docLink.outType != "": - result = generated.outType == docLink.outType - if not result: return - if docLink.parametersProvided: - result = generated.parameters.len == docLink.parameters.len - if not result: return - var onlyType = false - for i in 0 ..< generated.parameters.len: - let g = generated.parameters[i] - let d = docLink.parameters[i] - if i == 0: - if g.`type` == d.name: - onlyType = true # only types, not names, are provided in `docLink` - if onlyType: - result = g.`type` == d.name: - else: - if d.`type` != "": - result = g.`type` == d.`type` - if not result: return - result = g.name == d.name - if not result: return diff --git a/lib/packages/docutils/docutils.nimble b/lib/packages/docutils/docutils.nimble new file mode 100644 index 000000000..f1683c515 --- /dev/null +++ b/lib/packages/docutils/docutils.nimble @@ -0,0 +1,4 @@ +version = "0.10.0" +author = "Andreas Rumpf" +description = "Nim's reStructuredText processor." +license = "MIT" diff --git a/lib/packages/docutils/docutils.nimble.old b/lib/packages/docutils/docutils.nimble.old deleted file mode 100644 index f97c3bdde..000000000 --- a/lib/packages/docutils/docutils.nimble.old +++ /dev/null @@ -1,7 +0,0 @@ -# xxx disabled this as this isn't really a nimble package and it affects logic -# used to compute canonical imports, refs https://github.com/nim-lang/Nim/pull/16999#issuecomment-805442914 - -version = "0.10.0" -author = "Andreas Rumpf" -description = "Nim's reStructuredText processor." -license = "MIT" diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index c3caf31b4..fcc42add0 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -11,9 +11,11 @@ ## Currently only few languages are supported, other languages may be added. ## The interface supports one language nested in another. ## +## **Note:** Import ``packages/docutils/highlite`` to use this module +## ## You can use this to build your own syntax highlighting, check this example: ## -## ```Nim +## .. code::nim ## let code = """for x in $int.high: echo x.ord mod 2 == 0""" ## var toknizr: GeneralTokenizer ## initGeneralTokenizer(toknizr, code) @@ -31,43 +33,21 @@ ## else: ## echo toknizr.kind # All the kinds of tokens can be processed here. ## echo substr(code, toknizr.start, toknizr.length + toknizr.start - 1) -## ``` ## -## The proc `getSourceLanguage` can get the language `enum` from a string: -## ```Nim +## The proc ``getSourceLanguage`` can get the language ``enum`` from a string: +## +## .. code::nim ## for l in ["C", "c++", "jAvA", "Nim", "c#"]: echo getSourceLanguage(l) -## ``` ## -## There is also a `Cmd` pseudo-language supported, which is a simple generic -## shell/cmdline tokenizer (UNIX shell/Powershell/Windows Command): -## no escaping, no programming language constructs besides variable definition -## at the beginning of line. It supports these operators: -## ```Cmd -## & && | || ( ) '' "" ; # for comments -## ``` -## -## Instead of escaping always use quotes like here -## `nimgrep --ext:'nim|nims' file.name`:cmd: shows how to input ``|``. -## Any argument that contains ``.`` or ``/`` or ``\`` will be treated -## as a file or directory. -## -## In addition to `Cmd` there is also `Console` language for -## displaying interactive sessions. -## Lines with a command should start with ``$``, other lines are considered -## as program output. import strutils from algorithm import binarySearch -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - - type SourceLanguage* = enum langNone, langNim, langCpp, langCsharp, langC, langJava, - langYaml, langPython, langCmd, langConsole + langYaml, langPython TokenClass* = enum gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber, gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit, @@ -75,7 +55,7 @@ type gtOperator, gtPunctuation, gtComment, gtLongComment, gtRegularExpression, gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler, gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel, - gtReference, gtPrompt, gtProgramOutput, gtProgram, gtOption, gtOther + gtReference, gtOther GeneralTokenizer* = object of RootObj kind*: TokenClass start*, length*: int @@ -86,20 +66,14 @@ type const sourceLanguageToStr*: array[SourceLanguage, string] = ["none", - "Nim", "C++", "C#", "C", "Java", "Yaml", "Python", "Cmd", "Console"] - sourceLanguageToAlpha*: array[SourceLanguage, string] = ["none", - "Nim", "cpp", "csharp", "C", "Java", "Yaml", "Python", "Cmd", "Console"] - ## list of languages spelled with alpabetic characters + "Nim", "C++", "C#", "C", "Java", "Yaml", "Python"] tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace", "DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber", "Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit", "EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment", "RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData", "Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink", - "Label", "Reference", "Prompt", "ProgramOutput", - # start from lower-case if there is a corresponding RST role (see rst.nim) - "program", "option", - "Other"] + "Label", "Reference", "Other"] # The following list comes from doc/keywords.txt, make sure it is # synchronized with this array by running the module itself as a test case. @@ -116,11 +90,9 @@ const "xor", "yield"] proc getSourceLanguage*(name: string): SourceLanguage = - for i in succ(low(SourceLanguage)) .. high(SourceLanguage): + for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)): if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0: return i - if cmpIgnoreStyle(name, sourceLanguageToAlpha[i]) == 0: - return i result = langNone proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) = @@ -130,7 +102,9 @@ proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) = g.length = 0 g.state = low(TokenClass) g.lang = low(SourceLanguage) - g.pos = 0 + var pos = 0 # skip initial whitespace: + while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) + g.pos = pos proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: string) = initGeneralTokenizer(g, cstring(buf)) @@ -191,6 +165,9 @@ const proc isKeyword(x: openArray[string], y: string): int = binarySearch(x, y) +proc isKeywordIgnoreCase(x: openArray[string], y: string): int = + binarySearch(x, y, cmpIgnoreCase) + proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) = const hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'} @@ -200,38 +177,36 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) = var pos = g.pos g.start = g.pos if g.state == gtStringLit: - if g.buf[pos] == '\\': - g.kind = gtEscapeSequence - inc(pos) + g.kind = gtStringLit + while true: case g.buf[pos] - of 'x', 'X': + of '\\': + g.kind = gtEscapeSequence inc(pos) - if g.buf[pos] in hexChars: inc(pos) - if g.buf[pos] in hexChars: inc(pos) - of '0'..'9': - while g.buf[pos] in {'0'..'9'}: inc(pos) - of '\0': - g.state = gtNone - else: inc(pos) - else: - g.kind = gtStringLit - while true: case g.buf[pos] - of '\\': - break - of '\0', '\r', '\n': - g.state = gtNone - break - of '\"': + of 'x', 'X': inc(pos) + if g.buf[pos] in hexChars: inc(pos) + if g.buf[pos] in hexChars: inc(pos) + of '0'..'9': + while g.buf[pos] in {'0'..'9'}: inc(pos) + of '\0': g.state = gtNone - break else: inc(pos) + break + of '\0', '\x0D', '\x0A': + g.state = gtNone + break + of '\"': + inc(pos) + g.state = gtNone + break + else: inc(pos) else: case g.buf[pos] - of ' ', '\t'..'\r': + of ' ', '\x09'..'\x0D': g.kind = gtWhitespace - while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos) + while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) of '#': g.kind = gtComment inc(pos) @@ -268,7 +243,7 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) = else: inc pos else: - while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos) + while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos) of 'a'..'z', 'A'..'Z', '_', '\x80'..'\xFF': var id = "" while g.buf[pos] in SymChars + {'_'}: @@ -292,7 +267,7 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) = else: g.kind = gtRawData inc(pos) - while not (g.buf[pos] in {'\0', '\n', '\r'}): + while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}): if g.buf[pos] == '"' and g.buf[pos+1] != '"': break inc(pos) if g.buf[pos] == '\"': inc(pos) @@ -327,7 +302,7 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) = g.kind = gtCharLit while true: case g.buf[pos] - of '\0', '\r', '\n': + of '\0', '\x0D', '\x0A': break of '\'': inc(pos) @@ -355,7 +330,7 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) = g.kind = gtStringLit while true: case g.buf[pos] - of '\0', '\r', '\n': + of '\0', '\x0D', '\x0A': break of '\"': inc(pos) @@ -461,7 +436,7 @@ proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string], g.state = gtNone else: inc(pos) break - of '\0', '\r', '\n': + of '\0', '\x0D', '\x0A': g.state = gtNone break of '\"': @@ -471,14 +446,14 @@ proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string], else: inc(pos) else: case g.buf[pos] - of ' ', '\t'..'\r': + of ' ', '\x09'..'\x0D': g.kind = gtWhitespace - while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos) + while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) of '/': inc(pos) if g.buf[pos] == '/': g.kind = gtComment - while not (g.buf[pos] in {'\0', '\n', '\r'}): inc(pos) + while not (g.buf[pos] in {'\0', '\x0A', '\x0D'}): inc(pos) elif g.buf[pos] == '*': g.kind = gtLongComment var nested = 0 @@ -498,9 +473,6 @@ proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string], of '\0': break else: inc(pos) - else: - g.kind = gtOperator - while g.buf[pos] in OpChars: inc(pos) of '#': inc(pos) if hasPreprocessor in flags: @@ -621,9 +593,9 @@ proc javaNextToken(g: var GeneralTokenizer) = proc yamlPlainStrLit(g: var GeneralTokenizer, pos: var int) = g.kind = gtStringLit - while g.buf[pos] notin {'\0', '\t'..'\r', ',', ']', '}'}: + while g.buf[pos] notin {'\0', '\x09'..'\x0D', ',', ']', '}'}: if g.buf[pos] == ':' and - g.buf[pos + 1] in {'\0', '\t'..'\r', ' '}: + g.buf[pos + 1] in {'\0', '\x09'..'\x0D', ' '}: break inc(pos) @@ -636,14 +608,14 @@ proc yamlPossibleNumber(g: var GeneralTokenizer, pos: var int) = while g.buf[pos] in {'0'..'9'}: inc(pos) else: yamlPlainStrLit(g, pos) if g.kind == gtNone: - if g.buf[pos] in {'\0', '\t'..'\r', ' ', ',', ']', '}'}: + if g.buf[pos] in {'\0', '\x09'..'\x0D', ' ', ',', ']', '}'}: g.kind = gtDecNumber elif g.buf[pos] == '.': inc(pos) if g.buf[pos] notin {'0'..'9'}: yamlPlainStrLit(g, pos) else: while g.buf[pos] in {'0'..'9'}: inc(pos) - if g.buf[pos] in {'\0', '\t'..'\r', ' ', ',', ']', '}'}: + if g.buf[pos] in {'\0', '\x09'..'\x0D', ' ', ',', ']', '}'}: g.kind = gtFloatNumber if g.kind == gtNone: if g.buf[pos] in {'e', 'E'}: @@ -652,13 +624,13 @@ proc yamlPossibleNumber(g: var GeneralTokenizer, pos: var int) = if g.buf[pos] notin {'0'..'9'}: yamlPlainStrLit(g, pos) else: while g.buf[pos] in {'0'..'9'}: inc(pos) - if g.buf[pos] in {'\0', '\t'..'\r', ' ', ',', ']', '}'}: + if g.buf[pos] in {'\0', '\x09'..'\x0D', ' ', ',', ']', '}'}: g.kind = gtFloatNumber else: yamlPlainStrLit(g, pos) else: yamlPlainStrLit(g, pos) - while g.buf[pos] notin {'\0', ',', ']', '}', '\n', '\r'}: + while g.buf[pos] notin {'\0', ',', ']', '}', '\x0A', '\x0D'}: inc(pos) - if g.buf[pos] notin {'\t'..'\r', ' ', ',', ']', '}'}: + if g.buf[pos] notin {'\x09'..'\x0D', ' ', ',', ']', '}'}: yamlPlainStrLit(g, pos) break # theoretically, we would need to parse indentation (like with block scalars) @@ -727,13 +699,13 @@ proc yamlNextToken(g: var GeneralTokenizer) = while g.buf[pos] in {' ', '\t'}: inc(pos) of '#': g.kind = gtComment - while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos) - of '\n', '\r': discard + while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos) + of '\x0A', '\x0D': discard else: # illegal here. just don't parse a block scalar g.kind = gtNone g.state = gtOther - if g.buf[pos] in {'\n', '\r'} and g.state == gtCommand: + if g.buf[pos] in {'\x0A', '\x0D'} and g.state == gtCommand: g.state = gtLongStringLit elif g.state == gtLongStringLit: # beware, this is the only token where we actually have to parse @@ -742,10 +714,10 @@ proc yamlNextToken(g: var GeneralTokenizer) = g.kind = gtLongStringLit # first, we have to find the parent indentation of the block scalar, so that # we know when to stop - assert g.buf[pos] in {'\n', '\r'} + assert g.buf[pos] in {'\x0A', '\x0D'} var lookbehind = pos - 1 var headerStart = -1 - while lookbehind >= 0 and g.buf[lookbehind] notin {'\n', '\r'}: + while lookbehind >= 0 and g.buf[lookbehind] notin {'\x0A', '\x0D'}: if headerStart == -1 and g.buf[lookbehind] in {'|', '>'}: headerStart = lookbehind dec(lookbehind) @@ -756,12 +728,12 @@ proc yamlNextToken(g: var GeneralTokenizer) = # when the header is alone in a line, this line does not show the parent's # indentation, so we must go further. search the first previous line with # non-whitespace content. - while lookbehind >= 0 and g.buf[lookbehind] in {'\n', '\r'}: + while lookbehind >= 0 and g.buf[lookbehind] in {'\x0A', '\x0D'}: dec(lookbehind) while lookbehind >= 0 and g.buf[lookbehind] in {' ', '\t'}: dec(lookbehind) # now, find the beginning of the line... - while lookbehind >= 0 and g.buf[lookbehind] notin {'\n', '\r'}: + while lookbehind >= 0 and g.buf[lookbehind] notin {'\x0A', '\x0D'}: dec(lookbehind) # ... and its indentation indentation = 1 @@ -769,7 +741,7 @@ proc yamlNextToken(g: var GeneralTokenizer) = if lookbehind == -1: indentation = 0 # top level elif g.buf[lookbehind + 1] == '-' and g.buf[lookbehind + 2] == '-' and g.buf[lookbehind + 3] == '-' and - g.buf[lookbehind + 4] in {'\t'..'\r', ' '}: + g.buf[lookbehind + 4] in {'\x09'..'\x0D', ' '}: # this is a document start, therefore, we are at top level indentation = 0 # because lookbehind was at newline char when calculating indentation, we're @@ -777,7 +749,7 @@ proc yamlNextToken(g: var GeneralTokenizer) = let parentIndentation = indentation - 1 # find first content - while g.buf[pos] in {' ', '\n', '\r'}: + while g.buf[pos] in {' ', '\x0A', '\x0D'}: if g.buf[pos] == ' ': inc(indentation) else: indentation = 0 inc(pos) @@ -794,12 +766,12 @@ proc yamlNextToken(g: var GeneralTokenizer) = if (indentation < minIndentation and g.buf[pos] == '#') or (indentation == 0 and g.buf[pos] == '.' and g.buf[pos + 1] == '.' and g.buf[pos + 2] == '.' and - g.buf[pos + 3] in {'\0', '\t'..'\r', ' '}): + g.buf[pos + 3] in {'\0', '\x09'..'\x0D', ' '}): # comment after end of block scalar, or end of document break minIndentation = min(indentation, minIndentation) - while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos) - while g.buf[pos] in {' ', '\n', '\r'}: + while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos) + while g.buf[pos] in {' ', '\x0A', '\x0D'}: if g.buf[pos] == ' ': inc(indentation) else: indentation = 0 inc(pos) @@ -808,27 +780,27 @@ proc yamlNextToken(g: var GeneralTokenizer) = elif g.state == gtOther: # gtOther means 'inside YAML document' case g.buf[pos] - of ' ', '\t'..'\r': + of ' ', '\x09'..'\x0D': g.kind = gtWhitespace - while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos) + while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) of '#': g.kind = gtComment inc(pos) - while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos) + while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos) of '-': inc(pos) - if g.buf[pos] in {'\0', ' ', '\t'..'\r'}: + if g.buf[pos] in {'\0', ' ', '\x09'..'\x0D'}: g.kind = gtPunctuation elif g.buf[pos] == '-' and - (pos == 1 or g.buf[pos - 2] in {'\n', '\r'}): # start of line + (pos == 1 or g.buf[pos - 2] in {'\x0A', '\x0D'}): # start of line inc(pos) - if g.buf[pos] == '-' and g.buf[pos + 1] in {'\0', '\t'..'\r', ' '}: + if g.buf[pos] == '-' and g.buf[pos + 1] in {'\0', '\x09'..'\x0D', ' '}: inc(pos) g.kind = gtKeyword else: yamlPossibleNumber(g, pos) else: yamlPossibleNumber(g, pos) of '.': - if pos == 0 or g.buf[pos - 1] in {'\n', '\r'}: + if pos == 0 or g.buf[pos - 1] in {'\x0A', '\x0D'}: inc(pos) for i in 1..2: if g.buf[pos] != '.': break @@ -840,12 +812,12 @@ proc yamlNextToken(g: var GeneralTokenizer) = else: yamlPlainStrLit(g, pos) of '?': inc(pos) - if g.buf[pos] in {'\0', ' ', '\t'..'\r'}: + if g.buf[pos] in {'\0', ' ', '\x09'..'\x0D'}: g.kind = gtPunctuation else: yamlPlainStrLit(g, pos) of ':': inc(pos) - if g.buf[pos] in {'\0', '\t'..'\r', ' ', '\'', '\"'} or + if g.buf[pos] in {'\0', '\x09'..'\x0D', ' ', '\'', '\"'} or (pos > 0 and g.buf[pos - 2] in {'}', ']', '\"', '\''}): g.kind = gtPunctuation else: yamlPlainStrLit(g, pos) @@ -864,7 +836,7 @@ proc yamlNextToken(g: var GeneralTokenizer) = inc(pos) if g.buf[pos] == '<': # literal tag (e.g. `!`) - while g.buf[pos] notin {'\0', '>', '\t'..'\r', ' '}: inc(pos) + while g.buf[pos] notin {'\0', '>', '\x09'..'\x0D', ' '}: inc(pos) if g.buf[pos] == '>': inc(pos) else: while g.buf[pos] in {'A'..'Z', 'a'..'z', '0'..'9', '-'}: inc(pos) @@ -873,17 +845,17 @@ proc yamlNextToken(g: var GeneralTokenizer) = # prefixed tag (e.g. `!!str`) inc(pos) while g.buf[pos] notin - {'\0', '\t'..'\r', ' ', ',', '[', ']', '{', '}'}: inc(pos) - of '\0', '\t'..'\r', ' ': discard + {'\0', '\x09'..'\x0D', ' ', ',', '[', ']', '{', '}'}: inc(pos) + of '\0', '\x09'..'\x0D', ' ': discard else: # local tag (e.g. `!nim:system:int`) - while g.buf[pos] notin {'\0', '\t'..'\r', ' '}: inc(pos) + while g.buf[pos] notin {'\0', '\x09'..'\x0D', ' '}: inc(pos) of '&': g.kind = gtLabel - while g.buf[pos] notin {'\0', '\t'..'\r', ' '}: inc(pos) + while g.buf[pos] notin {'\0', '\x09'..'\x0D', ' '}: inc(pos) of '*': g.kind = gtReference - while g.buf[pos] notin {'\0', '\t'..'\r', ' '}: inc(pos) + while g.buf[pos] notin {'\0', '\x09'..'\x0D', ' '}: inc(pos) of '|', '>': # this can lead to incorrect tokenization when | or > appear inside flow # content. checking whether we're inside flow content is not @@ -899,18 +871,18 @@ proc yamlNextToken(g: var GeneralTokenizer) = # outside document case g.buf[pos] of '%': - if pos == 0 or g.buf[pos - 1] in {'\n', '\r'}: + if pos == 0 or g.buf[pos - 1] in {'\x0A', '\x0D'}: g.kind = gtDirective - while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos) + while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos) else: g.state = gtOther yamlPlainStrLit(g, pos) - of ' ', '\t'..'\r': + of ' ', '\x09'..'\x0D': g.kind = gtWhitespace - while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos) + while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) of '#': g.kind = gtComment - while g.buf[pos] notin {'\0', '\n', '\r'}: inc(pos) + while g.buf[pos] notin {'\0', '\x0A', '\x0D'}: inc(pos) of '\0': g.kind = gtEof else: g.kind = gtNone @@ -928,74 +900,6 @@ proc pythonNextToken(g: var GeneralTokenizer) = "with", "yield"] nimNextToken(g, keywords) -proc cmdNextToken(g: var GeneralTokenizer, dollarPrompt = false) = - var pos = g.pos - g.start = g.pos - if g.state == low(TokenClass): - g.state = if dollarPrompt: gtPrompt else: gtProgram - case g.buf[pos] - of ' ', '\t'..'\r': - g.kind = gtWhitespace - while g.buf[pos] in {' ', '\t'..'\r'}: - if g.buf[pos] == '\n': - g.state = if dollarPrompt: gtPrompt else: gtProgram - inc(pos) - of '\'', '"': - g.kind = gtOption - let q = g.buf[pos] - inc(pos) - while g.buf[pos] notin {q, '\0'}: - inc(pos) - if g.buf[pos] == q: inc(pos) - of '#': - g.kind = gtComment - while g.buf[pos] notin {'\n', '\0'}: - inc(pos) - of '&', '|': - g.kind = gtOperator - inc(pos) - if g.buf[pos] == g.buf[pos-1]: inc(pos) - g.state = gtProgram - of '(': - g.kind = gtOperator - g.state = gtProgram - inc(pos) - of ')': - g.kind = gtOperator - inc(pos) - of ';': - g.state = gtProgram - g.kind = gtOperator - inc(pos) - of '\0': g.kind = gtEof - elif dollarPrompt and g.state == gtPrompt: - if g.buf[pos] == '$' and g.buf[pos+1] in {' ', '\t'}: - g.kind = gtPrompt - inc pos, 2 - g.state = gtProgram - else: - g.kind = gtProgramOutput - while g.buf[pos] notin {'\n', '\0'}: - inc(pos) - else: - if g.state == gtProgram: - g.kind = gtProgram - g.state = gtOption - else: - g.kind = gtOption - while g.buf[pos] notin {' ', '\t'..'\r', '&', '|', '(', ')', '\'', '"', '\0'}: - if g.buf[pos] == ';' and g.buf[pos+1] == ' ': - # (check space because ';' can be used inside arguments in Win bat) - break - if g.kind == gtOption and g.buf[pos] in {'/', '\\', '.'}: - g.kind = gtIdentifier # for file/dir name - elif g.kind == gtProgram and g.buf[pos] == '=': - g.kind = gtIdentifier # for env variable setting at beginning of line - g.state = gtProgram - inc(pos) - g.length = pos - g.pos - g.pos = pos - proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) = g.lang = lang case lang @@ -1007,27 +911,13 @@ proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) = of langJava: javaNextToken(g) of langYaml: yamlNextToken(g) of langPython: pythonNextToken(g) - of langCmd: cmdNextToken(g) - of langConsole: cmdNextToken(g, dollarPrompt=true) - -proc tokenize*(text: string, lang: SourceLanguage): seq[(string, TokenClass)] = - var g: GeneralTokenizer - initGeneralTokenizer(g, text) - var prevPos = 0 - while true: - getNextToken(g, lang) - if g.kind == gtEof: - break - var s = text[prevPos ..< g.pos] - result.add (s, g.kind) - prevPos = g.pos when isMainModule: var keywords: seq[string] # Try to work running in both the subdir or at the root. for filename in ["doc/keywords.txt", "../../../doc/keywords.txt"]: try: - let input = readFile(filename) + let input = string(readFile(filename)) keywords = input.splitWhitespace() break except: diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index b3b1fb9c0..8d16edc61 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -7,44 +7,118 @@ # distribution, for details about the copyright. # -## 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). +## This module implements a `reStructuredText`:idx: (RST) parser. A large +## subset is implemented. Some features of the `markdown`:idx: syntax are +## also supported. Nim can output the result to HTML (command ``rst2html``) +## or Latex (command ``rst2tex``). ## -## * 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). +## If you are new to RST please consider reading the following: ## -## Choice between Markdown and RST as well as optional additional features are -## turned on by passing ``options:`` [RstParseOptions] to [proc rstParse]. +## 1) a short `quick introduction`_ +## 2) an `RST reference`_: a comprehensive cheatsheet for RST +## 3) a more formal 50-page `RST specification`_. +## +## 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. ... +## + definition lists +## + field lists +## + option lists +## + indented literal 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, substitution references, +## standalone hyperlinks +## + \`interpreted text\` with roles ``:literal:``, ``:strong:``, +## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:supscript:`` +## (see `RST roles list`_ for description). +## +## Additional features: +## +## * directives: ``code-block``, ``title``, ``index`` +## * ***triple emphasis*** (bold and italic) using \*\*\* +## * ``:idx:`` role for \`interpreted text\` to include the link to this +## text into an index (example: `Nim index`_). +## +## Optional additional features, turned on by ``options: RstParseOption`` in +## `rstParse proc <#rstParse,string,string,int,int,bool,RstParseOptions,FindFileHandler,MsgHandler>`_: +## +## * emoji / smiley symbols +## * markdown tables +## * markdown code blocks +## * markdown links +## * markdown headlines +## * 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. +## +## Limitations: +## +## * no Unicode support in character width calculations +## * body elements +## - no roman numerals in enumerated lists +## - no quoted literal blocks +## - 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 footnotes & citations support +## - no inline internal targets +## * inline markup +## - no simple-inline-markup +## - no embedded URI and aliases +## +## .. _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 +## +## See `Nim DocGen Tools Guide `_ for the details about +## ``nim doc``, ``nim rst2html`` and ``nim rst2tex`` commands. +## +## .. note:: Import ``packages/docutils/rst`` to use this module. +## +## See also `packages/docutils/rstgen module `_. import - os, strutils, rstast, dochelpers, std/enumutils, algorithm, lists, sequtils, - std/private/miscdollars, tables, strscans, rstidx -from highlite import SourceLanguage, getSourceLanguage - -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - + os, strutils, rstast type RstParseOption* = enum ## options for the RST parser + roSkipPounds, ## skip ``#`` at line beginning (documentation + ## embedded in Nim comments) roSupportSmilies, ## make the RST parser support smilies like ``:)`` roSupportRawDirective, ## support the ``raw`` directive (don't support ## it for sandboxing) - roSupportMarkdown, ## support additional features of Markdown - roPreferMarkdown, ## parse as Markdown (keeping RST as "extension" - ## to Markdown) -- implies `roSupportMarkdown` - roNimFile ## set for Nim files where default interpreted - ## text role should be :nim: - roSandboxDisabled ## this option enables certain options - ## (e.g. raw, include, importdoc) - ## which are disabled by default as they can - ## enable users to read arbitrary data and - ## perform XSS if the parser is used in a web - ## app. + roSupportMarkdown ## support additional features of markdown RstParseOptions* = set[RstParseOption] @@ -53,41 +127,26 @@ type mcWarning = "Warning", mcError = "Error" - # keep the order in sync with compiler/docgen.nim and compiler/lineinfos.nim: 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", + meMarkdownIllformedTable = "illformed delimiter row of a markdown table", + meNewSectionExpected = "new section expected", meGeneralParseError = "general parse error", meInvalidDirective = "invalid directive: '$1'", - meInvalidField = "invalid field: $1", - meFootnoteMismatch = "mismatch in number of footnotes and their refs: $1", mwRedefinitionOfLabel = "redefinition of label '$1'", mwUnknownSubstitution = "unknown substitution '$1'", - mwAmbiguousLink = "ambiguous doc link $1", - mwBrokenLink = "broken link '$1'", mwUnsupportedLanguage = "language '$1' not supported", - mwUnsupportedField = "field '$1' not supported", - mwRstStyle = "RST style: $1", - mwUnusedImportdoc = "importdoc for '$1' is not used", - meSandboxedDirective = "disabled directive: '$1'", + mwUnsupportedField = "field '$1' not supported" MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind, arg: string) {.closure, gcsafe.} ## what to do in case of an error FindFileHandler* = proc (filename: string): string {.closure, gcsafe.} - FindRefFileHandler* = - proc (targetRelPath: string): - tuple[targetPath: string, linkRelPath: string] {.closure, gcsafe.} - ## returns where .html or .idx file should be found by its relative path; - ## `linkRelPath` is a prefix to be added before a link anchor from such file proc rstnodeToRefname*(n: PRstNode): string proc addNodes*(n: PRstNode): string -proc getFieldValue*(n: PRstNode, fieldname: string): string {.gcsafe.} +proc getFieldValue*(n: PRstNode, fieldname: string): string proc getArgument*(n: PRstNode): string # ----------------------------- scanner part -------------------------------- @@ -134,10 +193,6 @@ const ":geek:": "icon_e_geek", ":ugeek:": "icon_e_ugeek" } - 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 @@ -158,8 +213,8 @@ type buf*: cstring bufpos*: int line*, col*, baseIndent*: int + skipPounds*: bool adornmentLine*: bool - escapeNext*: bool proc getThing(L: var Lexer, tok: var Token, s: set[char]) = tok.kind = tkWord @@ -197,18 +252,10 @@ proc getPunctAdornment(L: var Lexer, tok: var Token) = tok.col = L.col var pos = L.bufpos let c = L.buf[pos] - if not L.escapeNext and (c != '\\' or L.adornmentLine): - while true: - tok.symbol.add(L.buf[pos]) - inc pos - if L.buf[pos] != c: break - elif L.escapeNext: + while true: tok.symbol.add(L.buf[pos]) inc pos - else: # not L.escapeNext and c == '\\' and not L.adornmentLine - tok.symbol.add '\\' - inc pos - L.escapeNext = true + if L.buf[pos] != c: break inc L.col, pos - L.bufpos L.bufpos = pos if tok.symbol == "\\": tok.kind = tkPunct @@ -225,24 +272,27 @@ proc getBracket(L: var Lexer, tok: var Token) = proc getIndentAux(L: var Lexer, start: int): int = var pos = start # skip the newline (but include it in the token!) - if L.buf[pos] == '\r': - if L.buf[pos + 1] == '\n': inc pos, 2 + if L.buf[pos] == '\x0D': + if L.buf[pos + 1] == '\x0A': inc pos, 2 else: inc pos - elif L.buf[pos] == '\n': + elif L.buf[pos] == '\x0A': inc pos + if L.skipPounds: + if L.buf[pos] == '#': inc pos + if L.buf[pos] == '#': inc pos while true: case L.buf[pos] - of ' ', '\v', '\f': + of ' ', '\x0B', '\x0C': inc pos inc result - of '\t': + of '\x09': inc pos result = result - (result mod 8) + 8 else: break # EndOfFile also leaves the loop if L.buf[pos] == '\0': result = 0 - elif L.buf[pos] == '\n' or L.buf[pos] == '\r': + elif L.buf[pos] == '\x0A' or L.buf[pos] == '\x0D': # look at the next line for proper indentation: result = getIndentAux(L, pos) L.bufpos = pos # no need to set back buf @@ -266,12 +316,12 @@ proc rawGetTok(L: var Lexer, tok: var Token) = case c of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '0'..'9': getThing(L, tok, SymChars) - of ' ', '\t', '\v', '\f': - getThing(L, tok, {' ', '\t'}) + of ' ', '\x09', '\x0B', '\x0C': + getThing(L, tok, {' ', '\x09'}) tok.kind = tkWhite - if L.buf[L.bufpos] in {'\r', '\n'}: + if L.buf[L.bufpos] in {'\x0D', '\x0A'}: rawGetTok(L, tok) # ignore spaces before \n - of '\r', '\n': + of '\x0D', '\x0A': getIndent(L, tok) L.adornmentLine = false of '!', '\"', '#', '$', '%', '&', '\'', '*', '+', ',', '-', '.', @@ -294,19 +344,30 @@ proc rawGetTok(L: var Lexer, tok: var Token) = inc L.col tok.col = max(tok.col - L.baseIndent, 0) -proc getTokens(buffer: string, tokens: var TokenSeq) = +proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int = var L: Lexer var length = tokens.len L.buf = cstring(buffer) L.line = 0 # skip UTF-8 BOM if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF': inc L.bufpos, 3 + L.skipPounds = skipPounds + if skipPounds: + if L.buf[L.bufpos] == '#': + inc L.bufpos + inc result + if L.buf[L.bufpos] == '#': + inc L.bufpos + inc result + L.baseIndent = 0 + while L.buf[L.bufpos] == ' ': + inc L.bufpos + inc L.baseIndent + inc result while true: inc length setLen(tokens, length) - let toEscape = L.escapeNext rawGetTok(L, tokens[length - 1]) - if toEscape: L.escapeNext = false if tokens[length - 1].kind == tkEof: break if tokens[0].kind == tkWhite: # BUGFIX @@ -314,147 +375,46 @@ proc getTokens(buffer: string, tokens: var TokenSeq) = tokens[0].kind = tkIndent type - LevelInfo = object - symbol: char # adornment character - hasOverline: bool # has also overline (besides underline)? - line: int # the last line of this style occurrence - # (for error message) - hasPeers: bool # has headings on the same level of hierarchy? - LiteralBlockKind = enum # RST-style literal blocks after `::` - lbNone, - lbIndentedLiteralBlock, - lbQuotedLiteralBlock - LevelMap = seq[LevelInfo] # Saves for each possible title adornment - # style its level in the current document. - SubstitutionKind = enum - rstSubstitution = "substitution", - hyperlinkAlias = "hyperlink alias", - implicitHyperlinkAlias = "implicitly-generated hyperlink alias" + LevelMap = array[char, int] Substitution = object - kind*: SubstitutionKind key*: string value*: PRstNode - info*: TLineInfo # place where the substitution was defined - AnchorRule = enum - arInternalRst, ## For automatically generated RST anchors (from - ## headings, footnotes, inline internal targets): - ## case-insensitive, 1-space-significant (by RST spec) - arExternalRst, ## For external .nim doc comments or .rst/.md - arNim ## For anchors generated by ``docgen.nim``: Nim-style case - ## sensitivity, etc. (see `proc normalizeNimName`_ for details) - arHyperlink, ## For links with manually set anchors in - ## form `text `_ - RstAnchorKind = enum - manualDirectiveAnchor = "manual directive anchor", - manualInlineAnchor = "manual inline anchor", - footnoteAnchor = "footnote anchor", - headlineAnchor = "implicitly-generated headline anchor" - AnchorSubst = object - info: TLineInfo # where the anchor was defined - priority: int - case kind: range[arInternalRst .. arNim] - of arInternalRst: - anchorType: RstAnchorKind - target: PRstNode - of arExternalRst: - anchorTypeExt: RstAnchorKind - refnameExt: string - 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. - external: bool - AnchorSubstTable = Table[string, seq[AnchorSubst]] - # use `seq` to account for duplicate anchors - FootnoteType = enum - fnManualNumber, # manually numbered footnote like [3] - fnAutoNumber, # auto-numbered footnote [#] - fnAutoNumberLabel, # auto-numbered with label [#label] - fnAutoSymbol, # auto-symbol footnote [*] - fnCitation # simple text label like [citation2021] - FootnoteSubst = tuple - kind: FootnoteType # discriminator - number: int # valid for fnManualNumber (always) and fnAutoNumber, - # fnAutoNumberLabel after resolveSubs is called - autoNumIdx: int # order of occurence: fnAutoNumber, fnAutoNumberLabel - autoSymIdx: int # order of occurence: fnAutoSymbol - label: string # valid for fnAutoNumberLabel - RstFileTable* = object - filenameToIdx*: Table[string, FileIndex] - idxToFilename*: seq[string] - ImportdocInfo = object - used: bool # was this import used? - fromInfo: TLineInfo # place of `.. importdoc::` directive - idxPath: string # full path to ``.idx`` file - linkRelPath: string # prefix before target anchor - title: string # document title obtained from ``.idx`` - RstSharedState = object - 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 - hCurLevel: int # current section level - currRole: string # current interpreted text role - currRoleKind: RstNodeKind # ... and its node kind - subs: seq[Substitution] # substitutions - refs*: seq[Substitution] # references - anchors*: AnchorSubstTable - # internal target substitutions - lineFootnoteNum: seq[TLineInfo] # footnote line, auto numbers .. [#] - lineFootnoteNumRef: seq[TLineInfo] # footnote line, their reference [#]_ - currFootnoteNumRef: int # ... their counter for `resolveSubs` - lineFootnoteSym: seq[TLineInfo] # footnote line, auto symbols .. [*] - lineFootnoteSymRef: seq[TLineInfo] # footnote line, their reference [*]_ - currFootnoteSymRef: int # ... their counter for `resolveSubs` - footnotes: seq[FootnoteSubst] # correspondence b/w footnote label, - # number, order of occurrence - msgHandler: MsgHandler # How to handle errors. - findFile: FindFileHandler # How to find files for include. - findRefFile: FindRefFileHandler - # How to find files imported by importdoc. - 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 - idxImports*: Table[string, ImportdocInfo] - # map `importdoc`ed filename -> it's info - nimFileImported*: bool # Was any ``.nim`` module `importdoc`ed ? - PRstSharedState* = ref RstSharedState - ManualAnchor = object - alias: string # a (short) name that can substitute the `anchor` - anchor: string # anchor = id = refname - info: TLineInfo + SharedState = object + options: RstParseOptions # parsing options + uLevel, oLevel: int # counters for the section levels + subs: seq[Substitution] # substitutions + refs: seq[Substitution] # references + underlineToLevel: LevelMap # Saves for each possible title adornment + # character its level in the + # current document. + # This is for single underline adornments. + overlineToLevel: LevelMap # Saves for each possible title adornment + # character its level in the current + # document. + # This is for over-underline adornments. + msgHandler: MsgHandler # How to handle errors. + findFile: FindFileHandler # How to find files. + + PSharedState = ref SharedState RstParser = object of RootObj idx*: int tok*: TokenSeq - s*: PRstSharedState + s*: PSharedState indentStack*: seq[int] - line*, col*: int ## initial line/column of whole text or - ## documenation fragment that will be added - ## in case of error/warning reporting to - ## (relative) line/column of the token. - curAnchors*: seq[ManualAnchor] - ## seq to accumulate aliases for anchors: - ## because RST can have >1 alias per 1 anchor + filename*: string + line*, col*: int + hasToc*: bool EParseError* = object of ValueError -const - LineRstInit* = 1 ## Initial line number for standalone RST text - ColRstInit* = 0 ## Initial column number for standalone RST text - ## (Nim global reporting adds ColOffset=1) - ColRstOffset* = 1 ## 1: a replica of ColOffset for internal use - template currentTok(p: RstParser): Token = p.tok[p.idx] template prevTok(p: RstParser): Token = p.tok[p.idx - 1] template nextTok(p: RstParser): Token = p.tok[p.idx + 1] proc whichMsgClass*(k: MsgKind): MsgClass = ## returns which message class `k` belongs to. - case k.symbolName[1] + case ($k)[1] of 'e', 'E': result = mcError of 'w', 'W': result = mcWarning of 'h', 'H': result = mcHint @@ -464,9 +424,7 @@ proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind, arg: string) = let mc = msgkind.whichMsgClass let a = $msgkind % arg - var message: string - toLocation(message, filename, line, col + ColRstOffset) - message.add " $1: $2" % [$mc, a] + let message = "$1($2, $3) $4: $5" % [filename, $line, $col, $mc, a] if mc == mcError: raise newException(EParseError, message) else: writeLine(stdout, message) @@ -474,110 +432,31 @@ proc defaultFindFile*(filename: string): string = if fileExists(filename): result = filename else: result = "" -proc defaultFindRefFile*(filename: string): (string, string) = - (filename, "") - -proc defaultRole(options: RstParseOptions): string = - if roNimFile in options: "nim" else: "literal" - -proc whichRoleAux(sym: string): RstNodeKind = - let r = sym.toLowerAscii - case r - of "idx": result = rnIdx - of "literal": result = rnInlineLiteral - of "strong": result = rnStrongEmphasis - of "emphasis": result = rnEmphasis - of "sub", "subscript": result = rnSub - of "sup", "superscript": result = rnSup - # literal and code are the same in our implementation - of "code": result = rnInlineLiteral - of "program", "option", "tok": result = rnCodeFragment - # c++ currently can be spelled only as cpp, c# only as csharp - elif getSourceLanguage(r) != langNone: - result = rnInlineCode - else: # unknown role - result = rnUnknownRole - -proc len(filenames: RstFileTable): int = filenames.idxToFilename.len - -proc addFilename*(s: PRstSharedState, file1: string): FileIndex = - ## Returns index of filename, adding it if it has not been used before - let nextIdx = s.filenames.len.FileIndex - result = getOrDefault(s.filenames.filenameToIdx, file1, default = nextIdx) - if result == nextIdx: - s.filenames.filenameToIdx[file1] = result - s.filenames.idxToFilename.add file1 - -proc setCurrFilename*(s: PRstSharedState, file1: string) = - s.currFileIdx = addFilename(s, file1) - -proc getFilename(filenames: RstFileTable, fid: FileIndex): string = - doAssert(0 <= fid.int and fid.int < filenames.len, - "incorrect FileIndex $1 (range 0..$2)" % [ - $fid.int, $(filenames.len - 1)]) - result = filenames.idxToFilename[fid.int] - -proc getFilename(s: PRstSharedState, subst: AnchorSubst): string = - getFilename(s.filenames, subst.info.fileIndex) - -proc currFilename(s: PRstSharedState): string = - getFilename(s.filenames, s.currFileIdx) - -proc newRstSharedState*(options: RstParseOptions, - filename: string, - findFile: FindFileHandler, - findRefFile: FindRefFileHandler, - 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, - findRefFile: - if not isNil(findRefFile): findRefFile - else: defaultFindRefFile, - hasToc: hasToc - ) - setCurrFilename(result, filename) - -proc curLine(p: RstParser): int = p.line + currentTok(p).line +proc newSharedState(options: RstParseOptions, + findFile: FindFileHandler, + msgHandler: MsgHandler): PSharedState = + new(result) + result.subs = @[] + result.refs = @[] + result.options = options + result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler + result.findFile = if not isNil(findFile): findFile else: defaultFindFile proc findRelativeFile(p: RstParser; filename: string): string = - result = p.s.currFilename.splitFile.dir / filename + result = p.filename.splitFile.dir / filename if not fileExists(result): result = p.s.findFile(filename) proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) = - p.s.msgHandler(p.s.currFilename, curLine(p), + p.s.msgHandler(p.filename, p.line + currentTok(p).line, p.col + currentTok(p).col, msgKind, arg) -proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string) = - s.msgHandler(s.currFilename, LineRstInit, ColRstInit, msgKind, arg) - -proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string; - line, col: int) = - s.msgHandler(s.currFilename, line, col, msgKind, arg) - -proc rstMessage(s: PRstSharedState, filename: string, msgKind: MsgKind, - arg: string) = - s.msgHandler(filename, LineRstInit, ColRstInit, msgKind, arg) - -proc rstMessage*(filenames: RstFileTable, f: MsgHandler, - info: TLineInfo, msgKind: MsgKind, arg: string) = - ## Print warnings using `info`, i.e. in 2nd-pass warnings for - ## footnotes/substitutions/references or from ``rstgen.nim``. - let file = getFilename(filenames, info.fileIndex) - f(file, info.line.int, info.col.int, msgKind, arg) - proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) = - p.s.msgHandler(p.s.currFilename, p.line + line, + p.s.msgHandler(p.filename, p.line + line, p.col + col, msgKind, arg) proc rstMessage(p: RstParser, msgKind: MsgKind) = - p.s.msgHandler(p.s.currFilename, curLine(p), + p.s.msgHandler(p.filename, p.line + currentTok(p).line, p.col + currentTok(p).col, msgKind, currentTok(p).symbol) @@ -590,78 +469,17 @@ proc pushInd(p: var RstParser, ind: int) = proc popInd(p: var RstParser) = if p.indentStack.len > 1: setLen(p.indentStack, p.indentStack.len - 1) -# Working with indentation in rst.nim -# ----------------------------------- -# -# Every line break has an associated tkIndent. -# The tokenizer writes back the first column of next non-blank line -# in all preceeding tkIndent tokens to the `ival` field of tkIndent. -# -# RST document is separated into body elements (B.E.), every of which -# has a dedicated handler proc (or block of logic when B.E. is a block quote) -# that should follow the next rule: -# Every B.E. handler proc should finish at tkIndent (newline) -# after its B.E. finishes. -# Then its callers (which is `parseSection` or another B.E. handler) -# check for tkIndent ival (without necessity to advance `p.idx`) -# and decide themselves whether they continue processing or also stop. -# -# An example:: -# -# L RST text fragment indentation -# +--------------------+ -# 1 | | <- (empty line at the start of file) no tokens -# 2 |First paragraph. | <- tkIndent has ival=0, and next tkWord has col=0 -# 3 | | <- tkIndent has ival=0 -# 4 |* bullet item and | <- tkIndent has ival=0, and next tkPunct has col=0 -# 5 | its continuation | <- tkIndent has ival=2, and next tkWord has col=2 -# 6 | | <- tkIndent has ival=4 -# 7 | Block quote | <- tkIndent has ival=4, and next tkWord has col=4 -# 8 | | <- tkIndent has ival=0 -# 9 | | <- tkIndent has ival=0 -# 10|Final paragraph | <- tkIndent has ival=0, and tkWord has col=0 -# +--------------------+ -# C:01234 -# -# Here parser starts with initial `indentStack=[0]` and then calls the -# 1st `parseSection`: -# -# - `parseSection` calls `parseParagraph` and "First paragraph" is parsed -# - bullet list handler is started at reaching ``*`` (L4 C0), it -# starts bullet item logic (L4 C2), which calls `pushInd(p, ind=2)`, -# then calls `parseSection` (2nd call, nested) which parses -# paragraph "bullet list and its continuation" and then starts -# a block quote logic (L7 C4). -# The block quote logic calls calls `pushInd(p, ind=4)` and -# calls `parseSection` again, so a (simplified) sequence of calls now is:: -# -# parseSection -> parseBulletList -> -# parseSection (+block quote logic) -> parseSection -# -# 3rd `parseSection` finishes, block quote logic calls `popInd(p)`, -# it returns to bullet item logic, which sees that next tkIndent has -# ival=0 and stops there since the required indentation for a bullet item -# is 2 and 0<2; the bullet item logic calls `popInd(p)`. -# Then bullet list handler checks that next tkWord (L10 C0) has the -# right indentation but does not have ``*`` so stops at tkIndent (L10). -# - 1st `parseSection` invocation calls `parseParagraph` and the -# "Final paragraph" is parsed. -# -# If a B.E. handler has advanced `p.idx` past tkIndent to check -# whether it should continue its processing or not, and decided not to, -# then this B.E. handler should step back (e.g. do `dec p.idx`). - -proc initParser(p: var RstParser, sharedState: PRstSharedState) = +proc initParser(p: var RstParser, sharedState: PSharedState) = p.indentStack = @[0] p.tok = @[] p.idx = 0 - p.col = ColRstInit - p.line = LineRstInit + p.filename = "" + p.hasToc = false + p.col = 0 + p.line = 1 p.s = sharedState proc addNodesAux(n: PRstNode, result: var string) = - if n == nil: - return if n.kind == rnLeaf: result.add(n.text) else: @@ -670,11 +488,6 @@ proc addNodesAux(n: PRstNode, result: var string) = proc addNodes(n: PRstNode): string = n.addNodesAux(result) -proc linkName(n: PRstNode): string = - ## Returns a normalized reference name, see: - ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#reference-names - n.addNodes.toLowerAscii - proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) = template special(s) = if b: @@ -730,37 +543,26 @@ proc rstnodeToRefname(n: PRstNode): string = var b = false rstnodeToRefnameAux(n, result, b) -proc findSub(s: PRstSharedState, n: PRstNode): int = +proc findSub(p: var RstParser, n: PRstNode): int = var key = addNodes(n) # the spec says: if no exact match, try one without case distinction: - for i in countup(0, high(s.subs)): - if key == s.subs[i].key: + for i in countup(0, high(p.s.subs)): + if key == p.s.subs[i].key: return i - for i in countup(0, high(s.subs)): - if cmpIgnoreStyle(key, s.subs[i].key) == 0: + for i in countup(0, high(p.s.subs)): + if cmpIgnoreStyle(key, p.s.subs[i].key) == 0: return i result = -1 -proc lineInfo(p: RstParser, iTok: int): TLineInfo = - result.col = int16(p.col + p.tok[iTok].col) - result.line = uint16(p.line + p.tok[iTok].line) - result.fileIndex = p.s.currFileIdx - -proc lineInfo(p: RstParser): TLineInfo = lineInfo(p, p.idx) -# TODO: we need this simplification because we don't preserve exact starting -# token of currently parsed element: -proc prevLineInfo(p: RstParser): TLineInfo = lineInfo(p, p.idx-1) - proc setSub(p: var RstParser, key: string, value: PRstNode) = var length = p.s.subs.len for i in 0 ..< length: if key == p.s.subs[i].key: p.s.subs[i].value = value return - p.s.subs.add(Substitution(key: key, value: value, info: prevLineInfo(p))) + p.s.subs.add(Substitution(key: key, value: value)) -proc setRef(p: var RstParser, key: string, value: PRstNode, - refType: SubstitutionKind) = +proc setRef(p: var RstParser, key: string, value: PRstNode) = var length = p.s.refs.len for i in 0 ..< length: if key == p.s.refs[i].key: @@ -768,295 +570,15 @@ proc setRef(p: var RstParser, key: string, value: PRstNode, rstMessage(p, mwRedefinitionOfLabel, key) p.s.refs[i].value = value return - p.s.refs.add(Substitution(kind: refType, key: key, value: value, - info: prevLineInfo(p))) + p.s.refs.add(Substitution(key: key, value: value)) -proc findRef(s: PRstSharedState, key: string): seq[Substitution] = - for i in countup(0, high(s.refs)): - if key == s.refs[i].key: - result.add s.refs[i] - -# Ambiguity in links: we don't follow procedure of removing implicit targets -# defined in https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#implicit-hyperlink-targets -# Instead we just give explicit links a higher priority than to implicit ones -# and report ambiguities as warnings. Hopefully it is easy to remove -# ambiguities manually. Nim auto-generated links from ``docgen.nim`` -# have lowest priority: 1 (for procs) and below for other symbol types. - -proc refPriority(k: SubstitutionKind): int = - case k - of rstSubstitution: result = 8 - of hyperlinkAlias: result = 7 - of implicitHyperlinkAlias: result = 2 - -proc internalRefPriority(k: RstAnchorKind): int = - case k - of manualDirectiveAnchor: result = 6 - of manualInlineAnchor: result = 5 - of footnoteAnchor: result = 4 - of headlineAnchor: result = 3 - -proc `$`(subst: AnchorSubst): string = # for debug - let s = - case subst.kind - of arInternalRst: "type=" & $subst.anchorType - of arExternalRst: "type=" & $subst.anchorTypeExt - of arNim: "langsym=" & $subst.langSym - result = "(kind=$1, priority=$2, $3)" % [$subst.kind, $subst.priority, s] - -proc addAnchorRst(p: var RstParser, name: string, target: PRstNode, - anchorType: RstAnchorKind) = - ## Associates node `target` (which has field `anchor`) with an - ## alias `name` and updates the corresponding aliases in `p.curAnchors`. - let prio = internalRefPriority(anchorType) - 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, target: target, priority: prio, - info: prevLineInfo(p), anchorType: anchorType)) - p.curAnchors.setLen 0 - -proc addAnchorExtRst(s: var PRstSharedState, key: string, refn: string, - anchorType: RstAnchorKind, info: TLineInfo) = - let name = key.toLowerAscii - let prio = internalRefPriority(anchorType) - s.anchors.mgetOrPut(name, newSeq[AnchorSubst]()).add( - AnchorSubst(kind: arExternalRst, refnameExt: refn, priority: prio, - info: info, - anchorTypeExt: anchorType)) - -proc addAnchorNim*(s: var PRstSharedState, external: bool, refn: string, tooltip: string, - langSym: LangSymbol, priority: int, - info: TLineInfo) = - ## Adds an anchor `refn`, which follows - ## the rule `arNim` (i.e. a symbol in ``*.nim`` file) - s.anchors.mgetOrPut(langSym.name, newSeq[AnchorSubst]()).add( - AnchorSubst(kind: arNim, external: external, refname: refn, langSym: langSym, - tooltip: tooltip, priority: priority, - info: info)) - -proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode, - info: TLineInfo): - seq[AnchorSubst] = - var langSym: LangSymbol - try: - langSym = toLangSymbol(signature) - except ValueError: # parsing failed, not a Nim symbol - return - let substitutions = s.anchors.getOrDefault(langSym.name, - newSeq[AnchorSubst]()) - if substitutions.len == 0: - return - # logic to select only groups instead of concrete symbols - # with overloads, note that the same symbol can be defined - # in multiple modules and `importdoc`ed: - type GroupKey = tuple[symKind: string, origModule: string] - # map (symKind, file) (like "proc", "os.nim") -> found symbols/groups: - var found: Table[GroupKey, seq[AnchorSubst]] - for subst in substitutions: - if subst.kind == arNim: - if match(subst.langSym, langSym): - let key: GroupKey = (subst.langSym.symKind, getFilename(s, subst)) - found.mgetOrPut(key, newSeq[AnchorSubst]()).add subst - for key, sList in found: - if sList.len == 1: - result.add sList[0] - else: # > 1, there are overloads, potential ambiguity in this `symKind` - if langSym.parametersProvided: - # there are non-group signatures, select only them - for s in sList: - if not s.langSym.isGroup: - result.add s - else: # when there are many overloads a link like foo_ points to all - # of them, so selecting the group - var foundGroup = false - for s in sList: - if s.langSym.isGroup: - result.add s - foundGroup = true - break - doAssert(foundGroup, - "docgen has not generated the group for $1 (file $2)" % [ - langSym.name, getFilename(s, sList[0]) ]) - -proc findMainAnchorRst(s: PRstSharedState, linkText: string, info: TLineInfo): - seq[AnchorSubst] = - let name = linkText.toLowerAscii - let substitutions = s.anchors.getOrDefault(name, newSeq[AnchorSubst]()) - for s in substitutions: - if s.kind in {arInternalRst, arExternalRst}: - result.add s - -proc addFootnoteNumManual(p: var RstParser, num: int) = - ## add manually-numbered footnote - for fnote in p.s.footnotes: - if fnote.number == num: - rstMessage(p, mwRedefinitionOfLabel, $num) - return - p.s.footnotes.add((fnManualNumber, num, -1, -1, $num)) - -proc addFootnoteNumAuto(p: var RstParser, label: string) = - ## add auto-numbered footnote. - ## Empty label [#] means it'll be resolved by the occurrence. - if label == "": # simple auto-numbered [#] - p.s.lineFootnoteNum.add lineInfo(p) - p.s.footnotes.add((fnAutoNumber, -1, p.s.lineFootnoteNum.len, -1, label)) - else: # auto-numbered with label [#label] - for fnote in p.s.footnotes: - if fnote.label == label: - rstMessage(p, mwRedefinitionOfLabel, label) - return - p.s.footnotes.add((fnAutoNumberLabel, -1, -1, -1, label)) - -proc addFootnoteSymAuto(p: var RstParser) = - p.s.lineFootnoteSym.add lineInfo(p) - p.s.footnotes.add((fnAutoSymbol, -1, -1, p.s.lineFootnoteSym.len, "")) - -proc orderFootnotes(s: PRstSharedState) = - ## numerate auto-numbered footnotes taking into account that all - ## manually numbered ones always have preference. - ## Save the result back to `s.footnotes`. - - # Report an error if found any mismatch in number of automatic footnotes - proc listFootnotes(locations: seq[TLineInfo]): string = - var lines: seq[string] - for info in locations: - if s.filenames.len > 1: - let file = getFilename(s.filenames, info.fileIndex) - lines.add file & ":" - else: # no need to add file name here if there is only 1 - lines.add "" - lines[^1].add $info.line - result.add $lines.len & " (lines " & join(lines, ", ") & ")" - if s.lineFootnoteNum.len != s.lineFootnoteNumRef.len: - rstMessage(s, meFootnoteMismatch, - "$1 != $2" % [listFootnotes(s.lineFootnoteNum), - listFootnotes(s.lineFootnoteNumRef)] & - " for auto-numbered footnotes") - if s.lineFootnoteSym.len != s.lineFootnoteSymRef.len: - rstMessage(s, meFootnoteMismatch, - "$1 != $2" % [listFootnotes(s.lineFootnoteSym), - listFootnotes(s.lineFootnoteSymRef)] & - " for auto-symbol footnotes") - - var result: seq[FootnoteSubst] - var manuallyN, autoN, autoSymbol: seq[FootnoteSubst] - for fs in s.footnotes: - if fs.kind == fnManualNumber: manuallyN.add fs - elif fs.kind in {fnAutoNumber, fnAutoNumberLabel}: autoN.add fs - else: autoSymbol.add fs - - if autoN.len == 0: - result = manuallyN - else: - # fill gaps between manually numbered footnotes in ascending order - manuallyN.sort() # sort by number - its first field - var lst = initSinglyLinkedList[FootnoteSubst]() - for elem in manuallyN: lst.append(elem) - var firstAuto = 0 - if lst.head == nil or lst.head.value.number != 1: - # no manual footnote [1], start numeration from 1 for auto-numbered - lst.prepend (autoN[0].kind, 1, autoN[0].autoNumIdx, -1, autoN[0].label) - firstAuto = 1 - var curNode = lst.head - var nextNode: SinglyLinkedNode[FootnoteSubst] - # go simultaneously through `autoN` and `lst` looking for gaps - for (kind, x, autoNumIdx, y, label) in autoN[firstAuto .. ^1]: - while (nextNode = curNode.next; nextNode != nil): - if nextNode.value.number - curNode.value.number > 1: - # gap found, insert new node `n` between curNode and nextNode: - var n = newSinglyLinkedNode((kind, curNode.value.number + 1, - autoNumIdx, -1, label)) - curNode.next = n - n.next = nextNode - curNode = n - break - else: - curNode = nextNode - if nextNode == nil: # no gap found, just append - lst.append (kind, curNode.value.number + 1, autoNumIdx, -1, label) - curNode = lst.tail - result = lst.toSeq - - # we use ASCII symbols instead of those recommended in RST specification: - const footnoteAutoSymbols = ["*", "^", "+", "=", "~", "$", "@", "%", "&"] - for fs in autoSymbol: - # assignment order: *, **, ***, ^, ^^, ^^^, ... &&&, ****, *****, ... - let i = fs.autoSymIdx - 1 - let symbolNum = (i div 3) mod footnoteAutoSymbols.len - let nSymbols = (1 + i mod 3) + 3 * (i div (3 * footnoteAutoSymbols.len)) - let label = footnoteAutoSymbols[symbolNum].repeat(nSymbols) - result.add((fs.kind, -1, -1, fs.autoSymIdx, label)) - - s.footnotes = result - -proc getFootnoteNum(s: PRstSharedState, label: string): int = - ## get number from label. Must be called after `orderFootnotes`. - result = -1 - for fnote in s.footnotes: - if fnote.label == label: - return fnote.number - -proc getFootnoteNum(s: PRstSharedState, order: int): int = - ## get number from occurrence. Must be called after `orderFootnotes`. - result = -1 - for fnote in s.footnotes: - if fnote.autoNumIdx == order: - return fnote.number - -proc getAutoSymbol(s: PRstSharedState, order: int): string = - ## get symbol from occurrence of auto-symbol footnote. - result = "???" - for fnote in s.footnotes: - if fnote.autoSymIdx == order: - return fnote.label - -proc newRstNodeA(p: var RstParser, kind: RstNodeKind): PRstNode = - ## create node and consume the current anchor - result = newRstNode(kind) - if p.curAnchors.len > 0: - result.anchor = p.curAnchors[0].anchor - addAnchorRst(p, "", result, manualDirectiveAnchor) - -template newLeaf(s: string): PRstNode = newRstLeaf(s) +proc findRef(p: var RstParser, key: string): PRstNode = + for i in countup(0, high(p.s.refs)): + if key == p.s.refs[i].key: + return p.s.refs[i].value proc newLeaf(p: var RstParser): PRstNode = - result = newLeaf(currentTok(p).symbol) - -proc validRefnamePunct(x: string): bool = - ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#reference-names - x.len == 1 and x[0] in {'-', '_', '.', ':', '+'} - -func getRefnameIdx(p: RstParser, startIdx: int): int = - ## Gets last token index of a refname ("word" in RST terminology): - ## - ## reference names are single words consisting of alphanumerics plus - ## isolated (no two adjacent) internal hyphens, underscores, periods, - ## colons and plus signs; no whitespace or other characters are allowed. - ## - ## Refnames are used for: - ## - reference names - ## - role names - ## - directive names - ## - footnote labels - ## - # TODO: use this func in all other relevant places - var j = startIdx - if p.tok[j].kind == tkWord: - inc j - while p.tok[j].kind == tkPunct and validRefnamePunct(p.tok[j].symbol) and - p.tok[j+1].kind == tkWord: - inc j, 2 - result = j - 1 - -func getRefname(p: RstParser, startIdx: int): (string, int) = - let lastIdx = getRefnameIdx(p, startIdx) - result[1] = lastIdx - for j in startIdx..lastIdx: - result[0].add p.tok[j].symbol + result = newRstNode(rnLeaf, currentTok(p).symbol) proc getReferenceName(p: var RstParser, endStr: string): PRstNode = var res = newRstNode(rnInner) @@ -1086,82 +608,51 @@ proc expect(p: var RstParser, tok: string) = if currentTok(p).symbol == tok: inc p.idx else: rstMessage(p, meExpected, tok) -proc inlineMarkdownEnd(p: RstParser): bool = - result = prevTok(p).kind notin {tkIndent, tkWhite} - ## (For a special case of ` we don't allow spaces surrounding it - ## unlike original Markdown because this behavior confusing/useless) - -proc inlineRstEnd(p: RstParser): bool = +proc isInlineMarkupEnd(p: RstParser, markup: string): bool = # rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules + result = currentTok(p).symbol == markup + if not result: return # Rule 2: result = prevTok(p).kind notin {tkIndent, tkWhite} if not result: return # Rule 7: result = nextTok(p).kind in {tkIndent, tkWhite, tkEof} or + markup in ["``", "`"] and nextTok(p).kind in {tkIndent, tkWhite, tkWord, tkEof} or nextTok(p).symbol[0] in {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', '?', '_'} - -proc isInlineMarkupEnd(p: RstParser, markup: string, exact: bool): bool = - if exact: - result = currentTok(p).symbol == markup - else: - result = currentTok(p).symbol.endsWith markup - if (not result) and markup == "``": - # check that escaping may have splitted `` to 2 tokens ` and ` - result = currentTok(p).symbol == "`" and prevTok(p).symbol == "`" if not result: return - # surroundings check - if markup in ["_", "__"]: - result = inlineRstEnd(p) - else: - if roPreferMarkdown in p.s.options: result = inlineMarkdownEnd(p) - else: result = inlineRstEnd(p) - -proc rstRuleSurround(p: RstParser): bool = - result = true - # Rules 4 & 5: + # Rule 4: if p.idx > 0: - var d: char - var c = prevTok(p).symbol[0] - case c - of '\'', '\"': d = c - of '(': d = ')' - of '[': d = ']' - of '{': d = '}' - of '<': d = '>' - else: d = '\0' - if d != '\0': result = nextTok(p).symbol[0] != d + if markup != "``" and prevTok(p).symbol == "\\": + result = false -proc inlineMarkdownStart(p: RstParser): bool = - result = nextTok(p).kind notin {tkIndent, tkWhite, tkEof} +proc isInlineMarkupStart(p: RstParser, markup: string): bool = + # rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules + var d: char + result = currentTok(p).symbol == markup if not result: return - # this rst rule is really nice, let us use it in Markdown mode too. - result = rstRuleSurround(p) - -proc inlineRstStart(p: RstParser): bool = - ## rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules - # Rule 6 + # Rule 6: result = p.idx == 0 or prevTok(p).kind in {tkIndent, tkWhite} or + (markup in ["``", "`"] and prevTok(p).kind in {tkIndent, tkWhite, tkWord}) or prevTok(p).symbol[0] in {'\'', '\"', '(', '[', '{', '<', '-', '/', ':', '_'} if not result: return # Rule 1: result = nextTok(p).kind notin {tkIndent, tkWhite, tkEof} if not result: return - result = rstRuleSurround(p) - -proc isInlineMarkupStart(p: RstParser, markup: string): bool = - if markup != "_`": - result = currentTok(p).symbol == markup - else: # _` is a 2 token case - result = currentTok(p).symbol == "_" and nextTok(p).symbol == "`" - if not result: return - # surroundings check - if markup in ["_", "__", "[", "|"]: - # Note: we require space/punctuation even before [markdown link](...) - result = inlineRstStart(p) - else: - if roPreferMarkdown in p.s.options: result = inlineMarkdownStart(p) - else: result = inlineRstStart(p) + # Rules 4 & 5: + if p.idx > 0: + if prevTok(p).symbol == "\\": + result = false + else: + var c = prevTok(p).symbol[0] + case c + of '\'', '\"': d = c + of '(': d = ')' + of '[': d = ']' + of '{': d = '}' + of '<': d = '>' + else: d = '\0' + if d != '\0': result = nextTok(p).symbol[0] != d proc match(p: RstParser, start: int, expr: string): bool = # regular expressions are: @@ -1170,7 +661,6 @@ proc match(p: RstParser, start: int, expr: string): bool = # ' ' tkWhite # 'a' tkAdornment # 'i' tkIndent - # 'I' tkIndent or tkEof # 'p' tkPunct # 'T' always true # 'E' whitespace, indent or eof @@ -1182,13 +672,9 @@ proc match(p: RstParser, start: int, expr: string): bool = var last = expr.len - 1 while i <= last: case expr[i] - of 'w': - let lastIdx = getRefnameIdx(p, j) - result = lastIdx >= j - if result: j = lastIdx + of 'w': result = p.tok[j].kind == tkWord of ' ': result = p.tok[j].kind == tkWhite of 'i': result = p.tok[j].kind == tkIndent - of 'I': result = p.tok[j].kind in {tkIndent, tkEof} of 'p': result = p.tok[j].kind == tkPunct of 'a': result = p.tok[j].kind == tkAdornment of 'o': result = p.tok[j].kind == tkOther @@ -1219,20 +705,7 @@ proc match(p: RstParser, start: int, expr: string): bool = inc i result = true -proc safeProtocol*(linkStr: var string): string = - # Returns link's protocol and, if it's not safe, clears `linkStr` - result = "" - if scanf(linkStr, "$w:", result): - # if it has a protocol at all, ensure that it's not 'javascript:' or worse: - if cmpIgnoreCase(result, "http") == 0 or - cmpIgnoreCase(result, "https") == 0 or - cmpIgnoreCase(result, "ftp") == 0: - discard "it's fine" - else: - linkStr = "" - -proc fixupEmbeddedRef(p: var RstParser, n, a, b: PRstNode): bool = - # Returns `true` if the link belongs to an allowed protocol +proc fixupEmbeddedRef(n, a, b: PRstNode) = var sep = - 1 for i in countdown(n.len - 2, 0): if n.sons[i].text == "<": @@ -1240,89 +713,51 @@ proc fixupEmbeddedRef(p: var RstParser, n, a, b: PRstNode): bool = break var incr = if sep > 0 and n.sons[sep - 1].text[0] == ' ': 2 else: 1 for i in countup(0, sep - incr): a.add(n.sons[i]) - var linkStr = "" - for i in countup(sep + 1, n.len - 2): linkStr.add(n.sons[i].addNodes) - if linkStr != "": - let protocol = safeProtocol(linkStr) - result = linkStr != "" - if not result: - rstMessage(p, mwBrokenLink, protocol, - p.tok[p.idx-3].line, p.tok[p.idx-3].col) - b.add newLeaf(linkStr) - -proc whichRole(p: RstParser, sym: string): RstNodeKind = - result = whichRoleAux(sym) - if result == rnUnknownRole: - rstMessage(p, mwUnsupportedLanguage, sym) - -proc toInlineCode(n: PRstNode, language: string): PRstNode = - ## Creates rnInlineCode and attaches `n` contents as code (in 3rd son). - result = newRstNode(rnInlineCode, info=n.info) - let args = newRstNode(rnDirArg) - var lang = language - if language == "cpp": lang = "c++" - elif language == "csharp": lang = "c#" - args.add newLeaf(lang) - result.add args - result.add PRstNode(nil) - var lb = newRstNode(rnLiteralBlock) - var s: string - for i in n.sons: - assert i.kind == rnLeaf - s.add i.text - lb.add newLeaf(s) - result.add lb - -proc toOtherRole(n: PRstNode, kind: RstNodeKind, roleName: string): PRstNode = - let newN = newRstNode(rnInner, n.sons) - let newSons = @[newN, newLeaf(roleName)] - result = newRstNode(kind, newSons) + for i in countup(sep + 1, n.len - 2): b.add(n.sons[i]) proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = - ## Finalizes node `n` that was tentatively determined as interpreted text. - var newKind = n.kind - var newSons = n.sons - - proc finalizeInterpreted(node: PRstNode, newKind: RstNodeKind, - newSons: seq[PRstNode], roleName: string): - PRstNode {.nimcall.} = - # fixes interpreted text (`x` or `y`:role:) to proper internal AST format - if newKind in {rnUnknownRole, rnCodeFragment}: - result = node.toOtherRole(newKind, roleName) - elif newKind == rnInlineCode: - result = node.toInlineCode(language=roleName) - else: - result = newRstNode(newKind, newSons) - - if isInlineMarkupEnd(p, "_", exact=true) or - isInlineMarkupEnd(p, "__", exact=true): + result = n + if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"): inc p.idx if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">": var a = newRstNode(rnInner) var b = newRstNode(rnInner) - if fixupEmbeddedRef(p, n, a, b): - if a.len == 0: # e.g. ``_ - newKind = rnStandaloneHyperlink - newSons = @[b] - else: # e.g. `link title `_ - newKind = rnHyperlink - newSons = @[a, b] - setRef(p, rstnodeToRefname(a), b, implicitHyperlinkAlias) - else: # include as plain text, not a link - newKind = rnInner - newSons = n.sons - result = newRstNode(newKind, newSons) - else: # some link that will be resolved in `resolveSubs` - newKind = rnRstRef - result = newRstNode(newKind, sons=newSons, info=n.info) + fixupEmbeddedRef(n, a, b) + if a.len == 0: + result = newRstNode(rnStandaloneHyperlink) + result.add(b) + else: + result = newRstNode(rnHyperlink) + result.add(a) + result.add(b) + setRef(p, rstnodeToRefname(a), b) + elif n.kind == rnInterpretedText: + n.kind = rnRef + else: + result = newRstNode(rnRef) + result.add(n) elif match(p, p.idx, ":w:"): # a role: - let (roleName, lastIdx) = getRefname(p, p.idx+1) - newKind = whichRole(p, roleName) - result = n.finalizeInterpreted(newKind, newSons, roleName) - p.idx = lastIdx + 2 - else: - result = n.finalizeInterpreted(p.s.currRoleKind, newSons, p.s.currRole) + if nextTok(p).symbol == "idx": + n.kind = rnIdx + elif nextTok(p).symbol == "literal": + n.kind = rnInlineLiteral + elif nextTok(p).symbol == "strong": + n.kind = rnStrongEmphasis + elif nextTok(p).symbol == "emphasis": + n.kind = rnEmphasis + elif nextTok(p).symbol == "sub" or + nextTok(p).symbol == "subscript": + n.kind = rnSub + elif nextTok(p).symbol == "sup" or + nextTok(p).symbol == "supscript": + n.kind = rnSup + else: + result = newRstNode(rnGeneralRole) + n.kind = rnInner + result.add(n) + result.add(newRstNode(rnLeaf, nextTok(p).symbol)) + inc p.idx, 3 proc matchVerbatim(p: RstParser, start: int, expr: string): int = result = start @@ -1348,81 +783,32 @@ proc isUrl(p: RstParser, i: int): bool = p.tok[i+3].kind == tkWord and p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"] -proc checkParen(token: Token, parensStack: var seq[char]): bool {.inline.} = - ## Returns `true` iff `token` is a closing parenthesis for some - ## previous opening parenthesis saved in `parensStack`. - ## This is according Markdown balanced parentheses rule - ## (https://spec.commonmark.org/0.29/#link-destination) - ## to allow links like - ## https://en.wikipedia.org/wiki/APL_(programming_language), - ## we use it for RST also. - result = false - if token.kind == tkPunct: - let c = token.symbol[0] - if c in {'(', '[', '{'}: # push - parensStack.add c - elif c in {')', ']', '}'}: # try pop - # a case like ([) inside a link is allowed and [ is also `pop`ed: - for i in countdown(parensStack.len - 1, 0): - if (parensStack[i] == '(' and c == ')' or - parensStack[i] == '[' and c == ']' or - parensStack[i] == '{' and c == '}'): - parensStack.setLen i - result = true - break - -proc parseUrl(p: var RstParser): PRstNode = - ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#standalone-hyperlinks - result = newRstNode(rnStandaloneHyperlink) - var lastIdx = p.idx - var closedParenIdx = p.idx - 1 # for balanced parens rule - var parensStack: seq[char] - while p.tok[lastIdx].kind in {tkWord, tkPunct, tkOther}: - let isClosing = checkParen(p.tok[lastIdx], parensStack) - if isClosing: - closedParenIdx = lastIdx - inc lastIdx - dec lastIdx - # standalone URL can not end with punctuation in RST - while lastIdx > closedParenIdx and p.tok[lastIdx].kind == tkPunct and - p.tok[lastIdx].symbol != "/": - dec lastIdx - var s = "" - for i in p.idx .. lastIdx: s.add p.tok[i].symbol - result.add s - p.idx = lastIdx + 1 - -proc parseWordOrRef(p: var RstParser, father: PRstNode) = - ## Parses a normal word or may be a reference or URL. - if nextTok(p).kind != tkPunct: # <- main path, a normal word - father.add newLeaf(p) - inc p.idx - elif isUrl(p, p.idx): # URL http://something - father.add parseUrl(p) - else: - # check for reference (probably, long one like some.ref.with.dots_ ) - var saveIdx = p.idx - var reference: PRstNode = nil - inc p.idx - while currentTok(p).kind in {tkWord, tkPunct}: - if currentTok(p).kind == tkPunct: - if isInlineMarkupEnd(p, "_", exact=true): - reference = newRstNode(rnRstRef, info=lineInfo(p, saveIdx)) - break - if not validRefnamePunct(currentTok(p).symbol): +proc parseUrl(p: var RstParser, father: PRstNode) = + #if currentTok(p).symbol[strStart] == '<': + if isUrl(p, p.idx): + var n = newRstNode(rnStandaloneHyperlink) + while true: + case currentTok(p).kind + of tkWord, tkAdornment, tkOther: discard + of tkPunct: + if nextTok(p).kind notin {tkWord, tkAdornment, tkOther, tkPunct}: break + else: break + n.add(newLeaf(p)) inc p.idx - if reference != nil: - for i in saveIdx..p.idx-1: reference.add newLeaf(p.tok[i].symbol) - father.add reference - inc p.idx # skip final _ - else: # 1 normal word - father.add newLeaf(p.tok[saveIdx].symbol) - p.idx = saveIdx + 1 + father.add(n) + else: + var n = newLeaf(p) + inc p.idx + if currentTok(p).symbol == "_": n = parsePostfix(p, n) + father.add(n) proc parseBackslash(p: var RstParser, father: PRstNode) = assert(currentTok(p).kind == tkPunct) - if currentTok(p).symbol == "\\": + if currentTok(p).symbol == "\\\\": + father.add(newRstNode(rnLeaf, "\\")) + inc p.idx + elif currentTok(p).symbol == "\\": # XXX: Unicode? inc p.idx if currentTok(p).kind != tkWhite: father.add(newLeaf(p)) @@ -1440,295 +826,84 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, while true: case currentTok(p).kind of tkPunct: - if isInlineMarkupEnd(p, postfix, exact=false): - let l = currentTok(p).symbol.len - if l > postfix.len: - # handle cases like *emphasis with stars****. (It's valid RST!) - father.add newLeaf(currentTok(p).symbol[0 ..< l - postfix.len]) - elif postfix == "``" and currentTok(p).symbol == "`" and - prevTok(p).symbol == "`": - # handle cases like ``literal\`` - delete ` already added after \ - father.sons.setLen(father.sons.len - 1) + if isInlineMarkupEnd(p, postfix): inc p.idx break + elif interpretBackslash: + parseBackslash(p, father) else: - if postfix == "`": - if currentTok(p).symbol == "\\": - if nextTok(p).symbol == "\\": - father.add newLeaf("\\") - father.add newLeaf("\\") - inc p.idx, 2 - elif nextTok(p).symbol == "`": # escape ` - father.add newLeaf("`") - inc p.idx, 2 - else: - father.add newLeaf("\\") - inc p.idx - else: - father.add(newLeaf(p)) - inc p.idx - else: - if interpretBackslash: - parseBackslash(p, father) - else: - father.add(newLeaf(p)) - inc p.idx + father.add(newLeaf(p)) + inc p.idx of tkAdornment, tkWord, tkOther: father.add(newLeaf(p)) inc p.idx of tkIndent: - father.add newLeaf(" ") + father.add(newRstNode(rnLeaf, " ")) inc p.idx if currentTok(p).kind == tkIndent: rstMessage(p, meExpected, postfix, line, col) break of tkWhite: - father.add newLeaf(" ") + father.add(newRstNode(rnLeaf, " ")) 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("") + var n = newRstNode(rnLeaf, "") while true: - if currentTok(p).kind == tkEof: - rstMessage(p, meMissingClosing, - "$1 (started at line $2)" % [baseSym, $line]) + case currentTok(p).kind + of tkEof: + rstMessage(p, meExpected, "```") break - 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 + of tkPunct, tkAdornment: + if currentTok(p).symbol == "```": + inc p.idx + break + else: + n.text.add(currentTok(p).symbol) + inc p.idx else: n.text.add(currentTok(p).symbol) inc p.idx - 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) + var lb = newRstNode(rnLiteralBlock) + lb.add(n) + result = newRstNode(rnCodeBlock) + result.add(args) + result.add(PRstNode(nil)) + result.add(lb) proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = - # 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) + result = true + var desc, link = "" var i = p.idx - var parensStack: seq[char] template parse(endToken, dest) = - parensStack.setLen 0 inc i # skip begin token while true: - 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 - 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) + if p.tok[i].kind in {tkEof, tkIndent}: return false + if p.tok[i].symbol == endToken: break + dest.add p.tok[i].symbol inc i inc i # skip end token parse("]", desc) - 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 - label.sons[0].text == "#": - if label.sons.len == 1: - result = (fnAutoNumber, -1) - else: - result = (fnAutoNumberLabel, -1) - elif label.len == 1 and label.sons[0].kind == rnLeaf and - label.sons[0].text == "*": - result = (fnAutoSymbol, -1) - elif label.len == 1 and label.sons[0].kind == rnLeaf: - try: - result = (fnManualNumber, parseInt(label.sons[0].text)) - except ValueError: - result = (fnCitation, -1) - else: - result = (fnCitation, -1) - -proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode = - ## parse footnote/citation label. Precondition: start at `[`. - ## Label text should be valid ref. name symbol, otherwise nil is returned. - var i = p.idx + 1 - result = newRstNode(rnInner) - while true: - if p.tok[i].kind in {tkEof, tkIndent, tkWhite}: - return nil - if p.tok[i].kind == tkPunct: - case p.tok[i].symbol: - of "]": - if i > p.idx + 1 and (not reference or (p.tok[i+1].kind == tkPunct and p.tok[i+1].symbol == "_")): - inc i # skip ] - if reference: inc i # skip _ - break # to succeed, it's a footnote/citation indeed - else: - return nil - of "#": - if i != p.idx + 1: - return nil - of "*": - if i != p.idx + 1 and p.tok[i].kind != tkPunct and p.tok[i+1].symbol != "]": - return nil - else: - if not validRefnamePunct(p.tok[i].symbol): - return nil - result.add newLeaf(p.tok[i].symbol) - inc i + if p.tok[i].symbol != "(": return false + parse(")", link) + let child = newRstNode(rnHyperlink) + child.add desc + child.add link + # only commit if we detected no syntax error: + father.add child 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) + result = true proc parseInline(p: var RstParser, father: PRstNode) = - var n: PRstNode # to be used in `if` condition - let saveIdx = p.idx case currentTok(p).kind of tkPunct: if isInlineMarkupStart(p, "***"): @@ -1743,59 +918,26 @@ proc parseInline(p: var RstParser, father: PRstNode) = var n = newRstNode(rnEmphasis) parseUntil(p, n, "*", true) father.add(n) - elif isInlineMarkupStart(p, "_`"): - var n = newRstNode(rnInlineTarget) + elif roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": inc p.idx - parseUntil(p, n, "`", false) - n.anchor = rstnodeToRefname(n) - addAnchorRst(p, name = linkName(n), target = n, - anchorType=manualInlineAnchor) - father.add(n) - elif isMarkdownCodeBlock(p): father.add(parseMarkdownCodeblock(p)) elif isInlineMarkupStart(p, "``"): var n = newRstNode(rnInlineLiteral) parseUntil(p, n, "``", false) father.add(n) - elif match(p, p.idx, ":w:") and - (var lastIdx = getRefnameIdx(p, p.idx + 1); - p.tok[lastIdx+2].symbol == "`"): - let (roleName, _) = getRefname(p, p.idx+1) - let k = whichRole(p, roleName) - var n = newRstNode(k) - p.idx = lastIdx + 2 - if k == rnInlineCode: - n = n.toInlineCode(language=roleName) - parseUntil(p, n, "`", false) # bug #17260 - if k in {rnUnknownRole, rnCodeFragment}: - n = n.toOtherRole(k, roleName) - father.add(n) elif isInlineMarkupStart(p, "`"): - var n = newRstNode(rnInterpretedText, info=lineInfo(p, p.idx+1)) - parseUntil(p, n, "`", false) # bug #17260 + var n = newRstNode(rnInterpretedText) + parseUntil(p, n, "`", true) n = parsePostfix(p, n) father.add(n) elif isInlineMarkupStart(p, "|"): - var n = newRstNode(rnSubstitutionReferences, info=lineInfo(p, p.idx+1)) + var n = newRstNode(rnSubstitutionReferences) parseUntil(p, n, "|", false) father.add(n) elif roSupportMarkdown in p.s.options and currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and parseMarkdownLink(p, father): discard "parseMarkdownLink already processed it" - elif isInlineMarkupStart(p, "[") and nextTok(p).symbol != "[" and - (n = parseFootnoteName(p, reference=true); n != nil): - var nn = newRstNode(rnFootnoteRef) - nn.info = lineInfo(p, saveIdx+1) - nn.add n - let (fnType, _) = getFootnoteType(n) - case fnType - of fnAutoSymbol: - p.s.lineFootnoteSymRef.add lineInfo(p) - of fnAutoNumber: - p.s.lineFootnoteNumRef.add lineInfo(p) - else: discard - father.add(nn) else: if roSupportSmilies in p.s.options: let n = parseSmiley(p) @@ -1809,9 +951,10 @@ proc parseInline(p: var RstParser, father: PRstNode) = if n != nil: father.add(n) return - parseWordOrRef(p, father) + parseUrl(p, father) of tkAdornment, tkOther, tkWhite: - if isMarkdownCodeBlock(p): + if roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": + inc p.idx father.add(parseMarkdownCodeblock(p)) return if roSupportSmilies in p.s.options: @@ -1824,39 +967,44 @@ proc parseInline(p: var RstParser, father: PRstNode) = else: discard proc getDirective(p: var RstParser): string = - result = "" - if currentTok(p).kind == tkWhite: - let (name, lastIdx) = getRefname(p, p.idx + 1) - let afterIdx = lastIdx + 1 - if name.len > 0: - if p.tok[afterIdx].symbol == "::": - result = name - p.idx = afterIdx + 1 - if currentTok(p).kind == tkWhite: - inc p.idx - elif currentTok(p).kind != tkIndent: - rstMessage(p, mwRstStyle, - "whitespace or newline expected after directive " & name) - result = result.toLowerAscii() - elif p.tok[afterIdx].symbol == ":": - rstMessage(p, mwRstStyle, - "double colon :: may be missing at end of '" & name & "'", - p.tok[afterIdx].line, p.tok[afterIdx].col) - elif p.tok[afterIdx].kind == tkPunct and p.tok[afterIdx].symbol[0] == ':': - rstMessage(p, mwRstStyle, - "too many colons for a directive (should be ::)", - p.tok[afterIdx].line, p.tok[afterIdx].col) - -proc parseComment(p: var RstParser, col: int): PRstNode = - if currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent: - inc p.idx # empty comment + if currentTok(p).kind == tkWhite and nextTok(p).kind == tkWord: + var j = p.idx + inc p.idx + result = currentTok(p).symbol + inc p.idx + while currentTok(p).kind in {tkWord, tkPunct, tkAdornment, tkOther}: + if currentTok(p).symbol == "::": break + result.add(currentTok(p).symbol) + inc p.idx + if currentTok(p).kind == tkWhite: inc p.idx + if currentTok(p).symbol == "::": + inc p.idx + if currentTok(p).kind == tkWhite: inc p.idx + else: + p.idx = j # set back + result = "" # error else: - while currentTok(p).kind != tkEof: - if currentTok(p).kind == tkIndent and currentTok(p).ival > col or - currentTok(p).kind != tkIndent and currentTok(p).col > col: + result = "" + result = result.toLowerAscii() + +proc parseComment(p: var RstParser): PRstNode = + case currentTok(p).kind + of tkIndent, tkEof: + if currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent: + inc p.idx # empty comment + else: + var indent = currentTok(p).ival + while true: + case currentTok(p).kind + of tkEof: + break + of tkIndent: + if currentTok(p).ival < indent: break + else: + discard inc p.idx - else: - break + else: + while currentTok(p).kind notin {tkIndent, tkEof}: inc p.idx result = nil proc parseLine(p: var RstParser, father: PRstNode) = @@ -1872,85 +1020,22 @@ proc parseUntilNewline(p: var RstParser, father: PRstNode) = of tkEof, tkIndent: break proc parseSection(p: var RstParser, result: PRstNode) {.gcsafe.} - -proc tokenAfterNewline(p: RstParser, start: int): int = - result = start - while true: - case p.tok[result].kind - of tkEof: - break - of tkIndent: - inc result - break - else: inc result - -proc tokenAfterNewline(p: RstParser): int {.inline.} = - result = tokenAfterNewline(p, p.idx) - -proc getWrappableIndent(p: RstParser): int = - ## Gets baseline indentation for bodies of field lists and directives. - ## Handles situations like this (with possible de-indent in [case.3]):: - ## - ## :field: definition [case.1] - ## - ## currInd currentTok(p).col - ## | | - ## v v - ## - ## .. Note:: defItem: [case.2] - ## definition - ## - ## ^ - ## | - ## nextIndent - ## - ## .. Note:: - point1 [case.3] - ## - point 2 - ## - ## ^ - ## | - ## nextIndent - if currentTok(p).kind == tkIndent: - result = currentTok(p).ival - else: - var nextIndent = p.tok[tokenAfterNewline(p)-1].ival - if nextIndent <= currInd(p): # parse only this line [case.1] - result = currentTok(p).col - elif nextIndent >= currentTok(p).col: # may be a definition list [case.2] - result = currentTok(p).col - else: - 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. ## ## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody. - result = newRstNode(rnField, info=lineInfo(p)) + result = newRstNode(rnField) var col = currentTok(p).col var fieldname = newRstNode(rnFieldName) parseUntil(p, fieldname, ":", false) var fieldbody = newRstNode(rnFieldBody) - if currentTok(p).kind == tkWhite: inc p.idx - let indent = getWrappableIndent(p) - if indent > col: - pushInd(p, indent) - parseSection(p, fieldbody) - popInd(p) + if currentTok(p).kind != tkIndent: parseLine(p, fieldbody) + if currentTok(p).kind == tkIndent: + var indent = currentTok(p).ival + if indent > col: + pushInd(p, indent) + parseSection(p, fieldbody) + popInd(p) result.add(fieldname) result.add(fieldbody) @@ -1964,7 +1049,7 @@ proc parseFields(p: var RstParser): PRstNode = if currentTok(p).kind == tkIndent and nextTok(p).symbol == ":" or atStart: var col = if atStart: currentTok(p).col else: currentTok(p).ival - result = newRstNodeA(p, rnFieldList) + result = newRstNode(rnFieldList) if not atStart: inc p.idx while true: result.add(parseField(p)) @@ -2005,11 +1090,11 @@ proc getArgument(n: PRstNode): string = proc parseDotDot(p: var RstParser): PRstNode {.gcsafe.} proc parseLiteralBlock(p: var RstParser): PRstNode = - result = newRstNodeA(p, rnLiteralBlock) - var n = newLeaf("") + result = newRstNode(rnLiteralBlock) + var n = newRstNode(rnLeaf, "") if currentTok(p).kind == tkIndent: var indent = currentTok(p).ival - while currentTok(p).kind == tkIndent: inc p.idx # skip blank lines + inc p.idx while true: case currentTok(p).kind of tkEof: @@ -2030,109 +1115,34 @@ proc parseLiteralBlock(p: var RstParser): PRstNode = inc p.idx result.add(n) -proc parseQuotedLiteralBlock(p: var RstParser): PRstNode = - result = newRstNodeA(p, rnLiteralBlock) - var n = newLeaf("") - if currentTok(p).kind == tkIndent: - var indent = currInd(p) - while currentTok(p).kind == tkIndent: inc p.idx # skip blank lines - var quoteSym = currentTok(p).symbol[0] - while true: - case currentTok(p).kind - of tkEof: - break - of tkIndent: - if currentTok(p).ival < indent: - break - elif currentTok(p).ival == indent: - if nextTok(p).kind == tkPunct and nextTok(p).symbol[0] == quoteSym: - n.text.add("\n") - inc p.idx - elif nextTok(p).kind == tkIndent: - break - else: - rstMessage(p, mwRstStyle, "no newline after quoted literal block") - break - else: - rstMessage(p, mwRstStyle, - "unexpected indentation in quoted literal block") - break - else: - n.text.add(currentTok(p).symbol) - inc p.idx - result.add(n) +proc getLevel(map: var LevelMap, lvl: var int, c: char): int = + if map[c] == 0: + inc lvl + map[c] = lvl + result = map[c] -proc parseRstLiteralBlock(p: var RstParser, kind: LiteralBlockKind): PRstNode = - if kind == lbIndentedLiteralBlock: - result = parseLiteralBlock(p) - else: - result = parseQuotedLiteralBlock(p) - -proc getLevel(p: var RstParser, c: char, hasOverline: bool): int = - ## Returns (preliminary) heading level corresponding to `c` and - ## `hasOverline`. If level does not exist, add it first. - for i, hType in p.s.hLevels: - if hType.symbol == c and hType.hasOverline == hasOverline: - p.s.hLevels[i].line = curLine(p) - p.s.hLevels[i].hasPeers = true - return i - p.s.hLevels.add LevelInfo(symbol: c, hasOverline: hasOverline, - line: curLine(p), hasPeers: false) - result = p.s.hLevels.len - 1 - -proc countTitles(s: PRstSharedState, n: PRstNode) = - ## Fill `s.hTitleCnt` - if n == nil: return - for node in n.sons: - if node != nil: - if node.kind notin {rnOverline, rnSubstitutionDef, rnDefaultRole}: - break - if node.kind == rnOverline: - if s.hLevels[s.hTitleCnt].hasPeers: - break - inc s.hTitleCnt - if s.hTitleCnt >= 2: - break +proc tokenAfterNewline(p: RstParser): int = + result = p.idx + while true: + case p.tok[result].kind + of tkEof: + break + of tkIndent: + inc result + break + else: inc result proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool = - ## check that underline/overline length is enough for the heading. - ## 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 - if p.idx > 0: - headlineLen = currentTok(p).col - p.tok[adornmentIdx].col - if headlineLen > 0: - rstMessage(p, mwRstStyle, "indentation of heading text allowed" & - " only for overline titles") + if p.idx < adornmentIdx: # underline for i in p.idx ..< adornmentIdx-1: # adornmentIdx-1 is a linebreak headlineLen += p.tok[i].symbol.len - result = p.tok[adornmentIdx].symbol.len >= headlineLen and headlineLen != 0 - if not result: - failure = "(underline '" & p.tok[adornmentIdx].symbol & "' is too short)" - else: # p.idx == adornmentIdx, at overline. Check overline and underline + else: # overline var i = p.idx + 2 - headlineLen = p.tok[i].col - p.tok[adornmentIdx].col while p.tok[i].kind notin {tkEof, tkIndent}: headlineLen += p.tok[i].symbol.len inc i - result = p.tok[adornmentIdx].symbol.len >= headlineLen and - headlineLen != 0 - if result: - result = result and p.tok[i].kind == tkIndent and - p.tok[i+1].kind == tkAdornment and - p.tok[i+1].symbol == p.tok[adornmentIdx].symbol - if not result: - failure = "(underline '" & p.tok[i+1].symbol & "' does not match " & - "overline '" & p.tok[adornmentIdx].symbol & "')" - else: - failure = "(overline '" & p.tok[adornmentIdx].symbol & "' is too short)" - if not result: - rstMessage(p, meNewSectionExpected, failure) + return p.tok[adornmentIdx].symbol.len >= headlineLen proc isLineBlock(p: RstParser): bool = var j = tokenAfterNewline(p) @@ -2140,33 +1150,6 @@ proc isLineBlock(p: RstParser): bool = p.tok[j].col > currentTok(p).col or p.tok[j].symbol == "\n" -proc isMarkdownBlockQuote(p: RstParser): bool = - result = currentTok(p).symbol[0] == '>' - -proc whichRstLiteralBlock(p: RstParser): LiteralBlockKind = - ## Checks that the following tokens are either Indented Literal Block or - ## Quoted Literal Block (which is not quite the same as Markdown quote block). - ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#quoted-literal-blocks - if currentTok(p).symbol == "::" and nextTok(p).kind == tkIndent: - if currInd(p) > nextTok(p).ival: - result = lbNone - if currInd(p) < nextTok(p).ival: - result = lbIndentedLiteralBlock - elif currInd(p) == nextTok(p).ival: - var i = p.idx + 1 - while p.tok[i].kind == tkIndent: inc i - const validQuotingCharacters = { - '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', - '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', - '_', '`', '{', '|', '}', '~'} - if p.tok[i].kind in {tkPunct, tkAdornment} and - p.tok[i].symbol[0] in validQuotingCharacters: - result = lbQuotedLiteralBlock - else: - result = lbNone - else: - result = lbNone - proc predNL(p: RstParser): bool = result = true if p.idx > 0: @@ -2179,39 +1162,6 @@ 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") @@ -2238,16 +1188,15 @@ 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 isMarkdownCodeBlock(p): + if roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": return rnCodeBlock - elif isRst(p) and currentTok(p).symbol == "::": + elif currentTok(p).symbol == "::": return rnLiteralBlock - elif currentTok(p).symbol == ".." and - nextTok(p).kind in {tkWhite, tkIndent}: + elif currentTok(p).symbol == ".." and predNL(p): return rnDirective case currentTok(p).kind of tkAdornment: - if match(p, p.idx + 1, "iI") and currentTok(p).symbol.len >= 4: + if match(p, p.idx + 1, "ii") and currentTok(p).symbol.len >= 4: result = rnTransition elif match(p, p.idx, "+a+"): result = rnGridTable @@ -2255,70 +1204,62 @@ proc whichSection(p: RstParser): RstNodeKind = elif match(p, p.idx + 1, " a"): result = rnTable elif currentTok(p).symbol == "|" and isLineBlock(p): result = rnLineBlock - elif roSupportMarkdown in p.s.options and isMarkdownBlockQuote(p): - result = rnMarkdownBlockQuote elif match(p, p.idx + 1, "i") and isAdornmentHeadline(p, p.idx): result = rnOverline else: result = rnLeaf of tkPunct: if isMarkdownHeadline(p): - result = rnMarkdownHeadline + result = rnHeadline elif roSupportMarkdown in p.s.options and predNL(p) and match(p, p.idx, "| w") and findPipe(p, p.idx+3): result = rnMarkdownTable elif currentTok(p).symbol == "|" and isLineBlock(p): result = rnLineBlock - elif roSupportMarkdown in p.s.options and isMarkdownBlockQuote(p): - result = rnMarkdownBlockQuote - elif match(p, tokenAfterNewline(p), "aI") and + elif match(p, tokenAfterNewline(p), "ai") and isAdornmentHeadline(p, tokenAfterNewline(p)): result = rnHeadline - elif currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite: + elif predNL(p) and + currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite: result = rnBulletList - elif match(p, p.idx, ":w:E"): + elif match(p, p.idx, ":w:") and predNL(p): # (currentTok(p).symbol == ":") result = rnFieldList elif match(p, p.idx, "(e) ") or match(p, p.idx, "e) ") or match(p, p.idx, "e. "): result = rnEnumList + elif isDefList(p): + result = rnDefList elif isOptionList(p): result = rnOptionList - 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: let tokIdx = tokenAfterNewline(p) - if match(p, tokIdx, "aI"): + if match(p, tokIdx, "ai"): if isAdornmentHeadline(p, tokIdx): result = rnHeadline else: result = rnParagraph elif match(p, p.idx, "e) ") or match(p, p.idx, "e. "): result = rnEnumList - elif isRst(p) and isDefList(p): result = rnDefList - elif isMd(p) and isMdDefListItem(p, p.idx): - result = rnMdDefList + elif isDefList(p): result = rnDefList else: result = rnParagraph else: result = rnLeaf proc parseLineBlock(p: var RstParser): PRstNode = - ## Returns rnLineBlock with all sons of type rnLineBlockItem result = nil if nextTok(p).kind in {tkWhite, tkIndent}: var col = currentTok(p).col - result = newRstNodeA(p, rnLineBlock) + result = newRstNode(rnLineBlock) while true: var item = newRstNode(rnLineBlockItem) if nextTok(p).kind == tkWhite: if nextTok(p).symbol.len > 1: # pass additional indentation after '| ' - item.lineIndent = nextTok(p).symbol + item.text = nextTok(p).symbol inc p.idx, 2 pushInd(p, p.tok[p.idx].col) parseSection(p, item) popInd(p) else: # tkIndent => add an empty line - item.lineIndent = "\n" + item.text = "\n" inc p.idx, 1 result.add(item) if currentTok(p).kind == tkIndent and currentTok(p).ival == col and @@ -2328,131 +1269,30 @@ proc parseLineBlock(p: var RstParser): PRstNode = else: break -proc parseDoc(p: var RstParser): PRstNode {.gcsafe.} - -proc getQuoteSymbol(p: RstParser, idx: int): tuple[sym: string, depth: int, tokens: int] = - result = ("", 0, 0) - var i = idx - result.sym &= p.tok[i].symbol - result.depth += p.tok[i].symbol.len - inc result.tokens - inc i - while p.tok[i].kind == tkWhite and i+1 < p.tok.len and - p.tok[i+1].kind == tkPunct and p.tok[i+1].symbol[0] == '>': - result.sym &= p.tok[i].symbol - result.sym &= p.tok[i+1].symbol - result.depth += p.tok[i+1].symbol.len - inc result.tokens, 2 - inc i, 2 - -proc parseMarkdownQuoteSegment(p: var RstParser, curSym: string, col: int): - PRstNode = - ## We define *segment* as a group of lines that starts with exactly the - ## same quote symbol. If the following lines don't contain any `>` (*lazy* - ## continuation) they considered as continuation of the current segment. - var q: RstParser # to delete `>` at a start of line and then parse normally - initParser(q, p.s) - q.col = p.col - q.line = p.line - var minCol = int.high # minimum colum num in the segment - while true: # move tokens of segment from `p` to `q` skipping `curSym` - case currentTok(p).kind - of tkEof: - break - of tkIndent: - if nextTok(p).kind in {tkIndent, tkEof}: - break - else: - if nextTok(p).symbol[0] == '>': - var (quoteSym, _, quoteTokens) = getQuoteSymbol(p, p.idx + 1) - if quoteSym == curSym: # the segment continues - var iTok = tokenAfterNewline(p, p.idx+1) - if p.tok[iTok].kind notin {tkEof, tkIndent} and - p.tok[iTok].symbol[0] != '>': - rstMessage(p, mwRstStyle, - "two or more quoted lines are followed by unquoted line " & - $(curLine(p) + 1)) - break - q.tok.add currentTok(p) - var ival = currentTok(p).ival + quoteSym.len - inc p.idx, (1 + quoteTokens) # skip newline and > > > - if currentTok(p).kind == tkWhite: - ival += currentTok(p).symbol.len - inc p.idx - # fix up previous `tkIndent`s to ival (as if >>> were not there) - var j = q.tok.len - 1 - while j >= 0 and q.tok[j].kind == tkIndent: - q.tok[j].ival = ival - dec j - else: # next segment started - break - elif currentTok(p).ival < col: - break - else: # the segment continues, a case like: - # > beginning - # continuation - q.tok.add currentTok(p) - inc p.idx - else: - if currentTok(p).col < minCol: minCol = currentTok(p).col - q.tok.add currentTok(p) - inc p.idx - q.indentStack = @[minCol] - # if initial indentation `minCol` is > 0 then final newlines - # should be omitted so that parseDoc could advance to the end of tokens: - var j = q.tok.len - 1 - while q.tok[j].kind == tkIndent: dec j - q.tok.setLen (j+1) - q.tok.add Token(kind: tkEof, line: currentTok(p).line) - result = parseDoc(q) - -proc parseMarkdownBlockQuote(p: var RstParser): PRstNode = - var (curSym, quotationDepth, quoteTokens) = getQuoteSymbol(p, p.idx) - let col = currentTok(p).col - result = newRstNodeA(p, rnMarkdownBlockQuote) - inc p.idx, quoteTokens # skip first > - while true: - var item = newRstNode(rnMarkdownBlockQuoteItem) - item.quotationDepth = quotationDepth - if currentTok(p).kind == tkWhite: inc p.idx - item.add parseMarkdownQuoteSegment(p, curSym, col) - result.add(item) - if currentTok(p).kind == tkIndent and currentTok(p).ival == col and - nextTok(p).kind != tkEof and nextTok(p).symbol[0] == '>': - (curSym, quotationDepth, quoteTokens) = getQuoteSymbol(p, p.idx + 1) - inc p.idx, (1 + quoteTokens) # skip newline and > > > - else: - break - proc parseParagraph(p: var RstParser, result: PRstNode) = while true: case currentTok(p).kind of tkIndent: if nextTok(p).kind == tkIndent: inc p.idx - break # blank line breaks paragraph for both Md & Rst - elif currentTok(p).ival == currInd(p) or ( - isMd(p) and currentTok(p).ival > currInd(p)): - # (Md allows adding additional indentation inside paragraphs) + break + elif currentTok(p).ival == currInd(p): inc p.idx case whichSection(p) - of rnParagraph, rnLeaf, rnHeadline, rnMarkdownHeadline, - rnOverline, rnDirective: - result.add newLeaf(" ") + of rnParagraph, rnLeaf, rnHeadline, rnOverline, rnDirective: + result.add(newRstNode(rnLeaf, " ")) of rnLineBlock: result.addIfNotNil(parseLineBlock(p)) - of rnMarkdownBlockQuote: - result.addIfNotNil(parseMarkdownBlockQuote(p)) else: break else: break of tkPunct: - if isRst(p) and ( - let literalBlockKind = whichRstLiteralBlock(p); - literalBlockKind != lbNone): - result.add newLeaf(":") + if currentTok(p).symbol == "::" and + nextTok(p).kind == tkIndent and + currInd(p) < nextTok(p).ival: + result.add(newRstNode(rnLeaf, ":")) inc p.idx # skip '::' - result.add(parseRstLiteralBlock(p, literalBlockKind)) + result.add(parseLiteralBlock(p)) break else: parseInline(p, result) @@ -2460,262 +1300,99 @@ proc parseParagraph(p: var RstParser, result: PRstNode) = parseInline(p, result) else: break -proc checkHeadingHierarchy(p: RstParser, lvl: int) = - if lvl - p.s.hCurLevel > 1: # broken hierarchy! - proc descr(l: int): string = - (if p.s.hLevels[l].hasOverline: "overline " else: "underline ") & - repeat(p.s.hLevels[l].symbol, 5) - var msg = "(section level inconsistent: " - msg.add descr(lvl) & " unexpectedly found, " & - "while the following intermediate section level(s) are missing on lines " - msg.add $p.s.hLevels[p.s.hCurLevel].line & ".." & $curLine(p) & ":" - for l in p.s.hCurLevel+1 .. lvl-1: - msg.add " " & descr(l) - if l != lvl-1: msg.add "," - rstMessage(p, meNewSectionExpected, msg & ")") - proc parseHeadline(p: var RstParser): PRstNode = + result = newRstNode(rnHeadline) if isMarkdownHeadline(p): - result = newRstNode(rnMarkdownHeadline) - # Note that level hierarchy is not checked for markdown headings result.level = currentTok(p).symbol.len assert(nextTok(p).kind == tkWhite) inc p.idx, 2 parseUntilNewline(p, result) else: - result = newRstNode(rnHeadline) parseUntilNewline(p, result) assert(currentTok(p).kind == tkIndent) assert(nextTok(p).kind == tkAdornment) var c = nextTok(p).symbol[0] inc p.idx, 2 - result.level = getLevel(p, c, hasOverline=false) - checkHeadingHierarchy(p, result.level) - p.s.hCurLevel = result.level - addAnchorRst(p, linkName(result), result, anchorType=headlineAnchor) - p.s.tocPart.add result - -proc parseOverline(p: var RstParser): PRstNode = - var c = currentTok(p).symbol[0] - inc p.idx, 2 - result = newRstNode(rnOverline) - while true: - parseUntilNewline(p, result) - if currentTok(p).kind == tkIndent: - inc p.idx - if prevTok(p).ival > currInd(p): - result.add newLeaf(" ") - else: - break - else: - break - result.level = getLevel(p, c, hasOverline=true) - checkHeadingHierarchy(p, result.level) - p.s.hCurLevel = result.level - if currentTok(p).kind == tkAdornment: - inc p.idx - if currentTok(p).kind == tkIndent: inc p.idx - 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 + result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c) type - ColSpec = object - start, stop: int - RstCols = seq[ColSpec] - ColumnLimits = tuple # for Markdown + IntSeq = seq[int] + ColumnLimits = tuple 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 = tokEnd(p, p.idx) + result = currentTok(p).col + currentTok(p).symbol.len - 1 -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. +proc getColumns(p: var RstParser, cols: var IntSeq) = var L = 0 - result = startIdx while true: inc L setLen(cols, L) - 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 + 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 -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 parseDoc(p: var RstParser): PRstNode {.gcsafe.} proc parseSimpleTable(p: var RstParser): PRstNode = - var cols: RstCols - result = newRstNodeA(p, rnTable) - let startIdx = getColumns(p, cols, p.idx) - let colChar = currentTok(p).symbol[0] - checkColumns(p, cols) - p.idx = startIdx - result.colCount = cols.len + var + cols: IntSeq + row: seq[string] + i, last, line: int + c: char + q: RstParser + a, b: PRstNode + result = newRstNode(rnTable) + cols = @[] + row = @[] + a = nil + c = currentTok(p).symbol[0] while true: if currentTok(p).kind == tkAdornment: - checkColumns(p, cols) - p.idx = tokenAfterNewline(p) - if currentTok(p).kind in {tkEof, tkIndent}: + last = tokenAfterNewline(p) + if p.tok[last].kind in {tkEof, tkIndent}: # skip last adornment line: + p.idx = last break - 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) + getColumns(p, cols) + setLen(row, cols.len) + if a != nil: + for j in 0 ..< a.len: a.sons[j].kind = rnTableHeaderCell if currentTok(p).kind == tkEof: break - let tabRow = parseSimpleTableRow(p, cols, colChar) - result.add tabRow + 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('\x0A') + a = newRstNode(rnTableRow) + for j in countup(0, high(row)): + initParser(q, p.s) + q.col = cols[j] + q.line = line - 1 + q.filename = p.filename + q.col += getTokens(row[j], false, q.tok) + b = newRstNode(rnTableDataCell) + b.add(parseDoc(q)) + a.add(b) + result.add(a) proc readTableRow(p: var RstParser): ColSeq = if currentTok(p).symbol == "|": inc p.idx @@ -2748,43 +1425,63 @@ 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) + result = newRstNode(rnMarkdownTable) proc parseRow(p: var RstParser, cellKind: RstNodeKind, result: PRstNode) = row = readTableRow(p) - if result.colCount == 0: result.colCount = row.len # table header - elif row.len < result.colCount: row.setLen(result.colCount) + if colNum == 0: colNum = row.len # table header + elif row.len < colNum: row.setLen(colNum) a = newRstNode(rnTableRow) - for j in 0 ..< result.colCount: + for j in 0 ..< colNum: b = newRstNode(cellKind) initParser(q, p.s) q.col = p.col q.line = currentTok(p).line - 1 - getTokens(getColContents(p, row[j]), q.tok) + q.filename = p.filename + q.col += getTokens(getColContents(p, row[j]), false, q.tok) b.add(parseDoc(q)) a.add(b) result.add(a) parseRow(p, rnTableHeaderCell, result) - if not isValidDelimiterRow(p, result.colCount): - rstMessage(p, meMarkdownIllformedTable) + if not isValidDelimiterRow(p, colNum): rstMessage(p, meMarkdownIllformedTable) while predNL(p) and currentTok(p).symbol == "|": parseRow(p, rnTableDataCell, result) proc parseTransition(p: var RstParser): PRstNode = - result = newRstNodeA(p, rnTransition) + result = newRstNode(rnTransition) inc p.idx if currentTok(p).kind == tkIndent: inc p.idx if currentTok(p).kind == tkIndent: inc p.idx +proc parseOverline(p: var RstParser): PRstNode = + var c = currentTok(p).symbol[0] + inc p.idx, 2 + result = newRstNode(rnOverline) + while true: + parseUntilNewline(p, result) + if currentTok(p).kind == tkIndent: + inc p.idx + if prevTok(p).ival > currInd(p): + result.add(newRstNode(rnLeaf, " ")) + else: + break + else: + break + result.level = getLevel(p.s.overlineToLevel, p.s.oLevel, c) + if currentTok(p).kind == tkAdornment: + inc p.idx # XXX: check? + if currentTok(p).kind == tkIndent: inc p.idx + proc parseBulletList(p: var RstParser): PRstNode = result = nil if nextTok(p).kind == tkWhite: var bullet = currentTok(p).symbol var col = currentTok(p).col - result = newRstNodeA(p, rnBulletList) + result = newRstNode(rnBulletList) pushInd(p, p.tok[p.idx + 2].col) inc p.idx, 2 while true: @@ -2800,11 +1497,9 @@ proc parseBulletList(p: var RstParser): PRstNode = popInd(p) proc parseOptionList(p: var RstParser): PRstNode = - result = newRstNodeA(p, rnOptionList) - let col = currentTok(p).col - var order = 1 + result = newRstNode(rnOptionList) while true: - if currentTok(p).col == col and isOptionList(p): + if isOptionList(p): var a = newRstNode(rnOptionGroup) var b = newRstNode(rnDescription) var c = newRstNode(rnOptionListItem) @@ -2822,42 +1517,10 @@ proc parseOptionList(p: var RstParser): PRstNode = popInd(p) else: parseLine(p, b) - while currentTok(p).kind == tkIndent: inc p.idx + if currentTok(p).kind == tkIndent: inc p.idx c.add(a) c.add(b) - c.order = order; inc order result.add(c) - else: - 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 @@ -2867,10 +1530,8 @@ proc parseDefinitionList(p: var RstParser): PRstNode = if j >= 1 and p.tok[j].kind == tkIndent and p.tok[j].ival > currInd(p) and p.tok[j - 1].symbol != "::": var col = currentTok(p).col - result = newRstNodeA(p, rnDefList) + result = newRstNode(rnDefList) while true: - if isOptionList(p): - break # option list has priority over def.list j = p.idx var a = newRstNode(rnDefName) parseLine(p, a) @@ -2907,49 +1568,22 @@ proc parseEnumList(p: var RstParser): PRstNode = wildToken: array[0..5, int] = [4, 3, 3, 4, 3, 3] # number of tokens wildIndex: array[0..5, int] = [1, 0, 0, 1, 0, 0] # position of enumeration sequence (number/letter) in enumerator + result = newRstNode(rnEnumList) let col = currentTok(p).col var w = 0 while w < wildcards.len: if match(p, p.idx, wildcards[w]): break inc w assert w < wildcards.len - - proc checkAfterNewline(p: RstParser, report: bool): bool = - ## If no indentation on the next line then parse as a normal paragraph - ## according to the RST spec. And report a warning with suggestions - let j = tokenAfterNewline(p, start=p.idx+1) - let requiredIndent = p.tok[p.idx+wildToken[w]].col - if p.tok[j].kind notin {tkIndent, tkEof} and - p.tok[j].col < requiredIndent and - (p.tok[j].col > col or - (p.tok[j].col == col and not match(p, j, wildcards[w]))): - if report: - let n = p.line + p.tok[j].line - let msg = "\n" & """ - not enough indentation on line $2 - (should be at column $3 if it's a continuation of enum. list), - or no blank line after line $1 (if it should be the next paragraph), - or no escaping \ at the beginning of line $1 - (if lines $1..$2 are a normal paragraph, not enum. list)""".dedent - let c = p.col + requiredIndent + ColRstOffset - rstMessage(p, mwRstStyle, msg % [$(n-1), $n, $c], - p.tok[j].line, p.tok[j].col) - result = false - else: - result = true - - if not checkAfterNewline(p, report = true): - return nil - result = newRstNodeA(p, rnEnumList) let autoEnums = if roSupportMarkdown in p.s.options: @["#", "1"] else: @["#"] var prevAE = "" # so as not allow mixing auto-enumerators `1` and `#` var curEnum = 1 for i in 0 ..< wildToken[w]-1: # add first enumerator with (, ), and . if p.tok[p.idx + i].symbol == "#": prevAE = "#" - result.labelFmt.add "1" + result.text.add "1" else: - result.labelFmt.add p.tok[p.idx + i].symbol + result.text.add p.tok[p.idx + i].symbol var prevEnum = p.tok[p.idx + wildIndex[w]].symbol inc p.idx, wildToken[w] while true: @@ -2960,20 +1594,16 @@ proc parseEnumList(p: var RstParser): PRstNode = result.add(item) if currentTok(p).kind == tkIndent and currentTok(p).ival == col and match(p, p.idx+1, wildcards[w]): - # don't report to avoid duplication of warning since for - # subsequent enum. items parseEnumList will be called second time: - if not checkAfterNewline(p, report = false): - break let enumerator = p.tok[p.idx + 1 + wildIndex[w]].symbol # check that it's in sequence: enumerator == next(prevEnum) if "n" in wildcards[w]: # arabic numeral - let prevEnumI = try: parseInt(prevEnum) except ValueError: 1 + let prevEnumI = try: parseInt(prevEnum) except: 1 if enumerator in autoEnums: if prevAE != "" and enumerator != prevAE: break prevAE = enumerator curEnum = prevEnumI + 1 - else: curEnum = (try: parseInt(enumerator) except ValueError: 1) + else: curEnum = (try: parseInt(enumerator) except: 1) if curEnum - prevEnumI != 1: break prevEnum = enumerator @@ -2993,7 +1623,6 @@ proc sonKind(father: PRstNode, i: int): RstNodeKind = if i < father.len: result = father.sons[i].kind proc parseSection(p: var RstParser, result: PRstNode) = - ## parse top-level RST elements: sections, transitions and body elements. while true: var leave = false assert(p.idx >= 0) @@ -3001,19 +1630,11 @@ proc parseSection(p: var RstParser, result: PRstNode) = if currInd(p) == currentTok(p).ival: inc p.idx elif currentTok(p).ival > currInd(p): - if roPreferMarkdown in p.s.options: # Markdown => normal paragraphs - if currentTok(p).ival - currInd(p) >= 4: - result.add parseLiteralBlock(p) - else: - 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) + pushInd(p, currentTok(p).ival) + var a = newRstNode(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 @@ -3028,18 +1649,16 @@ proc parseSection(p: var RstParser, result: PRstNode) = a = parseLiteralBlock(p) of rnBulletList: a = parseBulletList(p) of rnLineBlock: a = parseLineBlock(p) - of rnMarkdownBlockQuote: a = parseMarkdownBlockQuote(p) of rnDirective: a = parseDotDot(p) of rnEnumList: a = parseEnumList(p) - of rnLeaf: rstMessage(p, meNewSectionExpected, "(syntax error)") + of rnLeaf: rstMessage(p, meNewSectionExpected) 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) of rnTransition: a = parseTransition(p) - of rnHeadline, rnMarkdownHeadline: a = parseHeadline(p) + of rnHeadline: a = parseHeadline(p) of rnOverline: a = parseOverline(p) of rnTable: a = parseSimpleTable(p) of rnMarkdownTable: a = parseMarkdownTable(p) @@ -3048,12 +1667,11 @@ proc parseSection(p: var RstParser, result: PRstNode) = #InternalError("rst.parseSection()") discard if a == nil and k != rnDirective: - a = newRstNodeA(p, rnParagraph) + a = newRstNode(rnParagraph) parseParagraph(p, a) result.addIfNotNil(a) if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph: - result.sons[0] = newRstNode(rnInner, result.sons[0].sons, - anchor=result.sons[0].anchor) + result.sons[0].kind = rnInner proc parseSectionWrapper(p: var RstParser): PRstNode = result = newRstNode(rnInner) @@ -3061,6 +1679,9 @@ 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: @@ -3070,9 +1691,9 @@ type DirFlag = enum hasArg, hasOptions, argIsFile, argIsWord DirFlags = set[DirFlag] - SectionParser = proc (p: var RstParser): PRstNode {.nimcall, gcsafe.} + SectionParser = proc (p: var RstParser): PRstNode {.nimcall.} -proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode = +proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode = ## Parses arguments and options for a directive block. ## ## A directive block will always have three sons: the arguments for the @@ -3082,8 +1703,7 @@ proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode ## ## Both rnDirArg and rnFieldList children nodes might be nil, so you need to ## check them before accessing. - result = newRstNodeA(p, k) - if k == rnCodeBlock: result.info = lineInfo(p) + result = newRstNode(rnDirective) var args: PRstNode = nil var options: PRstNode = nil if hasArg in flags: @@ -3106,29 +1726,15 @@ proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode parseLine(p, args) result.add(args) if hasOptions in flags: - if currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p) and + if currentTok(p).kind == tkIndent and currentTok(p).ival >= 3 and nextTok(p).symbol == ":": - pushInd(p, currentTok(p).ival) options = parseFields(p) - popInd(p) result.add(options) proc indFollows(p: RstParser): bool = result = currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p) -proc parseBlockContent(p: var RstParser, father: var PRstNode, - contentParser: SectionParser): bool {.gcsafe.} = - ## parse the final content part of explicit markup blocks (directives, - ## footnotes, etc). Returns true if succeeded. - if currentTok(p).kind != tkIndent or indFollows(p): - let blockIndent = getWrappableIndent(p) - pushInd(p, blockIndent) - let content = contentParser(p) - popInd(p) - father.add content - result = true - -proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags, +proc parseDirective(p: var RstParser, flags: DirFlags, contentParser: SectionParser): PRstNode = ## A helper proc that does main work for specific directive procs. ## Always returns a generic rnDirective tree with these 3 children: @@ -3138,10 +1744,16 @@ proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags, ## 3) a node returned by `contentParser`. ## ## .. warning:: Any of the 3 children may be nil. - result = parseDirective(p, k, flags) + result = parseDirective(p, flags) if not isNil(contentParser) and - parseBlockContent(p, result, contentParser): - discard "result is updated by parseBlockContent" + (currentTok(p).kind != tkIndent or indFollows(p)): + var nextIndent = p.tok[tokenAfterNewline(p)-1].ival + if nextIndent <= currInd(p): # parse only this line + nextIndent = currentTok(p).col + pushInd(p, nextIndent) + var content = contentParser(p) + popInd(p) + result.add(content) else: result.add(PRstNode(nil)) @@ -3174,7 +1786,7 @@ proc dirInclude(p: var RstParser): PRstNode = # encoding (if specified). # result = nil - var n = parseDirective(p, rnDirective, {hasArg, argIsFile, hasOptions}, nil) + var n = parseDirective(p, {hasArg, argIsFile, hasOptions}, nil) var filename = strip(addNodes(n.sons[0])) var path = p.findRelativeFile(filename) if path == "": @@ -3183,9 +1795,9 @@ proc dirInclude(p: var RstParser): PRstNode = # XXX: error handling; recursive file inclusion! if getFieldValue(n, "literal") != "": result = newRstNode(rnLiteralBlock) - result.add newLeaf(readFile(path)) + result.add(newRstNode(rnLeaf, readFile(path))) else: - let inputString = readFile(path) + let inputString = readFile(path).string() let startPosition = block: let searchFor = n.getFieldValue("start-after").strip() @@ -3208,16 +1820,15 @@ proc dirInclude(p: var RstParser): PRstNode = var q: RstParser initParser(q, p.s) - let saveFileIdx = p.s.currFileIdx - setCurrFilename(p.s, path) - getTokens( - inputString[startPosition..endPosition], + q.filename = path + q.col += getTokens( + inputString[startPosition..endPosition].strip(), + false, q.tok) # workaround a GCC bug; more like the interior pointer bug? #if find(q.tok[high(q.tok)].symbol, "\0\x01\x02") > 0: # InternalError("Too many binary zeros in include file") result = parseDoc(q) - p.s.currFileIdx = saveFileIdx proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode = ## Parses a code block. @@ -3235,54 +1846,62 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode = ## ## As an extension this proc will process the ``file`` extension field and if ## present will replace the code block with the contents of the referenced - ## file. This behaviour is disabled in sandboxed mode and can be re-enabled - ## with the `roSandboxDisabled` flag. - result = parseDirective(p, rnCodeBlock, {hasArg, hasOptions}, parseLiteralBlock) - mayLoadFile(p, result) + ## file. + result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock) + var filename = strip(getFieldValue(result, "file")) + if filename != "": + var path = p.findRelativeFile(filename) + if path == "": rstMessage(p, meCannotOpenFile, filename) + var n = newRstNode(rnLiteralBlock) + n.add(newRstNode(rnLeaf, readFile(path))) + result.sons[2] = n # Extend the field block if we are using our custom Nim extension. if nimExtension: - defaultCodeLangNim(p, result) + # 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) + extraNode.add(newRstNode(rnFieldName)) + extraNode.add(newRstNode(rnFieldBody)) + extraNode.sons[0].add(newRstNode(rnLeaf, "default-language")) + extraNode.sons[1].add(newRstNode(rnLeaf, "Nim")) + result.sons[1].add(extraNode) + + result.kind = rnCodeBlock proc dirContainer(p: var RstParser): PRstNode = - result = parseDirective(p, rnContainer, {hasArg}, parseSectionWrapper) + result = parseDirective(p, {hasArg}, parseSectionWrapper) + assert(result.kind == rnDirective) assert(result.len == 3) + result.kind = rnContainer proc dirImage(p: var RstParser): PRstNode = - result = parseDirective(p, rnImage, {hasOptions, hasArg, argIsFile}, nil) + result = parseDirective(p, {hasOptions, hasArg, argIsFile}, nil) + result.kind = rnImage proc dirFigure(p: var RstParser): PRstNode = - result = parseDirective(p, rnFigure, {hasOptions, hasArg, argIsFile}, + result = parseDirective(p, {hasOptions, hasArg, argIsFile}, parseSectionWrapper) + result.kind = rnFigure proc dirTitle(p: var RstParser): PRstNode = - result = parseDirective(p, rnTitle, {hasArg}, nil) + result = parseDirective(p, {hasArg}, nil) + result.kind = rnTitle proc dirContents(p: var RstParser): PRstNode = - result = parseDirective(p, rnContents, {hasArg}, nil) - p.s.hasToc = true + result = parseDirective(p, {hasArg}, nil) + result.kind = rnContents proc dirIndex(p: var RstParser): PRstNode = - result = parseDirective(p, rnIndex, {}, parseSectionWrapper) + result = parseDirective(p, {}, parseSectionWrapper) + result.kind = rnIndex proc dirAdmonition(p: var RstParser, d: string): PRstNode = - result = parseDirective(p, rnAdmonition, {}, parseSectionWrapper) - result.adType = d - -proc dirDefaultRole(p: var RstParser): PRstNode = - result = parseDirective(p, rnDefaultRole, {hasArg}, nil) - if result.sons[0].len == 0: p.s.currRole = defaultRole(p.s.options) - else: - assert result.sons[0].sons[0].kind == rnLeaf - p.s.currRole = result.sons[0].sons[0].text - p.s.currRoleKind = whichRole(p, p.s.currRole) - -proc dirRole(p: var RstParser): PRstNode = - result = parseDirective(p, rnDirective, {hasArg, hasOptions}, nil) - # just check that language is supported, TODO: real role association - let lang = getFieldValue(result, "language").strip - if lang != "" and getSourceLanguage(lang) == langNone: - rstMessage(p, mwUnsupportedLanguage, lang) + result = parseDirective(p, {}, parseSectionWrapper) + result.kind = rnAdmonition + result.text = d proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind, contentParser: SectionParser) = @@ -3294,9 +1913,9 @@ proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind, else: var f = readFile(path) result = newRstNode(kind) - result.add newLeaf(f) + result.add(newRstNode(rnLeaf, f)) else: - result = newRstNode(kind, result.sons) + result.kind = kind result.add(parseDirBody(p, contentParser)) proc dirRaw(p: var RstParser): PRstNode = @@ -3308,7 +1927,7 @@ proc dirRaw(p: var RstParser): PRstNode = # # html # latex - result = parseDirective(p, rnDirective, {hasOptions, hasArg, argIsWord}) + result = parseDirective(p, {hasOptions, hasArg, argIsWord}) if result.sons[0] != nil: if cmpIgnoreCase(result.sons[0].sons[0].text, "html") == 0: dirRawAux(p, result, rnRawHtml, parseLiteralBlock) @@ -3319,36 +1938,19 @@ proc dirRaw(p: var RstParser): PRstNode = else: dirRawAux(p, result, rnRaw, parseSectionWrapper) -proc dirImportdoc(p: var RstParser): PRstNode = - result = parseDirective(p, rnDirective, {}, parseLiteralBlock) - assert result.sons[2].kind == rnLiteralBlock - assert result.sons[2].sons[0].kind == rnLeaf - let filenames: seq[string] = split(result.sons[2].sons[0].text, seps = {','}) - proc rmSpaces(s: string): string = s.split.join("") - for origFilename in filenames: - p.s.idxImports[origFilename.rmSpaces] = ImportdocInfo(fromInfo: lineInfo(p)) - proc selectDir(p: var RstParser, d: string): PRstNode = result = nil - let tok = p.tok[p.idx-2] # report on directive in ".. directive::" - if roSandboxDisabled notin p.s.options: - if d notin SandboxDirAllowlist: - rstMessage(p, meSandboxedDirective, d, tok.line, tok.col) - case d of "admonition", "attention", "caution": result = dirAdmonition(p, d) of "code": result = dirCodeBlock(p) of "code-block": result = dirCodeBlock(p, nimExtension = true) of "container": result = dirContainer(p) of "contents": result = dirContents(p) - of "danger": result = dirAdmonition(p, d) - of "default-role": result = dirDefaultRole(p) - of "error": result = dirAdmonition(p, d) + of "danger", "error": result = dirAdmonition(p, d) of "figure": result = dirFigure(p) of "hint": result = dirAdmonition(p, d) of "image": result = dirImage(p) of "important": result = dirAdmonition(p, d) - of "importdoc": result = dirImportdoc(p) of "include": result = dirInclude(p) of "index": result = dirIndex(p) of "note": result = dirAdmonition(p, d) @@ -3357,65 +1959,15 @@ proc selectDir(p: var RstParser, d: string): PRstNode = result = dirRaw(p) else: rstMessage(p, meInvalidDirective, d) - of "role": result = dirRole(p) of "tip": result = dirAdmonition(p, d) of "title": result = dirTitle(p) of "warning": result = dirAdmonition(p, d) else: - rstMessage(p, meInvalidDirective, d, tok.line, tok.col) - -proc prefix(ftnType: FootnoteType): string = - case ftnType - of fnManualNumber: result = "footnote-" - of fnAutoNumber: result = "footnoteauto-" - of fnAutoNumberLabel: result = "footnote-" - of fnAutoSymbol: result = "footnotesym-" - of fnCitation: result = "citation-" - -proc parseFootnote(p: var RstParser): PRstNode {.gcsafe.} = - ## Parses footnotes and citations, always returns 2 sons: - ## - ## 1) footnote label, always containing rnInner with 1 or more sons - ## 2) footnote body, which may be nil - inc p.idx - let label = parseFootnoteName(p, reference=false) - if label == nil: - dec p.idx - return nil - result = newRstNode(rnFootnote) - result.add label - let (fnType, i) = getFootnoteType(label) - var name = "" - var anchor = fnType.prefix - case fnType - of fnManualNumber: - addFootnoteNumManual(p, i) - anchor.add $i - of fnAutoNumber, fnAutoNumberLabel: - name = rstnodeToRefname(label) - addFootnoteNumAuto(p, name) - if fnType == fnAutoNumberLabel: - anchor.add name - else: # fnAutoNumber - result.order = p.s.lineFootnoteNum.len - anchor.add $result.order - of fnAutoSymbol: - addFootnoteSymAuto(p) - result.order = p.s.lineFootnoteSym.len - anchor.add $p.s.lineFootnoteSym.len - of fnCitation: - anchor.add rstnodeToRefname(label) - addAnchorRst(p, anchor, target = result, anchorType = footnoteAnchor) - result.anchor = anchor - if currentTok(p).kind == tkWhite: inc p.idx - discard parseBlockContent(p, result, parseSectionWrapper) - if result.len < 2: - result.add nil + rstMessage(p, meInvalidDirective, d) proc parseDotDot(p: var RstParser): PRstNode = # parse "explicit markup blocks" result = nil - var n: PRstNode # to store result, workaround for bug 16855 var col = currentTok(p).col inc p.idx var d = getDirective(p) @@ -3426,24 +1978,10 @@ proc parseDotDot(p: var RstParser): PRstNode = elif match(p, p.idx, " _"): # hyperlink target: inc p.idx, 2 - var ending = ":" - if currentTok(p).symbol == "`": - inc p.idx - ending = "`" - var a = getReferenceName(p, ending) - if ending == "`": - if currentTok(p).symbol == ":": - inc p.idx - else: - rstMessage(p, meExpected, ":") + var a = getReferenceName(p, ":") if currentTok(p).kind == tkWhite: inc p.idx var b = untilEol(p) - if len(b) == 0: # set internal anchor - p.curAnchors.add ManualAnchor( - alias: linkName(a), anchor: rstnodeToRefname(a), info: prevLineInfo(p) - ) - else: # external hyperlink - setRef(p, rstnodeToRefname(a), b, refType=hyperlinkAlias) + setRef(p, rstnodeToRefname(a), b) elif match(p, p.idx, " |"): # substitution definitions: inc p.idx, 2 @@ -3460,337 +1998,52 @@ proc parseDotDot(p: var RstParser): PRstNode = else: rstMessage(p, meInvalidDirective, currentTok(p).symbol) setSub(p, addNodes(a), b) - elif match(p, p.idx, " [") and - (n = parseFootnote(p); n != nil): - result = n + elif match(p, p.idx, " ["): + # footnotes, citations + inc p.idx, 2 + var a = getReferenceName(p, "]") + if currentTok(p).kind == tkWhite: inc p.idx + var b = untilEol(p) + setRef(p, rstnodeToRefname(a), b) else: - result = parseComment(p, col) + result = parseComment(p) -proc rstParsePass1*(fragment: string, - line, column: int, - sharedState: PRstSharedState): PRstNode = - ## Parses an RST `fragment`. - ## The result should be further processed by - ## preparePass2_ and resolveSubs_ (which is pass 2). - var p: RstParser - initParser(p, sharedState) - p.line = line - p.col = column - getTokens(fragment, p.tok) - result = parseDoc(p) - -proc extractLinkEnd(x: string): string = - ## From links like `path/to/file.html#/%` extract `file.html#/%`. - let i = find(x, '#') - let last = - if i >= 0: i - else: x.len - 1 - let j = rfind(x, '/', start=0, last=last) - if j >= 0: - result = x[j+1 .. ^1] - else: - result = x - -proc loadIdxFile(s: var PRstSharedState, origFilename: string) = - doAssert roSandboxDisabled in s.options - var info: TLineInfo - info.fileIndex = addFilename(s, origFilename) - var (dir, basename, ext) = origFilename.splitFile - if ext notin [".md", ".rst", ".nim", ""]: - rstMessage(s.filenames, s.msgHandler, s.idxImports[origFilename].fromInfo, - meCannotOpenFile, origFilename & ": unknown extension") - let idxFilename = dir / basename & ".idx" - let (idxPath, linkRelPath) = s.findRefFile(idxFilename) - s.idxImports[origFilename].linkRelPath = linkRelPath - var - fileEntries: seq[IndexEntry] - title: IndexEntry - try: - (fileEntries, title) = parseIdxFile(idxPath) - except IOError: - rstMessage(s.filenames, s.msgHandler, s.idxImports[origFilename].fromInfo, - meCannotOpenFile, idxPath) - except ValueError as e: - s.msgHandler(idxPath, LineRstInit, ColRstInit, meInvalidField, e.msg) - - var isMarkup = false # for sanity check to avoid mixing .md <-> .nim - for entry in fileEntries: - # Though target .idx already has inside it the path to HTML relative - # project's root, we won't rely on it and use `linkRelPath` instead. - let refn = extractLinkEnd(entry.link) - # select either markup (rst/md) or Nim cases: - if entry.kind in {ieMarkupTitle, ieNimTitle}: - s.idxImports[origFilename].title = entry.keyword - case entry.kind - of ieIdxRole, ieHeading, ieMarkupTitle: - if ext == ".nim" and entry.kind == ieMarkupTitle: - rstMessage(s, idxPath, meInvalidField, - $ieMarkupTitle & " in supposedly .nim-derived file") - if entry.kind == ieMarkupTitle: - isMarkup = true - info.line = entry.line.uint16 - addAnchorExtRst(s, key = entry.keyword, refn = refn, - anchorType = headlineAnchor, info=info) - of ieNim, ieNimGroup, ieNimTitle: - if ext in [".md", ".rst"] or isMarkup: - rstMessage(s, idxPath, meInvalidField, - $entry.kind & " in supposedly markup-derived file") - s.nimFileImported = true - var langSym: LangSymbol - if entry.kind in {ieNim, ieNimTitle}: - var q: RstParser - initParser(q, s) - info.line = entry.line.uint16 - setLen(q.tok, 0) - q.idx = 0 - getTokens(entry.linkTitle, q.tok) - var sons = newSeq[PRstNode](q.tok.len) - for i in 0 ..< q.tok.len: sons[i] = newLeaf(q.tok[i].symbol) - let linkTitle = newRstNode(rnInner, sons) - langSym = linkTitle.toLangSymbol - else: # entry.kind == ieNimGroup - langSym = langSymbolGroup(kind=entry.linkTitle, name=entry.keyword) - addAnchorNim(s, external = true, refn = refn, tooltip = entry.linkDesc, - langSym = langSym, priority = -4, # lowest - info=info) - doAssert s.idxImports[origFilename].title != "" - -proc preparePass2*(s: var PRstSharedState, mainNode: PRstNode, importdoc = true) = - ## Records titles in node `mainNode` and orders footnotes. - countTitles(s, mainNode) - fixHeadlines(s) - orderFootnotes(s) - if importdoc: - for origFilename in s.idxImports.keys: - loadIdxFile(s, origFilename) - -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 - tooltip: string - target: PRstNode - info: TLineInfo - externFilename: string - # when external anchor: origin filename where anchor was defined - isTitle: bool - proc cmp(x, y: LinkDef): int = - result = cmp(x.priority, y.priority) - if result == 0: - result = cmp(x.target, y.target) - var foundLinks: seq[LinkDef] - 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, alias.addNodes, n.info) - template getExternFilename(subst: AnchorSubst): string = - if subst.kind == arExternalRst or - (subst.kind == arNim and subst.external): - getFilename(s, subst) - else: "" - for subst in substRst: - var refname, fullRefname: string - if subst.kind == arInternalRst: - refname = subst.target.anchor - fullRefname = refname - else: # arExternalRst - refname = subst.refnameExt - fullRefname = s.idxImports[getFilename(s, subst)].linkRelPath & - "/" & refname - let anchorType = - if subst.kind == arInternalRst: subst.anchorType - else: subst.anchorTypeExt # arExternalRst - foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority, - target: newLeaf(fullRefname), - info: subst.info, - externFilename: getExternFilename(subst), - isTitle: isDocumentationTitle(refname), - tooltip: "(" & $anchorType & ")") - # find anchors automatically generated from Nim symbols - if roNimFile in s.options or s.nimFileImported: - let substNim = findMainAnchorNim(s, signature=alias, n.info) - for subst in substNim: - let fullRefname = - if subst.external: - s.idxImports[getFilename(s, subst)].linkRelPath & - "/" & subst.refname - else: subst.refname - foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority, - target: newLeaf(fullRefname), - externFilename: getExternFilename(subst), - isTitle: isDocumentationTitle(subst.refname), - info: subst.info, tooltip: subst.tooltip) - foundLinks.sort(cmp = cmp, order = Descending) - let aliasStr = addNodes(alias) - if foundLinks.len >= 1: - if foundLinks[0].externFilename != "": - s.idxImports[foundLinks[0].externFilename].used = true - let kind = if foundLinks[0].ar in {arHyperlink, arExternalRst}: rnHyperlink - elif foundLinks[0].ar == arNim: - if foundLinks[0].externFilename == "": rnNimdocRef - else: rnHyperlink - else: rnInternalRef - result = newRstNode(kind) - let documentName = # filename without ext for `.nim`, title for `.md` - if foundLinks[0].ar == arNim: - changeFileExt(foundLinks[0].externFilename.extractFilename, "") - elif foundLinks[0].externFilename != "": - s.idxImports[foundLinks[0].externFilename].title - else: foundLinks[0].externFilename.extractFilename - let linkText = - if foundLinks[0].externFilename != "": - if foundLinks[0].isTitle: newLeaf(addNodes(desc)) - else: newLeaf(documentName & ": " & addNodes(desc)) - else: - newRstNode(rnInner, desc.sons) - result.sons = @[linkText, foundLinks[0].target] - if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip - if foundLinks.len > 1: # report ambiguous link - var targets = newSeq[string]() - for l in foundLinks: - var t = " " - if s.filenames.len > 1: - t.add getFilename(s.filenames, l.info.fileIndex) - let n = l.info.line - let c = l.info.col + ColRstOffset - t.add "($1, $2): $3" % [$n, $c, l.tooltip] - targets.add t - rstMessage(s.filenames, s.msgHandler, n.info, mwAmbiguousLink, - "`$1`\n clash:\n$2" % [ - aliasStr, targets.join("\n")]) - else: # nothing found - result = n - rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, aliasStr) - -proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = - ## Makes pass 2 of RST parsing. - ## Resolves substitutions and anchor aliases, groups footnotes. - ## Takes input node `n` and returns the same node with recursive - ## substitutions in `n.sons` to `result`. +proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode = result = n if n == nil: return case n.kind of rnSubstitutionReferences: - var x = findSub(s, n) + var x = findSub(p, n) if x >= 0: - result = s.subs[x].value + result = p.s.subs[x].value else: var key = addNodes(n) var e = getEnv(key) - if e != "": result = newLeaf(e) - else: rstMessage(s.filenames, s.msgHandler, n.info, - mwUnknownSubstitution, key) - of rnRstRef, rnPandocRef: - result = resolveLink(s, n) - of rnFootnote: - var (fnType, num) = getFootnoteType(n.sons[0]) - case fnType - of fnManualNumber, fnCitation: - discard "no need to alter fixed text" - of fnAutoNumberLabel, fnAutoNumber: - if fnType == fnAutoNumberLabel: - let labelR = rstnodeToRefname(n.sons[0]) - num = getFootnoteNum(s, labelR) - else: - num = getFootnoteNum(s, n.order) - var nn = newRstNode(rnInner) - nn.add newLeaf($num) - result.sons[0] = nn - of fnAutoSymbol: - let sym = getAutoSymbol(s, n.order) - n.sons[0].sons[0].text = sym - n.sons[1] = resolveSubs(s, n.sons[1]) - of rnFootnoteRef: - var (fnType, num) = getFootnoteType(n.sons[0]) - template addLabel(number: int | string) = - var nn = newRstNode(rnInner) - nn.add newLeaf($number) - result.add(nn) - var refn = fnType.prefix - # create new rnFootnoteRef, add final label, and finalize target refn: - result = newRstNode(rnFootnoteRef, info = n.info) - case fnType - of fnManualNumber: - addLabel num - refn.add $num - of fnAutoNumber: - inc s.currFootnoteNumRef - addLabel getFootnoteNum(s, s.currFootnoteNumRef) - refn.add $s.currFootnoteNumRef - of fnAutoNumberLabel: - addLabel getFootnoteNum(s, rstnodeToRefname(n)) - refn.add rstnodeToRefname(n) - of fnAutoSymbol: - inc s.currFootnoteSymRef - addLabel getAutoSymbol(s, s.currFootnoteSymRef) - refn.add $s.currFootnoteSymRef - of fnCitation: - result.add n.sons[0] - refn.add rstnodeToRefname(n) - # TODO: correctly report ambiguities - let anchorInfo = findMainAnchorRst(s, refn, n.info) - if anchorInfo.len != 0: - 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 + if e != "": result = newRstNode(rnLeaf, e) + else: rstMessage(p, mwUnknownSubstitution, key) + of rnRef: + var y = findRef(p, rstnodeToRefname(n)) + if y != nil: + result = newRstNode(rnHyperlink) + n.kind = rnInner + result.add(n) + result.add(y) of rnLeaf: discard + of rnContents: + p.hasToc = true else: - var regroup = false - for i in 0 ..< n.len: - n.sons[i] = resolveSubs(s, n.sons[i]) - if n.sons[i] != nil and n.sons[i].kind == rnFootnote: - regroup = true - if regroup: # group footnotes together into rnFootnoteGroup - var newSons: seq[PRstNode] - var i = 0 - while i < n.len: - if n.sons[i] != nil and n.sons[i].kind == rnFootnote: - var grp = newRstNode(rnFootnoteGroup) - while i < n.len and n.sons[i].kind == rnFootnote: - grp.sons.add n.sons[i] - inc i - newSons.add grp - else: - newSons.add n.sons[i] - inc i - result.sons = newSons - -proc completePass2*(s: PRstSharedState) = - for (filename, importdocInfo) in s.idxImports.pairs: - if not importdocInfo.used: - rstMessage(s.filenames, s.msgHandler, importdocInfo.fromInfo, - mwUnusedImportdoc, filename) + for i in 0 ..< n.len: n.sons[i] = resolveSubs(p, n.sons[i]) proc rstParse*(text, filename: string, - line, column: int, + line, column: int, hasToc: var bool, options: RstParseOptions, findFile: FindFileHandler = nil, - findRefFile: FindRefFileHandler = nil, - msgHandler: MsgHandler = nil): - tuple[node: PRstNode, filenames: RstFileTable, hasToc: bool] = - ## Parses the whole `text`. The result is ready for `rstgen.renderRstToOut`, - ## 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, findRefFile, - msgHandler, hasToc=false) - let unresolved = rstParsePass1(text, line, column, sharedState) - preparePass2(sharedState, unresolved) - result.node = resolveSubs(sharedState, unresolved) - completePass2(sharedState) - result.filenames = sharedState.filenames - result.hasToc = sharedState.hasToc + msgHandler: MsgHandler = nil): PRstNode = + var p: RstParser + initParser(p, newSharedState(options, findFile, msgHandler)) + p.filename = filename + p.line = line + p.col = column + getTokens(text, roSkipPounds in options, p.tok) + result = resolveSubs(p, parseDoc(p)) + hasToc = p.hasToc diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index c808318b5..e4e192fa3 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -8,26 +8,23 @@ # ## This module implements an AST for the `reStructuredText`:idx: parser. +## +## **Note:** Import ``packages/docutils/rstast`` to use this module 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 rnHeadline, # a headline rnOverline, # an over- and underlined headline - rnMarkdownHeadline, # a Markdown headline rnTransition, # a transition (the -------------
    thingie) rnParagraph, # a paragraph rnBulletList, # a bullet list rnBulletItem, # a bullet item rnEnumList, # an enumerated list rnEnumItem, # an enumerated item - rnDefList, rnMdDefList, # a definition list (RST/Markdown) + rnDefList, # a definition list rnDefItem, # an item of a definition list consisting of ... rnDefName, # ... a name part ... rnDefBody, # ... and a body part ... @@ -36,24 +33,16 @@ type rnFieldName, # consisting of a field name ... rnFieldBody, # ... and a field body rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString, - rnOptionArgument, rnDescription, rnLiteralBlock, - rnMarkdownBlockQuote, # a quote starting from punctuation like >>> - rnMarkdownBlockQuoteItem, # a quotation block, quote lines starting with - # the same number of chars + rnOptionArgument, rnDescription, rnLiteralBlock, rnQuotedLiteralBlock, rnLineBlock, # the | thingie rnLineBlockItem, # a son of rnLineBlock - one line inside it. - # When `RstNode` lineIndent="\n" the line's empty + # When `RstNode` text="\n" the line's empty rnBlockQuote, # text just indented rnTable, rnGridTable, rnMarkdownTable, rnTableRow, rnTableHeaderCell, rnTableDataCell, + rnLabel, # used for footnotes and other things rnFootnote, # a footnote - 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, - rnRstRef, # RST reference like `section name`_ - rnPandocRef, # Pandoc Markdown reference like [section name] - rnInternalRef, rnFootnoteRef, - rnNimdocRef, # reference to automatically generated Nim symbol + rnCitation, # similar to footnote + rnStandaloneHyperlink, rnHyperlink, rnRef, rnDirective, # a general directive rnDirArg, # a directive argument (for some directives). # here are directives that are not rnDirective: @@ -66,94 +55,41 @@ type # * `file#id `_ # * `file#id '_ rnSubstitutionDef, # a definition of a substitution - # Inline markup: - rnInlineCode, # interpreted text with code in a known language - rnCodeFragment, # inline code for highlighting with the specified - # class (which cannot be inferred from context) - rnUnknownRole, # interpreted text with an unknown role + rnGeneralRole, # Inline markup: rnSub, rnSup, rnIdx, rnEmphasis, # "*" rnStrongEmphasis, # "**" rnTripleEmphasis, # "***" - rnInterpretedText, # "`" an auxiliary role for parsing that will - # be converted into other kinds like rnInlineCode + rnInterpretedText, # "`" rnInlineLiteral, # "``" - rnInlineTarget, # "_`target`" rnSubstitutionReferences, # "|" rnSmiley, # some smiley - rnDefaultRole, # .. default-role:: code rnLeaf # a leaf; the node's text field contains the # leaf val - FileIndex* = distinct int32 - TLineInfo* = object - line*: uint16 - col*: int16 - fileIndex*: FileIndex PRstNode* = ref RstNode ## an RST node RstNodeSeq* = seq[PRstNode] - RstNode* {.acyclic, final.} = object ## AST node (result of RST parsing) - case kind*: RstNodeKind ## the node's kind - of rnLeaf, rnSmiley: - text*: string ## string that is expected to be displayed - of rnEnumList: - labelFmt*: string ## label format like "(1)" - of rnLineBlockItem: - lineIndent*: string ## a few spaces or newline at the line beginning - of rnAdmonition: - adType*: string ## admonition type: "note", "caution", etc. This - ## text will set the style and also be displayed - of rnOverline, rnHeadline, rnMarkdownHeadline: - level*: int ## level of headings starting from 1 (main - ## chapter) to larger ones (minor sub-sections) - ## level=0 means it's document title or subtitle - of rnFootnote, rnCitation, rnOptionListItem: - order*: int ## footnote order (for auto-symbol footnotes and - ## auto-numbered ones without a label) - of rnMarkdownBlockQuoteItem: - quotationDepth*: int ## number of characters in line prefix - 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 - ## (aka HTML id tag, aka Latex label/hypertarget) + RstNode* {.acyclic, final.} = object ## an RST node's description + kind*: RstNodeKind ## the node's kind + text*: string ## valid for leafs in the AST; and the title of + ## the document or the section; and rnEnumList + ## and rnAdmonition; and rnLineBlockItem + level*: int ## valid for some node kinds sons*: RstNodeSeq ## the node's sons -proc `==`*(a, b: FileIndex): bool {.borrow.} - proc len*(n: PRstNode): int = result = len(n.sons) -proc newRstNode*(kind: RstNodeKind, sons: seq[PRstNode] = @[], - anchor = ""): PRstNode = - result = PRstNode(kind: kind, sons: sons, anchor: anchor) +proc newRstNode*(kind: RstNodeKind): PRstNode = + new(result) + result.sons = @[] + result.kind = kind -proc newRstNode*(kind: RstNodeKind, info: TLineInfo, - sons: seq[PRstNode] = @[]): PRstNode = - result = PRstNode(kind: kind, sons: sons) - result.info = info - -proc newRstNode*(kind: RstNodeKind, s: string): PRstNode {.deprecated.} = - assert kind in {rnLeaf, rnSmiley} +proc newRstNode*(kind: RstNodeKind, s: string): PRstNode = result = newRstNode(kind) result.text = s -proc newRstLeaf*(s: string): PRstNode = - result = newRstNode(rnLeaf) - result.text = s - proc lastSon*(n: PRstNode): PRstNode = result = n.sons[len(n.sons)-1] @@ -161,7 +97,7 @@ proc add*(father, son: PRstNode) = add(father.sons, son) proc add*(father: PRstNode; s: string) = - add(father.sons, newRstLeaf(s)) + add(father.sons, newRstNode(rnLeaf, s)) proc addIfNotNil*(father, son: PRstNode) = if son != nil: add(father, son) @@ -284,7 +220,7 @@ proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) = inc(d.indent, 2) renderRstSons(d, n, result) dec(d.indent, 2) - of rnRstRef: + of rnRef: result.add("`") renderRstSons(d, n, result) result.add("`_") @@ -294,7 +230,7 @@ proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) = result.add(" <") renderRstToRst(d, n.sons[1], result) result.add(">`_") - of rnUnknownRole: + of rnGeneralRole: result.add('`') renderRstToRst(d, n.sons[0],result) result.add("`:") @@ -367,7 +303,7 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode = (key: "kind", val: %($node.kind)), (key: "level", val: %BiggestInt(node.level)) ] - if node.kind in {rnLeaf, rnSmiley} and node.text.len > 0: + if node.text.len > 0: result.add("text", %node.text) if len(node.sons) > 0: var accm = newSeq[JsonNode](len(node.sons)) @@ -377,68 +313,25 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode = proc renderRstToJson*(node: PRstNode): string = ## Writes the given RST node as JSON that is in the form - ## - ## { - ## "kind":string node.kind, - ## "text":optional string node.text, - ## "level":optional int node.level, - ## "sons":optional node array - ## } + ## :: + ## { + ## "kind":string node.kind, + ## "text":optional string node.text, + ## "level":optional int node.level, + ## "sons":optional node array + ## } renderRstToJsonNode(node).pretty -proc renderRstToText*(node: PRstNode): string = - ## minimal text representation of markup node - const code = {rnCodeFragment, rnInterpretedText, rnInlineLiteral, rnInlineCode} - if node == nil: - return "" - case node.kind - of rnLeaf, rnSmiley: - result.add node.text - else: - if node.kind in code: result.add "`" - for i in 0 ..< node.sons.len: - if node.kind in {rnInlineCode, rnCodeBlock} and i == 0: - continue # omit language specifier - result.add renderRstToText(node.sons[i]) - if node.kind in code: result.add "`" - -proc treeRepr*(node: PRstNode, indent=0): string = - ## Writes the parsed RST `node` into an AST tree with compact string +proc renderRstToStr*(node: PRstNode, indent=0): string = + ## Writes the parsed RST `node` into a compact string ## representation in the format (one line per every sub-node): - ## ``indent - kind - [text|level|order|adType] - anchor (if non-zero)`` + ## ``indent - kind - text - level (if non-zero)`` ## (suitable for debugging of RST parsing). if node == nil: result.add " ".repeat(indent) & "[nil]\n" return - result.add " ".repeat(indent) & $node.kind - case node.kind - of rnLeaf, rnSmiley: - result.add (if node.text == "": "" else: " '" & node.text & "'") - of rnEnumList: - result.add " labelFmt=" & node.labelFmt - of rnLineBlockItem: - var txt: string - if node.lineIndent == "\n": txt = " (blank line)" - else: txt = " lineIndent=" & $node.lineIndent.len - result.add txt - of rnAdmonition: - result.add " adType=" & node.adType - of rnHeadline, rnOverline, rnMarkdownHeadline: - result.add " level=" & $node.level - of rnFootnote, rnCitation, rnOptionListItem: - 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 & "'") - result.add "\n" + result.add " ".repeat(indent) & $node.kind & "\t" & + (if node.text == "": "" else: "'" & node.text & "'") & + (if node.level == 0: "" else: "\tlevel=" & $node.level) & "\n" for son in node.sons: - result.add treeRepr(son, indent=indent+2) + result.add renderRstToStr(son, indent=indent+2) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 57bc00fcb..52125b52c 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -23,30 +23,10 @@ ## many options and tweaking, but you are not limited to snippets and can ## generate `LaTeX documents `_ too. ## -## `Docutils configuration files`_ are not supported. Instead HTML generation -## can be tweaked by editing file ``config/nimdoc.cfg``. -## -## .. _Docutils configuration files: https://docutils.sourceforge.io/docs/user/config.htm -## -## There are stylistic difference between how this module renders some elements -## and how original Python Docutils does: -## -## * Backreferences to TOC in section headings are not generated. -## In HTML each section is also a link that points to the section itself: -## this is done for user to be able to copy the link into clipboard. -## -## * 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]``. - -import strutils, os, hashes, strtabs, rstast, rst, rstidx, - highlite, tables, sequtils, - algorithm, parseutils, std/strbasics - - -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio, formatfloat] +## **Note:** Import ``packages/docutils/rstgen`` to use this module +import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, + algorithm, parseutils import ../../std/private/since @@ -59,26 +39,26 @@ type outHtml, # output is HTML outLatex # output is Latex - MetaEnum* = enum - metaNone, metaTitleRaw, metaTitle, metaSubtitle, metaAuthor, metaVersion + TocEntry = object + n*: PRstNode + refname*, header*: string - EscapeMode* = enum # in Latex text inside options [] and URLs is - # escaped slightly differently than in normal text - emText, emOption, emUrl # emText is currently used for code also + MetaEnum* = enum + metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion RstGenerator* = object of RootObj target*: OutputTarget config*: StringTableRef splitAfter*: int # split too long entries in the TOC listingCounter*: int - tocPart*: seq[PRstNode] # headings for Table of Contents + tocPart*: seq[TocEntry] hasToc*: bool theIndex: string # Contents of the index file to be dumped at the end. + options*: RstParseOptions findFile*: FindFileHandler msgHandler*: MsgHandler outDir*: string ## output directory, initialized by docgen.nim destFile*: string ## output (HTML) file, initialized by docgen.nim - filenames*: RstFileTable filename*: string ## source Nim or Rst file meta*: array[MetaEnum, string] currentSection: string ## \ @@ -89,9 +69,7 @@ type ## for hyperlinks. See renderIndexTerm proc for details. id*: int ## A counter useful for generating IDs. onTestSnippet*: proc (d: var RstGenerator; filename, cmd: string; status: int; - content: string) {.gcsafe.} - escMode*: EscapeMode - curQuotationDepth: int + content: string) PDoc = var RstGenerator ## Alias to type less. @@ -115,10 +93,9 @@ proc init(p: var CodeBlockParams) = proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, config: StringTableRef, filename: string, + options: RstParseOptions, findFile: FindFileHandler = nil, - msgHandler: MsgHandler = nil, - filenames = default(RstFileTable), - hasToc = false) = + msgHandler: MsgHandler = nil) = ## Initializes a ``RstGenerator``. ## ## You need to call this before using a ``RstGenerator`` with any other @@ -154,25 +131,22 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, ## ## Example: ## - ## ```nim + ## .. code-block:: nim + ## ## import packages/docutils/rstgen ## ## var gen: RstGenerator ## gen.initRstGenerator(outHtml, defaultConfig(), "filename", {}) - ## ``` g.config = config g.target = target g.tocPart = @[] - g.hasToc = hasToc g.filename = filename - g.filenames = filenames g.splitAfter = 20 g.theIndex = "" + g.options = options g.findFile = findFile g.currentSection = "" g.id = 0 - g.escMode = emText - g.curQuotationDepth = 0 let fileParts = filename.splitFile if fileParts.ext == ".nim": g.currentSection = "Module " & fileParts.name @@ -191,9 +165,7 @@ proc writeIndexFile*(g: var RstGenerator, outfile: string) = ## If the index is empty the file won't be created. if g.theIndex.len > 0: writeFile(outfile, g.theIndex) -proc addHtmlChar(dest: var string, c: char) = - # Escapes HTML characters. Note that single quote ' is not escaped as - # ' -- unlike XML (for standards pre HTML5 it was even forbidden). +proc addXmlChar(dest: var string, c: char) = case c of '&': add(dest, "&") of '<': add(dest, "<") @@ -201,36 +173,35 @@ proc addHtmlChar(dest: var string, c: char) = of '\"': add(dest, """) else: add(dest, c) -proc addTexChar(dest: var string, c: char, escMode: EscapeMode) = - ## Escapes 10 special Latex characters and sometimes ` and [, ]. - ## TODO: @ is always a normal symbol (besides the header), am I wrong? - ## All escapes that need to work in text and code blocks (`emText` mode) - ## should start from \ (to be compatible with fancyvrb/fvextra). +proc addRtfChar(dest: var string, c: char) = case c - of '_', '&', '#', '%': add(dest, "\\" & c) - # commands \label and \pageref don't accept \$ by some reason but OK with $: - of '$': (if escMode == emUrl: add(dest, c) else: add(dest, "\\" & c)) - # \~ and \^ have a special meaning unless they are followed by {} - of '~', '^': add(dest, "\\" & c & "{}") - # Latex loves to substitute ` to opening quote, even in texttt mode! - of '`': add(dest, "\\textasciigrave{}") - # add {} to avoid gobbling up space by \textbackslash - of '\\': add(dest, "\\textbackslash{}") - # Using { and } in URL in Latex: https://tex.stackexchange.com/a/469175 - of '{': - add(dest, if escMode == emUrl: "\\%7B" else: "\\{") - of '}': - add(dest, if escMode == emUrl: "\\%7D" else: "\\}") - of ']': - # escape ] inside an optional argument in e.g. \section[static[T]]{.. - add(dest, if escMode == emOption: "\\text{]}" else: "]") + of '{': add(dest, "\\{") + of '}': add(dest, "\\}") + of '\\': add(dest, "\\\\") else: add(dest, c) -proc escChar*(target: OutputTarget, dest: var string, - c: char, escMode: EscapeMode) {.inline.} = +proc addTexChar(dest: var string, c: char) = + case c + of '_': add(dest, "\\_") + of '{': add(dest, "\\symbol{123}") + of '}': add(dest, "\\symbol{125}") + of '[': add(dest, "\\symbol{91}") + of ']': add(dest, "\\symbol{93}") + of '\\': add(dest, "\\symbol{92}") + of '$': add(dest, "\\$") + of '&': add(dest, "\\&") + of '#': add(dest, "\\#") + of '%': add(dest, "\\%") + of '~': add(dest, "\\symbol{126}") + of '@': add(dest, "\\symbol{64}") + of '^': add(dest, "\\symbol{94}") + of '`': add(dest, "\\symbol{96}") + else: add(dest, c) + +proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} = case target - of outHtml: addHtmlChar(dest, c) - of outLatex: addTexChar(dest, c, escMode) + of outHtml: addXmlChar(dest, c) + of outLatex: addTexChar(dest, c) proc addSplitter(target: OutputTarget; dest: var string) {.inline.} = case target @@ -249,7 +220,7 @@ proc nextSplitPoint*(s: string, start: int): int = inc(result) dec(result) # last valid index -proc esc*(target: OutputTarget, s: string, splitAfter = -1, escMode = emText): string = +proc esc*(target: OutputTarget, s: string, splitAfter = -1): string = ## Escapes the HTML. result = "" if splitAfter >= 0: @@ -260,11 +231,11 @@ proc esc*(target: OutputTarget, s: string, splitAfter = -1, escMode = emText): s #if (splitter != " ") or (partLen + k - j + 1 > splitAfter): partLen = 0 addSplitter(target, result) - for i in countup(j, k): escChar(target, result, s[i], escMode) + for i in countup(j, k): escChar(target, result, s[i]) inc(partLen, k - j + 1) j = k + 1 else: - for i in countup(0, len(s) - 1): escChar(target, result, s[i], escMode) + for i in countup(0, len(s) - 1): escChar(target, result, s[i]) proc disp(target: OutputTarget, xml, tex: string): string = @@ -284,46 +255,58 @@ proc dispA(target: OutputTarget, dest: var string, proc `or`(x, y: string): string {.inline.} = result = if x.len == 0: y else: x -proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string) {.gcsafe.} +proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string) ## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration. ## ## Before using this proc you need to initialise a ``RstGenerator`` with ## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the ## `packages/docutils/rst module `_. Example: - ## ```nim + ## + ## .. code-block:: nim + ## ## # ...configure gen and rst vars... ## var generatedHtml = "" ## renderRstToOut(gen, rst, generatedHtml) ## echo generatedHtml - ## ``` proc renderAux(d: PDoc, n: PRstNode, result: var string) = for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result) -template idS(txt: string): string = - if txt == "": "" - else: - case d.target - of outHtml: - " id=\"" & txt & "\"" - of outLatex: - "\\label{" & txt & "}\\hypertarget{" & txt & "}{}" - # we add \label for page number references via \pageref, while - # \hypertarget is for clickable links via \hyperlink. - -proc renderAux(d: PDoc, n: PRstNode, html, tex: string, result: var string) = - # formats sons of `n` as substitution variable $1 inside strings `html` and - # `tex`, internal target (anchor) is provided as substitute $2. +proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) = var tmp = "" for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], tmp) - case d.target - of outHtml: result.addf(html, [tmp, n.anchor.idS]) - of outLatex: result.addf(tex, [tmp, n.anchor.idS]) + if d.target != outLatex: + result.addf(frmtA, [tmp]) + else: + result.addf(frmtB, [tmp]) # ---------------- index handling -------------------------------------------- -proc setIndexTerm*(d: var RstGenerator; k: IndexEntryKind, htmlFile, id, term: string, - linkTitle, linkDesc = "", line = 0) = +proc quoteIndexColumn(text: string): string = + ## Returns a safe version of `text` for serialization to the ``.idx`` file. + ## + ## The returned version can be put without worries in a line based tab + ## separated column text file. The following character sequence replacements + ## will be performed for that goal: + ## + ## * ``"\\"`` => ``"\\\\"`` + ## * ``"\n"`` => ``"\\n"`` + ## * ``"\t"`` => ``"\\t"`` + result = newStringOfCap(text.len + 3) + for c in text: + case c + of '\\': result.add "\\" + of '\L': result.add "\\n" + of '\C': discard + of '\t': result.add "\\t" + else: result.add c + +proc unquoteIndexColumn(text: string): string = + ## Returns the unquoted version generated by ``quoteIndexColumn``. + result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\")) + +proc setIndexTerm*(d: var RstGenerator, htmlFile, id, term: string, + linkTitle, linkDesc = "") = ## Adds a `term` to the index using the specified hyperlink identifier. ## ## A new entry will be added to the index using the format @@ -346,8 +329,21 @@ proc setIndexTerm*(d: var RstGenerator; k: IndexEntryKind, htmlFile, id, term: s ## <#writeIndexFile,RstGenerator,string>`_. The purpose of the index is ## documented in the `docgen tools guide ## `_. - let (entry, isTitle) = formatIndexEntry(k, htmlFile, id, term, - linkTitle, linkDesc, line) + var + entry = term + isTitle = false + entry.add('\t') + entry.add(htmlFile) + if id.len > 0: + entry.add('#') + entry.add(id) + else: + isTitle = true + if linkTitle.len > 0 or linkDesc.len > 0: + entry.add('\t' & linkTitle.quoteIndexColumn) + entry.add('\t' & linkDesc.quoteIndexColumn) + entry.add("\n") + if isTitle: d.theIndex.insert(entry) else: d.theIndex.add(entry) @@ -360,15 +356,6 @@ proc hash(n: PRstNode): int = result = result !& hash(n.sons[i]) result = !$result -proc htmlFileRelPath(d: PDoc): string = - if d.outDir.len == 0: - # /foo/bar/zoo.nim -> zoo.html - changeFileExt(extractFilename(d.filename), HtmlExt) - else: # d is initialized in docgen.nim - # outDir = /foo -\ - # destFile = /foo/bar/zoo.html -|-> bar/zoo.html - d.destFile.relativePath(d.outDir, '/') - proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = ## Renders the string decorated within \`foobar\`\:idx\: markers. ## @@ -385,13 +372,18 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = var term = "" renderAux(d, n, term) - setIndexTerm(d, ieIdxRole, - htmlFileRelPath(d), id, term, d.currentSection) - dispA(d.target, result, "$2", "\\nimindexterm{$1}{$2}", + setIndexTerm(d, changeFileExt(extractFilename(d.filename), HtmlExt), id, term, d.currentSection) + dispA(d.target, result, "$2", "$2\\label{$1}", [id, term]) type - IndexedDocs* = Table[IndexEntry, seq[IndexEntry]] ## \ + 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]] ## \ ## Contains the index sequences for doc types. ## ## The key is a *fake* IndexEntry which will contain the title of the @@ -401,6 +393,21 @@ type ## The value indexed by this IndexEntry is a sequence with the real index ## entries found in the ``.idx`` file. +proc cmp(a, b: IndexEntry): int = + ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`. + result = cmpIgnoreStyle(a.keyword, b.keyword) + if result == 0: + result = cmpIgnoreStyle(a.link, b.link) + +proc hash(x: IndexEntry): Hash = + ## Returns the hash for the combined fields of the type. + ## + ## The hash is computed as the chained hash of the individual string hashes. + result = x.keyword.hash !& x.link.hash + result = result !& x.linkTitle.hash + result = result !& x.linkDesc.hash + result = !$result + when defined(gcDestructors): template `<-`(a, b: var IndexEntry) = a = move(b) else: @@ -409,7 +416,6 @@ else: shallowCopy a.link, b.link shallowCopy a.linkTitle, b.linkTitle shallowCopy a.linkDesc, b.linkDesc - shallowCopy a.module, b.module proc sortIndex(a: var openArray[IndexEntry]) = # we use shellsort here; fast and simple @@ -449,20 +455,16 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string = result = "
    " var i = 0 while i < symbols.len: - let keyword = esc(outHtml, symbols[i].keyword) + let keyword = symbols[i].keyword let cleanedKeyword = keyword.escapeLink result.addf("
    $1:
      \n", [keyword, cleanedKeyword]) var j = i - while j < symbols.len and symbols[i].keyword == symbols[j].keyword: + while j < symbols.len and keyword == symbols[j].keyword: let url = symbols[j].link.escapeLink - module = symbols[j].module - text = - if symbols[j].linkTitle.len > 0: - esc(outHtml, module & ": " & symbols[j].linkTitle) - else: url - desc = symbols[j].linkDesc + text = if symbols[j].linkTitle.len > 0: symbols[j].linkTitle else: url + desc = if symbols[j].linkDesc.len > 0: symbols[j].linkDesc else: "" if desc.len > 0: result.addf("""
    • $2
    • @@ -476,6 +478,13 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string = i = j result.add("
    ") +proc isDocumentationTitle(hyperlink: string): bool = + ## Returns true if the hyperlink is actually a documentation title. + ## + ## Documentation titles lack the hash. See `mergeIndexes() + ## <#mergeIndexes,string>`_ for a more detailed explanation. + result = hyperlink.find('#') < 0 + proc stripTocLevel(s: string): tuple[level: int, text: string] = ## Returns the *level* of the toc along with the text without it. for c in 0 ..< s.len: @@ -509,15 +518,17 @@ proc generateDocumentationToc(entries: seq[IndexEntry]): string = level = 1 levels.newSeq(entries.len) for entry in entries: - let (rawLevel, rawText) = stripTocLevel(entry.linkTitle) + let (rawLevel, rawText) = stripTocLevel(entry.linkTitle or entry.keyword) if rawLevel < 1: # This is a normal symbol, push it *inside* one level from the last one. levels[L].level = level + 1 + # Also, ignore the linkTitle and use directly the keyword. + levels[L].text = entry.keyword else: # The level did change, update the level indicator. level = rawLevel levels[L].level = rawLevel - levels[L].text = rawText + levels[L].text = rawText inc L # Now generate hierarchical lists based on the precalculated levels. @@ -548,7 +559,7 @@ proc generateDocumentationIndex(docs: IndexedDocs): string = for title in titles: let tocList = generateDocumentationToc(docs.getOrDefault(title)) result.add("\n") + title.link & "\">" & title.keyword & "\n" & tocList & "\n") proc generateDocumentationJumps(docs: IndexedDocs): string = ## Returns a plain list of hyperlinks to documentation TOCs in HTML. @@ -560,7 +571,7 @@ proc generateDocumentationJumps(docs: IndexedDocs): string = var chunks: seq[string] = @[] for title in titles: - chunks.add("" & title.linkTitle & "") + chunks.add("" & title.keyword & "") result.add(chunks.join(", ") & ".
    ") @@ -574,7 +585,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. ## @@ -589,11 +600,39 @@ proc readIndexDir*(dir: string): # Scan index files and build the list of symbols. for path in walkDirRec(dir): if path.endsWith(IndexExt): - var (fileEntries, title) = parseIdxFile(path) + var + fileEntries: seq[IndexEntry] + title: IndexEntry + f = 0 + newSeq(fileEntries, 500) + setLen(fileEntries, 0) + for line in lines(path): + let s = line.find('\t') + if s < 0: continue + setLen(fileEntries, f+1) + fileEntries[f].keyword = line.substr(0, s-1) + fileEntries[f].link = line.substr(s+1) + # See if we detect a title, a link without a `#foobar` trailing part. + if title.keyword.len == 0 and fileEntries[f].link.isDocumentationTitle: + title.keyword = fileEntries[f].keyword + title.link = fileEntries[f].link + + if fileEntries[f].link.find('\t') > 0: + let extraCols = fileEntries[f].link.split('\t') + fileEntries[f].link = extraCols[0] + assert extraCols.len == 3 + fileEntries[f].linkTitle = extraCols[1].unquoteIndexColumn + fileEntries[f].linkDesc = extraCols[2].unquoteIndexColumn + else: + fileEntries[f].linkTitle = "" + fileEntries[f].linkDesc = "" + inc f # Depending on type add this to the list of symbols or table of APIs. - if title.kind == ieNimTitle: - for i in 0 ..< fileEntries.len: - if fileEntries[i].kind != ieNim: + if title.keyword.len == 0: + for i in 0 ..< f: + # Don't add to symbols TOC entries (they start with a whitespace). + let toc = fileEntries[i].linkTitle + if toc.len > 0 and toc[0] == ' ': continue # Ok, non TOC entry, add it. setLen(result.symbols, L + 1) @@ -603,15 +642,17 @@ proc readIndexDir*(dir: string): var x = fileEntries[0].link let i = find(x, '#') if i > 0: - x.setLen(i) + x = x.substr(0, i-1) if i != 0: # don't add entries starting with '#' result.modules.add(x.changeFileExt("")) else: # Generate the symbolic anchor for index quickjumps. - title.aux = "doc_toc_" & $result.docs.len + 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. ## @@ -641,7 +682,6 @@ 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. @@ -669,36 +709,71 @@ proc mergeIndexes*(dir: string): string = # ---------------------------------------------------------------------------- +proc stripTocHtml(s: string): string = + ## Ugly quick hack to remove HTML tags from TOC titles. + ## + ## A TocEntry.header field already contains rendered HTML tags. Instead of + ## implementing a proper version of renderRstToOut() which recursively + ## renders an rst tree to plain text, we simply remove text found between + ## angled brackets. Given the limited possibilities of rst inside TOC titles + ## this should be enough. + result = s + var first = result.find('<') + while first >= 0: + let last = result.find('>', first) + if last < 0: + # Abort, since we didn't found a closing angled bracket. + return + result.delete(first, last) + first = result.find('<', first) + 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 - var tocName = esc(d.target, renderRstToText(n), escMode = emOption) - # for Latex: simple text without commands that may break TOC/hyperref + # 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) if d.hasToc: - d.tocPart.add n - dispA(d.target, result, "\n$3", "\\rsth$4[$6]{$3}$2\n", - [$n.level, n.anchor.idS, tmp, - $chr(n.level - 1 + ord('A')), n.anchor, tocName]) + 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 + + dispA(d.target, result, "\n$3", "\\rsth$4{$3}\\label{$2}\n", + [$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))]) else: - dispA(d.target, result, "\n$3", - "\\rsth$4[$5]{$3}$2\n", [ - $n.level, n.anchor.idS, tmp, - $chr(n.level - 1 + ord('A')), tocName]) + dispA(d.target, result, "\n$3", + "\\rsth$4{$3}\\label{$2}\n", [ + $n.level, refname, tmp, + $chr(n.level - 1 + ord('A'))]) # Generate index entry using spaces to indicate TOC level for the output HTML. assert n.level >= 0 - setIndexTerm(d, ieHeading, htmlFile = d.htmlFileRelPath, id = n.anchor, - term = n.addNodes, linkTitle = spaces(max(0, n.level)) & tmp) + let + htmlFileRelPath = if d.outDir.len == 0: + # /foo/bar/zoo.nim -> zoo.html + changeFileExt(extractFilename(d.filename), HtmlExt) + else: # d is initialized in docgen.nim + # outDir = /foo -\ + # destFile = /foo/bar/zoo.html -|-> bar/zoo.html + d.destFile.relativePath(d.outDir, '/') + setIndexTerm(d, htmlFileRelPath, refname, tmp.stripTocHtml, + spaces(max(0, n.level)) & tmp) proc renderOverline(d: PDoc, n: PRstNode, result: var string) = - if n.level == 0 and d.meta[metaTitle].len == 0: - d.meta[metaTitleRaw] = n.addNodes + if d.meta[metaTitle].len == 0: for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], d.meta[metaTitle]) d.currentSection = d.meta[metaTitle] - elif n.level == 0 and d.meta[metaSubtitle].len == 0: + elif d.meta[metaSubtitle].len == 0: for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], d.meta[metaSubtitle]) d.currentSection = d.meta[metaSubtitle] @@ -706,25 +781,21 @@ proc renderOverline(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 - var tocName = esc(d.target, renderRstToText(n), escMode=emOption) - dispA(d.target, result, "
    $3
    ", - "\\rstov$4[$5]{$3}$2\n", [$n.level, - n.anchor.idS, tmp, $chr(n.level - 1 + ord('A')), tocName]) - setIndexTerm(d, ieHeading, htmlFile = d.htmlFileRelPath, id = n.anchor, - term = n.addNodes, linkTitle = spaces(max(0, n.level)) & tmp) + dispA(d.target, result, "
    $3
    ", + "\\rstov$4{$3}\\label{$2}\n", [$n.level, + rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))]) -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) + +proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) = dispA(d.target, result, "
  • $2
  • \n", - "\\item\\label{$1_toc} $2\\ref{$1}\n", [n.anchor, header]) + "\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.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].level) + var a = abs(d.tocPart[j].n.level) if a == lvl: renderTocEntry(d, d.tocPart[j], tmp) inc(j) @@ -770,17 +841,15 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = if arg.endsWith(".mp4") or arg.endsWith(".ogg") or arg.endsWith(".webm"): htmlOut = """ - + """ else: - htmlOut = "" + htmlOut = "" # support for `:target:` links for images: - var target = esc(d.target, getFieldValue(n, "target").strip(), escMode=emUrl) - discard safeProtocol(target) - + var target = esc(d.target, getFieldValue(n, "target").strip()) if target.len > 0: # `htmlOut` needs to be of the following format for link to work for images: # @@ -790,8 +859,8 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = "\\href{$2}{$1}", [htmlOut, target]) htmlOut = htmlOutWithLink - dispA(d.target, result, htmlOut, "$3\\includegraphics$2{$1}", - [esc(d.target, arg), options, n.anchor.idS]) + dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}", + [esc(d.target, arg), options]) if len(n) >= 3: renderRstToOut(d, n.sons[2], result) proc renderSmiley(d: PDoc, n: PRstNode, result: var string) = @@ -801,25 +870,6 @@ proc renderSmiley(d: PDoc, n: PRstNode, result: var string) = "\\includegraphics{$1}", [d.config.getOrDefault"doc.smiley_format" % n.text]) -proc getField1Int(d: PDoc, n: PRstNode, fieldName: string): int = - template err(msg: string) = - rstMessage(d.filenames, d.msgHandler, n.info, meInvalidField, msg) - let value = n.getFieldValue - var number: int - let nChars = parseInt(value, number) - if nChars == 0: - if value.len == 0: - # use a good default value: - result = 1 - else: - err("field $1 requires an integer, but '$2' was given" % - [fieldName, value]) - elif nChars < value.len: - err("extra arguments were given to $1: '$2'" % - [fieldName, value[nChars..^1]]) - else: - result = number - proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = ## Parses useful fields which can appear before a code block. ## @@ -829,7 +879,9 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = of "number-lines": params.numberLines = true # See if the field has a parameter specifying a different line than 1. - params.startLine = getField1Int(d, n, "number-lines") + var number: int + if parseInt(n.getFieldValue, number) > 0: + params.startLine = number of "file", "filename": # The ``file`` option is a Nim extension to the official spec, it acts # like it would for other directives like ``raw`` or ``cvs-table``. This @@ -840,20 +892,18 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = of "test": params.testCmd = n.getFieldValue.strip if params.testCmd.len == 0: - # factor with D20210224T221756. Note that `$docCmd` should appear before `$file` - # but after all other options, but currently `$options` merges both options and `$file` so it's tricky. - params.testCmd = "$nim r --backend:$backend --lib:$libpath $docCmd $options" + params.testCmd = "$nim r --backend:$backend $options" # see `interpSnippetCmd` else: - # consider whether `$docCmd` should be appended here too params.testCmd = unescape(params.testCmd) of "status", "exitcode": - params.status = getField1Int(d, n, n.getArgument) + var status: int + if parseInt(n.getFieldValue, status) > 0: + params.status = status of "default-language": params.langStr = n.getFieldValue.strip params.lang = params.langStr.getSourceLanguage else: - rstMessage(d.filenames, d.msgHandler, n.info, mwUnsupportedField, - n.getArgument) + d.msgHandler(d.filename, 1, 0, mwUnsupportedField, n.getArgument) proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = ## Iterates over all code block fields and returns processed params. @@ -863,7 +913,8 @@ proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = result.init if n.isNil: return - assert n.kind in {rnCodeBlock, rnInlineCode} + assert n.kind == rnCodeBlock + assert(not n.sons[2].isNil) # Parse the field list for rendering parameters if there are any. if not n.sons[1].isNil: @@ -874,8 +925,7 @@ proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = if result.langStr != "": result.lang = getSourceLanguage(result.langStr) -proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string, - idStr: string): +proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string): tuple[beginTable, endTable: string] = ## Returns the necessary tags to start/end a code block in HTML. ## @@ -887,14 +937,13 @@ proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string, let id = $d.listingCounter if not params.numberLines: result = (d.config.getOrDefault"doc.listing_start" % - [id, sourceLanguageToStr[params.lang], idStr], + [id, sourceLanguageToStr[params.lang]], d.config.getOrDefault"doc.listing_end" % id) return var codeLines = code.strip.countLines assert codeLines > 0 - result.beginTable = """""" % [idStr] & - """
    """
    +  result.beginTable = """
    """
       var line = params.startLine
       while codeLines > 0:
         result.beginTable.add($line & "\n")
    @@ -902,32 +951,13 @@ proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string,
         codeLines.dec
       result.beginTable.add("
    " & ( d.config.getOrDefault"doc.listing_start" % - [id, sourceLanguageToStr[params.lang], idStr])) + [id, sourceLanguageToStr[params.lang]])) result.endTable = (d.config.getOrDefault"doc.listing_end" % id) & "
    " & ( d.config.getOrDefault"doc.listing_button" % id) -proc renderCodeLang*(result: var string, lang: SourceLanguage, code: string, - target: OutputTarget) = - var g: GeneralTokenizer - initGeneralTokenizer(g, code) - while true: - getNextToken(g, lang) - case g.kind - of gtEof: break - of gtNone, gtWhitespace: - add(result, substr(code, g.start, g.length + g.start - 1)) - else: - dispA(target, result, "$1", "\\span$2{$1}", [ - esc(target, substr(code, g.start, g.length+g.start-1)), - tokenClassToStr[g.kind]]) - deinitGeneralTokenizer(g) - -proc renderNimCode*(result: var string, code: string, target: OutputTarget) = - renderCodeLang(result, langNim, code, target) - -proc renderCode(d: PDoc, n: PRstNode, result: var string) {.gcsafe.} = - ## Renders a code (code block or inline code), appending it to `result`. +proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = + ## Renders a code block, appending it to `result`. ## ## If the code block uses the ``number-lines`` option, a table will be ## generated with two columns, the first being a list of numbers and the @@ -936,40 +966,37 @@ proc renderCode(d: PDoc, n: PRstNode, result: var string) {.gcsafe.} = ## may also come from the parser through the internal ``default-language`` ## option to differentiate between a plain code block and Nim's code block ## extension. - assert n.kind in {rnCodeBlock, rnInlineCode} - var params = d.parseCodeBlockParams(n) + assert n.kind == rnCodeBlock if n.sons[2] == nil: return + var params = d.parseCodeBlockParams(n) var m = n.sons[2].sons[0] assert m.kind == rnLeaf if params.testCmd.len > 0 and d.onTestSnippet != nil: d.onTestSnippet(d, params.filename, params.testCmd, params.status, m.text) - var blockStart, blockEnd: string - case d.target - of outHtml: - if n.kind == rnCodeBlock: - (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text, - n.anchor.idS) - else: # rnInlineCode - blockStart = "" - blockEnd = "" - of outLatex: - if n.kind == rnCodeBlock: - blockStart = "\n\n" & n.anchor.idS & "\\begin{rstpre}\n" - blockEnd = "\n\\end{rstpre}\n\n" - else: # rnInlineCode - blockStart = "\\rstcode{" - blockEnd = "}" - dispA(d.target, result, blockStart, blockStart, []) + let (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text) + + dispA(d.target, result, blockStart, "\\begin{rstpre}\n", []) if params.lang == langNone: - 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) + if len(params.langStr) > 0: + d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr) + for letter in m.text: escChar(d.target, result, letter) else: - renderCodeLang(result, params.lang, m.text, d.target) - dispA(d.target, result, blockEnd, blockEnd) + var g: GeneralTokenizer + initGeneralTokenizer(g, m.text) + while true: + getNextToken(g, params.lang) + case g.kind + of gtEof: break + of gtNone, gtWhitespace: + add(result, substr(m.text, g.start, g.length + g.start - 1)) + else: + dispA(d.target, result, "$1", "\\span$2{$1}", [ + esc(d.target, substr(m.text, g.start, g.length+g.start-1)), + tokenClassToStr[g.kind]]) + deinitGeneralTokenizer(g) + dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n") proc renderContainer(d: PDoc, n: PRstNode, result: var string) = var tmp = "" @@ -980,6 +1007,11 @@ proc renderContainer(d: PDoc, n: PRstNode, result: var string) = else: dispA(d.target, result, "
    $2
    ", "$2", [arg, tmp]) +proc texColumns(n: PRstNode): string = + result = "" + let nColumns = if n.sons.len > 0: len(n.sons[0]) else: 1 + for i in countup(1, nColumns): add(result, "|X") + proc renderField(d: PDoc, n: PRstNode, result: var string) = var b = false if d.target == outLatex: @@ -1003,48 +1035,48 @@ proc renderEnumList(d: PDoc, n: PRstNode, result: var string) = specStart = "" i1 = 0 pre = "" - i2 = n.labelFmt.len - 1 + i2 = n.text.len-1 post = "" - if n.labelFmt[0] == '(': + if n.text[0] == '(': i1 = 1 pre = "(" - if n.labelFmt[^1] == ')' or n.labelFmt[^1] == '.': - i2 = n.labelFmt.len - 2 - post = $n.labelFmt[^1] + if n.text[^1] == ')' or n.text[^1] == '.': + i2 = n.text.len-2 + post = $n.text[^1] let enumR = i1 .. i2 # enumerator range without surrounding (, ), . if d.target == outLatex: - result.add ("\n%" & n.labelFmt & "\n") + result.add ("\n%"&n.text&"\n") # use enumerate parameters from package enumitem - if n.labelFmt[i1].isDigit: + if n.text[i1].isDigit: var labelDef = "" if pre != "" or post != "": labelDef = "label=" & pre & "\\arabic*" & post & "," - if n.labelFmt[enumR] != "1": - specStart = "start=$1" % [n.labelFmt[enumR]] + if n.text[enumR] != "1": + specStart = "start=$1" % [n.text[enumR]] if labelDef != "" or specStart != "": specifier = "[$1$2]" % [labelDef, specStart] else: let (first, labelDef) = - if n.labelFmt[i1].isUpperAscii: ('A', "label=" & pre & "\\Alph*" & post) + if n.text[i1].isUpperAscii: ('A', "label=" & pre & "\\Alph*" & post) else: ('a', "label=" & pre & "\\alph*" & post) - if n.labelFmt[i1] != first: - specStart = ",start=" & $(ord(n.labelFmt[i1]) - ord(first) + 1) + if n.text[i1] != first: + specStart = ",start=" & $(ord(n.text[i1]) - ord(first) + 1) specifier = "[$1$2]" % [labelDef, specStart] else: # HTML # TODO: implement enumerator formatting using pre and post ( and ) for HTML - if n.labelFmt[i1].isDigit: - if n.labelFmt[enumR] != "1": - specStart = " start=\"$1\"" % [n.labelFmt[enumR]] + if n.text[i1].isDigit: + if n.text[enumR] != "1": + specStart = " start=\"$1\"" % [n.text[enumR]] specifier = "class=\"simple\"" & specStart else: let (first, labelDef) = - if n.labelFmt[i1].isUpperAscii: ('A', "class=\"upperalpha simple\"") + if n.text[i1].isUpperAscii: ('A', "class=\"upperalpha simple\"") else: ('a', "class=\"loweralpha simple\"") - if n.labelFmt[i1] != first: - specStart = " start=\"$1\"" % [ $(ord(n.labelFmt[i1]) - ord(first) + 1) ] + if n.text[i1] != first: + specStart = " start=\"$1\"" % [ $(ord(n.text[i1]) - ord(first) + 1) ] specifier = labelDef & specStart - renderAux(d, n, "$1\n", - "\\begin{enumerate}" & specifier & "$2$1\\end{enumerate}\n", + renderAux(d, n, "
      $1
    \n", + "\\begin{enumerate}" & specifier & "$1\\end{enumerate}\n", result) proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) = @@ -1052,95 +1084,57 @@ proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) = htmlCls = "admonition_warning" texSz = "\\large" texColor = "orange" - case n.adType + case n.text of "hint", "note", "tip": htmlCls = "admonition-info"; texSz = "\\normalsize"; texColor = "green" - of "attention", "admonition", "important", "warning", "caution": + of "attention", "admonition", "important", "warning": htmlCls = "admonition-warning"; texSz = "\\large"; texColor = "orange" of "danger", "error": htmlCls = "admonition-error"; texSz = "\\Large"; texColor = "red" else: discard - let txt = n.adType.capitalizeAscii() + let txt = n.text.capitalizeAscii() let htmlHead = "
    " renderAux(d, n, - htmlHead & "" & txt & + htmlHead & "" & txt & ":\n" & "$1
    \n", - "\n\n\\begin{rstadmonition}[borderline west={0.2em}{0pt}{" & - texColor & "}]$2\n" & + "\n\n\\begin{mdframed}[linecolor=" & texColor & "]\n" & "{" & texSz & "\\color{" & texColor & "}{\\textbf{" & txt & ":}}} " & - "$1\n\\end{rstadmonition}\n", + "$1\n\\end{mdframed}\n", result) -proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string, - external: bool, nimdoc = false, tooltip="") = - var linkStr = "" - block: - let mode = d.escMode - d.escMode = emUrl - renderRstToOut(d, link, linkStr) - d.escMode = mode - discard safeProtocol(linkStr) - var textStr = "" - renderRstToOut(d, text, textStr) - let nimDocStr = if nimdoc: " nimdoc" else: "" - var tooltipStr = "" - if tooltip != "": - tooltipStr = """ title="$1"""" % [ esc(d.target, tooltip) ] - if external: - dispA(d.target, result, - "$1", - "\\href{$2}{$1}", [textStr, linkStr, nimDocStr, tooltipStr]) - else: - dispA(d.target, result, - "$1", - "\\hyperlink{$2}{$1} (p.~\\pageref{$2})", - [textStr, linkStr, nimDocStr, tooltipStr]) - -proc traverseForIndex*(d: PDoc, n: PRstNode) = - ## A version of [renderRstToOut] that only fills entries for ``.idx`` files. - var discarded: string - if n == nil: return - case n.kind - of rnIdx: renderIndexTerm(d, n, discarded) - of rnHeadline, rnMarkdownHeadline: renderHeadline(d, n, discarded) - of rnOverline: renderOverline(d, n, discarded) - else: - for i in 0 ..< len(n): - traverseForIndex(d, n.sons[i]) - proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = if n == nil: return case n.kind of rnInner: renderAux(d, n, result) - of rnHeadline, rnMarkdownHeadline: renderHeadline(d, n, result) + of rnHeadline: renderHeadline(d, n, result) of rnOverline: renderOverline(d, n, result) - of rnTransition: renderAux(d, n, "\n", "\n\n\\vspace{0.6em}\\hrule$2\n", result) - of rnParagraph: renderAux(d, n, "$1

    \n", "\n\n$2\n$1\n\n", result) + of rnTransition: renderAux(d, n, "
    \n", "\\hrule\n", result) + of rnParagraph: renderAux(d, n, "

    $1

    \n", "$1\n\n", result) of rnBulletList: - renderAux(d, n, "$1\n", - "\\begin{itemize}\n$2\n$1\\end{itemize}\n", result) + renderAux(d, n, "
      $1
    \n", + "\\begin{itemize}$1\\end{itemize}\n", result) of rnBulletItem, rnEnumItem: - renderAux(d, n, "$1\n", "\\item $2$1\n", result) + renderAux(d, n, "
  • $1
  • \n", "\\item $1\n", result) of rnEnumList: renderEnumList(d, n, result) - of rnDefList, rnMdDefList: - renderAux(d, n, "$1\n", - "\\begin{description}\n$2\n$1\\end{description}\n", result) + of rnDefList: + renderAux(d, n, "
    $1
    \n", + "\\begin{description}$1\\end{description}\n", result) of rnDefItem: renderAux(d, n, result) - of rnDefName: renderAux(d, n, "$1\n", "$2\\item[$1]\\ ", result) - of rnDefBody: renderAux(d, n, "$1\n", "$2\n$1\n", result) + of rnDefName: renderAux(d, n, "
    $1
    \n", "\\item[$1] ", result) + of rnDefBody: renderAux(d, n, "
    $1
    \n", "$1\n", result) of rnFieldList: var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) if tmp.len != 0: dispA(d.target, result, - "" & + "" & "" & "" & "$1" & "
    ", - "\\begin{description}\n$2\n$1\\end{description}\n", - [tmp, n.anchor.idS]) + "\\begin{description}$1\\end{description}\n", + [tmp]) of rnField: renderField(d, n, result) of rnFieldName: renderAux(d, n, "$1:", @@ -1150,159 +1144,87 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnIndex: renderRstToOut(d, n.sons[2], result) of rnOptionList: - renderAux(d, n, "$1
    ", - "\\begin{rstoptlist}$2\n$1\\end{rstoptlist}", result) + renderAux(d, n, "$1
    ", + "\\begin{description}\n$1\\end{description}\n", result) of rnOptionListItem: - var addclass = if n.order mod 2 == 1: " odd" else: "" - renderAux(d, n, - "
    $1
    \n", - "$1", result) + renderAux(d, n, "$1\n", "$1", result) of rnOptionGroup: - renderAux(d, n, - "
    " & - "$1
    ", - "\\item[\\rstcodeitem{\\spanoption{$1}}]", result) + renderAux(d, n, "$1", "\\item[$1]", result) of rnDescription: - renderAux(d, n, "
    $1
    ", - " $1\n", result) + renderAux(d, n, "$1\n", " $1\n", result) of rnOption, rnOptionString, rnOptionArgument: doAssert false, "renderRstToOut" of rnLiteralBlock: - renderAux(d, n, "$1
    \n", - "\n\n$2\\begin{rstpre}\n$1\n\\end{rstpre}\n\n", result) - of rnMarkdownBlockQuote: - d.curQuotationDepth = 1 - var tmp = "" - renderAux(d, n, "$1", "$1", tmp) - let itemEnding = - if d.target == outHtml: "" else: "\\end{rstquote}" - tmp.add itemEnding.repeat(d.curQuotationDepth - 1) - dispA(d.target, result, - "$1\n", - "\n\\begin{rstquote}\n$2\n$1\\end{rstquote}\n", [tmp, n.anchor.idS]) - of rnMarkdownBlockQuoteItem: - let addQuotationDepth = n.quotationDepth - d.curQuotationDepth - var itemPrefix: string # start or ending (quotation grey bar on the left) - if addQuotationDepth >= 0: - let s = - if d.target == outHtml: "
    " - else: "\\begin{rstquote}" - itemPrefix = s.repeat(addQuotationDepth) - else: - let s = - if d.target == outHtml: "
    " - else: "\\end{rstquote}" - itemPrefix = s.repeat(-addQuotationDepth) - renderAux(d, n, itemPrefix & "

    $1

    ", itemPrefix & "\n$1", result) - d.curQuotationDepth = n.quotationDepth + renderAux(d, n, "
    $1
    \n", + "\\begin{rstpre}\n$1\n\\end{rstpre}\n", result) + of rnQuotedLiteralBlock: + doAssert false, "renderRstToOut" of rnLineBlock: - if n.sons.len == 1 and n.sons[0].lineIndent == "\n": + if n.sons.len == 1 and n.sons[0].text == "\n": # whole line block is one empty line, no need to add extra spacing - renderAux(d, n, "$1

    ", "\n\n$2\n$1", result) + renderAux(d, n, "

    $1

    ", "\n\n$1", result) else: # add extra spacing around the line block for Latex - renderAux(d, n, "$1

    ", - "\n\\vspace{0.5em}$2\n$1\\vspace{0.5em}\n", result) + renderAux(d, n, "

    $1

    ", "\n\\vspace{0.5em}\n$1\\vspace{0.5em}\n", result) of rnLineBlockItem: - if n.lineIndent.len == 0: # normal case - no additional indentation + if n.text.len == 0: # normal case - no additional indentation renderAux(d, n, "$1
    ", "\\noindent $1\n\n", result) - elif n.lineIndent == "\n": # add one empty line + elif n.text == "\n": # add one empty line renderAux(d, n, "
    ", "\\vspace{1em}\n", result) else: # additional indentation w.r.t. '| ' - let indent = $(0.5 * (n.lineIndent.len - 1).toFloat) & "em" + let indent = $(0.5 * (n.text.len - 1).toFloat) & "em" renderAux(d, n, "$1
    ", "\\noindent\\hspace{" & indent & "}$1\n\n", result) of rnBlockQuote: - renderAux(d, n, "

    $1

    \n", - "\\begin{quote}\n$2\n$1\\end{quote}\n", result) + renderAux(d, n, "

    $1

    \n", + "\\begin{quote}$1\\end{quote}\n", result) of rnAdmonition: renderAdmonition(d, n, result) of rnTable, rnGridTable, rnMarkdownTable: renderAux(d, n, - "$1", - "\n$2\n\\begin{rsttab}{" & - "L".repeat(n.colCount) & "}\n\\toprule\n$1" & - "\\addlinespace[0.1em]\\bottomrule\n\\end{rsttab}", result) + "$1
    ", + "\\begin{table}\\begin{rsttab}{" & + texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result) of rnTableRow: if len(n) >= 1: - case d.target - of outHtml: + 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: result.add("") renderAux(d, n, result) result.add("\n") - 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, - "
    " & - "
    \n$1
    \n", - "\n\n\\noindent\\rule{0.25\\linewidth}{.4pt}\n" & - "\\begin{rstfootnote}\n$1\\end{rstfootnote}\n\n", - result) - of rnFootnote, rnCitation: - var mark = "" - renderAux(d, n.sons[0], mark) - var body = "" - renderRstToOut(d, n.sons[1], body) + of rnTableDataCell: + renderAux(d, n, "$1", "$1", result) + of rnTableHeaderCell: + renderAux(d, n, "$1", "\\textbf{$1}", result) + of rnLabel: + doAssert false, "renderRstToOut" # used for footnotes and other + of rnFootnote: + doAssert false, "renderRstToOut" # a footnote + of rnCitation: + doAssert false, "renderRstToOut" # similar to footnote + of rnRef: + var tmp = "" + renderAux(d, n, tmp) dispA(d.target, result, - "
    " & - "[$3]" & - "
      $1\n\n", - "\\item[\\textsuperscript{[$3]}]$2 $1\n", - [body, n.anchor.idS, mark, n.anchor]) - 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) + "$1", + "$1\\ref{$2}", [tmp, rstnodeToRefname(n)]) of rnStandaloneHyperlink: - renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=true) - of rnInternalRef: - renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false) - of rnNimdocRef: - renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false, - nimdoc=true, tooltip=n.tooltip) + renderAux(d, n, + "$1", + "\\href{$1}{$1}", result) of rnHyperlink: - renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=true) - of rnFootnoteRef: - var tmp = "[" - renderAux(d, n.sons[0], tmp) - tmp.add "]" + var tmp0 = "" + var tmp1 = "" + renderRstToOut(d, n.sons[0], tmp0) + renderRstToOut(d, n.sons[1], tmp1) dispA(d.target, result, - "" & - "$1", - "\\textsuperscript{\\hyperlink{$2}{\\textbf{$1}}}", - [tmp, n.sons[1].text]) + "$1", + "\\href{$2}{$1}", [tmp0, tmp1]) of rnDirArg, rnRaw: renderAux(d, n, result) of rnRawHtml: if d.target != outLatex and not lastSon(n).isNil: @@ -1312,28 +1234,19 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = result.add addNodes(lastSon(n)) of rnImage, rnFigure: renderImage(d, n, result) - of rnCodeBlock, rnInlineCode: renderCode(d, n, result) + of rnCodeBlock: renderCodeBlock(d, n, result) of rnContainer: renderContainer(d, n, result) of rnSubstitutionReferences, rnSubstitutionDef: renderAux(d, n, "|$1|", "|$1|", result) of rnDirective: renderAux(d, n, "", "", result) - of rnUnknownRole, rnCodeFragment: + of rnGeneralRole: var tmp0 = "" var tmp1 = "" renderRstToOut(d, n.sons[0], tmp0) renderRstToOut(d, n.sons[1], tmp1) - var class = tmp1 - # don't allow missing role break latex compilation: - if d.target == outLatex and n.kind == rnUnknownRole: class = "Other" - if n.kind == rnCodeFragment: - dispA(d.target, result, - "" & - "$1", - "\\rstcode{\\span$2{$1}}", [tmp0, class]) - else: # rnUnknownRole, not necessarily code/monospace font - dispA(d.target, result, "$1", "\\span$2{$1}", - [tmp0, class]) + dispA(d.target, result, "$1", "\\span$2{$1}", + [tmp0, tmp1]) of rnSub: renderAux(d, n, "$1", "\\rstsub{$1}", result) of rnSup: renderAux(d, n, "$1", "\\rstsup{$1}", result) of rnEmphasis: renderAux(d, n, "$1", "\\emph{$1}", result) @@ -1347,22 +1260,13 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnInlineLiteral, rnInterpretedText: renderAux(d, n, "$1", - "\\rstcode{$1}", result) - of rnInlineTarget: - var tmp = "" - renderAux(d, n, tmp) - dispA(d.target, result, - "$1", - "\\label{$2}\\hypertarget{$2}{$1}", - [tmp, rstnodeToRefname(n)]) + "\\texttt{$1}", result) of rnSmiley: renderSmiley(d, n, result) - of rnLeaf: result.add(esc(d.target, n.text, escMode=d.escMode)) + of rnLeaf: result.add(esc(d.target, n.text)) of rnContents: d.hasToc = true - of rnDefaultRole: discard of rnTitle: d.meta[metaTitle] = "" renderRstToOut(d, n.sons[0], d.meta[metaTitle]) - d.meta[metaTitleRaw] = n.sons[0].addNodes # ----------------------------------------------------------------------------- @@ -1492,7 +1396,7 @@ $moduledesc $content """) - setConfigVar("doc.listing_start", "") + setConfigVar("doc.listing_start", "
    ")
       setConfigVar("doc.listing_end", "
    ") setConfigVar("doc.listing_button", "
    ") setConfigVar("doc.body_no_toc", "$moduledesc $content") @@ -1502,8 +1406,7 @@ $content # ---------- forum --------------------------------------------------------- proc rstToHtml*(s: string, options: RstParseOptions, - config: StringTableRef, - msgHandler: MsgHandler = rst.defaultMsgHandler): string {.gcsafe.} = + config: StringTableRef): string = ## Converts an input rst string into embeddable HTML. ## ## This convenience proc parses any input string using rst markup (it doesn't @@ -1513,13 +1416,12 @@ proc rstToHtml*(s: string, options: RstParseOptions, ## work. For an explanation of the ``config`` parameter see the ## ``initRstGenerator`` proc. Example: ## - ## ```nim + ## .. code-block:: nim ## import packages/docutils/rstgen, strtabs ## ## echo rstToHtml("*Hello* **world**!", {}, ## newStringTable(modeStyleInsensitive)) ## # --> Hello world! - ## ``` ## ## If you need to allow the rst ``include`` directive or tweak the generated ## output you have to create your own ``RstGenerator`` with @@ -1528,30 +1430,22 @@ proc rstToHtml*(s: string, options: RstParseOptions, proc myFindFile(filename: string): string = # we don't find any files in online mode: result = "" - proc myFindRefFile(filename: string): (string, string) = - result = ("", "") const filen = "input" - let (rst, filenames, t) = rstParse(s, filen, - line=LineRstInit, column=ColRstInit, - options, myFindFile, myFindRefFile, msgHandler) var d: RstGenerator - initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler, - filenames, hasToc = t) + initRstGenerator(d, outHtml, config, filen, options, myFindFile, + rst.defaultMsgHandler) + var dummyHasToc = false + var rst = rstParse(s, filen, 0, 1, dummyHasToc, options) result = "" renderRstToOut(d, rst, result) - strbasics.strip(result) proc rstToLatex*(rstSource: string; options: RstParseOptions): string {.inline, since: (1, 3).} = ## Convenience proc for `renderRstToOut` and `initRstGenerator`. runnableExamples: doAssert rstToLatex("*Hello* **world**", {}) == """\emph{Hello} \textbf{world}""" if rstSource.len == 0: return - let (rst, filenames, t) = rstParse(rstSource, "", - line=LineRstInit, column=ColRstInit, - options) + var option: bool var rstGenera: RstGenerator - rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", - filenames=filenames, hasToc = t) - rstGenera.renderRstToOut(rst, result) - strbasics.strip(result) + rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", options) + rstGenera.renderRstToOut(rstParse(rstSource, "", 1, 1, option, options), result) diff --git a/lib/packages/docutils/rstidx.nim b/lib/packages/docutils/rstidx.nim deleted file mode 100644 index c109636d7..000000000 --- a/lib/packages/docutils/rstidx.nim +++ /dev/null @@ -1,138 +0,0 @@ -# -# Nim's Runtime Library -# (c) Copyright 2022 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. - -## Nim `idx`:idx: file format related definitions. - -import strutils, std/syncio, hashes -from os import splitFile - -type - IndexEntryKind* = enum ## discriminator tag - ieMarkupTitle = "markupTitle" - ## RST/Markdown title, text in `keyword` + - ## HTML text in `linkTitle` - ieNimTitle = "nimTitle" - ## Nim title - ieHeading = "heading" ## RST/Markdown markup heading, escaped - ieIdxRole = "idx" ## RST :idx: definition, escaped - ieNim = "nim" ## Nim symbol, unescaped - ieNimGroup = "nimgrp" ## Nim overload group, unescaped - IndexEntry* = object - kind*: IndexEntryKind ## 0. - keyword*: string ## 1. - link*: string ## 2. - linkTitle*: string ## 3. contains a prettier text for the href - linkDesc*: string ## 4. the title attribute of the final href - line*: int ## 5. - module*: string ## origin file, NOT a field in ``.idx`` file - aux*: string ## auxuliary field, NOT a field in ``.idx`` file - -proc isDocumentationTitle*(hyperlink: string): bool = - ## Returns true if the hyperlink is actually a documentation title. - ## - ## Documentation titles lack the hash. See `mergeIndexes() - ## <#mergeIndexes,string>`_ for a more detailed explanation. - result = hyperlink.find('#') < 0 - -proc `$`*(e: IndexEntry): string = - """("$1", "$2", "$3", "$4", $5)""" % [ - e.keyword, e.link, e.linkTitle, e.linkDesc, $e.line] - -proc quoteIndexColumn(text: string): string = - ## Returns a safe version of `text` for serialization to the ``.idx`` file. - ## - ## The returned version can be put without worries in a line based tab - ## separated column text file. The following character sequence replacements - ## will be performed for that goal: - ## - ## * ``"\\"`` => ``"\\\\"`` - ## * ``"\n"`` => ``"\\n"`` - ## * ``"\t"`` => ``"\\t"`` - result = newStringOfCap(text.len + 3) - for c in text: - case c - of '\\': result.add "\\" - of '\L': result.add "\\n" - of '\C': discard - of '\t': result.add "\\t" - else: result.add c - -proc unquoteIndexColumn*(text: string): string = - ## Returns the unquoted version generated by ``quoteIndexColumn``. - result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\")) - -proc formatIndexEntry*(kind: IndexEntryKind; htmlFile, id, term, linkTitle, - linkDesc: string, line: int): - tuple[entry: string, isTitle: bool] = - result.entry = $kind - result.entry.add('\t') - result.entry.add term - result.entry.add('\t') - result.entry.add(htmlFile) - if id.len > 0: - result.entry.add('#') - result.entry.add(id) - result.isTitle = false - else: - result.isTitle = true - result.entry.add('\t' & linkTitle.quoteIndexColumn) - result.entry.add('\t' & linkDesc.quoteIndexColumn) - result.entry.add('\t' & $line) - result.entry.add("\n") - -proc parseIndexEntryKind(s: string): IndexEntryKind = - result = case s: - of "nim": ieNim - of "nimgrp": ieNimGroup - of "heading": ieHeading - of "idx": ieIdxRole - of "nimTitle": ieNimTitle - of "markupTitle": ieMarkupTitle - else: raise newException(ValueError, "unknown index entry value $1" % [s]) - -proc parseIdxFile*(path: string): - tuple[fileEntries: seq[IndexEntry], title: IndexEntry] = - var - f = 0 - newSeq(result.fileEntries, 500) - setLen(result.fileEntries, 0) - let (_, base, _) = path.splitFile - for line in lines(path): - let s = line.find('\t') - if s < 0: continue - setLen(result.fileEntries, f+1) - let cols = line.split('\t') - result.fileEntries[f].kind = parseIndexEntryKind(cols[0]) - result.fileEntries[f].keyword = cols[1] - result.fileEntries[f].link = cols[2] - if result.title.keyword.len == 0: - result.fileEntries[f].module = base - else: - result.fileEntries[f].module = result.title.keyword - - result.fileEntries[f].linkTitle = cols[3].unquoteIndexColumn - result.fileEntries[f].linkDesc = cols[4].unquoteIndexColumn - result.fileEntries[f].line = parseInt(cols[5]) - - if result.fileEntries[f].kind in {ieNimTitle, ieMarkupTitle}: - result.title = result.fileEntries[f] - inc f - -proc cmp*(a, b: IndexEntry): int = - ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`. - result = cmpIgnoreStyle(a.keyword, b.keyword) - if result == 0: - result = cmpIgnoreStyle(a.link, b.link) - -proc hash*(x: IndexEntry): Hash = - ## Returns the hash for the combined fields of the type. - ## - ## The hash is computed as the chained hash of the individual string hashes. - result = x.keyword.hash !& x.link.hash - result = result !& x.linkTitle.hash - result = result !& x.linkDesc.hash - result = !$result diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index 7ee062e4f..9f0cf75ed 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -33,28 +33,19 @@ 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: "epoll_data_t", - header: "", pure, final, union.} = object - `ptr`* {.importc: "ptr".}: pointer - fd* {.importc: "fd".}: cint - u32* {.importc: "u32".}: uint32 + EpollData* {.importc: "union epoll_data", + header: "", pure, final.} = object # TODO: This is actually a union. u64* {.importc: "u64".}: uint64 - EpollEvent* {.importc: "struct epoll_event", header: "", pure, final, epollPacked.} = object + EpollEvent* {.importc: "struct epoll_event", header: "", pure, final.} = 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(). @@ -66,10 +57,10 @@ proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1", proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr EpollEvent): cint {. 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_*` + ## 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 ## is interested in and any associated user data. diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim index 109d3680e..db698c59c 100644 --- a/lib/posix/inotify.nim +++ b/lib/posix/inotify.nim @@ -7,9 +7,6 @@ # distribution, for details about the copyright. # -when defined(nimPreviewSlimSystem): - import std/syncio - # Get the platform-dependent flags. # Structure describing an inotify event. type @@ -78,11 +75,11 @@ proc inotify_rm_watch*(fd: cint; wd: cint): cint {.cdecl, iterator inotify_events*(evs: pointer, n: int): ptr InotifyEvent = ## Abstract the packed buffer interface to yield event object pointers. - ## ```Nim + ## + ## .. code-block:: Nim ## var evs = newSeq[byte](8192) # Already did inotify_init+add_watch ## while (let n = read(fd, evs[0].addr, 8192); n) > 0: # read forever ## for e in inotify_events(evs[0].addr, n): echo e[].len # echo name lens - ## ``` var ev: ptr InotifyEvent = cast[ptr InotifyEvent](evs) var n = n while n > 0: diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim index c83ae33ea..18b47f5d5 100644 --- a/lib/posix/kqueue.nim +++ b/lib/posix/kqueue.nim @@ -153,7 +153,7 @@ proc kevent*(kqFD: cint, changelist: ptr KEvent, nchanges: cint, eventlist: ptr KEvent, nevents: cint, timeout: ptr Timespec): cint {.importc: "kevent", header: "".} - ## Manipulates queue for given `kqFD` descriptor. + ## Manipulates queue for given ``kqFD`` descriptor. proc EV_SET*(event: ptr KEvent, ident: uint, filter: cshort, flags: cushort, fflags: cuint, data: int, udata: pointer) diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim index 9a5e9f062..5ce9bf2fb 100644 --- a/lib/posix/linux.nim +++ b/lib/posix/linux.nim @@ -29,9 +29,9 @@ const CLONE_NEWPID* = 0x20000000'i32 CLONE_NEWNET* = 0x40000000'i32 CLONE_IO* = 0x80000000'i32 + CLONE_STOPPED* {.deprecated.} = 0x02000000'i32 - -# fn should be of type proc (a2: pointer) {.cdecl.} +# fn should be of type proc (a2: pointer): void {.cdecl.} proc clone*(fn: pointer; child_stack: pointer; flags: cint; arg: pointer; ptid: ptr Pid; tls: pointer; ctid: ptr Pid): cint {.importc, header: "".} diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 12fc8fd57..c7a324cde 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -27,7 +27,7 @@ ## the \`identifier\` notation is used. ## ## This library relies on the header files of your C compiler. The -## resulting C code will just `#include ` and *not* define the +## resulting C code will just ``#include `` and *not* define the ## symbols declared here. # Dead code elimination ensures that we don't accidentally generate #includes @@ -37,9 +37,6 @@ when defined(nimHasStyleChecks): {.push styleChecks: off.} -when defined(nimPreviewSlimSystem): - import std/syncio - # TODO these constants don't seem to be fetched from a header file for unknown # platforms - where do they come from and why are they here? when false: @@ -93,8 +90,8 @@ type Sighandler = proc (a: cint) {.noconv.} const StatHasNanoseconds* = defined(linux) or defined(freebsd) or defined(osx) or defined(openbsd) or defined(dragonfly) or defined(haiku) ## \ ## Boolean flag that indicates if the system supports nanosecond time - ## resolution in the fields of `Stat`. Note that the nanosecond based fields - ## (`Stat.st_atim`, `Stat.st_mtim` and `Stat.st_ctim`) can be accessed + ## resolution in the fields of ``Stat``. Note that the nanosecond based fields + ## (``Stat.st_atim``, ``Stat.st_mtim`` and ``Stat.st_ctim``) can be accessed ## without checking this flag, because this module defines fallback procs ## when they are not available. @@ -154,13 +151,11 @@ proc htons*(a1: uint16): uint16 {.importc, header: "".} proc ntohl*(a1: uint32): uint32 {.importc, header: "".} proc ntohs*(a1: uint16): uint16 {.importc, header: "".} -when not defined(zephyr): - proc inet_addr*(a1: cstring): InAddrT {.importc, header: "".} - proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "".} - -proc inet_ntop*(a1: cint, a2: pointer | ptr InAddr | ptr In6Addr, a3: cstring, a4: int32): cstring {. +proc inet_addr*(a1: cstring): InAddrT {.importc, header: "".} +proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "".} +proc inet_ntop*(a1: cint, a2: pointer, a3: cstring, a4: int32): cstring {. importc:"(char *)$1", header: "".} -proc inet_pton*(a1: cint, a2: cstring, a3: pointer | ptr InAddr | ptr In6Addr): cint {. +proc inet_pton*(a1: cint, a2: cstring, a3: pointer): cint {. importc, header: "".} var @@ -188,38 +183,13 @@ proc dlsym*(a1: pointer, a2: cstring): pointer {.importc, header: "", s proc creat*(a1: cstring, a2: Mode): cint {.importc, header: "", sideEffect.} proc fcntl*(a1: cint | SocketHandle, a2: cint): cint {.varargs, importc, header: "", sideEffect.} -proc openImpl(a1: cstring, a2: cint): cint {.varargs, importc: "open", header: "", sideEffect.} -proc open*(a1: cstring, a2: cint, mode: Mode | cint = 0.Mode): cint {.inline.} = - # prevents bug #17888 - openImpl(a1, a2, mode) - +proc open*(a1: cstring, a2: cint): cint {.varargs, importc, header: "", sideEffect.} proc posix_fadvise*(a1: cint, a2, a3: Off, a4: cint): cint {. importc, header: "".} +proc posix_fallocate*(a1: cint, a2, a3: Off): cint {. + importc, header: "".} -proc ftruncate*(a1: cint, a2: Off): cint {.importc, header: "".} -when defined(osx): # 2001 POSIX evidently does not concern Apple - type FStore {.importc: "fstore_t", header: "", bycopy.} = object - fst_flags: uint32 ## IN: flags word - fst_posmode: cint ## IN: indicates offset field - fst_offset, ## IN: start of the region - fst_length, ## IN: size of the region - fst_bytesalloc: Off ## OUT: number of bytes allocated - var F_PEOFPOSMODE {.importc, header: "".}: cint - var F_ALLOCATEALL {.importc, header: "".}: uint32 - var F_PREALLOCATE {.importc, header: "".}: cint - proc posix_fallocate*(a1: cint, a2, a3: Off): cint = - var fst = FStore(fst_flags: F_ALLOCATEALL, fst_posmode: F_PEOFPOSMODE, - fst_offset: a2, fst_length: a3) - # Must also call ftruncate to match what POSIX does. Unlike posix_fallocate, - # this can shrink files. Could guard w/getFileSize, but caller likely knows - # present size & has no good reason to call this unless it is growing. - if fcntl(a1, F_PREALLOCATE, fst.addr) != cint(-1): ftruncate(a1, a2 + a3) - else: cint(-1) -else: - proc posix_fallocate*(a1: cint, a2, a3: Off): cint {. - importc, header: "".} - -when not defined(haiku) and not defined(openbsd): +when not defined(haiku) and not defined(OpenBSD): proc fmtmsg*(a1: int, a2: cstring, a3: cint, a4, a5, a6: cstring): cint {.importc, header: "".} @@ -270,52 +240,26 @@ proc setlocale*(a1: cint, a2: cstring): cstring {. proc strfmon*(a1: cstring, a2: int, a3: cstring): int {.varargs, importc, header: "".} -when not (defined(nintendoswitch) or defined(macos) or defined(macosx)): - proc mq_notify*(mqdes: Mqd, event: ptr SigEvent): cint {. +when not defined(nintendoswitch): + proc mq_close*(a1: Mqd): cint {.importc, header: "".} + proc mq_getattr*(a1: Mqd, a2: ptr MqAttr): cint {. importc, header: "".} - - proc mq_open*(name: cstring, flags: cint): Mqd {. + proc mq_notify*(a1: Mqd, a2: ptr SigEvent): cint {. + importc, header: "".} + proc mq_open*(a1: cstring, a2: cint): Mqd {. varargs, importc, header: "".} - - proc mq_close*(mqdes: Mqd): cint {.importc, header: "".} - - proc mq_receive*( - mqdes: Mqd, - buffer: cstring, - length: csize_t, - priority: var cuint - ): int {.importc, header: "".} - - proc mq_timedreceive*( - mqdes: Mqd, - buffer: cstring, - length: csize_t, - priority: cuint, - timeout: ptr Timespec - ): int {.importc, header: "".} - - proc mq_send*( - mqdes: Mqd, - buffer: cstring, - length: csize_t, - priority: cuint - ): cint {.importc, header: "".} - - proc mq_timedsend*( - mqdes: Mqd, - buffer: cstring, - length: csize_t, - priority: cuint, - timeout: ptr Timespec - ): cint {.importc, header: "".} - - proc mq_getattr*(mqdes: Mqd, attribute: ptr MqAttr): cint {. + proc mq_receive*(a1: Mqd, a2: cstring, a3: int, a4: var int): int {. + importc, header: "".} + proc mq_send*(a1: Mqd, a2: cstring, a3: int, a4: int): cint {. + importc, header: "".} + proc mq_setattr*(a1: Mqd, a2, a3: ptr MqAttr): cint {. importc, header: "".} - proc mq_setattr*(mqdes: Mqd, newAttribute, oldAttribute: ptr MqAttr): cint {. - importc, header: "".} - - proc mq_unlink*(mqdes: cstring): cint {.importc, header: "".} + proc mq_timedreceive*(a1: Mqd, a2: cstring, a3: int, a4: int, + a5: ptr Timespec): int {.importc, header: "".} + proc mq_timedsend*(a1: Mqd, a2: cstring, a3: int, a4: int, + a5: ptr Timespec): cint {.importc, header: "".} + proc mq_unlink*(a1: cstring): cint {.importc, header: "".} proc getpwnam*(a1: cstring): ptr Passwd {.importc, header: "".} @@ -503,7 +447,7 @@ proc pthread_spin_unlock*(a1: ptr Pthread_spinlock): cint {. proc pthread_testcancel*() {.importc, header: "".} -proc exitnow*(code: int) {.importc: "_exit", header: "".} +proc exitnow*(code: int): void {.importc: "_exit", header: "".} proc access*(a1: cstring, a2: cint): cint {.importc, header: "".} proc alarm*(a1: cint): cint {.importc, header: "".} proc chdir*(a1: cstring): cint {.importc, header: "".} @@ -532,6 +476,7 @@ proc fpathconf*(a1, a2: cint): int {.importc, header: "".} proc fsync*(a1: cint): cint {.importc, header: "".} ## synchronize a file's buffer cache to the storage device +proc ftruncate*(a1: cint, a2: Off): cint {.importc, header: "".} proc getcwd*(a1: cstring, a2: int): cstring {.importc, header: "", sideEffect.} proc getuid*(): Uid {.importc, header: "", sideEffect.} ## returns the real user ID of the calling process @@ -584,8 +529,7 @@ proc pread*(a1: cint, a2: pointer, a3: int, a4: Off): int {. proc pwrite*(a1: cint, a2: pointer, a3: int, a4: Off): int {. importc, header: "".} proc read*(a1: cint, a2: pointer, a3: int): int {.importc, header: "".} -when not defined(nintendoswitch): - proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "".} +proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "".} proc ioctl*(f: FileHandle, device: uint): int {.importc: "ioctl", header: "", varargs, tags: [WriteIOEffect].} ## A system call for device-specific input/output operations and other @@ -604,10 +548,7 @@ proc setsid*(): Pid {.importc, header: "".} proc setuid*(a1: Uid): cint {.importc, header: "".} proc sleep*(a1: cint): cint {.importc, header: "".} proc swab*(a1, a2: pointer, a3: int) {.importc, header: "".} -when not defined(nintendoswitch): - proc symlink*(a1, a2: cstring): cint {.importc, header: "".} -else: - proc symlink*(a1, a2: cstring): cint = -1 +proc symlink*(a1, a2: cstring): cint {.importc, header: "".} proc sync*() {.importc, header: "".} proc sysconf*(a1: cint): int {.importc, header: "".} proc tcgetpgrp*(a1: cint): Pid {.importc, header: "".} @@ -645,8 +586,6 @@ proc fstatvfs*(a1: cint, a2: var Statvfs): cint {. importc, header: "".} proc chmod*(a1: cstring, a2: Mode): cint {.importc, header: "", sideEffect.} -when defined(osx) or defined(freebsd): - proc lchmod*(a1: cstring, a2: Mode): cint {.importc, header: "", sideEffect.} proc fchmod*(a1: cint, a2: Mode): cint {.importc, header: "", sideEffect.} proc fstat*(a1: cint, a2: var Stat): cint {.importc, header: "", sideEffect.} proc lstat*(a1: cstring, a2: var Stat): cint {.importc, header: "", sideEffect.} @@ -825,13 +764,6 @@ else: proc sigtimedwait*(a1: var Sigset, a2: var SigInfo, a3: var Timespec): cint {.importc, header: "".} -when defined(sunos) or defined(solaris): - # The following compile time flag is needed on Illumos/Solaris to use the POSIX - # `sigwait` implementation. See the documentation here: - # https://docs.oracle.com/cd/E19455-01/806-5257/6je9h033k/index.html - # https://www.illumos.org/man/2/sigwait - {.passc: "-D_POSIX_PTHREAD_SEMANTICS".} - proc sigwait*(a1: var Sigset, a2: var cint): cint {. importc, header: "".} proc sigwaitinfo*(a1: var Sigset, a2: var SigInfo): cint {. @@ -944,9 +876,15 @@ proc CMSG_NXTHDR*(mhdr: ptr Tmsghdr, cmsg: ptr Tcmsghdr): ptr Tcmsghdr {. proc CMSG_FIRSTHDR*(mhdr: ptr Tmsghdr): ptr Tcmsghdr {. importc, header: "".} +proc CMSG_SPACE*(len: csize): csize {. + importc, header: "", deprecated: "argument `len` should be of type `csize_t`".} + proc CMSG_SPACE*(len: csize_t): csize_t {. importc, header: "".} +proc CMSG_LEN*(len: csize): csize {. + importc, header: "", deprecated: "argument `len` should be of type `csize_t`".} + proc CMSG_LEN*(len: csize_t): csize_t {. importc, header: "".} @@ -964,7 +902,7 @@ when defined(linux) or defined(bsd): proc bindSocket*(a1: SocketHandle, a2: ptr SockAddr, a3: SockLen): cint {. importc: "bind", header: "".} - ## is Posix's `bind`, because `bind` is a reserved word + ## is Posix's ``bind``, because ``bind`` is a reserved word proc connect*(a1: SocketHandle, a2: ptr SockAddr, a3: SockLen): cint {. importc, header: "".} @@ -1056,7 +994,7 @@ proc endhostent*() {.importc, header: "".} proc endnetent*() {.importc, header: "".} proc endprotoent*() {.importc, header: "".} proc endservent*() {.importc, header: "".} -proc freeAddrInfo*(a1: ptr AddrInfo) {.importc: "freeaddrinfo", header: "".} +proc freeaddrinfo*(a1: ptr AddrInfo) {.importc, header: "".} proc gai_strerror*(a1: cint): cstring {.importc:"(char *)$1", header: "".} @@ -1104,16 +1042,16 @@ proc realpath*(name, resolved: cstring): cstring {. proc mkstemp*(tmpl: cstring): cint {.importc, header: "", sideEffect.} ## Creates a unique temporary file. ## - ## .. warning:: The `tmpl` argument is written to by `mkstemp` and thus - ## can't be a string literal. If in doubt make a copy of the cstring before - ## passing it in. + ## **Warning**: The `tmpl` argument is written to by `mkstemp` and thus + ## can't be a string literal. If in doubt make a copy of the cstring before + ## passing it in. proc mkstemps*(tmpl: cstring, suffixlen: int): cint {.importc, header: "", sideEffect.} ## Creates a unique temporary file. ## - ## .. warning:: The `tmpl` argument is written to by `mkstemps` and thus - ## can't be a string literal. If in doubt make a copy of the cstring before - ## passing it in. + ## **Warning**: The `tmpl` argument is written to by `mkstemps` and thus + ## can't be a string literal. If in doubt make a copy of the cstring before + ## passing it in. proc mkdtemp*(tmpl: cstring): pointer {.importc, header: "", sideEffect.} @@ -1139,15 +1077,15 @@ proc handle_signal(sig: cint, handler: proc (a: cint) {.noconv.}) {.importc: "si template onSignal*(signals: varargs[cint], body: untyped) = ## Setup code to be executed when Unix signals are received. The - ## currently handled signal is injected as `sig` into the calling + ## currently handled signal is injected as ``sig`` into the calling ## scope. ## ## Example: - ## ```Nim - ## from std/posix import SIGINT, SIGTERM, onSignal + ## + ## .. code-block:: + ## from posix import SIGINT, SIGTERM, onSignal ## onSignal(SIGINT, SIGTERM): ## echo "bye from signal ", sig - ## ``` for s in signals: handle_signal(s, @@ -1164,12 +1102,12 @@ type ## The getrlimit() and setrlimit() system calls get and set resource limits respectively. ## Each resource has an associated soft and hard limit, as defined by the RLimit structure -proc setrlimit*(resource: cint, rlp: var RLimit): cint {. - importc: "setrlimit", header: "".} +proc setrlimit*(resource: cint, rlp: var RLimit): cint + {.importc: "setrlimit",header: "".} ## The setrlimit() system calls sets resource limits. -proc getrlimit*(resource: cint, rlp: var RLimit): cint {. - importc: "getrlimit", header: "".} +proc getrlimit*(resource: cint, rlp: var RLimit): cint + {.importc: "getrlimit",header: "".} ## The getrlimit() system call gets resource limits. when defined(nimHasStyleChecks): diff --git a/lib/posix/posix_freertos_consts.nim b/lib/posix/posix_freertos_consts.nim index 0f0fc0aae..efe87aacf 100644 --- a/lib/posix/posix_freertos_consts.nim +++ b/lib/posix/posix_freertos_consts.nim @@ -363,8 +363,6 @@ var SEM_FAILED* {.importc: "SEM_FAILED", header: "".}: pointer # # var RLIMIT_NOFILE* {.importc: "RLIMIT_NOFILE", header: "".}: cint -var FD_MAX* {.importc: "CONFIG_LWIP_MAX_SOCKETS", header: "".}: cint - # var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "".}: cint @@ -500,7 +498,3 @@ 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 32a6d24e2..eaf2cfb85 100644 --- a/lib/posix/posix_haiku.nim +++ b/lib/posix/posix_haiku.nim @@ -58,7 +58,7 @@ type d_type*: int8 ## Type of file; not supported by all filesystem types. ## (not POSIX) when defined(linux) or defined(openbsd): - d_off*: Off ## Not an offset. Value that `telldir()` would return. + d_off*: Off ## Not an offset. Value that ``telldir()`` would return. elif defined(haiku): d_pino*: Ino ## Parent inode (only for queries) (not POSIX) d_reclen*: cushort ## Length of this record. (not POSIX) @@ -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_t ## Stack size. + ss_size*: csize ## 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_t ## The size of the memory pointed to by iov_base. + iov_len*: csize ## The size of the memory pointed to by iov_base. Tmsghdr* {.importc: "struct msghdr", pure, final, header: "".} = object ## struct msghdr @@ -551,7 +551,7 @@ else: var SO_REUSEPORT* {.importc, header: "".}: cint when defined(macosx): - # We can't use the NOSIGNAL flag in the `send` function, it has no effect + # We can't use the NOSIGNAL flag in the ``send`` function, it has no effect # Instead we should use SO_NOSIGPIPE in setsockopt const MSG_NOSIGNAL* = 0'i32 diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 4eb357d62..7f6a589f0 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -47,7 +47,7 @@ type d_ino*: Ino d_off*: Off d_reclen*: cushort - d_type*: int8 # uint8 really! + d_type*: int8 # cuchar really! d_name*: array[256, cchar] Tflock* {.importc: "struct flock", final, pure, @@ -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[40 div sizeof(clong), clong] + abi: array[48 div sizeof(clong), clong] Pthread_mutexattr* {.importc: "pthread_mutexattr_t", header: "", pure, final.} = object abi: array[4 div sizeof(cint), cint] @@ -334,7 +334,7 @@ type si_status*: cint ## Exit value or signal. si_band*: int ## Band event for SIGPOLL. si_value*: SigVal ## Signal value. - pad {.importc: "_pad".}: array[128 - 56, uint8] + pad {.importc: "_pad"}: array[128 - 56, uint8] Nl_item* {.importc: "nl_item", header: "".} = cint Nl_catd* {.importc: "nl_catd", header: "".} = pointer @@ -428,8 +428,8 @@ type header: "", pure, final.} = object ## struct sockaddr_storage ss_family*: TSa_Family ## Address family. - ss_padding {.importc: "__ss_padding".}: array[128 - sizeof(cshort) - sizeof(culong), char] - ss_align {.importc: "__ss_align".}: clong + ss_padding: array[128 - sizeof(cshort) - sizeof(culong), char] + ss_align: clong Tif_nameindex* {.importc: "struct if_nameindex", final, pure, header: "".} = object ## struct if_nameindex diff --git a/lib/posix/posix_macos_amd64.nim b/lib/posix/posix_macos_amd64.nim index d6b5834cb..94c08acad 100644 --- a/lib/posix/posix_macos_amd64.nim +++ b/lib/posix/posix_macos_amd64.nim @@ -45,7 +45,7 @@ type d_type*: int8 ## Type of file; not supported by all filesystem types. ## (not POSIX) when defined(linux) or defined(openbsd): - d_off*: Off ## Not an offset. Value that `telldir()` would return. + d_off*: Off ## Not an offset. Value that ``telldir()`` would return. elif defined(haiku): d_pino*: Ino ## Parent inode (only for queries) (not POSIX) d_reclen*: cushort ## Length of this record. (not POSIX) @@ -108,6 +108,15 @@ type p_sign_posn*: char thousands_sep*: cstring + Mqd* {.importc: "mqd_t", header: "", final, pure.} = object + MqAttr* {.importc: "struct mq_attr", + header: "", + final, pure.} = object ## message queue attribute + mq_flags*: int ## Message queue flags. + mq_maxmsg*: int ## Maximum number of messages. + mq_msgsize*: int ## Maximum message size. + mq_curmsgs*: int ## Number of messages currently queued. + Passwd* {.importc: "struct passwd", header: "", final, pure.} = object ## struct passwd pw_name*: cstring ## User's login name. @@ -366,18 +375,6 @@ when hasSpawnH: Tposix_spawn_file_actions* {.importc: "posix_spawn_file_actions_t", header: "", final, pure.} = object - -when not defined(macos) and not defined(macosx): # freebsd - type - Mqd* {.importc: "mqd_t", header: "", final, pure.} = object - MqAttr* {.importc: "struct mq_attr", - header: "", - final, pure.} = object ## message queue attribute - mq_flags*: int ## Message queue flags. - mq_maxmsg*: int ## Maximum number of messages. - mq_msgsize*: int ## Maximum message size. - mq_curmsgs*: int ## Number of messages currently queued. - when defined(linux): # from sys/un.h const Sockaddr_un_path_length* = 108 @@ -564,7 +561,7 @@ when defined(linux) or defined(bsd): var SOCK_CLOEXEC* {.importc, header: "".}: cint when defined(macosx): - # We can't use the NOSIGNAL flag in the `send` function, it has no effect + # We can't use the NOSIGNAL flag in the ``send`` function, it has no effect # Instead we should use SO_NOSIGPIPE in setsockopt const MSG_NOSIGNAL* = 0'i32 diff --git a/lib/posix/posix_nintendoswitch.nim b/lib/posix/posix_nintendoswitch.nim index b66563695..44e431437 100644 --- a/lib/posix/posix_nintendoswitch.nim +++ b/lib/posix/posix_nintendoswitch.nim @@ -33,7 +33,7 @@ type Dirent* {.importc: "struct dirent", header: "", final, pure.} = object ## dirent_t struct d_ino*: Ino - d_type*: int8 # uint8 really! + d_type*: int8 # cuchar really! d_name*: array[256, cchar] Tflock* {.importc: "struct flock", final, pure, @@ -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_t ## Stack size. + ss_size*: csize ## 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_t ## Length of transfer. + aio_nbytes*: csize ## Length of transfer. aio_sigevent*: SigEvent ## Signal number and value. next_prio: pointer abs_prio: cint @@ -347,20 +347,20 @@ type SockAddr* {.importc: "struct sockaddr", header: "", pure, final.} = object ## struct sockaddr - sa_len: uint8 + sa_len: cuchar sa_family*: TSa_Family ## Address family. sa_data*: array[14, char] ## Socket address (variable-length data). Sockaddr_storage* {.importc: "struct sockaddr_storage", header: "", pure, final.} = object ## struct sockaddr_storage - ss_len: uint8 + ss_len: cuchar ss_family*: TSa_Family ## Address family. - ss_padding1: array[64 - sizeof(uint8) - sizeof(cshort), char] + ss_padding1: array[64 - sizeof(cuchar) - sizeof(cshort), char] ss_align: clonglong ss_padding2: array[ - 128 - sizeof(uint8) - sizeof(cshort) - - (64 - sizeof(uint8) - sizeof(cshort)) - 64, char] + 128 - sizeof(cuchar) - sizeof(cshort) - + (64 - sizeof(cuchar) - sizeof(cshort)) - 64, char] Tif_nameindex* {.importc: "struct if_nameindex", final, pure, header: "".} = object ## struct if_nameindex @@ -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_t ## Members in msg_iov. + msg_iovlen*: csize ## Members in msg_iov. msg_control*: pointer ## Ancillary data; see below. - msg_controllen*: csize_t ## Ancillary data buffer len. + msg_controllen*: csize ## Ancillary data buffer len. msg_flags*: cint ## Flags on received message. Tcmsghdr* {.importc: "struct cmsghdr", pure, final, header: "".} = object ## struct cmsghdr - cmsg_len*: csize_t ## Data byte count, including the cmsghdr. + cmsg_len*: csize ## Data byte count, including the cmsghdr. cmsg_level*: cint ## Originating protocol. cmsg_type*: cint ## Protocol-specific type. diff --git a/lib/posix/posix_openbsd_amd64.nim b/lib/posix/posix_openbsd_amd64.nim index 1ef4a4182..584d06501 100644 --- a/lib/posix/posix_openbsd_amd64.nim +++ b/lib/posix/posix_openbsd_amd64.nim @@ -45,7 +45,7 @@ type d_type*: int8 ## Type of file; not supported by all filesystem types. ## (not POSIX) when defined(linux) or defined(openbsd): - d_off*: Off ## Not an offset. Value that `telldir()` would return. + d_off*: Off ## Not an offset. Value that ``telldir()`` would return. elif defined(haiku): d_pino*: Ino ## Parent inode (only for queries) (not POSIX) d_reclen*: cushort ## Length of this record. (not POSIX) diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 1b6734b51..a5eb32d22 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -10,7 +10,7 @@ when defined(nimHasStyleChecks): {.push styleChecks: off.} -when defined(freertos) or defined(zephyr) or defined(nuttx): +when defined(freertos): const hasSpawnH = false # should exist for every Posix system nowadays hasAioH = false @@ -64,7 +64,7 @@ type d_type*: int8 ## Type of file; not supported by all filesystem types. ## (not POSIX) when defined(linux) or defined(openbsd): - d_off*: Off ## Not an offset. Value that `telldir()` would return. + d_off*: Off ## Not an offset. Value that ``telldir()`` would return. elif defined(haiku): d_pino*: Ino ## Parent inode (only for queries) (not POSIX) d_reclen*: cushort ## Length of this record. (not POSIX) @@ -391,33 +391,9 @@ when hasSpawnH: header: "", final, pure.} = object when defined(linux): - const Sockaddr_max_length* = 255 # from sys/un.h const Sockaddr_un_path_length* = 108 -elif defined(zephyr): - when defined(net_ipv6): - const Sockaddr_max_length* = 24 - elif defined(net_raw): - const Sockaddr_max_length* = 20 - elif defined(net_ipv4): - const Sockaddr_max_length* = 8 - else: - const Sockaddr_max_length* = 255 # just for compilation purposes - - const Sockaddr_un_path_length* = Sockaddr_max_length - # Zephyr is heavily customizable so it's easy to get to a state - # where Nim & Zephyr IPv6 settings are out of sync, causing painful runtime failures. - when defined(net_ipv4) or defined(net_ipv6) or defined(net_raw): - {.emit: ["NIM_STATIC_ASSERT(NET_SOCKADDR_MAX_SIZE == ", - Sockaddr_max_length, - ",\"NET_SOCKADDR_MAX_SIZE and Sockaddr_max_length size mismatch!", - " Check that Nim and Zephyr IPv4/IPv6 settings match.", - " Try adding -d:net_ipv6 to enable IPv6 for Nim on Zephyr.\" );"].} -elif defined(freertos) or defined(lwip): - const Sockaddr_max_length* = 14 - const Sockaddr_un_path_length* = 108 else: - const Sockaddr_max_length* = 255 # according to http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/un.h.html # this is >=92 const Sockaddr_un_path_length* = 92 @@ -432,50 +408,48 @@ when defined(lwip): pure, final.} = object ## struct sockaddr sa_len*: uint8 ## Address family. sa_family*: TSa_Family ## Address family. - sa_data*: array[0..Sockaddr_max_length-sizeof(uint8)-sizeof(TSa_Family), char] ## Socket address (variable-length data). - - Sockaddr_storage* {.importc: "struct sockaddr_storage", - header: "", - pure, final.} = object ## struct sockaddr_storage - s2_len*: uint8 ## Address family. - ss_family*: TSa_Family ## Address family. - s2_data1*: array[2, char] ## Address family. - s2_data2*: array[3, uint32] ## Address family. - when defined(lwip6) or defined(net_ipv6): - s2_data3*: array[3, uint32] ## Address family. -elif defined(zephyr): - type - SockAddr* {.importc: "struct sockaddr", header: "", - pure, final.} = object ## struct sockaddr - sa_family*: TSa_Family ## Address family. - data*: array[0..Sockaddr_max_length-sizeof(TSa_Family), char] ## Socket address (variable-length data). - - Sockaddr_storage* {.importc: "struct sockaddr_storage", - header: "", - pure, final.} = object ## struct sockaddr_storage - ss_family*: TSa_Family ## Address family. - data*: array[0..Sockaddr_max_length-sizeof(TSa_Family), char] ## Socket address (variable-length data). - {.emit: ["NIM_STATIC_ASSERT(sizeof(struct sockaddr) == ", sizeof(Sockaddr), ",\"struct size mismatch\" );"].} - {.emit: ["NIM_STATIC_ASSERT(sizeof(struct sockaddr_storage) == ", sizeof(Sockaddr_storage), ",\"struct size mismatch\" );"].} + sa_data*: array[0..255, char] ## Socket address (variable-length data). else: type SockAddr* {.importc: "struct sockaddr", header: "", pure, final.} = object ## struct sockaddr sa_family*: TSa_Family ## Address family. - sa_data*: array[0..Sockaddr_max_length-sizeof(TSa_Family), char] ## Socket address (variable-length data). - - Sockaddr_storage* {.importc: "struct sockaddr_storage", - header: "", - pure, final.} = object ## struct sockaddr_storage - ss_family*: TSa_Family ## Address family. + sa_data*: array[0..255, char] ## Socket address (variable-length data). type Sockaddr_un* {.importc: "struct sockaddr_un", header: "", pure, final.} = object ## struct sockaddr_un sun_family*: TSa_Family ## Address family. - sun_path*: array[0..Sockaddr_un_path_length-sizeof(TSa_Family), char] ## Socket path + sun_path*: array[0..Sockaddr_un_path_length-1, char] ## Socket path +when defined(lwip): + when not defined(lwip6): + type + Sockaddr_storage* {.importc: "struct sockaddr_storage", + header: "", + pure, final.} = object ## struct sockaddr_storage + s2_len*: uint8 ## Address family. + ss_family*: TSa_Family ## Address family. + s2_data1*: array[2, char] ## Address family. + s2_data2*: array[3, uint32] ## Address family. + else: + type + Sockaddr_storage* {.importc: "struct sockaddr_storage", + header: "", + pure, final.} = object ## struct sockaddr_storage + s2_len*: uint8 ## Address family. + ss_family*: TSa_Family ## Address family. + s2_data1*: array[2, char] ## Address family. + s2_data2*: array[3, uint32] ## Address family. + s2_data3*: array[3, uint32] ## Address family. +else: + type + Sockaddr_storage* {.importc: "struct sockaddr_storage", + header: "", + pure, final.} = object ## struct sockaddr_storage + ss_family*: TSa_Family ## Address family. + type Tif_nameindex* {.importc: "struct if_nameindex", final, pure, header: "".} = object ## struct if_nameindex @@ -520,7 +494,6 @@ type header: "".} = object ## struct in_addr s_addr*: InAddrScalar - # TODO: Fixme for FreeRTOS/LwIP, these are incorrect Sockaddr_in* {.importc: "struct sockaddr_in", pure, final, header: "".} = object ## struct sockaddr_in sin_family*: TSa_Family ## AF_INET. @@ -604,12 +577,7 @@ when not defined(lwip): events*: cshort ## The input event flags (see below). revents*: cshort ## The output event flags (see below). - when defined(zephyr): - type - Tnfds* = distinct cint - else: - type - Tnfds* {.importc: "nfds_t", header: "".} = cint + Tnfds* {.importc: "nfds_t", header: "".} = cint var errno* {.importc, header: "".}: cint ## error variable @@ -640,9 +608,6 @@ when defined(linux) or defined(nimdoc): ## or UDP packets. (Requires Linux kernel > 3.9) else: const SO_REUSEPORT* = cint(15) -elif defined(nuttx): - # Not supported, use SO_REUSEADDR to avoid compilation errors. - var SO_REUSEPORT* {.importc: "SO_REUSEADDR", header: "".}: cint else: var SO_REUSEPORT* {.importc, header: "".}: cint @@ -650,7 +615,7 @@ when defined(linux) or defined(bsd): var SOCK_CLOEXEC* {.importc, header: "".}: cint when defined(macosx): - # We can't use the NOSIGNAL flag in the `send` function, it has no effect + # We can't use the NOSIGNAL flag in the ``send`` function, it has no effect # Instead we should use SO_NOSIGPIPE in setsockopt const MSG_NOSIGNAL* = 0'i32 @@ -660,7 +625,7 @@ elif defined(solaris): # Solaris doesn't have MSG_NOSIGNAL const MSG_NOSIGNAL* = 0'i32 -elif defined(zephyr) or defined(freertos) or defined(lwip): +elif defined(freertos) or defined(lwip): # LwIP/FreeRTOS doesn't have MSG_NOSIGNAL const MSG_NOSIGNAL* = 0x20'i32 diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim index 08069fe9a..f43407b40 100644 --- a/lib/posix/posix_other_consts.nim +++ b/lib/posix/posix_other_consts.nim @@ -99,10 +99,7 @@ var EXDEV* {.importc: "EXDEV", header: "".}: cint # var F_DUPFD* {.importc: "F_DUPFD", header: "".}: cint -when defined(nuttx): - var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD_CLOEXEC", header: "".}: cint -else: - var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD", header: "".}: cint +var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD", header: "".}: cint var F_GETFD* {.importc: "F_GETFD", header: "".}: cint var F_SETFD* {.importc: "F_SETFD", header: "".}: cint var F_GETFL* {.importc: "F_GETFL", header: "".}: cint @@ -130,11 +127,6 @@ var O_RDONLY* {.importc: "O_RDONLY", header: "".}: cint var O_RDWR* {.importc: "O_RDWR", header: "".}: cint var O_WRONLY* {.importc: "O_WRONLY", header: "".}: cint var O_CLOEXEC* {.importc: "O_CLOEXEC", header: "".}: cint -when defined(nuttx): - var O_DIRECT* {.importc: "O_DIRECT", header: "".}: cint - var O_PATH* {.importc: "O_PATH", header: "".}: cint - var O_NOATIME* {.importc: "O_NOATIME", header: "".}: cint - var O_TMPFILE* {.importc: "O_TMPFILE", header: "".}: cint var POSIX_FADV_NORMAL* {.importc: "POSIX_FADV_NORMAL", header: "".}: cint var POSIX_FADV_SEQUENTIAL* {.importc: "POSIX_FADV_SEQUENTIAL", header: "".}: cint var POSIX_FADV_RANDOM* {.importc: "POSIX_FADV_RANDOM", header: "".}: cint @@ -470,9 +462,6 @@ var RLIMIT_NOFILE* {.importc: "RLIMIT_NOFILE", header: "".}: cin # var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "".}: cint -when defined(zephyr): - # Zephyr specific hardcoded value - var FD_MAX* {.importc: "CONFIG_POSIX_MAX_FDS ", header: "".}: cint # var MSG_CTRUNC* {.importc: "MSG_CTRUNC", header: "".}: cint @@ -498,15 +487,10 @@ var SO_SNDTIMEO* {.importc: "SO_SNDTIMEO", header: "".}: cint var SO_TYPE* {.importc: "SO_TYPE", header: "".}: cint var SOCK_DGRAM* {.importc: "SOCK_DGRAM", header: "".}: cint var SOCK_RAW* {.importc: "SOCK_RAW", header: "".}: cint -when defined(zephyr): - const SOCK_SEQPACKET* = cint(5) - var SOMAXCONN* {.importc: "CONFIG_NET_SOCKETS_POLL_MAX", header: "".}: cint -else: - var SOCK_SEQPACKET* {.importc: "SOCK_SEQPACKET", header: "".}: cint - var SOMAXCONN* {.importc: "SOMAXCONN", header: "".}: cint - +var SOCK_SEQPACKET* {.importc: "SOCK_SEQPACKET", header: "".}: cint var SOCK_STREAM* {.importc: "SOCK_STREAM", header: "".}: cint var SOL_SOCKET* {.importc: "SOL_SOCKET", header: "".}: cint +var SOMAXCONN* {.importc: "SOMAXCONN", header: "".}: cint var MSG_PEEK* {.importc: "MSG_PEEK", header: "".}: cint var MSG_TRUNC* {.importc: "MSG_TRUNC", header: "".}: cint var MSG_WAITALL* {.importc: "MSG_WAITALL", header: "".}: cint @@ -750,6 +734,3 @@ var SEEK_SET* {.importc: "SEEK_SET", header: "".}: cint var SEEK_CUR* {.importc: "SEEK_CUR", header: "".}: cint var SEEK_END* {.importc: "SEEK_END", header: "".}: cint -# -when defined(nuttx): - var NEPOLL_MAX* {.importc: "CONFIG_FS_NEPOLL_DESCRIPTORS", header: "".}: cint diff --git a/lib/posix/posix_utils.nim b/lib/posix/posix_utils.nim index 7e4a2eeda..7fd8b1fc9 100644 --- a/lib/posix/posix_utils.nim +++ b/lib/posix/posix_utils.nim @@ -7,21 +7,18 @@ # ## A set of helpers for the POSIX module. -## Raw interfaces are in the other ``posix*.nim`` files. +## Raw interfaces are in the other posix*.nim files. # Where possible, contribute OS-independent procs in `os `_ instead. import posix, parsecfg, os import std/private/since -when defined(nimPreviewSlimSystem): - import std/syncio - type Uname* = object sysname*, nodename*, release*, version*, machine*: string template charArrayToString(input: typed): string = - $cast[cstring](addr input) + $cstring(addr input) proc uname*(): Uname = ## Provides system information in a `Uname` struct with sysname, nodename, @@ -43,8 +40,8 @@ proc uname*(): Uname = result.machine = charArrayToString u.machine proc fsync*(fd: int) = - ## synchronize a file's buffer cache to the storage device - if fsync(fd.cint) != 0: + ## synchronize a file's buffer cache to the storage device + if fsync(fd.cint) != 0: raise newException(OSError, $strerror(errno)) proc stat*(path: string): Stat = @@ -60,10 +57,9 @@ proc memoryLock*(a1: pointer, a2: int) = proc memoryLockAll*(flags: int) = ## Locks all memory for the running process to prevent swapping. ## - ## example: - ## ```nim + ## example:: + ## ## memoryLockAll(MCL_CURRENT or MCL_FUTURE) - ## ``` if mlockall(flags.cint) != 0: raise newException(OSError, $strerror(errno)) @@ -90,7 +86,7 @@ proc mkstemp*(prefix: string, suffix=""): (string, File) = ## Returns the filename and a file opened in r/w mode. var tmpl = cstring(prefix & "XXXXXX" & suffix) let fd = - if len(suffix) == 0: + if len(suffix)==0: when declared(mkostemp): mkostemp(tmpl, O_CLOEXEC) else: @@ -121,10 +117,10 @@ proc osReleaseFile*(): Config {.since: (1, 5).} = ## `os-release` file is not available on Windows and OS X by design. ## * https://www.freedesktop.org/software/systemd/man/os-release.html runnableExamples: - import std/parsecfg + import parsecfg when defined(linux): let data = osReleaseFile() - echo "OS name: ", data.getSectionValue("", "NAME") ## the data is up to each distro. + doAssert data.getSectionValue("", "NAME").len > 0 ## the data is up to each distro. # We do not use a {.strdefine.} because Standard says it *must* be that path. for osReleaseFile in ["/etc/os-release", "/usr/lib/os-release"]: diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim index f755c720d..b03ce74f3 100644 --- a/lib/posix/termios.nim +++ b/lib/posix/termios.nim @@ -237,11 +237,8 @@ proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow", header: "".} # Get process group ID for session leader for controlling terminal FD. -# Window size ioctl. Solaris based systems have an uncommen place for this. -when defined(solaris) or defined(sunos): - var TIOCGWINSZ*{.importc, header: "".}: culong -else: - var TIOCGWINSZ*{.importc, header: "".}: culong +# Window size ioctl. Should work on on any Unix that xterm has been ported to. +var TIOCGWINSZ*{.importc, header: "".}: culong when defined(nimHasStyleChecks): {.push styleChecks: off.} diff --git a/lib/prelude.nim b/lib/prelude.nim new file mode 100644 index 000000000..4d8c7d7f0 --- /dev/null +++ b/lib/prelude.nim @@ -0,0 +1,23 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This is an include file that simply imports common modules for your +## convenience: +## +## .. code-block:: nim +## include prelude +## +## Same as: +## +## .. code-block:: nim +## import os, strutils, times, parseutils, hashes, tables, sets, sequtils +## when not defined(js): import parseopt + +import os, strutils, times, parseutils, hashes, tables, sets, sequtils +when not defined(js): import parseopt diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 65402e9fb..029e9abf8 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -42,12 +42,6 @@ runnableExamples: ## * `sequtils module`_ for working with the built-in seq type ## * `tables module`_ for sorting tables -import std/private/since - -when defined(nimPreviewSlimSystem): - import std/assertions - - type SortOrder* = enum Descending, Ascending @@ -135,29 +129,43 @@ proc reverse*[T](a: var openArray[T]) = # the max is needed, since a.high is -1 if a is empty reverse(a, 0, max(0, a.high)) -proc reversed*[T](a: openArray[T]): seq[T] {.inline.} = - ## Returns the elements of `a` in reverse order. +proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] = + ## Returns the reverse of the slice `a[first..last]`. + ## + ## If an invalid range is passed, it raises `IndexDefect`. ## ## **See also:** + ## * `reverse proc<#reverse,openArray[T],Natural,Natural>`_ reverse a slice ## * `reverse proc<#reverse,openArray[T]>`_ runnableExamples: - assert [10, 11, 12].reversed == @[12, 11, 10] - assert seq[string].default.reversed == @[] - let n = a.len - result.setLen(n) - for i in 0..= first - 1 + var i = last - first + var x = first.int + result = newSeq[T](i + 1) + while i >= 0: + result[i] = a[x] + dec(i) + inc(x) -proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] - {.inline, deprecated: "use: `reversed(toOpenArray(a, first, last))`".} = - reversed(toOpenArray(a, first, last)) - -when defined(nimHasEffectsOf): - {.experimental: "strictEffects".} -else: - {.pragma: effectsOf.} +proc reversed*[T](a: openArray[T]): seq[T] = + ## Returns the reverse of the container `a`. + ## + ## **See also:** + ## * `reverse proc<#reverse,openArray[T],Natural,Natural>`_ reverse a slice + ## * `reverse proc<#reverse,openArray[T]>`_ + runnableExamples: + let + a = [1, 2, 3, 4, 5, 6] + b = reversed(a) + assert b == @[6, 5, 4, 3, 2, 1] + reversed(a, 0, a.high) proc binarySearch*[T, K](a: openArray[T], key: K, - cmp: proc (x: T, y: K): int {.closure.}): int {.effectsOf: cmp.} = + cmp: proc (x: T, y: K): int {.closure.}): int = ## Binary search for `key` in `a`. Return the index of `key` or -1 if not found. ## Assumes that `a` is sorted according to `cmp`. ## @@ -219,7 +227,7 @@ const onlySafeCode = true proc lowerBound*[T, K](a: openArray[T], key: K, - cmp: proc(x: T, k: K): int {.closure.}): int {.effectsOf: cmp.} = + cmp: proc(x: T, k: K): int {.closure.}): int = ## Returns the index of the first element in `a` that is not less than ## (i.e. greater or equal to) `key`, or last if no such element is found. ## In other words if you have a sorted sequence and you call @@ -269,7 +277,7 @@ proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T]) ## * `upperBound proc<#upperBound,openArray[T],T>`_ proc upperBound*[T, K](a: openArray[T], key: K, - cmp: proc(x: T, k: K): int {.closure.}): int {.effectsOf: cmp.} = + cmp: proc(x: T, k: K): int {.closure.}): int = ## Returns the index of the first element in `a` that is greater than ## `key`, or last if no such element is found. ## In other words if you have a sorted sequence and you call @@ -326,12 +334,12 @@ template `<-`(a, b) = else: copyMem(addr(a), addr(b), sizeof(T)) -proc mergeAlt[T](a, b: var openArray[T], lo, m, hi: int, - cmp: proc (x, y: T): int {.closure.}, order: SortOrder) {.effectsOf: cmp.} = +proc merge[T](a, b: var openArray[T], lo, m, hi: int, + cmp: proc (x, y: T): int {.closure.}, order: SortOrder) = # Optimization: If max(left) <= min(right) there is nothing to do! # 1 2 3 4 ## 5 6 7 8 # -> O(n) for sorted arrays. - # On random data this saves up to 40% of mergeAlt calls. + # On random data this saves up to 40% of merge calls. if cmp(a[m], a[m+1]) * order <= 0: return var j = lo # copy a[j..m] into b: @@ -367,7 +375,7 @@ proc mergeAlt[T](a, b: var openArray[T], lo, m, hi: int, func sort*[T](a: var openArray[T], cmp: proc (x, y: T): int {.closure.}, - order = SortOrder.Ascending) {.effectsOf: cmp.} = + order = SortOrder.Ascending) = ## Default Nim sort (an implementation of merge sort). The sorting ## is guaranteed to be stable (that is, equal elements stay in the same order) ## and the worst case is guaranteed to be O(n log n). @@ -379,22 +387,22 @@ func sort*[T](a: var openArray[T], ## `cmp`, you may use `system.cmp` or instead call the overloaded ## version of `sort`, which uses `system.cmp`. ## - ## ```nim - ## sort(myIntArray, system.cmp[int]) - ## # do not use cmp[string] here as we want to use the specialized - ## # overload: - ## sort(myStrArray, system.cmp) - ## ``` + ## .. code-block:: nim + ## + ## sort(myIntArray, system.cmp[int]) + ## # do not use cmp[string] here as we want to use the specialized + ## # overload: + ## sort(myStrArray, system.cmp) ## ## You can inline adhoc comparison procs with the `do notation ## `_. Example: ## - ## ```nim + ## .. code-block:: nim + ## ## people.sort do (x, y: Person) -> int: ## result = cmp(x.surname, y.surname) ## if result == 0: ## result = cmp(x.name, y.name) - ## ``` ## ## **See also:** ## * `sort proc<#sort,openArray[T]>`_ @@ -414,7 +422,7 @@ func sort*[T](a: var openArray[T], while s < n: var m = n-1-s while m >= 0: - mergeAlt(a, b, max(m-s+1, 0), m, m+s, cmp, order) + merge(a, b, max(m-s+1, 0), m, m+s, cmp, order) dec(m, s*2) s = s*2 @@ -429,7 +437,7 @@ proc sort*[T](a: var openArray[T], order = SortOrder.Ascending) = sort[T](a, ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_ proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.}, - order = SortOrder.Ascending): seq[T] {.effectsOf: cmp.} = + order = SortOrder.Ascending): seq[T] = ## Returns `a` sorted by `cmp` in the specified `order`. ## ## **See also:** @@ -506,7 +514,7 @@ template sortedByIt*(seq1, op: untyped): untyped = func isSorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.}, - order = SortOrder.Ascending): bool {.effectsOf: cmp.} = + order = SortOrder.Ascending): bool = ## Checks to see whether `a` is already sorted in `order` ## using `cmp` for the comparison. The parameters are identical ## to `sort`. Requires O(n) time. @@ -551,120 +559,26 @@ proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool = assert isSorted(e) == false isSorted(a, system.cmp[T], order) -proc merge*[T]( - result: var seq[T], - x, y: openArray[T], cmp: proc(x, y: T): int {.closure.} -) {.since: (1, 5, 1), effectsOf: cmp.} = - ## Merges two sorted `openArray`. `x` and `y` are assumed to be sorted. - ## If you do not wish to provide your own `cmp`, - ## you may use `system.cmp` or instead call the overloaded - ## version of `merge`, which uses `system.cmp`. - ## - ## .. note:: The original data of `result` is not cleared, - ## new data is appended to `result`. - ## - ## **See also:** - ## * `merge proc<#merge,seq[T],openArray[T],openArray[T]>`_ - runnableExamples: - let x = @[1, 3, 6] - let y = @[2, 3, 4] - - block: - var merged = @[7] # new data is appended to merged sequence - merged.merge(x, y, system.cmp[int]) - assert merged == @[7, 1, 2, 3, 3, 4, 6] - - block: - var merged = @[7] # if you only want new data, clear merged sequence first - merged.setLen(0) - merged.merge(x, y, system.cmp[int]) - assert merged.isSorted - assert merged == @[1, 2, 3, 3, 4, 6] - - import std/sugar - - var res: seq[(int, int)] - res.merge([(1, 1)], [(1, 2)], (a, b) => a[0] - b[0]) - assert res == @[(1, 1), (1, 2)] - - assert seq[int].default.dup(merge([1, 3], [2, 4])) == @[1, 2, 3, 4] - - let - sizeX = x.len - sizeY = y.len - oldLen = result.len - - result.setLen(oldLen + sizeX + sizeY) - - var - ix = 0 - iy = 0 - i = oldLen - - while true: - if ix == sizeX: - while iy < sizeY: - result[i] = y[iy] - inc i - inc iy - return - - if iy == sizeY: - while ix < sizeX: - result[i] = x[ix] - inc i - inc ix - return - - let itemX = x[ix] - let itemY = y[iy] - - if cmp(itemX, itemY) > 0: # to have a stable sort - result[i] = itemY - inc iy - else: - result[i] = itemX - inc ix - - inc i - -proc merge*[T](result: var seq[T], x, y: openArray[T]) {.inline, since: (1, 5, 1).} = - ## Shortcut version of `merge` that uses `system.cmp[T]` as the comparison function. - ## - ## **See also:** - ## * `merge proc<#merge,seq[T],openArray[T],openArray[T],proc(T,T)>`_ - runnableExamples: - let x = [5, 10, 15, 20, 25] - let y = [50, 40, 30, 20, 10].sorted - - var merged: seq[int] - merged.merge(x, y) - assert merged.isSorted - assert merged == @[5, 10, 10, 15, 20, 20, 25, 30, 40, 50] - merge(result, x, y, system.cmp) - proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = ## Produces the Cartesian product of the array. ## Every element of the result is a combination of one element from each seq in `x`, ## with the ith element coming from `x[i]`. - ## - ## .. warning:: complexity may explode. + ## Warning: complexity may explode. runnableExamples: assert product(@[@[1], @[2]]) == @[@[1, 2]] assert product(@[@["A", "K"], @["Q"]]) == @[@["K", "Q"], @["A", "Q"]] - let xLen = x.len result = newSeq[seq[T]]() - if xLen == 0: + if x.len == 0: return - if xLen == 1: + if x.len == 1: result = @x return var - indices = newSeq[int](xLen) - initial = newSeq[int](xLen) + indices = newSeq[int](x.len) + initial = newSeq[int](x.len) index = 0 - var next = newSeq[T](xLen) - for i in 0 ..< xLen: + var next = newSeq[T](x.len) + for i in 0..(x.len-1): if len(x[i]) == 0: return initial[i] = len(x[i]) - 1 indices = initial @@ -672,7 +586,7 @@ proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = while indices[index] == -1: indices[index] = initial[index] index += 1 - if index == xLen: return + if index == x.len: return indices[index] -= 1 for ni, i in indices: next[ni] = x[ni][i] @@ -796,8 +710,7 @@ proc rotateInternal[T](arg: var openArray[T]; first, middle, last: int): int = next = mMiddle proc rotatedInternal[T](arg: openArray[T]; first, middle, last: int): seq[T] = - let argLen = arg.len - result = newSeq[T](argLen) + result = newSeq[T](arg.len) for i in 0 ..< first: result[i] = arg[i] let n = last - middle @@ -806,7 +719,7 @@ proc rotatedInternal[T](arg: openArray[T]; first, middle, last: int): seq[T] = result[first+i] = arg[middle+i] for i in 0 ..< m: result[first+n+i] = arg[first+i] - for i in last ..< argLen: + for i in last ..< arg.len: result[i] = arg[i] proc rotateLeft*[T](arg: var openArray[T]; slice: HSlice[int, int]; @@ -825,10 +738,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:** @@ -863,9 +776,9 @@ proc rotateLeft*[T](arg: var openArray[T]; dist: int): int {.discardable.} = assert a == [2, 3, 4, 5, 1] a.rotateLeft(-6) assert a == [1, 2, 3, 4, 5] - let argLen = arg.len - let distLeft = ((dist mod argLen) + argLen) mod argLen - arg.rotateInternal(0, distLeft, argLen) + let arglen = arg.len + let distLeft = ((dist mod arglen) + arglen) mod arglen + arg.rotateInternal(0, distLeft, arglen) proc rotatedLeft*[T](arg: openArray[T]; slice: HSlice[int, int], dist: int): seq[T] = @@ -876,10 +789,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:** @@ -895,7 +808,7 @@ proc rotatedLeft*[T](arg: openArray[T]; slice: HSlice[int, int], assert a == @[1, 5, 2, 3, 4] let sliceLen = slice.b + 1 - slice.a let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen - arg.rotatedInternal(slice.a, slice.a + distLeft, slice.b + 1) + arg.rotatedInternal(slice.a, slice.a+distLeft, slice.b+1) proc rotatedLeft*[T](arg: openArray[T]; dist: int): seq[T] = ## Same as `rotateLeft`, just with the difference that it does @@ -912,6 +825,6 @@ proc rotatedLeft*[T](arg: openArray[T]; dist: int): seq[T] = assert a == @[2, 3, 4, 5, 1] a = rotatedLeft(a, -6) assert a == @[1, 2, 3, 4, 5] - let argLen = arg.len - let distLeft = ((dist mod argLen) + argLen) mod argLen - arg.rotatedInternal(0, distLeft, argLen) + let arglen = arg.len + let distLeft = ((dist mod arglen) + arglen) mod arglen + arg.rotatedInternal(0, distLeft, arg.len) diff --git a/lib/pure/async.nim b/lib/pure/async.nim index 249c5f639..97b29f81d 100644 --- a/lib/pure/async.nim +++ b/lib/pure/async.nim @@ -1,9 +1,6 @@ -## Exports [asyncmacro](asyncmacro.html) and [asyncfutures](asyncfutures.html) for native backends, -## and [asyncjs](asyncjs.html) on the JS backend. - when defined(js): - import asyncjs - export asyncjs + import asyncjs + export asyncjs else: - import asyncmacro, asyncfutures - export asyncmacro, asyncfutures + import asyncmacro, asyncfutures + export asyncmacro, asyncfutures diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 63062d8da..1b3548268 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -8,25 +8,25 @@ # ## This module implements asynchronous IO. This includes a dispatcher, -## a `Future` type implementation, and an `async` macro which allows -## asynchronous code to be written in a synchronous style with the `await` +## a ``Future`` type implementation, and an ``async`` macro which allows +## asynchronous code to be written in a synchronous style with the ``await`` ## keyword. ## -## The dispatcher acts as a kind of event loop. You must call `poll` on it -## (or a function which does so for you such as `waitFor` or `runForever`) +## The dispatcher acts as a kind of event loop. You must call ``poll`` on it +## (or a function which does so for you such as ``waitFor`` or ``runForever``) ## in order to poll for any outstanding events. The underlying implementation ## is based on epoll on Linux, IO Completion Ports on Windows and select on ## other operating systems. ## -## The `poll` function will not, on its own, return any events. Instead -## an appropriate `Future` object will be completed. A `Future` is a +## The ``poll`` function will not, on its own, return any events. Instead +## an appropriate ``Future`` object will be completed. A ``Future`` is a ## type which holds a value which is not yet available, but which *may* be ## available in the future. You can check whether a future is finished -## by using the `finished` function. When a future is finished it means that +## by using the ``finished`` function. When a future is finished it means that ## either the value that it holds is now available or it holds an error instead. ## The latter situation occurs when the operation to complete a future fails ## with an exception. You can distinguish between the two situations with the -## `failed` function. +## ``failed`` function. ## ## Future objects can also store a callback procedure which will be called ## automatically once the future completes. @@ -35,34 +35,34 @@ ## pattern. In this ## pattern you make a request for an action, and once that action is fulfilled ## a future is completed with the result of that action. Requests can be -## made by calling the appropriate functions. For example: calling the `recv` +## made by calling the appropriate functions. For example: calling the ``recv`` ## function will create a request for some data to be read from a socket. The -## future which the `recv` function returns will then complete once the +## future which the ``recv`` function returns will then complete once the ## requested amount of data is read **or** an exception occurs. ## ## Code to read some data from a socket may look something like this: -## ```Nim -## var future = socket.recv(100) -## future.addCallback( -## proc () = -## echo(future.read) -## ) -## ``` ## -## All asynchronous functions returning a `Future` will not block. They +## .. code-block::nim +## var future = socket.recv(100) +## future.addCallback( +## proc () = +## echo(future.read) +## ) +## +## All asynchronous functions returning a ``Future`` will not block. They ## will not however return immediately. An asynchronous function will have ## code which will be executed before an asynchronous request is made, in most ## cases this code sets up the request. ## -## In the above example, the `recv` function will return a brand new -## `Future` instance once the request for data to be read from the socket -## is made. This `Future` instance will complete once the requested amount +## In the above example, the ``recv`` function will return a brand new +## ``Future`` instance once the request for data to be read from the socket +## is made. This ``Future`` instance will complete once the requested amount ## of data is read, in this case it is 100 bytes. The second line sets a ## callback on this future which will be called once the future completes. -## All the callback does is write the data stored in the future to `stdout`. -## The `read` function is used for this and it checks whether the future -## completes with an error for you (if it did, it will simply raise the -## error), if there is no error, however, it returns the value of the future. +## All the callback does is write the data stored in the future to ``stdout``. +## The ``read`` function is used for this and it checks whether the future +## completes with an error for you (if it did it will simply raise the +## error), if there is no error however it returns the value of the future. ## ## Asynchronous procedures ## ======================= @@ -71,14 +71,14 @@ ## this by allowing you to write asynchronous code the same way as you would ## write synchronous code. ## -## An asynchronous procedure is marked using the `{.async.}` pragma. -## When marking a procedure with the `{.async.}` pragma it must have a -## `Future[T]` return type or no return type at all. If you do not specify -## a return type then `Future[void]` is assumed. +## An asynchronous procedure is marked using the ``{.async.}`` pragma. +## When marking a procedure with the ``{.async.}`` pragma it must have a +## ``Future[T]`` return type or no return type at all. If you do not specify +## a return type then ``Future[void]`` is assumed. ## -## Inside asynchronous procedures `await` can be used to call any +## Inside asynchronous procedures ``await`` can be used to call any ## procedures which return a -## `Future`; this includes asynchronous procedures. When a procedure is +## ``Future``; this includes asynchronous procedures. When a procedure is ## "awaited", the asynchronous procedure it is awaited in will ## suspend its execution ## until the awaited procedure's Future completes. At which point the @@ -86,83 +86,52 @@ ## when an asynchronous procedure is suspended other asynchronous procedures ## will be run by the dispatcher. ## -## The `await` call may be used in many contexts. It can be used on the right -## hand side of a variable declaration: `var data = await socket.recv(100)`, +## The ``await`` call may be used in many contexts. It can be used on the right +## hand side of a variable declaration: ``var data = await socket.recv(100)``, ## in which case the variable will be set to the value of the future -## automatically. It can be used to await a `Future` object, and it can -## be used to await a procedure returning a `Future[void]`: -## `await socket.send("foobar")`. +## automatically. It can be used to await a ``Future`` object, and it can +## be used to await a procedure returning a ``Future[void]``: +## ``await socket.send("foobar")``. ## -## If an awaited future completes with an error, then `await` will re-raise -## this error. To avoid this, you can use the `yield` keyword instead of -## `await`. The following section shows different ways that you can handle +## If an awaited future completes with an error, then ``await`` will re-raise +## this error. To avoid this, you can use the ``yield`` keyword instead of +## ``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 ## ------------------- ## -## You can handle exceptions in the same way as in ordinary Nim code; -## by using the try statement: +## The most reliable way to handle exceptions is to use ``yield`` on a future +## then check the future's ``failed`` property. For example: ## -## ```Nim -## try: -## let data = await sock.recv(100) -## echo("Received ", data) -## except: -## # Handle exception -## ``` +## .. code-block:: Nim +## var future = sock.recv(100) +## yield future +## if future.failed: +## # Handle exception ## -## An alternative approach to handling exceptions is to use `yield` on a future -## then check the future's `failed` property. For example: +## The ``async`` procedures also offer limited support for the try statement. ## -## ```Nim -## var future = sock.recv(100) -## yield future -## if future.failed: -## # Handle exception -## ``` +## .. code-block:: Nim +## try: +## let data = await sock.recv(100) +## echo("Received ", data) +## except: +## # Handle exception +## +## Unfortunately the semantics of the try statement may not always be correct, +## and occasionally the compilation may fail altogether. +## As such it is better to use the former style when possible. ## ## ## Discarding futures ## ================== ## -## 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 -## ============== ===================== ======================= +## 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. ## ## Examples ## ======== @@ -175,14 +144,14 @@ ## ============================= ## ## It's possible to get into a situation where an async proc, or more accurately -## a `Future[T]` gets stuck and +## a ``Future[T]`` gets stuck and ## never completes. This can happen for various reasons and can cause serious ## memory leaks. When this occurs it's hard to identify the procedure that is ## stuck. ## ## Thankfully there is a mechanism which tracks the count of each pending future. -## All you need to do to enable it is compile with `-d:futureLogging` and -## use the `getFuturesInProgress` procedure to get the list of pending futures +## All you need to do to enable it is compile with ``-d:futureLogging`` and +## use the ``getFuturesInProgress`` procedure to get the list of pending futures ## together with the stack traces to the moment of their creation. ## ## You may also find it useful to use this @@ -195,36 +164,7 @@ ## Limitations/Bugs ## ================ ## -## * The effect system (`raises: []`) does not work with async procedures. -## * Mutable parameters are not supported by async procedures. -## -## -## Multiple async backend support -## ============================== -## -## Thanks to its powerful macro support, Nim allows ``async``/``await`` to be -## implemented in libraries with only minimal support from the language - as -## such, multiple ``async`` libraries exist, including ``asyncdispatch`` and -## ``chronos``, and more may come to be developed in the future. -## -## Libraries built on top of async/await may wish to support multiple async -## backends - the best way to do so is to create separate modules for each backend -## that may be imported side-by-side. -## -## An alternative way is to select backend using a global compile flag - this -## method makes it difficult to compose applications that use both backends as may -## happen with transitive dependencies, but may be appropriate in some cases - -## libraries choosing this path should call the flag `asyncBackend`, allowing -## applications to choose the backend with `-d:asyncBackend=`. -## -## Known `async` backends include: -## -## * `-d:asyncBackend=none`: disable `async` support completely -## * `-d:asyncBackend=asyncdispatch`: https://nim-lang.org/docs/asyncdispatch.html -## * `-d:asyncBackend=chronos`: https://github.com/status-im/nim-chronos/ -## -## ``none`` can be used when a library supports both a synchronous and -## asynchronous API, to disable the latter. +## * The effect system (``raises: []``) does not work with async procedures. import os, tables, strutils, times, heapqueue, options, asyncstreams import options, math, std/monotimes @@ -232,13 +172,12 @@ import asyncfutures except callSoon import nativesockets, net, deques -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - export Port, SocketFlag export asyncfutures except callSoon export asyncstreams +#{.injectStmt: newGcInvariant().} + # TODO: Check if yielded future is nil and throw a more meaningful exception type @@ -281,8 +220,6 @@ proc adjustTimeout( result = max(nextTimer.get(), 0) result = min(pollTimeout, result) -proc runOnce(timeout: int): bool {.gcsafe.} - proc callSoon*(cbproc: proc () {.gcsafe.}) {.gcsafe.} ## Schedule `cbproc` to be called as soon as possible. ## The callback is called when control returns to the event loop. @@ -295,7 +232,7 @@ template implementSetInheritable() {.dirty.} = when declared(setInheritable): proc setInheritable*(fd: AsyncFD, inheritable: bool): bool = ## Control whether a file handle can be inherited by child processes. - ## Returns `true` on success. + ## Returns ``true`` on success. ## ## This procedure is not guaranteed to be available for all platforms. ## Test for availability with `declared() `_. @@ -370,7 +307,7 @@ when defined(windows) or defined(nimdoc): return disp.ioPort proc register*(fd: AsyncFD) = - ## Registers `fd` with the dispatcher. + ## Registers ``fd`` with the dispatcher. let p = getGlobalDispatcher() if createIoCompletionPort(fd.Handle, p.ioPort, @@ -392,7 +329,7 @@ when defined(windows) or defined(nimdoc): let p = getGlobalDispatcher() p.handles.len != 0 or p.timers.len != 0 or p.callbacks.len != 0 - proc runOnce(timeout: int): bool = + proc runOnce(timeout = 500): bool = let p = getGlobalDispatcher() if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0: raise newException(ValueError, @@ -493,16 +430,16 @@ when defined(windows) or defined(nimdoc): proc recv*(socket: AsyncFD, size: int, flags = {SocketFlag.SafeDisconn}): owned(Future[string]) = - ## Reads **up to** `size` bytes from `socket`. Returned future will + ## Reads **up to** ``size`` bytes from ``socket``. Returned future will ## complete once all the data requested is read, a part of the data has been ## read, or the socket has disconnected in which case the future will - ## complete with a value of `""`. + ## complete with a value of ``""``. ## - ## .. warning:: The `Peek` socket flag is not supported on Windows. + ## **Warning**: The ``Peek`` socket flag is not supported on Windows. # Things to note: - # * When WSARecv completes immediately then `bytesReceived` is very + # * When WSARecv completes immediately then ``bytesReceived`` is very # unreliable. # * Still need to implement message-oriented socket disconnection, # '\0' in the message currently signifies a socket disconnect. Who @@ -566,17 +503,17 @@ when defined(windows) or defined(nimdoc): proc recvInto*(socket: AsyncFD, buf: pointer, size: int, flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = - ## Reads **up to** `size` bytes from `socket` into `buf`, which must + ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must ## at least be of that size. Returned future will complete once all the ## data requested is read, a part of the data has been read, or the socket ## has disconnected in which case the future will complete with a value of - ## `0`. + ## ``0``. ## - ## .. warning:: The `Peek` socket flag is not supported on Windows. + ## **Warning**: The ``Peek`` socket flag is not supported on Windows. # Things to note: - # * When WSARecv completes immediately then `bytesReceived` is very + # * When WSARecv completes immediately then ``bytesReceived`` is very # unreliable. # * Still need to implement message-oriented socket disconnection, # '\0' in the message currently signifies a socket disconnect. Who @@ -632,11 +569,11 @@ when defined(windows) or defined(nimdoc): proc send*(socket: AsyncFD, buf: pointer, size: int, flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = - ## Sends `size` bytes from `buf` to `socket`. The returned future + ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future ## will complete once all data has been sent. ## - ## .. warning:: Use it with caution. If `buf` refers to GC'ed object, - ## you must use GC_ref/GC_unref calls to avoid early freeing of the buffer. + ## **WARNING**: Use it with caution. If ``buf`` refers to GC'ed object, + ## you must use GC_ref/GC_unref calls to avoid early freeing of the buffer. verifyPresence(socket) var retFuture = newFuture[void]("send") @@ -670,16 +607,16 @@ when defined(windows) or defined(nimdoc): retFuture.fail(newException(OSError, osErrorMsg(err))) else: retFuture.complete() - # We don't deallocate `ol` here because even though this completed + # We don't deallocate ``ol`` here because even though this completed # immediately poll will still be notified about its completion and it will - # free `ol`. + # free ``ol``. return retFuture proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr, saddrLen: SockLen, flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = - ## Sends `data` to specified destination `saddr`, using - ## socket `socket`. The returned future will complete once all data + ## Sends ``data`` to specified destination ``saddr``, using + ## socket ``socket``. The returned future will complete once all data ## has been sent. verifyPresence(socket) var retFuture = newFuture[void]("sendTo") @@ -715,17 +652,17 @@ when defined(windows) or defined(nimdoc): retFuture.fail(newException(OSError, osErrorMsg(err))) else: retFuture.complete() - # We don't deallocate `ol` here because even though this completed + # We don't deallocate ``ol`` here because even though this completed # immediately poll will still be notified about its completion and it will - # free `ol`. + # free ``ol``. return retFuture proc recvFromInto*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr, saddrLen: ptr SockLen, flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = - ## Receives a datagram data from `socket` into `buf`, which must - ## be at least of size `size`, address of datagram's sender will be - ## stored into `saddr` and `saddrLen`. Returned future will complete + ## Receives a datagram data from ``socket`` into ``buf``, which must + ## be at least of size ``size``, address of datagram's sender will be + ## stored into ``saddr`` and ``saddrLen``. Returned future will complete ## once one datagram has been received, and will return size of packet ## received. verifyPresence(socket) @@ -770,7 +707,7 @@ when defined(windows) or defined(nimdoc): proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}, inheritable = defined(nimInheritHandles)): - owned(Future[tuple[address: string, client: AsyncFD]]) {.gcsafe.} = + owned(Future[tuple[address: string, client: AsyncFD]]) = ## Accepts a new connection. Returns a future containing the client socket ## corresponding to that connection and the remote address of the client. ## The future will complete when the connection is successfully accepted. @@ -778,11 +715,11 @@ when defined(windows) or defined(nimdoc): ## The resulting client socket is automatically registered to the ## dispatcher. ## - ## If `inheritable` is false (the default), the resulting client socket will + ## If ``inheritable`` is false (the default), the resulting client socket will ## not be inheritable by child processes. ## - ## The `accept` call may result in an error if the connecting socket - ## disconnects during the duration of the `accept`. If the `SafeDisconn` + ## The ``accept`` call may result in an error if the connecting socket + ## disconnects during the duration of the ``accept``. If the ``SafeDisconn`` ## flag is specified then this error will not be raised and instead ## accept will be called again. verifyPresence(socket) @@ -837,7 +774,7 @@ when defined(windows) or defined(nimdoc): var ol = newCustom() ol.data = CompletionData(fd: socket, cb: - proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) {.gcsafe.} = + proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = if not retFuture.finished: if errcode == OSErrorCode(-1): completeAccept() @@ -859,9 +796,9 @@ when defined(windows) or defined(nimdoc): GC_unref(ol) else: completeAccept() - # We don't deallocate `ol` here because even though this completed + # We don't deallocate ``ol`` here because even though this completed # immediately poll will still be notified about its completion and it will - # free `ol`. + # free ``ol``. return retFuture @@ -873,7 +810,7 @@ when defined(windows) or defined(nimdoc): getGlobalDispatcher().handles.excl(socket) proc unregister*(fd: AsyncFD) = - ## Unregisters `fd`. + ## Unregisters ``fd``. getGlobalDispatcher().handles.excl(fd) proc contains*(disp: PDispatcher, fd: AsyncFD): bool = @@ -973,9 +910,9 @@ when defined(windows) or defined(nimdoc): proc addRead*(fd: AsyncFD, cb: Callback) = ## Start watching the file descriptor for read availability and then call - ## the callback `cb`. + ## the callback ``cb``. ## - ## This is not `pure` mechanism for Windows Completion Ports (IOCP), + ## This is not ``pure`` mechanism for Windows Completion Ports (IOCP), ## so if you can avoid it, please do it. Use `addRead` only if really ## need it (main usecase is adaptation of unix-like libraries to be ## asynchronous on Windows). @@ -984,16 +921,16 @@ when defined(windows) or defined(nimdoc): ## or asyncdispatch.accept(), because they are using IOCP, please use ## nativesockets.recv() and nativesockets.accept() instead. ## - ## Be sure your callback `cb` returns `true`, if you want to remove - ## watch of `read` notifications, and `false`, if you want to continue + ## Be sure your callback ``cb`` returns ``true``, if you want to remove + ## watch of `read` notifications, and ``false``, if you want to continue ## receiving notifications. registerWaitableEvent(fd, cb, FD_READ or FD_ACCEPT or FD_OOB or FD_CLOSE) proc addWrite*(fd: AsyncFD, cb: Callback) = ## Start watching the file descriptor for write availability and then call - ## the callback `cb`. + ## the callback ``cb``. ## - ## This is not `pure` mechanism for Windows Completion Ports (IOCP), + ## This is not ``pure`` mechanism for Windows Completion Ports (IOCP), ## so if you can avoid it, please do it. Use `addWrite` only if really ## need it (main usecase is adaptation of unix-like libraries to be ## asynchronous on Windows). @@ -1002,8 +939,8 @@ when defined(windows) or defined(nimdoc): ## or asyncdispatch.connect(), because they are using IOCP, please use ## nativesockets.send() and nativesockets.connect() instead. ## - ## Be sure your callback `cb` returns `true`, if you want to remove - ## watch of `write` notifications, and `false`, if you want to continue + ## Be sure your callback ``cb`` returns ``true``, if you want to remove + ## watch of `write` notifications, and ``false``, if you want to continue ## receiving notifications. registerWaitableEvent(fd, cb, FD_WRITE or FD_CONNECT or FD_CLOSE) @@ -1043,12 +980,12 @@ when defined(windows) or defined(nimdoc): raiseOSError(osLastError()) proc addTimer*(timeout: int, oneshot: bool, cb: Callback) = - ## Registers callback `cb` to be called when timer expired. + ## Registers callback ``cb`` to be called when timer expired. ## ## Parameters: ## - ## * `timeout` - timeout value in milliseconds. - ## * `oneshot` + ## * ``timeout`` - timeout value in milliseconds. + ## * ``oneshot`` ## * `true` - generate only one timeout event ## * `false` - generate timeout events periodically @@ -1077,8 +1014,8 @@ when defined(windows) or defined(nimdoc): registerWaitableHandle(p, hEvent, flags, pcd, timeout, timercb) proc addProcess*(pid: int, cb: Callback) = - ## Registers callback `cb` to be called when process with process ID - ## `pid` exited. + ## Registers callback ``cb`` to be called when process with process ID + ## ``pid`` exited. const NULL = Handle(0) let p = getGlobalDispatcher() let procFlags = SYNCHRONIZE @@ -1087,7 +1024,7 @@ when defined(windows) or defined(nimdoc): raiseOSError(osLastError()) var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData))) - var flags = WT_EXECUTEINWAITTHREAD.DWORD or WT_EXECUTEONLYONCE.DWORD + var flags = WT_EXECUTEINWAITTHREAD.DWORD proc proccb(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = closeWaitable(hProcess) @@ -1096,10 +1033,10 @@ when defined(windows) or defined(nimdoc): registerWaitableHandle(p, hProcess, flags, pcd, INFINITE, proccb) proc newAsyncEvent*(): AsyncEvent = - ## Creates a new thread-safe `AsyncEvent` object. + ## Creates a new thread-safe ``AsyncEvent`` object. ## - ## New `AsyncEvent` object is not automatically registered with - ## dispatcher like `AsyncSocket`. + ## New ``AsyncEvent`` object is not automatically registered with + ## dispatcher like ``AsyncSocket``. var sa = SECURITY_ATTRIBUTES( nLength: sizeof(SECURITY_ATTRIBUTES).cint, bInheritHandle: 1 @@ -1111,12 +1048,12 @@ when defined(windows) or defined(nimdoc): result.hEvent = event proc trigger*(ev: AsyncEvent) = - ## Set event `ev` to signaled state. + ## Set event ``ev`` to signaled state. if setEvent(ev.hEvent) == 0: raiseOSError(osLastError()) proc unregister*(ev: AsyncEvent) = - ## Unregisters event `ev`. + ## Unregisters event ``ev``. doAssert(ev.hWaiter != 0, "Event is not registered in the queue!") let p = getGlobalDispatcher() p.handles.excl(AsyncFD(ev.hEvent)) @@ -1127,14 +1064,14 @@ when defined(windows) or defined(nimdoc): ev.hWaiter = 0 proc close*(ev: AsyncEvent) = - ## Closes event `ev`. + ## Closes event ``ev``. let res = closeHandle(ev.hEvent) deallocShared(cast[pointer](ev)) if res == 0: raiseOSError(osLastError()) proc addEvent*(ev: AsyncEvent, cb: Callback) = - ## Registers callback `cb` to be called when `ev` will be signaled + ## Registers callback ``cb`` to be called when ``ev`` will be signaled doAssert(ev.hWaiter == 0, "Event is already registered in the queue!") let p = getGlobalDispatcher() @@ -1171,9 +1108,6 @@ else: MSG_NOSIGNAL when declared(posix.accept4): from posix import accept4, SOCK_CLOEXEC - when defined(genode): - import genode/env # get the implicit Genode env - import genode/signals const InitCallbackListSize = 4 # initial size of callbacks sequence, @@ -1192,8 +1126,6 @@ else: PDispatcher* = ref object of PDispatcherBase selector: Selector[AsyncData] - when defined(genode): - signalHandler: SignalHandler proc `==`*(x, y: AsyncFD): bool {.borrow.} proc `==`*(x, y: AsyncEvent): bool {.borrow.} @@ -1209,22 +1141,9 @@ else: result.selector = newSelector[AsyncData]() result.timers.clear() result.callbacks = initDeque[proc () {.closure, gcsafe.}](InitDelayedCallbackListSize) - when defined(genode): - let entrypoint = ep(cast[GenodeEnv](runtimeEnv)) - result.signalHandler = newSignalHandler(entrypoint): - discard runOnce(0) var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher - when defined(nuttx): - import std/exitprocs - - proc cleanDispatcher() {.noconv.} = - gDisp = nil - - proc addFinalyzer() = - addExitProc(cleanDispatcher) - proc setGlobalDispatcher*(disp: owned PDispatcher) = if not gDisp.isNil: assert gDisp.callbacks.len == 0 @@ -1234,8 +1153,6 @@ else: proc getGlobalDispatcher*(): PDispatcher = if gDisp.isNil: setGlobalDispatcher(newDispatcher()) - when defined(nuttx): - addFinalyzer() result = gDisp proc getIoHandler*(disp: PDispatcher): Selector[AsyncData] = @@ -1362,7 +1279,7 @@ else: var newList = newSeqOfCap[Callback](newLength) var cb = curList[0] - if not cb(fd): + if not cb(fd.AsyncFD): newList.add(cb) withData(p.selector, fd.int, adata) do: @@ -1393,11 +1310,10 @@ else: ValueError, "Expecting async operations to stop when fd has closed." ) - proc runOnce(timeout: int): bool = + + proc runOnce(timeout = 500): bool = let p = getGlobalDispatcher() if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0: - when defined(genode): - if timeout == 0: return raise newException(ValueError, "No handles or timers registered in dispatcher.") @@ -1544,8 +1460,8 @@ else: proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr, saddrLen: SockLen, flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = - ## Sends `data` of size `size` in bytes to specified destination - ## (`saddr` of size `saddrLen` in bytes, using socket `socket`. + ## Sends ``data`` of size ``size`` in bytes to specified destination + ## (``saddr`` of size ``saddrLen`` in bytes, using socket ``socket``. ## The returned future will complete once all data has been sent. var retFuture = newFuture[void]("sendTo") @@ -1575,9 +1491,9 @@ else: proc recvFromInto*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr, saddrLen: ptr SockLen, flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = - ## Receives a datagram data from `socket` into `data`, which must - ## be at least of size `size` in bytes, address of datagram's sender - ## will be stored into `saddr` and `saddrLen`. Returned future will + ## Receives a datagram data from ``socket`` into ``data``, which must + ## be at least of size ``size`` in bytes, address of datagram's sender + ## will be stored into ``saddr`` and ``saddrLen``. Returned future will ## complete once one datagram has been received, and will return size ## of packet received. var retFuture = newFuture[int]("recvFromInto") @@ -1602,7 +1518,7 @@ else: owned(Future[tuple[address: string, client: AsyncFD]]) = var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr") - proc cb(sock: AsyncFD): bool {.gcsafe.} = + proc cb(sock: AsyncFD): bool = result = true var sockAddress: Sockaddr_storage var addrLen = sizeof(sockAddress).SockLen @@ -1647,45 +1563,45 @@ else: proc addTimer*(timeout: int, oneshot: bool, cb: Callback) = ## Start watching for timeout expiration, and then call the - ## callback `cb`. - ## `timeout` - time in milliseconds, - ## `oneshot` - if `true` only one event will be dispatched, - ## if `false` continuous events every `timeout` milliseconds. + ## callback ``cb``. + ## ``timeout`` - time in milliseconds, + ## ``oneshot`` - if ``true`` only one event will be dispatched, + ## if ``false`` continuous events every ``timeout`` milliseconds. let p = getGlobalDispatcher() var data = newAsyncData() data.readList.add(cb) p.selector.registerTimer(timeout, oneshot, data) proc addSignal*(signal: int, cb: Callback) = - ## Start watching signal `signal`, and when signal appears, call the - ## callback `cb`. + ## Start watching signal ``signal``, and when signal appears, call the + ## callback ``cb``. let p = getGlobalDispatcher() var data = newAsyncData() data.readList.add(cb) p.selector.registerSignal(signal, data) proc addProcess*(pid: int, cb: Callback) = - ## Start watching for process exit with pid `pid`, and then call - ## the callback `cb`. + ## Start watching for process exit with pid ``pid``, and then call + ## the callback ``cb``. let p = getGlobalDispatcher() var data = newAsyncData() data.readList.add(cb) p.selector.registerProcess(pid, data) proc newAsyncEvent*(): AsyncEvent = - ## Creates new `AsyncEvent`. + ## Creates new ``AsyncEvent``. result = AsyncEvent(newSelectEvent()) proc trigger*(ev: AsyncEvent) = - ## Sets new `AsyncEvent` to signaled state. + ## Sets new ``AsyncEvent`` to signaled state. trigger(SelectEvent(ev)) proc close*(ev: AsyncEvent) = - ## Closes `AsyncEvent` + ## Closes ``AsyncEvent`` close(SelectEvent(ev)) proc addEvent*(ev: AsyncEvent, cb: Callback) = - ## Start watching for event `ev`, and call callback `cb`, when + ## Start watching for event ``ev``, and call callback ``cb``, when ## ev will be set to signaled state. let p = getGlobalDispatcher() var data = newAsyncData() @@ -1693,8 +1609,8 @@ else: p.selector.registerEvent(SelectEvent(ev), data) proc drain*(timeout = 500) = - ## Waits for completion of **all** events and processes them. Raises `ValueError` - ## if there are no pending operations. In contrast to `poll` this + ## Waits for completion of **all** events and processes them. Raises ``ValueError`` + ## if there are no pending operations. In contrast to ``poll`` this ## processes as many events as are available until the timeout has elapsed. var curTimeout = timeout let start = now() @@ -1705,7 +1621,7 @@ proc drain*(timeout = 500) = break proc poll*(timeout = 500) = - ## Waits for completion events and processes them. Raises `ValueError` + ## Waits for completion events and processes them. Raises ``ValueError`` ## if there are no pending operations. This runs the underlying OS ## `epoll`:idx: or `kqueue`:idx: primitive only once. discard runOnce(timeout) @@ -1759,8 +1675,6 @@ 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))) @@ -1772,13 +1686,13 @@ when defined(windows) or defined(nimdoc): if ret: # Request to connect completed immediately. retFuture.complete() - # We don't deallocate `ol` here because even though this completed + # We don't deallocate ``ol`` here because even though this completed # immediately poll will still be notified about its completion and it - # will free `ol`. + # will free ``ol``. else: let lastError = osLastError() if lastError.int32 != ERROR_IO_PENDING: - # With ERROR_IO_PENDING `ol` will be deallocated in `poll`, + # With ERROR_IO_PENDING ``ol`` will be deallocated in ``poll``, # and the future will be completed/failed there, too. GC_unref(ol) retFuture.fail(newException(OSError, osErrorMsg(lastError))) @@ -1851,7 +1765,7 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, curAddrInfo = curAddrInfo.ai_next if curAddrInfo == nil: - freeAddrInfo(addrInfo) + freeaddrinfo(addrInfo) when shouldCreateFd: closeUnusedFds() if lastException != nil: @@ -1867,7 +1781,7 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, try: curFd = createAsyncNativeSocket(domain, sockType, protocol) except: - freeAddrInfo(addrInfo) + freeaddrinfo(addrInfo) closeUnusedFds() raise getCurrentException() when defined(windows): @@ -1877,7 +1791,7 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo curAddrInfo = curAddrInfo.ai_next else: - freeAddrInfo(addrInfo) + freeaddrinfo(addrInfo) when shouldCreateFd: closeUnusedFds(ord(domain)) retFuture.complete(curFd) @@ -1888,9 +1802,9 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, proc dial*(address: string, port: Port, protocol: Protocol = IPPROTO_TCP): owned(Future[AsyncFD]) = - ## Establishes connection to the specified `address`:`port` pair via the + ## Establishes connection to the specified ``address``:``port`` pair via the ## specified protocol. The procedure iterates through possible - ## resolutions of the `address` until it succeeds, meaning that it + ## resolutions of the ``address`` until it succeeds, meaning that it ## seamlessly works with both IPv4 and IPv6. ## Returns the async file descriptor, registered in the dispatcher of ## the current thread, ready to send or receive data. @@ -1918,7 +1832,7 @@ proc connect*(socket: AsyncFD, address: string, port: Port, proc sleepAsync*(ms: int | float): owned(Future[void]) = ## Suspends the execution of the current async procedure for the next - ## `ms` milliseconds. + ## ``ms`` milliseconds. var retFuture = newFuture[void]("sleepAsync") let p = getGlobalDispatcher() when ms is int: @@ -1929,11 +1843,11 @@ proc sleepAsync*(ms: int | float): owned(Future[void]) = return retFuture proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) = - ## Returns a future which will complete once `fut` completes or after - ## `timeout` milliseconds has elapsed. + ## Returns a future which will complete once ``fut`` completes or after + ## ``timeout`` milliseconds has elapsed. ## - ## If `fut` completes first the returned future will hold true, - ## otherwise, if `timeout` milliseconds has elapsed first, the returned + ## If ``fut`` completes first the returned future will hold true, + ## otherwise, if ``timeout`` milliseconds has elapsed first, the returned ## future will hold false. var retFuture = newFuture[bool]("asyncdispatch.`withTimeout`") @@ -1956,7 +1870,7 @@ proc accept*(socket: AsyncFD, ## Accepts a new connection. Returns a future containing the client socket ## corresponding to that connection. ## - ## If `inheritable` is false (the default), the resulting client socket + ## If ``inheritable`` is false (the default), the resulting client socket ## will not be inheritable by child processes. ## ## The future will complete when the connection is successfully accepted. @@ -1976,7 +1890,7 @@ proc keepAlive(x: string) = proc send*(socket: AsyncFD, data: string, flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = - ## Sends `data` to `socket`. The returned future will complete once all + ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. var retFuture = newFuture[void]("send") if data.len > 0: @@ -1994,8 +1908,7 @@ proc send*(socket: AsyncFD, data: string, return retFuture # -- Await Macro -import asyncmacro -export asyncmacro +include asyncmacro proc readAll*(future: FutureStream[string]): owned(Future[string]) {.async.} = ## Returns a future that will complete when all the string data from the @@ -2034,32 +1947,15 @@ proc activeDescriptors*(): int {.inline.} = when defined(posix): import posix -when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or - defined(solaris) or defined(zephyr) or defined(freertos) or defined(nuttx): +when defined(linux) or defined(windows) or defined(macosx) or defined(bsd): proc maxDescriptors*(): int {.raises: OSError.} = ## Returns the maximum number of active file descriptors for the current ## process. This involves a system call. For now `maxDescriptors` is - ## supported on the following OSes: Windows, Linux, OSX, BSD, Solaris. + ## supported on the following OSes: Windows, Linux, OSX, BSD. when defined(windows): result = 16_700_000 - elif defined(zephyr) or defined(freertos): - result = FD_MAX else: var fdLim: RLimit if getrlimit(RLIMIT_NOFILE, fdLim) < 0: raiseOSError(osLastError()) result = int(fdLim.rlim_cur) - 1 - -when defined(genode): - proc scheduleCallbacks*(): bool {.discardable.} = - ## *Genode only.* - ## Schedule callback processing and return immediately. - ## Returns `false` if there is nothing to schedule. - ## RPC servers should call this to dispatch `callSoon` - ## bodies after retiring an RPC to its client. - ## This is effectively a non-blocking `poll(…)` and is - ## equivalent to scheduling a momentary no-op timeout - ## but faster and with less overhead. - let dis = getGlobalDispatcher() - result = dis.callbacks.len > 0 - if result: submit(dis.signalHandler.cap) diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 96d5e6d2e..71b42c763 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -9,27 +9,21 @@ ## This module implements asynchronous file reading and writing. ## -## ```Nim -## import std/[asyncfile, asyncdispatch, os] +## .. code-block:: Nim +## import asyncfile, asyncdispatch, os ## -## proc main() {.async.} = -## var file = openAsync(getTempDir() / "foobar.txt", fmReadWrite) -## await file.write("test") -## file.setFilePos(0) -## let data = await file.readAll() -## doAssert data == "test" -## file.close() +## proc main() {.async.} = +## var file = openAsync(getTempDir() / "foobar.txt", fmReadWrite) +## await file.write("test") +## file.setFilePos(0) +## let data = await file.readAll() +## doAssert data == "test" +## file.close() ## -## waitFor main() -## ``` +## waitFor main() import asyncdispatch, os -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - when defined(windows) or defined(nimdoc): - import std/widestrs - # TODO: Fix duplication introduced by PR #4683. when defined(windows) or defined(nimdoc): @@ -96,15 +90,20 @@ proc newAsyncFile*(fd: AsyncFD): AsyncFile = register(fd) proc openAsync*(filename: string, mode = fmRead): AsyncFile = - ## Opens a file specified by the path in `filename` using - ## the specified FileMode `mode` asynchronously. + ## Opens a file specified by the path in ``filename`` using + ## the specified FileMode ``mode`` asynchronously. when defined(windows) or defined(nimdoc): let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL let desiredAccess = getDesiredAccess(mode) let creationDisposition = getCreationDisposition(mode, filename) - let fd = createFileW(newWideCString(filename), desiredAccess, - FILE_SHARE_READ, - nil, creationDisposition, flags, 0) + when useWinUnicode: + let fd = createFileW(newWideCString(filename), desiredAccess, + FILE_SHARE_READ, + nil, creationDisposition, flags, 0) + else: + let fd = createFileA(filename, desiredAccess, + FILE_SHARE_READ, + nil, creationDisposition, flags, 0) if fd == INVALID_HANDLE_VALUE: raiseOSError(osLastError()) @@ -125,11 +124,11 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile = result = newAsyncFile(fd.AsyncFD) proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] = - ## Read `size` bytes from the specified file asynchronously starting at + ## Read ``size`` bytes from the specified file asynchronously starting at ## the current position of the file pointer. ## ## If the file pointer is past the end of the file then zero is returned - ## and no bytes are read into `buf` + ## and no bytes are read into ``buf`` var retFuture = newFuture[int]("asyncfile.readBuffer") when defined(windows) or defined(nimdoc): @@ -202,12 +201,11 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] = return retFuture proc read*(f: AsyncFile, size: int): Future[string] = - ## Read `size` bytes from the specified file asynchronously starting at - ## the current position of the file pointer. `size` should be greater than zero. + ## Read ``size`` bytes from the specified file asynchronously starting at + ## the current position of the file pointer. ## ## If the file pointer is past the end of the file then an empty string is ## returned. - assert size > 0 var retFuture = newFuture[string]("asyncfile.read") when defined(windows) or defined(nimdoc): @@ -334,7 +332,7 @@ proc readAll*(f: AsyncFile): Future[string] {.async.} = result.add data proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] = - ## Writes `size` bytes from `buf` to the file specified asynchronously. + ## Writes ``size`` bytes from ``buf`` to the file specified asynchronously. ## ## The returned Future will complete once all data has been written to the ## specified file. @@ -403,7 +401,7 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] = return retFuture proc write*(f: AsyncFile, data: string): Future[void] = - ## Writes `data` to the file specified asynchronously. + ## Writes ``data`` to the file specified asynchronously. ## ## The returned Future will complete once all data has been written to the ## specified file. diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim new file mode 100644 index 000000000..b538e10c9 --- /dev/null +++ b/lib/pure/asyncftpclient.nim @@ -0,0 +1,448 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Dominik Picheta +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements an asynchronous FTP client. It allows you to connect +## to an FTP server and perform operations on it such as for example: +## +## * The upload of new files. +## * The removal of existing files. +## * Download of files. +## * Changing of files' permissions. +## * Navigation through the FTP server's directories. +## +## Connecting to an FTP server +## =========================== +## +## In order to begin any sort of transfer of files you must first +## connect to an FTP server. You can do so with the ``connect`` procedure. +## +## .. code-block::nim +## import asyncdispatch, asyncftpclient +## proc main() {.async.} = +## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") +## await ftp.connect() +## echo("Connected") +## waitFor(main()) +## +## A new ``main`` async procedure must be declared to allow the use of the +## ``await`` keyword. The connection will complete asynchronously and the +## client will be connected after the ``await ftp.connect()`` call. +## +## Uploading a new file +## ==================== +## +## After a connection is made you can use the ``store`` procedure to upload +## a new file to the FTP server. Make sure to check you are in the correct +## working directory before you do so with the ``pwd`` procedure, you can also +## instead specify an absolute path. +## +## .. code-block::nim +## import asyncdispatch, asyncftpclient +## proc main() {.async.} = +## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") +## await ftp.connect() +## let currentDir = await ftp.pwd() +## assert currentDir == "/home/user/" +## await ftp.store("file.txt", "file.txt") +## echo("File finished uploading") +## waitFor(main()) +## +## Checking the progress of a file transfer +## ======================================== +## +## The progress of either a file upload or a file download can be checked +## by specifying a ``onProgressChanged`` procedure to the ``store`` or +## ``retrFile`` procedures. +## +## Procs that take an ``onProgressChanged`` callback will call this every +## ``progressInterval`` milliseconds. +## +## .. code-block::nim +## import asyncdispatch, asyncftpclient +## +## proc onProgressChanged(total, progress: BiggestInt, +## speed: float) {.async.} = +## echo("Uploaded ", progress, " of ", total, " bytes") +## echo("Current speed: ", speed, " kb/s") +## +## proc main() {.async.} = +## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test", progressInterval = 500) +## await ftp.connect() +## await ftp.store("file.txt", "/home/user/file.txt", onProgressChanged) +## echo("File finished uploading") +## waitFor(main()) + + +import asyncdispatch, asyncnet, nativesockets, strutils, parseutils, os, times +from net import BufferSize + +type + AsyncFtpClient* = ref object + csock*: AsyncSocket + dsock*: AsyncSocket + user*, pass*: string + address*: string + port*: Port + progressInterval: int + jobInProgress*: bool + job*: FtpJob + dsockConnected*: bool + + FtpJobType* = enum + JRetrText, JRetr, JStore + + FtpJob = ref object + prc: proc (ftp: AsyncFtpClient, async: bool): bool {.nimcall, gcsafe.} + case typ*: FtpJobType + of JRetrText: + lines: string + of JRetr, JStore: + file: File + filename: string + total: BiggestInt # In bytes. + progress: BiggestInt # In bytes. + oneSecond: BiggestInt # Bytes transferred in one second. + lastProgressReport: float # Time + toStore: string # Data left to upload (Only used with async) + + FtpEventType* = enum + EvTransferProgress, EvLines, EvRetr, EvStore + + FtpEvent* = object ## Event + filename*: string + case typ*: FtpEventType + of EvLines: + lines*: string ## Lines that have been transferred. + of EvRetr, EvStore: ## Retr/Store operation finished. + nil + of EvTransferProgress: + bytesTotal*: BiggestInt ## Bytes total. + bytesFinished*: BiggestInt ## Bytes transferred. + speed*: BiggestInt ## Speed in bytes/s + currentJob*: FtpJobType ## The current job being performed. + + ReplyError* = object of IOError + + ProgressChangedProc* = + proc (total, progress: BiggestInt, speed: float): + Future[void] {.closure, gcsafe.} + +const multiLineLimit = 10000 + +proc expectReply(ftp: AsyncFtpClient): Future[TaintedString] {.async.} = + var line = await ftp.csock.recvLine() + result = TaintedString(line) + var count = 0 + while line.len > 3 and line[3] == '-': + ## Multi-line reply. + line = await ftp.csock.recvLine() + string(result).add("\n" & line) + count.inc() + if count >= multiLineLimit: + raise newException(ReplyError, "Reached maximum multi-line reply count.") + +proc send*(ftp: AsyncFtpClient, m: string): Future[TaintedString] {.async.} = + ## Send a message to the server, and wait for a primary reply. + ## ``\c\L`` is added for you. + ## + ## You need to make sure that the message ``m`` doesn't contain any newline + ## characters. Failing to do so will raise ``AssertionDefect``. + ## + ## **Note:** The server may return multiple lines of coded replies. + doAssert(not m.contains({'\c', '\L'}), "message shouldn't contain any newline characters") + await ftp.csock.send(m & "\c\L") + return await ftp.expectReply() + +proc assertReply(received: TaintedString, expected: varargs[string]) = + for i in items(expected): + if received.string.startsWith(i): return + raise newException(ReplyError, + "Expected reply '$1' got: $2" % + [expected.join("' or '"), received.string]) + +proc pasv(ftp: AsyncFtpClient) {.async.} = + ## Negotiate a data connection. + ftp.dsock = newAsyncSocket() + + var pasvMsg = (await ftp.send("PASV")).string.strip.TaintedString + assertReply(pasvMsg, "227") + var betweenParens = captureBetween(pasvMsg.string, '(', ')') + var nums = betweenParens.split(',') + var ip = nums[0 .. ^3] + var port = nums[^2 .. ^1] + var properPort = port[0].parseInt()*256+port[1].parseInt() + await ftp.dsock.connect(ip.join("."), Port(properPort)) + ftp.dsockConnected = true + +proc normalizePathSep(path: string): string = + return replace(path, '\\', '/') + +proc connect*(ftp: AsyncFtpClient) {.async.} = + ## Connect to the FTP server specified by ``ftp``. + await ftp.csock.connect(ftp.address, ftp.port) + + var reply = await ftp.expectReply() + if string(reply).startsWith("120"): + # 120 Service ready in nnn minutes. + # We wait until we receive 220. + reply = await ftp.expectReply() + + # Handle 220 messages from the server + assertReply(reply, "220") + + if ftp.user != "": + assertReply(await(ftp.send("USER " & ftp.user)), "230", "331") + + if ftp.pass != "": + assertReply(await(ftp.send("PASS " & ftp.pass)), "230") + +proc pwd*(ftp: AsyncFtpClient): Future[TaintedString] {.async.} = + ## Returns the current working directory. + let wd = await ftp.send("PWD") + assertReply wd, "257" + return wd.string.captureBetween('"').TaintedString # " + +proc cd*(ftp: AsyncFtpClient, dir: string) {.async.} = + ## Changes the current directory on the remote FTP server to ``dir``. + assertReply(await(ftp.send("CWD " & dir.normalizePathSep)), "250") + +proc cdup*(ftp: AsyncFtpClient) {.async.} = + ## Changes the current directory to the parent of the current directory. + assertReply(await(ftp.send("CDUP")), "200") + +proc getLines(ftp: AsyncFtpClient): Future[string] {.async.} = + ## Downloads text data in ASCII mode + result = "" + assert ftp.dsockConnected + while ftp.dsockConnected: + let r = await ftp.dsock.recvLine() + if r.string == "": + ftp.dsockConnected = false + else: + result.add(r.string & "\n") + + assertReply(await(ftp.expectReply()), "226") + +proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} = + ## Returns a list of filenames in the given directory. If ``dir`` is "", + ## the current directory is used. If ``async`` is true, this + ## function will return immediately and it will be your job to + ## use asyncdispatch's ``poll`` to progress this operation. + await ftp.pasv() + + assertReply(await(ftp.send("NLST " & dir.normalizePathSep)), ["125", "150"]) + + result = splitLines(await ftp.getLines()) + +proc fileExists*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} = + ## Determines whether ``file`` exists. + var files = await ftp.listDirs() + for f in items(files): + if f.normalizePathSep == file.normalizePathSep: return true + +proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} = + ## Creates a directory ``dir``. If ``recursive`` is true, the topmost + ## subdirectory of ``dir`` will be created first, following the secondmost... + ## etc. this allows you to give a full path as the ``dir`` without worrying + ## about subdirectories not existing. + if not recursive: + assertReply(await(ftp.send("MKD " & dir.normalizePathSep)), "257") + else: + var reply = TaintedString"" + var previousDirs = "" + for p in split(dir, {os.DirSep, os.AltSep}): + if p != "": + previousDirs.add(p) + reply = await ftp.send("MKD " & previousDirs) + previousDirs.add('/') + assertReply reply, "257" + +proc chmod*(ftp: AsyncFtpClient, path: string, + permissions: set[FilePermission]) {.async.} = + ## Changes permission of ``path`` to ``permissions``. + var userOctal = 0 + var groupOctal = 0 + var otherOctal = 0 + for i in items(permissions): + case i + of fpUserExec: userOctal.inc(1) + of fpUserWrite: userOctal.inc(2) + of fpUserRead: userOctal.inc(4) + of fpGroupExec: groupOctal.inc(1) + of fpGroupWrite: groupOctal.inc(2) + of fpGroupRead: groupOctal.inc(4) + of fpOthersExec: otherOctal.inc(1) + of fpOthersWrite: otherOctal.inc(2) + of fpOthersRead: otherOctal.inc(4) + + var perm = $userOctal & $groupOctal & $otherOctal + assertReply(await(ftp.send("SITE CHMOD " & perm & + " " & path.normalizePathSep)), "200") + +proc list*(ftp: AsyncFtpClient, dir = ""): Future[string] {.async.} = + ## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current + ## working directory. + await ftp.pasv() + + let reply = await ftp.send("LIST" & " " & dir.normalizePathSep) + assertReply(reply, ["125", "150"]) + + result = await ftp.getLines() + +proc retrText*(ftp: AsyncFtpClient, file: string): Future[string] {.async.} = + ## Retrieves ``file``. File must be ASCII text. + await ftp.pasv() + let reply = await ftp.send("RETR " & file.normalizePathSep) + assertReply(reply, ["125", "150"]) + + result = await ftp.getLines() + +proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt, + onProgressChanged: ProgressChangedProc) {.async.} = + assert ftp.dsockConnected + var progress = 0 + var progressInSecond = 0 + var countdownFut = sleepAsync(ftp.progressInterval) + var dataFut = ftp.dsock.recv(BufferSize) + while ftp.dsockConnected: + await dataFut or countdownFut + if countdownFut.finished: + asyncCheck onProgressChanged(total, progress, + progressInSecond.float) + progressInSecond = 0 + countdownFut = sleepAsync(ftp.progressInterval) + + if dataFut.finished: + let data = dataFut.read + if data != "": + progress.inc(data.len) + progressInSecond.inc(data.len) + file.write(data) + dataFut = ftp.dsock.recv(BufferSize) + else: + ftp.dsockConnected = false + + assertReply(await(ftp.expectReply()), "226") + +proc defaultOnProgressChanged*(total, progress: BiggestInt, + speed: float): Future[void] {.nimcall, gcsafe.} = + ## Default FTP ``onProgressChanged`` handler. Does nothing. + result = newFuture[void]() + #echo(total, " ", progress, " ", speed) + result.complete() + +proc retrFile*(ftp: AsyncFtpClient, file, dest: string, + onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} = + ## Downloads ``file`` and saves it to ``dest``. + ## The ``EvRetr`` event is passed to the specified ``handleEvent`` function + ## when the download is finished. The event's ``filename`` field will be equal + ## to ``file``. + var destFile = open(dest, mode = fmWrite) + await ftp.pasv() + var reply = await ftp.send("RETR " & file.normalizePathSep) + assertReply reply, ["125", "150"] + if {'(', ')'} notin reply.string: + raise newException(ReplyError, "Reply has no file size.") + var fileSize: BiggestInt + if reply.string.captureBetween('(', ')').parseBiggestInt(fileSize) == 0: + raise newException(ReplyError, "Reply has no file size.") + + await getFile(ftp, destFile, fileSize, onProgressChanged) + destFile.close() + +proc doUpload(ftp: AsyncFtpClient, file: File, + onProgressChanged: ProgressChangedProc) {.async.} = + assert ftp.dsockConnected + + let total = file.getFileSize() + var data = newString(4000) + var progress = 0 + var progressInSecond = 0 + var countdownFut = sleepAsync(ftp.progressInterval) + var sendFut: Future[void] = nil + while ftp.dsockConnected: + if sendFut == nil or sendFut.finished: + # TODO: Async file reading. + let len = file.readBuffer(addr(data[0]), 4000) + setLen(data, len) + if len == 0: + # File finished uploading. + ftp.dsock.close() + ftp.dsockConnected = false + + assertReply(await(ftp.expectReply()), "226") + else: + progress.inc(len) + progressInSecond.inc(len) + sendFut = ftp.dsock.send(data) + + if countdownFut.finished: + asyncCheck onProgressChanged(total, progress, progressInSecond.float) + progressInSecond = 0 + countdownFut = sleepAsync(ftp.progressInterval) + + await countdownFut or sendFut + +proc store*(ftp: AsyncFtpClient, file, dest: string, + onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} = + ## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this + ## function asynchronously is recommended to view the progress of + ## the download. + ## The ``EvStore`` event is passed to the specified ``handleEvent`` function + ## when the upload is finished, and the ``filename`` field will be + ## equal to ``file``. + var destFile = open(file) + await ftp.pasv() + + let reply = await ftp.send("STOR " & dest.normalizePathSep) + assertReply reply, ["125", "150"] + + await doUpload(ftp, destFile, onProgressChanged) + +proc rename*(ftp: AsyncFtpClient, nameFrom: string, nameTo: string) {.async.} = + ## Rename a file or directory on the remote FTP Server from current name + ## ``name_from`` to new name ``name_to`` + assertReply(await ftp.send("RNFR " & nameFrom), "350") + assertReply(await ftp.send("RNTO " & nameTo), "250") + +proc removeFile*(ftp: AsyncFtpClient, filename: string) {.async.} = + ## Delete a file ``filename`` on the remote FTP server + assertReply(await ftp.send("DELE " & filename), "250") + +proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} = + ## Delete a directory ``dir`` on the remote FTP server + assertReply(await ftp.send("RMD " & dir), "250") + +proc newAsyncFtpClient*(address: string, port = Port(21), + user, pass = "", progressInterval: int = 1000): AsyncFtpClient = + ## Creates a new ``AsyncFtpClient`` object. + new result + result.user = user + result.pass = pass + result.address = address + result.port = port + result.progressInterval = progressInterval + result.dsockConnected = false + result.csock = newAsyncSocket() + +when not defined(testing) and isMainModule: + var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") + proc main(ftp: AsyncFtpClient) {.async.} = + await ftp.connect() + echo await ftp.pwd() + echo await ftp.listDirs() + await ftp.store("payload.jpg", "payload.jpg") + await ftp.retrFile("payload.jpg", "payload2.jpg") + await ftp.rename("payload.jpg", "payload_renamed.jpg") + await ftp.store("payload.jpg", "payload_remove.jpg") + await ftp.removeFile("payload_remove.jpg") + await ftp.createDir("deleteme") + await ftp.removeDir("deleteme") + echo("Finished") + + waitFor main(ftp) diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 51aaaca36..2333c5ef1 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -9,11 +9,7 @@ import os, tables, strutils, times, heapqueue, options, deques, cstrutils -import system/stacktraces - -when defined(nimPreviewSlimSystem): - import std/objectdollar # for StackTraceEntry - import std/assertions +import "system/stacktraces" # TODO: This shouldn't need to be included, but should ideally be exported. type @@ -29,7 +25,7 @@ type finished: bool error*: ref Exception ## Stored exception errorStackTrace*: string - when not defined(release) or defined(futureLogging): + when not defined(release): stackTrace: seq[StackTraceEntry] ## For debugging purposes only. id: int fromProc: string @@ -90,19 +86,19 @@ when isFutureLoggingEnabled: var callSoonProc {.threadvar.}: proc (cbproc: proc ()) {.gcsafe.} proc getCallSoonProc*(): (proc(cbproc: proc ()) {.gcsafe.}) = - ## Get current implementation of `callSoon`. + ## Get current implementation of ``callSoon``. return callSoonProc proc setCallSoonProc*(p: (proc(cbproc: proc ()) {.gcsafe.})) = - ## Change current implementation of `callSoon`. This is normally called when dispatcher from `asyncdispatcher` is initialized. + ## Change current implementation of ``callSoon``. This is normally called when dispatcher from ``asyncdispatcher`` is initialized. callSoonProc = p -proc callSoon*(cbproc: proc () {.gcsafe.}) = - ## Call `cbproc` "soon". +proc callSoon*(cbproc: proc ()) = + ## Call ``cbproc`` "soon". ## - ## If async dispatcher is running, `cbproc` will be executed during next dispatcher tick. + ## If async dispatcher is running, ``cbproc`` will be executed during next dispatcher tick. ## - ## If async dispatcher is not running, `cbproc` will be executed immediately. + ## If async dispatcher is not running, ``cbproc`` will be executed immediately. if callSoonProc.isNil: # Loop not initialized yet. Call the function directly to allow setup code to use futures. cbproc() @@ -121,29 +117,29 @@ template setupFutureBase(fromProc: string) = proc newFuture*[T](fromProc: string = "unspecified"): owned(Future[T]) = ## Creates a new future. ## - ## Specifying `fromProc`, which is a string specifying the name of the proc + ## Specifying ``fromProc``, which is a string specifying the name of the proc ## that this future belongs to, is a good habit as it helps with debugging. setupFutureBase(fromProc) when isFutureLoggingEnabled: logFutureStart(result) proc newFutureVar*[T](fromProc = "unspecified"): owned(FutureVar[T]) = - ## Create a new `FutureVar`. This Future type is ideally suited for + ## Create a new ``FutureVar``. This Future type is ideally suited for ## situations where you want to avoid unnecessary allocations of Futures. ## - ## Specifying `fromProc`, which is a string specifying the name of the proc + ## Specifying ``fromProc``, which is a string specifying the name of the proc ## that this future belongs to, is a good habit as it helps with debugging. let fo = newFuture[T](fromProc) result = typeof(result)(fo) when isFutureLoggingEnabled: logFutureStart(Future[T](result)) proc clean*[T](future: FutureVar[T]) = - ## Resets the `finished` status of `future`. + ## Resets the ``finished`` status of ``future``. Future[T](future).finished = false Future[T](future).error = nil proc checkFinished[T](future: Future[T]) = ## Checks whether `future` is finished. If it is then raises a - ## `FutureError`. + ## ``FutureError``. when not defined(release): if future.finished: var msg = "" @@ -193,25 +189,27 @@ proc add(callbacks: var CallbackList, function: CallbackFunc) = last = last.next last.next = newCallback -proc completeImpl[T, U](future: Future[T], val: U, isVoid: static bool) = +proc complete*[T](future: Future[T], val: T) = + ## Completes ``future`` with value ``val``. #assert(not future.finished, "Future already finished, cannot finish twice.") checkFinished(future) assert(future.error == nil) - when not isVoid: - future.value = val + future.value = val future.finished = true future.callbacks.call() when isFutureLoggingEnabled: logFutureFinish(future) -proc complete*[T](future: Future[T], val: T) = - ## Completes `future` with value `val`. - completeImpl(future, val, false) - -proc complete*(future: Future[void], val = Future[void].default) = - completeImpl(future, (), true) +proc complete*(future: Future[void]) = + ## Completes a void ``future``. + #assert(not future.finished, "Future already finished, cannot finish twice.") + checkFinished(future) + assert(future.error == nil) + future.finished = true + future.callbacks.call() + when isFutureLoggingEnabled: logFutureFinish(future) proc complete*[T](future: FutureVar[T]) = - ## Completes a `FutureVar`. + ## Completes a ``FutureVar``. template fut: untyped = Future[T](future) checkFinished(fut) assert(fut.error == nil) @@ -220,7 +218,7 @@ proc complete*[T](future: FutureVar[T]) = when isFutureLoggingEnabled: logFutureFinish(Future[T](future)) proc complete*[T](future: FutureVar[T], val: T) = - ## Completes a `FutureVar` with value `val`. + ## Completes a ``FutureVar`` with value ``val``. ## ## Any previously stored value will be overwritten. template fut: untyped = Future[T](future) @@ -229,10 +227,10 @@ proc complete*[T](future: FutureVar[T], val: T) = fut.finished = true fut.value = val fut.callbacks.call() - when isFutureLoggingEnabled: logFutureFinish(fut) + when isFutureLoggingEnabled: logFutureFinish(future) proc fail*[T](future: Future[T], error: ref Exception) = - ## Completes `future` with `error`. + ## Completes ``future`` with ``error``. #assert(not future.finished, "Future already finished, cannot finish twice.") checkFinished(future) future.finished = true @@ -249,7 +247,7 @@ proc clearCallbacks*(future: FutureBase) = proc addCallback*(future: FutureBase, cb: proc() {.closure, gcsafe.}) = ## Adds the callbacks proc to be called when the future completes. ## - ## If future has already completed then `cb` will be called immediately. + ## If future has already completed then ``cb`` will be called immediately. assert cb != nil if future.finished: callSoon(cb) @@ -260,7 +258,7 @@ proc addCallback*[T](future: Future[T], cb: proc (future: Future[T]) {.closure, gcsafe.}) = ## Adds the callbacks proc to be called when the future completes. ## - ## If future has already completed then `cb` will be called immediately. + ## If future has already completed then ``cb`` will be called immediately. future.addCallback( proc() = cb(future) @@ -269,9 +267,9 @@ proc addCallback*[T](future: Future[T], proc `callback=`*(future: FutureBase, cb: proc () {.closure, gcsafe.}) = ## Clears the list of callbacks and sets the callback proc to be called when the future completes. ## - ## If future has already completed then `cb` will be called immediately. + ## If future has already completed then ``cb`` will be called immediately. ## - ## It's recommended to use `addCallback` or `then` instead. + ## It's recommended to use ``addCallback`` or ``then`` instead. future.clearCallbacks future.addCallback cb @@ -279,36 +277,22 @@ proc `callback=`*[T](future: Future[T], cb: proc (future: Future[T]) {.closure, gcsafe.}) = ## Sets the callback proc to be called when the future completes. ## - ## If future has already completed then `cb` will be called immediately. + ## If future has already completed then ``cb`` will be called immediately. future.callback = proc () = cb(future) -template getFilenameProcname(entry: StackTraceEntry): (string, string) = - when compiles(entry.filenameStr) and compiles(entry.procnameStr): - # We can't rely on "entry.filename" and "entry.procname" still being valid - # cstring pointers, because the "string.data" buffers they pointed to might - # be already garbage collected (this entry being a non-shallow copy, - # "entry.filename" no longer points to "entry.filenameStr.data", but to the - # buffer of the original object). - (entry.filenameStr, entry.procnameStr) - else: - ($entry.filename, $entry.procname) - proc getHint(entry: StackTraceEntry): string = ## We try to provide some hints about stack trace entries that the user ## may not be familiar with, in particular calls inside the stdlib. - - let (filename, procname) = getFilenameProcname(entry) - result = "" - if procname == "processPendingCallbacks": - if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0: + if entry.procname == cstring"processPendingCallbacks": + if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: return "Executes pending callbacks" - elif procname == "poll": - if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0: + elif entry.procname == cstring"poll": + if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: return "Processes asynchronous completion events" - if procname.endsWith(NimAsyncContinueSuffix): - if cmpIgnoreStyle(filename, "asyncmacro.nim") == 0: + if entry.procname.endsWith(NimAsyncContinueSuffix): + if cmpIgnoreStyle(entry.filename, "asyncmacro.nim") == 0: return "Resumes an async procedure" proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string = @@ -321,29 +305,33 @@ proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string = # Find longest filename & line number combo for alignment purposes. var longestLeft = 0 for entry in entries: - let (filename, procname) = getFilenameProcname(entry) + if entry.procname.isNil: continue - if procname == "": continue - - let leftLen = filename.len + len($entry.line) - if leftLen > longestLeft: - longestLeft = leftLen + let left = $entry.filename & $entry.line + if left.len > longestLeft: + longestLeft = left.len + var indent = 2 # Format the entries. for entry in entries: - let (filename, procname) = getFilenameProcname(entry) + if entry.procname.isNil: + if entry.line == reraisedFromBegin: + result.add(spaces(indent) & "#[\n") + indent.inc(2) + elif entry.line == reraisedFromEnd: + indent.dec(2) + result.add(spaces(indent) & "]#\n") + continue - if procname == "" and entry.line == reraisedFromBegin: - break - - let left = "$#($#)" % [filename, $entry.line] - result.add((spaces(2) & "$# $#\n") % [ + let left = "$#($#)" % [$entry.filename, $entry.line] + result.add((spaces(indent) & "$#$# $#\n") % [ left, - procname + spaces(longestLeft - left.len + 2), + $entry.procname ]) let hint = getHint(entry) if hint.len > 0: - result.add(spaces(4) & "## " & hint & "\n") + result.add(spaces(indent+2) & "## " & hint & "\n") proc injectStacktrace[T](future: Future[T]) = when not defined(release): @@ -363,22 +351,21 @@ proc injectStacktrace[T](future: Future[T]) = newMsg.add($entries) newMsg.add("Exception message: " & exceptionMsg & "\n") + newMsg.add("Exception type:") # # For debugging purposes - # newMsg.add("Exception type:") # for entry in getStackTraceEntries(future.error): # newMsg.add "\n" & $entry future.error.msg = newMsg proc read*[T](future: Future[T] | FutureVar[T]): T = - ## Retrieves the value of `future`. Future must be finished otherwise - ## this function will fail with a `ValueError` exception. + ## Retrieves the value of ``future``. Future must be finished otherwise + ## this function will fail with a ``ValueError`` exception. ## ## If the result of the future is an error then that error will be raised. - when future is Future[T]: - let fut {.cursor.} = future - else: - let fut {.cursor.} = Future[T](future) + {.push hint[ConvFromXtoItselfNotNeeded]: off.} + let fut = Future[T](future) + {.pop.} if fut.finished: if fut.error != nil: injectStacktrace(fut) @@ -390,40 +377,40 @@ proc read*[T](future: Future[T] | FutureVar[T]): T = raise newException(ValueError, "Future still in progress.") proc readError*[T](future: Future[T]): ref Exception = - ## Retrieves the exception stored in `future`. + ## Retrieves the exception stored in ``future``. ## - ## An `ValueError` exception will be thrown if no exception exists + ## An ``ValueError`` exception will be thrown if no exception exists ## in the specified Future. if future.error != nil: return future.error else: raise newException(ValueError, "No error in future.") proc mget*[T](future: FutureVar[T]): var T = - ## Returns a mutable value stored in `future`. + ## Returns a mutable value stored in ``future``. ## - ## Unlike `read`, this function will not raise an exception if the + ## Unlike ``read``, this function will not raise an exception if the ## Future has not been finished. result = Future[T](future).value proc finished*(future: FutureBase | FutureVar): bool = - ## Determines whether `future` has completed. + ## Determines whether ``future`` has completed. ## - ## `True` may indicate an error or a value. Use `failed` to distinguish. + ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. when future is FutureVar: result = (FutureBase(future)).finished else: result = future.finished proc failed*(future: FutureBase): bool = - ## Determines whether `future` completed with an error. + ## Determines whether ``future`` completed with an error. return future.error != nil proc asyncCheck*[T](future: Future[T]) = - ## Sets a callback on `future` which raises an exception if the future + ## Sets a callback on ``future`` which raises an exception if the future ## finished with an error. ## - ## This should be used instead of `discard` to discard void futures, - ## or use `waitFor` if you need to wait for the future's completion. + ## This should be used instead of ``discard`` to discard void futures, + ## or use ``waitFor`` if you need to wait for the future's completion. assert(not future.isNil, "Future is nil") # TODO: We can likely look at the stack trace here and inject the location # where the `asyncCheck` was called to give a better error stack message. @@ -434,7 +421,7 @@ proc asyncCheck*[T](future: Future[T]) = future.callback = asyncCheckCallback proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = - ## Returns a future which will complete once both `fut1` and `fut2` + ## Returns a future which will complete once both ``fut1`` and ``fut2`` ## complete. var retFuture = newFuture[void]("asyncdispatch.`and`") fut1.callback = @@ -450,7 +437,7 @@ proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = return retFuture proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = - ## Returns a future which will complete once either `fut1` or `fut2` + ## Returns a future which will complete once either ``fut1`` or ``fut2`` ## complete. var retFuture = newFuture[void]("asyncdispatch.`or`") proc cb[X](fut: Future[X]) = @@ -463,14 +450,14 @@ proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = proc all*[T](futs: varargs[Future[T]]): auto = ## Returns a future which will complete once - ## all futures in `futs` complete. + ## all futures in ``futs`` complete. ## If the argument is empty, the returned future completes immediately. ## - ## If the awaited futures are not `Future[void]`, the returned future + ## If the awaited futures are not ``Future[void]``, the returned future ## will hold the values of all awaited futures in a sequence. ## - ## If the awaited futures *are* `Future[void]`, - ## this proc returns `Future[void]`. + ## If the awaited futures *are* ``Future[void]``, + ## this proc returns ``Future[void]``. when T is void: var diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index c09fe35ca..d9f5a3a0f 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -14,38 +14,36 @@ ## application in production you should use a reverse proxy (for example nginx) ## instead of allowing users to connect directly to this server. -runnableExamples("-r:off"): - # This example will create an HTTP server on an automatically chosen port. - # It will respond to all requests with a `200 OK` response code and "Hello World" - # as the response body. - import std/asyncdispatch - proc main {.async.} = - var server = newAsyncHttpServer() - proc cb(req: Request) {.async.} = - echo (req.reqMethod, req.url, req.headers) - let headers = {"Content-type": "text/plain; charset=utf-8"} - await req.respond(Http200, "Hello World", headers.newHttpHeaders()) +runnableExamples: + # This example will create an HTTP server on port 8080. The server will + # respond to all requests with a `200 OK` response code and "Hello World" + # as the response body. Run locally with: + # `nim doc --doccmd:-d:nimAsynchttpserverEnableTest --lib:lib lib/pure/asynchttpserver.nim` + import asyncdispatch + if defined(nimAsynchttpserverEnableTest): + proc main {.async.} = + const port = 8080 + var server = newAsyncHttpServer() + proc cb(req: Request) {.async.} = + echo (req.reqMethod, req.url, req.headers) + let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", + "Content-type": "text/plain; charset=utf-8"} + await req.respond(Http200, "Hello World", headers.newHttpHeaders()) - server.listen(Port(0)) # or Port(8080) to hardcode the standard HTTP port. - let port = server.getPort - echo "test this with: curl localhost:" & $port.uint16 & "/" - while true: - if server.shouldAcceptRequest(): - await server.acceptRequest(cb) - else: - # too many concurrent connections, `maxFDs` exceeded - # wait 500ms for FDs to be closed - await sleepAsync(500) + echo "test this with: curl localhost:" & $port & "/" + server.listen Port(port) + while true: + if server.shouldAcceptRequest(): + await server.acceptRequest(cb) + else: + # too many concurrent connections, `maxFDs` exceeded + poll() - waitFor main() + asyncCheck main() + runForever() import asyncnet, asyncdispatch, parseutils, uri, strutils 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 @@ -74,22 +72,9 @@ type maxBody: int ## The maximum content-length that will be read for the body. maxFDs: int -proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} = - ## Returns the port `self` was bound to. - ## - ## Useful for identifying what port `self` is bound to, if it - ## was chosen automatically, for example via `listen(Port(0))`. - runnableExamples: - from std/nativesockets import Port - let server = newAsyncHttpServer() - server.listen(Port(0)) - assert server.getPort.uint16 > 0 - server.close() - result = getLocalAddr(self.socket)[1] - proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, maxBody = 8388608): AsyncHttpServer = - ## Creates a new `AsyncHttpServer` instance. + ## Creates a new ``AsyncHttpServer`` instance. result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort, maxBody: maxBody) proc addHeaders(msg: var string, headers: HttpHeaders) = @@ -104,15 +89,15 @@ proc sendHeaders*(req: Request, headers: HttpHeaders): Future[void] = proc respond*(req: Request, code: HttpCode, content: string, headers: HttpHeaders = nil): Future[void] = - ## Responds to the request with the specified `HttpCode`, headers and + ## Responds to the request with the specified ``HttpCode``, headers and ## content. ## ## This procedure will **not** close the client socket. ## ## Example: ## - ## .. code-block:: Nim - ## import std/json + ## .. code-block::nim + ## import json ## proc handler(req: Request) {.async.} = ## if req.url.path == "/hello-world": ## let msg = %* {"message": "Hello World"} @@ -137,7 +122,7 @@ proc respond*(req: Request, code: HttpCode, content: string, result = req.client.send(msg) proc respondError(req: Request, code: HttpCode): Future[void] = - ## Responds to the request with the specified `HttpCode`. + ## Responds to the request with the specified ``HttpCode``. let content = $code var msg = "HTTP/1.1 " & content & "\c\L" @@ -158,22 +143,11 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = proc sendStatus(client: AsyncSocket, status: string): Future[void] = client.send("HTTP/1.1 " & status & "\c\L\c\L") -func hasChunkedEncoding(request: Request): bool = - ## Searches for a chunked transfer encoding - const transferEncoding = "Transfer-Encoding" - - if request.headers.hasKey(transferEncoding): - for encoding in seq[string](request.headers[transferEncoding]): - if "chunked" == encoding.strip: - # Returns true if it is both an HttpPost and has chunked encoding - return request.reqMethod == HttpPost - return false - proc processRequest( server: AsyncHttpServer, req: FutureVar[Request], client: AsyncSocket, - address: sink string, + address: string, lineFut: FutureVar[string], callback: proc (request: Request): Future[void] {.closure, gcsafe.}, ): Future[bool] {.async.} = @@ -187,10 +161,7 @@ proc processRequest( # \n request.headers.clear() request.body = "" - when defined(gcArc) or defined(gcOrc): - request.hostname = address - else: - request.hostname.shallowCopy(address) + request.hostname.shallowCopy(address) assert client != nil request.client = client @@ -291,43 +262,6 @@ proc processRequest( if request.body.len != contentLength: await request.respond(Http400, "Bad Request. Content-Length does not match actual.") return true - elif hasChunkedEncoding(request): - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding - var sizeOrData = 0 - var bytesToRead = 0 - request.body = "" - - while true: - lineFut.mget.setLen(0) - lineFut.clean() - - # The encoding format alternates between specifying a number of bytes to read - # and the data to be read, of the previously specified size - if sizeOrData mod 2 == 0: - # Expect a number of chars to read - await client.recvLineInto(lineFut, maxLength = maxLine) - try: - bytesToRead = lineFut.mget.parseHexInt - except ValueError: - # Malformed request - await request.respond(Http411, ("Invalid chunked transfer encoding - " & - "chunk data size must be hex encoded")) - return true - else: - if bytesToRead == 0: - # Done reading chunked data - break - - # Read bytesToRead and add to body - let chunk = await client.recv(bytesToRead) - request.body.add(chunk) - # Skip \r\n (chunk terminating bytes per spec) - let separator = await client.recv(2) - if separator != "\r\n": - await request.respond(Http400, "Bad Request. Encoding separator must be \\r\\n") - return true - - inc sizeOrData elif request.reqMethod == HttpPost: await request.respond(Http411, "Content-Length required.") return true @@ -368,9 +302,7 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string let retry = await processRequest( server, request, client, address, lineFut, callback ) - if not retry: - client.close() - break + if not retry: break const nimMaxDescriptorsFallback* {.intdefine.} = 16_000 ## fallback value for \ @@ -378,18 +310,17 @@ const ## This can be set on the command line during compilation ## via `-d:nimMaxDescriptorsFallback=N` -proc listen*(server: AsyncHttpServer; port: Port; address = ""; domain = AF_INET) = +proc listen*(server: AsyncHttpServer; port: Port; address = "") = ## Listen to the given port and address. when declared(maxDescriptors): server.maxFDs = try: maxDescriptors() except: nimMaxDescriptorsFallback else: server.maxFDs = nimMaxDescriptorsFallback - server.socket = newAsyncSocket(domain) + server.socket = newAsyncSocket() if server.reuseAddr: server.socket.setSockOpt(OptReuseAddr, true) - when not defined(nuttx): - if server.reusePort: - server.socket.setSockOpt(OptReusePort, true) + if server.reusePort: + server.socket.setSockOpt(OptReusePort, true) server.socket.bindAddr(port, address) server.socket.listen() @@ -411,8 +342,7 @@ proc acceptRequest*(server: AsyncHttpServer, proc serve*(server: AsyncHttpServer, port: Port, callback: proc (request: Request): Future[void] {.closure, gcsafe.}, address = ""; - assumedDescriptorsPerRequest = -1; - domain = AF_INET) {.async.} = + assumedDescriptorsPerRequest = -1) {.async.} = ## Starts the process of listening for incoming HTTP connections on the ## specified address and port. ## @@ -425,7 +355,7 @@ proc serve*(server: AsyncHttpServer, port: Port, ## ## You should prefer to call `acceptRequest` instead with a custom server ## loop so that you're in control over the error handling and logging. - listen server, port, address, domain + listen server, port, address while true: if shouldAcceptRequest(server, assumedDescriptorsPerRequest): var (address, client) = await server.socket.acceptAddr() diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index d80a47101..c60c5e4ca 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -7,10 +7,11 @@ # distribution, for details about the copyright. # -## Implements the `async` and `multisync` macros for `asyncdispatch`. +## `asyncdispatch` module depends on the `asyncmacro` module to work properly. import macros, strutils, asyncfutures + # TODO: Ref https://github.com/nim-lang/Nim/issues/5617 # TODO: Add more line infos proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimNode]): NimNode = @@ -20,8 +21,9 @@ proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimN template createCb(retFutureSym, iteratorNameSym, strName, identName, futureVarCompletions: untyped) = bind finished + var nameIterVar = iteratorNameSym - proc identName {.closure, stackTrace: off.} = + proc identName {.closure.} = try: if not nameIterVar.finished: var next = nameIterVar() @@ -33,11 +35,14 @@ template createCb(retFutureSym, iteratorNameSym, if next == nil: if not retFutureSym.finished: - let msg = "Async procedure ($1) yielded `nil`, are you await'ing a `nil` Future?" + let msg = "Async procedure ($1) yielded `nil`, are you await'ing a " & + "`nil` Future?" raise newException(AssertionDefect, msg % strName) else: {.gcsafe.}: + {.push hint[ConvFromXtoItselfNotNeeded]: off.} next.addCallback cast[proc() {.closure, gcsafe.}](identName) + {.pop.} except: futureVarCompletions if retFutureSym.finished: @@ -48,7 +53,8 @@ template createCb(retFutureSym, iteratorNameSym, retFutureSym.fail(getCurrentException()) identName() -proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode): NimNode = +proc createFutureVarCompletions(futureVarIdents: seq[NimNode], + fromNode: NimNode): NimNode {.compileTime.} = result = newNimNode(nnkStmtList, fromNode) # Add calls to complete each FutureVar parameter. for ident in futureVarIdents: @@ -63,7 +69,10 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode ) ) -proc processBody(node, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode = +proc processBody(node, retFutureSym: NimNode, + subTypeIsVoid: bool, + futureVarIdents: seq[NimNode]): NimNode {.compileTime.} = + #echo(node.treeRepr) result = node case node.kind of nnkReturnStmt: @@ -73,9 +82,14 @@ proc processBody(node, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): Ni result.add createFutureVarCompletions(futureVarIdents, node) if node[0].kind == nnkEmpty: - result.add newCall(newIdentNode("complete"), retFutureSym, newIdentNode("result")) + if not subTypeIsVoid: + result.add newCall(newIdentNode("complete"), retFutureSym, + newIdentNode("result")) + else: + result.add newCall(newIdentNode("complete"), retFutureSym) else: - let x = node[0].processBody(retFutureSym, futureVarIdents) + let x = node[0].processBody(retFutureSym, subTypeIsVoid, + futureVarIdents) if x.kind == nnkYieldStmt: result.add x else: result.add newCall(newIdentNode("complete"), retFutureSym, x) @@ -88,11 +102,12 @@ proc processBody(node, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): Ni else: discard for i in 0 ..< result.len: - result[i] = processBody(result[i], retFutureSym, futureVarIdents) + result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, + futureVarIdents) # echo result.repr -proc getName(node: NimNode): string = +proc getName(node: NimNode): string {.compileTime.} = case node.kind of nnkPostfix: return node[1].strVal @@ -103,7 +118,7 @@ proc getName(node: NimNode): string = else: error("Unknown name.", node) -proc getFutureVarIdents(params: NimNode): seq[NimNode] = +proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} = result = @[] for i in 1 ..< len(params): expectKind(params[i], nnkIdentDefs) @@ -115,7 +130,7 @@ proc getFutureVarIdents(params: NimNode): seq[NimNode] = proc isInvalidReturnType(typeName: string): bool = return typeName notin ["Future"] #, "FutureStream"] -proc verifyReturnType(typeName: string, node: NimNode = nil) = +proc verifyReturnType(typeName: string, node: NimNode = nil) {.compileTime.} = if typeName.isInvalidReturnType: error("Expected return type of 'Future' got '$1'" % typeName, node) @@ -125,35 +140,19 @@ template await*(f: typed): untyped {.used.} = error "await expects Future[T], got " & $typeof(f) template await*[T](f: Future[T]): auto {.used.} = - when not defined(nimHasTemplateRedefinitionPragma): - {.pragma: redefine.} - template yieldFuture {.redefine.} = yield FutureBase() + var internalTmpFuture: FutureBase = f + yield internalTmpFuture + (cast[typeof(f)](internalTmpFuture)).read() - when compiles(yieldFuture): - var internalTmpFuture: FutureBase = f - yield internalTmpFuture - (cast[typeof(f)](internalTmpFuture)).read() - else: - macro errorAsync(futureError: Future[T]) = - error( - "Can only 'await' inside a proc marked as 'async'. Use " & - "'waitFor' when calling an 'async' proc in a non-async scope instead", - futureError) - errorAsync(f) - -proc asyncSingleProc(prc: NimNode): NimNode = +proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = ## This macro transforms a single procedure into a closure iterator. - ## The `async` macro supports a stmtList holding multiple async procedures. + ## The ``async`` macro supports a stmtList holding multiple async procedures. if prc.kind == nnkProcTy: result = prc if prc[0][0].kind == nnkEmpty: - result[0][0] = quote do: Future[void] + result[0][0] = parseExpr("Future[void]") return result - if prc.kind in RoutineNodes and prc.name.kind != nnkEmpty: - # Only non anonymous functions need/can have stack trace disabled - prc.addPragma(nnkExprColonExpr.newTree(ident"stackTrace", ident"off")) - if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}: error("Cannot transform this node kind into an async proc." & " proc/method definition or lambda node expected.", prc) @@ -185,7 +184,11 @@ proc asyncSingleProc(prc: NimNode): NimNode = else: verifyReturnType(repr(returnType), returnType) + let subtypeIsVoid = returnType.kind == nnkEmpty or + (baseType.kind == nnkIdent and returnType[1].eqIdent("void")) + let futureVarIdents = getFutureVarIdents(prc.params) + var outerProcBody = newNimNode(nnkStmtList, prc.body) # Extract the documentation comment from the original procedure declaration. @@ -212,40 +215,43 @@ proc asyncSingleProc(prc: NimNode): NimNode = # -> {.pop.} # -> # -> complete(retFuture, result) - var iteratorNameSym = genSym(nskIterator, $prcName & " (Async)") - var procBody = prc.body.processBody(retFutureSym, futureVarIdents) + var iteratorNameSym = genSym(nskIterator, $prcName & "Iter") + var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid, + futureVarIdents) # don't do anything with forward bodies (empty) if procBody.kind != nnkEmpty: # fix #13899, defer should not escape its original scope - let blockStmt = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody)) - procBody = newStmtList() - let resultIdent = ident"result" - procBody.add quote do: - template nimAsyncDispatchSetResult(x: `subRetType`) {.used.} = - # If the proc has implicit return then this will get called - `resultIdent` = x - template nimAsyncDispatchSetResult(x: untyped) {.used.} = - # If the proc doesn't have implicit return then this will get called - x - procBody.add newCall(ident"nimAsyncDispatchSetResult", blockStmt) - procBody.add(createFutureVarCompletions(futureVarIdents, nil)) - procBody.insert(0): quote do: - {.push warning[resultshadowed]: off.} - when `subRetType` isnot void: - var `resultIdent`: `subRetType` - else: - var `resultIdent`: Future[void] - {.pop.} - procBody.add quote do: - complete(`retFutureSym`, `resultIdent`) + procBody = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody)) - var closureIterator = newProc(iteratorNameSym, [quote do: owned(FutureBase)], + procBody.add(createFutureVarCompletions(futureVarIdents, nil)) + + if not subtypeIsVoid: + procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"), + newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add( + newIdentNode("warning"), newIdentNode("resultshadowed")), + newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.} + + procBody.insert(1, newNimNode(nnkVarSection, prc.body).add( + newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T + + procBody.insert(2, newNimNode(nnkPragma).add( + newIdentNode("pop"))) # -> {.pop.}) + + procBody.add( + newCall(newIdentNode("complete"), + retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) + else: + # -> complete(retFuture) + procBody.add(newCall(newIdentNode("complete"), retFutureSym)) + + var closureIterator = newProc(iteratorNameSym, [parseExpr("owned(FutureBase)")], procBody, nnkIteratorDef) closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom = prc.body) closureIterator.addPragma(newIdentNode("closure")) # If proc has an explicit gcsafe pragma, we add it to iterator as well. - if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and $it == "gcsafe") != nil: + if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and $it == + "gcsafe") != nil: closureIterator.addPragma(newIdentNode("gcsafe")) outerProcBody.add(closureIterator) @@ -254,20 +260,21 @@ proc asyncSingleProc(prc: NimNode): NimNode = # friendlier stack traces: var cbName = genSym(nskProc, prcName & NimAsyncContinueSuffix) var procCb = getAst createCb(retFutureSym, iteratorNameSym, - newStrLitNode(prcName), - cbName, - createFutureVarCompletions(futureVarIdents, nil) - ) + newStrLitNode(prcName), + cbName, + createFutureVarCompletions(futureVarIdents, nil)) outerProcBody.add procCb # -> return retFuture outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym) result = prc - # Add discardable pragma. - if returnType.kind == nnkEmpty: - # xxx consider removing `owned`? it's inconsistent with non-void case - result.params[0] = quote do: owned(Future[void]) + + if subtypeIsVoid: + # Add discardable pragma. + if returnType.kind == nnkEmpty: + # Add Future[void] + result.params[0] = parseExpr("owned(Future[void])") # based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47 if procBody.kind != nnkEmpty: @@ -275,6 +282,10 @@ proc asyncSingleProc(prc: NimNode): NimNode = `outerProcBody` result.body = body2 + #echo(treeRepr(result)) + #if prcName == "recvLineInto": + # echo(toStrLit(result)) + macro async*(prc: untyped): untyped = ## Macro which processes async procedures into the appropriate ## iterators and yield statements. @@ -290,8 +301,8 @@ macro async*(prc: untyped): untyped = proc splitParamType(paramType: NimNode, async: bool): NimNode = result = paramType if paramType.kind == nnkInfix and paramType[0].strVal in ["|", "or"]: - let firstAsync = "async" in paramType[1].toStrLit().strVal.normalize - let secondAsync = "async" in paramType[2].toStrLit().strVal.normalize + let firstAsync = "async" in paramType[1].strVal.normalize + let secondAsync = "async" in paramType[2].strVal.normalize if firstAsync: result = paramType[if async: 1 else: 2] @@ -309,8 +320,8 @@ proc stripReturnType(returnType: NimNode): NimNode = proc splitProc(prc: NimNode): (NimNode, NimNode) = ## Takes a procedure definition which takes a generic union of arguments, ## for example: proc (socket: Socket | AsyncSocket). - ## It transforms them so that `proc (socket: Socket)` and - ## `proc (socket: AsyncSocket)` are returned. + ## It transforms them so that ``proc (socket: Socket)`` and + ## ``proc (socket: AsyncSocket)`` are returned. result[0] = prc.copyNimTree() # Retrieve the `T` inside `Future[T]`. @@ -338,9 +349,15 @@ macro multisync*(prc: untyped): untyped = ## Macro which processes async procedures into both asynchronous and ## synchronous procedures. ## - ## The generated async procedures use the `async` macro, whereas the - ## generated synchronous procedures simply strip off the `await` calls. + ## The generated async procedures use the ``async`` macro, whereas the + ## generated synchronous procedures simply strip off the ``await`` calls. let (sync, asyncPrc) = splitProc(prc) result = newStmtList() result.add(asyncSingleProc(asyncPrc)) result.add(sync) + # echo result.repr + +# overload for await as a fallback handler, based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47 +# template await*(f: typed): untyped = + # static: + # error "await only available within {.async.}" diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index d35dbdfaf..455efe1c1 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -8,27 +8,27 @@ # ## This module implements a high-level asynchronous sockets API based on the -## asynchronous dispatcher defined in the `asyncdispatch` module. +## asynchronous dispatcher defined in the ``asyncdispatch`` module. ## ## Asynchronous IO in Nim ## ====================== ## ## Async IO in Nim consists of multiple layers (from highest to lowest): ## -## * `asyncnet` module +## * ``asyncnet`` module ## ## * Async await ## -## * `asyncdispatch` module (event loop) +## * ``asyncdispatch`` module (event loop) ## -## * `selectors` module +## * ``selectors`` module ## ## Each builds on top of the layers below it. The selectors module is an -## abstraction for the various system `select()` mechanisms such as epoll or +## abstraction for the various system ``select()`` mechanisms such as epoll or ## kqueue. If you wish you can use it directly, and some people have done so ## `successfully `_. ## But you must be aware that on Windows it only supports -## `select()`. +## ``select()``. ## ## The async dispatcher implements the proactor pattern and also has an ## implementation of IOCP. It implements the proactor pattern for other @@ -45,16 +45,16 @@ ## layers interchangeably (as long as you only care about non-Windows ## platforms). ## -## For most applications using `asyncnet` is the way to go as it builds +## For most applications using ``asyncnet`` is the way to go as it builds ## over all the layers, providing some extra features such as buffering. ## ## SSL ## === ## -## SSL can be enabled by compiling with the `-d:ssl` flag. +## SSL can be enabled by compiling with the ``-d:ssl`` flag. ## -## You must create a new SSL context with the `newContext` function defined -## in the `net` module. You may then call `wrapSocket` on your socket using +## You must create a new SSL context with the ``newContext`` function defined +## in the ``net`` module. You may then call ``wrapSocket`` on your socket using ## the newly created SSL context to get an SSL socket. ## ## Examples @@ -65,8 +65,9 @@ ## ## The following example demonstrates a simple chat server. ## -## ```Nim -## import std/[asyncnet, asyncdispatch] +## .. code-block::nim +## +## import asyncnet, asyncdispatch ## ## var clients {.threadvar.}: seq[AsyncSocket] ## @@ -92,13 +93,9 @@ ## ## asyncCheck serve() ## runForever() -## ``` +## import std/private/since - -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - import asyncdispatch, nativesockets, net, os export SOBool @@ -106,8 +103,6 @@ export SOBool # TODO: Remove duplication introduced by PR #4683. const defineSsl = defined(ssl) or defined(nimdoc) -const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or - defined(nuttx) when defineSsl: import openssl @@ -139,16 +134,16 @@ proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET, protocol: Protocol = IPPROTO_TCP, buffered = true, inheritable = defined(nimInheritHandles)): owned(AsyncSocket) = - ## Creates a new `AsyncSocket` based on the supplied params. + ## Creates a new ``AsyncSocket`` based on the supplied params. ## - ## The supplied `fd`'s non-blocking state will be enabled implicitly. + ## The supplied ``fd``'s non-blocking state will be enabled implicitly. ## - ## If `inheritable` is false (the default), the supplied `fd` will not + ## If ``inheritable`` is false (the default), the supplied ``fd`` will not ## be inheritable by child processes. ## - ## **Note**: This procedure will **NOT** register `fd` with the global + ## **Note**: This procedure will **NOT** register ``fd`` with the global ## async dispatcher. You need to do this manually. If you have used - ## `newAsyncNativeSocket` to create `fd` then it's already registered. + ## ``newAsyncNativeSocket`` to create ``fd`` then it's already registered. assert fd != osInvalidSocket.AsyncFD new(result) result.fd = fd.SocketHandle @@ -170,7 +165,7 @@ proc newAsyncSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, ## This procedure will also create a brand new file descriptor for ## this socket. ## - ## If `inheritable` is false (the default), the new file descriptor will not + ## If ``inheritable`` is false (the default), the new file descriptor will not ## be inheritable by child processes. let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable) if fd.SocketHandle == osInvalidSocket: @@ -183,12 +178,11 @@ proc getLocalAddr*(socket: AsyncSocket): (string, Port) = ## This is high-level interface for `getsockname`:idx:. getLocalAddr(socket.fd, socket.domain) -when not useNimNetLite: - proc getPeerAddr*(socket: AsyncSocket): (string, Port) = - ## Get the socket's peer address and port number. - ## - ## This is high-level interface for `getpeername`:idx:. - getPeerAddr(socket.fd, socket.domain) +proc getPeerAddr*(socket: AsyncSocket): (string, Port) = + ## Get the socket's peer address and port number. + ## + ## This is high-level interface for `getpeername`:idx:. + getPeerAddr(socket.fd, socket.domain) proc newAsyncSocket*(domain, sockType, protocol: cint, buffered = true, @@ -198,7 +192,7 @@ proc newAsyncSocket*(domain, sockType, protocol: cint, ## This procedure will also create a brand new file descriptor for ## this socket. ## - ## If `inheritable` is false (the default), the new file descriptor will not + ## If ``inheritable`` is false (the default), the new file descriptor will not ## be inheritable by child processes. let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable) if fd.SocketHandle == osInvalidSocket: @@ -230,7 +224,7 @@ when defineSsl: let len = bioCtrlPending(socket.bioOut) if len > 0: var data = newString(len) - let read = bioRead(socket.bioOut, cast[cstring](addr data[0]), len) + let read = bioRead(socket.bioOut, addr data[0], len) assert read != 0 if read < 0: raiseSSLError() @@ -239,7 +233,7 @@ when defineSsl: proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag], sslError: cint): owned(Future[bool]) {.async.} = - ## Returns `true` if `socket` is still connected, otherwise `false`. + ## Returns ``true`` if ``socket`` is still connected, otherwise ``false``. result = true case sslError of SSL_ERROR_WANT_WRITE: @@ -248,7 +242,7 @@ when defineSsl: var data = await recv(socket.fd.AsyncFD, BufferSize, flags) let length = len(data) if length > 0: - let ret = bioWrite(socket.bioIn, cast[cstring](addr data[0]), length.cint) + let ret = bioWrite(socket.bioIn, addr data[0], length.cint) if ret < 0: raiseSSLError() elif length == 0: @@ -265,9 +259,11 @@ when defineSsl: ErrClearError() # Call the desired operation. opResult = op + # Bit hackish here. + # TODO: Introduce an async template transformation pragma? # Send any remaining pending SSL data. - await sendPendingSslData(socket, flags) + yield sendPendingSslData(socket, flags) # If the operation failed, try to see if SSL has some data to read # or write. @@ -285,9 +281,9 @@ when defineSsl: proc dial*(address: string, port: Port, protocol = IPPROTO_TCP, buffered = true): owned(Future[AsyncSocket]) {.async.} = - ## Establishes connection to the specified `address`:`port` pair via the + ## Establishes connection to the specified ``address``:``port`` pair via the ## specified protocol. The procedure iterates through possible - ## resolutions of the `address` until it succeeds, meaning that it + ## resolutions of the ``address`` until it succeeds, meaning that it ## seamlessly works with both IPv4 and IPv6. ## Returns AsyncSocket ready to send or receive data. let asyncFd = await asyncdispatch.dial(address, port, protocol) @@ -296,9 +292,9 @@ proc dial*(address: string, port: Port, protocol = IPPROTO_TCP, result = newAsyncSocket(asyncFd, domain, sockType, protocol, buffered) proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} = - ## Connects `socket` to server at `address:port`. + ## Connects ``socket`` to server at ``address:port``. ## - ## Returns a `Future` which will complete when the connection succeeds + ## Returns a ``Future`` which will complete when the connection succeeds ## or an error occurs. await connect(socket.fd.AsyncFD, address, port, socket.domain) if socket.isSsl: @@ -314,7 +310,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} = template readInto(buf: pointer, size: int, socket: AsyncSocket, flags: set[SocketFlag]): int = - ## Reads **up to** `size` bytes from `socket` into `buf`. Note that + ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that ## this is a template and not a proc. assert(not socket.closed, "Cannot `recv` on a closed socket") var res = 0 @@ -325,8 +321,10 @@ template readInto(buf: pointer, size: int, socket: AsyncSocket, sslRead(socket.sslHandle, cast[cstring](buf), size.cint)) res = opResult else: + var recvIntoFut = asyncdispatch.recvInto(socket.fd.AsyncFD, buf, size, flags) + yield recvIntoFut # Not in SSL mode. - res = await asyncdispatch.recvInto(socket.fd.AsyncFD, buf, size, flags) + res = recvIntoFut.read() res template readIntoBuf(socket: AsyncSocket, @@ -338,10 +336,10 @@ template readIntoBuf(socket: AsyncSocket, proc recvInto*(socket: AsyncSocket, buf: pointer, size: int, flags = {SocketFlag.SafeDisconn}): owned(Future[int]) {.async.} = - ## Reads **up to** `size` bytes from `socket` into `buf`. + ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. ## ## For buffered sockets this function will attempt to read all the requested - ## data. It will read this data in `BufferSize` chunks. + ## data. It will read this data in ``BufferSize`` chunks. ## ## For unbuffered sockets this function makes no effort to read ## all the data requested. It will return as much data as the operating system @@ -352,7 +350,7 @@ proc recvInto*(socket: AsyncSocket, buf: pointer, size: int, ## requested data. ## ## If socket is disconnected and no data is available - ## to be read then the future will complete with a value of `0`. + ## to be read then the future will complete with a value of ``0``. if socket.isBuffered: let originalBufPos = socket.currPos @@ -386,10 +384,10 @@ proc recvInto*(socket: AsyncSocket, buf: pointer, size: int, proc recv*(socket: AsyncSocket, size: int, flags = {SocketFlag.SafeDisconn}): owned(Future[string]) {.async.} = - ## Reads **up to** `size` bytes from `socket`. + ## Reads **up to** ``size`` bytes from ``socket``. ## ## For buffered sockets this function will attempt to read all the requested - ## data. It will read this data in `BufferSize` chunks. + ## data. It will read this data in ``BufferSize`` chunks. ## ## For unbuffered sockets this function makes no effort to read ## all the data requested. It will return as much data as the operating system @@ -400,11 +398,10 @@ proc recv*(socket: AsyncSocket, size: int, ## requested data. ## ## If socket is disconnected and no data is available - ## to be read then the future will complete with a value of `""`. + ## to be read then the future will complete with a value of ``""``. if socket.isBuffered: result = newString(size) - when not defined(nimSeqsV2): - shallow(result) + shallow(result) let originalBufPos = socket.currPos if socket.bufLen == 0: @@ -439,7 +436,7 @@ proc recv*(socket: AsyncSocket, size: int, proc send*(socket: AsyncSocket, buf: pointer, size: int, flags = {SocketFlag.SafeDisconn}) {.async.} = - ## Sends `size` bytes from `buf` to `socket`. The returned future will complete once all + ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future will complete once all ## data has been sent. assert socket != nil assert(not socket.closed, "Cannot `send` on a closed socket") @@ -453,14 +450,14 @@ proc send*(socket: AsyncSocket, buf: pointer, size: int, proc send*(socket: AsyncSocket, data: string, flags = {SocketFlag.SafeDisconn}) {.async.} = - ## Sends `data` to `socket`. The returned future will complete once all + ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. assert socket != nil if socket.isSsl: when defineSsl: var copy = data sslLoop(socket, flags, - sslWrite(socket.sslHandle, cast[cstring](addr copy[0]), copy.len.cint)) + sslWrite(socket.sslHandle, addr copy[0], copy.len.cint)) await sendPendingSslData(socket, flags) else: await send(socket.fd.AsyncFD, data, flags) @@ -471,7 +468,7 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}, ## Accepts a new connection. Returns a future containing the client socket ## corresponding to that connection and the remote address of the client. ## - ## If `inheritable` is false (the default), the resulting client socket will + ## If ``inheritable`` is false (the default), the resulting client socket will ## not be inheritable by child processes. ## ## The future will complete when the connection is successfully accepted. @@ -493,7 +490,7 @@ proc accept*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}): owned(Future[AsyncSocket]) = ## Accepts a new connection. Returns a future containing the client socket ## corresponding to that connection. - ## If `inheritable` is false (the default), the resulting client socket will + ## If ``inheritable`` is false (the default), the resulting client socket will ## not be inheritable by child processes. ## The future will complete when the connection is successfully accepted. var retFut = newFuture[AsyncSocket]("asyncnet.accept") @@ -509,24 +506,25 @@ proc accept*(socket: AsyncSocket, proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.async.} = - ## Reads a line of data from `socket` into `resString`. + ## Reads a line of data from ``socket`` into ``resString``. ## - ## If a full line is read `\r\L` is not - ## added to `line`, however if solely `\r\L` is read then `line` + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## will be set to it. ## - ## If the socket is disconnected, `line` will be set to `""`. + ## If the socket is disconnected, ``line`` will be set to ``""``. ## - ## If the socket is disconnected in the middle of a line (before `\r\L` - ## is read) then line will be set to `""`. + ## If the socket is disconnected in the middle of a line (before ``\r\L`` + ## is read) then line will be set to ``""``. ## The partial line **will be lost**. ## - ## The `maxLength` parameter determines the maximum amount of characters - ## that can be read. `resString` will be truncated after that. + ## The ``maxLength`` parameter determines the maximum amount of characters + ## that can be read. ``resString`` will be truncated after that. ## - ## .. warning:: The `Peek` flag is not yet implemented. + ## **Warning**: The ``Peek`` flag is not yet implemented. ## - ## .. warning:: `recvLineInto` on unbuffered sockets assumes that the protocol uses `\r\L` to delimit a new line. + ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the + ## protocol uses ``\r\L`` to delimit a new line. assert SocketFlag.Peek notin flags ## TODO: result = newFuture[void]("asyncnet.recvLineInto") @@ -601,25 +599,26 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], proc recvLine*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength): owned(Future[string]) {.async.} = - ## Reads a line of data from `socket`. Returned future will complete once + ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. ## - ## If a full line is read `\r\L` is not - ## added to `line`, however if solely `\r\L` is read then `line` + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## will be set to it. ## - ## If the socket is disconnected, `line` will be set to `""`. + ## If the socket is disconnected, ``line`` will be set to ``""``. ## - ## If the socket is disconnected in the middle of a line (before `\r\L` - ## is read) then line will be set to `""`. + ## If the socket is disconnected in the middle of a line (before ``\r\L`` + ## is read) then line will be set to ``""``. ## The partial line **will be lost**. ## - ## The `maxLength` parameter determines the maximum amount of characters + ## The ``maxLength`` parameter determines the maximum amount of characters ## that can be read. The result is truncated after that. ## - ## .. warning:: The `Peek` flag is not yet implemented. + ## **Warning**: The ``Peek`` flag is not yet implemented. ## - ## .. warning:: `recvLine` on unbuffered sockets assumes that the protocol uses `\r\L` to delimit a new line. + ## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol + ## uses ``\r\L`` to delimit a new line. assert SocketFlag.Peek notin flags ## TODO: # TODO: Optimise this @@ -630,8 +629,8 @@ proc recvLine*(socket: AsyncSocket, proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ ReadIOEffect].} = - ## Marks `socket` as accepting connections. - ## `Backlog` specifies the maximum length of the + ## Marks ``socket`` as accepting connections. + ## ``Backlog`` specifies the maximum length of the ## queue of pending connections. ## ## Raises an OSError error upon failure. @@ -639,9 +638,9 @@ proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {. tags: [ReadIOEffect].} = - ## Binds `address`:`port` to the socket. + ## Binds ``address``:``port`` to the socket. ## - ## If `address` is "" then ADDR_ANY will be bound. + ## If ``address`` is "" then ADDR_ANY will be bound. var realaddr = address if realaddr == "": case socket.domain @@ -653,16 +652,11 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {. var aiList = getAddrInfo(realaddr, port, socket.domain) if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: - freeAddrInfo(aiList) + freeaddrinfo(aiList) raiseOSError(osLastError()) - freeAddrInfo(aiList) + freeaddrinfo(aiList) -proc hasDataBuffered*(s: AsyncSocket): bool {.since: (1, 5).} = - ## Determines whether an AsyncSocket has data buffered. - # xxx dedup with std/net - s.isBuffered and s.bufLen > 0 and s.currPos != s.bufLen - -when defined(posix) and not useNimNetLite: +when defined(posix): proc connectUnix*(socket: AsyncSocket, path: string): owned(Future[void]) = ## Binds Unix socket to `path`. @@ -743,14 +737,9 @@ proc close*(socket: AsyncSocket) = raiseSSLError() when defineSsl: - proc sslHandle*(self: AsyncSocket): SslPtr = - ## Retrieve the ssl pointer of `socket`. - ## Useful for interfacing with `openssl`. - self.sslHandle - proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) = ## Wraps a socket in an SSL context. This function effectively turns - ## `socket` into an SSL socket. + ## ``socket`` into an SSL socket. ## ## **Disclaimer**: This code is not well tested, may be very unsafe and ## prone to security vulnerabilities. @@ -770,8 +759,8 @@ when defineSsl: handshake: SslHandshakeType, hostname: string = "") = ## Wraps a connected socket in an SSL context. This function effectively - ## turns `socket` into an SSL socket. - ## `hostname` should be specified so that the client knows which hostname + ## turns ``socket`` into an SSL socket. + ## ``hostname`` should be specified so that the client knows which hostname ## the server certificate should be validated against. ## ## This should be called on a connected socket, and will perform @@ -804,18 +793,18 @@ when defineSsl: proc getSockOpt*(socket: AsyncSocket, opt: SOBool, level = SOL_SOCKET): bool {. tags: [ReadIOEffect].} = - ## Retrieves option `opt` as a boolean value. + ## Retrieves option ``opt`` as a boolean value. var res = getSockOptInt(socket.fd, cint(level), toCInt(opt)) result = res != 0 proc setSockOpt*(socket: AsyncSocket, opt: SOBool, value: bool, level = SOL_SOCKET) {.tags: [WriteIOEffect].} = - ## Sets option `opt` to a boolean value specified by `value`. + ## Sets option ``opt`` to a boolean value specified by ``value``. var valuei = cint(if value: 1 else: 0) setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei) proc isSsl*(socket: AsyncSocket): bool = - ## Determines whether `socket` is a SSL socket. + ## Determines whether ``socket`` is a SSL socket. socket.isSsl proc getFd*(socket: AsyncSocket): SocketHandle = @@ -829,7 +818,7 @@ proc isClosed*(socket: AsyncSocket): bool = proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string, flags = {SocketFlag.SafeDisconn}): owned(Future[void]) {.async, since: (1, 3).} = - ## This proc sends `data` to the specified `address`, which may be an IP + ## This proc sends ``data`` to the specified ``address``, which may be an IP ## address or a hostname. If a hostname is specified this function will try ## each IP of that hostname. The returned future will complete once all data ## has been sent. @@ -864,7 +853,7 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string, it = it.ai_next - freeAddrInfo(aiList) + freeaddrinfo(aiList) if not success: if lastException != nil: @@ -876,9 +865,9 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int, address: FutureVar[string], port: FutureVar[Port], flags = {SocketFlag.SafeDisconn}): owned(Future[int]) {.async, since: (1, 3).} = - ## Receives a datagram data from `socket` into `data`, which must be at - ## least of size `size`. The address and port of datagram's sender will be - ## stored into `address` and `port`, respectively. Returned future will + ## Receives a datagram data from ``socket`` into ``data``, which must be at + ## least of size ``size``. The address and port of datagram's sender will be + ## stored into ``address`` and ``port``, respectively. Returned future will ## complete once one datagram has been received, and will return size of ## packet received. ## @@ -887,8 +876,8 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int, ## This proc is normally used with connectionless sockets (UDP sockets). ## ## **Notes** - ## * `data` must be initialized to the length of `size`. - ## * `address` must be initialized to 46 in length. + ## * ``data`` must be initialized to the length of ``size``. + ## * ``address`` must be initialized to 46 in length. template adaptRecvFromToDomain(domain: Domain) = var lAddr = sizeof(sAddr).SockLen @@ -930,8 +919,8 @@ proc recvFrom*(socket: AsyncSocket, size: int, flags = {SocketFlag.SafeDisconn}): owned(Future[tuple[data: string, address: string, port: Port]]) {.async, since: (1, 3).} = - ## Receives a datagram data from `socket`, which must be at least of size - ## `size`. Returned future will complete once one datagram has been received + ## Receives a datagram data from ``socket``, which must be at least of size + ## ``size``. Returned future will complete once one datagram has been received ## and will return tuple with: data of packet received; and address and port ## of datagram's sender. ## diff --git a/lib/pure/asyncstreams.nim b/lib/pure/asyncstreams.nim index 3f7774ed8..7ffde9c10 100644 --- a/lib/pure/asyncstreams.nim +++ b/lib/pure/asyncstreams.nim @@ -11,9 +11,6 @@ import asyncfutures -when defined(nimPreviewSlimSystem): - import std/assertions - import deques type @@ -24,17 +21,16 @@ type queue: Deque[T] finished: bool cb: proc () {.closure, gcsafe.} - error*: ref Exception proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] = - ## Create a new `FutureStream`. This future's callback is activated when + ## Create a new ``FutureStream``. This future's callback is activated when ## two events occur: ## ## * New data is written into the future stream. ## * The future stream is completed (this means that no more data will be ## written). ## - ## Specifying `fromProc`, which is a string specifying the name of the proc + ## Specifying ``fromProc``, which is a string specifying the name of the proc ## that this future belongs to, is a good habit as it helps with debugging. ## ## **Note:** The API of FutureStream is still new and so has a higher @@ -43,29 +39,20 @@ proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] = result.queue = initDeque[T]() proc complete*[T](future: FutureStream[T]) = - ## Completes a `FutureStream` signalling the end of data. - assert(future.error == nil, "Trying to complete failed stream") + ## Completes a ``FutureStream`` signalling the end of data. future.finished = true if not future.cb.isNil: future.cb() -proc fail*[T](future: FutureStream[T], error: ref Exception) = - ## Completes `future` with `error`. - assert(not future.finished) - future.finished = true - future.error = error - if not future.cb.isNil: - future.cb() - proc `callback=`*[T](future: FutureStream[T], cb: proc (future: FutureStream[T]) {.closure, gcsafe.}) = ## Sets the callback proc to be called when data was placed inside the ## future stream. ## ## The callback is also called when the future is completed. So you should - ## use `finished` to check whether data is available. + ## use ``finished`` to check whether data is available. ## - ## If the future stream already has data or is finished then `cb` will be + ## If the future stream already has data or is finished then ``cb`` will be ## called immediately. proc named() = cb(future) future.cb = named @@ -73,19 +60,15 @@ proc `callback=`*[T](future: FutureStream[T], callSoon(future.cb) proc finished*[T](future: FutureStream[T]): bool = - ## Check if a `FutureStream` is finished. `true` value means that + ## Check if a ``FutureStream`` is finished. ``true`` value means that ## no more data will be placed inside the stream *and* that there is ## no data waiting to be retrieved. result = future.finished and future.queue.len == 0 -proc failed*[T](future: FutureStream[T]): bool = - ## Determines whether `future` completed with an error. - return future.error != nil - proc write*[T](future: FutureStream[T], value: T): Future[void] = ## Writes the specified value inside the specified future stream. ## - ## This will raise `ValueError` if `future` is finished. + ## This will raise ``ValueError`` if ``future`` is finished. result = newFuture[void]("FutureStream.put") if future.finished: let msg = "FutureStream is finished and so no longer accepts new data." @@ -98,14 +81,14 @@ proc write*[T](future: FutureStream[T], value: T): Future[void] = result.complete() proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) = - ## Returns a future that will complete when the `FutureStream` has data + ## Returns a future that will complete when the ``FutureStream`` has data ## placed into it. The future will be completed with the oldest ## value stored inside the stream. The return value will also determine - ## whether data was retrieved, `false` means that the future stream was + ## whether data was retrieved, ``false`` means that the future stream was ## completed and no data was retrieved. ## ## This function will remove the data that was returned from the underlying - ## `FutureStream`. + ## ``FutureStream``. var resFut = newFuture[(bool, T)]("FutureStream.take") let savedCb = future.cb proc newCb(fs: FutureStream[T]) = @@ -124,17 +107,10 @@ proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) = res[0] = true res[1] = fs.queue.popFirst() - if fs.failed: - resFut.fail(fs.error) - else: - resFut.complete(res) + resFut.complete(res) # If the saved callback isn't nil then let's call it. - if not savedCb.isNil: - if fs.queue.len > 0: - savedCb() - else: - future.cb = savedCb + if not savedCb.isNil: savedCb() if future.queue.len > 0 or future.finished: newCb(future) diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 795820433..2abdc805d 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -16,48 +16,50 @@ ## Each Base64 digit represents exactly 6 bits of data. Three 8-bit ## bytes (i.e., a total of 24 bits) can therefore be represented by ## four 6-bit Base64 digits. - -##[ -# Basic usage +## +## Basic usage +## =========== +## ## Encoding data -]## - -runnableExamples: - let encoded = encode("Hello World") - assert encoded == "SGVsbG8gV29ybGQ=" - +## ------------- +## +## .. code-block::nim +## import base64 +## let encoded = encode("Hello World") +## assert encoded == "SGVsbG8gV29ybGQ=" ## ## Apart from strings you can also encode lists of integers or characters: ## - -runnableExamples: - let encodedInts = encode([1'u8,2,3]) - assert encodedInts == "AQID" - let encodedChars = encode(['h','e','y']) - assert encodedChars == "aGV5" - -##[ +## .. code-block::nim +## import base64 +## let encodedInts = encode([1,2,3]) +## assert encodedInts == "AQID" +## let encodedChars = encode(['h','e','y']) +## assert encodedChars == "aGV5" +## +## ## Decoding data -]## - -runnableExamples: - let decoded = decode("SGVsbG8gV29ybGQ=") - assert decoded == "Hello World" - -##[ +## ------------- +## +## .. code-block::nim +## import base64 +## let decoded = decode("SGVsbG8gV29ybGQ=") +## assert decoded == "Hello World" +## ## URL Safe Base64 -]## - -runnableExamples: - assert encode("c\xf7>", safe = true) == "Y_c-" - assert encode("c\xf7>", safe = false) == "Y/c+" - +## --------------- +## +## .. code-block::nim +## import base64 +## doAssert encode("c\xf7>", safe = true) == "Y_c-" +## doAssert encode("c\xf7>", safe = false) == "Y/c+" +## ## See also ## ======== ## ## * `hashes module`_ for efficient computations of hash values for diverse Nim types -## * `md5 module`_ for the MD5 checksum algorithm -## * `sha1 module`_ for the SHA-1 checksum algorithm +## * `md5 module`_ implements the MD5 checksum algorithm +## * `sha1 module`_ implements a sha1 encoder and decoder template cbBase(a, b): untyped = [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', @@ -84,13 +86,10 @@ template encodeInternal(s, alphabet: typed): untyped = result.setLen(encodeSize(s.len)) - let - padding = s.len mod 3 - inputEnds = s.len - padding - var inputIndex = 0 outputIndex = 0 + inputEnds = s.len - s.len mod 3 n: uint32 b: uint32 @@ -116,6 +115,7 @@ template encodeInternal(s, alphabet: typed): untyped = outputChar(n shr 6) outputChar(n shr 0) + var padding = s.len mod 3 if padding == 1: inputByte(b shl 16) outputChar(n shr 18) @@ -134,7 +134,7 @@ template encodeInternal(s, alphabet: typed): untyped = result.setLen(outputIndex) template encodeImpl() {.dirty.} = - when nimvm: + when nimVM: block: let lookupTableVM = if safe: cb64safeVM else: cb64VM encodeInternal(s, lookupTableVM) @@ -143,65 +143,61 @@ template encodeImpl() {.dirty.} = let lookupTable = if safe: unsafeAddr(cb64safe) else: unsafeAddr(cb64) encodeInternal(s, lookupTable) -proc encode*[T: byte|char](s: openArray[T], safe = false): string = +proc encode*[T: SomeInteger|char](s: openArray[T], safe = false): string = ## Encodes `s` into base64 representation. ## - ## If `safe` is `true` then it will encode using the + ## This procedure encodes an openarray (array or sequence) of either integers + ## or characters. + ## + ## If ``safe`` is ``true`` then it will encode using the ## URL-Safe and Filesystem-safe standard alphabet characters, - ## which substitutes `-` instead of `+` and `_` instead of `/`. + ## which substitutes ``-`` instead of ``+`` and ``_`` instead of ``/``. ## * https://en.wikipedia.org/wiki/Base64#URL_applications ## * https://tools.ietf.org/html/rfc4648#page-7 ## ## **See also:** + ## * `encode proc<#encode,string>`_ for encoding a string + ## * `decode proc<#decode,string>`_ for decoding a string + runnableExamples: + assert encode(['n', 'i', 'm']) == "bmlt" + assert encode(@['n', 'i', 'm']) == "bmlt" + assert encode([1, 2, 3, 4, 5]) == "AQIDBAU=" + encodeImpl() + +proc encode*(s: string, safe = false): string = + ## Encodes ``s`` into base64 representation. + ## + ## This procedure encodes a string. + ## + ## If ``safe`` is ``true`` then it will encode using the + ## URL-Safe and Filesystem-safe standard alphabet characters, + ## which substitutes ``-`` instead of ``+`` and ``_`` instead of ``/``. + ## * https://en.wikipedia.org/wiki/Base64#URL_applications + ## * https://tools.ietf.org/html/rfc4648#page-7 + ## + ## **See also:** + ## * `encode proc<#encode,openArray[T]>`_ for encoding an openarray ## * `decode proc<#decode,string>`_ for decoding a string runnableExamples: assert encode("Hello World") == "SGVsbG8gV29ybGQ=" - assert encode(['n', 'i', 'm']) == "bmlt" - assert encode(@['n', 'i', 'm']) == "bmlt" - assert encode([1'u8, 2, 3, 4, 5]) == "AQIDBAU=" encodeImpl() -proc encode*[T: SomeInteger and not byte](s: openArray[T], safe = false): string - {.deprecated: "use `byte` or `char` instead".} = - encodeImpl() - -proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n", - safe = false): string = - ## Encodes `s` into base64 representation as lines. - ## Used in email MIME format, use `lineLen` and `newline`. +proc encodeMime*(s: string, lineLen = 75, newLine = "\r\n"): string = + ## Encodes ``s`` into base64 representation as lines. + ## Used in email MIME format, use ``lineLen`` and ``newline``. ## ## This procedure encodes a string according to MIME spec. ## - ## If `safe` is `true` then it will encode using the - ## URL-Safe and Filesystem-safe standard alphabet characters, - ## which substitutes `-` instead of `+` and `_` instead of `/`. - ## * https://en.wikipedia.org/wiki/Base64#URL_applications - ## * https://tools.ietf.org/html/rfc4648#page-7 - ## ## **See also:** - ## * `encode proc<#encode,openArray[T]>`_ for encoding an openArray + ## * `encode proc<#encode,string>`_ for encoding a string ## * `decode proc<#decode,string>`_ for decoding a string runnableExamples: assert encodeMime("Hello World", 4, "\n") == "SGVs\nbG8g\nV29y\nbGQ=" - 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, safe) - 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) + 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) proc initDecodeTable*(): array[256, char] = # computes a decode table at compile time @@ -219,11 +215,12 @@ const decodeTable = initDecodeTable() proc decode*(s: string): string = - ## Decodes string `s` in base64 representation back into its original form. + ## Decodes string ``s`` in base64 representation back into its original form. ## The initial whitespace is skipped. ## ## **See also:** ## * `encode proc<#encode,openArray[T]>`_ for encoding an openarray + ## * `encode proc<#encode,string>`_ for encoding a string runnableExamples: assert decode("SGVsbG8gV29ybGQ=") == "Hello World" assert decode(" SGVsbG8gV29ybGQ=") == "Hello World" diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index a518c25d2..855289e84 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -8,28 +8,27 @@ # ## This module implements a series of low level methods for bit manipulation. + +## By default, this module use compiler intrinsics where possible to improve performance +## on supported compilers: ``GCC``, ``LLVM_GCC``, ``CLANG``, ``VCC``, ``ICC``. ## -## By default, compiler intrinsics are used where possible to improve performance -## on supported compilers: `GCC`, `LLVM_GCC`, `CLANG`, `VCC`, `ICC`. -## -## The module will fallback to pure nim procs in case the backend is not supported. +## The module will fallback to pure nim procs incase the backend is not supported. ## You can also use the flag `noIntrinsicsBitOpts` to disable compiler intrinsics. ## -## This module is also compatible with other backends: `JavaScript`, `NimScript` -## as well as the `compiletime VM`. +## This module is also compatible with other backends: ``Javascript``, ``Nimscript`` +## as well as the ``compiletime VM``. ## -## As a result of using optimized functions/intrinsics, some functions can return +## As a result of using optimized function/intrinsics some functions can return ## undefined results if the input is invalid. You can use the flag `noUndefinedBitOpts` ## to force predictable behaviour for all input, causing a small performance hit. ## -## At this time only `fastLog2`, `firstSetBit`, `countLeadingZeroBits` and `countTrailingZeroBits` -## may return undefined and/or platform dependent values if given invalid input. +## At this time only `fastLog2`, `firstSetBit, `countLeadingZeroBits`, `countTrailingZeroBits` +## may return undefined and/or platform dependent value if given invalid input. import macros import std/private/since -from std/private/bitops_utils import forwardImpl, castToUnsigned -func bitnot*[T: SomeInteger](x: T): T {.magic: "BitnotI".} +proc bitnot*[T: SomeInteger](x: T): T {.magic: "BitnotI", noSideEffect.} ## Computes the `bitwise complement` of the integer `x`. func internalBitand[T: SomeInteger](x, y: T): T {.magic: "BitandI".} @@ -59,314 +58,339 @@ macro bitxor*[T: SomeInteger](x, y: T; z: varargs[T]): T = for extra in z: result = newCall(fn, result, extra) +const useBuiltins = not defined(noIntrinsicsBitOpts) +const noUndefined = defined(noUndefinedBitOpts) +const useGCC_builtins = (defined(gcc) or defined(llvm_gcc) or + defined(clang)) and useBuiltins +const useICC_builtins = defined(icc) and useBuiltins +const useVCC_builtins = defined(vcc) and useBuiltins +const arch64 = sizeof(int) == 8 -type BitsRange*[T] = range[0..sizeof(T)*8-1] - ## A range with all bit positions for type `T`. +template toUnsigned(x: int8): uint8 = cast[uint8](x) +template toUnsigned(x: int16): uint16 = cast[uint16](x) +template toUnsigned(x: int32): uint32 = cast[uint32](x) +template toUnsigned(x: int64): uint64 = cast[uint64](x) +template toUnsigned(x: int): uint = cast[uint](x) -func bitsliced*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = - ## Returns an extracted (and shifted) slice of bits from `v`. - runnableExamples: - doAssert 0b10111.bitsliced(2 .. 4) == 0b101 - doAssert 0b11100.bitsliced(0 .. 2) == 0b100 - doAssert 0b11100.bitsliced(0 ..< 3) == 0b100 +template forwardImpl(impl, arg) {.dirty.} = + when sizeof(x) <= 4: + when x is SomeSignedInt: + impl(cast[uint32](x.int32)) + else: + impl(x.uint32) + else: + when x is SomeSignedInt: + impl(cast[uint64](x.int64)) + else: + impl(x.uint64) - let - upmost = sizeof(T) * 8 - 1 - uv = v.castToUnsigned - (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T +when defined(nimHasalignOf): + type BitsRange*[T] = range[0..sizeof(T)*8-1] + ## A range with all bit positions for type ``T`` -proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = - ## Mutates `v` into an extracted (and shifted) slice of bits from `v`. - runnableExamples: - var x = 0b101110 - x.bitslice(2 .. 4) - doAssert x == 0b011 + func bitsliced*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = + ## Returns an extracted (and shifted) slice of bits from ``v``. + runnableExamples: + doAssert 0b10111.bitsliced(2 .. 4) == 0b101 + doAssert 0b11100.bitsliced(0 .. 2) == 0b100 + doAssert 0b11100.bitsliced(0 ..< 3) == 0b100 - let - upmost = sizeof(T) * 8 - 1 - uv = v.castToUnsigned - v = (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T + let + upmost = sizeof(T) * 8 - 1 + uv = when v is SomeUnsignedInt: v else: v.toUnsigned + (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T -func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} = - ## Creates a bitmask based on a slice of bits. - runnableExamples: - doAssert toMask[int32](1 .. 3) == 0b1110'i32 - doAssert toMask[int32](0 .. 3) == 0b1111'i32 + proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = + ## Mutates ``v`` into an extracted (and shifted) slice of bits from ``v``. + runnableExamples: + var x = 0b101110 + x.bitslice(2 .. 4) + doAssert x == 0b011 - let - upmost = sizeof(T) * 8 - 1 - bitmask = bitnot(0.T).castToUnsigned - (bitmask shl (upmost - slice.b + slice.a) shr (upmost - slice.b)).T + let + upmost = sizeof(T) * 8 - 1 + uv = when v is SomeUnsignedInt: v else: v.toUnsigned + v = (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T -proc masked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} = - ## Returns `v`, with only the `1` bits from `mask` matching those of - ## `v` set to 1. - ## - ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation. - runnableExamples: - let v = 0b0000_0011'u8 - doAssert v.masked(0b0000_1010'u8) == 0b0000_0010'u8 + func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} = + ## Creates a bitmask based on a slice of bits. + runnableExamples: + doAssert toMask[int32](1 .. 3) == 0b1110'i32 + doAssert toMask[int32](0 .. 3) == 0b1111'i32 - bitand(v, mask) + let + upmost = sizeof(T) * 8 - 1 + bitmask = when T is SomeUnsignedInt: + bitnot(0.T) + else: + bitnot(0.T).toUnsigned + (bitmask shl (upmost - slice.b + slice.a) shr (upmost - slice.b)).T -func masked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = - ## Returns `v`, with only the `1` bits in the range of `slice` - ## matching those of `v` set to 1. - ## - ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation. - runnableExamples: - let v = 0b0000_1011'u8 - doAssert v.masked(1 .. 3) == 0b0000_1010'u8 + proc masked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} = + ## Returns ``v``, with only the ``1`` bits from ``mask`` matching those of + ## ``v`` set to 1. + ## + ## Effectively maps to a `bitand` operation. + runnableExamples: + var v = 0b0000_0011'u8 + doAssert v.masked(0b0000_1010'u8) == 0b0000_0010'u8 - bitand(v, toMask[T](slice)) + bitand(v, mask) -proc mask*[T: SomeInteger](v: var T; mask: T) {.inline, since: (1, 3).} = - ## Mutates `v`, with only the `1` bits from `mask` matching those of - ## `v` set to 1. - ## - ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation. - runnableExamples: - var v = 0b0000_0011'u8 - v.mask(0b0000_1010'u8) - doAssert v == 0b0000_0010'u8 + func masked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = + ## Mutates ``v``, with only the ``1`` bits in the range of ``slice`` + ## matching those of ``v`` set to 1. + ## + ## Effectively maps to a `bitand` operation. + runnableExamples: + var v = 0b0000_1011'u8 + doAssert v.masked(1 .. 3) == 0b0000_1010'u8 - v = bitand(v, mask) + bitand(v, toMask[T](slice)) -proc mask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = - ## Mutates `v`, with only the `1` bits in the range of `slice` - ## matching those of `v` set to 1. - ## - ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation. - runnableExamples: - var v = 0b0000_1011'u8 - v.mask(1 .. 3) - doAssert v == 0b0000_1010'u8 + proc mask*[T: SomeInteger](v: var T; mask: T) {.inline, since: (1, 3).} = + ## Mutates ``v``, with only the ``1`` bits from ``mask`` matching those of + ## ``v`` set to 1. + ## + ## Effectively maps to a `bitand` operation. + runnableExamples: + var v = 0b0000_0011'u8 + v.mask(0b0000_1010'u8) + doAssert v == 0b0000_0010'u8 - v = bitand(v, toMask[T](slice)) + v = bitand(v, mask) -func setMasked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} = - ## Returns `v`, with all the `1` bits from `mask` set to 1. - ## - ## Effectively maps to a `bitor <#bitor.m,T,T,varargs[T]>`_ operation. - runnableExamples: - let v = 0b0000_0011'u8 - doAssert v.setMasked(0b0000_1010'u8) == 0b0000_1011'u8 + proc mask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = + ## Mutates ``v``, with only the ``1`` bits in the range of ``slice`` + ## matching those of ``v`` set to 1. + ## + ## Effectively maps to a `bitand` operation. + runnableExamples: + var v = 0b0000_1011'u8 + v.mask(1 .. 3) + doAssert v == 0b0000_1010'u8 - bitor(v, mask) + v = bitand(v, toMask[T](slice)) -func setMasked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = - ## Returns `v`, with all the `1` bits in the range of `slice` set to 1. - ## - ## Effectively maps to a `bitor <#bitor.m,T,T,varargs[T]>`_ operation. - runnableExamples: - let v = 0b0000_0011'u8 - doAssert v.setMasked(2 .. 3) == 0b0000_1111'u8 + func setMasked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} = + ## Returns ``v``, with all the ``1`` bits from ``mask`` set to 1. + ## + ## Effectively maps to a `bitor` operation. + runnableExamples: + var v = 0b0000_0011'u8 + doAssert v.setMasked(0b0000_1010'u8) == 0b0000_1011'u8 - bitor(v, toMask[T](slice)) + bitor(v, mask) -proc setMask*[T: SomeInteger](v: var T; mask: T) {.inline.} = - ## Mutates `v`, with all the `1` bits from `mask` set to 1. - ## - ## Effectively maps to a `bitor <#bitor.m,T,T,varargs[T]>`_ operation. - runnableExamples: - var v = 0b0000_0011'u8 - v.setMask(0b0000_1010'u8) - doAssert v == 0b0000_1011'u8 + func setMasked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = + ## Returns ``v``, with all the ``1`` bits in the range of ``slice`` set to 1. + ## + ## Effectively maps to a `bitor` operation. + runnableExamples: + var v = 0b0000_0011'u8 + doAssert v.setMasked(2 .. 3) == 0b0000_1111'u8 - v = bitor(v, mask) + bitor(v, toMask[T](slice)) -proc setMask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = - ## Mutates `v`, with all the `1` bits in the range of `slice` set to 1. - ## - ## Effectively maps to a `bitor <#bitor.m,T,T,varargs[T]>`_ operation. - runnableExamples: - var v = 0b0000_0011'u8 - v.setMask(2 .. 3) - doAssert v == 0b0000_1111'u8 + proc setMask*[T: SomeInteger](v: var T; mask: T) {.inline.} = + ## Mutates ``v``, with all the ``1`` bits from ``mask`` set to 1. + ## + ## Effectively maps to a `bitor` operation. + runnableExamples: + var v = 0b0000_0011'u8 + v.setMask(0b0000_1010'u8) + doAssert v == 0b0000_1011'u8 - v = bitor(v, toMask[T](slice)) + v = bitor(v, mask) -func clearMasked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} = - ## Returns `v`, with all the `1` bits from `mask` set to 0. - ## - ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation - ## with an *inverted mask*. - runnableExamples: - let v = 0b0000_0011'u8 - doAssert v.clearMasked(0b0000_1010'u8) == 0b0000_0001'u8 + proc setMask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = + ## Mutates ``v``, with all the ``1`` bits in the range of ``slice`` set to 1. + ## + ## Effectively maps to a `bitor` operation. + runnableExamples: + var v = 0b0000_0011'u8 + v.setMask(2 .. 3) + doAssert v == 0b0000_1111'u8 - bitand(v, bitnot(mask)) + v = bitor(v, toMask[T](slice)) -func clearMasked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = - ## Returns `v`, with all the `1` bits in the range of `slice` set to 0. - ## - ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation - ## with an *inverted mask*. - runnableExamples: - let v = 0b0000_0011'u8 - doAssert v.clearMasked(1 .. 3) == 0b0000_0001'u8 + func clearMasked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} = + ## Returns ``v``, with all the ``1`` bits from ``mask`` set to 0. + ## + ## Effectively maps to a `bitand` operation with an *inverted mask.* + runnableExamples: + var v = 0b0000_0011'u8 + doAssert v.clearMasked(0b0000_1010'u8) == 0b0000_0001'u8 - bitand(v, bitnot(toMask[T](slice))) + bitand(v, bitnot(mask)) -proc clearMask*[T: SomeInteger](v: var T; mask: T) {.inline.} = - ## Mutates `v`, with all the `1` bits from `mask` set to 0. - ## - ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation - ## with an *inverted mask*. - runnableExamples: - var v = 0b0000_0011'u8 - v.clearMask(0b0000_1010'u8) - doAssert v == 0b0000_0001'u8 + func clearMasked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = + ## Returns ``v``, with all the ``1`` bits in the range of ``slice`` set to 0. + ## + ## Effectively maps to a `bitand` operation with an *inverted mask.* + runnableExamples: + var v = 0b0000_0011'u8 + doAssert v.clearMasked(1 .. 3) == 0b0000_0001'u8 - v = bitand(v, bitnot(mask)) + bitand(v, bitnot(toMask[T](slice))) -proc clearMask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = - ## Mutates `v`, with all the `1` bits in the range of `slice` set to 0. - ## - ## Effectively maps to a `bitand <#bitand.m,T,T,varargs[T]>`_ operation - ## with an *inverted mask*. - runnableExamples: - var v = 0b0000_0011'u8 - v.clearMask(1 .. 3) - doAssert v == 0b0000_0001'u8 + proc clearMask*[T: SomeInteger](v: var T; mask: T) {.inline.} = + ## Mutates ``v``, with all the ``1`` bits from ``mask`` set to 0. + ## + ## Effectively maps to a `bitand` operation with an *inverted mask.* + runnableExamples: + var v = 0b0000_0011'u8 + v.clearMask(0b0000_1010'u8) + doAssert v == 0b0000_0001'u8 - v = bitand(v, bitnot(toMask[T](slice))) + v = bitand(v, bitnot(mask)) -func flipMasked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} = - ## Returns `v`, with all the `1` bits from `mask` flipped. - ## - ## Effectively maps to a `bitxor <#bitxor.m,T,T,varargs[T]>`_ operation. - runnableExamples: - let v = 0b0000_0011'u8 - doAssert v.flipMasked(0b0000_1010'u8) == 0b0000_1001'u8 + proc clearMask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = + ## Mutates ``v``, with all the ``1`` bits in the range of ``slice`` set to 0. + ## + ## Effectively maps to a `bitand` operation with an *inverted mask.* + runnableExamples: + var v = 0b0000_0011'u8 + v.clearMask(1 .. 3) + doAssert v == 0b0000_0001'u8 - bitxor(v, mask) + v = bitand(v, bitnot(toMask[T](slice))) -func flipMasked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = - ## Returns `v`, with all the `1` bits in the range of `slice` flipped. - ## - ## Effectively maps to a `bitxor <#bitxor.m,T,T,varargs[T]>`_ operation. - runnableExamples: - let v = 0b0000_0011'u8 - doAssert v.flipMasked(1 .. 3) == 0b0000_1101'u8 + func flipMasked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} = + ## Returns ``v``, with all the ``1`` bits from ``mask`` flipped. + ## + ## Effectively maps to a `bitxor` operation. + runnableExamples: + var v = 0b0000_0011'u8 + doAssert v.flipMasked(0b0000_1010'u8) == 0b0000_1001'u8 - bitxor(v, toMask[T](slice)) + bitxor(v, mask) -proc flipMask*[T: SomeInteger](v: var T; mask: T) {.inline.} = - ## Mutates `v`, with all the `1` bits from `mask` flipped. - ## - ## Effectively maps to a `bitxor <#bitxor.m,T,T,varargs[T]>`_ operation. - runnableExamples: - var v = 0b0000_0011'u8 - v.flipMask(0b0000_1010'u8) - doAssert v == 0b0000_1001'u8 + func flipMasked*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} = + ## Returns ``v``, with all the ``1`` bits in the range of ``slice`` flipped. + ## + ## Effectively maps to a `bitxor` operation. + runnableExamples: + var v = 0b0000_0011'u8 + doAssert v.flipMasked(1 .. 3) == 0b0000_1101'u8 - v = bitxor(v, mask) + bitxor(v, toMask[T](slice)) -proc flipMask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = - ## Mutates `v`, with all the `1` bits in the range of `slice` flipped. - ## - ## Effectively maps to a `bitxor <#bitxor.m,T,T,varargs[T]>`_ operation. - runnableExamples: - var v = 0b0000_0011'u8 - v.flipMask(1 .. 3) - doAssert v == 0b0000_1101'u8 + proc flipMask*[T: SomeInteger](v: var T; mask: T) {.inline.} = + ## Mutates ``v``, with all the ``1`` bits from ``mask`` flipped. + ## + ## Effectively maps to a `bitxor` operation. + runnableExamples: + var v = 0b0000_0011'u8 + v.flipMask(0b0000_1010'u8) + doAssert v == 0b0000_1001'u8 - v = bitxor(v, toMask[T](slice)) + v = bitxor(v, mask) -proc setBit*[T: SomeInteger](v: var T; bit: BitsRange[T]) {.inline.} = - ## Mutates `v`, with the bit at position `bit` set to 1. - runnableExamples: - var v = 0b0000_0011'u8 - v.setBit(5'u8) - doAssert v == 0b0010_0011'u8 + proc flipMask*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = + ## Mutates ``v``, with all the ``1`` bits in the range of ``slice`` flipped. + ## + ## Effectively maps to a `bitxor` operation. + runnableExamples: + var v = 0b0000_0011'u8 + v.flipMask(1 .. 3) + doAssert v == 0b0000_1101'u8 - v.setMask(1.T shl bit) + v = bitxor(v, toMask[T](slice)) -proc clearBit*[T: SomeInteger](v: var T; bit: BitsRange[T]) {.inline.} = - ## Mutates `v`, with the bit at position `bit` set to 0. - runnableExamples: - var v = 0b0000_0011'u8 - v.clearBit(1'u8) - doAssert v == 0b0000_0001'u8 + proc setBit*[T: SomeInteger](v: var T; bit: BitsRange[T]) {.inline.} = + ## Mutates ``v``, with the bit at position ``bit`` set to 1 + runnableExamples: + var v = 0b0000_0011'u8 + v.setBit(5'u8) + doAssert v == 0b0010_0011'u8 - v.clearMask(1.T shl bit) + v.setMask(1.T shl bit) -proc flipBit*[T: SomeInteger](v: var T; bit: BitsRange[T]) {.inline.} = - ## Mutates `v`, with the bit at position `bit` flipped. - runnableExamples: - var v = 0b0000_0011'u8 - v.flipBit(1'u8) - doAssert v == 0b0000_0001'u8 + proc clearBit*[T: SomeInteger](v: var T; bit: BitsRange[T]) {.inline.} = + ## Mutates ``v``, with the bit at position ``bit`` set to 0 + runnableExamples: + var v = 0b0000_0011'u8 + v.clearBit(1'u8) + doAssert v == 0b0000_0001'u8 - v = 0b0000_0011'u8 - v.flipBit(2'u8) - doAssert v == 0b0000_0111'u8 + v.clearMask(1.T shl bit) - v.flipMask(1.T shl bit) + proc flipBit*[T: SomeInteger](v: var T; bit: BitsRange[T]) {.inline.} = + ## Mutates ``v``, with the bit at position ``bit`` flipped + runnableExamples: + var v = 0b0000_0011'u8 + v.flipBit(1'u8) + doAssert v == 0b0000_0001'u8 -macro setBits*(v: typed; bits: varargs[typed]): untyped = - ## Mutates `v`, with the bits at positions `bits` set to 1. - runnableExamples: - var v = 0b0000_0011'u8 - v.setBits(3, 5, 7) - doAssert v == 0b1010_1011'u8 + v = 0b0000_0011'u8 + v.flipBit(2'u8) + doAssert v == 0b0000_0111'u8 - bits.expectKind(nnkBracket) - result = newStmtList() - for bit in bits: - result.add newCall("setBit", v, bit) + v.flipMask(1.T shl bit) -macro clearBits*(v: typed; bits: varargs[typed]): untyped = - ## Mutates `v`, with the bits at positions `bits` set to 0. - runnableExamples: - var v = 0b1111_1111'u8 - v.clearBits(1, 3, 5, 7) - doAssert v == 0b0101_0101'u8 + macro setBits*(v: typed; bits: varargs[typed]): untyped = + ## Mutates ``v``, with the bits at positions ``bits`` set to 1 + runnableExamples: + var v = 0b0000_0011'u8 + v.setBits(3, 5, 7) + doAssert v == 0b1010_1011'u8 - bits.expectKind(nnkBracket) - result = newStmtList() - for bit in bits: - result.add newCall("clearBit", v, bit) + bits.expectKind(nnkBracket) + result = newStmtList() + for bit in bits: + result.add newCall("setBit", v, bit) -macro flipBits*(v: typed; bits: varargs[typed]): untyped = - ## Mutates `v`, with the bits at positions `bits` set to 0. - runnableExamples: - var v = 0b0000_1111'u8 - v.flipBits(1, 3, 5, 7) - doAssert v == 0b1010_0101'u8 + macro clearBits*(v: typed; bits: varargs[typed]): untyped = + ## Mutates ``v``, with the bits at positions ``bits`` set to 0 + runnableExamples: + var v = 0b1111_1111'u8 + v.clearBits(1, 3, 5, 7) + doAssert v == 0b0101_0101'u8 - bits.expectKind(nnkBracket) - result = newStmtList() - for bit in bits: - result.add newCall("flipBit", v, bit) + bits.expectKind(nnkBracket) + result = newStmtList() + for bit in bits: + result.add newCall("clearBit", v, bit) + + macro flipBits*(v: typed; bits: varargs[typed]): untyped = + ## Mutates ``v``, with the bits at positions ``bits`` set to 0 + runnableExamples: + var v = 0b0000_1111'u8 + v.flipBits(1, 3, 5, 7) + doAssert v == 0b1010_0101'u8 + + bits.expectKind(nnkBracket) + result = newStmtList() + for bit in bits: + result.add newCall("flipBit", v, bit) -proc testBit*[T: SomeInteger](v: T; bit: BitsRange[T]): bool {.inline.} = - ## Returns true if the bit in `v` at positions `bit` is set to 1. - runnableExamples: - let v = 0b0000_1111'u8 - doAssert v.testBit(0) - doAssert not v.testBit(7) + proc testBit*[T: SomeInteger](v: T; bit: BitsRange[T]): bool {.inline.} = + ## Returns true if the bit in ``v`` at positions ``bit`` is set to 1 + runnableExamples: + var v = 0b0000_1111'u8 + doAssert v.testBit(0) + doAssert not v.testBit(7) - let mask = 1.T shl bit - return (v and mask) == mask + let mask = 1.T shl bit + return (v and mask) == mask # #### Pure Nim version #### -func firstSetBitNim(x: uint32): int {.inline.} = +proc firstSetBitNim(x: uint32): int {.inline, noSideEffect.} = ## Returns the 1-based index of the least significant set bit of x, or if x is zero, returns zero. # https://graphics.stanford.edu/%7Eseander/bithacks.html#ZerosOnRightMultLookup const lookup: array[32, uint8] = [0'u8, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9] - let v = x.uint32 - let k = not v + 1 # get two's complement # cast[uint32](-cast[int32](v)) + var v = x.uint32 + var k = not v + 1 # get two's complement # cast[uint32](-cast[int32](v)) result = 1 + lookup[uint32((v and k) * 0x077CB531'u32) shr 27].int -func firstSetBitNim(x: uint64): int {.inline.} = +proc firstSetBitNim(x: uint64): int {.inline, noSideEffect.} = ## Returns the 1-based index of the least significant set bit of x, or if x is zero, returns zero. # https://graphics.stanford.edu/%7Eseander/bithacks.html#ZerosOnRightMultLookup - let v = uint64(x) + var v = uint64(x) var k = uint32(v and 0xFFFFFFFF'u32) if k == 0: k = uint32(v shr 32'u32) and 0xFFFFFFFF'u32 @@ -375,7 +399,7 @@ func firstSetBitNim(x: uint64): int {.inline.} = result = 0 result += firstSetBitNim(k) -func fastlog2Nim(x: uint32): int {.inline.} = +proc fastlog2Nim(x: uint32): int {.inline, noSideEffect.} = ## Quickly find the log base 2 of a 32-bit or less integer. # https://graphics.stanford.edu/%7Eseander/bithacks.html#IntegerLogDeBruijn # https://stackoverflow.com/questions/11376288/fast-computing-of-log2-for-64-bit-integers @@ -389,7 +413,7 @@ func fastlog2Nim(x: uint32): int {.inline.} = v = v or v shr 16 result = lookup[uint32(v * 0x07C4ACDD'u32) shr 27].int -func fastlog2Nim(x: uint64): int {.inline.} = +proc fastlog2Nim(x: uint64): int {.inline, noSideEffect.} = ## Quickly find the log base 2 of a 64-bit integer. # https://graphics.stanford.edu/%7Eseander/bithacks.html#IntegerLogDeBruijn # https://stackoverflow.com/questions/11376288/fast-computing-of-log2-for-64-bit-integers @@ -406,11 +430,13 @@ func fastlog2Nim(x: uint64): int {.inline.} = v = v or v shr 32 result = lookup[(v * 0x03F6EAF2CD271461'u64) shr 58].int -import system/countbits_impl +# sets.nim cannot import bitops, but bitops can use include +# system/sets to eliminate code duplication. sets.nim defines +# countBits32 and countBits64. +include system/sets -const useBuiltinsRotate = (defined(amd64) or defined(i386)) and - (defined(gcc) or defined(clang) or defined(vcc) or - (defined(icl) and not defined(cpp))) and useBuiltins +template countSetBitsNim(n: uint32): int = countBits32(n) +template countSetBitsNim(n: uint64): int = countBits64(n) template parityImpl[T](value: T): int = # formula id from: https://graphics.stanford.edu/%7Eseander/bithacks.html#ParityParallel @@ -427,6 +453,11 @@ template parityImpl[T](value: T): int = when useGCC_builtins: + # Returns the number of set 1-bits in value. + proc builtin_popcount(x: cuint): cint {.importc: "__builtin_popcount", cdecl.} + proc builtin_popcountll(x: culonglong): cint {. + importc: "__builtin_popcountll", cdecl.} + # Returns the bit parity in value proc builtin_parity(x: cuint): cint {.importc: "__builtin_parity", cdecl.} proc builtin_parityll(x: culonglong): cint {.importc: "__builtin_parityll", cdecl.} @@ -444,17 +475,25 @@ when useGCC_builtins: proc builtin_ctzll(x: culonglong): cint {.importc: "__builtin_ctzll", cdecl.} elif useVCC_builtins: + # Counts the number of one bits (population count) in a 16-, 32-, or 64-byte unsigned integer. + proc builtin_popcnt16(a2: uint16): uint16 {. + importc: "__popcnt16", header: "", noSideEffect.} + proc builtin_popcnt32(a2: uint32): uint32 {. + importc: "__popcnt", header: "", noSideEffect.} + proc builtin_popcnt64(a2: uint64): uint64 {. + importc: "__popcnt64", header: "", noSideEffect.} + # Search the mask data from most significant bit (MSB) to least significant bit (LSB) for a set bit (1). - func bitScanReverse(index: ptr culong, mask: culong): uint8 {. - importc: "_BitScanReverse", header: "".} - func bitScanReverse64(index: ptr culong, mask: uint64): uint8 {. - importc: "_BitScanReverse64", header: "".} + proc bitScanReverse(index: ptr culong, mask: culong): cuchar {. + importc: "_BitScanReverse", header: "", noSideEffect.} + proc bitScanReverse64(index: ptr culong, mask: uint64): cuchar {. + importc: "_BitScanReverse64", header: "", noSideEffect.} # Search the mask data from least significant bit (LSB) to the most significant bit (MSB) for a set bit (1). - func bitScanForward(index: ptr culong, mask: culong): uint8 {. - importc: "_BitScanForward", header: "".} - func bitScanForward64(index: ptr culong, mask: uint64): uint8 {. - importc: "_BitScanForward64", header: "".} + proc bitScanForward(index: ptr culong, mask: culong): cuchar {. + importc: "_BitScanForward", header: "", noSideEffect.} + proc bitScanForward64(index: ptr culong, mask: uint64): cuchar {. + importc: "_BitScanForward64", header: "", noSideEffect.} template vcc_scan_impl(fnc: untyped; v: untyped): int = var index: culong @@ -462,38 +501,71 @@ elif useVCC_builtins: index.int elif useICC_builtins: + + # Intel compiler intrinsics: http://fulla.fnal.gov/intel/compiler_c/main_cls/intref_cls/common/intref_allia_misc.htm + # see also: https://software.intel.com/en-us/node/523362 + # Count the number of bits set to 1 in an integer a, and return that count in dst. + proc builtin_popcnt32(a: cint): cint {. + importc: "_popcnt", header: "", noSideEffect.} + proc builtin_popcnt64(a: uint64): cint {. + importc: "_popcnt64", header: "", noSideEffect.} + # Returns the number of trailing 0-bits in x, starting at the least significant bit position. If x is 0, the result is undefined. - func bitScanForward(p: ptr uint32, b: uint32): uint8 {. - importc: "_BitScanForward", header: "".} - func bitScanForward64(p: ptr uint32, b: uint64): uint8 {. - importc: "_BitScanForward64", header: "".} + proc bitScanForward(p: ptr uint32, b: uint32): cuchar {. + importc: "_BitScanForward", header: "", noSideEffect.} + proc bitScanForward64(p: ptr uint32, b: uint64): cuchar {. + importc: "_BitScanForward64", header: "", noSideEffect.} # Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined. - func bitScanReverse(p: ptr uint32, b: uint32): uint8 {. - importc: "_BitScanReverse", header: "".} - func bitScanReverse64(p: ptr uint32, b: uint64): uint8 {. - importc: "_BitScanReverse64", header: "".} + proc bitScanReverse(p: ptr uint32, b: uint32): cuchar {. + importc: "_BitScanReverse", header: "", noSideEffect.} + proc bitScanReverse64(p: ptr uint32, b: uint64): cuchar {. + importc: "_BitScanReverse64", header: "", noSideEffect.} template icc_scan_impl(fnc: untyped; v: untyped): int = var index: uint32 discard fnc(index.addr, v) index.int -func countSetBits*(x: SomeInteger): int {.inline.} = - ## Counts the set bits in an integer (also called `Hamming weight`:idx:). + +proc countSetBits*(x: SomeInteger): int {.inline, noSideEffect.} = + ## Counts the set bits in integer. (also called `Hamming weight`:idx:.) runnableExamples: doAssert countSetBits(0b0000_0011'u8) == 2 doAssert countSetBits(0b1010_1010'u8) == 4 - result = countSetBitsImpl(x) + # TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT. + # like GCC and MSVC + when x is SomeSignedInt: + let x = x.toUnsigned + when nimvm: + result = forwardImpl(countSetBitsNim, x) + else: + when useGCC_builtins: + when sizeof(x) <= 4: result = builtin_popcount(x.cuint).int + else: result = builtin_popcountll(x.culonglong).int + elif useVCC_builtins: + when sizeof(x) <= 2: result = builtin_popcnt16(x.uint16).int + elif sizeof(x) <= 4: result = builtin_popcnt32(x.uint32).int + elif arch64: result = builtin_popcnt64(x.uint64).int + else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).uint32).int + + builtin_popcnt32((x.uint64 shr 32'u64).uint32).int + elif useICC_builtins: + when sizeof(x) <= 4: result = builtin_popcnt32(x.cint).int + elif arch64: result = builtin_popcnt64(x.uint64).int + else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).cint).int + + builtin_popcnt32((x.uint64 shr 32'u64).cint).int + else: + when sizeof(x) <= 4: result = countSetBitsNim(x.uint32) + else: result = countSetBitsNim(x.uint64) -func popcount*(x: SomeInteger): int {.inline.} = - ## Alias for `countSetBits <#countSetBits,SomeInteger>`_ (Hamming weight). +proc popcount*(x: SomeInteger): int {.inline, noSideEffect.} = + ## Alias for for `countSetBits <#countSetBits,SomeInteger>`_. (Hamming weight.) result = countSetBits(x) -func parityBits*(x: SomeInteger): int {.inline.} = - ## Calculate the bit parity in an integer. If the number of 1-bits - ## is odd, the parity is 1, otherwise 0. +proc parityBits*(x: SomeInteger): int {.inline, noSideEffect.} = + ## Calculate the bit parity in integer. If number of 1-bit + ## is odd parity is 1, otherwise 0. runnableExamples: doAssert parityBits(0b0000_0000'u8) == 0 doAssert parityBits(0b0101_0001'u8) == 1 @@ -502,7 +574,8 @@ func parityBits*(x: SomeInteger): int {.inline.} = # Can be used a base if creating ASM version. # https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd - let x = x.castToUnsigned + when x is SomeSignedInt: + let x = x.toUnsigned when nimvm: result = forwardImpl(parityImpl, x) else: @@ -513,10 +586,10 @@ func parityBits*(x: SomeInteger): int {.inline.} = when sizeof(x) <= 4: result = parityImpl(x.uint32) else: result = parityImpl(x.uint64) -func firstSetBit*(x: SomeInteger): int {.inline.} = - ## Returns the 1-based index of the least significant set bit of `x`. - ## If `x` is zero, when `noUndefinedBitOpts` is set, the result is 0, - ## otherwise the result is undefined. +proc firstSetBit*(x: SomeInteger): int {.inline, noSideEffect.} = + ## Returns the 1-based index of the least significant set bit of x. + ## If `x` is zero, when ``noUndefinedBitOpts`` is set, result is 0, + ## otherwise result is undefined. runnableExamples: doAssert firstSetBit(0b0000_0001'u8) == 1 doAssert firstSetBit(0b0000_0010'u8) == 2 @@ -525,7 +598,8 @@ func firstSetBit*(x: SomeInteger): int {.inline.} = doAssert firstSetBit(0b0000_1111'u8) == 1 # GCC builtin 'builtin_ffs' already handle zero input. - let x = x.castToUnsigned + when x is SomeSignedInt: + let x = x.toUnsigned when nimvm: when noUndefined: if x == 0: @@ -556,10 +630,10 @@ func firstSetBit*(x: SomeInteger): int {.inline.} = when sizeof(x) <= 4: result = firstSetBitNim(x.uint32) else: result = firstSetBitNim(x.uint64) -func fastLog2*(x: SomeInteger): int {.inline.} = +proc fastLog2*(x: SomeInteger): int {.inline, noSideEffect.} = ## Quickly find the log base 2 of an integer. - ## If `x` is zero, when `noUndefinedBitOpts` is set, the result is -1, - ## otherwise the result is undefined. + ## If `x` is zero, when ``noUndefinedBitOpts`` is set, result is -1, + ## otherwise result is undefined. runnableExamples: doAssert fastLog2(0b0000_0001'u8) == 0 doAssert fastLog2(0b0000_0010'u8) == 1 @@ -567,7 +641,8 @@ func fastLog2*(x: SomeInteger): int {.inline.} = doAssert fastLog2(0b0000_1000'u8) == 3 doAssert fastLog2(0b0000_1111'u8) == 3 - let x = x.castToUnsigned + when x is SomeSignedInt: + let x = x.toUnsigned when noUndefined: if x == 0: return -1 @@ -595,12 +670,12 @@ func fastLog2*(x: SomeInteger): int {.inline.} = when sizeof(x) <= 4: result = fastlog2Nim(x.uint32) else: result = fastlog2Nim(x.uint64) -func countLeadingZeroBits*(x: SomeInteger): int {.inline.} = - ## Returns the number of leading zero bits in an integer. - ## If `x` is zero, when `noUndefinedBitOpts` is set, the result is 0, - ## otherwise the result is undefined. +proc countLeadingZeroBits*(x: SomeInteger): int {.inline, noSideEffect.} = + ## Returns the number of leading zero bits in integer. + ## If `x` is zero, when ``noUndefinedBitOpts`` is set, result is 0, + ## otherwise result is undefined. ## - ## **See also:** + ## See also: ## * `countTrailingZeroBits proc <#countTrailingZeroBits,SomeInteger>`_ runnableExamples: doAssert countLeadingZeroBits(0b0000_0001'u8) == 7 @@ -609,7 +684,8 @@ func countLeadingZeroBits*(x: SomeInteger): int {.inline.} = doAssert countLeadingZeroBits(0b0000_1000'u8) == 4 doAssert countLeadingZeroBits(0b0000_1111'u8) == 4 - let x = x.castToUnsigned + when x is SomeSignedInt: + let x = x.toUnsigned when noUndefined: if x == 0: return 0 @@ -623,12 +699,12 @@ func countLeadingZeroBits*(x: SomeInteger): int {.inline.} = when sizeof(x) <= 4: result = sizeof(x)*8 - 1 - fastlog2Nim(x.uint32) else: result = sizeof(x)*8 - 1 - fastlog2Nim(x.uint64) -func countTrailingZeroBits*(x: SomeInteger): int {.inline.} = - ## Returns the number of trailing zeros in an integer. - ## If `x` is zero, when `noUndefinedBitOpts` is set, the result is 0, - ## otherwise the result is undefined. +proc countTrailingZeroBits*(x: SomeInteger): int {.inline, noSideEffect.} = + ## Returns the number of trailing zeros in integer. + ## If `x` is zero, when ``noUndefinedBitOpts`` is set, result is 0, + ## otherwise result is undefined. ## - ## **See also:** + ## See also: ## * `countLeadingZeroBits proc <#countLeadingZeroBits,SomeInteger>`_ runnableExamples: doAssert countTrailingZeroBits(0b0000_0001'u8) == 0 @@ -637,7 +713,8 @@ func countTrailingZeroBits*(x: SomeInteger): int {.inline.} = doAssert countTrailingZeroBits(0b0000_1000'u8) == 3 doAssert countTrailingZeroBits(0b0000_1111'u8) == 0 - let x = x.castToUnsigned + when x is SomeSignedInt: + let x = x.toUnsigned when noUndefined: if x == 0: return 0 @@ -650,196 +727,99 @@ func countTrailingZeroBits*(x: SomeInteger): int {.inline.} = else: result = firstSetBit(x) - 1 -when useBuiltinsRotate: - when defined(gcc): - # GCC was tested until version 4.8.1 and intrinsics were present. Not tested - # in previous versions. - func builtin_rotl8(value: uint8, shift: cint): uint8 - {.importc: "__rolb", header: "".} - func builtin_rotl16(value: cushort, shift: cint): cushort - {.importc: "__rolw", header: "".} - func builtin_rotl32(value: cuint, shift: cint): cuint - {.importc: "__rold", header: "".} - when defined(amd64): - func builtin_rotl64(value: culonglong, shift: cint): culonglong - {.importc: "__rolq", header: "".} - func builtin_rotr8(value: uint8, shift: cint): uint8 - {.importc: "__rorb", header: "".} - func builtin_rotr16(value: cushort, shift: cint): cushort - {.importc: "__rorw", header: "".} - func builtin_rotr32(value: cuint, shift: cint): cuint - {.importc: "__rord", header: "".} - when defined(amd64): - func builtin_rotr64(value: culonglong, shift: cint): culonglong - {.importc: "__rorq", header: "".} - elif defined(clang): - # In CLANG, builtins have been present since version 8.0.0 and intrinsics - # since version 9.0.0. This implementation chose the builtins, as they have - # been around for longer. - # https://releases.llvm.org/8.0.0/tools/clang/docs/ReleaseNotes.html#non-comprehensive-list-of-changes-in-this-release - # https://releases.llvm.org/8.0.0/tools/clang/docs/LanguageExtensions.html#builtin-rotateleft - # source for correct declarations: https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Basic/Builtins.def - func builtin_rotl8(value: uint8, shift: uint8): uint8 - {.importc: "__builtin_rotateleft8", nodecl.} - func builtin_rotl16(value: cushort, shift: cushort): cushort - {.importc: "__builtin_rotateleft16", nodecl.} - func builtin_rotl32(value: cuint, shift: cuint): cuint - {.importc: "__builtin_rotateleft32", nodecl.} - when defined(amd64): - func builtin_rotl64(value: culonglong, shift: culonglong): culonglong - {.importc: "__builtin_rotateleft64", nodecl.} - - func builtin_rotr8(value: uint8, shift: uint8): uint8 - {.importc: "__builtin_rotateright8", nodecl.} - func builtin_rotr16(value: cushort, shift: cushort): cushort - {.importc: "__builtin_rotateright16", nodecl.} - func builtin_rotr32(value: cuint, shift: cuint): cuint - {.importc: "__builtin_rotateright32", nodecl.} - when defined(amd64): - # shift is unsigned, refs https://github.com/llvm-mirror/clang/commit/892de415b7fde609dafc4e6c1643b7eaa0150a4d - func builtin_rotr64(value: culonglong, shift: culonglong): culonglong - {.importc: "__builtin_rotateright64", nodecl.} - elif defined(vcc): - # Tested on Microsoft (R) C/C++ Optimizing Compiler 19.28.29335 x64 and x86. - # Not tested in previous versions. - # https://docs.microsoft.com/en-us/cpp/intrinsics/rotl8-rotl16?view=msvc-160 - # https://docs.microsoft.com/en-us/cpp/intrinsics/rotr8-rotr16?view=msvc-160 - # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rotl-rotl64-rotr-rotr64?view=msvc-160 - func builtin_rotl8(value: uint8, shift: uint8): uint8 - {.importc: "_rotl8", header: "".} - func builtin_rotl16(value: cushort, shift: uint8): cushort - {.importc: "_rotl16", header: "".} - func builtin_rotl32(value: cuint, shift: cint): cuint - {.importc: "_rotl", header: "".} - when defined(amd64): - func builtin_rotl64(value: culonglong, shift: cint): culonglong - {.importc: "_rotl64", header: "".} - - func builtin_rotr8(value: uint8, shift: uint8): uint8 - {.importc: "_rotr8", header: "".} - func builtin_rotr16(value: cushort, shift: uint8): cushort - {.importc: "_rotr16", header: "".} - func builtin_rotr32(value: cuint, shift: cint): cuint - {.importc: "_rotr", header: "".} - when defined(amd64): - func builtin_rotr64(value: culonglong, shift: cint): culonglong - {.importc: "_rotr64", header: "".} - elif defined(icl): - # Tested on Intel(R) C++ Intel(R) 64 Compiler Classic Version 2021.1.2 Build - # 20201208_000000 x64 and x86. Not tested in previous versions. - func builtin_rotl8(value: uint8, shift: cint): uint8 - {.importc: "__rolb", header: "".} - func builtin_rotl16(value: cushort, shift: cint): cushort - {.importc: "__rolw", header: "".} - func builtin_rotl32(value: cuint, shift: cint): cuint - {.importc: "__rold", header: "".} - when defined(amd64): - func builtin_rotl64(value: culonglong, shift: cint): culonglong - {.importc: "__rolq", header: "".} - - func builtin_rotr8(value: uint8, shift: cint): uint8 - {.importc: "__rorb", header: "".} - func builtin_rotr16(value: cushort, shift: cint): cushort - {.importc: "__rorw", header: "".} - func builtin_rotr32(value: cuint, shift: cint): cuint - {.importc: "__rord", header: "".} - when defined(amd64): - func builtin_rotr64(value: culonglong, shift: cint): culonglong - {.importc: "__rorq", header: "".} - -func rotl[T: SomeUnsignedInt](value: T, rot: int32): T {.inline.} = - ## Left-rotate bits in a `value`. - # https://stackoverflow.com/a/776523 - const mask = 8 * sizeof(value) - 1 - let rot = rot and mask - (value shl rot) or (value shr ((-rot) and mask)) - -func rotr[T: SomeUnsignedInt](value: T, rot: int32): T {.inline.} = - ## Right-rotate bits in a `value`. - const mask = 8 * sizeof(value) - 1 - let rot = rot and mask - (value shr rot) or (value shl ((-rot) and mask)) - -func shiftTypeTo(size: static int, shift: int): auto {.inline.} = - ## Returns the `shift` for the rotation according to the compiler and the - ## `size`. - when (defined(vcc) and (size in [4, 8])) or defined(gcc) or defined(icl): - cint(shift) - elif (defined(vcc) and (size in [1, 2])) or (defined(clang) and size == 1): - uint8(shift) - elif defined(clang): - when size == 2: - cushort(shift) - elif size == 4: - cuint(shift) - elif size == 8: - culonglong(shift) - -func rotateLeftBits*[T: SomeUnsignedInt](value: T, shift: range[0..(sizeof(T) * 8)]): T {.inline.} = - ## Left-rotate bits in a `value`. +proc rotateLeftBits*(value: uint8; + amount: range[0..8]): uint8 {.inline, noSideEffect.} = + ## Left-rotate bits in a 8-bits value. runnableExamples: - doAssert rotateLeftBits(0b0110_1001'u8, 4) == 0b1001_0110'u8 - doAssert rotateLeftBits(0b00111100_11000011'u16, 8) == - 0b11000011_00111100'u16 - doAssert rotateLeftBits(0b0000111111110000_1111000000001111'u32, 16) == - 0b1111000000001111_0000111111110000'u32 - doAssert rotateLeftBits(0b00000000111111111111111100000000_11111111000000000000000011111111'u64, 32) == - 0b11111111000000000000000011111111_00000000111111111111111100000000'u64 - when nimvm: - rotl(value, shift.int32) - else: - when useBuiltinsRotate: - const size = sizeof(T) - when size == 1: - builtin_rotl8(value.uint8, shiftTypeTo(size, shift)).T - elif size == 2: - builtin_rotl16(value.cushort, shiftTypeTo(size, shift)).T - elif size == 4: - builtin_rotl32(value.cuint, shiftTypeTo(size, shift)).T - elif size == 8 and arch64: - builtin_rotl64(value.culonglong, shiftTypeTo(size, shift)).T - else: - rotl(value, shift.int32) - else: - rotl(value, shift.int32) + doAssert rotateLeftBits(0b0000_0001'u8, 1) == 0b0000_0010'u8 + doAssert rotateLeftBits(0b0000_0001'u8, 2) == 0b0000_0100'u8 + doAssert rotateLeftBits(0b0100_0001'u8, 1) == 0b1000_0010'u8 + doAssert rotateLeftBits(0b0100_0001'u8, 2) == 0b0000_0101'u8 -func rotateRightBits*[T: SomeUnsignedInt](value: T, shift: range[0..(sizeof(T) * 8)]): T {.inline.} = - ## Right-rotate bits in a `value`. + # using this form instead of the one below should handle any value + # out of range as well as negative values. + # result = (value shl amount) or (value shr (8 - amount)) + # taken from: https://en.wikipedia.org/wiki/Circular_shift#Implementing_circular_shifts + let amount = amount and 7 + result = (value shl amount) or (value shr ( (-amount) and 7)) + +proc rotateLeftBits*(value: uint16; + amount: range[0..16]): uint16 {.inline, noSideEffect.} = + ## Left-rotate bits in a 16-bits value. + ## + ## See also: + ## * `rotateLeftBits proc <#rotateLeftBits,uint8,range[]>`_ + let amount = amount and 15 + result = (value shl amount) or (value shr ( (-amount) and 15)) + +proc rotateLeftBits*(value: uint32; + amount: range[0..32]): uint32 {.inline, noSideEffect.} = + ## Left-rotate bits in a 32-bits value. + ## + ## See also: + ## * `rotateLeftBits proc <#rotateLeftBits,uint8,range[]>`_ + let amount = amount and 31 + result = (value shl amount) or (value shr ( (-amount) and 31)) + +proc rotateLeftBits*(value: uint64; + amount: range[0..64]): uint64 {.inline, noSideEffect.} = + ## Left-rotate bits in a 64-bits value. + ## + ## See also: + ## * `rotateLeftBits proc <#rotateLeftBits,uint8,range[]>`_ + let amount = amount and 63 + result = (value shl amount) or (value shr ( (-amount) and 63)) + + +proc rotateRightBits*(value: uint8; + amount: range[0..8]): uint8 {.inline, noSideEffect.} = + ## Right-rotate bits in a 8-bits value. runnableExamples: - doAssert rotateRightBits(0b0110_1001'u8, 4) == 0b1001_0110'u8 - doAssert rotateRightBits(0b00111100_11000011'u16, 8) == - 0b11000011_00111100'u16 - doAssert rotateRightBits(0b0000111111110000_1111000000001111'u32, 16) == - 0b1111000000001111_0000111111110000'u32 - doAssert rotateRightBits(0b00000000111111111111111100000000_11111111000000000000000011111111'u64, 32) == - 0b11111111000000000000000011111111_00000000111111111111111100000000'u64 - when nimvm: - rotr(value, shift.int32) - else: - when useBuiltinsRotate: - const size = sizeof(T) - when size == 1: - builtin_rotr8(value.uint8, shiftTypeTo(size, shift)).T - elif size == 2: - builtin_rotr16(value.cushort, shiftTypeTo(size, shift)).T - elif size == 4: - builtin_rotr32(value.cuint, shiftTypeTo(size, shift)).T - elif size == 8 and arch64: - builtin_rotr64(value.culonglong, shiftTypeTo(size, shift)).T - else: - rotr(value, shift.int32) - else: - rotr(value, shift.int32) + doAssert rotateRightBits(0b0000_0001'u8, 1) == 0b1000_0000'u8 + doAssert rotateRightBits(0b0000_0001'u8, 2) == 0b0100_0000'u8 + doAssert rotateRightBits(0b0100_0001'u8, 1) == 0b1010_0000'u8 + doAssert rotateRightBits(0b0100_0001'u8, 2) == 0b0101_0000'u8 -func repeatBits[T: SomeUnsignedInt](x: SomeUnsignedInt; retType: type[T]): T = + let amount = amount and 7 + result = (value shr amount) or (value shl ( (-amount) and 7)) + +proc rotateRightBits*(value: uint16; + amount: range[0..16]): uint16 {.inline, noSideEffect.} = + ## Right-rotate bits in a 16-bits value. + ## + ## See also: + ## * `rotateRightBits proc <#rotateRightBits,uint8,range[]>`_ + let amount = amount and 15 + result = (value shr amount) or (value shl ( (-amount) and 15)) + +proc rotateRightBits*(value: uint32; + amount: range[0..32]): uint32 {.inline, noSideEffect.} = + ## Right-rotate bits in a 32-bits value. + ## + ## See also: + ## * `rotateRightBits proc <#rotateRightBits,uint8,range[]>`_ + let amount = amount and 31 + result = (value shr amount) or (value shl ( (-amount) and 31)) + +proc rotateRightBits*(value: uint64; + amount: range[0..64]): uint64 {.inline, noSideEffect.} = + ## Right-rotate bits in a 64-bits value. + ## + ## See also: + ## * `rotateRightBits proc <#rotateRightBits,uint8,range[]>`_ + let amount = amount and 63 + result = (value shr amount) or (value shl ( (-amount) and 63)) + +proc repeatBits[T: SomeUnsignedInt](x: SomeUnsignedInt; retType: type[T]): T {. + noSideEffect.} = result = x var i = 1 while i != (sizeof(T) div sizeof(x)): result = (result shl (sizeof(x)*8*i)) or result i *= 2 -func reverseBits*[T: SomeUnsignedInt](x: T): T = +proc reverseBits*[T: SomeUnsignedInt](x: T): T {.noSideEffect.} = ## Return the bit reversal of x. runnableExamples: doAssert reverseBits(0b10100100'u8) == 0b00100101'u8 diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim index ab248db36..9b3b67104 100644 --- a/lib/pure/browsers.nim +++ b/lib/pure/browsers.nim @@ -16,23 +16,16 @@ import std/private/since import strutils -when defined(nimPreviewSlimSystem): - import std/assertions - when defined(windows): import winlean - when defined(nimPreviewSlimSystem): - import std/widestrs from os import absolutePath else: - import os - when not defined(osx): - import osproc + import os, osproc const osOpenCmd* = when defined(macos) or defined(macosx) or defined(windows): "open" else: "xdg-open" ## \ ## Alias for the operating system specific *"open"* command, - ## `"open"` on OSX, MacOS and Windows, `"xdg-open"` on Linux, BSD, etc. + ## ``"open"`` on OSX, MacOS and Windows, ``"xdg-open"`` on Linux, BSD, etc. proc prepare(s: string): string = if s.contains("://"): @@ -50,9 +43,9 @@ proc openDefaultBrowserImpl(url: string) = else: var u = quoteShell(prepare url) if execShellCmd(osOpenCmd & " " & u) == 0: return - for b in getEnv("BROWSER").split(PathSep): + for b in getEnv("BROWSER").string.split(PathSep): try: - # we use `startProcess` here because we don't want to block! + # we use ``startProcess`` here because we don't want to block! discard startProcess(command = b, args = [url], options = {poUsePath}) return except OSError: @@ -62,16 +55,15 @@ proc openDefaultBrowser*(url: string) = ## Opens `url` with the user's default browser. This does not block. ## The URL must not be empty string, to open on a blank page see `openDefaultBrowser()`. ## - ## Under Windows, `ShellExecute` is used. Under Mac OS X the `open` - ## command is used. Under Unix, it is checked if `xdg-open` exists and - ## used if it does. Otherwise the environment variable `BROWSER` is + ## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open`` + ## command is used. Under Unix, it is checked if ``xdg-open`` exists and + ## used if it does. Otherwise the environment variable ``BROWSER`` is ## used to determine the default browser to use. ## ## This proc doesn't raise an exception on error, beware. ## - ## ```nim + ## .. code-block:: nim ## block: openDefaultBrowser("https://nim-lang.org") - ## ``` doAssert url.len > 0, "URL must not be empty string" openDefaultBrowserImpl(url) @@ -79,18 +71,17 @@ proc openDefaultBrowser*() {.since: (1, 1).} = ## Opens the user's default browser without any `url` (blank page). This does not block. ## Implements IETF RFC-6694 Section 3, "about:blank" must be reserved for a blank page. ## - ## Under Windows, `ShellExecute` is used. Under Mac OS X the `open` - ## command is used. Under Unix, it is checked if `xdg-open` exists and - ## used if it does. Otherwise the environment variable `BROWSER` is + ## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open`` + ## command is used. Under Unix, it is checked if ``xdg-open`` exists and + ## used if it does. Otherwise the environment variable ``BROWSER`` is ## used to determine the default browser to use. ## ## This proc doesn't raise an exception on error, beware. ## - ## ```nim - ## block: openDefaultBrowser() - ## ``` - ## ## **See also:** ## ## * https://tools.ietf.org/html/rfc6694#section-3 + ## + ## .. code-block:: nim + ## block: openDefaultBrowser() openDefaultBrowserImpl("http:about:blank") # See IETF RFC-6694 Section 3. diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index e761569a1..8d827f555 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -9,32 +9,29 @@ ## This module implements helper procs for CGI applications. Example: ## -## ```Nim -## import std/[strtabs, cgi] +## .. code-block:: Nim ## -## # Fill the values when debugging: -## when debug: -## setTestData("name", "Klaus", "password", "123456") -## # read the data into `myData` -## var myData = readData() -## # check that the data's variable names are "name" or "password" -## validateData(myData, "name", "password") -## # start generating content: -## writeContentType() -## # generate content: -## write(stdout, "\n") -## write(stdout, "Test\n") -## writeLine(stdout, "your name: " & myData["name"]) -## writeLine(stdout, "your password: " & myData["password"]) -## writeLine(stdout, "") -## ``` +## import strtabs, cgi +## +## # Fill the values when debugging: +## when debug: +## setTestData("name", "Klaus", "password", "123456") +## # read the data into `myData` +## var myData = readData() +## # check that the data's variable names are "name" or "password" +## validateData(myData, "name", "password") +## # start generating content: +## writeContentType() +## # generate content: +## write(stdout, "\n") +## write(stdout, "Test\n") +## writeLine(stdout, "your name: " & myData["name"]) +## writeLine(stdout, "your password: " & myData["password"]) +## writeLine(stdout, "") -import strutils, os, strtabs, cookies, uri +import std/[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 @@ -66,11 +63,11 @@ proc cgiError*(msg: string) {.noreturn.} = raise newException(CgiError, msg) proc getEncodedData(allowedMethods: set[RequestMethod]): string = - case getEnv("REQUEST_METHOD") + case getEnv("REQUEST_METHOD").string of "POST": if methodPost notin allowedMethods: cgiError("'REQUEST_METHOD' 'POST' is not supported") - var L = parseInt(getEnv("CONTENT_LENGTH")) + var L = parseInt(getEnv("CONTENT_LENGTH").string) if L == 0: return "" result = newString(L) @@ -79,25 +76,31 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string = of "GET": if methodGet notin allowedMethods: cgiError("'REQUEST_METHOD' 'GET' is not supported") - result = getEnv("QUERY_STRING") + result = getEnv("QUERY_STRING").string else: if methodNone notin allowedMethods: cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'") -iterator decodeData*(data: string): tuple[key, value: string] = +iterator decodeData*(data: string): tuple[key, value: TaintedString] = ## Reads and decodes CGI data and yields the (name, value) pairs the ## data consists of. - for (key, value) in uri.decodeQuery(data): - yield (key, value) + try: + for (key, value) in uri.decodeQuery(data): + yield (key, value) + except UriParseError as e: + cgiError(e.msg) iterator decodeData*(allowedMethods: set[RequestMethod] = - {methodNone, methodPost, methodGet}): tuple[key, value: string] = + {methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] = ## Reads and decodes CGI data and yields the (name, value) pairs the ## data consists of. If the client does not use a method listed in the ## `allowedMethods` set, a `CgiError` exception is raised. let data = getEncodedData(allowedMethods) - for (key, value) in uri.decodeQuery(data): - yield (key, value) + try: + for (key, value) in uri.decodeQuery(data): + yield (key, value) + except UriParseError as e: + cgiError(e.msg) proc readData*(allowedMethods: set[RequestMethod] = {methodNone, methodPost, methodGet}): StringTableRef = @@ -105,13 +108,13 @@ proc readData*(allowedMethods: set[RequestMethod] = ## `allowedMethods` set, a `CgiError` exception is raised. result = newStringTable() for name, value in decodeData(allowedMethods): - result[name] = value + result[name.string] = value.string proc readData*(data: string): StringTableRef = ## Reads CGI data from a string. result = newStringTable() for name, value in decodeData(data): - result[name] = value + result[name.string] = value.string proc validateData*(data: StringTableRef, validKeys: varargs[string]) = ## Validates data; raises `CgiError` if this fails. This checks that each variable @@ -122,139 +125,139 @@ proc validateData*(data: StringTableRef, validKeys: varargs[string]) = proc getContentLength*(): string = ## Returns contents of the `CONTENT_LENGTH` environment variable. - return getEnv("CONTENT_LENGTH") + return getEnv("CONTENT_LENGTH").string proc getContentType*(): string = ## Returns contents of the `CONTENT_TYPE` environment variable. - return getEnv("CONTENT_Type") + return getEnv("CONTENT_Type").string proc getDocumentRoot*(): string = ## Returns contents of the `DOCUMENT_ROOT` environment variable. - return getEnv("DOCUMENT_ROOT") + return getEnv("DOCUMENT_ROOT").string proc getGatewayInterface*(): string = ## Returns contents of the `GATEWAY_INTERFACE` environment variable. - return getEnv("GATEWAY_INTERFACE") + return getEnv("GATEWAY_INTERFACE").string proc getHttpAccept*(): string = ## Returns contents of the `HTTP_ACCEPT` environment variable. - return getEnv("HTTP_ACCEPT") + return getEnv("HTTP_ACCEPT").string proc getHttpAcceptCharset*(): string = ## Returns contents of the `HTTP_ACCEPT_CHARSET` environment variable. - return getEnv("HTTP_ACCEPT_CHARSET") + return getEnv("HTTP_ACCEPT_CHARSET").string proc getHttpAcceptEncoding*(): string = ## Returns contents of the `HTTP_ACCEPT_ENCODING` environment variable. - return getEnv("HTTP_ACCEPT_ENCODING") + return getEnv("HTTP_ACCEPT_ENCODING").string proc getHttpAcceptLanguage*(): string = ## Returns contents of the `HTTP_ACCEPT_LANGUAGE` environment variable. - return getEnv("HTTP_ACCEPT_LANGUAGE") + return getEnv("HTTP_ACCEPT_LANGUAGE").string proc getHttpConnection*(): string = ## Returns contents of the `HTTP_CONNECTION` environment variable. - return getEnv("HTTP_CONNECTION") + return getEnv("HTTP_CONNECTION").string proc getHttpCookie*(): string = ## Returns contents of the `HTTP_COOKIE` environment variable. - return getEnv("HTTP_COOKIE") + return getEnv("HTTP_COOKIE").string proc getHttpHost*(): string = ## Returns contents of the `HTTP_HOST` environment variable. - return getEnv("HTTP_HOST") + return getEnv("HTTP_HOST").string proc getHttpReferer*(): string = ## Returns contents of the `HTTP_REFERER` environment variable. - return getEnv("HTTP_REFERER") + return getEnv("HTTP_REFERER").string proc getHttpUserAgent*(): string = ## Returns contents of the `HTTP_USER_AGENT` environment variable. - return getEnv("HTTP_USER_AGENT") + return getEnv("HTTP_USER_AGENT").string proc getPathInfo*(): string = ## Returns contents of the `PATH_INFO` environment variable. - return getEnv("PATH_INFO") + return getEnv("PATH_INFO").string proc getPathTranslated*(): string = ## Returns contents of the `PATH_TRANSLATED` environment variable. - return getEnv("PATH_TRANSLATED") + return getEnv("PATH_TRANSLATED").string proc getQueryString*(): string = ## Returns contents of the `QUERY_STRING` environment variable. - return getEnv("QUERY_STRING") + return getEnv("QUERY_STRING").string proc getRemoteAddr*(): string = ## Returns contents of the `REMOTE_ADDR` environment variable. - return getEnv("REMOTE_ADDR") + return getEnv("REMOTE_ADDR").string proc getRemoteHost*(): string = ## Returns contents of the `REMOTE_HOST` environment variable. - return getEnv("REMOTE_HOST") + return getEnv("REMOTE_HOST").string proc getRemoteIdent*(): string = ## Returns contents of the `REMOTE_IDENT` environment variable. - return getEnv("REMOTE_IDENT") + return getEnv("REMOTE_IDENT").string proc getRemotePort*(): string = ## Returns contents of the `REMOTE_PORT` environment variable. - return getEnv("REMOTE_PORT") + return getEnv("REMOTE_PORT").string proc getRemoteUser*(): string = ## Returns contents of the `REMOTE_USER` environment variable. - return getEnv("REMOTE_USER") + return getEnv("REMOTE_USER").string proc getRequestMethod*(): string = ## Returns contents of the `REQUEST_METHOD` environment variable. - return getEnv("REQUEST_METHOD") + return getEnv("REQUEST_METHOD").string proc getRequestURI*(): string = ## Returns contents of the `REQUEST_URI` environment variable. - return getEnv("REQUEST_URI") + return getEnv("REQUEST_URI").string proc getScriptFilename*(): string = ## Returns contents of the `SCRIPT_FILENAME` environment variable. - return getEnv("SCRIPT_FILENAME") + return getEnv("SCRIPT_FILENAME").string proc getScriptName*(): string = ## Returns contents of the `SCRIPT_NAME` environment variable. - return getEnv("SCRIPT_NAME") + return getEnv("SCRIPT_NAME").string proc getServerAddr*(): string = ## Returns contents of the `SERVER_ADDR` environment variable. - return getEnv("SERVER_ADDR") + return getEnv("SERVER_ADDR").string proc getServerAdmin*(): string = ## Returns contents of the `SERVER_ADMIN` environment variable. - return getEnv("SERVER_ADMIN") + return getEnv("SERVER_ADMIN").string proc getServerName*(): string = ## Returns contents of the `SERVER_NAME` environment variable. - return getEnv("SERVER_NAME") + return getEnv("SERVER_NAME").string proc getServerPort*(): string = ## Returns contents of the `SERVER_PORT` environment variable. - return getEnv("SERVER_PORT") + return getEnv("SERVER_PORT").string proc getServerProtocol*(): string = ## Returns contents of the `SERVER_PROTOCOL` environment variable. - return getEnv("SERVER_PROTOCOL") + return getEnv("SERVER_PROTOCOL").string proc getServerSignature*(): string = ## Returns contents of the `SERVER_SIGNATURE` environment variable. - return getEnv("SERVER_SIGNATURE") + return getEnv("SERVER_SIGNATURE").string proc getServerSoftware*(): string = ## Returns contents of the `SERVER_SOFTWARE` environment variable. - return getEnv("SERVER_SOFTWARE") + return getEnv("SERVER_SOFTWARE").string proc setTestData*(keysvalues: varargs[string]) = ## Fills the appropriate environment variables to test your CGI application. ## This can only simulate the 'GET' request method. `keysvalues` should ## provide embedded (name, value)-pairs. Example: - ## ```Nim - ## setTestData("name", "Hanz", "password", "12345") - ## ``` + ## + ## .. code-block:: Nim + ## setTestData("name", "Hanz", "password", "12345") putEnv("REQUEST_METHOD", "GET") var i = 0 var query = "" @@ -269,9 +272,9 @@ proc setTestData*(keysvalues: varargs[string]) = proc writeContentType*() = ## Calls this before starting to send your HTML data to `stdout`. This ## implements this part of the CGI protocol: - ## ```Nim - ## write(stdout, "Content-type: text/html\n\n") - ## ``` + ## + ## .. code-block:: Nim + ## write(stdout, "Content-type: text/html\n\n") write(stdout, "Content-type: text/html\n\n") proc resetForStacktrace() = @@ -304,10 +307,10 @@ proc setCookie*(name, value: string) = var gcookies {.threadvar.}: StringTableRef -proc getCookie*(name: string): string = +proc getCookie*(name: string): TaintedString = ## Gets a cookie. If no cookie of `name` exists, "" is returned. if gcookies == nil: gcookies = parseCookies(getHttpCookie()) - result = gcookies.getOrDefault(name) + result = TaintedString(gcookies.getOrDefault(name)) proc existsCookie*(name: string): bool = ## Checks if a cookie of `name` exists. diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 24257dacb..113939567 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -13,7 +13,7 @@ ## (A crit bit tree is a form of `radix tree`:idx: or `patricia trie`:idx:.) runnableExamples: - from std/sequtils import toSeq + from sequtils import toSeq var critbitAsSet: CritBitTree[void] = ["kitten", "puppy"].toCritBitTree doAssert critbitAsSet.len == 2 @@ -36,9 +36,6 @@ runnableExamples: import std/private/since -when defined(nimPreviewSlimSystem): - import std/assertions - type NodeObj[T] {.acyclic.} = object byte: int ## byte index of the difference @@ -200,7 +197,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: sink T): bool = +proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool = ## Returns true if `c` contains the given `key`. If the key does not exist, ## `c[key] = val` is performed. ## @@ -273,7 +270,7 @@ proc incl*(c: var CritBitTree[void], key: string) = discard rawInsert(c, key) -proc incl*[T](c: var CritBitTree[T], key: string, val: sink T) = +proc incl*[T](c: var CritBitTree[T], key: string, val: T) = ## Inserts `key` with value `val` into `c`. ## ## **See also:** @@ -287,7 +284,7 @@ proc incl*[T](c: var CritBitTree[T], key: string, val: sink T) = var n = rawInsert(c, key) n.val = val -proc `[]=`*[T](c: var CritBitTree[T], key: string, val: sink T) = +proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = ## Alias for `incl <#incl,CritBitTree[T],string,T>`_. ## ## **See also:** @@ -303,7 +300,7 @@ template get[T](c: CritBitTree[T], key: string): T = n.val -func `[]`*[T](c: CritBitTree[T], key: string): lent T {.inline.} = +func `[]`*[T](c: CritBitTree[T], key: string): 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. @@ -338,21 +335,21 @@ iterator leaves[T](n: Node[T]): Node[T] = iterator keys*[T](c: CritBitTree[T]): string = ## Yields all keys in lexicographical order. runnableExamples: - from std/sequtils import toSeq + from sequtils import toSeq let c = {"key1": 1, "key2": 2}.toCritBitTree doAssert toSeq(c.keys) == @["key1", "key2"] for x in leaves(c.root): yield x.key -iterator values*[T](c: CritBitTree[T]): lent T = +iterator values*[T](c: CritBitTree[T]): T = ## Yields all values of `c` in the lexicographical order of the ## corresponding keys. ## ## **See also:** ## * `mvalues iterator <#mvalues.i,CritBitTree[T]>`_ runnableExamples: - from std/sequtils import toSeq + from sequtils import toSeq let c = {"key1": 1, "key2": 2}.toCritBitTree doAssert toSeq(c.values) == @[1, 2] @@ -378,7 +375,7 @@ iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] = ## **See also:** ## * `mpairs iterator <#mpairs.i,CritBitTree[T]>`_ runnableExamples: - from std/sequtils import toSeq + from sequtils import toSeq let c = {"key1": 1, "key2": 2}.toCritBitTree doAssert toSeq(c.pairs) == @[(key: "key1", val: 1), (key: "key2", val: 2)] @@ -393,7 +390,8 @@ iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] = ## * `pairs iterator <#pairs.i,CritBitTree[T]>`_ for x in leaves(c.root): yield (x.key, x.val) -proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] = +proc allprefixedAux[T](c: CritBitTree[T], key: string; + longestMatch: bool): Node[T] = var p = c.root var top = p if p != nil: @@ -403,73 +401,80 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] = let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8 p = p.child[dir] if q.byte < key.len: top = p - for i in 0 ..< key.len: - if i >= p.key.len or p.key[i] != key[i]: return + if not longestMatch: + for i in 0 ..< key.len: + if i >= p.key.len or p.key[i] != key[i]: return result = top -iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string = +iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string; + longestMatch = false): string = ## Yields all keys starting with `prefix`. runnableExamples: - from std/sequtils import toSeq + from sequtils import toSeq let c = {"key1": 42, "key2": 43}.toCritBitTree doAssert toSeq(c.keysWithPrefix("key")) == @["key1", "key2"] - let top = allprefixedAux(c, prefix) + let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield x.key -iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): lent T = +iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string; + longestMatch = false): T = ## Yields all values of `c` starting with `prefix` of the ## corresponding keys. ## ## **See also:** ## * `mvaluesWithPrefix iterator <#mvaluesWithPrefix.i,CritBitTree[T],string>`_ runnableExamples: - from std/sequtils import toSeq + from sequtils import toSeq let c = {"key1": 42, "key2": 43}.toCritBitTree doAssert toSeq(c.valuesWithPrefix("key")) == @[42, 43] - let top = allprefixedAux(c, prefix) + let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield x.val -iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T = +iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string; + longestMatch = false): var T = ## Yields all values of `c` starting with `prefix` of the ## corresponding keys. The values can be modified. ## ## **See also:** ## * `valuesWithPrefix iterator <#valuesWithPrefix.i,CritBitTree[T],string>`_ - let top = allprefixedAux(c, prefix) + let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield x.val -iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string = +iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string; + longestMatch = false): string = ## Alias for `keysWithPrefix <#keysWithPrefix.i,CritBitTree[T],string>`_. - let top = allprefixedAux(c, prefix) + let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield x.key iterator pairsWithPrefix*[T](c: CritBitTree[T], - prefix: string): tuple[key: string, val: T] = + prefix: string; + longestMatch = false): tuple[key: string, val: T] = ## Yields all (key, value)-pairs of `c` starting with `prefix`. ## ## **See also:** ## * `mpairsWithPrefix iterator <#mpairsWithPrefix.i,CritBitTree[T],string>`_ runnableExamples: - from std/sequtils import toSeq + from sequtils import toSeq let c = {"key1": 42, "key2": 43}.toCritBitTree doAssert toSeq(c.pairsWithPrefix("key")) == @[(key: "key1", val: 42), (key: "key2", val: 43)] - let top = allprefixedAux(c, prefix) + let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield (x.key, x.val) iterator mpairsWithPrefix*[T](c: var CritBitTree[T], - prefix: string): tuple[key: string, val: var T] = + prefix: string; + longestMatch = false): tuple[key: string, val: var T] = ## Yields all (key, value)-pairs of `c` starting with `prefix`. ## The yielded values can be modified. ## ## **See also:** ## * `pairsWithPrefix iterator <#pairsWithPrefix.i,CritBitTree[T],string>`_ - let top = allprefixedAux(c, prefix) + let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield (x.key, x.val) func `$`*[T](c: CritBitTree[T]): string = @@ -521,7 +526,7 @@ func commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = else: c.root.byte else: 0 -proc toCritBitTree*[T](pairs: sink openArray[(string, T)]): CritBitTree[T] {.since: (1, 3).} = +func toCritBitTree*[T](pairs: 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] @@ -529,7 +534,7 @@ proc toCritBitTree*[T](pairs: sink openArray[(string, T)]): CritBitTree[T] {.sin for item in pairs: result.incl item[0], item[1] -proc toCritBitTree*(items: sink openArray[string]): CritBitTree[void] {.since: (1, 3).} = +func toCritBitTree*(items: 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/deques.nim b/lib/pure/collections/deques.nim index 60e0fd351..7614b9d20 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -10,9 +10,8 @@ ## An implementation of a `deque`:idx: (double-ended queue). ## The underlying implementation uses a `seq`. ## -## .. note:: None of the procs that get an individual value from the deque should be used -## on an empty deque. -## +## Note that none of the procs that get an individual value from the deque should be used +## on an empty deque. ## If compiled with the `boundChecks` option, those procs will raise an `IndexDefect` ## on such access. This should not be relied upon, as `-d:danger` or `--checks:off` will ## disable those checks and then the procs may return garbage or crash the program. @@ -47,6 +46,7 @@ runnableExamples: ## See also ## ======== ## * `lists module `_ for singly and doubly linked lists and rings +## * `channels module `_ for inter-thread communication import std/private/since @@ -86,6 +86,20 @@ proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] = ## * `toDeque proc <#toDeque,openArray[T]>`_ result.initImpl(initialSize) +proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} = + ## Creates a new deque that contains the elements of `x` (in the same order). + ## + ## **See also:** + ## * `initDeque proc <#initDeque,int>`_ + runnableExamples: + let a = toDeque([7, 8, 9]) + assert len(a) == 3 + assert $a == "[7, 8, 9]" + + result.initImpl(x.len) + for item in items(x): + result.addLast(item) + proc len*[T](deq: Deque[T]): int {.inline.} = ## Returns the number of elements of `deq`. result = deq.count @@ -106,7 +120,7 @@ template xBoundsCheck(deq, i) = raise newException(IndexDefect, "Out of bounds: " & $i & " < 0") -proc `[]`*[T](deq: Deque[T], i: Natural): lent T {.inline.} = +proc `[]`*[T](deq: Deque[T], i: Natural): T {.inline.} = ## Accesses the `i`-th element of `deq`. runnableExamples: let a = [10, 20, 30, 40, 50].toDeque @@ -128,7 +142,7 @@ proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = xBoundsCheck(deq, i) return deq.data[(deq.head + i) and deq.mask] -proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} = +proc `[]=`*[T](deq: var Deque[T], i: Natural, val: T) {.inline.} = ## Sets the `i`-th element of `deq` to `val`. runnableExamples: var a = [10, 20, 30, 40, 50].toDeque @@ -140,7 +154,7 @@ proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} = xBoundsCheck(deq, i) deq.data[(deq.head + i) and deq.mask] = val -proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): lent T {.inline.} = +proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} = ## Accesses the backwards indexed `i`-th element. ## ## `deq[^1]` is the last element. @@ -166,7 +180,7 @@ proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} = xBoundsCheck(deq, deq.len - int(i)) return deq[deq.len - int(i)] -proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: sink T) {.inline.} = +proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = ## Sets the backwards indexed `i`-th element of `deq` to `x`. ## ## `deq[^1]` is the last element. @@ -180,13 +194,13 @@ proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: sink T) {.inline.} = xBoundsCheck(deq, deq.len - int(i)) deq[deq.len - int(i)] = x -iterator items*[T](deq: Deque[T]): lent T = +iterator items*[T](deq: Deque[T]): T = ## Yields every element of `deq`. ## ## **See also:** - ## * `mitems iterator <#mitems.i,Deque[T]>`_ + ## * `mitems iterator <#mitems,Deque[T]>`_ runnableExamples: - from std/sequtils import toSeq + from sequtils import toSeq let a = [10, 20, 30, 40, 50].toDeque assert toSeq(a.items) == @[10, 20, 30, 40, 50] @@ -200,7 +214,7 @@ iterator mitems*[T](deq: var Deque[T]): var T = ## Yields every element of `deq`, which can be modified. ## ## **See also:** - ## * `items iterator <#items.i,Deque[T]>`_ + ## * `items iterator <#items,Deque[T]>`_ runnableExamples: var a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" @@ -216,7 +230,7 @@ iterator mitems*[T](deq: var Deque[T]): var T = iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] = ## Yields every `(position, value)`-pair of `deq`. runnableExamples: - from std/sequtils import toSeq + from sequtils import toSeq let a = [10, 20, 30].toDeque assert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)] @@ -248,7 +262,7 @@ proc expandIfNeeded[T](deq: var Deque[T]) = var n = newSeq[T](cap * 2) var i = 0 for x in mitems(deq): - when nimvm: n[i] = x # workaround for VM bug + when nimVM: n[i] = x # workaround for VM bug else: n[i] = move(x) inc i deq.data = move(n) @@ -256,11 +270,11 @@ proc expandIfNeeded[T](deq: var Deque[T]) = deq.tail = deq.count deq.head = 0 -proc addFirst*[T](deq: var Deque[T], item: sink T) = +proc addFirst*[T](deq: var Deque[T], item: T) = ## Adds an `item` to the beginning of `deq`. ## ## **See also:** - ## * `addLast proc <#addLast,Deque[T],sinkT>`_ + ## * `addLast proc <#addLast,Deque[T],T>`_ runnableExamples: var a = initDeque[int]() for i in 1 .. 5: @@ -272,11 +286,11 @@ proc addFirst*[T](deq: var Deque[T], item: sink T) = deq.head = (deq.head - 1) and deq.mask deq.data[deq.head] = item -proc addLast*[T](deq: var Deque[T], item: sink T) = +proc addLast*[T](deq: var Deque[T], item: T) = ## Adds an `item` to the end of `deq`. ## ## **See also:** - ## * `addFirst proc <#addFirst,Deque[T],sinkT>`_ + ## * `addFirst proc <#addFirst,Deque[T],T>`_ runnableExamples: var a = initDeque[int]() for i in 1 .. 5: @@ -288,21 +302,7 @@ proc addLast*[T](deq: var Deque[T], item: sink T) = deq.data[deq.tail] = item deq.tail = (deq.tail + 1) and deq.mask -proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} = - ## Creates a new deque that contains the elements of `x` (in the same order). - ## - ## **See also:** - ## * `initDeque proc <#initDeque,int>`_ - runnableExamples: - let a = toDeque([7, 8, 9]) - assert len(a) == 3 - assert $a == "[7, 8, 9]" - - result.initImpl(x.len) - for item in items(x): - result.addLast(item) - -proc peekFirst*[T](deq: Deque[T]): lent T {.inline.} = +proc peekFirst*[T](deq: Deque[T]): T {.inline.} = ## Returns the first element of `deq`, but does not remove it from the deque. ## ## **See also:** @@ -317,7 +317,7 @@ proc peekFirst*[T](deq: Deque[T]): lent T {.inline.} = emptyCheck(deq) result = deq.data[deq.head] -proc peekLast*[T](deq: Deque[T]): lent T {.inline.} = +proc peekLast*[T](deq: Deque[T]): T {.inline.} = ## Returns the last element of `deq`, but does not remove it from the deque. ## ## **See also:** @@ -379,7 +379,8 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = emptyCheck(deq) dec deq.count - result = move deq.data[deq.head] + result = deq.data[deq.head] + destroy(deq.data[deq.head]) deq.head = (deq.head + 1) and deq.mask proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = @@ -397,7 +398,8 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = emptyCheck(deq) dec deq.count deq.tail = (deq.tail - 1) and deq.mask - result = move deq.data[deq.tail] + result = deq.data[deq.tail] + destroy(deq.data[deq.tail]) proc clear*[T](deq: var Deque[T]) {.inline.} = ## Resets the deque so that it is empty. @@ -440,7 +442,7 @@ proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = deq.head = (deq.head + 1) and deq.mask for i in 0 ..< fromLast: - destroy(deq.data[(deq.tail - 1) and deq.mask]) + destroy(deq.data[deq.tail]) deq.tail = (deq.tail - 1) and deq.mask dec deq.count, fromFirst + fromLast diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim index 8fd4c6e08..336dbc07d 100644 --- a/lib/pure/collections/hashcommon.nim +++ b/lib/pure/collections/hashcommon.nim @@ -7,17 +7,17 @@ # distribution, for details about the copyright. # -# An `include` file which contains common code for +# An ``include`` file which contains common code for # hash sets and tables. -when defined(nimPreviewSlimSystem): - import std/assertions - -import std / outparams - const growthFactor = 2 +when not defined(nimHasDefault): + template default[T](t: typedesc[T]): T = + var v: T + v + # hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These # two procs retain clarity of that encoding without the space cost of an enum. proc isEmpty(hcode: Hash): bool {.inline.} = @@ -38,6 +38,16 @@ proc slotsNeeded(count: Natural): int {.inline.} = # Make sure to synchronize with `mustRehash` above result = nextPowerOfTwo(count * 3 div 2 + 4) +proc rightSize*(count: Natural): int {.inline, deprecated: "Deprecated since 1.4.0".} = + ## **Deprecated since Nim v1.4.0**, it is not needed anymore + ## because picking the correct size is done internally. + ## + ## Return the value of ``initialSize`` to support ``count`` items. + ## + ## If more items are expected to be added, simply add that + ## expected extra amount to the parameter before calling this. + result = count + template rawGetKnownHCImpl() {.dirty.} = if t.dataLen == 0: return -1 @@ -69,5 +79,5 @@ template rawGetImpl() {.dirty.} = genHashImpl(key, hc) rawGetKnownHCImpl() -proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} = +proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline.} = rawGetImpl() diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index 89e532951..30fa5dae8 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -8,27 +8,31 @@ ## The `heapqueue` module implements a -## `binary heap data structure`_ -## that can be used as a `priority queue`_. -## They are represented as arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]` -## for all indices `k` (counting elements from 0). The interesting property of a heap is that +## `heap data structure`_ +## that can be used as a +## `priority queue`_. +## Heaps are arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]` for +## all `k`, counting elements from 0. The interesting property of a heap is that ## `a[0]` is always its smallest element. ## ## Basic usage ## ----------- ## + runnableExamples: - var heap = [8, 2].toHeapQueue + var heap = initHeapQueue[int]() + heap.push(8) + heap.push(2) heap.push(5) - # the first element is the lowest element + # The first element is the lowest element assert heap[0] == 2 - # remove and return the lowest element + # Remove and return the lowest element assert heap.pop() == 2 - # the lowest element remaining is 5 + # The lowest element remaining is 5 assert heap[0] == 5 -## Usage with custom objects -## ------------------------- +## Usage with custom object +## ------------------------ ## To use a `HeapQueue` with a custom object, the `<` operator must be ## implemented. @@ -44,7 +48,6 @@ runnableExamples: assert jobs[0].priority == 1 - import std/private/since type HeapQueue*[T] = object @@ -54,33 +57,27 @@ type HeapQueue*[T] = object proc initHeapQueue*[T](): HeapQueue[T] = ## Creates a new empty heap. ## - ## Heaps are initialized by default, so it is not necessary to call - ## this function explicitly. - ## - ## **See also:** + ## See also: ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_ discard proc len*[T](heap: HeapQueue[T]): int {.inline.} = ## Returns the number of elements of `heap`. - runnableExamples: - let heap = [9, 5, 8].toHeapQueue - assert heap.len == 3 - heap.data.len proc `[]`*[T](heap: HeapQueue[T], i: Natural): lent T {.inline.} = ## Accesses the i-th element of `heap`. heap.data[i] -proc heapCmp[T](x, y: T): bool {.inline.} = x < y +proc heapCmp[T](x, y: T): bool {.inline.} = + return (x < y) -proc siftup[T](heap: var HeapQueue[T], startpos, p: int) = - ## `heap` is a heap at all indices >= `startpos`, except possibly for `p`. `p` +proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) = + ## 'heap' is a heap at all indices >= startpos, except possibly for `pos`. `pos` ## is the index of a leaf with a possibly out-of-order value. Restores the ## heap invariant. var pos = p - let newitem = heap[pos] + var newitem = heap[pos] # Follow the path to the root, moving parents down until finding a place # newitem fits. while pos > startpos: @@ -93,14 +90,13 @@ proc siftup[T](heap: var HeapQueue[T], startpos, p: int) = break heap.data[pos] = newitem -proc siftdownToBottom[T](heap: var HeapQueue[T], p: int) = - # This is faster when the element should be close to the bottom. +proc siftup[T](heap: var HeapQueue[T], p: int) = let endpos = len(heap) var pos = p let startpos = pos let newitem = heap[pos] # Bubble up the smaller child until hitting a leaf. - var childpos = 2 * pos + 1 # leftmost child position + var childpos = 2*pos + 1 # leftmost child position while childpos < endpos: # Set childpos to index of smaller child. let rightpos = childpos + 1 @@ -109,71 +105,52 @@ proc siftdownToBottom[T](heap: var HeapQueue[T], p: int) = # Move the smaller child up. heap.data[pos] = heap[childpos] pos = childpos - childpos = 2 * pos + 1 - # The leaf at pos is empty now. Put newitem there, and bubble it up + childpos = 2*pos + 1 + # The leaf at pos is empty now. Put newitem there, and bubble it up # to its final resting place (by sifting its parents down). heap.data[pos] = newitem - siftup(heap, startpos, pos) - -proc siftdown[T](heap: var HeapQueue[T], p: int) = - let endpos = len(heap) - var pos = p - let newitem = heap[pos] - var childpos = 2 * pos + 1 - while childpos < endpos: - let rightpos = childpos + 1 - if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]): - childpos = rightpos - if not heapCmp(heap[childpos], newitem): - break - heap.data[pos] = heap[childpos] - pos = childpos - childpos = 2 * pos + 1 - heap.data[pos] = newitem + siftdown(heap, startpos, pos) proc push*[T](heap: var HeapQueue[T], item: sink T) = - ## Pushes `item` onto `heap`, maintaining the heap invariant. + ## Pushes `item` onto heap, maintaining the heap invariant. heap.data.add(item) - siftup(heap, 0, len(heap) - 1) + siftdown(heap, 0, len(heap)-1) proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} = ## Creates a new HeapQueue that contains the elements of `x`. ## - ## **See also:** + ## See also: ## * `initHeapQueue proc <#initHeapQueue>`_ runnableExamples: - var heap = [9, 5, 8].toHeapQueue + var heap = toHeapQueue([9, 5, 8]) assert heap.pop() == 5 assert heap[0] == 8 - # see https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap - result.data = @x - for i in countdown(x.len div 2 - 1, 0): - siftdown(result, i) + result = initHeapQueue[T]() + for item in items(x): + result.push(item) proc pop*[T](heap: var HeapQueue[T]): T = ## Pops and returns the smallest item from `heap`, ## maintaining the heap invariant. runnableExamples: - var heap = [9, 5, 8].toHeapQueue + var heap = toHeapQueue([9, 5, 8]) assert heap.pop() == 5 - let lastelt = heap.data.pop() if heap.len > 0: result = heap[0] heap.data[0] = lastelt - siftdownToBottom(heap, 0) + siftup(heap, 0) else: result = lastelt proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} = - ## Linear scan to find the index of the item `x` or -1 if not found. + ## Linear scan to find index of item ``x`` or -1 if not found. runnableExamples: - let heap = [9, 5, 8].toHeapQueue + var heap = toHeapQueue([9, 5, 8]) assert heap.find(5) == 0 assert heap.find(9) == 1 assert heap.find(777) == -1 - result = -1 for i in 0 ..< heap.len: if heap[i] == x: return i @@ -181,69 +158,65 @@ proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} = proc del*[T](heap: var HeapQueue[T], index: Natural) = ## Removes the element at `index` from `heap`, maintaining the heap invariant. runnableExamples: - var heap = [9, 5, 8].toHeapQueue + var heap = toHeapQueue([9, 5, 8]) heap.del(1) assert heap[0] == 5 assert heap[1] == 8 - swap(heap.data[^1], heap.data[index]) let newLen = heap.len - 1 heap.data.setLen(newLen) if index < newLen: - siftdownToBottom(heap, index) + heap.siftup(index) proc replace*[T](heap: var HeapQueue[T], item: sink T): T = ## Pops and returns the current smallest value, and add the new item. - ## This is more efficient than `pop()` followed by `push()`, and can be + ## This is more efficient than pop() followed by push(), and can be ## more appropriate when using a fixed-size heap. Note that the value - ## returned may be larger than `item`! That constrains reasonable uses of - ## this routine unless written as part of a conditional replacement. - ## - ## **See also:** - ## * `pushpop proc <#pushpop,HeapQueue[T],sinkT>`_ + ## returned may be larger than item! That constrains reasonable uses of + ## this routine unless written as part of a conditional replacement: runnableExamples: - var heap = [5, 12].toHeapQueue + var heap = initHeapQueue[int]() + heap.push(5) + heap.push(12) assert heap.replace(6) == 5 assert heap.len == 2 assert heap[0] == 6 assert heap.replace(4) == 6 - result = heap[0] heap.data[0] = item - siftdown(heap, 0) + siftup(heap, 0) proc pushpop*[T](heap: var HeapQueue[T], item: sink T): T = - ## Fast version of a `push()` followed by a `pop()`. - ## - ## **See also:** - ## * `replace proc <#replace,HeapQueue[T],sinkT>`_ + ## Fast version of a push followed by a pop. runnableExamples: - var heap = [5, 12].toHeapQueue + var heap = initHeapQueue[int]() + heap.push(5) + heap.push(12) assert heap.pushpop(6) == 5 assert heap.len == 2 assert heap[0] == 6 assert heap.pushpop(4) == 4 - result = item if heap.len > 0 and heapCmp(heap.data[0], result): swap(result, heap.data[0]) - siftdown(heap, 0) + siftup(heap, 0) proc clear*[T](heap: var HeapQueue[T]) = ## Removes all elements from `heap`, making it empty. runnableExamples: - var heap = [9, 5, 8].toHeapQueue + var heap = initHeapQueue[int]() + heap.push(1) heap.clear() assert heap.len == 0 - heap.data.setLen(0) proc `$`*[T](heap: HeapQueue[T]): string = ## Turns a heap into its string representation. runnableExamples: - let heap = [1, 2].toHeapQueue + var heap = initHeapQueue[int]() + heap.push(1) + heap.push(2) assert $heap == "[1, 2]" - result = "[" for x in heap.data: if result.len > 1: result.add(", ") diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index 765a23e97..e4c216d08 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -7,8 +7,9 @@ # distribution, for details about the copyright. # -## Specialization of the generic `packedsets module `_ -## (see its documentation for more examples) for ordinal sparse sets. +## Deprecated by the generic `PackedSet` for ordinal sparse sets. +## **See also:** +## * `Ordinal packed sets module `_ for more general packed sets import std/private/since import std/packedsets diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index a08c62964..8af0ed826 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -13,90 +13,120 @@ ## * `singly linked rings <#SinglyLinkedRing>`_ (circular lists) ## * `doubly linked rings <#DoublyLinkedRing>`_ (circular lists) ## -## # Basic Usage +## +## Basic Usage +## =========== +## ## Because it makes no sense to do otherwise, the `next` and `prev` pointers ## are not hidden from you and can be manipulated directly for efficiency. ## -## ## Lists -runnableExamples: - var list = initDoublyLinkedList[int]() - let - a = newDoublyLinkedNode[int](3) - b = newDoublyLinkedNode[int](7) - c = newDoublyLinkedNode[int](9) - - list.add(a) - list.add(b) - list.prepend(c) - - assert a.next == b - assert a.prev == c - assert c.next == a - assert c.next.next == b - assert c.prev == nil - assert b.next == nil - -## ## Rings -runnableExamples: - var ring = initSinglyLinkedRing[int]() - let - a = newSinglyLinkedNode[int](3) - b = newSinglyLinkedNode[int](7) - c = newSinglyLinkedNode[int](9) - - ring.add(a) - ring.add(b) - ring.prepend(c) - - assert c.next == a - assert a.next == b - assert c.next.next == b - assert b.next == c - assert c.next.next.next == c - -## # See also +## Lists +## ----- +## +## .. code-block:: +## import lists +## +## var +## l = initDoublyLinkedList[int]() +## a = newDoublyLinkedNode[int](3) +## b = newDoublyLinkedNode[int](7) +## c = newDoublyLinkedNode[int](9) +## +## l.append(a) +## l.append(b) +## l.prepend(c) +## +## assert a.next == b +## assert a.prev == c +## assert c.next == a +## assert c.next.next == b +## assert c.prev == nil +## assert b.next == nil +## +## +## Rings +## ----- +## +## .. code-block:: +## import lists +## +## var +## l = initSinglyLinkedRing[int]() +## a = newSinglyLinkedNode[int](3) +## b = newSinglyLinkedNode[int](7) +## c = newSinglyLinkedNode[int](9) +## +## l.append(a) +## l.append(b) +## l.prepend(c) +## +## assert c.next == a +## assert a.next == b +## assert c.next.next == b +## assert b.next == c +## assert c.next.next.next == c +## +## See also +## ======== +## ## * `deques module `_ for double-ended queues +## * `sharedlist module `_ for shared singly-linked lists import std/private/since -when defined(nimPreviewSlimSystem): - import std/assertions +when not defined(nimhygiene): + {.pragma: dirty.} + +when not defined(nimHasCursor): + {.pragma: cursor.} type - DoublyLinkedNodeObj*[T] = object - ## A node of a doubly linked list. + DoublyLinkedNodeObj*[T] = object ## \ + ## A node a doubly linked list consists of. ## ## It consists of a `value` field, and pointers to `next` and `prev`. - next*: DoublyLinkedNode[T] - prev* {.cursor.}: DoublyLinkedNode[T] + next*: (ref DoublyLinkedNodeObj[T]) + prev* {.cursor.}: ref DoublyLinkedNodeObj[T] value*: T DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T] - SinglyLinkedNodeObj*[T] = object - ## A node of a singly linked list. + SinglyLinkedNodeObj*[T] = object ## \ + ## A node a singly linked list consists of. ## ## It consists of a `value` field, and a pointer to `next`. - next*: SinglyLinkedNode[T] + next*: (ref SinglyLinkedNodeObj[T]) value*: T SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T] - SinglyLinkedList*[T] = object + SinglyLinkedList*[T] = object ## \ ## A singly linked list. - head*: SinglyLinkedNode[T] + ## + ## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create + ## a new empty list. + head*: (SinglyLinkedNode[T]) tail* {.cursor.}: SinglyLinkedNode[T] - DoublyLinkedList*[T] = object + DoublyLinkedList*[T] = object ## \ ## A doubly linked list. - head*: DoublyLinkedNode[T] + ## + ## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create + ## a new empty list. + head*: (DoublyLinkedNode[T]) tail* {.cursor.}: DoublyLinkedNode[T] - SinglyLinkedRing*[T] = object + SinglyLinkedRing*[T] = object ## \ ## A singly linked ring. - head*: SinglyLinkedNode[T] + ## + ## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create + ## a new empty ring. + head*: (SinglyLinkedNode[T]) tail* {.cursor.}: SinglyLinkedNode[T] - DoublyLinkedRing*[T] = object + DoublyLinkedRing*[T] = object ## \ ## A doubly linked ring. + ## + ## Use `initDoublyLinkedRing proc <#initDoublyLinkedRing>`_ to create + ## a new empty ring. head*: DoublyLinkedNode[T] SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T] @@ -109,83 +139,65 @@ type proc initSinglyLinkedList*[T](): SinglyLinkedList[T] = ## Creates a new singly linked list that is empty. - ## - ## Singly linked lists are initialized by default, so it is not necessary to - ## call this function explicitly. runnableExamples: - let a = initSinglyLinkedList[int]() - + var a = initSinglyLinkedList[int]() discard proc initDoublyLinkedList*[T](): DoublyLinkedList[T] = ## Creates a new doubly linked list that is empty. - ## - ## Doubly linked lists are initialized by default, so it is not necessary to - ## call this function explicitly. runnableExamples: - let a = initDoublyLinkedList[int]() - + var a = initDoublyLinkedList[int]() discard proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] = ## Creates a new singly linked ring that is empty. - ## - ## Singly linked rings are initialized by default, so it is not necessary to - ## call this function explicitly. runnableExamples: - let a = initSinglyLinkedRing[int]() - + var a = initSinglyLinkedRing[int]() discard proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] = ## Creates a new doubly linked ring that is empty. - ## - ## Doubly linked rings are initialized by default, so it is not necessary to - ## call this function explicitly. runnableExamples: - let a = initDoublyLinkedRing[int]() - + var a = initDoublyLinkedRing[int]() discard -proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] = +proc newDoublyLinkedNode*[T](value: T): (DoublyLinkedNode[T]) = ## Creates a new doubly linked node with the given `value`. runnableExamples: - let n = newDoublyLinkedNode[int](5) + var n = newDoublyLinkedNode[int](5) assert n.value == 5 new(result) result.value = value -proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] = +proc newSinglyLinkedNode*[T](value: T): (SinglyLinkedNode[T]) = ## Creates a new singly linked node with the given `value`. runnableExamples: - let n = newSinglyLinkedNode[int](5) + var n = newSinglyLinkedNode[int](5) assert n.value == 5 new(result) result.value = value func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} = - ## Creates a new `SinglyLinkedList` from the members of `elems`. + ## Creates a new `SinglyLinkedList` from members of `elems`. runnableExamples: - from std/sequtils import toSeq + import sequtils let a = [1, 2, 3, 4, 5].toSinglyLinkedList assert a.toSeq == [1, 2, 3, 4, 5] - result = initSinglyLinkedList[T]() for elem in elems.items: - result.add(elem) + result.append(elem) func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = - ## Creates a new `DoublyLinkedList` from the members of `elems`. + ## Creates a new `DoublyLinkedList` from members of `elems`. runnableExamples: - from std/sequtils import toSeq + import sequtils let a = [1, 2, 3, 4, 5].toDoublyLinkedList assert a.toSeq == [1, 2, 3, 4, 5] - result = initDoublyLinkedList[T]() for elem in elems.items: - result.add(elem) + result.append(elem) template itemsListImpl() {.dirty.} = var it = L.head @@ -204,91 +216,101 @@ template itemsRingImpl() {.dirty.} = iterator items*[T](L: SomeLinkedList[T]): T = ## Yields every value of `L`. ## - ## **See also:** + ## See also: ## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_ ## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_ - runnableExamples: - from std/sugar import collect - from std/sequtils import toSeq - let a = collect(initSinglyLinkedList): - for i in 1..3: 10 * i - assert toSeq(items(a)) == toSeq(a) - assert toSeq(a) == @[10, 20, 30] - + ## + ## **Examples:** + ## + ## .. code-block:: + ## var a = initSinglyLinkedList[int]() + ## for i in 1 .. 3: + ## a.append(10*i) + ## + ## for x in a: # the same as: for x in items(a): + ## echo x + ## + ## # 10 + ## # 20 + ## # 30 itemsListImpl() iterator items*[T](L: SomeLinkedRing[T]): T = ## Yields every value of `L`. ## - ## **See also:** + ## See also: ## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_ ## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_ - runnableExamples: - from std/sugar import collect - from std/sequtils import toSeq - let a = collect(initSinglyLinkedRing): - for i in 1..3: 10 * i - assert toSeq(items(a)) == toSeq(a) - assert toSeq(a) == @[10, 20, 30] - + ## + ## **Examples:** + ## + ## .. code-block:: + ## var a = initSinglyLinkedRing[int]() + ## for i in 1 .. 3: + ## a.append(10*i) + ## + ## for x in a: # the same as: for x in items(a): + ## echo x + ## + ## # 10 + ## # 20 + ## # 30 itemsRingImpl() iterator mitems*[T](L: var SomeLinkedList[T]): var T = ## Yields every value of `L` so that you can modify it. ## - ## **See also:** + ## See also: ## * `items iterator <#items.i,SomeLinkedList[T]>`_ ## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_ runnableExamples: var a = initSinglyLinkedList[int]() - for i in 1..5: - a.add(10 * i) + for i in 1 .. 5: + a.append(10*i) assert $a == "[10, 20, 30, 40, 50]" for x in mitems(a): - x = 5 * x - 1 + x = 5*x - 1 assert $a == "[49, 99, 149, 199, 249]" - itemsListImpl() iterator mitems*[T](L: var SomeLinkedRing[T]): var T = ## Yields every value of `L` so that you can modify it. ## - ## **See also:** + ## See also: ## * `items iterator <#items.i,SomeLinkedRing[T]>`_ ## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_ runnableExamples: var a = initSinglyLinkedRing[int]() - for i in 1..5: - a.add(10 * i) + for i in 1 .. 5: + a.append(10*i) assert $a == "[10, 20, 30, 40, 50]" for x in mitems(a): - x = 5 * x - 1 + x = 5*x - 1 assert $a == "[49, 99, 149, 199, 249]" - itemsRingImpl() iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] = ## Iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. ## - ## **See also:** + ## See also: ## * `items iterator <#items.i,SomeLinkedList[T]>`_ ## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_ runnableExamples: var a = initDoublyLinkedList[int]() - for i in 1..5: - a.add(10 * i) + for i in 1 .. 5: + a.append(10*i) assert $a == "[10, 20, 30, 40, 50]" for x in nodes(a): if x.value == 30: a.remove(x) else: - x.value = 5 * x.value - 1 + x.value = 5*x.value - 1 assert $a == "[49, 99, 199, 249]" var it = L.head while it != nil: - let nxt = it.next + var nxt = it.next yield it it = nxt @@ -296,35 +318,31 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] = ## Iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. ## - ## **See also:** + ## See also: ## * `items iterator <#items.i,SomeLinkedRing[T]>`_ ## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_ runnableExamples: var a = initDoublyLinkedRing[int]() - for i in 1..5: - a.add(10 * i) + for i in 1 .. 5: + a.append(10*i) assert $a == "[10, 20, 30, 40, 50]" for x in nodes(a): if x.value == 30: a.remove(x) else: - x.value = 5 * x.value - 1 + x.value = 5*x.value - 1 assert $a == "[49, 99, 199, 249]" var it = L.head if it != nil: while true: - let nxt = it.next + var nxt = it.next yield it it = nxt if it == L.head: break proc `$`*[T](L: SomeLinkedCollection[T]): string = ## Turns a list into its string representation for logging and printing. - runnableExamples: - let a = [1, 2, 3, 4].toSinglyLinkedList - assert $a == "[1, 2, 3, 4]" - result = "[" for x in nodes(L): if result.len > 1: result.add(", ") @@ -335,10 +353,12 @@ proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = ## Searches in the list for a value. Returns `nil` if the value does not ## exist. ## - ## **See also:** + ## See also: ## * `contains proc <#contains,SomeLinkedCollection[T],T>`_ runnableExamples: - let a = [9, 8].toSinglyLinkedList + var a = initSinglyLinkedList[int]() + a.append(9) + a.append(8) assert a.find(9).value == 9 assert a.find(1) == nil @@ -347,13 +367,14 @@ proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} = ## Searches in the list for a value. Returns `false` if the value does not - ## exist, `true` otherwise. This allows the usage of the `in` and `notin` - ## operators. + ## exist, `true` otherwise. ## - ## **See also:** + ## See also: ## * `find proc <#find,SomeLinkedCollection[T],T>`_ runnableExamples: - let a = [9, 8].toSinglyLinkedList + var a = initSinglyLinkedList[int]() + a.append(9) + a.append(8) assert a.contains(9) assert 8 in a assert(not a.contains(1)) @@ -361,67 +382,20 @@ proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} = result = find(L, value) != nil -proc prepend*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} = - ## Prepends a shallow copy of `b` to the beginning of `a`. - ## - ## **See also:** - ## * `prependMoved proc <#prependMoved,T,T>`_ - ## for moving the second list instead of copying - runnableExamples: - from std/sequtils import toSeq - var a = [4, 5].toSinglyLinkedList - let b = [1, 2, 3].toSinglyLinkedList - a.prepend(b) - assert a.toSeq == [1, 2, 3, 4, 5] - assert b.toSeq == [1, 2, 3] - a.prepend(a) - assert a.toSeq == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5] - - var tmp = b.copy - tmp.addMoved(a) - a = tmp - -proc prependMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} = - ## Moves `b` before the head of `a`. Efficiency: O(1). - ## Note that `b` becomes empty after the operation unless it has the same address as `a`. - ## Self-prepending results in a cycle. - ## - ## **See also:** - ## * `prepend proc <#prepend,T,T>`_ - ## for prepending a copy of a list - runnableExamples: - import std/[sequtils, enumerate, sugar] - var - a = [4, 5].toSinglyLinkedList - b = [1, 2, 3].toSinglyLinkedList - c = [0, 1].toSinglyLinkedList - a.prependMoved(b) - assert a.toSeq == [1, 2, 3, 4, 5] - assert b.toSeq == [] - c.prependMoved(c) - let s = collect: - for i, ci in enumerate(c): - if i == 6: break - ci - assert s == [0, 1, 0, 1, 0, 1] - - b.addMoved(a) - when defined(js): # XXX: swap broken in js; bug #16771 - (b, a) = (a, b) - else: swap a, b - -proc add*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = +proc append*[T](L: var SinglyLinkedList[T], + n: SinglyLinkedNode[T]) {.inline.} = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value + ## See also: + ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value runnableExamples: - var a = initSinglyLinkedList[int]() - let n = newSinglyLinkedNode[int](9) - a.add(n) + var + a = initSinglyLinkedList[int]() + n = newSinglyLinkedNode[int](9) + a.append(n) assert a.contains(9) n.next = nil @@ -431,34 +405,34 @@ proc add*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = L.tail = n if L.head == nil: L.head = n -proc add*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = +proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value + ## See also: + ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value runnableExamples: var a = initSinglyLinkedList[int]() - a.add(9) - a.add(8) + a.append(9) + a.append(8) assert a.contains(9) - - add(L, newSinglyLinkedNode(value)) + append(L, newSinglyLinkedNode(value)) proc prepend*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for appending a node - ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value + ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value runnableExamples: - var a = initSinglyLinkedList[int]() - let n = newSinglyLinkedNode[int](9) + var + a = initSinglyLinkedList[int]() + n = newSinglyLinkedNode[int](9) a.prepend(n) assert a.contains(9) @@ -469,10 +443,10 @@ proc prepend*[T](L: var SinglyLinkedList[T], proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for appending a node - ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value + ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for prepending a node runnableExamples: @@ -480,20 +454,19 @@ proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = a.prepend(9) a.prepend(8) assert a.contains(9) - prepend(L, newSinglyLinkedNode(value)) func copy*[T](a: SinglyLinkedList[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} = ## Creates a shallow copy of `a`. runnableExamples: - from std/sequtils import toSeq + import sequtils type Foo = ref object x: int var f = Foo(x: 1) a = [f].toSinglyLinkedList let b = a.copy - a.add([f].toSinglyLinkedList) + a.add [f].toSinglyLinkedList assert a.toSeq == [f, f] assert b.toSeq == [f] # b isn't modified... f.x = 42 @@ -502,58 +475,57 @@ func copy*[T](a: SinglyLinkedList[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} let c = [1, 2, 3].toSinglyLinkedList assert $c == $c.copy - result = initSinglyLinkedList[T]() for x in a.items: - result.add(x) + result.append(x) proc addMoved*[T](a, b: var SinglyLinkedList[T]) {.since: (1, 5, 1).} = ## Moves `b` to the end of `a`. Efficiency: O(1). ## Note that `b` becomes empty after the operation unless it has the same address as `a`. ## Self-adding results in a cycle. ## - ## **See also:** - ## * `add proc <#add,T,T>`_ for adding a copy of a list + ## See also: + ## * `add proc <#add,T,T>`_ + ## for adding a copy of a list runnableExamples: - import std/[sequtils, enumerate, sugar] + import sequtils, std/enumerate, std/sugar var a = [1, 2, 3].toSinglyLinkedList b = [4, 5].toSinglyLinkedList c = [0, 1].toSinglyLinkedList - a.addMoved(b) + a.addMoved b assert a.toSeq == [1, 2, 3, 4, 5] assert b.toSeq == [] - c.addMoved(c) + c.addMoved c let s = collect: for i, ci in enumerate(c): if i == 6: break ci assert s == [0, 1, 0, 1, 0, 1] - - if b.head != nil: - if a.head == nil: - a.head = b.head - else: - a.tail.next = b.head - a.tail = b.tail + if a.tail != nil: + a.tail.next = b.head + a.tail = b.tail + if a.head == nil: + a.head = b.head if a.addr != b.addr: b.head = nil b.tail = nil -proc add*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = +proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value + ## See also: + ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for removing a node runnableExamples: - var a = initDoublyLinkedList[int]() - let n = newDoublyLinkedNode[int](9) - a.add(n) + var + a = initDoublyLinkedList[int]() + n = newDoublyLinkedNode[int](9) + a.append(n) assert a.contains(9) n.next = nil @@ -564,11 +536,11 @@ proc add*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = L.tail = n if L.head == nil: L.head = n -proc add*[T](L: var DoublyLinkedList[T], value: T) = +proc append*[T](L: var DoublyLinkedList[T], value: T) = ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for appending a node ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for prepending a node @@ -577,25 +549,25 @@ proc add*[T](L: var DoublyLinkedList[T], value: T) = ## for removing a node runnableExamples: var a = initDoublyLinkedList[int]() - a.add(9) - a.add(8) + a.append(9) + a.append(8) assert a.contains(9) - - add(L, newDoublyLinkedNode(value)) + append(L, newDoublyLinkedNode(value)) proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for appending a node - ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value + ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for removing a node runnableExamples: - var a = initDoublyLinkedList[int]() - let n = newDoublyLinkedNode[int](9) + var + a = initDoublyLinkedList[int]() + n = newDoublyLinkedNode[int](9) a.prepend(n) assert a.contains(9) @@ -610,10 +582,10 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = proc prepend*[T](L: var DoublyLinkedList[T], value: T) = ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for appending a node - ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value + ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for prepending a node ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ @@ -623,64 +595,57 @@ proc prepend*[T](L: var DoublyLinkedList[T], value: T) = a.prepend(9) a.prepend(8) assert a.contains(9) - prepend(L, newDoublyLinkedNode(value)) func copy*[T](a: DoublyLinkedList[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = ## Creates a shallow copy of `a`. runnableExamples: - from std/sequtils import toSeq type Foo = ref object x: int - var - f = Foo(x: 1) + var f = Foo(x: 1) + let a = [f].toDoublyLinkedList - let b = a.copy - a.add([f].toDoublyLinkedList) - assert a.toSeq == [f, f] - assert b.toSeq == [f] # b isn't modified... + b = a.copy f.x = 42 assert a.head.value.x == 42 - assert b.head.value.x == 42 # ... but the elements are not deep copied + assert b.head.value.x == 42 let c = [1, 2, 3].toDoublyLinkedList assert $c == $c.copy - result = initDoublyLinkedList[T]() for x in a.items: - result.add(x) + result.append(x) proc addMoved*[T](a, b: var DoublyLinkedList[T]) {.since: (1, 5, 1).} = ## Moves `b` to the end of `a`. Efficiency: O(1). ## Note that `b` becomes empty after the operation unless it has the same address as `a`. ## Self-adding results in a cycle. ## - ## **See also:** + ## See also: ## * `add proc <#add,T,T>`_ ## for adding a copy of a list runnableExamples: - import std/[sequtils, enumerate, sugar] + import sequtils, std/enumerate, std/sugar var a = [1, 2, 3].toDoublyLinkedList b = [4, 5].toDoublyLinkedList c = [0, 1].toDoublyLinkedList - a.addMoved(b) + a.addMoved b assert a.toSeq == [1, 2, 3, 4, 5] assert b.toSeq == [] - c.addMoved(c) + c.addMoved c let s = collect: for i, ci in enumerate(c): if i == 6: break ci assert s == [0, 1, 0, 1, 0, 1] - if b.head != nil: - if a.head == nil: - a.head = b.head - else: - b.head.prev = a.tail - a.tail.next = b.head - a.tail = b.tail + b.head.prev = a.tail + if a.tail != nil: + a.tail.next = b.head + a.tail = b.tail + if a.head == nil: + a.head = b.head if a.addr != b.addr: b.head = nil b.tail = nil @@ -688,82 +653,32 @@ proc addMoved*[T](a, b: var DoublyLinkedList[T]) {.since: (1, 5, 1).} = proc add*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} = ## Appends a shallow copy of `b` to the end of `a`. ## - ## **See also:** + ## See also: ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_ ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_ ## for moving the second list instead of copying runnableExamples: - from std/sequtils import toSeq + import sequtils var a = [1, 2, 3].toSinglyLinkedList let b = [4, 5].toSinglyLinkedList - a.add(b) + a.add b assert a.toSeq == [1, 2, 3, 4, 5] assert b.toSeq == [4, 5] - a.add(a) + a.add a assert a.toSeq == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5] - var tmp = b.copy - a.addMoved(tmp) - -proc remove*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.discardable.} = - ## Removes a node `n` from `L`. - ## Returns `true` if `n` was found in `L`. - ## Efficiency: O(n); the list is traversed until `n` is found. - ## Attempting to remove an element not contained in the list is a no-op. - ## When the list is cyclic, the cycle is preserved after removal. - runnableExamples: - import std/[sequtils, enumerate, sugar] - var a = [0, 1, 2].toSinglyLinkedList - let n = a.head.next - assert n.value == 1 - assert a.remove(n) == true - assert a.toSeq == [0, 2] - assert a.remove(n) == false - assert a.toSeq == [0, 2] - a.addMoved(a) # cycle: [0, 2, 0, 2, ...] - a.remove(a.head) - let s = collect: - for i, ai in enumerate(a): - if i == 4: break - ai - assert s == [2, 2, 2, 2] - - if n == L.head: - L.head = n.next - if L.tail.next == n: - L.tail.next = L.head # restore cycle - else: - var prev = L.head - while prev.next != n and prev.next != nil: - prev = prev.next - if prev.next == nil: - return false - prev.next = n.next - if L.tail == n: - L.tail = prev # update tail if we removed the last node - true + a.addMoved tmp proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## Removes a node `n` from `L`. Efficiency: O(1). - ## This function assumes, for the sake of efficiency, that `n` is contained in `L`, - ## otherwise the effects are undefined. - ## When the list is cyclic, the cycle is preserved after removal. runnableExamples: - import std/[sequtils, enumerate, sugar] - var a = [0, 1, 2].toSinglyLinkedList - let n = a.head.next - assert n.value == 1 + var + a = initDoublyLinkedList[int]() + n = newDoublyLinkedNode[int](5) + a.append(n) + assert 5 in a a.remove(n) - assert a.toSeq == [0, 2] - a.remove(n) - assert a.toSeq == [0, 2] - a.addMoved(a) # cycle: [0, 2, 0, 2, ...] - a.remove(a.head) - let s = collect: - for i, ai in enumerate(a): - if i == 4: break - ai - assert s == [2, 2, 2, 2] + assert 5 notin a if n == L.tail: L.tail = n.prev if n == L.head: L.head = n.next @@ -772,57 +687,59 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = -proc add*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = +proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value + ## See also: + ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value runnableExamples: - var a = initSinglyLinkedRing[int]() - let n = newSinglyLinkedNode[int](9) - a.add(n) + var + a = initSinglyLinkedRing[int]() + n = newSinglyLinkedNode[int](9) + a.append(n) assert a.contains(9) if L.head != nil: n.next = L.head assert(L.tail != nil) L.tail.next = n + L.tail = n else: n.next = n L.head = n - L.tail = n + L.tail = n -proc add*[T](L: var SinglyLinkedRing[T], value: T) = +proc append*[T](L: var SinglyLinkedRing[T], value: T) = ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for appending a node ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value runnableExamples: var a = initSinglyLinkedRing[int]() - a.add(9) - a.add(8) + a.append(9) + a.append(8) assert a.contains(9) - - add(L, newSinglyLinkedNode(value)) + append(L, newSinglyLinkedNode(value)) proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for appending a node - ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value + ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value runnableExamples: - var a = initSinglyLinkedRing[int]() - let n = newSinglyLinkedNode[int](9) + var + a = initSinglyLinkedRing[int]() + n = newSinglyLinkedNode[int](9) a.prepend(n) assert a.contains(9) @@ -838,10 +755,10 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = proc prepend*[T](L: var SinglyLinkedRing[T], value: T) = ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for appending a node - ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value + ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for prepending a node runnableExamples: @@ -849,25 +766,25 @@ proc prepend*[T](L: var SinglyLinkedRing[T], value: T) = a.prepend(9) a.prepend(8) assert a.contains(9) - prepend(L, newSinglyLinkedNode(value)) -proc add*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = +proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value + ## See also: + ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for removing a node runnableExamples: - var a = initDoublyLinkedRing[int]() - let n = newDoublyLinkedNode[int](9) - a.add(n) + var + a = initDoublyLinkedRing[int]() + n = newDoublyLinkedNode[int](9) + a.append(n) assert a.contains(9) if L.head != nil: @@ -880,11 +797,11 @@ proc add*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = n.next = n L.head = n -proc add*[T](L: var DoublyLinkedRing[T], value: T) = +proc append*[T](L: var DoublyLinkedRing[T], value: T) = ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for appending a node ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for prepending a node @@ -893,25 +810,25 @@ proc add*[T](L: var DoublyLinkedRing[T], value: T) = ## for removing a node runnableExamples: var a = initDoublyLinkedRing[int]() - a.add(9) - a.add(8) + a.append(9) + a.append(8) assert a.contains(9) - - add(L, newDoublyLinkedNode(value)) + append(L, newDoublyLinkedNode(value)) proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for appending a node - ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value + ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for removing a node runnableExamples: - var a = initDoublyLinkedRing[int]() - let n = newDoublyLinkedNode[int](9) + var + a = initDoublyLinkedRing[int]() + n = newDoublyLinkedNode[int](9) a.prepend(n) assert a.contains(9) @@ -928,10 +845,10 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = proc prepend*[T](L: var DoublyLinkedRing[T], value: T) = ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1). ## - ## **See also:** - ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ + ## See also: + ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for appending a node - ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value + ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for prepending a node ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ @@ -941,17 +858,15 @@ proc prepend*[T](L: var DoublyLinkedRing[T], value: T) = a.prepend(9) a.prepend(8) assert a.contains(9) - prepend(L, newDoublyLinkedNode(value)) proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## Removes `n` from `L`. Efficiency: O(1). - ## This function assumes, for the sake of efficiency, that `n` is contained in `L`, - ## otherwise the effects are undefined. runnableExamples: - var a = initDoublyLinkedRing[int]() - let n = newDoublyLinkedNode[int](5) - a.add(n) + var + a = initDoublyLinkedRing[int]() + n = newDoublyLinkedNode[int](5) + a.append(n) assert 5 in a a.remove(n) assert 5 notin a @@ -959,37 +874,9 @@ proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = n.next.prev = n.prev n.prev.next = n.next if n == L.head: - let p = L.head.prev + var p = L.head.prev if p == L.head: # only one element left: L.head = nil else: - L.head = p - -proc append*[T](a: var (SinglyLinkedList[T] | SinglyLinkedRing[T]), - b: SinglyLinkedList[T] | SinglyLinkedNode[T] | T) = - ## Alias for `a.add(b)`. - ## - ## **See also:** - ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ - ## * `add proc <#add,SinglyLinkedList[T],T>`_ - ## * `add proc <#add,T,T>`_ - a.add(b) - -proc append*[T](a: var (DoublyLinkedList[T] | DoublyLinkedRing[T]), - b: DoublyLinkedList[T] | DoublyLinkedNode[T] | T) = - ## Alias for `a.add(b)`. - ## - ## **See also:** - ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ - ## * `add proc <#add,DoublyLinkedList[T],T>`_ - ## * `add proc <#add,T,T>`_ - a.add(b) - -proc appendMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} = - ## Alias for `a.addMoved(b)`. - ## - ## **See also:** - ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_ - ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_ - a.addMoved(b) + L.head = L.head.prev diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 1773e827b..9d41a5389 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -39,7 +39,7 @@ ## `method call syntax`_. runnableExamples: - import std/sugar + import sugar # Creating a sequence from 1 to 10, multiplying each member by 2, # keeping only the members which are not divisible by 6. @@ -62,7 +62,7 @@ runnableExamples: runnableExamples: - from std/strutils import join + from strutils import join let vowels = @"aeiou" @@ -84,15 +84,10 @@ import std/private/since import macros -when defined(nimPreviewSlimSystem): - import std/assertions +when not defined(nimhygiene): + {.pragma: dirty.} -when defined(nimHasEffectsOf): - {.experimental: "strictEffects".} -else: - {.pragma: effectsOf.} - macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped = ## Injects `expAlias` in caller scope, to avoid bugs involving multiple @@ -173,7 +168,7 @@ func cycle*[T](s: openArray[T], n: Natural): seq[T] = result[o] = e inc o -proc repeat*[T](x: T, n: Natural): seq[T] = +func repeat*[T](x: T, n: Natural): seq[T] = ## Returns a new sequence with the item `x` repeated `n` times. ## `n` must be a non-negative number (zero or more). ## @@ -250,7 +245,7 @@ func maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} = template zipImpl(s1, s2, retType: untyped): untyped = - proc zip*[S, T](s1: openArray[S], s2: openArray[T]): retType = + func zip*[S, T](s1: openArray[S], s2: openArray[T]): retType = ## Returns a new sequence with a combination of the two input containers. ## ## The input containers can be of different types. @@ -293,7 +288,7 @@ when (NimMajor, NimMinor) <= (1, 0): else: zipImpl(s1, s2, seq[(S, T)]) -proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = +func unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = ## Returns a tuple of two sequences split out from a sequence of 2-field tuples. runnableExamples: let @@ -302,7 +297,8 @@ proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = unzipped2 = @['a', 'b', 'c'] assert zipped.unzip() == (unzipped1, unzipped2) assert zip(unzipped1, unzipped2).unzip() == (unzipped1, unzipped2) - result = (newSeq[S](s.len), newSeq[T](s.len)) + result[0] = newSeq[S](s.len) + result[1] = newSeq[T](s.len) for i in 0..`_ - ## * `filter proc<#filter,openArray[T],proc(T)>`_ + ## * `fliter proc<#filter,openArray[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## runnableExamples: @@ -461,7 +458,7 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T {.ef yield s[i] proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] - {.inline, effectsOf: pred.} = + {.inline.} = ## Returns a new sequence with all the items of `s` that fulfill the ## predicate `pred` (a function that returns a `bool`). ## @@ -488,7 +485,7 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] result.add(s[i]) proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) - {.inline, effectsOf: pred.} = + {.inline.} = ## Keeps the items in the passed sequence `s` if they fulfill the ## predicate `pred` (a function that returns a `bool`). ## @@ -517,51 +514,12 @@ proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) inc(pos) setLen(s, pos) -func delete*[T](s: var seq[T]; slice: Slice[int]) = - ## Deletes the items `s[slice]`, raising `IndexDefect` if the slice contains - ## elements out of range. - ## - ## This operation moves all elements after `s[slice]` in linear time. - runnableExamples: - var a = @[10, 11, 12, 13, 14] - doAssertRaises(IndexDefect): a.delete(4..5) - assert a == @[10, 11, 12, 13, 14] - a.delete(4..4) - assert a == @[10, 11, 12, 13] - a.delete(1..2) - assert a == @[10, 13] - a.delete(1..<1) # empty slice - assert a == @[10, 13] - when compileOption("boundChecks"): - if not (slice.a < s.len and slice.a >= 0 and slice.b < s.len): - raise newException(IndexDefect, $(slice: slice, len: s.len)) - if slice.b >= slice.a: - template defaultImpl = - var i = slice.a - var j = slice.b + 1 - var newLen = s.len - j + i - while i < newLen: - when defined(gcDestructors): - s[i] = move(s[j]) - else: - s[i].shallowCopy(s[j]) - inc(i) - inc(j) - setLen(s, newLen) - when nimvm: defaultImpl() - else: - when defined(js): - let n = slice.b - slice.a + 1 - let first = slice.a - {.emit: "`s`.splice(`first`, `n`);".} - else: - defaultImpl() - -func delete*[T](s: var seq[T]; first, last: Natural) {.deprecated: "use `delete(s, first..last)`".} = +func delete*[T](s: var seq[T]; first, last: Natural) = ## Deletes the items of a sequence `s` at positions `first..last` ## (including both ends of the range). ## This modifies `s` itself, it does not return a copy. - runnableExamples("--warning:deprecated:off"): + ## + runnableExamples: let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] dest.delete(3, 8) @@ -629,7 +587,7 @@ template filterIt*(s, pred: untyped): untyped = ## ## **See also:** ## * `sugar.collect macro`_ - ## * `filter proc<#filter,openArray[T],proc(T)>`_ + ## * `fliter proc<#filter,openArray[T],proc(T)>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ ## runnableExamples: @@ -693,7 +651,7 @@ since (1, 1): if pred: result += 1 result -proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} = +proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = ## Iterates through a container and checks if every item fulfills the ## predicate. ## @@ -735,7 +693,7 @@ template allIt*(s, pred: untyped): bool = break result -proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} = +proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = ## Iterates through a container and checks if at least one item ## fulfills the predicate. ## @@ -790,7 +748,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 @@ -807,7 +765,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() @@ -851,7 +809,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 @@ -913,7 +871,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 therefore defines the type of the result. + ## start value (the first `a`) and therefor defines the type of the result. ## ## **See also:** ## * `foldr template<#foldr.t,untyped,untyped>`_ @@ -998,10 +956,16 @@ template mapIt*(s: typed, op: untyped): untyped = strings = nums.mapIt($(4 * it)) assert strings == @["4", "8", "12", "16"] - type OutType = typeof(( - block: - var it{.inject.}: typeof(items(s), typeOfIter); - op), typeOfProc) + when defined(nimHasTypeof): + type OutType = typeof(( + block: + var it{.inject.}: typeof(items(s), typeOfIter); + op), typeOfProc) + else: + type OutType = typeof(( + block: + var it{.inject.}: typeof(items(s)); + op)) when OutType is not (proc): # Here, we avoid to create closures in loops. # This avoids https://github.com/nim-lang/Nim/issues/12625 @@ -1019,7 +983,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) @@ -1032,7 +996,11 @@ template mapIt*(s: typed, op: untyped): untyped = # With this fallback, above code can be simplified to: # [1, 2].mapIt((x: int) => it + x) # In this case, `mapIt` is just syntax sugar for `map`. - type InType = typeof(items(s), typeOfIter) + + when defined(nimHasTypeof): + type InType = typeof(items(s), typeOfIter) + else: + type InType = typeof(items(s)) # Use a help proc `f` to create closures for each element in `s` let f = proc (x: InType): OutType = let it {.inject.} = x @@ -1061,27 +1029,27 @@ template applyIt*(varSeq, op: untyped) = template newSeqWith*(len: int, init: untyped): untyped = - ## Creates a new `seq` of length `len`, calling `init` to initialize - ## each value of the seq. + ## Creates a new sequence of length `len`, calling `init` to initialize + ## each value of the sequence. + ## + ## Useful for creating "2D" sequences - sequences containing other sequences + ## or to populate fields of the created sequence. ## - ## Useful for creating "2D" seqs - seqs containing other seqs - ## or to populate fields of the created seq. runnableExamples: - ## Creates a seq containing 5 bool seqs, each of length of 3. + ## Creates a sequence containing 5 bool sequences, each of length of 3. var seq2D = newSeqWith(5, newSeq[bool](3)) assert seq2D.len == 5 assert seq2D[0].len == 3 assert seq2D[4][2] == false - ## Creates a seq with random numbers - import std/random - var seqRand = newSeqWith(20, rand(1.0)) - assert seqRand[0] != seqRand[1] + ## Creates a sequence of 20 random numbers from 1 to 10 + import random + var seqRand = newSeqWith(20, rand(10)) var result = newSeq[typeof(init)](len) for i in 0 ..< len: result[i] = init - move(result) # refs bug #7295 + result func mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool; filter = nnkLiterals): NimNode = diff --git a/lib/pure/collections/setimpl.nim b/lib/pure/collections/setimpl.nim index 7ebd22760..20da6b6c2 100644 --- a/lib/pure/collections/setimpl.nim +++ b/lib/pure/collections/setimpl.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -# An `include` file for the different hash set implementations. +# An ``include`` file for the different hash set implementations. template maxHash(t): untyped = high(t.data) @@ -86,7 +86,7 @@ proc exclImpl[A](s: var HashSet[A], key: A): bool {.inline.} = var j = i # The correctness of this depends on (h+1) in nextTry, var r = j # though may be adaptable to other simple sequences. s.data[i].hcode = 0 # mark current EMPTY - s.data[i].key = default(typeof(s.data[i].key)) + s.data[i].key = default(type(s.data[i].key)) doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): i = (i + 1) and msk # increment mod table size if isEmpty(s.data[i].hcode): # end of collision cluster; So all done diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 11e324923..a9df9ded8 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## The `sets` module implements an efficient `hash set`:idx: and +## The ``sets`` module implements an efficient `hash set`:idx: and ## ordered hash set. ## ## Hash sets are different from the `built in set type @@ -40,7 +40,7 @@ ## ## ## Note: The data types declared here have *value semantics*: This means -## that `=` performs a copy of the set. +## that ``=`` performs a copy of the set. ## ## **See also:** ## * `intsets module `_ for efficient int sets @@ -50,10 +50,10 @@ import hashes, math -when not defined(nimHasEffectsOf): - {.pragma: effectsOf.} - {.pragma: myShallow.} +when not defined(nimhygiene): + {.pragma: dirty.} + # For "integer-like A" that are too big for intsets/bit-vectors to be practical, # it would be best to shrink hcode to the same size as the integer. Larger # codes should never be needed, and this can pack more entries per cache-line. @@ -64,7 +64,7 @@ type HashSet*[A] {.myShallow.} = object ## \ ## A generic hash set. ## - ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet>`_ + ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet,int>`_ ## before calling other procs on it. data: KeyValuePairSeq[A] counter: int @@ -116,7 +116,7 @@ proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] = ## Wrapper around `init proc <#init,HashSet[A]>`_ for initialization of ## hash sets. ## - ## Returns an empty hash set you can assign directly in `var` blocks in a + ## Returns an empty hash set you can assign directly in ``var`` blocks in a ## single line. ## ## Starting from Nim v0.20, sets are initialized by default and it is @@ -133,7 +133,7 @@ proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] = proc `[]`*[A](s: var HashSet[A], key: A): var A = ## Returns the element that is actually stored in `s` which has the same - ## value as `key` or raises the `KeyError` exception. + ## value as `key` or raises the ``KeyError`` exception. ## ## This is useful when one overloaded `hash` and `==` but still needs ## reference semantics for sharing. @@ -167,27 +167,6 @@ proc contains*[A](s: HashSet[A], key: A): bool = var index = rawGet(s, key, hc) result = index >= 0 -proc len*[A](s: HashSet[A]): int = - ## Returns the number of elements in `s`. - ## - ## Due to an implementation detail you can call this proc on variables which - ## have not been initialized yet. The proc will return zero as the length - ## then. - runnableExamples: - var a: HashSet[string] - assert len(a) == 0 - let s = toHashSet([3, 5, 7]) - assert len(s) == 3 - - result = s.counter - -proc card*[A](s: HashSet[A]): int = - ## Alias for `len() <#len,HashSet[A]>`_. - ## - ## Card stands for the `cardinality - ## `_ of a set. - result = s.counter - proc incl*[A](s: var HashSet[A], key: A) = ## Includes an element `key` in `s`. ## @@ -262,11 +241,8 @@ iterator items*[A](s: HashSet[A]): A = ## assert a.len == 2 ## echo b ## # --> {(a: 1, b: 3), (a: 0, b: 4)} - let length = s.len for h in 0 .. high(s.data): - if isFilled(s.data[h].hcode): - yield s.data[h].key - assert(len(s) == length, "the length of the HashSet changed while iterating over it") + if isFilled(s.data[h].hcode): yield s.data[h].key proc containsOrIncl*[A](s: var HashSet[A], key: A): bool = ## Includes `key` in the set `s` and tells if `key` was already in `s`. @@ -347,7 +323,7 @@ proc missingOrExcl*[A](s: var HashSet[A], key: A): bool = proc pop*[A](s: var HashSet[A]): A = ## Removes and returns an arbitrary element from the set `s`. ## - ## Raises `KeyError` if the set `s` is empty. + ## Raises KeyError if the set `s` is empty. ## ## See also: ## * `clear proc <#clear,HashSet[A]>`_ @@ -379,7 +355,28 @@ proc clear*[A](s: var HashSet[A]) = s.counter = 0 for i in 0 ..< s.data.len: s.data[i].hcode = 0 - s.data[i].key = default(typeof(s.data[i].key)) + s.data[i].key = default(type(s.data[i].key)) + +proc len*[A](s: HashSet[A]): int = + ## Returns the number of elements in `s`. + ## + ## Due to an implementation detail you can call this proc on variables which + ## have not been initialized yet. The proc will return zero as the length + ## then. + runnableExamples: + var a: HashSet[string] + assert len(a) == 0 + let s = toHashSet([3, 5, 7]) + assert len(s) == 3 + + result = s.counter + +proc card*[A](s: HashSet[A]): int = + ## Alias for `len() <#len,HashSet[A]>`_. + ## + ## Card stands for the `cardinality + ## `_ of a set. + result = s.counter proc union*[A](s1, s2: HashSet[A]): HashSet[A] = @@ -425,7 +422,7 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] = assert c == toHashSet(["b"]) result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2)) - + # iterate over the elements of the smaller set if s1.data.len < s2.data.len: for item in s1: @@ -433,7 +430,7 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] = else: for item in s2: if item in s1: incl(result, item) - + proc difference*[A](s1, s2: HashSet[A]): HashSet[A] = ## Returns the difference of the sets `s1` and `s2`. @@ -559,7 +556,7 @@ proc `==`*[A](s, t: HashSet[A]): bool = s.counter == t.counter and s <= t -proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] {.effectsOf: op.} = +proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] = ## Returns a new set after applying `op` proc on each of the elements of ##`data` set. ## @@ -606,10 +603,12 @@ proc isValid*[A](s: HashSet[A]): bool {.deprecated: ## Returns `true` if the set has been initialized (with `initHashSet proc ## <#initHashSet>`_ or `init proc <#init,HashSet[A]>`_). ## - runnableExamples: - proc savePreferences(options: HashSet[string]) = - assert options.isValid, "Pass an initialized set!" - # Do stuff here, may crash in release builds! + ## **Examples:** + ## + ## .. code-block :: + ## proc savePreferences(options: HashSet[string]) = + ## assert options.isValid, "Pass an initialized set!" + ## # Do stuff here, may crash in release builds! result = s.data.len > 0 @@ -653,7 +652,7 @@ proc initOrderedSet*[A](initialSize = defaultInitialSize): OrderedSet[A] = ## Wrapper around `init proc <#init,OrderedSet[A]>`_ for initialization of ## ordered hash sets. ## - ## Returns an empty ordered hash set you can assign directly in `var` blocks + ## Returns an empty ordered hash set you can assign directly in ``var`` blocks ## in a single line. ## ## Starting from Nim v0.20, sets are initialized by default and it is @@ -813,7 +812,7 @@ proc clear*[A](s: var OrderedSet[A]) = for i in 0 ..< s.data.len: s.data[i].hcode = 0 s.data[i].next = 0 - s.data[i].key = default(typeof(s.data[i].key)) + s.data[i].key = default(type(s.data[i].key)) proc len*[A](s: OrderedSet[A]): int {.inline.} = ## Returns the number of elements in `s`. @@ -902,10 +901,8 @@ iterator items*[A](s: OrderedSet[A]): A = ## # --> Got 5 ## # --> Got 8 ## # --> Got 4 - let length = s.len forAllOrderedPairs: yield s.data[h].key - assert(len(s) == length, "the length of the OrderedSet changed while iterating over it") iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] = ## Iterates through (position, value) tuples of OrderedSet `s`. @@ -916,7 +913,5 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] = p.add(x) assert p == @[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'c'), (4, 'd')] - let length = s.len forAllOrderedPairs: yield (idx, s.data[h].key) - assert(len(s) == length, "the length of the OrderedSet changed while iterating over it") diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim index e61883220..c72477675 100644 --- a/lib/pure/collections/sharedlist.nim +++ b/lib/pure/collections/sharedlist.nim @@ -11,8 +11,6 @@ ## ## Unstable API. -{.deprecated.} - {.push stackTrace: off.} import @@ -37,10 +35,8 @@ template withLock(t, x: untyped) = release(t.lock) proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = - ## Iterates over the list. If `action` returns true, the + ## iterates over the list. If 'action' returns true, the ## current item is removed from the list. - ## - ## .. warning:: It may not preserve the element order after some modifications. withLock(x): var n = x.head while n != nil: @@ -51,9 +47,8 @@ proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = if action(n.d[i]): acquire(x.lock) let t = x.tail - dec t.dataLen # TODO considering t.dataLen == 0, - # probably the module should be refactored using doubly linked lists n.d[i] = t.d[t.dataLen] + dec t.dataLen else: acquire(x.lock) inc i diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index 816ab49ab..af498d70d 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -14,8 +14,6 @@ ## ## Unstable API. -{.deprecated.} - import hashes, math, locks @@ -62,27 +60,17 @@ template withLock(t, x: untyped) = template withValue*[A, B](t: var SharedTable[A, B], key: A, value, body: untyped) = - ## Retrieves the value at `t[key]`. - ## `value` can be modified in the scope of the `withValue` call. - runnableExamples: - var table: SharedTable[string, string] - init(table) - - table["a"] = "x" - table["b"] = "y" - table["c"] = "z" - - table.withValue("a", value): - assert value[] == "x" - - table.withValue("b", value): - value[] = "modified" - - table.withValue("b", value): - assert value[] == "modified" - - table.withValue("nonexistent", value): - assert false # not called + ## retrieves the value at ``t[key]``. + ## `value` can be modified in the scope of the ``withValue`` call. + ## + ## .. code-block:: nim + ## + ## sharedTable.withValue(key, value) do: + ## # block is executed only if ``key`` in ``t`` + ## # value is threadsafe in block + ## value.name = "username" + ## value.uid = 1000 + ## acquire(t.lock) try: var hc: Hash @@ -96,33 +84,20 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A, template withValue*[A, B](t: var SharedTable[A, B], key: A, value, body1, body2: untyped) = - ## Retrieves the value at `t[key]`. - ## `value` can be modified in the scope of the `withValue` call. - runnableExamples: - var table: SharedTable[string, string] - init(table) - - table["a"] = "x" - table["b"] = "y" - table["c"] = "z" - - - table.withValue("a", value): - value[] = "m" - - var flag = false - table.withValue("d", value): - discard value - doAssert false - do: # if "d" notin table - flag = true - - if flag: - table["d"] = "n" - - assert table.mget("a") == "m" - assert table.mget("d") == "n" - + ## retrieves the value at ``t[key]``. + ## `value` can be modified in the scope of the ``withValue`` call. + ## + ## .. code-block:: nim + ## + ## sharedTable.withValue(key, value) do: + ## # block is executed only if ``key`` in ``t`` + ## # value is threadsafe in block + ## value.name = "username" + ## value.uid = 1000 + ## do: + ## # block is executed when ``key`` not in ``t`` + ## raise newException(KeyError, "Key not found") + ## acquire(t.lock) try: var hc: Hash @@ -137,8 +112,8 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A, release(t.lock) proc mget*[A, B](t: var SharedTable[A, B], key: A): var B = - ## Retrieves the value at `t[key]`. The value can be modified. - ## If `key` is not in `t`, the `KeyError` exception is raised. + ## retrieves the value at ``t[key]``. The value can be modified. + ## If `key` is not in `t`, the ``KeyError`` exception is raised. withLock t: var hc: Hash var index = rawGet(t, key, hc) @@ -151,15 +126,15 @@ proc mget*[A, B](t: var SharedTable[A, B], key: A): var B = raise newException(KeyError, "key not found") proc mgetOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): var B = - ## Retrieves value at `t[key]` or puts `val` if not present, either way + ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way ## returning a value which can be modified. **Note**: This is inherently ## unsafe in the context of multi-threading since it returns a pointer - ## to `B`. + ## to ``B``. withLock t: mgetOrPutImpl(enlarge) proc hasKeyOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): bool = - ## Returns true if `key` is in the table, otherwise inserts `value`. + ## returns true if `key` is in the table, otherwise inserts `value`. withLock t: hasKeyOrPutImpl(enlarge) @@ -169,24 +144,24 @@ template tabCellHash(i) = t.data[i].hcode proc withKey*[A, B](t: var SharedTable[A, B], key: A, mapper: proc(key: A, val: var B, pairExists: var bool)) = - ## Computes a new mapping for the `key` with the specified `mapper` + ## Computes a new mapping for the ``key`` with the specified ``mapper`` ## procedure. ## - ## The `mapper` takes 3 arguments: + ## The ``mapper`` takes 3 arguments: ## - ## 1. `key` - the current key, if it exists, or the key passed to - ## `withKey` otherwise; - ## 2. `val` - the current value, if the key exists, or default value + ## 1. ``key`` - the current key, if it exists, or the key passed to + ## ``withKey`` otherwise; + ## 2. ``val`` - the current value, if the key exists, or default value ## of the type otherwise; - ## 3. `pairExists` - `true` if the key exists, `false` otherwise. + ## 3. ``pairExists`` - ``true`` if the key exists, ``false`` otherwise. ## - ## The `mapper` can can modify `val` and `pairExists` values to change + ## The ``mapper`` can can modify ``val`` and ``pairExists`` values to change ## the mapping of the key or delete it from the table. - ## When adding a value, make sure to set `pairExists` to `true` along - ## with modifying the `val`. + ## When adding a value, make sure to set ``pairExists`` to ``true`` along + ## with modifying the ``val``. ## ## The operation is performed atomically and other operations on the table - ## will be blocked while the `mapper` is invoked, so it should be short and + ## will be blocked while the ``mapper`` is invoked, so it should be short and ## simple. ## ## Example usage: @@ -216,28 +191,28 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A, st_maybeRehashPutImpl(enlarge) proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) = - ## Puts a (key, value)-pair into `t`. + ## puts a (key, value)-pair into `t`. withLock t: putImpl(enlarge) proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) = - ## Puts a new (key, value)-pair into `t` even if `t[key]` already exists. + ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. ## This can introduce duplicate keys into the table! withLock t: addImpl(enlarge) proc del*[A, B](t: var SharedTable[A, B], key: A) = - ## Deletes `key` from hash table `t`. + ## deletes `key` from hash table `t`. withLock t: delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash) proc len*[A, B](t: var SharedTable[A, B]): int = - ## Number of elements in `t`. + ## number of elements in `t` withLock t: result = t.counter proc init*[A, B](t: var SharedTable[A, B], initialSize = 32) = - ## Creates a new hash table that is empty. + ## creates a new hash table that is empty. ## ## This proc must be called before any other usage of `t`. let initialSize = slotsNeeded(initialSize) diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index 81079a3d1..c66da513c 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -# An `include` file for the different table implementations. +# An ``include`` file for the different table implementations. include hashcommon @@ -23,7 +23,7 @@ template rawInsertImpl() {.dirty.} = data[h].val = val data[h].hcode = hc -proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} = +proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline.} = rawGetDeepImpl() proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B], @@ -117,8 +117,8 @@ template delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) = var j = i # The correctness of this depends on (h+1) in nextTry var r = j # though may be adaptable to other simple sequences. makeEmpty(i) # mark current EMPTY - t.data[i].key = default(typeof(t.data[i].key)) - t.data[i].val = default(typeof(t.data[i].val)) + t.data[i].key = default(type(t.data[i].key)) + t.data[i].val = default(type(t.data[i].val)) while true: i = (i + 1) and msk # increment mod table size if cellEmpty(i): # end of collision cluster; So all done @@ -149,8 +149,8 @@ template clearImpl() {.dirty.} = for i in 0 ..< t.dataLen: when compiles(t.data[i].hcode): # CountTable records don't contain a hcode t.data[i].hcode = 0 - t.data[i].key = default(typeof(t.data[i].key)) - t.data[i].val = default(typeof(t.data[i].val)) + t.data[i].key = default(type(t.data[i].key)) + t.data[i].val = default(type(t.data[i].val)) t.counter = 0 template ctAnd(a, b): bool = @@ -161,7 +161,7 @@ template ctAnd(a, b): bool = template initImpl(result: typed, size: int) = let correctSize = slotsNeeded(size) - when ctAnd(declared(SharedTable), typeof(result) is SharedTable): + when ctAnd(declared(SharedTable), type(result) is SharedTable): init(result, correctSize) else: result.counter = 0 diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 4737fa478..d7376b065 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -7,137 +7,158 @@ # distribution, for details about the copyright. # -## The `tables` module implements variants of an efficient `hash table`:idx: +## The ``tables`` module implements variants of an efficient `hash table`:idx: ## (also often named `dictionary`:idx: in other programming languages) that is ## a mapping from keys to values. ## ## There are several different types of hash tables available: ## * `Table<#Table>`_ is the usual hash table, -## * `OrderedTable<#OrderedTable>`_ is like `Table` but remembers insertion order, +## * `OrderedTable<#OrderedTable>`_ is like ``Table`` but remembers insertion order, ## * `CountTable<#CountTable>`_ is a mapping from a key to its number of occurrences ## ## For consistency with every other data type in Nim these have **value** -## semantics, this means that `=` performs a copy of the hash table. +## semantics, this means that ``=`` performs a copy of the hash table. ## ## For `ref semantics`_ -## use their `Ref` variants: `TableRef<#TableRef>`_, +## use their ``Ref`` variants: `TableRef<#TableRef>`_, ## `OrderedTableRef<#OrderedTableRef>`_, and `CountTableRef<#CountTableRef>`_. ## -## To give an example, when `a` is a `Table`, then `var b = a` gives `b` -## as a new independent table. `b` is initialised with the contents of `a`. -## Changing `b` does not affect `a` and vice versa: - -runnableExamples: - var - a = {1: "one", 2: "two"}.toTable # creates a Table - b = a - - assert a == b - - b[3] = "three" - assert 3 notin a - assert 3 in b - assert a != b - -## On the other hand, when `a` is a `TableRef` instead, then changes to `b` -## also affect `a`. Both `a` and `b` **ref** the same data structure: - -runnableExamples: - var - a = {1: "one", 2: "two"}.newTable # creates a TableRef - b = a - - assert a == b - - b[3] = "three" - - assert 3 in a - assert 3 in b - assert a == b - +## To give an example, when ``a`` is a ``Table``, then ``var b = a`` gives ``b`` +## as a new independent table. ``b`` is initialised with the contents of ``a``. +## Changing ``b`` does not affect ``a`` and vice versa: +## +## .. code-block:: +## import tables +## +## var +## a = {1: "one", 2: "two"}.toTable # creates a Table +## b = a +## +## echo a, b # output: {1: one, 2: two}{1: one, 2: two} +## +## b[3] = "three" +## echo a, b # output: {1: one, 2: two}{1: one, 2: two, 3: three} +## echo a == b # output: false +## +## On the other hand, when ``a`` is a ``TableRef`` instead, then changes to ``b`` +## also affect ``a``. Both ``a`` and ``b`` **ref** the same data structure: +## +## .. code-block:: +## import tables +## +## var +## a = {1: "one", 2: "two"}.newTable # creates a TableRef +## b = a +## +## echo a, b # output: {1: one, 2: two}{1: one, 2: two} +## +## b[3] = "three" +## echo a, b # output: {1: one, 2: two, 3: three}{1: one, 2: two, 3: three} +## echo a == b # output: true ## ## ---- ## - -## # Basic usage - - -## ## Table -runnableExamples: - from std/sequtils import zip - - let - names = ["John", "Paul", "George", "Ringo"] - years = [1940, 1942, 1943, 1940] - - var beatles = initTable[string, int]() - - for pairs in zip(names, years): - let (name, birthYear) = pairs - beatles[name] = birthYear - - assert beatles == {"George": 1943, "Ringo": 1940, "Paul": 1942, "John": 1940}.toTable - - - var beatlesByYear = initTable[int, seq[string]]() - - for pairs in zip(years, names): - let (birthYear, name) = pairs - if not beatlesByYear.hasKey(birthYear): - # if a key doesn't exist, we create one with an empty sequence - # before we can add elements to it - beatlesByYear[birthYear] = @[] - beatlesByYear[birthYear].add(name) - - assert beatlesByYear == {1940: @["John", "Ringo"], 1942: @["Paul"], 1943: @["George"]}.toTable - -## ## OrderedTable +## Basic usage +## =========== +## +## Table +## ----- +## +## .. code-block:: +## import tables +## from sequtils import zip +## +## let +## names = ["John", "Paul", "George", "Ringo"] +## years = [1940, 1942, 1943, 1940] +## +## var beatles = initTable[string, int]() +## +## for pairs in zip(names, years): +## let (name, birthYear) = pairs +## beatles[name] = birthYear +## +## echo beatles +## # {"George": 1943, "Ringo": 1940, "Paul": 1942, "John": 1940} +## +## +## var beatlesByYear = initTable[int, seq[string]]() +## +## for pairs in zip(years, names): +## let (birthYear, name) = pairs +## if not beatlesByYear.hasKey(birthYear): +## # if a key doesn't exist, we create one with an empty sequence +## # before we can add elements to it +## beatlesByYear[birthYear] = @[] +## beatlesByYear[birthYear].add(name) +## +## echo beatlesByYear +## # {1940: @["John", "Ringo"], 1942: @["Paul"], 1943: @["George"]} +## +## +## +## OrderedTable +## ------------ +## ## `OrderedTable<#OrderedTable>`_ is used when it is important to preserve ## the insertion order of keys. - -runnableExamples: - let - a = [('z', 1), ('y', 2), ('x', 3)] - ot = a.toOrderedTable # ordered tables - - assert $ot == """{'z': 1, 'y': 2, 'x': 3}""" - -## ## CountTable +## +## .. code-block:: +## import tables +## +## let +## a = [('z', 1), ('y', 2), ('x', 3)] +## t = a.toTable # regular table +## ot = a.toOrderedTable # ordered tables +## +## echo t # {'x': 3, 'y': 2, 'z': 1} +## echo ot # {'z': 1, 'y': 2, 'x': 3} +## +## +## +## CountTable +## ---------- +## ## `CountTable<#CountTable>`_ is useful for counting number of items of some ## container (e.g. string, sequence or array), as it is a mapping where the ## items are the keys, and their number of occurrences are the values. ## For that purpose `toCountTable proc<#toCountTable,openArray[A]>`_ ## comes handy: - -runnableExamples: - let myString = "abracadabra" - let letterFrequencies = toCountTable(myString) - assert $letterFrequencies == "{'a': 5, 'd': 1, 'b': 2, 'r': 2, 'c': 1}" - +## +## .. code-block:: +## import tables +## +## let myString = "abracadabra" +## let letterFrequencies = toCountTable(myString) +## echo letterFrequencies +## # output: {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2} +## ## The same could have been achieved by manually iterating over a container ## and increasing each key's value with `inc proc ## <#inc,CountTable[A],A,int>`_: - -runnableExamples: - let myString = "abracadabra" - var letterFrequencies = initCountTable[char]() - for c in myString: - letterFrequencies.inc(c) - assert $letterFrequencies == "{'d': 1, 'r': 2, 'c': 1, 'a': 5, 'b': 2}" - +## +## .. code-block:: +## import tables +## +## let myString = "abracadabra" +## var letterFrequencies = initCountTable[char]() +## for c in myString: +## letterFrequencies.inc(c) +## echo letterFrequencies +## # output: {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2} ## ## ---- ## - -## ## Hashing ## -## If you are using simple standard types like `int` or `string` for the +## +## Hashing +## ------- +## +## If you are using simple standard types like ``int`` or ``string`` for the ## keys of the table you won't have any problems, but as soon as you try to use ## a more complex object as a key you will be greeted by a strange compiler ## error: ## -## .. code:: -## ## Error: type mismatch: got (Person) ## but expected one of: ## hashes.hash(x: openArray[A]): Hash @@ -146,63 +167,60 @@ runnableExamples: ## … ## ## What is happening here is that the types used for table keys require to have -## a `hash()` proc which will convert them to a `Hash `_ +## a ``hash()`` proc which will convert them to a `Hash `_ ## value, and the compiler is listing all the hash functions it knows. -## Additionally there has to be a `==` operator that provides the same -## semantics as its corresponding `hash` proc. +## Additionally there has to be a ``==`` operator that provides the same +## semantics as its corresponding ``hash`` proc. ## -## After you add `hash` and `==` for your custom type everything will work. -## Currently, however, `hash` for objects is not defined, whereas -## `system.==` for objects does exist and performs a "deep" comparison (every +## After you add ``hash`` and ``==`` for your custom type everything will work. +## Currently, however, ``hash`` for objects is not defined, whereas +## ``system.==`` for objects does exist and performs a "deep" comparison (every ## field is compared) which is usually what you want. So in the following -## example implementing only `hash` suffices: - -runnableExamples: - import std/hashes - - type - Person = object - firstName, lastName: string - - proc hash(x: Person): Hash = - ## Piggyback on the already available string hash proc. - ## - ## Without this proc nothing works! - result = x.firstName.hash !& x.lastName.hash - result = !$result - - var - salaries = initTable[Person, int]() - p1, p2: Person - - p1.firstName = "Jon" - p1.lastName = "Ross" - salaries[p1] = 30_000 - - p2.firstName = "소진" - p2.lastName = "박" - salaries[p2] = 45_000 - +## example implementing only ``hash`` suffices: +## +## .. code-block:: +## import tables, hashes +## +## type +## Person = object +## firstName, lastName: string +## +## proc hash(x: Person): Hash = +## ## Piggyback on the already available string hash proc. +## ## +## ## Without this proc nothing works! +## result = x.firstName.hash !& x.lastName.hash +## result = !$result +## +## var +## salaries = initTable[Person, int]() +## p1, p2: Person +## +## p1.firstName = "Jon" +## p1.lastName = "Ross" +## salaries[p1] = 30_000 +## +## p2.firstName = "소진" +## p2.lastName = "박" +## salaries[p2] = 45_000 ## ## ---- ## - -## # See also +## See also +## ======== ## ## * `json module`_ for table-like structure which allows ## heterogeneous members +## * `sharedtables module`_ for shared hash table support ## * `strtabs module`_ for efficient hash tables ## mapping from strings to strings ## * `hashes module`_ for helper functions for hashing import std/private/since + import hashes, math, algorithm - -when not defined(nimHasEffectsOf): - {.pragma: effectsOf.} - type KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B] KeyValuePairSeq[A, B] = seq[KeyValuePair[A, B]] @@ -232,21 +250,18 @@ template dataLen(t): untyped = len(t.data) include tableimpl -proc raiseKeyError[T](key: T) {.noinline, noreturn.} = - when compiles($key): - raise newException(KeyError, "key not found: " & $key) - else: - raise newException(KeyError, "key not found") - template get(t, key): untyped = - ## retrieves the value at `t[key]`. The value can be modified. - ## If `key` is not in `t`, the `KeyError` exception is raised. + ## retrieves the value at ``t[key]``. The value can be modified. + ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. mixin rawGet var hc: Hash var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val else: - raiseKeyError(key) + when compiles($key): + raise newException(KeyError, "key not found: " & $key) + else: + raise newException(KeyError, "key not found") proc enlarge[A, B](t: var Table[A, B]) = var n: KeyValuePairSeq[A, B] @@ -286,7 +301,7 @@ proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] = initImpl(result, initialSize) proc `[]=`*[A, B](t: var Table[A, B], key: A, val: sink B) = - ## Inserts a `(key, value)` pair into `t`. + ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key @@ -302,9 +317,9 @@ proc `[]=`*[A, B](t: var Table[A, B], key: A, val: sink B) = putImpl(enlarge) proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] = - ## Creates a new hash table that contains the given `pairs`. + ## Creates a new hash table that contains the given ``pairs``. ## - ## `pairs` is a container consisting of `(key, value)` tuples. + ## ``pairs`` is a container consisting of ``(key, value)`` tuples. ## ## See also: ## * `initTable proc<#initTable>`_ @@ -318,9 +333,9 @@ proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] = for key, val in items(pairs): result[key] = val proc `[]`*[A, B](t: Table[A, B], key: A): B = - ## Retrieves the value at `t[key]`. + ## Retrieves the value at ``t[key]``. ## - ## If `key` is not in `t`, the `KeyError` exception is raised. + ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. ## One can check with `hasKey proc<#hasKey,Table[A,B],A>`_ whether ## the key exists. ## @@ -329,7 +344,7 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new + ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in ## the table @@ -341,23 +356,23 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B = get(t, key) proc `[]`*[A, B](t: var Table[A, B], key: A): var B = - ## Retrieves the value at `t[key]`. The value can be modified. + ## Retrieves the value at ``t[key]``. The value can be modified. ## - ## If `key` is not in `t`, the `KeyError` exception is raised. + ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. ## ## See also: ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new + ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in ## the table get(t, key) proc hasKey*[A, B](t: Table[A, B], key: A): bool = - ## Returns true if `key` is in the table `t`. + ## Returns true if ``key`` is in the table ``t``. ## ## See also: ## * `contains proc<#contains,Table[A,B],A>`_ for use with the `in` operator @@ -376,7 +391,7 @@ proc hasKey*[A, B](t: Table[A, B], key: A): bool = proc contains*[A, B](t: Table[A, B], key: A): bool = ## Alias of `hasKey proc<#hasKey,Table[A,B],A>`_ for use with - ## the `in` operator. + ## the ``in`` operator. runnableExamples: let a = {'a': 5, 'b': 9}.toTable doAssert 'b' in a == true @@ -385,7 +400,7 @@ proc contains*[A, B](t: Table[A, B], key: A): bool = return hasKey[A, B](t, key) proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool = - ## Returns true if `key` is in the table, otherwise inserts `value`. + ## Returns true if ``key`` is in the table, otherwise inserts ``value``. ## ## See also: ## * `hasKey proc<#hasKey,Table[A,B],A>`_ @@ -405,8 +420,8 @@ proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool = hasKeyOrPutImpl(enlarge) proc getOrDefault*[A, B](t: Table[A, B], key: A): B = - ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the - ## default initialization value for type `B` is returned (e.g. 0 for any + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the + ## default initialization value for type ``B`` is returned (e.g. 0 for any ## integer type). ## ## See also: @@ -424,8 +439,8 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A): B = getOrDefaultImpl(t, key) proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B = - ## Retrieves the value at `t[key]` if `key` is in `t`. - ## Otherwise, `default` is returned. + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. + ## Otherwise, ``default`` is returned. ## ## See also: ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key @@ -442,12 +457,12 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B = getOrDefaultImpl(t, key, default) proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B = - ## Retrieves value at `t[key]` or puts `val` if not present, either way + ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way ## returning a value which can be modified. ## ## ## Note that while the value returned is of type `var B`, - ## it is easy to accidentally create a copy of the value at `t[key]`. + ## it is easy to accidentally create an copy of the value at `t[key]`. ## Remember that seqs and strings are value types, and therefore ## cannot be copied into a separate variable for modification. ## See the example below. @@ -480,7 +495,7 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B = mgetOrPutImpl(enlarge) proc len*[A, B](t: Table[A, B]): int = - ## Returns the number of keys in `t`. + ## Returns the number of keys in ``t``. runnableExamples: let a = {'a': 5, 'b': 9}.toTable doAssert len(a) == 2 @@ -489,11 +504,11 @@ proc len*[A, B](t: Table[A, B]): int = proc add*[A, B](t: var Table[A, B], key: A, val: sink B) {.deprecated: "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = - ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists. + ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## ## **This can introduce duplicate keys into the table!** ## - ## Use `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new + ## Use `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new ## (key, value) pair in the table without introducing duplicates. addImpl(enlarge) @@ -502,10 +517,7 @@ template tabCellEmpty(i) = isEmpty(t.data[i].hcode) template tabCellHash(i) = t.data[i].hcode proc del*[A, B](t: var Table[A, B], key: A) = - ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. - ## - ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), - ## this may need to be called multiple times. + ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist. ## ## See also: ## * `pop proc<#pop,Table[A,B],A,B>`_ @@ -520,14 +532,11 @@ proc del*[A, B](t: var Table[A, B], key: A) = delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash) proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool = - ## Deletes the `key` from the table. - ## Returns `true`, if the `key` existed, and sets `val` to the - ## mapping of the key. Otherwise, returns `false`, and the `val` is + ## Deletes the ``key`` from the table. + ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the + ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is ## unchanged. ## - ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), - ## this may need to be called multiple times. - ## ## See also: ## * `del proc<#del,Table[A,B],A>`_ ## * `clear proc<#clear,Table[A,B]>`_ to empty the whole table @@ -570,12 +579,12 @@ proc clear*[A, B](t: var Table[A, B]) = clearImpl() proc `$`*[A, B](t: Table[A, B]): string = - ## The `$` operator for hash tables. Used internally when calling `echo` + ## The ``$`` operator for hash tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A, B](s, t: Table[A, B]): bool = - ## The `==` operator for hash tables. Returns `true` if the content of both + ## The ``==`` operator for hash tables. Returns ``true`` if the content of both ## tables contains the same key-value pairs. Insert order does not matter. runnableExamples: let @@ -595,31 +604,17 @@ proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] = template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) = - ## Retrieves the value at `t[key]`. + ## Retrieves the value at ``t[key]``. + ## + ## ``value`` can be modified in the scope of the ``withValue`` call. + ## + ## .. code-block:: nim + ## + ## sharedTable.withValue(key, value) do: + ## # block is executed only if ``key`` in ``t`` + ## value.name = "username" + ## value.uid = 1000 ## - ## `value` can be modified in the scope of the `withValue` call. - runnableExamples: - type - User = object - name: string - uid: int - - var t = initTable[int, User]() - let u = User(name: "Hello", uid: 99) - t[1] = u - - t.withValue(1, value): - # block is executed only if `key` in `t` - value.name = "Nim" - value.uid = 1314 - - t.withValue(2, value): - value.name = "No" - value.uid = 521 - - assert t[1].name == "Nim" - assert t[1].uid == 1314 - mixin rawGet var hc: Hash var index = rawGet(t, key, hc) @@ -630,35 +625,20 @@ template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) = template withValue*[A, B](t: var Table[A, B], key: A, value, body1, body2: untyped) = - ## Retrieves the value at `t[key]`. + ## Retrieves the value at ``t[key]``. + ## + ## ``value`` can be modified in the scope of the ``withValue`` call. + ## + ## .. code-block:: nim + ## + ## table.withValue(key, value) do: + ## # block is executed only if ``key`` in ``t`` + ## value.name = "username" + ## value.uid = 1000 + ## do: + ## # block is executed when ``key`` not in ``t`` + ## raise newException(KeyError, "Key not found") ## - ## `value` can be modified in the scope of the `withValue` call. - runnableExamples: - type - User = object - name: string - uid: int - - var t = initTable[int, User]() - let u = User(name: "Hello", uid: 99) - t[1] = u - - t.withValue(1, value): - # block is executed only if `key` in `t` - value.name = "Nim" - value.uid = 1314 - - t.withValue(521, value): - doAssert false - do: - # block is executed when `key` not in `t` - t[1314] = User(name: "exist", uid: 521) - - assert t[1].name == "Nim" - assert t[1].uid == 1314 - assert t[1314].name == "exist" - assert t[1314].uid == 521 - mixin rawGet var hc: Hash var index = rawGet(t, key, hc) @@ -671,7 +651,7 @@ template withValue*[A, B](t: var Table[A, B], key: A, iterator pairs*[A, B](t: Table[A, B]): (A, B) = - ## Iterates over any `(key, value)` pair in the table `t`. + ## Iterates over any ``(key, value)`` pair in the table ``t``. ## ## See also: ## * `mpairs iterator<#mpairs.i,Table[A,B]>`_ @@ -701,7 +681,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) = - ## Iterates over any `(key, value)` pair in the table `t` (must be + ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be ## declared as `var`). The values can be modified. ## ## See also: @@ -722,8 +702,8 @@ iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: Table[A, B]): lent A = - ## Iterates over any key in the table `t`. +iterator keys*[A, B](t: Table[A, B]): A = + ## Iterates over any key in the table ``t``. ## ## See also: ## * `pairs iterator<#pairs.i,Table[A,B]>`_ @@ -743,8 +723,8 @@ iterator keys*[A, B](t: Table[A, B]): lent A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: Table[A, B]): lent B = - ## Iterates over any value in the table `t`. +iterator values*[A, B](t: Table[A, B]): B = + ## Iterates over any value in the table ``t``. ## ## See also: ## * `pairs iterator<#pairs.i,Table[A,B]>`_ @@ -765,7 +745,7 @@ iterator values*[A, B](t: Table[A, B]): lent B = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A, B](t: var Table[A, B]): var B = - ## Iterates over any value in the table `t` (must be + ## Iterates over any value in the table ``t`` (must be ## declared as `var`). The values can be modified. ## ## See also: @@ -788,13 +768,13 @@ iterator mvalues*[A, B](t: var Table[A, B]): var B = iterator allValues*[A, B](t: Table[A, B]; key: A): B {.deprecated: "Deprecated since v1.4; tables with duplicated keys are deprecated".} = - ## Iterates over any value in the table `t` that belongs to the given `key`. + ## Iterates over any value in the table ``t`` that belongs to the given ``key``. ## ## Used if you have a table with duplicate keys (as a result of using ## `add proc<#add,Table[A,B],A,sinkB>`_). ## runnableExamples: - import std/[sequtils, algorithm] + import sequtils, algorithm var a = {'a': 3, 'b': 5}.toTable for i in 1..3: a.add('z', 10*i) @@ -815,7 +795,7 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B {.deprecated: # ------------------------------------------------------------------- -proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = +proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = ## Creates a new ref hash table that is empty. ## ## See also: @@ -828,13 +808,12 @@ proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = b = newTable[char, seq[int]]() new(result) - {.noSideEffect.}: - result[] = initTable[A, B](initialSize) + result[] = initTable[A, B](initialSize) -proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = - ## Creates a new ref hash table that contains the given `pairs`. +proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = + ## Creates a new ref hash table that contains the given ``pairs``. ## - ## `pairs` is a container consisting of `(key, value)` tuples. + ## ``pairs`` is a container consisting of ``(key, value)`` tuples. ## ## See also: ## * `newTable proc<#newTable>`_ @@ -845,21 +824,19 @@ proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = assert b == {'a': 5, 'b': 9}.newTable new(result) - {.noSideEffect.}: - result[] = toTable[A, B](pairs) + result[] = toTable[A, B](pairs) -proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = +proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = ## Index the collection with the proc provided. # TODO: As soon as supported, change collection: A to collection: A[B] result = newTable[C, B]() - {.noSideEffect.}: - for item in collection: - result[index(item)] = item + for item in collection: + result[index(item)] = item proc `[]`*[A, B](t: TableRef[A, B], key: A): var B = - ## Retrieves the value at `t[key]`. + ## Retrieves the value at ``t[key]``. ## - ## If `key` is not in `t`, the `KeyError` exception is raised. + ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. ## One can check with `hasKey proc<#hasKey,TableRef[A,B],A>`_ whether ## the key exists. ## @@ -881,7 +858,7 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B = result = t[][key] proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: sink B) = - ## Inserts a `(key, value)` pair into `t`. + ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key @@ -897,7 +874,7 @@ proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: sink B) = t[][key] = val proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = - ## Returns true if `key` is in the table `t`. + ## Returns true if ``key`` is in the table ``t``. ## ## See also: ## * `contains proc<#contains,TableRef[A,B],A>`_ for use with the `in` @@ -916,7 +893,7 @@ proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = proc contains*[A, B](t: TableRef[A, B], key: A): bool = ## Alias of `hasKey proc<#hasKey,TableRef[A,B],A>`_ for use with - ## the `in` operator. + ## the ``in`` operator. runnableExamples: let a = {'a': 5, 'b': 9}.newTable doAssert 'b' in a == true @@ -924,8 +901,8 @@ proc contains*[A, B](t: TableRef[A, B], key: A): bool = return hasKey[A, B](t, key) -proc hasKeyOrPut*[A, B](t: TableRef[A, B], key: A, val: B): bool = - ## Returns true if `key` is in the table, otherwise inserts `value`. +proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool = + ## Returns true if ``key`` is in the table, otherwise inserts ``value``. ## ## See also: ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_ @@ -945,8 +922,8 @@ proc hasKeyOrPut*[A, B](t: TableRef[A, B], key: A, val: B): bool = t[].hasKeyOrPut(key, val) proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B = - ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the - ## default initialization value for type `B` is returned (e.g. 0 for any + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the + ## default initialization value for type ``B`` is returned (e.g. 0 for any ## integer type). ## ## See also: @@ -964,8 +941,8 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B = getOrDefault(t[], key) proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B = - ## Retrieves the value at `t[key]` if `key` is in `t`. - ## Otherwise, `default` is returned. + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. + ## Otherwise, ``default`` is returned. ## ## See also: ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key @@ -982,7 +959,7 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B = getOrDefault(t[], key, default) proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B = - ## Retrieves value at `t[key]` or puts `val` if not present, either way + ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way ## returning a value which can be modified. ## ## Note that while the value returned is of type `var B`, @@ -1018,7 +995,7 @@ proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B = t[].mgetOrPut(key, val) proc len*[A, B](t: TableRef[A, B]): int = - ## Returns the number of keys in `t`. + ## Returns the number of keys in ``t``. runnableExamples: let a = {'a': 5, 'b': 9}.newTable doAssert len(a) == 2 @@ -1027,7 +1004,7 @@ proc len*[A, B](t: TableRef[A, B]): int = proc add*[A, B](t: TableRef[A, B], key: A, val: sink B) {.deprecated: "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = - ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists. + ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## ## **This can introduce duplicate keys into the table!** ## @@ -1036,10 +1013,9 @@ proc add*[A, B](t: TableRef[A, B], key: A, val: sink B) {.deprecated: t[].add(key, val) proc del*[A, B](t: TableRef[A, B], key: A) = - ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. + ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist. ## - ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), - ## this may need to be called multiple times. + ## **If duplicate keys were added, this may need to be called multiple times.** ## ## See also: ## * `pop proc<#pop,TableRef[A,B],A,B>`_ @@ -1054,13 +1030,12 @@ proc del*[A, B](t: TableRef[A, B], key: A) = t[].del(key) proc pop*[A, B](t: TableRef[A, B], key: A, val: var B): bool = - ## Deletes the `key` from the table. - ## Returns `true`, if the `key` existed, and sets `val` to the - ## mapping of the key. Otherwise, returns `false`, and the `val` is + ## Deletes the ``key`` from the table. + ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the + ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is ## unchanged. ## - ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), - ## this may need to be called multiple times. + ## **If duplicate keys were added, this may need to be called multiple times.** ## ## See also: ## * `del proc<#del,TableRef[A,B],A>`_ @@ -1099,13 +1074,13 @@ proc clear*[A, B](t: TableRef[A, B]) = clearImpl() proc `$`*[A, B](t: TableRef[A, B]): string = - ## The `$` operator for hash tables. Used internally when calling `echo` + ## The ``$`` operator for hash tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A, B](s, t: TableRef[A, B]): bool = - ## The `==` operator for hash tables. Returns `true` if either both tables - ## are `nil`, or neither is `nil` and the content of both tables contains the + ## The ``==`` operator for hash tables. Returns ``true`` if either both tables + ## are ``nil``, or neither is ``nil`` and the content of both tables contains the ## same key-value pairs. Insert order does not matter. runnableExamples: let @@ -1120,7 +1095,7 @@ proc `==`*[A, B](s, t: TableRef[A, B]): bool = iterator pairs*[A, B](t: TableRef[A, B]): (A, B) = - ## Iterates over any `(key, value)` pair in the table `t`. + ## Iterates over any ``(key, value)`` pair in the table ``t``. ## ## See also: ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_ @@ -1150,7 +1125,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) = - ## Iterates over any `(key, value)` pair in the table `t`. The values + ## Iterates over any ``(key, value)`` pair in the table ``t``. The values ## can be modified. ## ## See also: @@ -1171,8 +1146,8 @@ iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: TableRef[A, B]): lent A = - ## Iterates over any key in the table `t`. +iterator keys*[A, B](t: TableRef[A, B]): A = + ## Iterates over any key in the table ``t``. ## ## See also: ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_ @@ -1192,8 +1167,8 @@ iterator keys*[A, B](t: TableRef[A, B]): lent A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: TableRef[A, B]): lent B = - ## Iterates over any value in the table `t`. +iterator values*[A, B](t: TableRef[A, B]): B = + ## Iterates over any value in the table ``t``. ## ## See also: ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_ @@ -1214,7 +1189,7 @@ iterator values*[A, B](t: TableRef[A, B]): lent B = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A, B](t: TableRef[A, B]): var B = - ## Iterates over any value in the table `t`. The values can be modified. + ## Iterates over any value in the table ``t``. The values can be modified. ## ## See also: ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_ @@ -1328,7 +1303,7 @@ proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A, initImpl(result, initialSize) proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) = - ## Inserts a `(key, value)` pair into `t`. + ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key @@ -1344,9 +1319,9 @@ proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) = putImpl(enlarge) proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] = - ## Creates a new ordered hash table that contains the given `pairs`. + ## Creates a new ordered hash table that contains the given ``pairs``. ## - ## `pairs` is a container consisting of `(key, value)` tuples. + ## ``pairs`` is a container consisting of ``(key, value)`` tuples. ## ## See also: ## * `initOrderedTable proc<#initOrderedTable>`_ @@ -1361,9 +1336,9 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] = for key, val in items(pairs): result[key] = val proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = - ## Retrieves the value at `t[key]`. + ## Retrieves the value at ``t[key]``. ## - ## If `key` is not in `t`, the `KeyError` exception is raised. + ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. ## One can check with `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ whether ## the key exists. ## @@ -1385,9 +1360,9 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = get(t, key) proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B = - ## Retrieves the value at `t[key]`. The value can be modified. + ## Retrieves the value at ``t[key]``. The value can be modified. ## - ## If `key` is not in `t`, the `KeyError` exception is raised. + ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. ## ## See also: ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return @@ -1401,7 +1376,7 @@ proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B = get(t, key) proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = - ## Returns true if `key` is in the table `t`. + ## Returns true if ``key`` is in the table ``t``. ## ## See also: ## * `contains proc<#contains,OrderedTable[A,B],A>`_ for use with the `in` @@ -1421,7 +1396,7 @@ proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = proc contains*[A, B](t: OrderedTable[A, B], key: A): bool = ## Alias of `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for use with - ## the `in` operator. + ## the ``in`` operator. runnableExamples: let a = {'a': 5, 'b': 9}.toOrderedTable doAssert 'b' in a == true @@ -1430,7 +1405,7 @@ proc contains*[A, B](t: OrderedTable[A, B], key: A): bool = return hasKey[A, B](t, key) proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool = - ## Returns true if `key` is in the table, otherwise inserts `value`. + ## Returns true if ``key`` is in the table, otherwise inserts ``value``. ## ## See also: ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ @@ -1450,8 +1425,8 @@ proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool = hasKeyOrPutImpl(enlarge) proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B = - ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the - ## default initialization value for type `B` is returned (e.g. 0 for any + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the + ## default initialization value for type ``B`` is returned (e.g. 0 for any ## integer type). ## ## See also: @@ -1469,8 +1444,8 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B = getOrDefaultImpl(t, key) proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B = - ## Retrieves the value at `t[key]` if `key` is in `t`. - ## Otherwise, `default` is returned. + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. + ## Otherwise, ``default`` is returned. ## ## See also: ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key @@ -1487,7 +1462,7 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B = getOrDefaultImpl(t, key, default) proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B = - ## Retrieves value at `t[key]` or puts `val` if not present, either way + ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way ## returning a value which can be modified. ## ## See also: @@ -1507,7 +1482,7 @@ proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B = mgetOrPutImpl(enlarge) proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} = - ## Returns the number of keys in `t`. + ## Returns the number of keys in ``t``. runnableExamples: let a = {'a': 5, 'b': 9}.toOrderedTable doAssert len(a) == 2 @@ -1516,7 +1491,7 @@ proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} = proc add*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) {.deprecated: "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = - ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists. + ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## ## **This can introduce duplicate keys into the table!** ## @@ -1525,7 +1500,7 @@ proc add*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) {.deprecated: addImpl(enlarge) proc del*[A, B](t: var OrderedTable[A, B], key: A) = - ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. + ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist. ## ## O(n) complexity. ## @@ -1558,9 +1533,9 @@ proc del*[A, B](t: var OrderedTable[A, B], key: A) = h = nxt proc pop*[A, B](t: var OrderedTable[A, B], key: A, val: var B): bool {.since: (1, 1).} = - ## Deletes the `key` from the table. - ## Returns `true`, if the `key` existed, and sets `val` to the - ## mapping of the key. Otherwise, returns `false`, and the `val` is + ## Deletes the ``key`` from the table. + ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the + ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is ## unchanged. ## ## O(n) complexity. @@ -1604,15 +1579,15 @@ proc clear*[A, B](t: var OrderedTable[A, B]) = t.last = -1 proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x, y: (A, B)): int, - order = SortOrder.Ascending) {.effectsOf: cmp.} = - ## Sorts `t` according to the function `cmp`. + order = SortOrder.Ascending) = + ## Sorts ``t`` according to the function ``cmp``. ## ## This modifies the internal list ## that kept the insertion order, so insertion order is lost after this - ## call but key lookup and insertions remain possible after `sort` (in + ## call but key lookup and insertions remain possible after ``sort`` (in ## contrast to the `sort proc<#sort,CountTable[A]>`_ for count tables). runnableExamples: - import std/[algorithm] + import algorithm var a = initOrderedTable[char, int]() for i, c in "cab": a[c] = 10*i @@ -1663,12 +1638,12 @@ proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x, y: (A, B)): int, t.last = tail proc `$`*[A, B](t: OrderedTable[A, B]): string = - ## The `$` operator for ordered hash tables. Used internally when calling + ## The ``$`` operator for ordered hash tables. Used internally when calling ## `echo` on a table. dollarImpl() proc `==`*[A, B](s, t: OrderedTable[A, B]): bool = - ## The `==` operator for ordered hash tables. Returns `true` if both the + ## The ``==`` operator for ordered hash tables. Returns ``true`` if both the ## content and the order are equal. runnableExamples: let @@ -1695,7 +1670,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool = iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) = - ## Iterates over any `(key, value)` pair in the table `t` in insertion + ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion ## order. ## ## See also: @@ -1726,7 +1701,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) = - ## Iterates over any `(key, value)` pair in the table `t` (must be + ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be ## declared as `var`) in insertion order. The values can be modified. ## ## See also: @@ -1747,8 +1722,8 @@ iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: OrderedTable[A, B]): lent A = - ## Iterates over any key in the table `t` in insertion order. +iterator keys*[A, B](t: OrderedTable[A, B]): A = + ## Iterates over any key in the table ``t`` in insertion order. ## ## See also: ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_ @@ -1768,8 +1743,8 @@ iterator keys*[A, B](t: OrderedTable[A, B]): lent A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: OrderedTable[A, B]): lent B = - ## Iterates over any value in the table `t` in insertion order. +iterator values*[A, B](t: OrderedTable[A, B]): B = + ## Iterates over any value in the table ``t`` in insertion order. ## ## See also: ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_ @@ -1789,7 +1764,7 @@ iterator values*[A, B](t: OrderedTable[A, B]): lent B = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B = - ## Iterates over any value in the table `t` (must be + ## Iterates over any value in the table ``t`` (must be ## declared as `var`) in insertion order. The values ## can be modified. ## @@ -1815,7 +1790,7 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B = # --------------------------- OrderedTableRef ------------------------------- # --------------------------------------------------------------------------- -proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A, B] = +proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A, B] = ## Creates a new ordered ref hash table that is empty. ## ## See also: @@ -1828,13 +1803,12 @@ proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A a = newOrderedTable[int, string]() b = newOrderedTable[char, seq[int]]() new(result) - {.noSideEffect.}: - result[] = initOrderedTable[A, B](initialSize) + result[] = initOrderedTable[A, B](initialSize) -proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] = - ## Creates a new ordered ref hash table that contains the given `pairs`. +proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] = + ## Creates a new ordered ref hash table that contains the given ``pairs``. ## - ## `pairs` is a container consisting of `(key, value)` tuples. + ## ``pairs`` is a container consisting of ``(key, value)`` tuples. ## ## See also: ## * `newOrderedTable proc<#newOrderedTable>`_ @@ -1846,14 +1820,13 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] = assert b == {'a': 5, 'b': 9}.newOrderedTable result = newOrderedTable[A, B](pairs.len) - {.noSideEffect.}: - for key, val in items(pairs): result[key] = val + for key, val in items(pairs): result[key] = val proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = - ## Retrieves the value at `t[key]`. + ## Retrieves the value at ``t[key]``. ## - ## If `key` is not in `t`, the `KeyError` exception is raised. + ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. ## One can check with `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ whether ## the key exists. ## @@ -1874,7 +1847,7 @@ proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = result = t[][key] proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) = - ## Inserts a `(key, value)` pair into `t`. + ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key @@ -1890,7 +1863,7 @@ proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) = t[][key] = val proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool = - ## Returns true if `key` is in the table `t`. + ## Returns true if ``key`` is in the table ``t``. ## ## See also: ## * `contains proc<#contains,OrderedTableRef[A,B],A>`_ for use with the `in` @@ -1909,7 +1882,7 @@ proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool = proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool = ## Alias of `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ for use with - ## the `in` operator. + ## the ``in`` operator. runnableExamples: let a = {'a': 5, 'b': 9}.newOrderedTable doAssert 'b' in a == true @@ -1917,8 +1890,8 @@ proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool = return hasKey[A, B](t, key) -proc hasKeyOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): bool = - ## Returns true if `key` is in the table, otherwise inserts `value`. +proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool = + ## Returns true if ``key`` is in the table, otherwise inserts ``value``. ## ## See also: ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ @@ -1938,8 +1911,8 @@ proc hasKeyOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): bool = result = t[].hasKeyOrPut(key, val) proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B = - ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the - ## default initialization value for type `B` is returned (e.g. 0 for any + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the + ## default initialization value for type ``B`` is returned (e.g. 0 for any ## integer type). ## ## See also: @@ -1957,8 +1930,8 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B = getOrDefault(t[], key) proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B = - ## Retrieves the value at `t[key]` if `key` is in `t`. - ## Otherwise, `default` is returned. + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. + ## Otherwise, ``default`` is returned. ## ## See also: ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key @@ -1975,7 +1948,7 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B = getOrDefault(t[], key, default) proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B = - ## Retrieves value at `t[key]` or puts `val` if not present, either way + ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way ## returning a value which can be modified. ## ## See also: @@ -1995,7 +1968,7 @@ proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B = result = t[].mgetOrPut(key, val) proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} = - ## Returns the number of keys in `t`. + ## Returns the number of keys in ``t``. runnableExamples: let a = {'a': 5, 'b': 9}.newOrderedTable doAssert len(a) == 2 @@ -2004,7 +1977,7 @@ proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} = proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) {.deprecated: "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = - ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists. + ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## ## **This can introduce duplicate keys into the table!** ## @@ -2013,7 +1986,7 @@ proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) {.deprecated: t[].add(key, val) proc del*[A, B](t: OrderedTableRef[A, B], key: A) = - ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. + ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist. ## ## See also: ## * `clear proc<#clear,OrderedTableRef[A,B]>`_ to empty the whole table @@ -2027,9 +2000,9 @@ proc del*[A, B](t: OrderedTableRef[A, B], key: A) = t[].del(key) proc pop*[A, B](t: OrderedTableRef[A, B], key: A, val: var B): bool {.since: (1, 1).} = - ## Deletes the `key` from the table. - ## Returns `true`, if the `key` existed, and sets `val` to the - ## mapping of the key. Otherwise, returns `false`, and the `val` is + ## Deletes the ``key`` from the table. + ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the + ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is ## unchanged. ## ## See also: @@ -2063,15 +2036,15 @@ proc clear*[A, B](t: OrderedTableRef[A, B]) = clear(t[]) proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x, y: (A, B)): int, - order = SortOrder.Ascending) {.effectsOf: cmp.} = - ## Sorts `t` according to the function `cmp`. + order = SortOrder.Ascending) = + ## Sorts ``t`` according to the function ``cmp``. ## ## This modifies the internal list ## that kept the insertion order, so insertion order is lost after this - ## call but key lookup and insertions remain possible after `sort` (in + ## call but key lookup and insertions remain possible after ``sort`` (in ## contrast to the `sort proc<#sort,CountTableRef[A]>`_ for count tables). runnableExamples: - import std/[algorithm] + import algorithm var a = newOrderedTable[char, int]() for i, c in "cab": a[c] = 10*i @@ -2084,13 +2057,13 @@ proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x, y: (A, B)): int, t[].sort(cmp, order = order) proc `$`*[A, B](t: OrderedTableRef[A, B]): string = - ## The `$` operator for hash tables. Used internally when calling `echo` + ## The ``$`` operator for hash tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool = - ## The `==` operator for ordered hash tables. Returns true if either both - ## tables are `nil`, or neither is `nil` and the content and the order of + ## The ``==`` operator for ordered hash tables. Returns true if either both + ## tables are ``nil``, or neither is ``nil`` and the content and the order of ## both are equal. runnableExamples: let @@ -2105,7 +2078,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool = iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) = - ## Iterates over any `(key, value)` pair in the table `t` in insertion + ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion ## order. ## ## See also: @@ -2136,7 +2109,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) = - ## Iterates over any `(key, value)` pair in the table `t` in insertion + ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion ## order. The values can be modified. ## ## See also: @@ -2157,8 +2130,8 @@ iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: OrderedTableRef[A, B]): lent A = - ## Iterates over any key in the table `t` in insertion order. +iterator keys*[A, B](t: OrderedTableRef[A, B]): A = + ## Iterates over any key in the table ``t`` in insertion order. ## ## See also: ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_ @@ -2178,8 +2151,8 @@ iterator keys*[A, B](t: OrderedTableRef[A, B]): lent A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: OrderedTableRef[A, B]): lent B = - ## Iterates over any value in the table `t` in insertion order. +iterator values*[A, B](t: OrderedTableRef[A, B]): B = + ## Iterates over any value in the table ``t`` in insertion order. ## ## See also: ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_ @@ -2199,7 +2172,7 @@ iterator values*[A, B](t: OrderedTableRef[A, B]): lent B = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B = - ## Iterates over any value in the table `t` in insertion order. The values + ## Iterates over any value in the table ``t`` in insertion order. The values ## can be modified. ## ## See also: @@ -2292,18 +2265,19 @@ proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] = initImpl(result, initialSize) proc toCountTable*[A](keys: openArray[A]): CountTable[A] = - ## Creates a new count table with every member of a container `keys` + ## Creates a new count table with every member of a container ``keys`` ## having a count of how many times it occurs in that container. result = initCountTable[A](keys.len) for key in items(keys): result.inc(key) proc `[]`*[A](t: CountTable[A], key: A): int = - ## Retrieves the value at `t[key]` if `key` is in `t`. - ## Otherwise `0` is returned. + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. + ## Otherwise ``0`` is returned. ## ## See also: ## * `getOrDefault<#getOrDefault,CountTable[A],A,int>`_ to return ## a custom value if the key doesn't exist + ## * `mget proc<#mget,CountTable[A],A>`_ ## * `[]= proc<#[]%3D,CountTable[A],A,int>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,CountTable[A],A>`_ for checking if a key @@ -2316,7 +2290,7 @@ template cntCellEmpty(i) = t.data[i].val == 0 template cntCellHash(i) = hash(t.data[i].key) proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = - ## Inserts a `(key, value)` pair into `t`. + ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key @@ -2334,7 +2308,7 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = insertImpl() proc inc*[A](t: var CountTable[A], key: A, val = 1) = - ## Increments `t[key]` by `val` (default: 1). + ## Increments ``t[key]`` by ``val`` (default: 1). runnableExamples: var a = toCountTable("aab") a.inc('a') @@ -2352,11 +2326,11 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) = insertImpl() proc len*[A](t: CountTable[A]): int = - ## Returns the number of keys in `t`. + ## Returns the number of keys in ``t``. result = t.counter proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] = - ## Returns the `(key, value)` pair with the smallest `val`. Efficiency: O(n) + ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n) ## ## See also: ## * `largest proc<#largest,CountTable[A]>`_ @@ -2369,7 +2343,7 @@ proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] = result.val = t.data[minIdx].val proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] = - ## Returns the `(key, value)` pair with the largest `val`. Efficiency: O(n) + ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n) ## ## See also: ## * `smallest proc<#smallest,CountTable[A]>`_ @@ -2381,7 +2355,7 @@ proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] = result.val = t.data[maxIdx].val proc hasKey*[A](t: CountTable[A], key: A): bool = - ## Returns true if `key` is in the table `t`. + ## Returns true if ``key`` is in the table ``t``. ## ## See also: ## * `contains proc<#contains,CountTable[A],A>`_ for use with the `in` @@ -2394,12 +2368,12 @@ proc hasKey*[A](t: CountTable[A], key: A): bool = proc contains*[A](t: CountTable[A], key: A): bool = ## Alias of `hasKey proc<#hasKey,CountTable[A],A>`_ for use with - ## the `in` operator. + ## the ``in`` operator. return hasKey[A](t, key) proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int = - ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the - ## integer value of `default` is returned. + ## Retrieves the value at ``t[key]`` if``key`` is in ``t``. Otherwise, the + ## integer value of ``default`` is returned. ## ## See also: ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key @@ -2408,7 +2382,7 @@ proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int = ctget(t, key, default) proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} = - ## Deletes `key` from table `t`. Does nothing if the key does not exist. + ## Deletes ``key`` from table ``t``. Does nothing if the key does not exist. ## ## See also: ## * `pop proc<#pop,CountTable[A],A,int>`_ @@ -2425,9 +2399,9 @@ proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} = delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash) proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).} = - ## Deletes the `key` from the table. - ## Returns `true`, if the `key` existed, and sets `val` to the - ## mapping of the key. Otherwise, returns `false`, and the `val` is + ## Deletes the ``key`` from the table. + ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the + ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is ## unchanged. ## ## See also: @@ -2464,13 +2438,13 @@ proc sort*[A](t: var CountTable[A], order = SortOrder.Descending) = ## Sorts the count table so that, by default, the entry with the ## highest counter comes first. ## - ## .. warning:: This is destructive! Once sorted, you must not modify `t` afterwards! + ## **WARNING:** This is destructive! Once sorted, you must not modify ``t`` afterwards! ## ## You can use the iterators `pairs<#pairs.i,CountTable[A]>`_, ## `keys<#keys.i,CountTable[A]>`_, and `values<#values.i,CountTable[A]>`_ - ## to iterate over `t` in the sorted order. + ## to iterate over ``t`` in the sorted order. runnableExamples: - import std/[algorithm, sequtils] + import algorithm, sequtils var a = toCountTable("abracadabra") doAssert a == "aaaaabbrrcd".toCountTable a.sort() @@ -2508,18 +2482,18 @@ when (NimMajor, NimMinor) <= (1, 0): result.inc(key, value) proc `$`*[A](t: CountTable[A]): string = - ## The `$` operator for count tables. Used internally when calling `echo` + ## The ``$`` operator for count tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A](s, t: CountTable[A]): bool = - ## The `==` operator for count tables. Returns `true` if both tables + ## The ``==`` operator for count tables. Returns ``true`` if both tables ## contain the same keys with the same count. Insert order does not matter. equalsImpl(s, t) iterator pairs*[A](t: CountTable[A]): (A, int) = - ## Iterates over any `(key, value)` pair in the table `t`. + ## Iterates over any ``(key, value)`` pair in the table ``t``. ## ## See also: ## * `mpairs iterator<#mpairs.i,CountTable[A]>`_ @@ -2552,7 +2526,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A](t: var CountTable[A]): (A, var int) = - ## Iterates over any `(key, value)` pair in the table `t` (must be + ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be ## declared as `var`). The values can be modified. ## ## See also: @@ -2570,8 +2544,8 @@ iterator mpairs*[A](t: var CountTable[A]): (A, var int) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A](t: CountTable[A]): lent A = - ## Iterates over any key in the table `t`. +iterator keys*[A](t: CountTable[A]): A = + ## Iterates over any key in the table ``t``. ## ## See also: ## * `pairs iterator<#pairs.i,CountTable[A]>`_ @@ -2589,7 +2563,7 @@ iterator keys*[A](t: CountTable[A]): lent A = assert(len(t) == L, "the length of the table changed while iterating over it") iterator values*[A](t: CountTable[A]): int = - ## Iterates over any value in the table `t`. + ## Iterates over any value in the table ``t``. ## ## See also: ## * `pairs iterator<#pairs.i,CountTable[A]>`_ @@ -2607,7 +2581,7 @@ iterator values*[A](t: CountTable[A]): int = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A](t: var CountTable[A]): var int = - ## Iterates over any value in the table `t` (must be + ## Iterates over any value in the table ``t`` (must be ## declared as `var`). The values can be modified. ## ## See also: @@ -2637,7 +2611,7 @@ iterator mvalues*[A](t: var CountTable[A]): var int = proc inc*[A](t: CountTableRef[A], key: A, val = 1) -proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = +proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = ## Creates a new ref count table that is empty. ## ## See also: @@ -2646,19 +2620,17 @@ proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = ## * `initCountTable proc<#initCountTable>`_ for creating a ## `CountTable` new(result) - {.noSideEffect.}: - result[] = initCountTable[A](initialSize) + result[] = initCountTable[A](initialSize) -proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] = - ## Creates a new ref count table with every member of a container `keys` +proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] = + ## Creates a new ref count table with every member of a container ``keys`` ## having a count of how many times it occurs in that container. result = newCountTable[A](keys.len) - {.noSideEffect.}: - for key in items(keys): result.inc(key) + for key in items(keys): result.inc(key) proc `[]`*[A](t: CountTableRef[A], key: A): int = - ## Retrieves the value at `t[key]` if `key` is in `t`. - ## Otherwise `0` is returned. + ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. + ## Otherwise ``0`` is returned. ## ## See also: ## * `getOrDefault<#getOrDefault,CountTableRef[A],A,int>`_ to return @@ -2671,42 +2643,40 @@ proc `[]`*[A](t: CountTableRef[A], key: A): int = result = t[][key] proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) = - ## Inserts a `(key, value)` pair into `t`. + ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key ## * `inc proc<#inc,CountTableRef[A],A,int>`_ for incrementing a ## value of a key assert val > 0 - {.noSideEffect.}: - t[][key] = val + t[][key] = val proc inc*[A](t: CountTableRef[A], key: A, val = 1) = - ## Increments `t[key]` by `val` (default: 1). + ## Increments ``t[key]`` by ``val`` (default: 1). runnableExamples: var a = newCountTable("aab") a.inc('a') a.inc('b', 10) doAssert a == newCountTable("aaabbbbbbbbbbb") - {.noSideEffect.}: - t[].inc(key, val) + t[].inc(key, val) proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: int] = - ## Returns the `(key, value)` pair with the smallest `val`. Efficiency: O(n) + ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n) ## ## See also: ## * `largest proc<#largest,CountTableRef[A]>`_ t[].smallest proc largest*[A](t: CountTableRef[A]): tuple[key: A, val: int] = - ## Returns the `(key, value)` pair with the largest `val`. Efficiency: O(n) + ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n) ## ## See also: ## * `smallest proc<#smallest,CountTable[A]>`_ t[].largest proc hasKey*[A](t: CountTableRef[A], key: A): bool = - ## Returns true if `key` is in the table `t`. + ## Returns true if ``key`` is in the table ``t``. ## ## See also: ## * `contains proc<#contains,CountTableRef[A],A>`_ for use with the `in` @@ -2718,12 +2688,12 @@ proc hasKey*[A](t: CountTableRef[A], key: A): bool = proc contains*[A](t: CountTableRef[A], key: A): bool = ## Alias of `hasKey proc<#hasKey,CountTableRef[A],A>`_ for use with - ## the `in` operator. + ## the ``in`` operator. return hasKey[A](t, key) proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int = - ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the - ## integer value of `default` is returned. + ## Retrieves the value at ``t[key]`` if``key`` is in ``t``. Otherwise, the + ## integer value of ``default`` is returned. ## ## See also: ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key @@ -2732,11 +2702,11 @@ proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int = result = t[].getOrDefault(key, default) proc len*[A](t: CountTableRef[A]): int = - ## Returns the number of keys in `t`. + ## Returns the number of keys in ``t``. result = t.counter proc del*[A](t: CountTableRef[A], key: A) {.since: (1, 1).} = - ## Deletes `key` from table `t`. Does nothing if the key does not exist. + ## Deletes ``key`` from table ``t``. Does nothing if the key does not exist. ## ## See also: ## * `pop proc<#pop,CountTableRef[A],A,int>`_ @@ -2744,9 +2714,9 @@ proc del*[A](t: CountTableRef[A], key: A) {.since: (1, 1).} = del(t[], key) proc pop*[A](t: CountTableRef[A], key: A, val: var int): bool {.since: (1, 1).} = - ## Deletes the `key` from the table. - ## Returns `true`, if the `key` existed, and sets `val` to the - ## mapping of the key. Otherwise, returns `false`, and the `val` is + ## Deletes the ``key`` from the table. + ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the + ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is ## unchanged. ## ## See also: @@ -2770,7 +2740,7 @@ proc sort*[A](t: CountTableRef[A], order = SortOrder.Descending) = ## ## You can use the iterators `pairs<#pairs.i,CountTableRef[A]>`_, ## `keys<#keys.i,CountTableRef[A]>`_, and `values<#values.i,CountTableRef[A]>`_ - ## to iterate over `t` in the sorted order. + ## to iterate over ``t`` in the sorted order. t[].sort(order = order) proc merge*[A](s, t: CountTableRef[A]) = @@ -2785,13 +2755,13 @@ proc merge*[A](s, t: CountTableRef[A]) = s[].merge(t[]) proc `$`*[A](t: CountTableRef[A]): string = - ## The `$` operator for count tables. Used internally when calling `echo` + ## The ``$`` operator for count tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A](s, t: CountTableRef[A]): bool = - ## The `==` operator for count tables. Returns `true` if either both tables - ## are `nil`, or neither is `nil` and both contain the same keys with the same + ## The ``==`` operator for count tables. Returns ``true`` if either both tables + ## are ``nil``, or neither is ``nil`` and both contain the same keys with the same ## count. Insert order does not matter. if isNil(s): result = isNil(t) elif isNil(t): result = false @@ -2799,7 +2769,7 @@ proc `==`*[A](s, t: CountTableRef[A]): bool = iterator pairs*[A](t: CountTableRef[A]): (A, int) = - ## Iterates over any `(key, value)` pair in the table `t`. + ## Iterates over any ``(key, value)`` pair in the table ``t``. ## ## See also: ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_ @@ -2832,7 +2802,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A](t: CountTableRef[A]): (A, var int) = - ## Iterates over any `(key, value)` pair in the table `t`. The values can + ## Iterates over any ``(key, value)`` pair in the table ``t``. The values can ## be modified. ## ## See also: @@ -2851,7 +2821,7 @@ iterator mpairs*[A](t: CountTableRef[A]): (A, var int) = assert(len(t) == L, "table modified while iterating over it") iterator keys*[A](t: CountTableRef[A]): A = - ## Iterates over any key in the table `t`. + ## Iterates over any key in the table ``t``. ## ## See also: ## * `pairs iterator<#pairs.i,CountTable[A]>`_ @@ -2869,7 +2839,7 @@ iterator keys*[A](t: CountTableRef[A]): A = assert(len(t) == L, "the length of the table changed while iterating over it") iterator values*[A](t: CountTableRef[A]): int = - ## Iterates over any value in the table `t`. + ## Iterates over any value in the table ``t``. ## ## See also: ## * `pairs iterator<#pairs.i,CountTableRef[A]>`_ @@ -2887,7 +2857,7 @@ iterator values*[A](t: CountTableRef[A]): int = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A](t: CountTableRef[A]): var int = - ## Iterates over any value in the table `t`. The values can be modified. + ## Iterates over any value in the table ``t``. The values can be modified. ## ## See also: ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_ diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index b688fad54..57b854103 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -6,8 +6,7 @@ # distribution, for details about the copyright. # -## This module implements color handling for Nim, -## namely color mixing and parsing the CSS color names. +## This module implements color handling for Nim. import strutils from algorithm import binarySearch @@ -15,7 +14,7 @@ from algorithm import binarySearch type Color* = distinct int ## A color stored as RGB, e.g. `0xff00cc`. -proc `==`*(a, b: Color): bool {.borrow.} +proc `==` *(a, b: Color): bool {.borrow.} ## Compares two colors. ## ## .. code-block:: @@ -24,7 +23,7 @@ proc `==`*(a, b: Color): bool {.borrow.} ## b = colFuchsia ## c = Color(0x00_ff_cc) ## assert a == b - ## assert not (a == c) + ## assert not a == c template extract(a: Color, r, g, b: untyped) = var r = a.int shr 16 and 0xff @@ -171,7 +170,6 @@ const colDarkGoldenRod* = Color(0xB8860B) colDarkGray* = Color(0xA9A9A9) colDarkGreen* = Color(0x006400) - colDarkGrey* = Color(0xA9A9A9) colDarkKhaki* = Color(0xBDB76B) colDarkMagenta* = Color(0x8B008B) colDarkOliveGreen* = Color(0x556B2F) @@ -182,13 +180,11 @@ const colDarkSeaGreen* = Color(0x8FBC8F) colDarkSlateBlue* = Color(0x483D8B) colDarkSlateGray* = Color(0x2F4F4F) - colDarkSlateGrey* = Color(0x2F4F4F) colDarkTurquoise* = Color(0x00CED1) colDarkViolet* = Color(0x9400D3) colDeepPink* = Color(0xFF1493) colDeepSkyBlue* = Color(0x00BFFF) colDimGray* = Color(0x696969) - colDimGrey* = Color(0x696969) colDodgerBlue* = Color(0x1E90FF) colFireBrick* = Color(0xB22222) colFloralWhite* = Color(0xFFFAF0) @@ -200,7 +196,6 @@ const colGoldenRod* = Color(0xDAA520) colGray* = Color(0x808080) colGreen* = Color(0x008000) - colGrey* = Color(0x808080) colGreenYellow* = Color(0xADFF2F) colHoneyDew* = Color(0xF0FFF0) colHotPink* = Color(0xFF69B4) @@ -216,15 +211,13 @@ const colLightCoral* = Color(0xF08080) colLightCyan* = Color(0xE0FFFF) colLightGoldenRodYellow* = Color(0xFAFAD2) - colLightGray* = Color(0xD3D3D3) - colLightGreen* = Color(0x90EE90) colLightGrey* = Color(0xD3D3D3) + colLightGreen* = Color(0x90EE90) colLightPink* = Color(0xFFB6C1) colLightSalmon* = Color(0xFFA07A) colLightSeaGreen* = Color(0x20B2AA) colLightSkyBlue* = Color(0x87CEFA) colLightSlateGray* = Color(0x778899) - colLightSlateGrey* = Color(0x778899) colLightSteelBlue* = Color(0xB0C4DE) colLightYellow* = Color(0xFFFFE0) colLime* = Color(0x00FF00) @@ -235,7 +228,7 @@ const colMediumAquaMarine* = Color(0x66CDAA) colMediumBlue* = Color(0x0000CD) colMediumOrchid* = Color(0xBA55D3) - colMediumPurple* = Color(0x9370DB) + colMediumPurple* = Color(0x9370D8) colMediumSeaGreen* = Color(0x3CB371) colMediumSlateBlue* = Color(0x7B68EE) colMediumSpringGreen* = Color(0x00FA9A) @@ -256,7 +249,7 @@ const colPaleGoldenRod* = Color(0xEEE8AA) colPaleGreen* = Color(0x98FB98) colPaleTurquoise* = Color(0xAFEEEE) - colPaleVioletRed* = Color(0xDB7093) + colPaleVioletRed* = Color(0xD87093) colPapayaWhip* = Color(0xFFEFD5) colPeachPuff* = Color(0xFFDAB9) colPeru* = Color(0xCD853F) @@ -264,7 +257,6 @@ const colPlum* = Color(0xDDA0DD) colPowderBlue* = Color(0xB0E0E6) colPurple* = Color(0x800080) - colRebeccaPurple* = Color(0x663399) colRed* = Color(0xFF0000) colRosyBrown* = Color(0xBC8F8F) colRoyalBlue* = Color(0x4169E1) @@ -278,7 +270,6 @@ const colSkyBlue* = Color(0x87CEEB) colSlateBlue* = Color(0x6A5ACD) colSlateGray* = Color(0x708090) - colSlateGrey* = Color(0x708090) colSnow* = Color(0xFFFAFA) colSpringGreen* = Color(0x00FF7F) colSteelBlue* = Color(0x4682B4) @@ -294,155 +285,147 @@ const colYellow* = Color(0xFFFF00) colYellowGreen* = Color(0x9ACD32) - colorNames = { - "aliceblue": colAliceBlue, - "antiquewhite": colAntiqueWhite, - "aqua": colAqua, - "aquamarine": colAquamarine, - "azure": colAzure, - "beige": colBeige, - "bisque": colBisque, - "black": colBlack, - "blanchedalmond": colBlanchedAlmond, - "blue": colBlue, - "blueviolet": colBlueViolet, - "brown": colBrown, - "burlywood": colBurlyWood, - "cadetblue": colCadetBlue, - "chartreuse": colChartreuse, - "chocolate": colChocolate, - "coral": colCoral, - "cornflowerblue": colCornflowerBlue, - "cornsilk": colCornsilk, - "crimson": colCrimson, - "cyan": colCyan, - "darkblue": colDarkBlue, - "darkcyan": colDarkCyan, - "darkgoldenrod": colDarkGoldenRod, - "darkgray": colDarkGray, - "darkgreen": colDarkGreen, - "darkgrey": colDarkGrey, - "darkkhaki": colDarkKhaki, - "darkmagenta": colDarkMagenta, - "darkolivegreen": colDarkOliveGreen, - "darkorange": colDarkorange, - "darkorchid": colDarkOrchid, - "darkred": colDarkRed, - "darksalmon": colDarkSalmon, - "darkseagreen": colDarkSeaGreen, - "darkslateblue": colDarkSlateBlue, - "darkslategray": colDarkSlateGray, - "darkslategrey": colDarkSlateGrey, - "darkturquoise": colDarkTurquoise, - "darkviolet": colDarkViolet, - "deeppink": colDeepPink, - "deepskyblue": colDeepSkyBlue, - "dimgray": colDimGray, - "dimgrey": colDimGrey, - "dodgerblue": colDodgerBlue, - "firebrick": colFireBrick, - "floralwhite": colFloralWhite, - "forestgreen": colForestGreen, - "fuchsia": colFuchsia, - "gainsboro": colGainsboro, - "ghostwhite": colGhostWhite, - "gold": colGold, - "goldenrod": colGoldenRod, - "gray": colGray, - "green": colGreen, - "grey": colGrey, - "greenyellow": colGreenYellow, - "honeydew": colHoneyDew, - "hotpink": colHotPink, - "indianred": colIndianRed, - "indigo": colIndigo, - "ivory": colIvory, - "khaki": colKhaki, - "lavender": colLavender, - "lavenderblush": colLavenderBlush, - "lawngreen": colLawnGreen, - "lemonchiffon": colLemonChiffon, - "lightblue": colLightBlue, - "lightcoral": colLightCoral, - "lightcyan": colLightCyan, - "lightgoldenrodyellow": colLightGoldenRodYellow, - "lightgray": colLightGray, - "lightgreen": colLightGreen, - "lightgrey": colLightGrey, - "lightpink": colLightPink, - "lightsalmon": colLightSalmon, - "lightseagreen": colLightSeaGreen, - "lightskyblue": colLightSkyBlue, - "lightslategray": colLightSlateGray, - "lightslategrey": colLightSlateGrey, - "lightsteelblue": colLightSteelBlue, - "lightyellow": colLightYellow, - "lime": colLime, - "limegreen": colLimeGreen, - "linen": colLinen, - "magenta": colMagenta, - "maroon": colMaroon, - "mediumaquamarine": colMediumAquaMarine, - "mediumblue": colMediumBlue, - "mediumorchid": colMediumOrchid, - "mediumpurple": colMediumPurple, - "mediumseagreen": colMediumSeaGreen, - "mediumslateblue": colMediumSlateBlue, - "mediumspringgreen": colMediumSpringGreen, - "mediumturquoise": colMediumTurquoise, - "mediumvioletred": colMediumVioletRed, - "midnightblue": colMidnightBlue, - "mintcream": colMintCream, - "mistyrose": colMistyRose, - "moccasin": colMoccasin, - "navajowhite": colNavajoWhite, - "navy": colNavy, - "oldlace": colOldLace, - "olive": colOlive, - "olivedrab": colOliveDrab, - "orange": colOrange, - "orangered": colOrangeRed, - "orchid": colOrchid, - "palegoldenrod": colPaleGoldenRod, - "palegreen": colPaleGreen, - "paleturquoise": colPaleTurquoise, - "palevioletred": colPaleVioletRed, - "papayawhip": colPapayaWhip, - "peachpuff": colPeachPuff, - "peru": colPeru, - "pink": colPink, - "plum": colPlum, - "powderblue": colPowderBlue, - "purple": colPurple, - "rebeccapurple": colRebeccaPurple, - "red": colRed, - "rosybrown": colRosyBrown, - "royalblue": colRoyalBlue, - "saddlebrown": colSaddleBrown, - "salmon": colSalmon, - "sandybrown": colSandyBrown, - "seagreen": colSeaGreen, - "seashell": colSeaShell, - "sienna": colSienna, - "silver": colSilver, - "skyblue": colSkyBlue, - "slateblue": colSlateBlue, - "slategray": colSlateGray, - "slategrey": colSlateGrey, - "snow": colSnow, - "springgreen": colSpringGreen, - "steelblue": colSteelBlue, - "tan": colTan, - "teal": colTeal, - "thistle": colThistle, - "tomato": colTomato, - "turquoise": colTurquoise, - "violet": colViolet, - "wheat": colWheat, - "white": colWhite, - "whitesmoke": colWhiteSmoke, - "yellow": colYellow, - "yellowgreen": colYellowGreen} + colorNames = [ + ("aliceblue", colAliceBlue), + ("antiquewhite", colAntiqueWhite), + ("aqua", colAqua), + ("aquamarine", colAquamarine), + ("azure", colAzure), + ("beige", colBeige), + ("bisque", colBisque), + ("black", colBlack), + ("blanchedalmond", colBlanchedAlmond), + ("blue", colBlue), + ("blueviolet", colBlueViolet), + ("brown", colBrown), + ("burlywood", colBurlyWood), + ("cadetblue", colCadetBlue), + ("chartreuse", colChartreuse), + ("chocolate", colChocolate), + ("coral", colCoral), + ("cornflowerblue", colCornflowerBlue), + ("cornsilk", colCornsilk), + ("crimson", colCrimson), + ("cyan", colCyan), + ("darkblue", colDarkBlue), + ("darkcyan", colDarkCyan), + ("darkgoldenrod", colDarkGoldenRod), + ("darkgray", colDarkGray), + ("darkgreen", colDarkGreen), + ("darkkhaki", colDarkKhaki), + ("darkmagenta", colDarkMagenta), + ("darkolivegreen", colDarkOliveGreen), + ("darkorange", colDarkorange), + ("darkorchid", colDarkOrchid), + ("darkred", colDarkRed), + ("darksalmon", colDarkSalmon), + ("darkseagreen", colDarkSeaGreen), + ("darkslateblue", colDarkSlateBlue), + ("darkslategray", colDarkSlateGray), + ("darkturquoise", colDarkTurquoise), + ("darkviolet", colDarkViolet), + ("deeppink", colDeepPink), + ("deepskyblue", colDeepSkyBlue), + ("dimgray", colDimGray), + ("dodgerblue", colDodgerBlue), + ("firebrick", colFireBrick), + ("floralwhite", colFloralWhite), + ("forestgreen", colForestGreen), + ("fuchsia", colFuchsia), + ("gainsboro", colGainsboro), + ("ghostwhite", colGhostWhite), + ("gold", colGold), + ("goldenrod", colGoldenRod), + ("gray", colGray), + ("green", colGreen), + ("greenyellow", colGreenYellow), + ("honeydew", colHoneyDew), + ("hotpink", colHotPink), + ("indianred", colIndianRed), + ("indigo", colIndigo), + ("ivory", colIvory), + ("khaki", colKhaki), + ("lavender", colLavender), + ("lavenderblush", colLavenderBlush), + ("lawngreen", colLawnGreen), + ("lemonchiffon", colLemonChiffon), + ("lightblue", colLightBlue), + ("lightcoral", colLightCoral), + ("lightcyan", colLightCyan), + ("lightgoldenrodyellow", colLightGoldenRodYellow), + ("lightgrey", colLightGrey), + ("lightgreen", colLightGreen), + ("lightpink", colLightPink), + ("lightsalmon", colLightSalmon), + ("lightseagreen", colLightSeaGreen), + ("lightskyblue", colLightSkyBlue), + ("lightslategray", colLightSlateGray), + ("lightsteelblue", colLightSteelBlue), + ("lightyellow", colLightYellow), + ("lime", colLime), + ("limegreen", colLimeGreen), + ("linen", colLinen), + ("magenta", colMagenta), + ("maroon", colMaroon), + ("mediumaquamarine", colMediumAquaMarine), + ("mediumblue", colMediumBlue), + ("mediumorchid", colMediumOrchid), + ("mediumpurple", colMediumPurple), + ("mediumseagreen", colMediumSeaGreen), + ("mediumslateblue", colMediumSlateBlue), + ("mediumspringgreen", colMediumSpringGreen), + ("mediumturquoise", colMediumTurquoise), + ("mediumvioletred", colMediumVioletRed), + ("midnightblue", colMidnightBlue), + ("mintcream", colMintCream), + ("mistyrose", colMistyRose), + ("moccasin", colMoccasin), + ("navajowhite", colNavajoWhite), + ("navy", colNavy), + ("oldlace", colOldLace), + ("olive", colOlive), + ("olivedrab", colOliveDrab), + ("orange", colOrange), + ("orangered", colOrangeRed), + ("orchid", colOrchid), + ("palegoldenrod", colPaleGoldenRod), + ("palegreen", colPaleGreen), + ("paleturquoise", colPaleTurquoise), + ("palevioletred", colPaleVioletRed), + ("papayawhip", colPapayaWhip), + ("peachpuff", colPeachPuff), + ("peru", colPeru), + ("pink", colPink), + ("plum", colPlum), + ("powderblue", colPowderBlue), + ("purple", colPurple), + ("red", colRed), + ("rosybrown", colRosyBrown), + ("royalblue", colRoyalBlue), + ("saddlebrown", colSaddleBrown), + ("salmon", colSalmon), + ("sandybrown", colSandyBrown), + ("seagreen", colSeaGreen), + ("seashell", colSeaShell), + ("sienna", colSienna), + ("silver", colSilver), + ("skyblue", colSkyBlue), + ("slateblue", colSlateBlue), + ("slategray", colSlateGray), + ("snow", colSnow), + ("springgreen", colSpringGreen), + ("steelblue", colSteelBlue), + ("tan", colTan), + ("teal", colTeal), + ("thistle", colThistle), + ("tomato", colTomato), + ("turquoise", colTurquoise), + ("violet", colViolet), + ("wheat", colWheat), + ("white", colWhite), + ("whitesmoke", colWhiteSmoke), + ("yellow", colYellow), + ("yellowgreen", colYellowGreen)] proc `$`*(c: Color): string = ## Converts a color into its textual representation. @@ -457,7 +440,7 @@ proc colorNameCmp(x: tuple[name: string, col: Color], y: string): int = proc parseColor*(name: string): Color = ## Parses `name` to a color value. ## - ## If no valid color could be parsed `ValueError` is raised. + ## If no valid color could be parsed ``ValueError`` is raised. ## Case insensitive. ## runnableExamples: @@ -478,7 +461,7 @@ proc parseColor*(name: string): Color = proc isColor*(name: string): bool = ## Returns true if `name` is a known color name or a hexadecimal color - ## prefixed with `#`. Case insensitive. + ## prefixed with ``#``. Case insensitive. ## runnableExamples: var diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 1e8349ef6..b9371c1e1 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -36,7 +36,7 @@ runnableExamples: {.push checks: off, line_dir: off, stack_trace: off, debugger: off.} # the user does not want to trace a part of the standard library! -import math +import std/math type Complex*[T: SomeFloat] = object @@ -81,13 +81,6 @@ 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)`). @@ -163,7 +156,18 @@ func `/`*[T](x: T; y: Complex[T]): Complex[T] = func `/`*[T](x, y: Complex[T]): Complex[T] = ## Divides two complex numbers. - x * conjugate(y) / abs2(y) + 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 + 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 c7b881bc5..895944177 100644 --- a/lib/pure/concurrency/atomics.nim +++ b/lib/pure/concurrency/atomics.nim @@ -51,6 +51,8 @@ runnableExamples: assert not flag.testAndSet +import macros + when defined(cpp) or defined(nimdoc): # For the C++ backend, types and operations map directly to C++11 atomics. @@ -211,8 +213,8 @@ else: # MSVC intrinsics proc interlockedExchange(location: pointer; desired: int8): int8 {.importc: "_InterlockedExchange8".} - proc interlockedExchange(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange16".} - proc interlockedExchange(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange".} + proc interlockedExchange(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange".} + proc interlockedExchange(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange16".} proc interlockedExchange(location: pointer; desired: int64): int64 {.importc: "_InterlockedExchange64".} proc interlockedCompareExchange(location: pointer; desired, expected: int8): int8 {.importc: "_InterlockedCompareExchange8".} @@ -286,23 +288,26 @@ else: moSequentiallyConsistent type - # Atomic*[T] {.importcpp: "_Atomic('0)".} = object + # Atomic* {.importcpp: "_Atomic('0)".} [T] = object - AtomicInt8 {.importc: "_Atomic NI8".} = int8 - AtomicInt16 {.importc: "_Atomic NI16".} = int16 - AtomicInt32 {.importc: "_Atomic NI32".} = int32 - AtomicInt64 {.importc: "_Atomic NI64".} = int64 + AtomicInt8 {.importc: "_Atomic NI8", size: 1.} = object + AtomicInt16 {.importc: "_Atomic NI16", size: 2.} = object + AtomicInt32 {.importc: "_Atomic NI32", size: 4.} = object + AtomicInt64 {.importc: "_Atomic NI64", size: 8.} = object + + template atomicType*(T: typedesc[Trivial]): untyped = + # Maps the size of a trivial type to it's internal atomic type + when sizeof(T) == 1: AtomicInt8 + elif sizeof(T) == 2: AtomicInt16 + elif sizeof(T) == 4: AtomicInt32 + elif sizeof(T) == 8: AtomicInt64 type AtomicFlag* {.importc: "atomic_flag", size: 1.} = object Atomic*[T] = object when T is Trivial: - # Maps the size of a trivial type to it's internal atomic type - when sizeof(T) == 1: value: AtomicInt8 - elif sizeof(T) == 2: value: AtomicInt16 - elif sizeof(T) == 4: value: AtomicInt32 - elif sizeof(T) == 8: value: AtomicInt64 + value: T.atomicType else: nonAtomicValue: T guard: AtomicFlag @@ -333,7 +338,7 @@ else: {.pop.} proc load*[T: Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} = - cast[T](atomic_load_explicit[nonAtomicType(T), typeof(location.value)](addr(location.value), order)) + cast[T](atomic_load_explicit[nonAtomicType(T), type(location.value)](addr(location.value), order)) proc store*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} = atomic_store_explicit(addr(location.value), cast[nonAtomicType(T)](desired), order) proc exchange*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} = @@ -361,11 +366,11 @@ else: cast[T](atomic_fetch_xor_explicit(addr(location.value), cast[nonAtomicType(T)](value), order)) template withLock[T: not Trivial](location: var Atomic[T]; order: MemoryOrder; body: untyped): untyped = - while testAndSet(location.guard, moAcquire): discard + while location.guard.testAndSet(moAcquire): discard try: body finally: - clear(location.guard, moRelease) + location.guard.clear(moRelease) proc load*[T: not Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} = withLock(location, order): diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index 1d2ff63e1..57d13fde3 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -15,12 +15,9 @@ runnableExamples: include "system/inclrtl" -when defined(posix) and not (defined(macosx) or defined(bsd)): +when not defined(windows): import posix -when defined(windows): - import std/private/win_getsysteminfo - when defined(freebsd) or defined(macosx): {.emit: "#include ".} @@ -40,7 +37,7 @@ when defined(macosx) or defined(bsd): importc: "sysctl", nodecl.} when defined(genode): - import genode/env + include genode/env proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {. importcpp: "@->cpu().affinity_space().total()".} @@ -57,10 +54,25 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = ## Returns the number of the processors/cores the machine has. ## Returns 0 if it cannot be detected. when defined(windows): + type + SYSTEM_INFO {.final, pure.} = object + u1: int32 + dwPageSize: int32 + lpMinimumApplicationAddress: pointer + lpMaximumApplicationAddress: pointer + dwActiveProcessorMask: ptr int32 + dwNumberOfProcessors: int32 + dwProcessorType: int32 + dwAllocationGranularity: int32 + wProcessorLevel: int16 + wProcessorRevision: int16 + + proc GetSystemInfo(lpSystemInfo: var SYSTEM_INFO) {.stdcall, dynlib: "kernel32", importc: "GetSystemInfo".} + var - si: SystemInfo - getSystemInfo(addr si) - result = int(si.dwNumberOfProcessors) + si: SYSTEM_INFO + GetSystemInfo(si) + result = si.dwNumberOfProcessors elif defined(macosx) or defined(bsd): var mib: array[0..3, cint] diff --git a/lib/pure/concurrency/cpuload.nim b/lib/pure/concurrency/cpuload.nim index 137dd67ad..841d58d86 100644 --- a/lib/pure/concurrency/cpuload.nim +++ b/lib/pure/concurrency/cpuload.nim @@ -19,9 +19,6 @@ 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 0eb20dc9a..a4460165d 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -7,24 +7,21 @@ # distribution, for details about the copyright. # -## Implements Nim's `parallel & spawn statements `_. +## Implements Nim's `spawn `_. +## +## **See also:** +## * `threads module `_ +## * `channels module `_ +## * `locks module `_ +## * `asyncdispatch module `_ ## ## Unstable API. -## -## See also -## ======== -## * `threads module `_ for basic thread support -## * `locks module `_ for locks and condition variables -## * `asyncdispatch module `_ for asynchronous IO when not compileOption("threads"): {.error: "Threadpool requires --threads:on option.".} import cpuinfo, cpuload, locks, os -when defined(nimPreviewSlimSystem): - import std/[assertions, typedthreads, sysatomics] - {.push stackTrace:off.} type @@ -54,14 +51,17 @@ proc signal(cv: var Semaphore) = release(cv.L) signal(cv.c) -const CacheLineSize = 64 # true for most archs +const CacheLineSize = 32 # true for most archs type Barrier {.compilerproc.} = object entered: int cv: Semaphore # Semaphore takes 3 words at least - left {.align(CacheLineSize).}: int - interest {.align(CacheLineSize).} : bool # whether the master is interested in the "all done" event + when sizeof(int) < 8: + cacheAlign: array[CacheLineSize-4*sizeof(int), byte] + left: int + cacheAlign2: array[CacheLineSize-sizeof(int), byte] + interest: bool # whether the master is interested in the "all done" event proc barrierEnter(b: ptr Barrier) {.compilerproc, inline.} = # due to the signaling between threads, it is ensured we are the only @@ -101,8 +101,8 @@ type cv: Semaphore idx: int - FlowVarBase* = ref FlowVarBaseObj ## Untyped base class for `FlowVar[T] <#FlowVar>`_. - FlowVarBaseObj {.acyclic.} = object of RootObj + FlowVarBase* = ref FlowVarBaseObj ## Untyped base class for ``FlowVar[T]``. + FlowVarBaseObj = object of RootObj ready, usesSemaphore, awaited: bool cv: Semaphore # for 'blockUntilAny' support ai: ptr AwaitInfo @@ -111,10 +111,10 @@ type # be RootRef here otherwise the wrong GC keeps track of it! owner: pointer # ptr Worker - FlowVarObj[T] {.acyclic.} = object of FlowVarBaseObj + FlowVarObj[T] = object of FlowVarBaseObj blob: T - FlowVar*[T] {.compilerproc.} = ref FlowVarObj[T] ## A data flow variable. + FlowVar*{.compilerproc.}[T] = ref FlowVarObj[T] ## A data flow variable. ToFreeQueue = object len: int @@ -138,7 +138,7 @@ type const threadpoolWaitMs {.intdefine.}: int = 100 proc blockUntil*(fv: var FlowVarBaseObj) = - ## Waits until the value for `fv` arrives. + ## Waits until the value for the ``fv`` arrives. ## ## Usually it is not necessary to call this explicitly. if fv.usesSemaphore and not fv.awaited: @@ -230,12 +230,12 @@ proc nimFlowVarSignal(fv: FlowVarBase) {.compilerproc.} = signal(fv.cv) proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) = - ## Blocks until `fv` is available and then passes its value - ## to `action`. + ## Blocks until the ``fv`` is available and then passes its value + ## to ``action``. ## - ## Note that due to Nim's parameter passing semantics, this - ## means that `T` doesn't need to be copied, so `awaitAndThen` can - ## sometimes be more efficient than the `^ proc <#^,FlowVar[T]>`_. + ## Note that due to Nim's parameter passing semantics this + ## means that ``T`` doesn't need to be copied so ``awaitAndThen`` can + ## sometimes be more efficient than `^ proc <#^,FlowVar[T]>`_. blockUntil(fv[]) when defined(nimV2): action(fv.blob) @@ -266,15 +266,15 @@ proc `^`*[T](fv: FlowVar[T]): T = finished(fv[]) proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int = - ## Awaits any of the given `flowVars`. Returns the index of one `flowVar` + ## Awaits any of the given ``flowVars``. Returns the index of one ``flowVar`` ## for which a value arrived. ## - ## A `flowVar` only supports one call to `blockUntilAny` at the same time. - ## That means if you `blockUntilAny([a,b])` and `blockUntilAny([b,c])` - ## the second call will only block until `c`. If there is no `flowVar` left + ## A ``flowVar`` only supports one call to ``blockUntilAny`` at the same time. + ## That means if you ``blockUntilAny([a,b])`` and ``blockUntilAny([b,c])`` + ## the second call will only block until ``c``. If there is no ``flowVar`` left ## to be able to wait on, -1 is returned. ## - ## **Note:** This results in non-deterministic behaviour and should be avoided. + ## **Note**: This results in non-deterministic behaviour and should be avoided. var ai: AwaitInfo ai.cv.initSemaphore() var conflicts = 0 @@ -295,9 +295,9 @@ proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int = destroySemaphore(ai.cv) proc isReady*(fv: FlowVarBase): bool = - ## Determines whether the specified `FlowVarBase`'s value is available. + ## Determines whether the specified ``FlowVarBase``'s value is available. ## - ## If `true`, awaiting `fv` will not block. + ## If ``true``, awaiting ``fv`` will not block. if fv.usesSemaphore and not fv.awaited: acquire(fv.cv.L) result = fv.cv.counter > 0 @@ -315,7 +315,7 @@ const MaxDistinguishedThread* {.intdefine.} = 32 ## Maximum number of "distinguished" threads. type - ThreadId* = range[0..MaxDistinguishedThread-1] ## A thread identifier. + ThreadId* = range[0..MaxDistinguishedThread-1] var currentPoolSize: int @@ -402,7 +402,7 @@ proc setMinPoolSize*(size: range[1..MaxThreadPoolSize]) = proc setMaxPoolSize*(size: range[1..MaxThreadPoolSize]) = ## Sets the maximum thread pool size. The default value of this - ## is `MaxThreadPoolSize <#MaxThreadPoolSize>`_. + ## is ``MaxThreadPoolSize`` (256). maxPoolSize = size if currentPoolSize > maxPoolSize: for i in maxPoolSize..currentPoolSize-1: @@ -442,45 +442,43 @@ proc setup() = for i in 0..`_ instead. result = gSomeReady.counter > 0 -proc spawn*(call: sink typed) {.magic: "Spawn".} = - ## Always spawns a new task, so that the `call` is never executed on +proc spawn*(call: sink typed): void {.magic: "Spawn".} + ## Always spawns a new task, so that the ``call`` is never executed on ## the calling thread. ## - ## `call` has to be a proc call `p(...)` where `p` is gcsafe and has a - ## return type that is either `void` or compatible with `FlowVar[T]`. - discard "It uses `nimSpawn3` internally" + ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a + ## return type that is either ``void`` or compatible with ``FlowVar[T]``. -proc pinnedSpawn*(id: ThreadId; call: sink typed) {.magic: "Spawn".} = - ## Always spawns a new task on the worker thread with `id`, so that - ## the `call` is **always** executed on the thread. +proc pinnedSpawn*(id: ThreadId; call: sink typed): void {.magic: "Spawn".} + ## Always spawns a new task on the worker thread with ``id``, so that + ## the ``call`` is **always** executed on the thread. ## - ## `call` has to be a proc call `p(...)` where `p` is gcsafe and has a - ## return type that is either `void` or compatible with `FlowVar[T]`. - discard "It uses `nimSpawn4` internally" + ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a + ## return type that is either ``void`` or compatible with ``FlowVar[T]``. template spawnX*(call) = ## Spawns a new task if a CPU core is ready, otherwise executes the ## call in the calling thread. ## - ## Usually, it is advised to use the `spawn proc <#spawn,sinktyped>`_ - ## in order to not block the producer for an unknown amount of time. + ## Usually it is advised to use `spawn proc <#spawn,sinktyped>`_ in order to + ## not block the producer for an unknown amount of time. ## - ## `call` has to be a proc call `p(...)` where `p` is gcsafe and has a - ## return type that is either 'void' or compatible with `FlowVar[T]`. + ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a + ## return type that is either 'void' or compatible with ``FlowVar[T]``. (if preferSpawn(): spawn call else: call) proc parallel*(body: untyped) {.magic: "Parallel".} ## A parallel section can be used to execute a block in parallel. ## - ## `body` has to be in a DSL that is a particular subset of the language. + ## ``body`` has to be in a DSL that is a particular subset of the language. ## ## Please refer to `the manual `_ ## for further information. @@ -587,7 +585,7 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerproc.} = proc sync*() = - ## A simple barrier to wait for all `spawn`ed tasks. + ## A simple barrier to wait for all ``spawn``'ed tasks. ## ## If you need more elaborate waiting, you have to use an explicit barrier. while true: diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 22704e434..7e686d44f 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -9,28 +9,18 @@ ## This module implements helper procs for parsing Cookies. -import strtabs, times, options - -when defined(nimPreviewSlimSystem): - import std/assertions - - -type - SameSite* {.pure.} = enum ## The SameSite cookie attribute. - ## `Default` means that `setCookie` - ## proc will not set `SameSite` attribute. - Default, None, Lax, Strict +import strtabs, times proc parseCookies*(s: string): StringTableRef = - ## Parses cookies into a string table. + ## parses cookies into a string table. ## ## The proc is meant to parse the Cookie header set by a client, not the ## "Set-Cookie" header set by servers. - runnableExamples: - import std/strtabs - let cookieJar = parseCookies("a=1; foo=bar") - assert cookieJar["a"] == "1" - assert cookieJar["foo"] == "bar" + ## + ## Example: + ## + ## .. code-block::Nim + ## doAssert parseCookies("a=1; foo=bar") == {"a": 1, "foo": "bar"}.newStringTable result = newStringTable(modeCaseInsensitive) var i = 0 @@ -49,12 +39,9 @@ proc parseCookies*(s: string): StringTableRef = proc setCookie*(key, value: string, domain = "", path = "", expires = "", noName = false, - secure = false, httpOnly = false, - maxAge = none(int), sameSite = SameSite.Default): string = + secure = false, httpOnly = false): string = ## Creates a command in the format of - ## `Set-Cookie: key=value; Domain=...; ...` - ## - ## .. tip:: Cookies can be vulnerable. Consider setting `secure=true`, `httpOnly=true` and `sameSite=Strict`. + ## ``Set-Cookie: key=value; Domain=...; ...`` result = "" if not noName: result.add("Set-Cookie: ") result.add key & "=" & value @@ -63,19 +50,12 @@ proc setCookie*(key, value: string, domain = "", path = "", if expires != "": result.add("; Expires=" & expires) if secure: result.add("; Secure") if httpOnly: result.add("; HttpOnly") - if maxAge.isSome: result.add("; Max-Age=" & $maxAge.unsafeGet) - - if sameSite != SameSite.Default: - if sameSite == SameSite.None: - doAssert secure, "Cookies with SameSite=None must specify the Secure attribute!" - result.add("; SameSite=" & $sameSite) proc setCookie*(key, value: string, expires: DateTime|Time, domain = "", path = "", noName = false, - secure = false, httpOnly = false, - maxAge = none(int), sameSite = SameSite.Default): string = + secure = false, httpOnly = false): string = ## Creates a command in the format of - ## `Set-Cookie: key=value; Domain=...; ...` - result = setCookie(key, value, domain, path, + ## ``Set-Cookie: key=value; Domain=...; ...`` + return setCookie(key, value, domain, path, format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"), - noName, secure, httpOnly, maxAge, sameSite) + noname, secure, httpOnly) diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index 3b9bba59b..2a702b65b 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. @@ -21,8 +21,6 @@ ## ## Unstable API. -import system/coro_detection - when not nimCoroutines and not defined(nimdoc): when defined(noNimCoroutines): {.error: "Coroutines can not be used with -d:noNimCoroutines".} @@ -33,14 +31,7 @@ import os import lists include system/timers -when defined(nimPreviewSlimSystem): - import std/assertions - const defaultStackSize = 512 * 1024 -const useOrcArc = defined(gcArc) or defined(gcOrc) - -when useOrcArc: - proc nimGC_setStackBottom*(theStackBottom: pointer) = discard proc GC_addStack(bottom: pointer) {.cdecl, importc.} proc GC_removeStack(bottom: pointer) {.cdecl, importc.} @@ -68,7 +59,7 @@ else: const coroBackend = CORO_BACKEND_UCONTEXT when coroBackend == CORO_BACKEND_FIBERS: - import windows/winlean + import windows.winlean type Context = pointer @@ -194,8 +185,7 @@ proc initialize() = ctx.coroutines = initDoublyLinkedList[CoroutinePtr]() ctx.loop = Coroutine() ctx.loop.state = CORO_EXECUTING - when not useOrcArc: - ctx.ncbottom = GC_getActiveStack() + ctx.ncbottom = GC_getActiveStack() when coroBackend == CORO_BACKEND_FIBERS: ctx.loop.execContext = ConvertThreadToFiberEx(nil, FIBER_FLAG_FLOAT_SWITCH) @@ -205,8 +195,7 @@ proc switchTo(current, to: CoroutinePtr) = ## Switches execution from `current` into `to` context. to.lastRun = getTicks() # Update position of current stack so gc invoked from another stack knows how much to scan. - when not useOrcArc: - GC_setActiveStack(current.stack.bottom) + GC_setActiveStack(current.stack.bottom) nimGC_setStackBottom(current.stack.bottom) var frame = getFrameState() block: @@ -229,12 +218,11 @@ proc switchTo(current, to: CoroutinePtr) = {.error: "Invalid coroutine backend set.".} # Execution was just resumed. Restore frame information and set active stack. setFrameState(frame) - when not useOrcArc: - GC_setActiveStack(current.stack.bottom) + GC_setActiveStack(current.stack.bottom) nimGC_setStackBottom(ctx.ncbottom) proc suspend*(sleepTime: float = 0) = - ## Stops coroutine execution and resumes no sooner than after `sleeptime` seconds. + ## Stops coroutine execution and resumes no sooner than after ``sleeptime`` seconds. ## Until then other coroutines are executed. var current = getCurrent() current.sleepTime = sleepTime @@ -253,10 +241,9 @@ proc runCurrentTask() = # have to set active stack here as well. GC_removeStack() has to be called in main loop # because we still need stack available in final suspend(0) call from which we will not # return. - when not useOrcArc: - GC_addStack(sp) - # Activate current stack because we are executing in a new coroutine. - GC_setActiveStack(sp) + GC_addStack(sp) + # Activate current stack because we are executing in a new coroutine. + GC_setActiveStack(sp) current.state = CORO_EXECUTING try: current.fn() # Start coroutine execution @@ -278,11 +265,13 @@ proc start*(c: proc(), stacksize: int = defaultStackSize): CoroutineRef {.discar coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine))) coro.execContext = CreateFiberEx(stacksize, stacksize, FIBER_FLAG_FLOAT_SWITCH, - (proc(p: pointer) {.stdcall.} = runCurrentTask()), nil) + (proc(p: pointer): void {.stdcall.} = runCurrentTask()), + nil) + coro.stack.size = stacksize else: coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine) + stacksize)) - coro.stack.top = cast[pointer](cast[int](coro) + sizeof(Coroutine)) - coro.stack.bottom = cast[pointer](cast[int](coro.stack.top) + stacksize) + coro.stack.top = cast[pointer](cast[ByteAddress](coro) + sizeof(Coroutine)) + coro.stack.bottom = cast[pointer](cast[ByteAddress](coro.stack.top) + stacksize) when coroBackend == CORO_BACKEND_UCONTEXT: discard getcontext(coro.execContext) coro.execContext.uc_stack.ss_sp = coro.stack.top @@ -297,9 +286,9 @@ proc start*(c: proc(), stacksize: int = defaultStackSize): CoroutineRef {.discar return coro.reference proc run*() = + initialize() ## Starts main coroutine scheduler loop which exits when all coroutines exit. ## Calling this proc starts execution of first coroutine. - initialize() ctx.current = ctx.coroutines.head var minDelay: float = 0 while ctx.current != nil: @@ -323,8 +312,7 @@ proc run*() = next = ctx.current.next current.reference.coro = nil ctx.coroutines.remove(ctx.current) - when not useOrcArc: - GC_removeStack(current.stack.bottom) + GC_removeStack(current.stack.bottom) when coroBackend == CORO_BACKEND_FIBERS: DeleteFiber(current.execContext) else: @@ -338,9 +326,9 @@ proc run*() = ctx.current = ctx.current.next proc alive*(c: CoroutineRef): bool = c.coro != nil and c.coro.state != CORO_FINISHED - ## Returns `true` if coroutine has not returned, `false` otherwise. + ## Returns ``true`` if coroutine has not returned, ``false`` otherwise. proc wait*(c: CoroutineRef, interval = 0.01) = - ## Returns only after coroutine `c` has returned. `interval` is time in seconds how often. + ## Returns only after coroutine ``c`` has returned. ``interval`` is time in seconds how often. while alive(c): suspend(interval) diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim index c907e54d8..cdef7c804 100644 --- a/lib/pure/cstrutils.nim +++ b/lib/pure/cstrutils.nim @@ -8,14 +8,10 @@ # ## This module supports helper routines for working with `cstring` -## without having to convert `cstring` to `string`, in order to +## without having to convert `cstring` to `string` in order to ## save allocations. -## -## See also -## ======== -## * `strutils module `_ for working with `string` -include system/inclrtl +include "system/inclrtl" import std/private/strimpl @@ -26,13 +22,12 @@ when defined(js): func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} = ## Returns true if `s` starts with `prefix`. - ## - ## The JS backend uses the native `String.prototype.startsWith` function. + ## + ## JS backend uses native `String.prototype.startsWith`. runnableExamples: assert startsWith(cstring"Hello, Nimion", cstring"Hello") assert not startsWith(cstring"Hello, Nimion", cstring"Nimion") assert startsWith(cstring"Hello", cstring"") - when nimvm: startsWithImpl(s, prefix) else: @@ -48,12 +43,11 @@ func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} = func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} = ## Returns true if `s` ends with `suffix`. ## - ## The JS backend uses the native `String.prototype.endsWith` function. + ## JS backend uses native `String.prototype.endsWith`. runnableExamples: assert endsWith(cstring"Hello, Nimion", cstring"Nimion") assert not endsWith(cstring"Hello, Nimion", cstring"Hello") assert endsWith(cstring"Hello", cstring"") - when nimvm: endsWithImpl(s, suffix) else: @@ -63,22 +57,23 @@ func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} = let slen = s.len var i = 0 var j = slen - len(suffix) - while i + j <% slen: - if s[i + j] != suffix[i]: return false + while i+j <% slen: + if s[i+j] != suffix[i]: return false inc(i) if suffix[i] == '\0': return true func cmpIgnoreStyle*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreStyle".} = ## Semantically the same as `cmp(normalize($a), normalize($b))`. It - ## is just optimized to not allocate temporary strings. This should - ## NOT be used to compare Nim identifier names, use `macros.eqIdent` + ## is just optimized to not allocate temporary strings. This should + ## NOT be used to compare Nim identifier names. use `macros.eqIdent` ## for that. Returns: - ## * 0 if `a == b` - ## * < 0 if `a < b` - ## * \> 0 if `a > b` + ## + ## .. code-block:: + ## 0 if a == b + ## < 0 if a < b + ## > 0 if a > b runnableExamples: assert cmpIgnoreStyle(cstring"hello", cstring"H_e_L_Lo") == 0 - when nimvm: cmpIgnoreStyleImpl(a, b) else: @@ -99,14 +94,15 @@ func cmpIgnoreStyle*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreStyle".} = func cmpIgnoreCase*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreCase".} = ## Compares two strings in a case insensitive manner. Returns: - ## * 0 if `a == b` - ## * < 0 if `a < b` - ## * \> 0 if `a > b` + ## + ## .. code-block:: + ## 0 if a == b + ## < 0 if a < b + ## > 0 if a > b runnableExamples: assert cmpIgnoreCase(cstring"hello", cstring"HeLLo") == 0 assert cmpIgnoreCase(cstring"echo", cstring"hello") < 0 assert cmpIgnoreCase(cstring"yellow", cstring"hello") > 0 - when nimvm: cmpIgnoreCaseImpl(a, b) else: diff --git a/lib/pure/db_common.nim b/lib/pure/db_common.nim new file mode 100644 index 000000000..96c13c003 --- /dev/null +++ b/lib/pure/db_common.nim @@ -0,0 +1,100 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Common datatypes and definitions for all ``db_*.nim`` ( +## `db_mysql `_, `db_postgres `_, +## and `db_sqlite `_) modules. + +type + DbError* = object of IOError ## exception that is raised if a database error occurs + + SqlQuery* = distinct string ## an SQL query string + + + DbEffect* = object of IOEffect ## effect that denotes a database operation + ReadDbEffect* = object of DbEffect ## effect that denotes a read operation + WriteDbEffect* = object of DbEffect ## effect that denotes a write operation + + DbTypeKind* = enum ## a superset of datatypes that might be supported. + dbUnknown, ## unknown datatype + dbSerial, ## datatype used for primary auto-increment keys + dbNull, ## datatype used for the NULL value + dbBit, ## bit datatype + dbBool, ## boolean datatype + dbBlob, ## blob datatype + dbFixedChar, ## string of fixed length + dbVarchar, ## string datatype + dbJson, ## JSON datatype + dbXml, ## XML datatype + dbInt, ## some integer type + dbUInt, ## some unsigned integer type + dbDecimal, ## decimal numbers (fixed-point number) + dbFloat, ## some floating point type + dbDate, ## a year-month-day description + dbTime, ## HH:MM:SS information + dbDatetime, ## year-month-day and HH:MM:SS information, + ## plus optional time or timezone information + dbTimestamp, ## Timestamp values are stored as the number of seconds + ## since the epoch ('1970-01-01 00:00:00' UTC). + dbTimeInterval, ## an interval [a,b] of times + dbEnum, ## some enum + dbSet, ## set of enum values + dbArray, ## an array of values + dbComposite, ## composite type (record, struct, etc) + dbUrl, ## a URL + dbUuid, ## a UUID + dbInet, ## an IP address + dbMacAddress, ## a MAC address + dbGeometry, ## some geometric type + dbPoint, ## Point on a plane (x,y) + dbLine, ## Infinite line ((x1,y1),(x2,y2)) + dbLseg, ## Finite line segment ((x1,y1),(x2,y2)) + dbBox, ## Rectangular box ((x1,y1),(x2,y2)) + dbPath, ## Closed or open path (similar to polygon) ((x1,y1),...) + dbPolygon, ## Polygon (similar to closed path) ((x1,y1),...) + dbCircle, ## Circle <(x,y),r> (center point and radius) + dbUser1, ## user definable datatype 1 (for unknown extensions) + dbUser2, ## user definable datatype 2 (for unknown extensions) + dbUser3, ## user definable datatype 3 (for unknown extensions) + dbUser4, ## user definable datatype 4 (for unknown extensions) + dbUser5 ## user definable datatype 5 (for unknown extensions) + + DbType* = object ## describes a database type + kind*: DbTypeKind ## the kind of the described type + notNull*: bool ## does the type contain NULL? + name*: string ## the name of the type + size*: Natural ## the size of the datatype; 0 if of variable size + maxReprLen*: Natural ## maximal length required for the representation + precision*, scale*: Natural ## precision and scale of the number + min*, max*: BiggestInt ## the minimum and maximum of allowed values + validValues*: seq[string] ## valid values of an enum or a set + + DbColumn* = object ## information about a database column + name*: string ## name of the column + tableName*: string ## name of the table the column belongs to (optional) + typ*: DbType ## type of the column + primaryKey*: bool ## is this a primary key? + foreignKey*: bool ## is this a foreign key? + DbColumns* = seq[DbColumn] + +template sql*(query: string): SqlQuery = + ## constructs a SqlQuery from the string `query`. This is supposed to be + ## used as a raw-string-literal modifier: + ## ``sql"update user set counter = counter + 1"`` + ## + ## If assertions are turned off, it does nothing. If assertions are turned + ## on, later versions will check the string for valid syntax. + SqlQuery(query) + +proc dbError*(msg: string) {.noreturn, noinline.} = + ## raises an DbError exception with message `msg`. + var e: ref DbError + new(e) + e.msg = msg + raise e diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index 25c961197..8debbf9c8 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -9,12 +9,12 @@ ## This module implements the basics for Linux distribution ("distro") ## detection and the OS's native package manager. Its primary purpose is to -## produce output for Nimble packages, like: +## produce output for Nimble packages like:: ## -## To complete the installation, run: +## To complete the installation, run: ## -## sudo apt-get install libblas-dev -## sudo apt-get install libvoodoo +## sudo apt-get install libblas-dev +## sudo apt-get install libvoodoo ## ## The above output could be the result of a code snippet like: ## @@ -27,16 +27,16 @@ ## ## See `packaging `_ for hints on distributing Nim using OS packages. -from std/strutils import contains, toLowerAscii +from strutils import contains, toLowerAscii when not defined(nimscript): - from std/osproc import execProcess - from std/envvars import existsEnv + from osproc import execProcess + from os import existsEnv type Distribution* {.pure.} = enum ## the list of known distributions Windows ## some version of Windows - Posix ## some POSIX system + Posix ## some Posix system MacOSX ## some version of OSX Linux ## some version of Linux Ubuntu @@ -52,7 +52,6 @@ type CentOS Deepin ArchLinux - Artix Antergos PCLinuxOS Mageia @@ -109,7 +108,7 @@ type Clonezilla SteamOS Absolute - NixOS ## NixOS or a Nix build environment + NixOS ## NixOS or a Nix build environment AUSTRUMI Arya Porteus @@ -124,7 +123,6 @@ type ExTiX Rockstor GoboLinux - Void BSD FreeBSD @@ -136,8 +134,7 @@ type const LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware, - Distribution.ArchLinux, Distribution.Artix, Distribution.Antergos, - Distribution.BlackArch, Distribution.ArchBang} + Distribution.ArchLinux} # we cache the result of the 'cmdRelease' # execution for faster platform detections. @@ -156,12 +153,12 @@ template hostnamectl(): untyped = cmdRelease("hostnamectl", hostnamectlRes) proc detectOsWithAllCmd(d: Distribution): bool = let dd = toLowerAscii($d) result = dd in toLowerAscii(osReleaseID()) or dd in toLowerAscii(release()) or - dd in toLowerAscii(uname()) or ("operating system: " & dd) in - toLowerAscii(hostnamectl()) + dd in toLowerAscii(uname()) or ("operating system: " & dd) in toLowerAscii(hostnamectl()) proc detectOsImpl(d: Distribution): bool = case d - of Distribution.Windows: result = defined(windows) + of Distribution.Windows: ## some version of Windows + result = defined(windows) of Distribution.Posix: result = defined(posix) of Distribution.MacOSX: result = defined(macosx) of Distribution.Linux: result = defined(linux) @@ -177,19 +174,17 @@ proc detectOsImpl(d: Distribution): bool = case d of Distribution.Gentoo: result = ("-" & $d & " ") in uname() - of Distribution.Elementary, Distribution.Ubuntu, Distribution.Debian, - Distribution.Fedora, Distribution.OpenMandriva, Distribution.CentOS, - Distribution.Alpine, Distribution.Mageia, Distribution.Zorin, Distribution.Void: + of Distribution.Elementary, Distribution.Ubuntu, Distribution.Debian, Distribution.Fedora, + Distribution.OpenMandriva, Distribution.CentOS, Distribution.Alpine, + Distribution.Mageia, Distribution.Zorin: result = toLowerAscii($d) in osReleaseID() of Distribution.RedHat: result = "rhel" in osReleaseID() of Distribution.ArchLinux: result = "arch" in osReleaseID() - of Distribution.Artix: - result = "artix" in osReleaseID() of Distribution.NixOS: - # Check if this is a Nix build or NixOS environment result = existsEnv("NIX_BUILD_TOP") or existsEnv("__NIXOS_SET_ENVIRONMENT_DONE") + # Check if this is a Nix build or NixOS environment of Distribution.OpenSUSE: result = "suse" in toLowerAscii(uname()) or "suse" in toLowerAscii(release()) of Distribution.GoboLinux: @@ -205,16 +200,16 @@ proc detectOsImpl(d: Distribution): bool = result = false template detectOs*(d: untyped): bool = - ## Distro/OS detection. For convenience, the - ## required `Distribution.` qualifier is added to the + ## Distro/OS detection. For convenience the + ## required ``Distribution.`` qualifier is added to the ## enum value. detectOsImpl(Distribution.d) when not defined(nimble): - var foreignDeps*: seq[string] = @[] ## Registered foreign deps. + var foreignDeps: seq[string] = @[] proc foreignCmd*(cmd: string; requiresSudo = false) = - ## Registers a foreign command to the internal list of commands + ## Registers a foreign command to the intern list of commands ## that can be queried later. let c = (if requiresSudo: "sudo " else: "") & cmd when defined(nimble): @@ -223,11 +218,11 @@ proc foreignCmd*(cmd: string; requiresSudo = false) = foreignDeps.add(c) proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) = - ## Returns the distro's native command to install `foreignPackageName` + ## Returns the distro's native command line to install 'foreignPackageName' ## and whether it requires root/admin rights. let p = foreignPackageName when defined(windows): - result = ("choco install " & p, false) + result = ("Chocolatey install " & p, false) elif defined(bsd): result = ("ports install " & p, true) elif defined(linux): @@ -256,10 +251,8 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) = result = ("pkg_add " & p, true) elif detectOs(PCLinuxOS): result = ("rpm -ivh " & p, true) - elif detectOs(ArchLinux) or detectOs(Manjaro) or detectOs(Artix): + elif detectOs(ArchLinux) or detectOs(Manjaro): result = ("pacman -S " & p, true) - elif detectOs(Void): - result = ("xbps-install " & p, true) else: result = (" install " & p, true) elif defined(haiku): @@ -268,12 +261,17 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) = result = ("brew install " & p, false) proc foreignDep*(foreignPackageName: string) = - ## Registers `foreignPackageName` to the internal list of foreign deps. - ## It is your job to ensure that the package name is correct. + ## Registers 'foreignPackageName' to the internal list of foreign deps. + ## It is your job to ensure the package name let (installCmd, sudo) = foreignDepInstallCmd(foreignPackageName) - foreignCmd(installCmd, sudo) + foreignCmd installCmd, sudo proc echoForeignDeps*() = ## Writes the list of registered foreign deps to stdout. for d in foreignDeps: echo d + +when false: + foreignDep("libblas-dev") + foreignDep "libfoo" + echoForeignDeps() diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index 48fd91b8f..42d13535f 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -8,8 +8,8 @@ # ## This module implements the ability to access symbols from shared -## libraries. On POSIX this uses the `dlsym` mechanism, on -## Windows `LoadLibrary`. +## libraries. On POSIX this uses the ``dlsym`` mechanism, on +## Windows ``LoadLibrary``. ## ## Examples ## ======== @@ -17,64 +17,75 @@ ## Loading a simple C function ## --------------------------- ## -## The following example demonstrates loading a function called `greet` +## The following example demonstrates loading a function called 'greet' ## from a library that is determined at runtime based upon a language choice. -## If the library fails to load or the function `greet` is not found, +## If the library fails to load or the function 'greet' is not found, ## it quits with a failure error code. ## -runnableExamples: - type - GreetFunction = proc (): cstring {.gcsafe, stdcall.} +## .. code-block::nim +## +## import dynlib +## +## type +## greetFunction = proc(): cstring {.gcsafe, stdcall.} +## +## let lang = stdin.readLine() +## +## let lib = case lang +## of "french": +## loadLib("french.dll") +## else: +## loadLib("english.dll") +## +## if lib == nil: +## echo "Error loading library" +## quit(QuitFailure) +## +## let greet = cast[greetFunction](lib.symAddr("greet")) +## +## if greet == nil: +## echo "Error loading 'greet' function from library" +## quit(QuitFailure) +## +## let greeting = greet() +## +## echo greeting +## +## unloadLib(lib) +## - proc loadGreet(lang: string) = - let lib = - case lang - of "french": - loadLib("french.dll") - else: - loadLib("english.dll") - assert lib != nil, "Error loading library" - - let greet = cast[GreetFunction](lib.symAddr("greet")) - assert greet != nil, "Error loading 'greet' function from library" - - echo greet() - - unloadLib(lib) - - -import std/strutils +import strutils type - LibHandle* = pointer ## A handle to a dynamically loaded library. + LibHandle* = pointer ## a handle to a dynamically loaded library proc loadLib*(path: string, globalSymbols = false): LibHandle {.gcsafe.} - ## Loads a library from `path`. Returns nil if the library could not + ## loads a library from `path`. Returns nil if the library could not ## be loaded. proc loadLib*(): LibHandle {.gcsafe.} - ## Gets the handle from the current executable. Returns nil if the + ## gets the handle from the current executable. Returns nil if the ## library could not be loaded. proc unloadLib*(lib: LibHandle) {.gcsafe.} - ## Unloads the library `lib`. + ## unloads the library `lib` proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} = - ## Raises a `LibraryError` exception. + ## raises an `EInvalidLibrary` exception. raise newException(LibraryError, "could not find symbol: " & $name) proc symAddr*(lib: LibHandle, name: cstring): pointer {.gcsafe.} - ## Retrieves the address of a procedure/variable from `lib`. Returns nil + ## retrieves the address of a procedure/variable from `lib`. Returns nil ## if the symbol could not be found. proc checkedSymAddr*(lib: LibHandle, name: cstring): pointer = - ## Retrieves the address of a procedure/variable from `lib`. Raises - ## `LibraryError` if the symbol could not be found. + ## retrieves the address of a procedure/variable from `lib`. Raises + ## `EInvalidLibrary` if the symbol could not be found. result = symAddr(lib, name) if result == nil: raiseInvalidLibrary(name) proc libCandidates*(s: string, dest: var seq[string]) = - ## Given a library name pattern `s`, write possible library names to `dest`. + ## given a library name pattern `s` write possible library names to `dest`. var le = strutils.find(s, '(') var ri = strutils.find(s, ')', le+1) if le >= 0 and ri > le: @@ -86,10 +97,9 @@ proc libCandidates*(s: string, dest: var seq[string]) = add(dest, s) proc loadLibPattern*(pattern: string, globalSymbols = false): LibHandle = - ## Loads a library with name matching `pattern`, similar to what the `dynlib` + ## loads a library with name matching `pattern`, similar to what `dynlib` ## pragma does. Returns nil if the library could not be loaded. - ## - ## .. warning:: this proc uses the GC and so cannot be used to load the GC. + ## Warning: this proc uses the GC and so cannot be used to load the GC. var candidates = newSeq[string]() libCandidates(pattern, candidates) for c in candidates: @@ -140,32 +150,6 @@ elif defined(nintendoswitch): proc symAddr(lib: LibHandle, name: cstring): pointer = raise newException(OSError, "symAddr not implemented on Nintendo Switch!") -elif defined(genode): - # - # ========================================================================= - # Not implemented for Genode without POSIX. Raise an error if called. - # ========================================================================= - # - - template raiseErr(prc: string) = - raise newException(OSError, prc & " not implemented, compile with POSIX suport") - - proc dlclose(lib: LibHandle) = - raiseErr(OSError, "dlclose") - proc dlopen(path: cstring, mode: int): LibHandle = - raiseErr(OSError, "dlopen") - proc dlsym(lib: LibHandle, name: cstring): pointer = - raiseErr(OSError, "dlsym") - proc loadLib(path: string, global_symbols = false): LibHandle = - raiseErr(OSError, "loadLib") - proc loadLib(): LibHandle = - raiseErr(OSError, "loadLib") - proc unloadLib(lib: LibHandle) = - raiseErr(OSError, "unloadLib") - proc symAddr(lib: LibHandle, name: cstring): pointer = - raiseErr(OSError, "symAddr") - - elif defined(windows) or defined(dos): # # ======================================================================= diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 0d7e878e5..1d8512018 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -7,46 +7,15 @@ # distribution, for details about the copyright. # -## Routines for converting between different character encodings. On UNIX, this uses +## Converts between different character encodings. On UNIX, this uses ## the `iconv`:idx: library, on Windows the Windows API. -## -## The following example shows how to change character encodings. -runnableExamples: - when defined(windows): - let - orig = "öäüß" - # convert `orig` from "UTF-8" to "CP1252" - cp1252 = convert(orig, "CP1252", "UTF-8") - # convert `cp1252` from "CP1252" to "ibm850" - ibm850 = convert(cp1252, "ibm850", "CP1252") - current = getCurrentEncoding() - assert orig == "\195\182\195\164\195\188\195\159" - assert ibm850 == "\148\132\129\225" - assert convert(ibm850, current, "ibm850") == orig - -## The example below uses a reuseable `EncodingConverter` object which is -## created by `open` with `destEncoding` and `srcEncoding` specified. You can use -## `convert` on this object multiple times. -runnableExamples: - when defined(windows): - var fromGB2312 = open("utf-8", "gb2312") - let first = "\203\173\197\194\163\191\210\187" & - "\203\242\209\204\211\234\200\206\198\189\201\250" - assert fromGB2312.convert(first) == "谁怕?一蓑烟雨任平生" - - let second = "\211\208\176\215\205\183\200\231" & - "\208\194\163\172\199\227\184\199\200\231\185\202" - assert fromGB2312.convert(second) == "有白头如新,倾盖如故" - import os -when defined(nimPreviewSlimSystem): - import std/assertions when not defined(windows): type ConverterObj = object - EncodingConverter* = ptr ConverterObj ## Can convert between two character sets. + EncodingConverter* = ptr ConverterObj ## can convert between two character sets else: type @@ -55,8 +24,8 @@ else: dest, src: CodePage type - EncodingError* = object of ValueError ## Exception that is raised - ## for encoding errors. + EncodingError* = object of ValueError ## exception that is raised + ## for encoding errors when defined(windows): import parseutils, strutils @@ -103,7 +72,6 @@ when defined(windows): (875, "cp875"), # IBM EBCDIC Greek Modern (932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS) (936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) - (936, "gbk"), # Alias for GB2312 encoding (949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code) (950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) (1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5) @@ -313,10 +281,8 @@ else: var errno {.importc, header: "".}: cint - when defined(bsd): + when defined(freebsd) or defined(netbsd): {.pragma: importIconv, cdecl, header: "".} - when defined(openbsd): - {.passL: "-liconv".} else: {.pragma: importIconv, cdecl, dynlib: iconvDll.} @@ -329,7 +295,7 @@ else: importc: "iconv", importIconv.} proc getCurrentEncoding*(uiApp = false): string = - ## Retrieves the current encoding. On Unix, "UTF-8" is always returned. + ## retrieves the current encoding. On Unix, always "UTF-8" is returned. ## The `uiApp` parameter is Windows specific. If true, the UI's code-page ## is returned, if false, the Console's code-page is returned. when defined(windows): @@ -338,8 +304,8 @@ proc getCurrentEncoding*(uiApp = false): string = result = "UTF-8" proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = - ## Opens a converter that can convert from `srcEncoding` to `destEncoding`. - ## Raises `EncodingError` if it cannot fulfill the request. + ## opens a converter that can convert from `srcEncoding` to `destEncoding`. + ## Raises `IOError` if it cannot fulfill the request. when not defined(windows): result = iconvOpen(destEncoding, srcEncoding) if result == nil: @@ -357,7 +323,7 @@ proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = "cannot find encoding " & srcEncoding) proc close*(c: EncodingConverter) = - ## Frees the resources the converter `c` holds. + ## frees the resources the converter `c` holds. when not defined(windows): iconvClose(c) @@ -452,13 +418,12 @@ when defined(windows): else: convertFromWideString(codePageTo, wideString) proc convert*(c: EncodingConverter, s: string): string = + ## converts `s` to `destEncoding` that was given to the converter `c`. It + ## assumed that `s` is in `srcEncoding`. + ## utf-16BE, utf-32 conversions not supported on windows result = convertWin(c.src, c.dest, s) else: proc convert*(c: EncodingConverter, s: string): string = - ## Converts `s` to `destEncoding` that was given to the converter `c`. It - ## assumes that `s` is in `srcEncoding`. - ## - ## .. warning:: UTF-16BE and UTF-32 conversions are not supported on Windows. result = newString(s.len) var inLen = csize_t len(s) var outLen = csize_t len(result) @@ -499,13 +464,84 @@ else: proc convert*(s: string, destEncoding = "UTF-8", srcEncoding = "CP1252"): string = - ## Converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`. + ## converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`. ## This opens a converter, uses it and closes it again and is thus more ## convenient but also likely less efficient than re-using a converter. - ## - ## .. warning:: UTF-16BE and UTF-32 conversions are not supported on Windows. + ## utf-16BE, utf-32 conversions not supported on windows var c = open(destEncoding, srcEncoding) try: result = convert(c, s) finally: close(c) + +when not defined(testing) and isMainModule: + let + orig = "öäüß" + cp1252 = convert(orig, "CP1252", "UTF-8") + ibm850 = convert(cp1252, "ibm850", "CP1252") + current = getCurrentEncoding() + echo "Original string from source code: ", orig + echo "Forced ibm850 encoding: ", ibm850 + echo "Current encoding: ", current + echo "From ibm850 to current: ", convert(ibm850, current, "ibm850") + +when not defined(testing) and isMainModule and defined(windows): + block should_throw_on_unsupported_conversions: + let original = "some string" + + doAssertRaises(EncodingError): + discard convert(original, "utf-8", "utf-32") + + doAssertRaises(EncodingError): + discard convert(original, "utf-8", "unicodeFFFE") + + doAssertRaises(EncodingError): + discard convert(original, "utf-8", "utf-32BE") + + doAssertRaises(EncodingError): + discard convert(original, "unicodeFFFE", "utf-8") + + doAssertRaises(EncodingError): + discard convert(original, "utf-32", "utf-8") + + doAssertRaises(EncodingError): + discard convert(original, "utf-32BE", "utf-8") + + block should_convert_from_utf16_to_utf8: + let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" + let result = convert(original, "utf-8", "utf-16") + doAssert(result == "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82") + + block should_convert_from_utf16_to_win1251: + let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" + let result = convert(original, "windows-1251", "utf-16") + doAssert(result == "\xf2\xe5\xf1\xf2") + + block should_convert_from_win1251_to_koi8r: + let original = "\xf2\xe5\xf1\xf2" # win1251 test string "тест" + let result = convert(original, "koi8-r", "windows-1251") + doAssert(result == "\xd4\xc5\xd3\xd4") + + block should_convert_from_koi8r_to_win1251: + let original = "\xd4\xc5\xd3\xd4" # koi8r test string "тест" + let result = convert(original, "windows-1251", "koi8-r") + doAssert(result == "\xf2\xe5\xf1\xf2") + + block should_convert_from_utf8_to_win1251: + let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" + let result = convert(original, "windows-1251", "utf-8") + doAssert(result == "\xf2\xe5\xf1\xf2") + + block should_convert_from_utf8_to_utf16: + let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" + let result = convert(original, "utf-16", "utf-8") + doAssert(result == "\x42\x04\x35\x04\x41\x04\x42\x04") + + block should_handle_empty_string_for_any_conversion: + let original = "" + var result = convert(original, "utf-16", "utf-8") + doAssert(result == "") + result = convert(original, "utf-8", "utf-16") + doAssert(result == "") + result = convert(original, "windows-1251", "koi8-r") + doAssert(result == "") diff --git a/lib/pure/endians.nim b/lib/pure/endians.nim index a0dc97c1d..29fde1c80 100644 --- a/lib/pure/endians.nim +++ b/lib/pure/endians.nim @@ -10,11 +10,6 @@ ## This module contains helpers that deal with different byte orders ## (`endian`:idx:). ## -## Endianess is the order of bytes of a value in memory. Big-endian means that -## the most significant byte is stored at the smallest memory address, -## while little endian means that the least-significant byte is stored -## at the smallest address. See also https://en.wikipedia.org/wiki/Endianness. -## ## Unstable API. when defined(gcc) or defined(llvm_gcc) or defined(clang): @@ -52,7 +47,7 @@ else: when useBuiltinSwap: template swapOpImpl(T: typedesc, op: untyped) = - ## We have to use `copyMem` here instead of a simple dereference because they + ## We have to use `copyMem` here instead of a simple deference because they ## may point to a unaligned address. A sufficiently smart compiler _should_ ## be able to elide them when they're not necessary. var tmp: T @@ -61,40 +56,18 @@ when useBuiltinSwap: copyMem(outp, addr tmp, sizeof(T)) proc swapEndian64*(outp, inp: pointer) {.inline, noSideEffect.} = - ## Copies `inp` to `outp`, reversing the byte order. - ## Both buffers are supposed to contain at least 8 bytes. - runnableExamples: - var a = [1'u8, 2, 3, 4, 5, 6, 7, 8] - var b: array[8, uint8] - swapEndian64(addr b, addr a) - assert b == [8'u8, 7, 6, 5, 4, 3, 2, 1] - swapOpImpl(uint64, builtin_bswap64) proc swapEndian32*(outp, inp: pointer) {.inline, noSideEffect.} = - ## Copies `inp` to `outp`, reversing the byte order. - ## Both buffers are supposed to contain at least 4 bytes. - runnableExamples: - var a = [1'u8, 2, 3, 4] - var b: array[4, uint8] - swapEndian32(addr b, addr a) - assert b == [4'u8, 3, 2, 1] - swapOpImpl(uint32, builtin_bswap32) proc swapEndian16*(outp, inp: pointer) {.inline, noSideEffect.} = - ## Copies `inp` to `outp`, reversing the byte order. - ## Both buffers are supposed to contain at least 2 bytes. - runnableExamples: - var a = [1'u8, 2] - var b: array[2, uint8] - swapEndian16(addr b, addr a) - assert b == [2'u8, 1] - swapOpImpl(uint16, builtin_bswap16) else: proc swapEndian64*(outp, inp: pointer) = + ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to + ## contain at least 8 bytes. var i = cast[cstring](inp) var o = cast[cstring](outp) o[0] = i[7] @@ -107,6 +80,8 @@ else: o[7] = i[0] proc swapEndian32*(outp, inp: pointer) = + ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to + ## contain at least 4 bytes. var i = cast[cstring](inp) var o = cast[cstring](outp) o[0] = i[3] @@ -115,6 +90,8 @@ else: o[3] = i[0] proc swapEndian16*(outp, inp: pointer) = + ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to + ## contain at least 2 bytes. var i = cast[cstring](inp) var o = cast[cstring](outp) o[0] = i[1] @@ -129,20 +106,8 @@ when system.cpuEndian == bigEndian: proc bigEndian16*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2) else: proc littleEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8) - ## Copies `inp` to `outp`, storing it in 64-bit little-endian order. - ## Both buffers are supposed to contain at least 8 bytes. proc littleEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4) - ## Copies `inp` to `outp`, storing it in 32-bit little-endian order. - ## Both buffers are supposed to contain at least 4 bytes. proc littleEndian16*(outp, inp: pointer){.inline.} = copyMem(outp, inp, 2) - ## Copies `inp` to `outp`, storing it in 16-bit little-endian order. - ## Both buffers are supposed to contain at least 2 bytes. proc bigEndian64*(outp, inp: pointer) {.inline.} = swapEndian64(outp, inp) - ## Copies `inp` to `outp`, storing it in 64-bit big-endian order. - ## Both buffers are supposed to contain at least 8 bytes. proc bigEndian32*(outp, inp: pointer) {.inline.} = swapEndian32(outp, inp) - ## Copies `inp` to `outp`, storing it in 32-bit big-endian order. - ## Both buffers are supposed to contain at least 4 bytes. proc bigEndian16*(outp, inp: pointer) {.inline.} = swapEndian16(outp, inp) - ## Copies `inp` to `outp`, storing it in 16-bit big-endian order. - ## Both buffers are supposed to contain at least 2 bytes. diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim index ddb6a1a0c..1a895bfa8 100644 --- a/lib/pure/fenv.nim +++ b/lib/pure/fenv.nim @@ -12,7 +12,7 @@ ## The types, vars and procs are bindings for the C standard library ## [](https://en.cppreference.com/w/c/numeric/fenv) header. -when defined(posix) and not defined(genode): +when defined(Posix) and not defined(genode): {.passl: "-lm".} var @@ -130,7 +130,7 @@ template fpRadix*: int = FLT_RADIX ## point type on the architecture used to build the program. template mantissaDigits*(T: typedesc[float32]): int = FLT_MANT_DIG - ## Number of digits (in base `floatingPointRadix`) in the mantissa + ## Number of digits (in base ``floatingPointRadix``) in the mantissa ## of 32-bit floating-point numbers. template digits*(T: typedesc[float32]): int = FLT_DIG ## Number of decimal digits that can be represented in a @@ -156,7 +156,7 @@ template epsilon*(T: typedesc[float32]): float32 = FLT_EPSILON ## 1.0 that can be represented in a 32-bit floating-point type. template mantissaDigits*(T: typedesc[float64]): int = DBL_MANT_DIG - ## Number of digits (in base `floatingPointRadix`) in the mantissa + ## Number of digits (in base ``floatingPointRadix``) in the mantissa ## of 64-bit floating-point numbers. template digits*(T: typedesc[float64]): int = DBL_DIG ## Number of decimal digits that can be represented in a diff --git a/lib/pure/future.nim b/lib/pure/future.nim new file mode 100644 index 000000000..40dba7846 --- /dev/null +++ b/lib/pure/future.nim @@ -0,0 +1,4 @@ + +{.deprecated: "Use the new 'sugar' module instead".} + +include sugar diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 4ae4938c6..dde4be128 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -10,76 +10,51 @@ ## This module implements efficient computations of hash values for diverse ## Nim types. All the procs are based on these two building blocks: ## - `!& proc <#!&,Hash,int>`_ used to start or mix a hash value, and -## - `!$ proc <#!$,Hash>`_ used to finish the hash value. +## - `!$ proc <#!$,Hash>`_ used to *finish* the hash value. ## ## If you want to implement hash procs for your custom types, ## you will end up writing the following kind of skeleton of code: - -runnableExamples: - type - Something = object - foo: int - bar: string - - iterator items(x: Something): Hash = - yield hash(x.foo) - yield hash(x.bar) - - proc hash(x: Something): Hash = - ## Computes a Hash from `x`. - var h: Hash = 0 - # Iterate over parts of `x`. - for xAtom in x: - # Mix the atom with the partial hash. - h = h !& xAtom - # Finish the hash. - result = !$h - -## If your custom types contain fields for which there already is a `hash` proc, -## you can simply hash together the hash values of the individual fields: - -runnableExamples: - type - Something = object - foo: int - bar: string - - proc hash(x: Something): Hash = - ## Computes a Hash from `x`. - var h: Hash = 0 - h = h !& hash(x.foo) - h = h !& hash(x.bar) - result = !$h - -## .. important:: Use `-d:nimPreviewHashRef` to -## enable hashing `ref`s. It is expected that this behavior -## becomes the new default in upcoming versions. ## -## .. note:: If the type has a `==` operator, the following must hold: -## If two values compare equal, their hashes must also be equal. +## .. code-block:: Nim +## proc hash(x: Something): Hash = +## ## Computes a Hash from `x`. +## var h: Hash = 0 +## # Iterate over parts of `x`. +## for xAtom in x: +## # Mix the atom with the partial hash. +## h = h !& xAtom +## # Finish the hash. +## result = !$h ## -## See also -## ======== -## * `md5 module `_ for the MD5 checksum algorithm -## * `base64 module `_ for a Base64 encoder and decoder -## * `std/sha1 module `_ for the SHA-1 checksum algorithm +## If your custom types contain fields for which there already is a hash proc, +## like for example objects made up of ``strings``, you can simply hash +## together the hash value of the individual fields: +## +## .. code-block:: Nim +## proc hash(x: Something): Hash = +## ## Computes a Hash from `x`. +## var h: Hash = 0 +## h = h !& hash(x.foo) +## h = h !& hash(x.bar) +## result = !$h +## +## **See also:** +## * `md5 module `_ for MD5 checksum algorithm +## * `base64 module `_ for a base64 encoder and decoder +## * `std/sha1 module `_ for a sha1 encoder and decoder ## * `tables module `_ for hash tables import std/private/since -when defined(nimPreviewSlimSystem): - import std/assertions - - type Hash* = int ## A hash value. Hash tables using these values should - ## always have a size of a power of two so they can use the `and` - ## operator instead of `mod` for truncation of the hash value. + ## always have a size of a power of two and can use the ``and`` + ## operator instead of ``mod`` for truncation of the hash value. proc `!&`*(h: Hash, val: int): Hash {.inline.} = ## Mixes a hash value `h` with `val` to produce a new hash value. ## - ## This is only needed if you need to implement a `hash` proc for a new datatype. + ## This is only needed if you need to implement a hash proc for a new datatype. let h = cast[uint](h) let val = cast[uint](val) var res = h + val @@ -90,7 +65,7 @@ proc `!&`*(h: Hash, val: int): Hash {.inline.} = proc `!$`*(h: Hash): Hash {.inline.} = ## Finishes the computation of the hash value. ## - ## This is only needed if you need to implement a `hash` proc for a new datatype. + ## This is only needed if you need to implement a hash proc for a new datatype. let h = cast[uint](h) # Hash is practically unsigned. var res = h + h shl 3 res = res xor (res shr 11) @@ -115,7 +90,7 @@ proc hiXorLoFallback64(a, b: uint64): uint64 {.inline.} = return hi xor lo proc hiXorLo(a, b: uint64): uint64 {.inline.} = - # XOR of the high & low 8 bytes of the full 16 byte product. + # Xor of high & low 8B of full 16B product when nimvm: result = hiXorLoFallback64(a, b) # `result =` is necessary here. else: @@ -131,42 +106,10 @@ proc hiXorLo(a, b: uint64): uint64 {.inline.} = else: result = hiXorLoFallback64(a, b) -when defined(js): - import std/jsbigints - import std/private/jsutils - - proc hiXorLoJs(a, b: JsBigInt): JsBigInt = - let - prod = a * b - mask = big"0xffffffffffffffff" # (big"1" shl big"64") - big"1" - result = (prod shr big"64") xor (prod and mask) - - template hashWangYiJS(x: JsBigInt): Hash = - let - P0 = big"0xa0761d6478bd642f" - P1 = big"0xe7037ed1a0b428db" - P58 = big"0xeb44accab455d16d" # big"0xeb44accab455d165" xor big"8" - res = hiXorLoJs(hiXorLoJs(P0, x xor P1), P58) - cast[Hash](toNumber(wrapToInt(res, 32))) - - template toBits(num: float): JsBigInt = - let - x = newArrayBuffer(8) - y = newFloat64Array(x) - if hasBigUint64Array(): - let z = newBigUint64Array(x) - y[0] = num - z[0] - else: - let z = newUint32Array(x) - y[0] = num - big(z[0]) + big(z[1]) shl big(32) - proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} = - ## Wang Yi's hash_v1 for 64-bit ints (see https://github.com/rurban/smhasher for - ## more details). This passed all scrambling tests in Spring 2019 and is simple. - ## - ## **Note:** It's ok to define `proc(x: int16): Hash = hashWangYi1(Hash(x))`. + ## Wang Yi's hash_v1 for 8B int. https://github.com/rurban/smhasher has more + ## details. This passed all scrambling tests in Spring 2019 and is simple. + ## NOTE: It's ok to define ``proc(x: int16): Hash = hashWangYi1(Hash(x))``. const P0 = 0xa0761d6478bd642f'u64 const P1 = 0xe7037ed1a0b428db'u64 const P58 = 0xeb44accab455d165'u64 xor 8'u64 @@ -178,10 +121,22 @@ proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} = result = cast[Hash](h(x)) else: when defined(js): - if hasJsBigInt(): - result = hashWangYiJS(big(x)) - else: - result = cast[Hash](x) and cast[Hash](0xFFFFFFFF) + asm """ + if (typeof BigInt == 'undefined') { + `result` = `x`; // For Node < 10.4, etc. we do the old identity hash + } else { // Otherwise we match the low 32-bits of C/C++ hash + function hi_xor_lo_js(a, b) { + const prod = BigInt(a) * BigInt(b); + const mask = (BigInt(1) << BigInt(64)) - BigInt(1); + return (prod >> BigInt(64)) ^ (prod & mask); + } + const P0 = BigInt(0xa0761d64)<`_ ## * `hashIgnoreCase <#hashIgnoreCase,string>`_ runnableExamples: doAssert hash("abracadabra") != hash("AbracadabrA") - when nimvm: - result = hashVmImpl(x, 0, high(x)) + when not defined(nimToOpenArrayCString): + result = 0 + for c in x: + result = result !& ord(c) + result = !$result else: - result = murmurHash(toOpenArrayByte(x, 0, high(x))) + when nimvm: + result = hashVmImpl(x, 0, high(x)) + else: + result = murmurHash(toOpenArrayByte(x, 0, high(x))) proc hash*(x: cstring): Hash = ## Efficient hashing of null-terminated strings. @@ -392,32 +336,46 @@ proc hash*(x: cstring): Hash = doAssert hash(cstring"AbracadabrA") == hash("AbracadabrA") doAssert hash(cstring"abracadabra") != hash(cstring"AbracadabrA") - when nimvm: - hashVmImpl(x, 0, high(x)) + when not defined(nimToOpenArrayCString): + result = 0 + var i = 0 + while x[i] != '\0': + result = result !& ord(x[i]) + inc i + result = !$result else: - when not defined(js): - murmurHash(toOpenArrayByte(x, 0, x.high)) + when nimvm: + hashVmImpl(x, 0, high(x)) else: - let xx = $x - murmurHash(toOpenArrayByte(xx, 0, high(xx))) + when not defined(js) and defined(nimToOpenArrayCString): + murmurHash(toOpenArrayByte(x, 0, x.high)) + else: + let xx = $x + murmurHash(toOpenArrayByte(xx, 0, high(xx))) proc hash*(sBuf: string, sPos, ePos: int): Hash = ## Efficient hashing of a string buffer, from starting ## position `sPos` to ending position `ePos` (included). ## - ## `hash(myStr, 0, myStr.high)` is equivalent to `hash(myStr)`. + ## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)``. runnableExamples: var a = "abracadabra" doAssert hash(a, 0, 3) == hash(a, 7, 10) - murmurHash(toOpenArrayByte(sBuf, sPos, ePos)) + when not defined(nimToOpenArrayCString): + result = 0 + for i in sPos..ePos: + result = result !& ord(sBuf[i]) + result = !$result + else: + murmurHash(toOpenArrayByte(sBuf, sPos, ePos)) proc hashIgnoreStyle*(x: string): Hash = ## Efficient hashing of strings; style is ignored. ## - ## **Note:** This uses a different hashing algorithm than `hash(string)`. + ## **Note:** This uses different hashing algorithm than `hash(string)`. ## - ## **See also:** + ## See also: ## * `hashIgnoreCase <#hashIgnoreCase,string>`_ runnableExamples: doAssert hashIgnoreStyle("aBr_aCa_dAB_ra") == hashIgnoreStyle("abracadabra") @@ -441,10 +399,10 @@ proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash = ## Efficient hashing of a string buffer, from starting ## position `sPos` to ending position `ePos` (included); style is ignored. ## - ## **Note:** This uses a different hashing algorithm than `hash(string)`. + ## **Note:** This uses different hashing algorithm than `hash(string)`. ## - ## `hashIgnoreStyle(myBuf, 0, myBuf.high)` is equivalent - ## to `hashIgnoreStyle(myBuf)`. + ## ``hashIgnoreStyle(myBuf, 0, myBuf.high)`` is equivalent + ## to ``hashIgnoreStyle(myBuf)``. runnableExamples: var a = "ABracada_b_r_a" doAssert hashIgnoreStyle(a, 0, 3) == hashIgnoreStyle(a, 7, a.high) @@ -465,9 +423,9 @@ proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash = proc hashIgnoreCase*(x: string): Hash = ## Efficient hashing of strings; case is ignored. ## - ## **Note:** This uses a different hashing algorithm than `hash(string)`. + ## **Note:** This uses different hashing algorithm than `hash(string)`. ## - ## **See also:** + ## See also: ## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_ runnableExamples: doAssert hashIgnoreCase("ABRAcaDABRA") == hashIgnoreCase("abRACAdabra") @@ -485,10 +443,10 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = ## Efficient hashing of a string buffer, from starting ## position `sPos` to ending position `ePos` (included); case is ignored. ## - ## **Note:** This uses a different hashing algorithm than `hash(string)`. + ## **Note:** This uses different hashing algorithm than `hash(string)`. ## - ## `hashIgnoreCase(myBuf, 0, myBuf.high)` is equivalent - ## to `hashIgnoreCase(myBuf)`. + ## ``hashIgnoreCase(myBuf, 0, myBuf.high)`` is equivalent + ## to ``hashIgnoreCase(myBuf)``. runnableExamples: var a = "ABracadabRA" doAssert hashIgnoreCase(a, 0, 3) == hashIgnoreCase(a, 7, 10) @@ -501,49 +459,16 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = h = h !& ord(c) result = !$h -proc hash*[T: tuple | object | proc](x: T): Hash = - ## Efficient `hash` overload. - runnableExamples: - # for `tuple|object`, `hash` must be defined for each component of `x`. - type Obj = object - x: int - y: string - type Obj2[T] = object - x: int - y: string - assert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2")) - # you can define custom hashes for objects (even if they're generic): - proc hash(a: Obj2): Hash = hash((a.x)) - assert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2")) - runnableExamples: - # proc - proc fn1() = discard - const fn1b = fn1 - assert hash(fn1b) == hash(fn1) - # closure - proc outer = - var a = 0 - proc fn2() = a.inc - assert fn2 is "closure" - let fn2b = fn2 - assert hash(fn2b) == hash(fn2) - assert hash(fn2) != hash(fn1) - outer() +proc hash*[T: tuple](x: T): Hash = + ## Efficient hashing of tuples. + for f in fields(x): + result = result !& hash(f) + result = !$result - when T is "closure": - result = hash((rawProc(x), rawEnv(x))) - elif T is (proc): - result = hash(pointer(x)) - else: - result = 0 - for f in fields(x): - result = result !& hash(f) - result = !$result proc hash*[A](x: openArray[A]): Hash = ## Efficient hashing of arrays and sequences. - ## There must be a `hash` proc defined for the element type `A`. when A is byte: result = murmurHash(x) elif A is char: @@ -552,7 +477,6 @@ proc hash*[A](x: openArray[A]): Hash = else: result = murmurHash(toOpenArrayByte(x, 0, x.high)) else: - result = 0 for a in x: result = result !& hash(a) result = !$result @@ -560,9 +484,8 @@ proc hash*[A](x: openArray[A]): Hash = proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = ## Efficient hashing of portions of arrays and sequences, from starting ## position `sPos` to ending position `ePos` (included). - ## There must be a `hash` proc defined for the element type `A`. ## - ## `hash(myBuf, 0, myBuf.high)` is equivalent to `hash(myBuf)`. + ## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)``. runnableExamples: let a = [1, 2, 5, 1, 2, 6] doAssert hash(a, 0, 1) == hash(a, 3, 4) @@ -584,8 +507,6 @@ proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = proc hash*[A](x: set[A]): Hash = ## Efficient hashing of sets. - ## There must be a `hash` proc defined for the element type `A`. - result = 0 for it in items(x): result = result !& hash(it) result = !$result diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index bf31c0239..acec05832 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -8,9 +8,9 @@ # ## Do yourself a favor and import the module -## as `from htmlgen import nil` and then fully qualify the macros. +## as ``from htmlgen import nil`` and then fully qualify the macros. ## -## *Note*: The Karax project (`nimble install karax`) has a better +## *Note*: The Karax project (``nimble install karax``) has a better ## way to achieve the same, see https://github.com/pragmagic/karax/blob/master/tests/nativehtmlgen.nim ## for an example. ## @@ -34,9 +34,9 @@ ## var nim = "Nim" ## echo h1(a(href="https://nim-lang.org", nim)) ## -## Writes the string: +## Writes the string:: ## -##

    Nim

    +##

    Nim

    ## import @@ -56,7 +56,7 @@ const ariaAttr* = " role " ## HTML DOM Aria Attributes commonAttr* = coreAttr & eventAttr & ariaAttr ## HTML DOM Common Attributes -proc getIdent(e: NimNode): string = +proc getIdent(e: NimNode): string {.compileTime.} = case e.kind of nnkIdent: result = e.strVal.normalize @@ -75,7 +75,7 @@ proc delete[T](s: var seq[T], attr: T): bool = result = true proc xmlCheckedTag*(argsList: NimNode, tag: string, optAttr = "", reqAttr = "", - isLeaf = false): NimNode = + isLeaf = false): NimNode {.compileTime.} = ## use this procedure to define a new XML tag # copy the attributes; when iterating over them these lists @@ -113,233 +113,236 @@ proc xmlCheckedTag*(argsList: NimNode, tag: string, optAttr = "", reqAttr = "", result.add(newStrLitNode("")) - result = nestList(ident"&", result) + when compiles(nestList(ident"&", result)): + result = nestList(ident"&", result) + else: + result = nestList(!"&", result) macro a*(e: varargs[untyped]): untyped = - ## Generates the HTML `a` element. + ## Generates the HTML ``a`` element. result = xmlCheckedTag(e, "a", "href target download rel hreflang type " & commonAttr) macro abbr*(e: varargs[untyped]): untyped = - ## Generates the HTML `abbr` element. + ## Generates the HTML ``abbr`` element. result = xmlCheckedTag(e, "abbr", commonAttr) macro address*(e: varargs[untyped]): untyped = - ## Generates the HTML `address` element. + ## Generates the HTML ``address`` element. result = xmlCheckedTag(e, "address", commonAttr) macro area*(e: varargs[untyped]): untyped = - ## Generates the HTML `area` element. + ## Generates the HTML ``area`` element. result = xmlCheckedTag(e, "area", "coords download href hreflang rel " & "shape target type" & commonAttr, "alt", true) macro article*(e: varargs[untyped]): untyped = - ## Generates the HTML `article` element. + ## Generates the HTML ``article`` element. result = xmlCheckedTag(e, "article", commonAttr) macro aside*(e: varargs[untyped]): untyped = - ## Generates the HTML `aside` element. + ## Generates the HTML ``aside`` element. result = xmlCheckedTag(e, "aside", commonAttr) macro audio*(e: varargs[untyped]): untyped = - ## Generates the HTML `audio` element. + ## Generates the HTML ``audio`` element. result = xmlCheckedTag(e, "audio", "src crossorigin preload " & "autoplay mediagroup loop muted controls" & commonAttr) macro b*(e: varargs[untyped]): untyped = - ## Generates the HTML `b` element. + ## Generates the HTML ``b`` element. result = xmlCheckedTag(e, "b", commonAttr) macro base*(e: varargs[untyped]): untyped = - ## Generates the HTML `base` element. + ## Generates the HTML ``base`` element. result = xmlCheckedTag(e, "base", "href target" & commonAttr, "", true) macro bdi*(e: varargs[untyped]): untyped = - ## Generates the HTML `bdi` element. + ## Generates the HTML ``bdi`` element. result = xmlCheckedTag(e, "bdi", commonAttr) macro bdo*(e: varargs[untyped]): untyped = - ## Generates the HTML `bdo` element. + ## Generates the HTML ``bdo`` element. result = xmlCheckedTag(e, "bdo", commonAttr) macro big*(e: varargs[untyped]): untyped = - ## Generates the HTML `big` element. + ## Generates the HTML ``big`` element. result = xmlCheckedTag(e, "big", commonAttr) macro blockquote*(e: varargs[untyped]): untyped = - ## Generates the HTML `blockquote` element. + ## Generates the HTML ``blockquote`` element. result = xmlCheckedTag(e, "blockquote", " cite" & commonAttr) macro body*(e: varargs[untyped]): untyped = - ## Generates the HTML `body` element. + ## Generates the HTML ``body`` element. result = xmlCheckedTag(e, "body", "onafterprint onbeforeprint " & "onbeforeunload onhashchange onmessage onoffline ononline onpagehide " & "onpageshow onpopstate onstorage onunload" & commonAttr) macro br*(e: varargs[untyped]): untyped = - ## Generates the HTML `br` element. + ## Generates the HTML ``br`` element. result = xmlCheckedTag(e, "br", commonAttr, "", true) macro button*(e: varargs[untyped]): untyped = - ## Generates the HTML `button` element. + ## Generates the HTML ``button`` element. result = xmlCheckedTag(e, "button", "autofocus disabled form formaction " & "formenctype formmethod formnovalidate formtarget menu name type value" & commonAttr) macro canvas*(e: varargs[untyped]): untyped = - ## Generates the HTML `canvas` element. + ## Generates the HTML ``canvas`` element. result = xmlCheckedTag(e, "canvas", "width height" & commonAttr) macro caption*(e: varargs[untyped]): untyped = - ## Generates the HTML `caption` element. + ## Generates the HTML ``caption`` element. result = xmlCheckedTag(e, "caption", commonAttr) macro center*(e: varargs[untyped]): untyped = - ## Generates the HTML `center` element. + ## Generates the HTML ``center`` element. result = xmlCheckedTag(e, "center", commonAttr) macro cite*(e: varargs[untyped]): untyped = - ## Generates the HTML `cite` element. + ## Generates the HTML ``cite`` element. result = xmlCheckedTag(e, "cite", commonAttr) macro code*(e: varargs[untyped]): untyped = - ## Generates the HTML `code` element. + ## Generates the HTML ``code`` element. result = xmlCheckedTag(e, "code", commonAttr) macro col*(e: varargs[untyped]): untyped = - ## Generates the HTML `col` element. + ## Generates the HTML ``col`` element. result = xmlCheckedTag(e, "col", "span" & commonAttr, "", true) macro colgroup*(e: varargs[untyped]): untyped = - ## Generates the HTML `colgroup` element. + ## Generates the HTML ``colgroup`` element. result = xmlCheckedTag(e, "colgroup", "span" & commonAttr) macro data*(e: varargs[untyped]): untyped = - ## Generates the HTML `data` element. + ## Generates the HTML ``data`` element. result = xmlCheckedTag(e, "data", "value" & commonAttr) macro datalist*(e: varargs[untyped]): untyped = - ## Generates the HTML `datalist` element. + ## Generates the HTML ``datalist`` element. result = xmlCheckedTag(e, "datalist", commonAttr) macro dd*(e: varargs[untyped]): untyped = - ## Generates the HTML `dd` element. + ## Generates the HTML ``dd`` element. result = xmlCheckedTag(e, "dd", commonAttr) macro del*(e: varargs[untyped]): untyped = - ## Generates the HTML `del` element. + ## Generates the HTML ``del`` element. result = xmlCheckedTag(e, "del", "cite datetime" & commonAttr) macro details*(e: varargs[untyped]): untyped = - ## Generates the HTML `details` element. + ## Generates the HTML ``details`` element. result = xmlCheckedTag(e, "details", commonAttr & "open") macro dfn*(e: varargs[untyped]): untyped = - ## Generates the HTML `dfn` element. + ## Generates the HTML ``dfn`` element. result = xmlCheckedTag(e, "dfn", commonAttr) macro dialog*(e: varargs[untyped]): untyped = - ## Generates the HTML `dialog` element. + ## Generates the HTML ``dialog`` element. result = xmlCheckedTag(e, "dialog", commonAttr & "open") macro `div`*(e: varargs[untyped]): untyped = - ## Generates the HTML `div` element. + ## Generates the HTML ``div`` element. result = xmlCheckedTag(e, "div", commonAttr) macro dl*(e: varargs[untyped]): untyped = - ## Generates the HTML `dl` element. + ## Generates the HTML ``dl`` element. result = xmlCheckedTag(e, "dl", commonAttr) macro dt*(e: varargs[untyped]): untyped = - ## Generates the HTML `dt` element. + ## Generates the HTML ``dt`` element. result = xmlCheckedTag(e, "dt", commonAttr) macro em*(e: varargs[untyped]): untyped = - ## Generates the HTML `em` element. + ## Generates the HTML ``em`` element. result = xmlCheckedTag(e, "em", commonAttr) macro embed*(e: varargs[untyped]): untyped = - ## Generates the HTML `embed` element. + ## Generates the HTML ``embed`` element. result = xmlCheckedTag(e, "embed", "src type height width" & commonAttr, "", true) macro fieldset*(e: varargs[untyped]): untyped = - ## Generates the HTML `fieldset` element. + ## Generates the HTML ``fieldset`` element. result = xmlCheckedTag(e, "fieldset", "disabled form name" & commonAttr) macro figure*(e: varargs[untyped]): untyped = - ## Generates the HTML `figure` element. + ## Generates the HTML ``figure`` element. result = xmlCheckedTag(e, "figure", commonAttr) macro figcaption*(e: varargs[untyped]): untyped = - ## Generates the HTML `figcaption` element. + ## Generates the HTML ``figcaption`` element. result = xmlCheckedTag(e, "figcaption", commonAttr) macro footer*(e: varargs[untyped]): untyped = - ## Generates the HTML `footer` element. + ## Generates the HTML ``footer`` element. result = xmlCheckedTag(e, "footer", commonAttr) macro form*(e: varargs[untyped]): untyped = - ## Generates the HTML `form` element. + ## Generates the HTML ``form`` element. result = xmlCheckedTag(e, "form", "accept-charset action autocomplete " & "enctype method name novalidate target" & commonAttr) macro h1*(e: varargs[untyped]): untyped = - ## Generates the HTML `h1` element. + ## Generates the HTML ``h1`` element. result = xmlCheckedTag(e, "h1", commonAttr) macro h2*(e: varargs[untyped]): untyped = - ## Generates the HTML `h2` element. + ## Generates the HTML ``h2`` element. result = xmlCheckedTag(e, "h2", commonAttr) macro h3*(e: varargs[untyped]): untyped = - ## Generates the HTML `h3` element. + ## Generates the HTML ``h3`` element. result = xmlCheckedTag(e, "h3", commonAttr) macro h4*(e: varargs[untyped]): untyped = - ## Generates the HTML `h4` element. + ## Generates the HTML ``h4`` element. result = xmlCheckedTag(e, "h4", commonAttr) macro h5*(e: varargs[untyped]): untyped = - ## Generates the HTML `h5` element. + ## Generates the HTML ``h5`` element. result = xmlCheckedTag(e, "h5", commonAttr) macro h6*(e: varargs[untyped]): untyped = - ## Generates the HTML `h6` element. + ## Generates the HTML ``h6`` element. result = xmlCheckedTag(e, "h6", commonAttr) macro head*(e: varargs[untyped]): untyped = - ## Generates the HTML `head` element. + ## Generates the HTML ``head`` element. result = xmlCheckedTag(e, "head", commonAttr) macro header*(e: varargs[untyped]): untyped = - ## Generates the HTML `header` element. + ## Generates the HTML ``header`` element. result = xmlCheckedTag(e, "header", commonAttr) macro html*(e: varargs[untyped]): untyped = - ## Generates the HTML `html` element. + ## Generates the HTML ``html`` element. result = xmlCheckedTag(e, "html", "xmlns" & commonAttr, "") macro hr*(): untyped = - ## Generates the HTML `hr` element. - result = xmlCheckedTag(newNimNode(nnkArgList), "hr", commonAttr, "", true) + ## Generates the HTML ``hr`` element. + result = xmlCheckedTag(newNimNode(nnkArglist), "hr", commonAttr, "", true) macro i*(e: varargs[untyped]): untyped = - ## Generates the HTML `i` element. + ## Generates the HTML ``i`` element. result = xmlCheckedTag(e, "i", commonAttr) macro iframe*(e: varargs[untyped]): untyped = - ## Generates the HTML `iframe` element. + ## Generates the HTML ``iframe`` element. result = xmlCheckedTag(e, "iframe", "src srcdoc name sandbox width height loading" & commonAttr) macro img*(e: varargs[untyped]): untyped = - ## Generates the HTML `img` element. + ## Generates the HTML ``img`` element. result = xmlCheckedTag(e, "img", "crossorigin usemap ismap height width loading" & commonAttr, "src alt", true) macro input*(e: varargs[untyped]): untyped = - ## Generates the HTML `input` element. + ## Generates the HTML ``input`` element. result = xmlCheckedTag(e, "input", "accept alt autocomplete autofocus " & "checked dirname disabled form formaction formenctype formmethod " & "formnovalidate formtarget height inputmode list max maxlength min " & @@ -347,401 +350,397 @@ macro input*(e: varargs[untyped]): untyped = "src step type value width" & commonAttr, "", true) macro ins*(e: varargs[untyped]): untyped = - ## Generates the HTML `ins` element. + ## Generates the HTML ``ins`` element. result = xmlCheckedTag(e, "ins", "cite datetime" & commonAttr) macro kbd*(e: varargs[untyped]): untyped = - ## Generates the HTML `kbd` element. + ## Generates the HTML ``kbd`` element. result = xmlCheckedTag(e, "kbd", commonAttr) macro keygen*(e: varargs[untyped]): untyped = - ## Generates the HTML `keygen` element. + ## Generates the HTML ``keygen`` element. result = xmlCheckedTag(e, "keygen", "autofocus challenge disabled " & "form keytype name" & commonAttr) macro label*(e: varargs[untyped]): untyped = - ## Generates the HTML `label` element. + ## Generates the HTML ``label`` element. result = xmlCheckedTag(e, "label", "form for" & commonAttr) macro legend*(e: varargs[untyped]): untyped = - ## Generates the HTML `legend` element. + ## Generates the HTML ``legend`` element. result = xmlCheckedTag(e, "legend", commonAttr) macro li*(e: varargs[untyped]): untyped = - ## Generates the HTML `li` element. + ## Generates the HTML ``li`` element. result = xmlCheckedTag(e, "li", "value" & commonAttr) macro link*(e: varargs[untyped]): untyped = - ## Generates the HTML `link` element. + ## Generates the HTML ``link`` element. result = xmlCheckedTag(e, "link", "href crossorigin rel media hreflang " & "type sizes" & commonAttr, "", true) macro main*(e: varargs[untyped]): untyped = - ## Generates the HTML `main` element. + ## Generates the HTML ``main`` element. result = xmlCheckedTag(e, "main", commonAttr) macro map*(e: varargs[untyped]): untyped = - ## Generates the HTML `map` element. + ## Generates the HTML ``map`` element. result = xmlCheckedTag(e, "map", "name" & commonAttr) macro mark*(e: varargs[untyped]): untyped = - ## Generates the HTML `mark` element. + ## Generates the HTML ``mark`` element. result = xmlCheckedTag(e, "mark", commonAttr) macro marquee*(e: varargs[untyped]): untyped = - ## Generates the HTML `marquee` element. + ## Generates the HTML ``marquee`` element. result = xmlCheckedTag(e, "marquee", coreAttr & "behavior bgcolor direction height hspace loop scrollamount " & "scrolldelay truespeed vspace width onbounce onfinish onstart") macro meta*(e: varargs[untyped]): untyped = - ## Generates the HTML `meta` element. + ## Generates the HTML ``meta`` element. result = xmlCheckedTag(e, "meta", "name http-equiv content charset" & commonAttr, "", true) macro meter*(e: varargs[untyped]): untyped = - ## Generates the HTML `meter` element. + ## Generates the HTML ``meter`` element. result = xmlCheckedTag(e, "meter", "value min max low high optimum" & commonAttr) macro nav*(e: varargs[untyped]): untyped = - ## Generates the HTML `nav` element. + ## Generates the HTML ``nav`` element. result = xmlCheckedTag(e, "nav", commonAttr) macro noscript*(e: varargs[untyped]): untyped = - ## Generates the HTML `noscript` element. + ## Generates the HTML ``noscript`` element. result = xmlCheckedTag(e, "noscript", commonAttr) macro `object`*(e: varargs[untyped]): untyped = - ## Generates the HTML `object` element. + ## Generates the HTML ``object`` element. result = xmlCheckedTag(e, "object", "data type typemustmatch name usemap " & "form width height" & commonAttr) macro ol*(e: varargs[untyped]): untyped = - ## Generates the HTML `ol` element. + ## Generates the HTML ``ol`` element. result = xmlCheckedTag(e, "ol", "reversed start type" & commonAttr) macro optgroup*(e: varargs[untyped]): untyped = - ## Generates the HTML `optgroup` element. + ## Generates the HTML ``optgroup`` element. result = xmlCheckedTag(e, "optgroup", "disabled" & commonAttr, "label", false) macro option*(e: varargs[untyped]): untyped = - ## Generates the HTML `option` element. + ## Generates the HTML ``option`` element. result = xmlCheckedTag(e, "option", "disabled label selected value" & commonAttr) macro output*(e: varargs[untyped]): untyped = - ## Generates the HTML `output` element. + ## Generates the HTML ``output`` element. result = xmlCheckedTag(e, "output", "for form name" & commonAttr) macro p*(e: varargs[untyped]): untyped = - ## Generates the HTML `p` element. + ## Generates the HTML ``p`` element. result = xmlCheckedTag(e, "p", commonAttr) macro param*(e: varargs[untyped]): untyped = - ## Generates the HTML `param` element. + ## Generates the HTML ``param`` element. result = xmlCheckedTag(e, "param", commonAttr, "name value", true) macro picture*(e: varargs[untyped]): untyped = - ## Generates the HTML `picture` element. + ## Generates the HTML ``picture`` element. result = xmlCheckedTag(e, "picture", commonAttr) macro pre*(e: varargs[untyped]): untyped = - ## Generates the HTML `pre` element. + ## Generates the HTML ``pre`` element. result = xmlCheckedTag(e, "pre", commonAttr) macro progress*(e: varargs[untyped]): untyped = - ## Generates the HTML `progress` element. + ## Generates the HTML ``progress`` element. result = xmlCheckedTag(e, "progress", "value max" & commonAttr) macro q*(e: varargs[untyped]): untyped = - ## Generates the HTML `q` element. + ## Generates the HTML ``q`` element. result = xmlCheckedTag(e, "q", "cite" & commonAttr) macro rb*(e: varargs[untyped]): untyped = - ## Generates the HTML `rb` element. + ## Generates the HTML ``rb`` element. result = xmlCheckedTag(e, "rb", commonAttr) macro rp*(e: varargs[untyped]): untyped = - ## Generates the HTML `rp` element. + ## Generates the HTML ``rp`` element. result = xmlCheckedTag(e, "rp", commonAttr) macro rt*(e: varargs[untyped]): untyped = - ## Generates the HTML `rt` element. + ## Generates the HTML ``rt`` element. result = xmlCheckedTag(e, "rt", commonAttr) macro rtc*(e: varargs[untyped]): untyped = - ## Generates the HTML `rtc` element. + ## Generates the HTML ``rtc`` element. result = xmlCheckedTag(e, "rtc", commonAttr) macro ruby*(e: varargs[untyped]): untyped = - ## Generates the HTML `ruby` element. + ## Generates the HTML ``ruby`` element. result = xmlCheckedTag(e, "ruby", commonAttr) macro s*(e: varargs[untyped]): untyped = - ## Generates the HTML `s` element. + ## Generates the HTML ``s`` element. result = xmlCheckedTag(e, "s", commonAttr) macro samp*(e: varargs[untyped]): untyped = - ## Generates the HTML `samp` element. + ## Generates the HTML ``samp`` element. result = xmlCheckedTag(e, "samp", commonAttr) macro script*(e: varargs[untyped]): untyped = - ## Generates the HTML `script` element. + ## Generates the HTML ``script`` element. result = xmlCheckedTag(e, "script", "src type charset async defer " & "crossorigin" & commonAttr) macro section*(e: varargs[untyped]): untyped = - ## Generates the HTML `section` element. + ## Generates the HTML ``section`` element. result = xmlCheckedTag(e, "section", commonAttr) macro select*(e: varargs[untyped]): untyped = - ## Generates the HTML `select` element. + ## Generates the HTML ``select`` element. result = xmlCheckedTag(e, "select", "autofocus disabled form multiple " & "name required size" & commonAttr) macro slot*(e: varargs[untyped]): untyped = - ## Generates the HTML `slot` element. + ## Generates the HTML ``slot`` element. result = xmlCheckedTag(e, "slot", commonAttr) macro small*(e: varargs[untyped]): untyped = - ## Generates the HTML `small` element. + ## Generates the HTML ``small`` element. result = xmlCheckedTag(e, "small", commonAttr) macro source*(e: varargs[untyped]): untyped = - ## Generates the HTML `source` element. + ## Generates the HTML ``source`` element. result = xmlCheckedTag(e, "source", "type" & commonAttr, "src", true) macro span*(e: varargs[untyped]): untyped = - ## Generates the HTML `span` element. + ## Generates the HTML ``span`` element. result = xmlCheckedTag(e, "span", commonAttr) macro strong*(e: varargs[untyped]): untyped = - ## Generates the HTML `strong` element. + ## Generates the HTML ``strong`` element. result = xmlCheckedTag(e, "strong", commonAttr) macro style*(e: varargs[untyped]): untyped = - ## Generates the HTML `style` element. + ## Generates the HTML ``style`` element. result = xmlCheckedTag(e, "style", "media type" & commonAttr) macro sub*(e: varargs[untyped]): untyped = - ## Generates the HTML `sub` element. + ## Generates the HTML ``sub`` element. result = xmlCheckedTag(e, "sub", commonAttr) macro summary*(e: varargs[untyped]): untyped = - ## Generates the HTML `summary` element. + ## Generates the HTML ``summary`` element. result = xmlCheckedTag(e, "summary", commonAttr) macro sup*(e: varargs[untyped]): untyped = - ## Generates the HTML `sup` element. + ## Generates the HTML ``sup`` element. result = xmlCheckedTag(e, "sup", commonAttr) macro table*(e: varargs[untyped]): untyped = - ## Generates the HTML `table` element. + ## Generates the HTML ``table`` element. result = xmlCheckedTag(e, "table", "border sortable" & commonAttr) macro tbody*(e: varargs[untyped]): untyped = - ## Generates the HTML `tbody` element. + ## Generates the HTML ``tbody`` element. result = xmlCheckedTag(e, "tbody", commonAttr) macro td*(e: varargs[untyped]): untyped = - ## Generates the HTML `td` element. + ## Generates the HTML ``td`` element. result = xmlCheckedTag(e, "td", "colspan rowspan headers" & commonAttr) macro `template`*(e: varargs[untyped]): untyped = - ## Generates the HTML `template` element. + ## Generates the HTML ``template`` element. result = xmlCheckedTag(e, "template", commonAttr) macro textarea*(e: varargs[untyped]): untyped = - ## Generates the HTML `textarea` element. + ## Generates the HTML ``textarea`` element. result = xmlCheckedTag(e, "textarea", "autocomplete autofocus cols " & "dirname disabled form inputmode maxlength minlength name placeholder " & "readonly required rows wrap" & commonAttr) macro tfoot*(e: varargs[untyped]): untyped = - ## Generates the HTML `tfoot` element. + ## Generates the HTML ``tfoot`` element. result = xmlCheckedTag(e, "tfoot", commonAttr) macro th*(e: varargs[untyped]): untyped = - ## Generates the HTML `th` element. + ## Generates the HTML ``th`` element. result = xmlCheckedTag(e, "th", "colspan rowspan headers abbr scope axis" & " sorted" & commonAttr) macro thead*(e: varargs[untyped]): untyped = - ## Generates the HTML `thead` element. + ## Generates the HTML ``thead`` element. result = xmlCheckedTag(e, "thead", commonAttr) macro time*(e: varargs[untyped]): untyped = - ## Generates the HTML `time` element. + ## Generates the HTML ``time`` element. result = xmlCheckedTag(e, "time", "datetime" & commonAttr) macro title*(e: varargs[untyped]): untyped = - ## Generates the HTML `title` element. + ## Generates the HTML ``title`` element. result = xmlCheckedTag(e, "title", commonAttr) macro tr*(e: varargs[untyped]): untyped = - ## Generates the HTML `tr` element. + ## Generates the HTML ``tr`` element. result = xmlCheckedTag(e, "tr", commonAttr) macro track*(e: varargs[untyped]): untyped = - ## Generates the HTML `track` element. + ## Generates the HTML ``track`` element. result = xmlCheckedTag(e, "track", "kind srclang label default" & commonAttr, "src", true) macro tt*(e: varargs[untyped]): untyped = - ## Generates the HTML `tt` element. + ## Generates the HTML ``tt`` element. result = xmlCheckedTag(e, "tt", commonAttr) macro u*(e: varargs[untyped]): untyped = - ## Generates the HTML `u` element. + ## Generates the HTML ``u`` element. result = xmlCheckedTag(e, "u", commonAttr) macro ul*(e: varargs[untyped]): untyped = - ## Generates the HTML `ul` element. + ## Generates the HTML ``ul`` element. result = xmlCheckedTag(e, "ul", commonAttr) macro `var`*(e: varargs[untyped]): untyped = - ## Generates the HTML `var` element. + ## Generates the HTML ``var`` element. result = xmlCheckedTag(e, "var", commonAttr) macro video*(e: varargs[untyped]): untyped = - ## Generates the HTML `video` element. + ## Generates the HTML ``video`` element. result = xmlCheckedTag(e, "video", "src crossorigin poster preload " & "autoplay mediagroup loop muted controls width height" & commonAttr) macro wbr*(e: varargs[untyped]): untyped = - ## Generates the HTML `wbr` element. + ## Generates the HTML ``wbr`` element. result = xmlCheckedTag(e, "wbr", commonAttr, "", true) -macro portal*(e: varargs[untyped]): untyped = - ## Generates the HTML `portal` element. - result = xmlCheckedTag(e, "portal", "width height type src disabled" & commonAttr, "", false) - macro math*(e: varargs[untyped]): untyped = - ## Generates the HTML `math` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``math`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math#Examples result = xmlCheckedTag(e, "math", "mathbackground mathcolor href overflow" & commonAttr) macro maction*(e: varargs[untyped]): untyped = - ## Generates the HTML `maction` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``maction`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/maction result = xmlCheckedTag(e, "maction", "mathbackground mathcolor href" & commonAttr) macro menclose*(e: varargs[untyped]): untyped = - ## Generates the HTML `menclose` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``menclose`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/menclose result = xmlCheckedTag(e, "menclose", "mathbackground mathcolor href notation" & commonAttr) macro merror*(e: varargs[untyped]): untyped = - ## Generates the HTML `merror` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``merror`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/merror result = xmlCheckedTag(e, "merror", "mathbackground mathcolor href" & commonAttr) macro mfenced*(e: varargs[untyped]): untyped = - ## Generates the HTML `mfenced` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mfenced`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfenced result = xmlCheckedTag(e, "mfenced", "mathbackground mathcolor href open separators" & commonAttr) macro mfrac*(e: varargs[untyped]): untyped = - ## Generates the HTML `mfrac` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mfrac`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mfrac result = xmlCheckedTag(e, "mfrac", "mathbackground mathcolor href linethickness numalign" & commonAttr) macro mglyph*(e: varargs[untyped]): untyped = - ## Generates the HTML `mglyph` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mglyph`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mglyph result = xmlCheckedTag(e, "mglyph", "mathbackground mathcolor href src valign" & commonAttr) macro mi*(e: varargs[untyped]): untyped = - ## Generates the HTML `mi` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mi`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mi result = xmlCheckedTag(e, "mi", "mathbackground mathcolor href mathsize mathvariant" & commonAttr) macro mlabeledtr*(e: varargs[untyped]): untyped = - ## Generates the HTML `mlabeledtr` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mlabeledtr`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mlabeledtr result = xmlCheckedTag(e, "mlabeledtr", "mathbackground mathcolor href columnalign groupalign rowalign" & commonAttr) macro mmultiscripts*(e: varargs[untyped]): untyped = - ## Generates the HTML `mmultiscripts` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mmultiscripts`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mmultiscripts result = xmlCheckedTag(e, "mmultiscripts", "mathbackground mathcolor href subscriptshift superscriptshift" & commonAttr) macro mn*(e: varargs[untyped]): untyped = - ## Generates the HTML `mn` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mn`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mn result = xmlCheckedTag(e, "mn", "mathbackground mathcolor href mathsize mathvariant" & commonAttr) macro mo*(e: varargs[untyped]): untyped = - ## Generates the HTML `mo` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mo`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo result = xmlCheckedTag(e, "mo", "mathbackground mathcolor fence form largeop lspace mathsize mathvariant movablelimits rspace separator stretchy symmetric" & commonAttr) macro mover*(e: varargs[untyped]): untyped = - ## Generates the HTML `mover` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mover`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mover result = xmlCheckedTag(e, "mover", "mathbackground mathcolor accent href" & commonAttr) macro mpadded*(e: varargs[untyped]): untyped = - ## Generates the HTML `mpadded` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mpadded`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mpadded result = xmlCheckedTag(e, "mpadded", "mathbackground mathcolor depth href lspace voffset" & commonAttr) macro mphantom*(e: varargs[untyped]): untyped = - ## Generates the HTML `mphantom` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mphantom`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mphantom result = xmlCheckedTag(e, "mphantom", "mathbackground" & commonAttr) macro mroot*(e: varargs[untyped]): untyped = - ## Generates the HTML `mroot` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mroot`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mroot result = xmlCheckedTag(e, "mroot", "mathbackground mathcolor href" & commonAttr) macro mrow*(e: varargs[untyped]): untyped = - ## Generates the HTML `mrow` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mrow`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mrow result = xmlCheckedTag(e, "mrow", "mathbackground mathcolor href" & commonAttr) macro ms*(e: varargs[untyped]): untyped = - ## Generates the HTML `ms` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``ms`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/ms result = xmlCheckedTag(e, "ms", "mathbackground mathcolor href lquote mathsize mathvariant rquote" & commonAttr) macro mspace*(e: varargs[untyped]): untyped = - ## Generates the HTML `mspace` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mspace`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mspace result = xmlCheckedTag(e, "mspace", "mathbackground mathcolor href linebreak" & commonAttr) macro msqrt*(e: varargs[untyped]): untyped = - ## Generates the HTML `msqrt` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``msqrt`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msqrt result = xmlCheckedTag(e, "msqrt", "mathbackground mathcolor href" & commonAttr) macro mstyle*(e: varargs[untyped]): untyped = - ## Generates the HTML `mstyle` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mstyle`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mstyle result = xmlCheckedTag(e, "mstyle", ("mathbackground mathcolor href decimalpoint displaystyle " & "infixlinebreakstyle scriptlevel scriptminsize scriptsizemultiplier" & commonAttr)) macro msub*(e: varargs[untyped]): untyped = - ## Generates the HTML `msub` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``msub`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msub result = xmlCheckedTag(e, "msub", "mathbackground mathcolor href subscriptshift" & commonAttr) macro msubsup*(e: varargs[untyped]): untyped = - ## Generates the HTML `msubsup` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``msubsup`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msubsup result = xmlCheckedTag(e, "msubsup", "mathbackground mathcolor href subscriptshift superscriptshift" & commonAttr) macro msup*(e: varargs[untyped]): untyped = - ## Generates the HTML `msup` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``msup`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/msup result = xmlCheckedTag(e, "msup", "mathbackground mathcolor href superscriptshift" & commonAttr) macro mtable*(e: varargs[untyped]): untyped = - ## Generates the HTML `mtable` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mtable`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtable result = xmlCheckedTag(e, "mtable", ("mathbackground mathcolor href align " & "alignmentscope columnalign columnlines columnspacing columnwidth " & @@ -749,38 +748,38 @@ macro mtable*(e: varargs[untyped]): untyped = "rowalign rowlines rowspacing side width" & commonAttr)) macro mtd*(e: varargs[untyped]): untyped = - ## Generates the HTML `mtd` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mtd`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtd result = xmlCheckedTag(e, "mtd", "mathbackground mathcolor href columnalign columnspan groupalign rowalign rowspan" & commonAttr) macro mtext*(e: varargs[untyped]): untyped = - ## Generates the HTML `mtext` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``mtext`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mtext result = xmlCheckedTag(e, "mtext", "mathbackground mathcolor href mathsize mathvariant" & commonAttr) macro munder*(e: varargs[untyped]): untyped = - ## Generates the HTML `munder` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``munder`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/munder result = xmlCheckedTag(e, "munder", "mathbackground mathcolor href accentunder align" & commonAttr) macro munderover*(e: varargs[untyped]): untyped = - ## Generates the HTML `munderover` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``munderover`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/munderover result = xmlCheckedTag(e, "munderover", "mathbackground mathcolor href accentunder accent align" & commonAttr) macro semantics*(e: varargs[untyped]): untyped = - ## Generates the HTML `semantics` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``semantics`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics result = xmlCheckedTag(e, "semantics", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr) macro annotation*(e: varargs[untyped]): untyped = - ## Generates the HTML `annotation` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``annotation`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics result = xmlCheckedTag(e, "annotation", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr) macro `annotation-xml`*(e: varargs[untyped]): untyped = - ## Generates the HTML `annotation-xml` element. MathML https://wikipedia.org/wiki/MathML + ## Generates the HTML ``annotation-xml`` element. MathML https://wikipedia.org/wiki/MathML ## https://developer.mozilla.org/en-US/docs/Web/MathML/Element/semantics result = xmlCheckedTag(e, "annotation", "mathbackground mathcolor href definitionURL encoding cd src" & commonAttr) diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index 24eab3abb..4b305cfd6 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -19,7 +19,7 @@ ## ## Every tag in the resulting tree is in lower case. ## -## **Note:** The resulting `XmlNode` already uses the `clientData` field, +## **Note:** The resulting ``XmlNode`` already uses the ``clientData`` field, ## so it cannot be used by clients of this library. ## ## Example: Transforming hyperlinks @@ -27,16 +27,16 @@ ## ## This code demonstrates how you can iterate over all the tags in an HTML file ## and write back the modified version. In this case we look for hyperlinks -## ending with the extension `.rst` and convert them to `.html`. +## ending with the extension ``.rst`` and convert them to ``.html``. ## ## .. code-block:: Nim ## :test: ## -## import std/htmlparser -## import std/xmltree # To use '$' for XmlNode -## import std/strtabs # To access XmlAttributes -## import std/os # To use splitFile -## import std/strutils # To use cmpIgnoreCase +## import htmlparser +## import xmltree # To use '$' for XmlNode +## import strtabs # To access XmlAttributes +## import os # To use splitFile +## import strutils # To use cmpIgnoreCase ## ## proc transformHyperlinks() = ## let html = loadHtml("input.html") @@ -51,136 +51,133 @@ import strutils, streams, parsexml, xmltree, unicode, strtabs -when defined(nimPreviewSlimSystem): - import std/syncio - type HtmlTag* = enum ## list of all supported HTML tags; order will always be ## alphabetically tagUnknown, ## unknown HTML element - tagA, ## the HTML `a` element - tagAbbr, ## the deprecated HTML `abbr` element - tagAcronym, ## the HTML `acronym` element - tagAddress, ## the HTML `address` element - tagApplet, ## the deprecated HTML `applet` element - tagArea, ## the HTML `area` element - tagArticle, ## the HTML `article` element - tagAside, ## the HTML `aside` element - tagAudio, ## the HTML `audio` element - tagB, ## the HTML `b` element - tagBase, ## the HTML `base` element - tagBdi, ## the HTML `bdi` element - tagBdo, ## the deprecated HTML `dbo` element - tagBasefont, ## the deprecated HTML `basefont` element - tagBig, ## the HTML `big` element - tagBlockquote, ## the HTML `blockquote` element - tagBody, ## the HTML `body` element - tagBr, ## the HTML `br` element - tagButton, ## the HTML `button` element - tagCanvas, ## the HTML `canvas` element - tagCaption, ## the HTML `caption` element - tagCenter, ## the deprecated HTML `center` element - tagCite, ## the HTML `cite` element - tagCode, ## the HTML `code` element - tagCol, ## the HTML `col` element - tagColgroup, ## the HTML `colgroup` element - tagCommand, ## the HTML `command` element - tagDatalist, ## the HTML `datalist` element - tagDd, ## the HTML `dd` element - tagDel, ## the HTML `del` element - tagDetails, ## the HTML `details` element - tagDfn, ## the HTML `dfn` element - tagDialog, ## the HTML `dialog` element - tagDiv, ## the HTML `div` element - tagDir, ## the deprecated HTLM `dir` element - tagDl, ## the HTML `dl` element - tagDt, ## the HTML `dt` element - tagEm, ## the HTML `em` element - tagEmbed, ## the HTML `embed` element - tagFieldset, ## the HTML `fieldset` element - tagFigcaption, ## the HTML `figcaption` element - tagFigure, ## the HTML `figure` element - tagFont, ## the deprecated HTML `font` element - tagFooter, ## the HTML `footer` element - tagForm, ## the HTML `form` element - tagFrame, ## the HTML `frame` element - tagFrameset, ## the deprecated HTML `frameset` element - tagH1, ## the HTML `h1` element - tagH2, ## the HTML `h2` element - tagH3, ## the HTML `h3` element - tagH4, ## the HTML `h4` element - tagH5, ## the HTML `h5` element - tagH6, ## the HTML `h6` element - tagHead, ## the HTML `head` element - tagHeader, ## the HTML `header` element - tagHgroup, ## the HTML `hgroup` element - tagHtml, ## the HTML `html` element - tagHr, ## the HTML `hr` element - tagI, ## the HTML `i` element - tagIframe, ## the deprecated HTML `iframe` element - tagImg, ## the HTML `img` element - tagInput, ## the HTML `input` element - tagIns, ## the HTML `ins` element - tagIsindex, ## the deprecated HTML `isindex` element - tagKbd, ## the HTML `kbd` element - tagKeygen, ## the HTML `keygen` element - tagLabel, ## the HTML `label` element - tagLegend, ## the HTML `legend` element - tagLi, ## the HTML `li` element - tagLink, ## the HTML `link` element - tagMap, ## the HTML `map` element - tagMark, ## the HTML `mark` element - tagMenu, ## the deprecated HTML `menu` element - tagMeta, ## the HTML `meta` element - tagMeter, ## the HTML `meter` element - tagNav, ## the HTML `nav` element - tagNobr, ## the deprecated HTML `nobr` element - tagNoframes, ## the deprecated HTML `noframes` element - tagNoscript, ## the HTML `noscript` element - tagObject, ## the HTML `object` element - tagOl, ## the HTML `ol` element - tagOptgroup, ## the HTML `optgroup` element - tagOption, ## the HTML `option` element - tagOutput, ## the HTML `output` element - tagP, ## the HTML `p` element - tagParam, ## the HTML `param` element - tagPre, ## the HTML `pre` element - tagProgress, ## the HTML `progress` element - tagQ, ## the HTML `q` element - tagRp, ## the HTML `rp` element - tagRt, ## the HTML `rt` element - tagRuby, ## the HTML `ruby` element - tagS, ## the deprecated HTML `s` element - tagSamp, ## the HTML `samp` element - tagScript, ## the HTML `script` element - tagSection, ## the HTML `section` element - tagSelect, ## the HTML `select` element - tagSmall, ## the HTML `small` element - tagSource, ## the HTML `source` element - tagSpan, ## the HTML `span` element - tagStrike, ## the deprecated HTML `strike` element - tagStrong, ## the HTML `strong` element - tagStyle, ## the HTML `style` element - tagSub, ## the HTML `sub` element - tagSummary, ## the HTML `summary` element - tagSup, ## the HTML `sup` element - tagTable, ## the HTML `table` element - tagTbody, ## the HTML `tbody` element - tagTd, ## the HTML `td` element - tagTextarea, ## the HTML `textarea` element - tagTfoot, ## the HTML `tfoot` element - tagTh, ## the HTML `th` element - tagThead, ## the HTML `thead` element - tagTime, ## the HTML `time` element - tagTitle, ## the HTML `title` element - tagTr, ## the HTML `tr` element - tagTrack, ## the HTML `track` element - tagTt, ## the HTML `tt` element - tagU, ## the deprecated HTML `u` element - tagUl, ## the HTML `ul` element - tagVar, ## the HTML `var` element - tagVideo, ## the HTML `video` element - tagWbr ## the HTML `wbr` element + tagA, ## the HTML ``a`` element + tagAbbr, ## the deprecated HTML ``abbr`` element + tagAcronym, ## the HTML ``acronym`` element + tagAddress, ## the HTML ``address`` element + tagApplet, ## the deprecated HTML ``applet`` element + tagArea, ## the HTML ``area`` element + tagArticle, ## the HTML ``article`` element + tagAside, ## the HTML ``aside`` element + tagAudio, ## the HTML ``audio`` element + tagB, ## the HTML ``b`` element + tagBase, ## the HTML ``base`` element + tagBdi, ## the HTML ``bdi`` element + tagBdo, ## the deprecated HTML ``dbo`` element + tagBasefont, ## the deprecated HTML ``basefont`` element + tagBig, ## the HTML ``big`` element + tagBlockquote, ## the HTML ``blockquote`` element + tagBody, ## the HTML ``body`` element + tagBr, ## the HTML ``br`` element + tagButton, ## the HTML ``button`` element + tagCanvas, ## the HTML ``canvas`` element + tagCaption, ## the HTML ``caption`` element + tagCenter, ## the deprecated HTML ``center`` element + tagCite, ## the HTML ``cite`` element + tagCode, ## the HTML ``code`` element + tagCol, ## the HTML ``col`` element + tagColgroup, ## the HTML ``colgroup`` element + tagCommand, ## the HTML ``command`` element + tagDatalist, ## the HTML ``datalist`` element + tagDd, ## the HTML ``dd`` element + tagDel, ## the HTML ``del`` element + tagDetails, ## the HTML ``details`` element + tagDfn, ## the HTML ``dfn`` element + tagDialog, ## the HTML ``dialog`` element + tagDiv, ## the HTML ``div`` element + tagDir, ## the deprecated HTLM ``dir`` element + tagDl, ## the HTML ``dl`` element + tagDt, ## the HTML ``dt`` element + tagEm, ## the HTML ``em`` element + tagEmbed, ## the HTML ``embed`` element + tagFieldset, ## the HTML ``fieldset`` element + tagFigcaption, ## the HTML ``figcaption`` element + tagFigure, ## the HTML ``figure`` element + tagFont, ## the deprecated HTML ``font`` element + tagFooter, ## the HTML ``footer`` element + tagForm, ## the HTML ``form`` element + tagFrame, ## the HTML ``frame`` element + tagFrameset, ## the deprecated HTML ``frameset`` element + tagH1, ## the HTML ``h1`` element + tagH2, ## the HTML ``h2`` element + tagH3, ## the HTML ``h3`` element + tagH4, ## the HTML ``h4`` element + tagH5, ## the HTML ``h5`` element + tagH6, ## the HTML ``h6`` element + tagHead, ## the HTML ``head`` element + tagHeader, ## the HTML ``header`` element + tagHgroup, ## the HTML ``hgroup`` element + tagHtml, ## the HTML ``html`` element + tagHr, ## the HTML ``hr`` element + tagI, ## the HTML ``i`` element + tagIframe, ## the deprecated HTML ``iframe`` element + tagImg, ## the HTML ``img`` element + tagInput, ## the HTML ``input`` element + tagIns, ## the HTML ``ins`` element + tagIsindex, ## the deprecated HTML ``isindex`` element + tagKbd, ## the HTML ``kbd`` element + tagKeygen, ## the HTML ``keygen`` element + tagLabel, ## the HTML ``label`` element + tagLegend, ## the HTML ``legend`` element + tagLi, ## the HTML ``li`` element + tagLink, ## the HTML ``link`` element + tagMap, ## the HTML ``map`` element + tagMark, ## the HTML ``mark`` element + tagMenu, ## the deprecated HTML ``menu`` element + tagMeta, ## the HTML ``meta`` element + tagMeter, ## the HTML ``meter`` element + tagNav, ## the HTML ``nav`` element + tagNobr, ## the deprecated HTML ``nobr`` element + tagNoframes, ## the deprecated HTML ``noframes`` element + tagNoscript, ## the HTML ``noscript`` element + tagObject, ## the HTML ``object`` element + tagOl, ## the HTML ``ol`` element + tagOptgroup, ## the HTML ``optgroup`` element + tagOption, ## the HTML ``option`` element + tagOutput, ## the HTML ``output`` element + tagP, ## the HTML ``p`` element + tagParam, ## the HTML ``param`` element + tagPre, ## the HTML ``pre`` element + tagProgress, ## the HTML ``progress`` element + tagQ, ## the HTML ``q`` element + tagRp, ## the HTML ``rp`` element + tagRt, ## the HTML ``rt`` element + tagRuby, ## the HTML ``ruby`` element + tagS, ## the deprecated HTML ``s`` element + tagSamp, ## the HTML ``samp`` element + tagScript, ## the HTML ``script`` element + tagSection, ## the HTML ``section`` element + tagSelect, ## the HTML ``select`` element + tagSmall, ## the HTML ``small`` element + tagSource, ## the HTML ``source`` element + tagSpan, ## the HTML ``span`` element + tagStrike, ## the deprecated HTML ``strike`` element + tagStrong, ## the HTML ``strong`` element + tagStyle, ## the HTML ``style`` element + tagSub, ## the HTML ``sub`` element + tagSummary, ## the HTML ``summary`` element + tagSup, ## the HTML ``sup`` element + tagTable, ## the HTML ``table`` element + tagTbody, ## the HTML ``tbody`` element + tagTd, ## the HTML ``td`` element + tagTextarea, ## the HTML ``textarea`` element + tagTfoot, ## the HTML ``tfoot`` element + tagTh, ## the HTML ``th`` element + tagThead, ## the HTML ``thead`` element + tagTime, ## the HTML ``time`` element + tagTitle, ## the HTML ``title`` element + tagTr, ## the HTML ``tr`` element + tagTrack, ## the HTML ``track`` element + tagTt, ## the HTML ``tt`` element + tagU, ## the deprecated HTML ``u`` element + tagUl, ## the HTML ``ul`` element + tagVar, ## the HTML ``var`` element + tagVideo, ## the HTML ``video`` element + tagWbr ## the HTML ``wbr`` element const tagToStr* = [ @@ -218,7 +215,7 @@ const tagMenu, tagNoframes} SingleTags* = {tagArea, tagBase, tagBasefont, tagBr, tagCol, tagFrame, tagHr, tagImg, tagIsindex, - tagLink, tagMeta, tagParam, tagWbr, tagSource} + tagLink, tagMeta, tagParam, tagWbr} proc allLower(s: string): bool = for c in s: @@ -354,13 +351,13 @@ proc toHtmlTag(s: string): HtmlTag = proc htmlTag*(n: XmlNode): HtmlTag = - ## Gets `n`'s tag as a `HtmlTag`. + ## Gets `n`'s tag as a ``HtmlTag``. if n.clientData == 0: n.clientData = toHtmlTag(n.tag).ord result = HtmlTag(n.clientData) proc htmlTag*(s: string): HtmlTag = - ## Converts `s` to a `HtmlTag`. If `s` is no HTML tag, `tagUnknown` is + ## Converts `s` to a ``HtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is ## returned. let s = if allLower(s): s else: toLowerAscii(s) result = toHtmlTag(s) @@ -368,7 +365,7 @@ proc htmlTag*(s: string): HtmlTag = proc runeToEntity*(rune: Rune): string = ## converts a Rune to its numeric HTML entity equivalent. runnableExamples: - import std/unicode + import unicode doAssert runeToEntity(Rune(0)) == "" doAssert runeToEntity(Rune(-1)) == "" doAssert runeToEntity("Ü".runeAt(0)) == "#220" @@ -377,11 +374,11 @@ proc runeToEntity*(rune: Rune): string = else: result = '#' & $rune.ord proc entityToRune*(entity: string): Rune = - ## Converts an HTML entity name like `Ü` or values like `Ü` - ## or `Ü` to its UTF-8 equivalent. + ## Converts an HTML entity name like ``Ü`` or values like ``Ü`` + ## or ``Ü`` to its UTF-8 equivalent. ## Rune(0) is returned if the entity name is unknown. runnableExamples: - import std/unicode + import unicode doAssert entityToRune("") == Rune(0) doAssert entityToRune("a") == Rune(0) doAssert entityToRune("gt") == ">".runeAt(0) @@ -394,11 +391,11 @@ proc entityToRune*(entity: string): Rune = case entity[1] of '0'..'9': try: runeValue = parseInt(entity[1..^1]) - except ValueError: discard + except: discard of 'x', 'X': # not case sensitive here try: runeValue = parseHexInt(entity[2..^1]) - except ValueError: discard - else: discard # other entities are not defined with prefix `#` + except: discard + else: discard # other entities are not defined with prefix ``#`` if runeValue notin 0..0x10FFFF: runeValue = 0 # only return legal values return Rune(runeValue) case entity # entity names are case sensitive @@ -1870,8 +1867,8 @@ proc entityToRune*(entity: string): Rune = else: Rune(0) proc entityToUtf8*(entity: string): string = - ## Converts an HTML entity name like `Ü` or values like `Ü` - ## or `Ü` to its UTF-8 equivalent. + ## Converts an HTML entity name like ``Ü`` or values like ``Ü`` + ## or ``Ü`` to its UTF-8 equivalent. ## "" is returned if the entity name is unknown. The HTML parser ## already converts entities to UTF-8. runnableExamples: @@ -1908,7 +1905,7 @@ template adderr(x: untyped) = proc untilElementEnd(x: var XmlParser, result: XmlNode, errors: var seq[string]) = - # we parsed e.g. `
    ` and don't really expect a `
    `: + # we parsed e.g. ``
    `` and don't really expect a ``
    ``: if result.htmlTag in SingleTags: if x.kind != xmlElementEnd or cmpIgnoreCase(x.elemName, result.tag) != 0: return @@ -1917,8 +1914,8 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode, of xmlElementStart, xmlElementOpen: case result.htmlTag of tagP, tagInput, tagOption: - # some tags are common to have no ``, like `
  • ` but - # allow `

    ` in `

    `, `
    ` and `
  • ` in next case + # some tags are common to have no ````, like ``
  • `` but + # allow ``

    `` in `

    `, `
    ` and ``
  • `` in next case if htmlTag(x.elemName) in {tagLi, tagP, tagDt, tagDd, tagInput, tagOption}: adderr(expected(x, result)) @@ -2015,7 +2012,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = proc parseHtml*(s: Stream, filename: string, errors: var seq[string]): XmlNode = - ## Parses the XML from stream `s` and returns a `XmlNode`. Every + ## Parses the XML from stream `s` and returns a ``XmlNode``. Every ## occurred parsing error is added to the `errors` sequence. var x: XmlParser open(x, s, filename, {reportComments, reportWhitespace, allowUnquotedAttribs, @@ -2039,27 +2036,27 @@ proc parseHtml*(s: Stream, filename: string, result = result[0] proc parseHtml*(s: Stream): XmlNode = - ## Parses the HTML from stream `s` and returns a `XmlNode`. All parsing + ## Parses the HTML from stream `s` and returns a ``XmlNode``. All parsing ## errors are ignored. var errors: seq[string] = @[] result = parseHtml(s, "unknown_html_doc", errors) proc parseHtml*(html: string): XmlNode = - ## Parses the HTML from string `html` and returns a `XmlNode`. All parsing + ## Parses the HTML from string ``html`` and returns a ``XmlNode``. All parsing ## errors are ignored. parseHtml(newStringStream(html)) proc loadHtml*(path: string, errors: var seq[string]): XmlNode = - ## Loads and parses HTML from file specified by `path`, and returns - ## a `XmlNode`. Every occurred parsing error is added to + ## Loads and parses HTML from file specified by ``path``, and returns + ## a ``XmlNode``. Every occurred parsing error is added to ## the `errors` sequence. var s = newFileStream(path, fmRead) if s == nil: raise newException(IOError, "Unable to read file: " & path) result = parseHtml(s, path, errors) proc loadHtml*(path: string): XmlNode = - ## Loads and parses HTML from file specified by `path`, and returns - ## a `XmlNode`. All parsing errors are ignored. + ## Loads and parses HTML from file specified by ``path``, and returns + ## a ``XmlNode``. All parsing errors are ignored. var errors: seq[string] = @[] result = loadHtml(path, errors) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index d2cf64149..70f3327b1 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -14,47 +14,37 @@ ## ==================== ## ## This example uses HTTP GET to retrieve -## `http://google.com`: +## ``http://google.com``: ## ## .. code-block:: Nim -## import std/httpclient +## import httpclient ## var client = newHttpClient() -## try: -## echo client.getContent("http://google.com") -## finally: -## client.close() +## echo client.getContent("http://google.com") ## ## The same action can also be performed asynchronously, simply use the -## `AsyncHttpClient`: +## ``AsyncHttpClient``: ## ## .. code-block:: Nim -## import std/[asyncdispatch, httpclient] +## import asyncdispatch, httpclient ## ## proc asyncProc(): Future[string] {.async.} = ## var client = newAsyncHttpClient() -## try: -## return await client.getContent("http://google.com") -## finally: -## client.close() +## return await client.getContent("http://example.com") ## ## echo waitFor asyncProc() ## -## The functionality implemented by `HttpClient` and `AsyncHttpClient` +## The functionality implemented by ``HttpClient`` and ``AsyncHttpClient`` ## is the same, so you can use whichever one suits you best in the examples ## shown here. ## ## **Note:** You need to run asynchronous examples in an async proc -## otherwise you will get an `Undeclared identifier: 'await'` error. -## -## **Note:** An asynchronous client instance can only deal with one -## request at a time. To send multiple requests in parallel, use -## multiple client instances. +## otherwise you will get an ``Undeclared identifier: 'await'`` error. ## ## Using HTTP POST ## =============== ## ## This example demonstrates the usage of the W3 HTML Validator, it -## uses `multipart/form-data` as the `Content-Type` to send the HTML to be +## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to be ## validated to the server. ## ## .. code-block:: Nim @@ -63,43 +53,36 @@ ## data["output"] = "soap12" ## data["uploaded_file"] = ("test.html", "text/html", ## "

    test

    ") -## 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`. +## echo client.postContent("http://validator.w3.org/check", multipart=data) ## -## **Note:** This will allocate a new `Mimetypes` database every time you call -## it, you can pass your own via the `mimeDb` parameter to avoid this. +## To stream files from disk when performing the request, use ``addFiles``. +## +## **Note:** This will allocate a new ``Mimetypes`` database every time you call +## it, you can pass your own via the ``mimeDb`` parameter to avoid this. ## ## .. code-block:: Nim ## let mimes = newMimetypes() ## var client = newHttpClient() ## var data = newMultipartData() ## data.addFiles({"uploaded_file": "test.html"}, mimeDb = mimes) -## try: -## echo client.postContent("http://validator.w3.org/check", multipart=data) -## finally: -## client.close() +## +## echo client.postContent("http://validator.w3.org/check", multipart=data) ## ## You can also make post requests with custom headers. -## This example sets `Content-Type` to `application/json` +## This example sets ``Content-Type`` to ``application/json`` ## and uses a json object for the body ## ## .. code-block:: Nim -## import std/[httpclient, json] +## import httpclient, json ## ## let client = newHttpClient() ## client.headers = newHttpHeaders({ "Content-Type": "application/json" }) ## let body = %*{ ## "data": "some text" ## } -## try: -## let response = client.request("http://some.api", httpMethod = HttpPost, body = $body) -## echo response.status -## finally: -## client.close() +## let response = client.request("http://some.api", httpMethod = HttpPost, body = $body) +## echo response.status ## ## Progress reporting ## ================== @@ -109,7 +92,7 @@ ## progress of the HTTP request. ## ## .. code-block:: Nim -## import std/[asyncdispatch, httpclient] +## import asyncdispatch, httpclient ## ## proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} = ## echo("Downloaded ", progress, " of ", total) @@ -118,50 +101,34 @@ ## proc asyncProc() {.async.} = ## var client = newAsyncHttpClient() ## client.onProgressChanged = onProgressChanged -## try: -## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") -## finally: -## client.close() +## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") ## ## waitFor asyncProc() ## -## If you would like to remove the callback simply set it to `nil`. +## If you would like to remove the callback simply set it to ``nil``. ## ## .. code-block:: Nim ## client.onProgressChanged = nil ## -## .. warning:: The `total` reported by httpclient may be 0 in some cases. +## **Warning:** The ``total`` reported by httpclient may be 0 in some cases. ## ## ## SSL/TLS support ## =============== -## This requires the OpenSSL library. Fortunately it's widely used and installed +## This requires the OpenSSL library, fortunately it's widely used and installed ## on many operating systems. httpclient will use SSL automatically if you give -## any of the functions a url with the `https` schema, for example: -## `https://github.com/`. +## any of the functions a url with the ``https`` schema, for example: +## ``https://github.com/``. ## -## You will also have to compile with `ssl` defined like so: -## `nim c -d:ssl ...`. +## You will also have to compile with ``ssl`` defined like so: +## ``nim c -d:ssl ...``. ## -## Certificate validation is performed by default. +## Certificate validation is NOT performed by default. +## This will change in future. ## ## A set of directories and files from the `ssl_certs `_ ## module are scanned to locate CA certificates. ## -## Example of setting SSL verification parameters in a new client: -## -## .. code-block:: Nim -## import httpclient -## var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer)) -## -## There are three options for verify mode: -## -## * ``CVerifyNone``: certificates are not verified; -## * ``CVerifyPeer``: certificates are verified; -## * ``CVerifyPeerUseEnvVars``: certificates are verified and the optional -## environment variables SSL_CERT_FILE and SSL_CERT_DIR are also used to -## locate certificates -## ## See `newContext `_ to tweak or disable certificate validation. ## ## Timeouts @@ -177,12 +144,12 @@ ## individual internal calls on the socket are affected. In practice this means ## that as long as the server is sending data an exception will not be raised, ## if however data does not reach the client within the specified timeout a -## `TimeoutError` exception will be raised. +## ``TimeoutError`` exception will be raised. ## -## Here is how to set a timeout when creating an `HttpClient` instance: +## Here is how to set a timeout when creating an ``HttpClient`` instance: ## ## .. code-block:: Nim -## import std/httpclient +## import httpclient ## ## let client = newHttpClient(timeout = 42) ## @@ -190,29 +157,21 @@ ## ===== ## ## A proxy can be specified as a param to any of the procedures defined in -## this module. To do this, use the `newProxy` constructor. Unfortunately, +## this module. To do this, use the ``newProxy`` constructor. Unfortunately, ## only basic authentication is supported at the moment. ## -## Some examples on how to configure a Proxy for `HttpClient`: +## Some examples on how to configure a Proxy for ``HttpClient``: ## ## .. code-block:: Nim -## import std/httpclient +## import httpclient ## ## let myProxy = newProxy("http://myproxy.network") ## let client = newHttpClient(proxy = myProxy) ## -## Use proxies with basic authentication: -## -## .. code-block:: Nim -## import std/httpclient -## -## let myProxy = newProxy("http://myproxy.network", auth="user:password") -## let client = newHttpClient(proxy = myProxy) -## ## Get Proxy URL from environment variables: ## ## .. code-block:: Nim -## import std/httpclient +## import httpclient ## ## var url = "" ## try: @@ -229,30 +188,26 @@ ## Redirects ## ========= ## -## The maximum redirects can be set with the `maxRedirects` of `int` type, +## The maximum redirects can be set with the ``maxRedirects`` of ``int`` type, ## it specifies the maximum amount of redirects to follow, -## it defaults to `5`, you can set it to `0` to disable redirects. +## it defaults to ``5``, you can set it to ``0`` to disable redirects. ## -## Here you can see an example about how to set the `maxRedirects` of `HttpClient`: +## Here you can see an example about how to set the ``maxRedirects`` of ``HttpClient``: ## ## .. code-block:: Nim -## import std/httpclient +## import httpclient ## ## let client = newHttpClient(maxRedirects = 0) ## import std/private/since -import std/[ - net, strutils, uri, parseutils, base64, os, mimetypes, - math, random, httpcore, times, tables, streams, monotimes, - asyncnet, asyncdispatch, asyncfile, nativesockets, -] +import net, strutils, uri, parseutils, base64, os, mimetypes, streams, + math, random, httpcore, times, tables, streams, std/monotimes +import asyncnet, asyncdispatch, asyncfile +import nativesockets -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - -export httpcore except parseHeader # TODO: The `except` doesn't work +export httpcore except parseHeader # TODO: The ``except`` doesn't work type Response* = ref object @@ -271,10 +226,10 @@ type proc code*(response: Response | AsyncResponse): HttpCode {.raises: [ValueError, OverflowDefect].} = - ## Retrieves the specified response's `HttpCode`. + ## Retrieves the specified response's ``HttpCode``. ## - ## Raises a `ValueError` if the response's `status` does not have a - ## corresponding `HttpCode`. + ## Raises a ``ValueError`` if the response's ``status`` does not have a + ## corresponding ``HttpCode``. return response.status[0 .. 2].parseInt.HttpCode proc contentType*(response: Response | AsyncResponse): string {.inline.} = @@ -288,17 +243,17 @@ 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. - ## 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"]) + ## A ``ValueError`` exception will be raised if the value is not an integer. + var contentLengthHeader = response.headers.getOrDefault("Content-Length") result = contentLengthHeader.parseInt() + doAssert(result >= 0 and result <= high(int32)) proc lastModified*(response: Response | AsyncResponse): DateTime = ## Retrieves the specified response's last modified time. ## ## This is effectively the value of the "Last-Modified" header. ## - ## Raises a `ValueError` if the parsing fails or the value is not a correctly + ## Raises a ``ValueError`` if the parsing fails or the value is not a correctly ## formatted time. var lastModifiedHeader = response.headers.getOrDefault("last-modified") result = parse(lastModifiedHeader, "ddd, dd MMM yyyy HH:mm:ss 'GMT'", utc()) @@ -340,8 +295,8 @@ type ## does not conform to the implemented ## protocol - HttpRequestError* = object of IOError ## Thrown in the `getContent` proc - ## and `postContent` proc, + HttpRequestError* = object of IOError ## Thrown in the ``getContent`` proc + ## and ``postContent`` proc, ## when the server returns an error const defUserAgent* = "Nim httpclient/" & NimVersion @@ -366,20 +321,16 @@ proc getDefaultSSL(): SslContext = result = defaultSslContext when defined(ssl): if result == nil: - defaultSslContext = newContext(verifyMode = CVerifyPeer) + defaultSslContext = newContext(verifyMode = CVerifyNone) result = defaultSslContext doAssert result != nil, "failure to initialize the SSL context" -proc newProxy*(url: string; auth = ""): Proxy = - ## Constructs a new `TProxy` object. +proc newProxy*(url: string, auth = ""): Proxy = + ## Constructs a new ``TProxy`` object. result = Proxy(url: parseUri(url), auth: auth) -proc newProxy*(url: Uri; auth = ""): Proxy = - ## Constructs a new `TProxy` object. - result = Proxy(url: url, auth: auth) - proc newMultipartData*: MultipartData {.inline.} = - ## Constructs a new `MultipartData` object. + ## Constructs a new ``MultipartData`` object. MultipartData() proc `$`*(data: MultipartData): string {.since: (1, 1).} = @@ -398,10 +349,10 @@ proc add*(p: MultipartData, name, content: string, filename: string = "", contentType: string = "", useStream = true) = ## Add a value to the multipart data. ## - ## When `useStream` is `false`, the file will be read into memory. + ## When ``useStream`` is ``false``, the file will be read into memory. ## - ## Raises a `ValueError` exception if - ## `name`, `filename` or `contentType` contain newline characters. + ## Raises a ``ValueError`` exception if + ## ``name``, ``filename`` or ``contentType`` contain newline characters. if {'\c', '\L'} in name: raise newException(ValueError, "name contains a newline character") if {'\c', '\L'} in filename: @@ -424,7 +375,7 @@ proc add*(p: MultipartData, name, content: string, filename: string = "", proc add*(p: MultipartData, xs: MultipartEntries): MultipartData {.discardable.} = - ## Add a list of multipart entries to the multipart data `p`. All values are + ## Add a list of multipart entries to the multipart data ``p``. All values are ## added without a filename and without a content type. ## ## .. code-block:: Nim @@ -434,7 +385,7 @@ proc add*(p: MultipartData, xs: MultipartEntries): MultipartData result = p proc newMultipartData*(xs: MultipartEntries): MultipartData = - ## Create a new multipart data object and fill it with the entries `xs` + ## Create a new multipart data object and fill it with the entries ``xs`` ## directly. ## ## .. code-block:: Nim @@ -447,11 +398,11 @@ proc addFiles*(p: MultipartData, xs: openArray[tuple[name, file: string]], mimeDb = newMimetypes(), useStream = true): MultipartData {.discardable.} = ## Add files to a multipart data object. The files will be streamed from disk - ## when the request is being made. When `stream` is `false`, the files are + ## when the request is being made. When ``stream`` is ``false``, the files are ## instead read into memory, but beware this is very memory ineffecient even ## for small files. The MIME types will automatically be determined. - ## Raises an `IOError` if the file cannot be opened or reading fails. To - ## manually specify file content, filename and MIME type, use `[]=` instead. + ## Raises an ``IOError`` if the file cannot be opened or reading fails. To + ## manually specify file content, filename and MIME type, use ``[]=`` instead. ## ## .. code-block:: Nim ## data.addFiles({"uploaded_file": "public/test.html"}) @@ -460,12 +411,12 @@ proc addFiles*(p: MultipartData, xs: openArray[tuple[name, file: string]], let (_, fName, ext) = splitFile(file) if ext.len > 0: contentType = mimeDb.getMimetype(ext[1..ext.high], "") - let content = if useStream: file else: readFile(file) + let content = if useStream: file else: readFile(file).string p.add(name, content, fName & ext, contentType, useStream = useStream) result = p proc `[]=`*(p: MultipartData, name, content: string) {.inline.} = - ## Add a multipart entry to the multipart data `p`. The value is added + ## Add a multipart entry to the multipart data ``p``. The value is added ## without a filename and without a content type. ## ## .. code-block:: Nim @@ -474,7 +425,7 @@ proc `[]=`*(p: MultipartData, name, content: string) {.inline.} = proc `[]=`*(p: MultipartData, name: string, file: tuple[name, contentType, content: string]) {.inline.} = - ## Add a file to the multipart data `p`, specifying filename, contentType + ## Add a file to the multipart data ``p``, specifying filename, contentType ## and content manually. ## ## .. code-block:: Nim @@ -500,29 +451,35 @@ proc sendFile(socket: Socket | AsyncSocket, var buffer: string while true: buffer = - when socket is AsyncSocket: (await read(file, chunkSize)) - else: readStr(file, chunkSize) + when socket is AsyncSocket: (await read(file, chunkSize)).string + else: readStr(file, chunkSize).string if buffer.len == 0: break await socket.send(buffer) file.close() -proc getNewLocation(lastURL: Uri, headers: HttpHeaders): Uri = - let newLocation = headers.getOrDefault"Location" - if newLocation == "": httpError("location header expected") - # Relative URLs. (Not part of the spec, but soon will be.) - let parsedLocation = parseUri(newLocation) - if parsedLocation.hostname == "" and parsedLocation.path != "": - result = lastURL - result.path = parsedLocation.path - result.query = parsedLocation.query - result.anchor = parsedLocation.anchor - else: - result = parsedLocation +proc redirection(status: string): bool = + const redirectionNRs = ["301", "302", "303", "307", "308"] + for i in items(redirectionNRs): + if status.startsWith(i): + return true -proc generateHeaders(requestUrl: Uri, httpMethod: HttpMethod, headers: HttpHeaders, +proc getNewLocation(lastURL: string, headers: HttpHeaders): string = + result = headers.getOrDefault"Location" + if result == "": httpError("location header expected") + # Relative URLs. (Not part of the spec, but soon will be.) + let r = parseUri(result) + if r.hostname == "" and r.path != "": + var parsed = parseUri(lastURL) + parsed.path = r.path + parsed.query = r.query + parsed.anchor = r.anchor + result = $parsed + +proc generateHeaders(requestUrl: Uri, httpMethod: string, headers: HttpHeaders, proxy: Proxy): string = # GET - result = $httpMethod + let upperMethod = httpMethod.toUpperAscii() + result = upperMethod result.add ' ' if proxy.isNil or requestUrl.scheme == "https": @@ -554,7 +511,7 @@ proc generateHeaders(requestUrl: Uri, httpMethod: HttpMethod, headers: HttpHeade # Proxy auth header. if not proxy.isNil and proxy.auth != "": let auth = base64.encode(proxy.auth) - add(result, "Proxy-Authorization: Basic " & auth & httpNewLine) + add(result, "Proxy-Authorization: basic " & auth & httpNewLine) for key, val in headers: add(result, key & ": " & val & httpNewLine) @@ -571,11 +528,11 @@ type connected: bool currentURL: Uri ## Where we are currently connected. headers*: HttpHeaders ## Headers to send in requests. - maxRedirects: Natural ## Maximum redirects, set to `0` to disable. + maxRedirects: Natural ## Maximum redirects, set to ``0`` to disable. userAgent: string timeout*: int ## Only used for blocking HttpClient for now. proxy: Proxy - ## `nil` or the callback to call when request progress changes. + ## ``nil`` or the callback to call when request progress changes. when SocketType is Socket: onProgressChanged*: ProgressChangedProc[void] else: @@ -601,28 +558,32 @@ proc newHttpClient*(userAgent = defUserAgent, maxRedirects = 5, timeout = -1, headers = newHttpHeaders()): HttpClient = ## Creates a new HttpClient instance. ## - ## `userAgent` specifies the user agent that will be used when making + ## ``userAgent`` specifies the user agent that will be used when making ## requests. ## - ## `maxRedirects` specifies the maximum amount of redirects to follow, + ## ``maxRedirects`` specifies the maximum amount of redirects to follow, ## default is 5. ## - ## `sslContext` specifies the SSL context to use for HTTPS requests. + ## ``sslContext`` specifies the SSL context to use for HTTPS requests. ## See `SSL/TLS support <#sslslashtls-support>`_ ## - ## `proxy` specifies an HTTP proxy to use for this HTTP client's + ## ``proxy`` specifies an HTTP proxy to use for this HTTP client's ## connections. ## - ## `timeout` specifies the number of milliseconds to allow before a - ## `TimeoutError` is raised. + ## ``timeout`` specifies the number of milliseconds to allow before a + ## ``TimeoutError`` is raised. ## - ## `headers` specifies the HTTP Headers. + ## ``headers`` specifies the HTTP Headers. runnableExamples: - import std/strutils + import asyncdispatch, httpclient, strutils - let exampleHtml = newHttpClient().getContent("http://example.com") + proc asyncProc(): Future[string] {.async.} = + var client = newAsyncHttpClient() + return await client.getContent("http://example.com") + + let exampleHtml = waitFor asyncProc() assert "Example Domain" in exampleHtml - assert "Pizza" notin exampleHtml + assert not ("Pizza" in exampleHtml) new result result.headers = headers @@ -644,29 +605,18 @@ proc newAsyncHttpClient*(userAgent = defUserAgent, maxRedirects = 5, headers = newHttpHeaders()): AsyncHttpClient = ## Creates a new AsyncHttpClient instance. ## - ## `userAgent` specifies the user agent that will be used when making + ## ``userAgent`` specifies the user agent that will be used when making ## requests. ## - ## `maxRedirects` specifies the maximum amount of redirects to follow, + ## ``maxRedirects`` specifies the maximum amount of redirects to follow, ## default is 5. ## - ## `sslContext` specifies the SSL context to use for HTTPS requests. + ## ``sslContext`` specifies the SSL context to use for HTTPS requests. ## - ## `proxy` specifies an HTTP proxy to use for this HTTP client's + ## ``proxy`` specifies an HTTP proxy to use for this HTTP client's ## connections. ## - ## `headers` specifies the HTTP Headers. - runnableExamples: - import std/[asyncdispatch, strutils] - - proc asyncProc(): Future[string] {.async.} = - let client = newAsyncHttpClient() - result = await client.getContent("http://example.com") - - let exampleHtml = waitFor asyncProc() - assert "Example Domain" in exampleHtml - assert "Pizza" notin exampleHtml - + ## ``headers`` specifies the HTTP Headers. new result result.headers = headers result.userAgent = userAgent @@ -704,7 +654,7 @@ proc reportProgress(client: HttpClient | AsyncHttpClient, progress: BiggestInt) {.multisync.} = client.contentProgress += progress client.oneSecondProgress += progress - if (getMonoTime() - client.lastProgressReport).inSeconds >= 1: + if (getMonoTime() - client.lastProgressReport).inSeconds > 1: if not client.onProgressChanged.isNil: await client.onProgressChanged(client.contentTotal, client.contentProgress, @@ -742,7 +692,7 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void] {.multisync.} = while true: var chunkSize = 0 - var chunkSizeStr = await client.socket.recvLine() + var chunkSizeStr = (await client.socket.recvLine()).string var i = 0 if chunkSizeStr == "": httpError("Server terminated connection prematurely") @@ -802,7 +752,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient, headers: HttpHeaders, httpError("Got disconnected while trying to read body.") if recvLen != length: httpError("Received length doesn't match expected length. Wanted " & - $length & " got: " & $recvLen) + $length & " got " & $recvLen) else: # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO @@ -841,9 +791,9 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, while true: linei = 0 when client is HttpClient: - line = await client.socket.recvLine(client.timeout) + line = (await client.socket.recvLine(client.timeout)).string else: - line = await client.socket.recvLine() + line = (await client.socket.recvLine()).string if line == "": # We've been disconnected. client.close() @@ -884,25 +834,17 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, if not fullyRead: httpError("Connection was closed before full request has been made") - when client is HttpClient: - result.bodyStream = newStringStream() - else: - result.bodyStream = newFutureStream[string]("parseResponse") - if getBody and result.code != Http204: - client.bodyStream = result.bodyStream when client is HttpClient: + client.bodyStream = newStringStream() + result.bodyStream = client.bodyStream parseBody(client, result.headers, result.version) else: + client.bodyStream = newFutureStream[string]("parseResponse") + result.bodyStream = client.bodyStream assert(client.parseBodyFut.isNil or client.parseBodyFut.finished) - # do not wait here for the body request to complete client.parseBodyFut = parseBody(client, result.headers, result.version) - client.parseBodyFut.addCallback do(): - if client.parseBodyFut.failed: - client.bodyStream.fail(client.parseBodyFut.error) - else: - when client is AsyncHttpClient: - result.bodyStream.complete() + # do not wait here for the body request to complete proc newConnection(client: HttpClient | AsyncHttpClient, url: Uri) {.multisync.} = @@ -956,7 +898,7 @@ proc newConnection(client: HttpClient | AsyncHttpClient, connectUrl.hostname = url.hostname connectUrl.port = if url.port != "": url.port else: "443" - let proxyHeaderString = generateHeaders(connectUrl, HttpConnect, + let proxyHeaderString = generateHeaders(connectUrl, $HttpConnect, newHttpHeaders(), client.proxy) await client.socket.send(proxyHeaderString) let proxyResp = await parseResponse(client, false) @@ -1009,62 +951,54 @@ proc format(client: HttpClient | AsyncHttpClient, if entry.isFile: length += entry.fileSize + httpNewLine.len - result.add "--" & bound & "--" & httpNewLine + result.add "--" & bound & "--" for s in result: length += s.len client.headers["Content-Length"] = $length proc override(fallback, override: HttpHeaders): HttpHeaders = # Right-biased map union for `HttpHeaders` + if override.isNil: + return fallback result = newHttpHeaders() # Copy by value result.table[] = fallback.table[] - - if override.isNil: - # Return the copy of fallback so it does not get modified - return result - for k, vs in override.table: result[k] = vs -proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri, - httpMethod: HttpMethod, body = "", headers: HttpHeaders = nil, +proc requestAux(client: HttpClient | AsyncHttpClient, url, httpMethod: string, + body = "", headers: HttpHeaders = nil, multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = # Helper that actually makes the request. Does not handle redirects. - if url.scheme == "": + let requestUrl = parseUri(url) + + if requestUrl.scheme == "": raise newException(ValueError, "No uri scheme supplied.") + var data: seq[string] + if multipart != nil and multipart.content.len > 0: + data = await client.format(multipart) + else: + if body.len != 0: + client.headers["Content-Length"] = $body.len + elif httpMethod notin ["GET", "HEAD"] and not client.headers.hasKey("Content-Length"): + client.headers["Content-Length"] = "0" + when client is AsyncHttpClient: if not client.parseBodyFut.isNil: # let the current operation finish before making another request await client.parseBodyFut client.parseBodyFut = nil - await newConnection(client, url) - - var newHeaders: HttpHeaders - - var data: seq[string] - if multipart != nil and multipart.content.len > 0: - # `format` modifies `client.headers`, see - # https://github.com/nim-lang/Nim/pull/18208#discussion_r647036979 - data = await client.format(multipart) - newHeaders = client.headers.override(headers) - else: - newHeaders = client.headers.override(headers) - # Only change headers if they have not been specified already - if not newHeaders.hasKey("Content-Length"): - if body.len != 0: - newHeaders["Content-Length"] = $body.len - elif httpMethod notin {HttpGet, HttpHead}: - newHeaders["Content-Length"] = "0" + await newConnection(client, requestUrl) + let newHeaders = client.headers.override(headers) if not newHeaders.hasKey("user-agent") and client.userAgent.len > 0: newHeaders["User-Agent"] = client.userAgent - let headerString = generateHeaders(url, httpMethod, newHeaders, + let headerString = generateHeaders(requestUrl, httpMethod, newHeaders, client.proxy) await client.socket.send(headerString) @@ -1086,116 +1020,58 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri, elif body.len > 0: await client.socket.send(body) - let getBody = httpMethod notin {HttpHead, HttpConnect} and + let getBody = httpMethod.toLowerAscii() notin ["head", "connect"] and client.getBody result = await parseResponse(client, getBody) -proc request*(client: HttpClient | AsyncHttpClient, url: Uri | string, - httpMethod: HttpMethod | string = HttpGet, body = "", - headers: HttpHeaders = nil, +proc request*(client: HttpClient | AsyncHttpClient, url: string, + httpMethod: string, body = "", headers: HttpHeaders = nil, multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a request - ## using the custom method string specified by `httpMethod`. + ## using the custom method string specified by ``httpMethod``. ## - ## Connection will be kept alive. Further requests on the same `client` to + ## Connection will be kept alive. Further requests on the same ``client`` to ## the same hostname will not require a new connection to be made. The - ## connection can be closed by using the `close` procedure. + ## connection can be closed by using the ``close`` procedure. ## ## This procedure will follow redirects up to a maximum number of redirects - ## specified in `client.maxRedirects`. + ## specified in ``client.maxRedirects``. ## - ## You need to make sure that the `url` doesn't contain any newline - ## characters. Failing to do so will raise `AssertionDefect`. - ## - ## `headers` are HTTP headers that override the `client.headers` for - ## this specific request only and will not be persisted. - ## - ## **Deprecated since v1.5**: use HttpMethod enum instead; string parameter httpMethod is deprecated - when url is string: - doAssert(not url.contains({'\c', '\L'}), "url shouldn't contain any newline characters") - let url = parseUri(url) - - when httpMethod is string: - {.warning: - "Deprecated since v1.5; use HttpMethod enum instead; string parameter httpMethod is deprecated".} - let httpMethod = case httpMethod - of "HEAD": - HttpHead - of "GET": - HttpGet - of "POST": - HttpPost - of "PUT": - HttpPut - of "DELETE": - HttpDelete - of "TRACE": - HttpTrace - of "OPTIONS": - HttpOptions - of "CONNECT": - HttpConnect - of "PATCH": - HttpPatch - else: - raise newException(ValueError, "Invalid HTTP method name: " & httpMethod) + ## You need to make sure that the ``url`` doesn't contain any newline + ## characters. Failing to do so will raise ``AssertionDefect``. + doAssert(not url.contains({'\c', '\L'}), "url shouldn't contain any newline characters") result = await client.requestAux(url, httpMethod, body, headers, multipart) var lastURL = url for i in 1..client.maxRedirects: - let statusCode = result.code + if result.status.redirection(): + let redirectTo = getNewLocation(lastURL, result.headers) + # Guarantee method for HTTP 307: see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307 + var meth = if result.status == "307": httpMethod else: "GET" + result = await client.requestAux(redirectTo, meth, body, headers, multipart) + lastURL = redirectTo - if statusCode notin {Http301, Http302, Http303, Http307, Http308}: - break - - let redirectTo = getNewLocation(lastURL, result.headers) - var redirectMethod: HttpMethod - var redirectBody: string - # For more informations about the redirect methods see: - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections - case statusCode - of Http301, Http302, Http303: - # The method is changed to GET unless it is GET or HEAD (RFC2616) - if httpMethod notin {HttpGet, HttpHead}: - redirectMethod = HttpGet - else: - redirectMethod = httpMethod - # The body is stripped away - redirectBody = "" - # Delete any header value associated with the body - if not headers.isNil(): - headers.del("Content-Length") - headers.del("Content-Type") - headers.del("Transfer-Encoding") - of Http307, Http308: - # The method and the body are unchanged - redirectMethod = httpMethod - redirectBody = body - else: - # Unreachable - doAssert(false) - - # Check if the redirection is to the same domain or a sub-domain (foo.com - # -> sub.foo.com) - if redirectTo.hostname != lastURL.hostname and - not redirectTo.hostname.endsWith("." & lastURL.hostname): - # Perform some cleanup of the header values - if headers != nil: - # Delete the Host header - headers.del("Host") - # Do not send any sensitive info to a unknown host - headers.del("Authorization") - - result = await client.requestAux(redirectTo, redirectMethod, redirectBody, - headers, multipart) - lastURL = redirectTo +proc request*(client: HttpClient | AsyncHttpClient, url: string, + httpMethod = HttpGet, body = "", headers: HttpHeaders = nil, + multipart: MultipartData = nil): Future[Response | AsyncResponse] + {.multisync.} = + ## Connects to the hostname specified by the URL and performs a request + ## using the method specified. + ## + ## Connection will be kept alive. Further requests on the same ``client`` to + ## the same hostname will not require a new connection to be made. The + ## connection can be closed by using the ``close`` procedure. + ## + ## When a request is made to a different hostname, the current connection will + ## be closed. + result = await request(client, url, $httpMethod, body, headers, multipart) proc responseContent(resp: Response | AsyncResponse): Future[string] {.multisync.} = ## Returns the content of a response as a string. ## - ## A `HttpRequestError` will be raised if the server responds with a + ## A ``HttpRequestError`` will be raised if the server responds with a ## client error (status code 4xx) or a server error (status code 5xx). if resp.code.is4xx or resp.code.is5xx: raise newException(HttpRequestError, resp.status) @@ -1203,87 +1079,84 @@ proc responseContent(resp: Response | AsyncResponse): Future[string] {.multisync return await resp.bodyStream.readAll() proc head*(client: HttpClient | AsyncHttpClient, - url: Uri | string): Future[Response | AsyncResponse] {.multisync.} = + url: string): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a HEAD request. ## - ## This procedure uses httpClient values such as `client.maxRedirects`. + ## This procedure uses httpClient values such as ``client.maxRedirects``. result = await client.request(url, HttpHead) proc get*(client: HttpClient | AsyncHttpClient, - url: Uri | string): Future[Response | AsyncResponse] {.multisync.} = + url: string): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a GET request. ## - ## This procedure uses httpClient values such as `client.maxRedirects`. + ## This procedure uses httpClient values such as ``client.maxRedirects``. result = await client.request(url, HttpGet) proc getContent*(client: HttpClient | AsyncHttpClient, - url: Uri | string): Future[string] {.multisync.} = + url: string): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL and returns the content of a GET request. let resp = await get(client, url) return await responseContent(resp) proc delete*(client: HttpClient | AsyncHttpClient, - url: Uri | string): Future[Response | AsyncResponse] {.multisync.} = + url: string): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a DELETE request. - ## This procedure uses httpClient values such as `client.maxRedirects`. + ## This procedure uses httpClient values such as ``client.maxRedirects``. result = await client.request(url, HttpDelete) proc deleteContent*(client: HttpClient | AsyncHttpClient, - url: Uri | string): Future[string] {.multisync.} = + url: string): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL and returns the content of a DELETE request. let resp = await delete(client, url) return await responseContent(resp) -proc post*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", +proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a POST request. - ## This procedure uses httpClient values such as `client.maxRedirects`. - result = await client.request(url, HttpPost, body, multipart=multipart) + ## This procedure uses httpClient values such as ``client.maxRedirects``. + result = await client.request(url, $HttpPost, body, multipart=multipart) -proc postContent*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", +proc postContent*(client: HttpClient | AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL and returns the content of a POST request. let resp = await post(client, url, body, multipart) return await responseContent(resp) -proc put*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", +proc put*(client: HttpClient | AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a PUT request. - ## This procedure uses httpClient values such as `client.maxRedirects`. - result = await client.request(url, HttpPut, body, multipart=multipart) + ## This procedure uses httpClient values such as ``client.maxRedirects``. + result = await client.request(url, $HttpPut, body, multipart=multipart) -proc putContent*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", +proc putContent*(client: HttpClient | AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL andreturns the content of a PUT request. let resp = await put(client, url, body, multipart) return await responseContent(resp) -proc patch*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", +proc patch*(client: HttpClient | AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a PATCH request. - ## This procedure uses httpClient values such as `client.maxRedirects`. - result = await client.request(url, HttpPatch, body, multipart=multipart) + ## This procedure uses httpClient values such as ``client.maxRedirects``. + result = await client.request(url, $HttpPatch, body, multipart=multipart) -proc patchContent*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", +proc patchContent*(client: HttpClient | AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL and returns the content of a PATCH request. let resp = await patch(client, url, body, multipart) return await responseContent(resp) -proc downloadFile*(client: HttpClient, url: Uri | string, filename: string) = - ## Downloads `url` and saves it to `filename`. +proc downloadFile*(client: HttpClient, url: string, filename: string) = + ## Downloads ``url`` and saves it to ``filename``. client.getBody = false defer: client.getBody = true let resp = client.get(url) - - if resp.code.is4xx or resp.code.is5xx: - raise newException(HttpRequestError, resp.status) client.bodyStream = newFileStream(filename, fmWrite) if client.bodyStream.isNil: @@ -1291,30 +1164,30 @@ proc downloadFile*(client: HttpClient, url: Uri | string, filename: string) = parseBody(client, resp.headers, resp.version) client.bodyStream.close() -proc downloadFileEx(client: AsyncHttpClient, - url: Uri | string, filename: string): Future[void] {.async.} = - ## Downloads `url` and saves it to `filename`. - client.getBody = false - let resp = await client.get(url) - if resp.code.is4xx or resp.code.is5xx: raise newException(HttpRequestError, resp.status) - client.bodyStream = newFutureStream[string]("downloadFile") - var file = openAsync(filename, fmWrite) - defer: file.close() - # Let `parseBody` write response data into client.bodyStream in the - # background. - let parseBodyFut = parseBody(client, resp.headers, resp.version) - parseBodyFut.addCallback do(): - if parseBodyFut.failed: - client.bodyStream.fail(parseBodyFut.error) - # The `writeFromStream` proc will complete once all the data in the - # `bodyStream` has been written to the file. - await file.writeFromStream(client.bodyStream) - -proc downloadFile*(client: AsyncHttpClient, url: Uri | string, +proc downloadFile*(client: AsyncHttpClient, url: string, filename: string): Future[void] = + proc downloadFileEx(client: AsyncHttpClient, + url, filename: string): Future[void] {.async.} = + ## Downloads ``url`` and saves it to ``filename``. + client.getBody = false + let resp = await client.get(url) + + client.bodyStream = newFutureStream[string]("downloadFile") + var file = openAsync(filename, fmWrite) + # Let `parseBody` write response data into client.bodyStream in the + # background. + asyncCheck parseBody(client, resp.headers, resp.version) + # The `writeFromStream` proc will complete once all the data in the + # `bodyStream` has been written to the file. + await file.writeFromStream(client.bodyStream) + file.close() + + if resp.code.is4xx or resp.code.is5xx: + raise newException(HttpRequestError, resp.status) + result = newFuture[void]("downloadFile") try: result = downloadFileEx(client, url, filename) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index d639bef05..9287e9864 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -7,8 +7,8 @@ # distribution, for details about the copyright. # -## Contains functionality shared between the `httpclient` and -## `asynchttpserver` modules. +## Contains functionality shared between the ``httpclient`` and +## ``asynchttpserver`` modules. ## ## Unstable API. import std/private/since @@ -29,26 +29,24 @@ type HttpVer11, HttpVer10 - HttpMethod* = enum ## the requested HttpMethod - HttpHead = "HEAD" ## Asks for the response identical to the one that - ## would correspond to a GET request, but without - ## the response body. - HttpGet = "GET" ## Retrieves the specified resource. - HttpPost = "POST" ## Submits data to be processed to the identified - ## resource. The data is included in the body of - ## the request. - HttpPut = "PUT" ## Uploads a representation of the specified - ## resource. - HttpDelete = "DELETE" ## Deletes the specified resource. - HttpTrace = "TRACE" ## Echoes back the received request, so that a - ## client - ## can see what intermediate servers are adding or - ## changing in the request. - HttpOptions = "OPTIONS" ## Returns the HTTP methods that the server - ## supports for specified address. - HttpConnect = "CONNECT" ## Converts the request connection to a transparent - ## TCP/IP tunnel, usually used for proxies. - HttpPatch = "PATCH" ## Applies partial modifications to a resource. + HttpMethod* = enum ## the requested HttpMethod + HttpHead, ## Asks for the response identical to the one that would + ## correspond to a GET request, but without the response + ## body. + HttpGet, ## Retrieves the specified resource. + HttpPost, ## Submits data to be processed to the identified + ## resource. The data is included in the body of the + ## request. + HttpPut, ## Uploads a representation of the specified resource. + HttpDelete, ## Deletes the specified resource. + HttpTrace, ## Echoes back the received request, so that a client + ## can see what intermediate servers are adding or + ## changing in the request. + HttpOptions, ## Returns the HTTP methods that the server supports + ## for specified address. + HttpConnect, ## Converts the request connection to a transparent + ## TCP/IP tunnel, usually used for proxies. + HttpPatch ## Applies partial modifications to a resource. const @@ -130,15 +128,19 @@ func toCaseInsensitive(headers: HttpHeaders, s: string): string {.inline.} = return if headers.isTitleCase: toTitleCase(s) else: toLowerAscii(s) func newHttpHeaders*(titleCase=false): HttpHeaders = - ## Returns a new `HttpHeaders` object. if `titleCase` is set to true, + ## Returns a new ``HttpHeaders`` object. if ``titleCase`` is set to true, ## headers are passed to the server in title case (e.g. "Content-Length") - result = HttpHeaders(table: newTable[string, seq[string]](), isTitleCase: titleCase) + new result + result.table = newTable[string, seq[string]]() + result.isTitleCase = titleCase func newHttpHeaders*(keyValuePairs: openArray[tuple[key: string, val: string]], titleCase=false): HttpHeaders = - ## Returns a new `HttpHeaders` object from an array. if `titleCase` is set to true, + ## Returns a new ``HttpHeaders`` object from an array. if ``titleCase`` is set to true, ## headers are passed to the server in title case (e.g. "Content-Length") - result = HttpHeaders(table: newTable[string, seq[string]](), isTitleCase: titleCase) + new result + result.table = newTable[string, seq[string]]() + result.isTitleCase = titleCase for pair in keyValuePairs: let key = result.toCaseInsensitive(pair.key) @@ -148,6 +150,7 @@ func newHttpHeaders*(keyValuePairs: else: result.table[key] = @[pair.val] + func `$`*(headers: HttpHeaders): string {.inline.} = $headers.table @@ -155,13 +158,13 @@ proc clear*(headers: HttpHeaders) {.inline.} = headers.table.clear() func `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues = - ## Returns the values associated with the given `key`. If the returned - ## values are passed to a procedure expecting a `string`, the first + ## Returns the values associated with the given ``key``. If the returned + ## values are passed to a procedure expecting a ``string``, the first ## value is automatically picked. If there are ## no values associated with the key, an exception is raised. ## - ## To access multiple values of a key, use the overloaded `[]` below or - ## to get all of them access the `table` field directly. + ## To access multiple values of a key, use the overloaded ``[]`` below or + ## to get all of them access the ``table`` field directly. {.cast(noSideEffect).}: return headers.table[headers.toCaseInsensitive(key)].HttpHeaderValues @@ -169,21 +172,21 @@ converter toString*(values: HttpHeaderValues): string = return seq[string](values)[0] func `[]`*(headers: HttpHeaders, key: string, i: int): string = - ## Returns the `i`'th value associated with the given key. If there are - ## no values associated with the key or the `i`'th value doesn't exist, + ## Returns the ``i``'th value associated with the given key. If there are + ## no values associated with the key or the ``i``'th value doesn't exist, ## an exception is raised. {.cast(noSideEffect).}: return headers.table[headers.toCaseInsensitive(key)][i] proc `[]=`*(headers: HttpHeaders, key, value: string) = - ## Sets the header entries associated with `key` to the specified value. + ## Sets the header entries associated with ``key`` to the specified value. ## Replaces any existing values. headers.table[headers.toCaseInsensitive(key)] = @[value] proc `[]=`*(headers: HttpHeaders, key: string, value: seq[string]) = - ## Sets the header entries associated with `key` to the specified list of - ## values. Replaces any existing values. If `value` is empty, - ## deletes the header entries associated with `key`. + ## Sets the header entries associated with ``key`` to the specified list of + ## values. Replaces any existing values. If ``value`` is empty, + ## deletes the header entries associated with ``key``. if value.len > 0: headers.table[headers.toCaseInsensitive(key)] = value else: @@ -198,7 +201,7 @@ proc add*(headers: HttpHeaders, key, value: string) = headers.table[headers.toCaseInsensitive(key)].add(value) proc del*(headers: HttpHeaders, key: string) = - ## Deletes the header entries associated with `key` + ## Deletes the header entries associated with ``key`` headers.table.del(headers.toCaseInsensitive(key)) iterator pairs*(headers: HttpHeaders): tuple[key, value: string] = @@ -208,7 +211,7 @@ iterator pairs*(headers: HttpHeaders): tuple[key, value: string] = yield (k, value) func contains*(values: HttpHeaderValues, value: string): bool = - ## Determines if `value` is one of the values inside `values`. Comparison + ## Determines if ``value`` is one of the values inside ``values``. Comparison ## is performed without case sensitivity. for val in seq[string](values): if val.toLowerAscii == value.toLowerAscii: return true @@ -218,8 +221,8 @@ func hasKey*(headers: HttpHeaders, key: string): bool = func getOrDefault*(headers: HttpHeaders, key: string, default = @[""].HttpHeaderValues): HttpHeaderValues = - ## Returns the values associated with the given `key`. If there are no - ## values associated with the key, then `default` is returned. + ## Returns the values associated with the given ``key``. If there are no + ## values associated with the key, then ``default`` is returned. if headers.hasKey(key): return headers[key] else: @@ -241,7 +244,7 @@ func parseList(line: string, list: var seq[string], start: int): int = func parseHeader*(line: string): tuple[key: string, value: seq[string]] = ## Parses a single raw header HTTP line into key value pairs. ## - ## Used by `asynchttpserver` and `httpclient` internally and should not + ## Used by ``asynchttpserver`` and ``httpclient`` internally and should not ## be used by you. result.value = @[] var i = 0 @@ -273,7 +276,7 @@ func contains*(methods: set[HttpMethod], x: string): bool = return parseEnum[HttpMethod](x) in methods func `$`*(code: HttpCode): string = - ## Converts the specified `HttpCode` into a HTTP status. + ## Converts the specified ``HttpCode`` into a HTTP status. runnableExamples: doAssert($Http404 == "404 Not Found") case code.int @@ -343,25 +346,53 @@ func `$`*(code: HttpCode): string = func `==`*(a, b: HttpCode): bool {.borrow.} +proc `==`*(rawCode: string, code: HttpCode): bool + {.deprecated: "Deprecated since v1.2; use rawCode == $code instead".} = + ## Compare the string form of the status code with a HttpCode + ## + ## **Note**: According to HTTP/1.1 specification, the reason phrase is + ## optional and should be ignored by the client, making this + ## proc only suitable for comparing the ``HttpCode`` against the + ## string form of itself. + return cmpIgnoreCase(rawCode, $code) == 0 + func is1xx*(code: HttpCode): bool {.inline, since: (1, 5).} = - ## Determines whether `code` is a 1xx HTTP status code. + ## Determines whether ``code`` is a 1xx HTTP status code. runnableExamples: doAssert is1xx(HttpCode(103)) code.int in {100 .. 199} func is2xx*(code: HttpCode): bool {.inline.} = - ## Determines whether `code` is a 2xx HTTP status code. + ## Determines whether ``code`` is a 2xx HTTP status code. code.int in {200 .. 299} func is3xx*(code: HttpCode): bool {.inline.} = - ## Determines whether `code` is a 3xx HTTP status code. + ## Determines whether ``code`` is a 3xx HTTP status code. code.int in {300 .. 399} func is4xx*(code: HttpCode): bool {.inline.} = - ## Determines whether `code` is a 4xx HTTP status code. + ## Determines whether ``code`` is a 4xx HTTP status code. code.int in {400 .. 499} func is5xx*(code: HttpCode): bool {.inline.} = - ## Determines whether `code` is a 5xx HTTP status code. + ## Determines whether ``code`` is a 5xx HTTP status code. code.int in {500 .. 599} + +func `$`*(httpMethod: HttpMethod): string {.inline.} = + runnableExamples: + doAssert $HttpHead == "HEAD" + doAssert $HttpPatch == "PATCH" + doAssert $HttpGet == "GET" + doAssert $HttpPost == "POST" + + result = case httpMethod + of HttpHead: "HEAD" + of HttpGet: "GET" + of HttpPost: "POST" + of HttpPut: "PUT" + of HttpDelete: "DELETE" + of HttpTrace: "TRACE" + of HttpOptions: "OPTIONS" + of HttpConnect: "CONNECT" + of HttpPatch: "PATCH" diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim new file mode 100644 index 000000000..55ace92f2 --- /dev/null +++ b/lib/pure/includes/osenv.nim @@ -0,0 +1,266 @@ +# Include file that implements 'getEnv' and friends. Do not import it! + +when not declared(os) and not declared(ospaths): + {.error: "This is an include file for os.nim!".} + +when defined(nodejs): + proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} = + var ret: cstring + let key2 = key.cstring + {.emit: "`ret` = process.env[`key2`];".} + result = $ret + + proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = + var key2 = key.cstring + var ret: bool + {.emit: "`ret` = `key2` in process.env;".} + result = ret + + proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = + var key2 = key.cstring + var val2 = val.cstring + {.emit: "process.env[`key2`] = `val2`;".} + + proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = + var key2 = key.cstring + {.emit: "delete process.env[`key2`];".} + + iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = + var num: int + var keys: RootObj + {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".} + for i in 0..".} + proc c_putenv(env: cstring): cint {. + importc: "putenv", header: "".} + proc c_unsetenv(env: cstring): cint {. + importc: "unsetenv", header: "".} + + # Environment handling cannot be put into RTL, because the ``envPairs`` + # iterator depends on ``environment``. + + var + envComputed {.threadvar.}: bool + environment {.threadvar.}: seq[string] + + when defined(nimV2): + proc unpairedEnvAllocs*(): int = + result = environment.len + if result > 0: inc result + + when defined(windows) and not defined(nimscript): + # because we support Windows GUI applications, things get really + # messy here... + when useWinUnicode: + when defined(cpp): + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. + importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "".} + else: + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. + importc: "wcschr", header: "".} + else: + proc strEnd(cstr: cstring, c = 0'i32): cstring {. + importc: "strchr", header: "".} + + proc getEnvVarsC() = + if not envComputed: + environment = @[] + when useWinUnicode: + var + env = getEnvironmentStringsW() + e = env + if e == nil: return # an error occurred + while true: + var eend = strEnd(e) + add(environment, $e) + e = cast[WideCString](cast[ByteAddress](eend)+2) + if eend[1].int == 0: break + discard freeEnvironmentStringsW(env) + else: + var + env = getEnvironmentStringsA() + e = env + if e == nil: return # an error occurred + while true: + var eend = strEnd(e) + add(environment, $e) + e = cast[cstring](cast[ByteAddress](eend)+1) + if eend[1] == '\0': break + discard freeEnvironmentStringsA(env) + envComputed = true + + else: + const + useNSGetEnviron = (defined(macosx) and not defined(ios) and not defined(emscripten)) or defined(nimscript) + + when useNSGetEnviron: + # From the manual: + # Shared libraries and bundles don't have direct access to environ, + # which is only available to the loader ld(1) when a complete program + # is being linked. + # The environment routines can still be used, but if direct access to + # environ is needed, the _NSGetEnviron() routine, defined in + # , can be used to retrieve the address of environ + # at runtime. + proc NSGetEnviron(): ptr cstringArray {. + importc: "_NSGetEnviron", header: "".} + elif defined(haiku): + var gEnv {.importc: "environ", header: "".}: cstringArray + else: + var gEnv {.importc: "environ".}: cstringArray + + proc getEnvVarsC() = + # retrieves the variables of char** env of C's main proc + if not envComputed: + environment = @[] + when useNSGetEnviron: + var gEnv = NSGetEnviron()[] + var i = 0 + while gEnv[i] != nil: + add environment, $gEnv[i] + inc(i) + envComputed = true + + proc findEnvVar(key: string): int = + getEnvVarsC() + var temp = key & '=' + for i in 0..high(environment): + when defined(windows): + if skipIgnoreCase(environment[i], temp) == len(temp): return i + else: + if startsWith(environment[i], temp): return i + return -1 + + proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} = + ## Returns the value of the `environment variable`:idx: named `key`. + ## + ## If the variable does not exist, `""` is returned. To distinguish + ## whether a variable exists or it's value is just `""`, call + ## `existsEnv(key) proc <#existsEnv,string>`_. + ## + ## See also: + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + runnableExamples: + assert getEnv("unknownEnv") == "" + assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" + + when nimvm: + discard "built into the compiler" + else: + var i = findEnvVar(key) + if i >= 0: + return TaintedString(substr(environment[i], find(environment[i], '=')+1)) + else: + var env = c_getenv(key) + if env == nil: return TaintedString(default) + result = TaintedString($env) + + proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = + ## Checks whether the environment variable named `key` exists. + ## Returns true if it exists, false otherwise. + ## + ## See also: + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + runnableExamples: + assert not existsEnv("unknownEnv") + + when nimvm: + discard "built into the compiler" + else: + if c_getenv(key) != nil: return true + else: return findEnvVar(key) >= 0 + + proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = + ## Sets the value of the `environment variable`:idx: named `key` to `val`. + ## If an error occurs, `OSError` is raised. + ## + ## See also: + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + + # Note: by storing the string in the environment sequence, + # we guarantee that we don't free the memory before the program + # ends (this is needed for POSIX compliance). It is also needed so that + # the process itself may access its modified environment variables! + when nimvm: + discard "built into the compiler" + else: + var indx = findEnvVar(key) + if indx >= 0: + environment[indx] = key & '=' & val + else: + add environment, (key & '=' & val) + indx = high(environment) + when defined(windows) and not defined(nimscript): + when useWinUnicode: + var k = newWideCString(key) + var v = newWideCString(val) + if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) + else: + if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) + else: + if c_putenv(environment[indx]) != 0'i32: + raiseOSError(osLastError()) + + proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = + ## Deletes the `environment variable`:idx: named `key`. + ## If an error occurs, `OSError` is raised. + ## + ## See also:ven + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + when nimvm: + discard "built into the compiler" + else: + var indx = findEnvVar(key) + if indx < 0: return # Do nothing if the env var is not already set + when defined(windows) and not defined(nimscript): + when useWinUnicode: + var k = newWideCString(key) + if setEnvironmentVariableW(k, nil) == 0'i32: raiseOSError(osLastError()) + else: + if setEnvironmentVariableA(key, nil) == 0'i32: raiseOSError(osLastError()) + else: + if c_unsetenv(key) != 0'i32: + raiseOSError(osLastError()) + environment.delete(indx) + + iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = + ## Iterate over all `environments variables`:idx:. + ## + ## In the first component of the tuple is the name of the current variable stored, + ## in the second its value. + ## + ## See also: + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + getEnvVarsC() + for i in 0..high(environment): + var p = find(environment[i], '=') + yield (TaintedString(substr(environment[i], 0, p-1)), + TaintedString(substr(environment[i], p+1))) diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim new file mode 100644 index 000000000..673422973 --- /dev/null +++ b/lib/pure/includes/oserr.nim @@ -0,0 +1,120 @@ +# Include file that implements 'osErrorMsg' and friends. Do not import it! + +when not declared(os) and not declared(ospaths): + {.error: "This is an include file for os.nim!".} + +when not defined(nimscript): + var errno {.importc, header: "".}: cint + + proc c_strerror(errnum: cint): cstring {. + importc: "strerror", header: "".} + + when defined(windows): + import winlean + +proc `==`*(err1, err2: OSErrorCode): bool {.borrow.} +proc `$`*(err: OSErrorCode): string {.borrow.} + +proc osErrorMsg*(errorCode: OSErrorCode): string = + ## Converts an OS error code into a human readable string. + ## + ## The error code can be retrieved using the `osLastError proc <#osLastError>`_. + ## + ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be + ## returned. + ## + ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to + ## make this procedure use the non-unicode Win API calls to retrieve the + ## message. + ## + ## See also: + ## * `raiseOSError proc <#raiseOSError,OSErrorCode,string>`_ + ## * `osLastError proc <#osLastError>`_ + runnableExamples: + when defined(linux): + assert osErrorMsg(OSErrorCode(0)) == "" + assert osErrorMsg(OSErrorCode(1)) == "Operation not permitted" + assert osErrorMsg(OSErrorCode(2)) == "No such file or directory" + + result = "" + when defined(nimscript): + discard + elif defined(Windows): + if errorCode != OSErrorCode(0'i32): + when useWinUnicode: + var msgbuf: WideCString + if formatMessageW(0x00000100 or 0x00001000 or 0x00000200, + nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: + result = $msgbuf + if msgbuf != nil: localFree(cast[pointer](msgbuf)) + else: + var msgbuf: cstring + if formatMessageA(0x00000100 or 0x00001000 or 0x00000200, + nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: + result = $msgbuf + if msgbuf != nil: localFree(msgbuf) + else: + if errorCode != OSErrorCode(0'i32): + result = $c_strerror(errorCode.int32) + +proc newOSError*( + errorCode: OSErrorCode, additionalInfo = "" +): owned(ref OSError) {.noinline.} = + ## Creates a new `OSError exception `_. + ## + ## The ``errorCode`` will determine the + ## message, `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_ will be used + ## to get this message. + ## + ## The error code can be retrieved using the `osLastError proc + ## <#osLastError>`_. + ## + ## If the error code is ``0`` or an error message could not be retrieved, + ## the message ``unknown OS error`` will be used. + ## + ## See also: + ## * `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_ + ## * `osLastError proc <#osLastError>`_ + var e: owned(ref OSError); new(e) + e.errorCode = errorCode.int32 + e.msg = osErrorMsg(errorCode) + if additionalInfo.len > 0: + if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n' + e.msg.add "Additional info: " + e.msg.addQuoted additionalInfo + if e.msg == "": + e.msg = "unknown OS error" + return e + +proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} = + ## Raises an `OSError exception `_. + ## + ## Read the description of the `newOSError proc <#newOSError,OSErrorCode,string>`_ to learn + ## how the exception object is created. + raise newOSError(errorCode, additionalInfo) + +{.push stackTrace:off.} +proc osLastError*(): OSErrorCode {.sideEffect.} = + ## Retrieves the last operating system error code. + ## + ## This procedure is useful in the event when an OS call fails. In that case + ## this procedure will return the error code describing the reason why the + ## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert + ## this code into a string. + ## + ## **Warning**: + ## The behaviour of this procedure varies between Windows and POSIX systems. + ## On Windows some OS calls can reset the error code to ``0`` causing this + ## procedure to return ``0``. It is therefore advised to call this procedure + ## immediately after an OS call fails. On POSIX systems this is not a problem. + ## + ## See also: + ## * `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_ + ## * `raiseOSError proc <#raiseOSError,OSErrorCode,string>`_ + when defined(nimscript): + discard + elif defined(windows): + result = cast[OSErrorCode](getLastError()) + else: + result = OSErrorCode(errno) +{.pop.} diff --git a/lib/std/private/osseps.nim b/lib/pure/includes/osseps.nim similarity index 91% rename from lib/std/private/osseps.nim rename to lib/pure/includes/osseps.nim index f2d49d886..bafef686d 100644 --- a/lib/std/private/osseps.nim +++ b/lib/pure/includes/osseps.nim @@ -1,10 +1,8 @@ # Include file that implements 'DirSep' and friends. Do not import this when -# you also import `os.nim`! +# you also import ``os.nim``! # Improved based on info in 'compiler/platform.nim' -## .. importdoc:: ospaths2.nim - const doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS) @@ -39,9 +37,9 @@ const when doslikeFileSystem: '/' else: DirSep ## An alternative character used by the operating system to separate - ## pathname components, or the same as DirSep_ if only one separator + ## pathname components, or the same as `DirSep <#DirSep>`_ if only one separator ## character exists. This is set to `'/'` on Windows systems - ## where DirSep_ is a backslash (`'\\'`). + ## where `DirSep <#DirSep>`_ is a backslash (`'\\'`). PathSep* = when defined(macos) or defined(RISCOS): ',' @@ -49,7 +47,7 @@ const elif defined(PalmOS) or defined(MorphOS): ':' # platform has ':' but osseps has ';' else: ':' ## The character conventionally used by the operating system to separate - ## search path components (as in PATH), such as `':'` for POSIX + ## search patch components (as in PATH), such as `':'` for POSIX ## or `';'` for Windows. FileSystemCaseSensitive* = @@ -57,7 +55,7 @@ const defined(PalmOS) or defined(MorphOS): false else: true ## True if the file system is case sensitive, false otherwise. Used by - ## `cmpPaths proc`_ to compare filenames properly. + ## `cmpPaths proc <#cmpPaths,string,string>`_ to compare filenames properly. ExeExt* = when doslikeFileSystem: "exe" @@ -90,7 +88,7 @@ const ExtSep* = '.' ## The character which separates the base filename from the extension; - ## for example, the `'.'` in `os.nim`. + ## for example, the `'.'` in ``os.nim``. # MacOS paths # =========== diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim index 08cb6ed74..b62b4c2db 100644 --- a/lib/pure/ioselects/ioselectors_epoll.nim +++ b/lib/pure/ioselects/ioselectors_epoll.nim @@ -72,16 +72,14 @@ type SelectEvent* = ptr SelectEventImpl proc newSelector*[T](): Selector[T] = - proc initialNumFD(): int {.inline.} = - when defined(nuttx): - result = NEPOLL_MAX - else: - result = 1024 # Retrieve the maximum fd count (for current OS) via getrlimit() - var maxFD = maxDescriptors() + var a = RLimit() + if getrlimit(posix.RLIMIT_NOFILE, a) != 0: + raiseOSError(osLastError()) + var maxFD = int(a.rlim_max) doAssert(maxFD > 0) # Start with a reasonable size, checkFd() will grow this on demand - let numFD = initialNumFD() + const numFD = 1024 var epollFD = epoll_create1(O_CLOEXEC) if epollFD < 0: @@ -531,4 +529,4 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1, body2 proc getFd*[T](s: Selector[T]): int = - return s.epollFD.int + return s.epollFd.int diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim index 12812ac80..6b10e19ae 100644 --- a/lib/pure/ioselects/ioselectors_poll.nim +++ b/lib/pure/ioselects/ioselectors_poll.nim @@ -14,12 +14,6 @@ import posix, times # Maximum number of events that can be returned const MAX_POLL_EVENTS = 64 -const hasEventFds = defined(zephyr) or defined(nimPollHasEventFds) - -when hasEventFds: - proc eventfd(count: cuint, flags: cint): cint - {.cdecl, importc: "eventfd", header: "".} - when hasThreadSupport: type SelectorImpl[T] = object @@ -59,7 +53,10 @@ else: body proc newSelector*[T](): Selector[T] = - var maxFD = maxDescriptors() + var a = RLimit() + if getrlimit(posix.RLIMIT_NOFILE, a) != 0: + raiseIOSelectorsError(osLastError()) + var maxFD = int(a.rlim_max) when hasThreadSupport: result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T]))) @@ -190,22 +187,14 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) = s.pollRemove(fdi.cint) proc newSelectEvent*(): SelectEvent = - when not hasEventFds: - var fds: array[2, cint] - if posix.pipe(fds) != 0: - raiseIOSelectorsError(osLastError()) - setNonBlocking(fds[0]) - setNonBlocking(fds[1]) - result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl))) - result.rfd = fds[0] - result.wfd = fds[1] - else: - let fdci = eventfd(0, posix.O_NONBLOCK) - if fdci == -1: - raiseIOSelectorsError(osLastError()) - result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl))) - result.rfd = fdci - result.wfd = fdci + var fds: array[2, cint] + if posix.pipe(fds) != 0: + raiseIOSelectorsError(osLastError()) + setNonBlocking(fds[0]) + setNonBlocking(fds[1]) + result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl))) + result.rfd = fds[0] + result.wfd = fds[1] proc trigger*(ev: SelectEvent) = var data: uint64 = 1 @@ -214,10 +203,7 @@ proc trigger*(ev: SelectEvent) = proc close*(ev: SelectEvent) = let res1 = posix.close(ev.rfd) - let res2 = - when hasEventFds: 0 - else: posix.close(ev.wfd) - + let res2 = posix.close(ev.wfd) deallocShared(cast[pointer](ev)) if res1 != 0 or res2 != 0: raiseIOSelectorsError(osLastError()) diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index a6f54e791..6f216ac85 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -26,27 +26,27 @@ else: #include #include """ type - FdSet {.importc: "fd_set", header: platformHeaders, pure, final.} = object + Fdset {.importc: "fd_set", header: platformHeaders, pure, final.} = object var FD_SETSIZE {.importc: "FD_SETSIZE", header: platformHeaders.}: cint -proc IOFD_SET(fd: SocketHandle, fdset: ptr FdSet) +proc IOFD_SET(fd: SocketHandle, fdset: ptr Fdset) {.cdecl, importc: "FD_SET", header: platformHeaders, inline.} -proc IOFD_CLR(fd: SocketHandle, fdset: ptr FdSet) +proc IOFD_CLR(fd: SocketHandle, fdset: ptr Fdset) {.cdecl, importc: "FD_CLR", header: platformHeaders, inline.} -proc IOFD_ZERO(fdset: ptr FdSet) +proc IOFD_ZERO(fdset: ptr Fdset) {.cdecl, importc: "FD_ZERO", header: platformHeaders, inline.} when defined(windows): - proc IOFD_ISSET(fd: SocketHandle, fdset: ptr FdSet): cint + proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint {.stdcall, importc: "FD_ISSET", header: platformHeaders, inline.} - proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr FdSet, + proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset, timeout: ptr Timeval): cint {.stdcall, importc: "select", header: platformHeaders.} else: - proc IOFD_ISSET(fd: SocketHandle, fdset: ptr FdSet): cint + proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint {.cdecl, importc: "FD_ISSET", header: platformHeaders, inline.} - proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr FdSet, + proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset, timeout: ptr Timeval): cint {.cdecl, importc: "select", header: platformHeaders.} @@ -313,7 +313,7 @@ proc selectInto*[T](s: Selector[T], timeout: int, verifySelectParams(timeout) if timeout != -1: - when defined(genode) or defined(freertos) or defined(zephyr) or defined(nuttx): + when defined(genode) or defined(freertos): tv.tv_sec = Time(timeout div 1_000) else: tv.tv_sec = timeout.int32 div 1_000 @@ -398,6 +398,18 @@ 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 d91da3545..a9d0ed4cb 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -14,35 +14,27 @@ ## JSON is based on a subset of the JavaScript Programming Language, ## Standard ECMA-262 3rd Edition - December 1999. ## -## See also -## ======== -## * `std/parsejson `_ -## * `std/jsonutils `_ -## * `std/marshal `_ -## * `std/jscore `_ -## -## ## Overview ## ======== ## ## Parsing JSON ## ------------ ## -## JSON often arrives into your program (via an API or a file) as a `string`. +## JSON often arrives into your program (via an API or a file) as a ``string``. ## The first step is to change it from its serialized form into a nested object -## structure called a `JsonNode`. +## structure called a ``JsonNode``. ## -## The `parseJson` procedure takes a string containing JSON and returns a -## `JsonNode` object. This is an object variant and it is either a -## `JObject`, `JArray`, `JString`, `JInt`, `JFloat`, `JBool` or -## `JNull`. You check the kind of this object variant by using the `kind` +## The ``parseJson`` procedure takes a string containing JSON and returns a +## ``JsonNode`` object. This is an object variant and it is either a +## ``JObject``, ``JArray``, ``JString``, ``JInt``, ``JFloat``, ``JBool`` or +## ``JNull``. You check the kind of this object variant by using the ``kind`` ## accessor. ## -## For a `JsonNode` who's kind is `JObject`, you can access its fields using -## the `[]` operator. The following example shows how to do this: +## For a ``JsonNode`` who's kind is ``JObject``, you can access its fields using +## the ``[]`` operator. The following example shows how to do this: ## ## .. code-block:: Nim -## import std/json +## import json ## ## let jsonNode = parseJson("""{"key": 3.14}""") ## @@ -52,35 +44,35 @@ ## Reading values ## -------------- ## -## Once you have a `JsonNode`, retrieving the values can then be achieved +## Once you have a ``JsonNode``, retrieving the values can then be achieved ## by using one of the helper procedures, which include: ## -## * `getInt` -## * `getFloat` -## * `getStr` -## * `getBool` +## * ``getInt`` +## * ``getFloat`` +## * ``getStr`` +## * ``getBool`` ## -## To retrieve the value of `"key"` you can do the following: +## To retrieve the value of ``"key"`` you can do the following: ## ## .. code-block:: Nim -## import std/json +## import json ## ## let jsonNode = parseJson("""{"key": 3.14}""") ## ## doAssert jsonNode["key"].getFloat() == 3.14 ## -## **Important:** The `[]` operator will raise an exception when the +## **Important:** The ``[]`` operator will raise an exception when the ## specified field does not exist. ## ## Handling optional keys ## ---------------------- ## -## By using the `{}` operator instead of `[]`, it will return `nil` -## when the field is not found. The `get`-family of procedures will return a -## type's default value when called on `nil`. +## By using the ``{}`` operator instead of ``[]``, it will return ``nil`` +## when the field is not found. The ``get``-family of procedures will return a +## type's default value when called on ``nil``. ## ## .. code-block:: Nim -## import std/json +## import json ## ## let jsonNode = parseJson("{}") ## @@ -92,11 +84,11 @@ ## Using default values ## -------------------- ## -## The `get`-family helpers also accept an additional parameter which allow -## you to fallback to a default value should the key's values be `null`: +## The ``get``-family helpers also accept an additional parameter which allow +## you to fallback to a default value should the key's values be ``null``: ## ## .. code-block:: Nim -## import std/json +## import json ## ## let jsonNode = parseJson("""{"key": 3.14, "key2": null}""") ## @@ -108,14 +100,14 @@ ## ------------- ## ## In addition to reading dynamic data, Nim can also unmarshal JSON directly -## into a type with the `to` macro. +## into a type with the ``to`` macro. ## ## Note: Use `Option `_ for keys sometimes missing in json ## responses, and backticks around keys with a reserved keyword as name. ## ## .. code-block:: Nim -## import std/json -## import std/options +## import json +## import options ## ## type ## User = object @@ -131,11 +123,11 @@ ## Creating JSON ## ============= ## -## This module can also be used to comfortably create JSON using the `%*` +## This module can also be used to comfortably create JSON using the ``%*`` ## operator: ## ## .. code-block:: nim -## import std/json +## import json ## ## var hisName = "John" ## let herAge = 31 @@ -159,14 +151,12 @@ runnableExamples: a1, a2, a0, a3, a4: int doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}""" -import hashes, tables, strutils, lexbase, streams, macros, parsejson +import + hashes, tables, strutils, lexbase, streams, macros, parsejson import options # xxx remove this dependency using same approach as https://github.com/nim-lang/Nim/pull/14563 import std/private/since -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions, formatfloat] - export tables.`$` @@ -205,19 +195,21 @@ type of JArray: elems*: seq[JsonNode] -const DepthLimit = 1000 - proc newJString*(s: string): JsonNode = ## Creates a new `JString JsonNode`. result = JsonNode(kind: JString, str: s) proc newJRawNumber(s: string): JsonNode = ## Creates a "raw JS number", that is a number that does not - ## fit into Nim's `BiggestInt` field. This is really a `JString` + ## fit into Nim's ``BiggestInt`` field. This is really a `JString` ## with the additional information that it should be converted back ## 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) @@ -245,28 +237,28 @@ proc newJArray*(): JsonNode = proc getStr*(n: JsonNode, default: string = ""): string = ## Retrieves the string value of a `JString JsonNode`. ## - ## Returns `default` if `n` is not a `JString`, or if `n` is nil. + ## Returns ``default`` if ``n`` is not a ``JString``, or if ``n`` is nil. if n.isNil or n.kind != JString: return default else: return n.str proc getInt*(n: JsonNode, default: int = 0): int = ## Retrieves the int value of a `JInt JsonNode`. ## - ## Returns `default` if `n` is not a `JInt`, or if `n` is nil. + ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil. if n.isNil or n.kind != JInt: return default else: return int(n.num) proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt = ## Retrieves the BiggestInt value of a `JInt JsonNode`. ## - ## Returns `default` if `n` is not a `JInt`, or if `n` is nil. + ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil. if n.isNil or n.kind != JInt: return default else: return n.num proc getFloat*(n: JsonNode, default: float = 0.0): float = ## Retrieves the float value of a `JFloat JsonNode`. ## - ## Returns `default` if `n` is not a `JFloat` or `JInt`, or if `n` is nil. + ## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil. if n.isNil: return default case n.kind of JFloat: return n.fnum @@ -276,7 +268,7 @@ proc getFloat*(n: JsonNode, default: float = 0.0): float = proc getBool*(n: JsonNode, default: bool = false): bool = ## Retrieves the bool value of a `JBool JsonNode`. ## - ## Returns `default` if `n` is not a `JBool`, or if `n` is nil. + ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil. if n.isNil or n.kind != JBool: return default else: return n.bval @@ -285,14 +277,14 @@ proc getFields*(n: JsonNode, OrderedTable[string, JsonNode] = ## Retrieves the key, value pairs of a `JObject JsonNode`. ## - ## Returns `default` if `n` is not a `JObject`, or if `n` is nil. + ## Returns ``default`` if ``n`` is not a ``JObject``, or if ``n`` is nil. if n.isNil or n.kind != JObject: return default else: return n.fields proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] = ## Retrieves the array of a `JArray JsonNode`. ## - ## Returns `default` if `n` is not a `JArray`, or if `n` is nil. + ## Returns ``default`` if ``n`` is not a ``JArray``, or if ``n`` is nil. if n.isNil or n.kind != JArray: return default else: return n.elems @@ -312,10 +304,7 @@ proc `%`*(s: string): JsonNode = proc `%`*(n: uint): JsonNode = ## Generic constructor for JSON data. Creates a new `JInt JsonNode`. - if n > cast[uint](int.high): - result = newJRawNumber($n) - else: - result = JsonNode(kind: JInt, num: BiggestInt(n)) + result = JsonNode(kind: JInt, num: BiggestInt(n)) proc `%`*(n: int): JsonNode = ## Generic constructor for JSON data. Creates a new `JInt JsonNode`. @@ -323,10 +312,7 @@ proc `%`*(n: int): JsonNode = proc `%`*(n: BiggestUInt): JsonNode = ## Generic constructor for JSON data. Creates a new `JInt JsonNode`. - if n > cast[BiggestUInt](BiggestInt.high): - result = newJRawNumber($n) - else: - result = JsonNode(kind: JInt, num: BiggestInt(n)) + result = JsonNode(kind: JInt, num: BiggestInt(n)) proc `%`*(n: BiggestInt): JsonNode = ## Generic constructor for JSON data. Creates a new `JInt JsonNode`. @@ -334,16 +320,7 @@ proc `%`*(n: BiggestInt): JsonNode = proc `%`*(n: float): JsonNode = ## Generic constructor for JSON data. Creates a new `JFloat JsonNode`. - runnableExamples: - assert $(%[NaN, Inf, -Inf, 0.0, -0.0, 1.0, 1e-2]) == """["nan","inf","-inf",0.0,-0.0,1.0,0.01]""" - assert (%NaN).kind == JString - assert (%0.0).kind == JFloat - # for those special cases, we could also have used `newJRawNumber` but then - # it would've been inconsisten with the case of `parseJson` vs `%` for representing them. - if n != n: newJString("nan") - elif n == Inf: newJString("inf") - elif n == -Inf: newJString("-inf") - else: JsonNode(kind: JFloat, fnum: n) + result = JsonNode(kind: JFloat, fnum: n) proc `%`*(b: bool): JsonNode = ## Generic constructor for JSON data. Creates a new `JBool JsonNode`. @@ -363,13 +340,13 @@ proc `%`*[T](elements: openArray[T]): JsonNode = for elem in elements: result.add(%elem) proc `%`*[T](table: Table[string, T]|OrderedTable[string, T]): JsonNode = - ## Generic constructor for JSON data. Creates a new `JObject JsonNode`. + ## Generic constructor for JSON data. Creates a new ``JObject JsonNode``. result = newJObject() for k, v in table: result[k] = %v proc `%`*[T](opt: Option[T]): JsonNode = - ## Generic constructor for JSON data. Creates a new `JNull JsonNode` - ## if `opt` is empty, otherwise it delegates to the underlying value. + ## Generic constructor for JSON data. Creates a new ``JNull JsonNode`` + ## if ``opt`` is empty, otherwise it delegates to the underlying value. if opt.isSome: %opt.get else: newJNull() when false: @@ -378,8 +355,8 @@ when false: # causing problems later on. proc `%`*(elements: set[bool]): JsonNode = ## Generic constructor for JSON data. Creates a new `JObject JsonNode`. - ## This can only be used with the empty set `{}` and is supported - ## to prevent the gotcha `%*{}` which used to produce an empty + ## This can only be used with the empty set ``{}`` and is supported + ## to prevent the gotcha ``%*{}`` which used to produce an empty ## JSON array. result = newJObject() assert false notin elements, "usage error: only empty sets allowed" @@ -404,10 +381,10 @@ proc `%`*(o: ref object): JsonNode = proc `%`*(o: enum): JsonNode = ## Construct a JsonNode that represents the specified enum value as a - ## string. Creates a new `JString JsonNode`. + ## string. Creates a new ``JString JsonNode``. result = %($o) -proc toJsonImpl(x: NimNode): NimNode = +proc toJsonImpl(x: NimNode): NimNode {.compileTime.} = case x.kind of nnkBracket: # array if x.len == 0: return newCall(bindSym"newJArray") @@ -438,7 +415,7 @@ macro `%*`*(x: untyped): untyped = ## `%` for every element. result = toJsonImpl(x) -proc `==`*(a, b: JsonNode): bool {.noSideEffect.} = +proc `==`*(a, b: JsonNode): bool = ## Check two nodes for equality if a.isNil: if b.isNil: return true @@ -465,17 +442,12 @@ proc `==`*(a, b: JsonNode): bool {.noSideEffect.} = if a.fields.len != b.fields.len: return false for key, val in a.fields: if not b.fields.hasKey(key): return false - {.cast(raises: []).}: - when defined(nimHasEffectsOf): - {.noSideEffect.}: - if b.fields[key] != val: return false - else: - if b.fields[key] != val: return false + if b.fields[key] != val: return false result = true proc hash*(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect.} -proc hash*(n: JsonNode): Hash {.noSideEffect.} = +proc hash*(n: JsonNode): Hash = ## Compute the hash for a JSON node case n.kind of JArray: @@ -537,24 +509,6 @@ proc `[]`*(node: JsonNode, index: BackwardsIndex): JsonNode {.inline, since: (1, `[]`(node, node.len - int(index)) -proc `[]`*[U, V](a: JsonNode, x: HSlice[U, V]): JsonNode = - ## Slice operation for JArray. - ## - ## Returns the inclusive range `[a[x.a], a[x.b]]`: - runnableExamples: - import json - let arr = %[0,1,2,3,4,5] - doAssert arr[2..4] == %[2,3,4] - doAssert arr[2..^2] == %[2,3,4] - doAssert arr[^4..^2] == %[2,3,4] - - assert(a.kind == JArray) - result = newJArray() - let xa = (when x.a is BackwardsIndex: a.len - int(x.a) else: int(x.a)) - let L = (when x.b is BackwardsIndex: a.len - int(x.b) else: int(x.b)) - xa + 1 - for i in 0.. DepthLimit: - raiseParseErr(p, "}") result = newJObject() discard getTok(p) while p.tok != tkCurlyRi: @@ -901,18 +857,16 @@ proc parseJson(p: var JsonParser; rawIntegers, rawFloats: bool, depth = 0): Json var key = p.a discard getTok(p) eat(p, tkColon) - var val = parseJson(p, rawIntegers, rawFloats, depth+1) + var val = parseJson(p, rawIntegers, rawFloats) result[key] = val if p.tok != tkComma: break discard getTok(p) eat(p, tkCurlyRi) of tkBracketLe: - if depth > DepthLimit: - raiseParseErr(p, "]") result = newJArray() discard getTok(p) while p.tok != tkBracketRi: - result.add(parseJson(p, rawIntegers, rawFloats, depth+1)) + result.add(parseJson(p, rawIntegers, rawFloats)) if p.tok != tkComma: break discard getTok(p) eat(p, tkBracketRi) @@ -924,7 +878,7 @@ iterator parseJsonFragments*(s: Stream, filename: string = ""; rawIntegers = fal ## for nice error messages. ## The JSON fragments are separated by whitespace. This can be substantially ## faster than the comparable loop - ## `for x in splitWhitespace(s): yield parseJson(x)`. + ## ``for x in splitWhitespace(s): yield parseJson(x)``. ## This closes the stream `s` after it's done. ## If `rawIntegers` is true, integer literals will not be converted to a `JInt` ## field but kept as raw numbers via `JString`. @@ -959,23 +913,21 @@ proc parseJson*(s: Stream, filename: string = ""; rawIntegers = false, rawFloats when defined(js): from math import `mod` - from std/jsffi import JsObject, `[]`, to - from std/private/jsutils import getProtoName, isInteger, isSafeInteger + type + JSObject = object - proc parseNativeJson(x: cstring): JsObject {.importjs: "JSON.parse(#)".} + proc parseNativeJson(x: cstring): JSObject {.importc: "JSON.parse".} - proc getVarType(x: JsObject, isRawNumber: var bool): JsonNodeKind = + proc getVarType(x: JSObject): JsonNodeKind = result = JNull + proc getProtoName(y: JSObject): cstring + {.importc: "Object.prototype.toString.call".} case $getProtoName(x) # TODO: Implicit returns fail here. of "[object Array]": return JArray of "[object Object]": return JObject of "[object Number]": - if isInteger(x) and 1.0 / cast[float](x) != -Inf: # preserve -0.0 as float - if isSafeInteger(x): - return JInt - else: - isRawNumber = true - return JString + if cast[float](x) mod 1.0 == 0: + return JInt else: return JFloat of "[object Boolean]": return JBool @@ -983,14 +935,26 @@ when defined(js): of "[object String]": return JString else: assert false - proc len(x: JsObject): int = + proc len(x: JSObject): int = + assert x.getVarType == JArray asm """ `result` = `x`.length; """ - proc convertObject(x: JsObject): JsonNode = - var isRawNumber = false - case getVarType(x, isRawNumber) + proc `[]`(x: JSObject, y: string): JSObject = + assert x.getVarType == JObject + asm """ + `result` = `x`[`y`]; + """ + + proc `[]`(x: JSObject, y: int): JSObject = + assert x.getVarType == JArray + asm """ + `result` = `x`[`y`]; + """ + + proc convertObject(x: JSObject): JsonNode = + case getVarType(x) of JArray: result = newJArray() for i in 0 ..< x.len: @@ -1001,24 +965,19 @@ when defined(js): if (`x`.hasOwnProperty(property)) { """ var nimProperty: cstring - var nimValue: JsObject + var nimValue: JSObject asm "`nimProperty` = property; `nimValue` = `x`[property];" result[$nimProperty] = nimValue.convertObject() asm "}}" of JInt: - result = newJInt(x.to(int)) + result = newJInt(cast[int](x)) of JFloat: - result = newJFloat(x.to(float)) + result = newJFloat(cast[float](x)) of JString: # Dunno what to do with isUnquoted here - if isRawNumber: - var value: cstring - {.emit: "`value` = `x`.toString();".} - result = newJRawNumber($value) - else: - result = newJString($x.to(cstring)) + result = newJString($cast[cstring](x)) of JBool: - result = newJBool(x.to(bool)) + result = newJBool(cast[bool](x)) of JNull: result = newJNull() @@ -1060,309 +1019,286 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind], ] raise newException(JsonKindError, msg) -macro isRefSkipDistinct*(arg: typed): untyped = - ## internal only, do not use - var impl = getTypeImpl(arg) - if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"): - impl = getTypeImpl(impl[1]) - while impl.kind == nnkDistinctTy: - impl = getTypeImpl(impl[0]) - result = newLit(impl.kind == nnkRefTy) +when defined(nimFixedForwardGeneric): -# The following forward declarations don't work in older versions of Nim + macro isRefSkipDistinct*(arg: typed): untyped = + ## internal only, do not use + var impl = getTypeImpl(arg) + if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"): + impl = getTypeImpl(impl[1]) + while impl.kind == nnkDistinctTy: + impl = getTypeImpl(impl[0]) + result = newLit(impl.kind == nnkRefTy) -# forward declare all initFromJson + # The following forward declarations don't work in older versions of Nim -proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) -proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) + # forward declare all initFromJson -# initFromJson definitions + proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) + proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) -proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JString, JNull}, jsonPath) - # since strings don't have a nil state anymore, this mapping of - # JNull to the default string is questionable. `none(string)` and - # `some("")` have the same potentional json value `JNull`. - if jsonNode.kind == JNull: - dst = "" - else: - dst = jsonNode.str + # initFromJson definitions -proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JBool}, jsonPath) - dst = jsonNode.bval - -proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) = - if jsonNode == nil: - raise newException(KeyError, "key not found: " & jsonPath) - dst = jsonNode.copy - -proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) = - when T is uint|uint64 or (not defined(js) and int.sizeof == 4): - verifyJsonKind(jsonNode, {JInt, JString}, jsonPath) - case jsonNode.kind - of JString: - let x = parseBiggestUInt(jsonNode.str) - dst = cast[T](x) + proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JString, JNull}, jsonPath) + # since strings don't have a nil state anymore, this mapping of + # JNull to the default string is questionable. `none(string)` and + # `some("")` have the same potentional json value `JNull`. + if jsonNode.kind == JNull: + dst = "" else: - dst = T(jsonNode.num) - else: - verifyJsonKind(jsonNode, {JInt}, jsonPath) - dst = cast[T](jsonNode.num) + dst = jsonNode.str -proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - if jsonNode.kind == JString: - case jsonNode.str - of "nan": - let b = NaN - dst = T(b) - # dst = NaN # would fail some tests because range conversions would cause CT error - # in some cases; but this is not a hot-spot inside this branch and backend can optimize this. - of "inf": - let b = Inf - dst = T(b) - of "-inf": - let b = -Inf - dst = T(b) - else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str) - else: + proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JBool}, jsonPath) + dst = jsonNode.bval + + proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) = + if jsonNode == nil: + raise newException(KeyError, "key not found: " & jsonPath) + dst = jsonNode.copy + + proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) = + verifyJsonKind(jsonNode, {JInt}, jsonPath) + dst = T(jsonNode.num) + + proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) = verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) if jsonNode.kind == JFloat: dst = T(jsonNode.fnum) else: dst = T(jsonNode.num) -proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JString}, jsonPath) - dst = parseEnum[T](jsonNode.getStr) + proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JString}, jsonPath) + dst = parseEnum[T](jsonNode.getStr) -proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JArray}, jsonPath) - dst.setLen jsonNode.len - let orignalJsonPathLen = jsonPath.len - for i in 0 ..< jsonNode.len: - jsonPath.add '[' - jsonPath.addInt i - jsonPath.add ']' - initFromJson(dst[i], jsonNode[i], jsonPath) - jsonPath.setLen orignalJsonPathLen + proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JArray}, jsonPath) + dst.setLen jsonNode.len + let orignalJsonPathLen = jsonPath.len + for i in 0 ..< jsonNode.len: + jsonPath.add '[' + jsonPath.addInt i + jsonPath.add ']' + initFromJson(dst[i], jsonNode[i], jsonPath) + jsonPath.setLen orignalJsonPathLen -proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JArray}, jsonPath) - let originalJsonPathLen = jsonPath.len - for i in 0 ..< jsonNode.len: - jsonPath.add '[' - jsonPath.addInt i - jsonPath.add ']' - initFromJson(dst[i.S], jsonNode[i], jsonPath) # `.S` for enum indexed arrays - jsonPath.setLen originalJsonPathLen + proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JArray}, jsonPath) + let originalJsonPathLen = jsonPath.len + for i in 0 ..< jsonNode.len: + jsonPath.add '[' + jsonPath.addInt i + jsonPath.add ']' + initFromJson(dst[i], jsonNode[i], jsonPath) + jsonPath.setLen originalJsonPathLen -proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) = - dst = initTable[string, T]() - verifyJsonKind(jsonNode, {JObject}, jsonPath) - let originalJsonPathLen = jsonPath.len - for key in keys(jsonNode.fields): - jsonPath.add '.' - jsonPath.add key - initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) - jsonPath.setLen originalJsonPathLen + proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) = + dst = initTable[string, T]() + verifyJsonKind(jsonNode, {JObject}, jsonPath) + let originalJsonPathLen = jsonPath.len + for key in keys(jsonNode.fields): + jsonPath.add '.' + jsonPath.add key + initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) + jsonPath.setLen originalJsonPathLen -proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) = - dst = initOrderedTable[string,T]() - verifyJsonKind(jsonNode, {JObject}, jsonPath) - let originalJsonPathLen = jsonPath.len - for key in keys(jsonNode.fields): - jsonPath.add '.' - jsonPath.add key - initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) - jsonPath.setLen originalJsonPathLen + proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) = + dst = initOrderedTable[string,T]() + verifyJsonKind(jsonNode, {JObject}, jsonPath) + let originalJsonPathLen = jsonPath.len + for key in keys(jsonNode.fields): + jsonPath.add '.' + jsonPath.add key + initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) + jsonPath.setLen originalJsonPathLen -proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath) - if jsonNode.kind == JNull: - dst = nil - else: - dst = new(T) - initFromJson(dst[], jsonNode, jsonPath) - -proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) = - if jsonNode != nil and jsonNode.kind != JNull: - when T is ref: - dst = some(new(T)) + proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath) + if jsonNode.kind == JNull: + dst = nil else: - dst = some(default(T)) - initFromJson(dst.get, jsonNode, jsonPath) + dst = new(T) + initFromJson(dst[], jsonNode, jsonPath) -macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = - let typInst = getTypeInst(dst) - let typImpl = getTypeImpl(dst) - let baseTyp = typImpl[0] + proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) = + if jsonNode != nil and jsonNode.kind != JNull: + when T is ref: + dst = some(new(T)) + else: + dst = some(default(T)) + initFromJson(dst.get, jsonNode, jsonPath) - result = quote do: - when nimvm: - # workaround #12282 - var tmp: `baseTyp` - initFromJson( tmp, `jsonNode`, `jsonPath`) - `dst` = `typInst`(tmp) - else: - initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`) + macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = + let typInst = getTypeInst(dst) + let typImpl = getTypeImpl(dst) + let baseTyp = typImpl[0] -proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - assignDistinctImpl(dst, jsonNode, jsonPath) - -proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode) = - if typeExpr.kind == nnkTupleConstr: - error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode) - -proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode) = - case typeNode.kind - of nnkEmpty: - discard - of nnkRecList, nnkTupleTy: - for it in typeNode: - foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen) - - of nnkIdentDefs: - typeNode.expectLen 3 - let fieldSym = typeNode[0] - let fieldNameLit = newLit(fieldSym.strVal) - let fieldPathLit = newLit("." & fieldSym.strVal) - let fieldType = typeNode[1] - - # Detecting incompatiple tuple types in `assignObjectImpl` only - # would be much cleaner, but the ast for tuple types does not - # contain usable type information. - detectIncompatibleType(fieldType, fieldSym) - - dst.add quote do: - jsonPath.add `fieldPathLit` + result = quote do: when nimvm: - when isRefSkipDistinct(`tmpSym`.`fieldSym`): - # workaround #12489 - var tmp: `fieldType` - initFromJson(tmp, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) - `tmpSym`.`fieldSym` = tmp + # workaround #12282 + var tmp: `baseTyp` + initFromJson( tmp, `jsonNode`, `jsonPath`) + `dst` = `typInst`(tmp) + else: + initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`) + + proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + assignDistinctImpl(dst, jsonNode, jsonPath) + + proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode): void = + if typeExpr.kind == nnkTupleConstr: + error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode) + + proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode): void {.compileTime.} = + case typeNode.kind + of nnkEmpty: + discard + of nnkRecList, nnkTupleTy: + for it in typeNode: + foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen) + + of nnkIdentDefs: + typeNode.expectLen 3 + let fieldSym = typeNode[0] + let fieldNameLit = newLit(fieldSym.strVal) + let fieldPathLit = newLit("." & fieldSym.strVal) + let fieldType = typeNode[1] + + # Detecting incompatiple tuple types in `assignObjectImpl` only + # would be much cleaner, but the ast for tuple types does not + # contain usable type information. + detectIncompatibleType(fieldType, fieldSym) + + dst.add quote do: + jsonPath.add `fieldPathLit` + when nimvm: + when isRefSkipDistinct(`tmpSym`.`fieldSym`): + # workaround #12489 + var tmp: `fieldType` + initFromJson(tmp, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) + `tmpSym`.`fieldSym` = tmp + else: + initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) else: initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) - else: - initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) - jsonPath.setLen `originalJsonPathLen` + jsonPath.setLen `originalJsonPathLen` - of nnkRecCase: - let kindSym = typeNode[0][0] - let kindNameLit = newLit(kindSym.strVal) - let kindPathLit = newLit("." & kindSym.strVal) - let kindType = typeNode[0][1] - let kindOffsetLit = newLit(uint(getOffset(kindSym))) - dst.add quote do: - var kindTmp: `kindType` - jsonPath.add `kindPathLit` - initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`) - jsonPath.setLen `originalJsonPathLen` - when defined js: - `tmpSym`.`kindSym` = kindTmp - else: - when nimvm: + of nnkRecCase: + let kindSym = typeNode[0][0] + let kindNameLit = newLit(kindSym.strVal) + let kindPathLit = newLit("." & kindSym.strVal) + let kindType = typeNode[0][1] + let kindOffsetLit = newLit(uint(getOffset(kindSym))) + dst.add quote do: + var kindTmp: `kindType` + jsonPath.add `kindPathLit` + initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`) + jsonPath.setLen `originalJsonPathLen` + when defined js: `tmpSym`.`kindSym` = kindTmp else: - # fuck it, assign kind field anyway - ((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp - dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym)) - for i in 1 ..< typeNode.len: - foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, originalJsonPathLen) + when nimvm: + `tmpSym`.`kindSym` = kindTmp + else: + # fuck it, assign kind field anyway + ((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp + dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym)) + for i in 1 ..< typeNode.len: + foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, originalJsonPathLen) - of nnkOfBranch, nnkElse: - let ofBranch = newNimNode(typeNode.kind) - for i in 0 ..< typeNode.len-1: - ofBranch.add copyNimTree(typeNode[i]) - let dstInner = newNimNode(nnkStmtListExpr) - foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, originalJsonPathLen) - # resOuter now contains the inner stmtList - ofBranch.add dstInner - dst[^1].expectKind nnkCaseStmt - dst[^1].add ofBranch + of nnkOfBranch, nnkElse: + let ofBranch = newNimNode(typeNode.kind) + for i in 0 ..< typeNode.len-1: + ofBranch.add copyNimTree(typeNode[i]) + let dstInner = newNimNode(nnkStmtListExpr) + foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, originalJsonPathLen) + # resOuter now contains the inner stmtList + ofBranch.add dstInner + dst[^1].expectKind nnkCaseStmt + dst[^1].add ofBranch - of nnkObjectTy: - typeNode[0].expectKind nnkEmpty - typeNode[1].expectKind {nnkEmpty, nnkOfInherit} - if typeNode[1].kind == nnkOfInherit: - let base = typeNode[1][0] - var impl = getTypeImpl(base) - while impl.kind in {nnkRefTy, nnkPtrTy}: - impl = getTypeImpl(impl[0]) - foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen) - let body = typeNode[2] - foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen) + of nnkObjectTy: + typeNode[0].expectKind nnkEmpty + typeNode[1].expectKind {nnkEmpty, nnkOfInherit} + if typeNode[1].kind == nnkOfInherit: + let base = typeNode[1][0] + var impl = getTypeImpl(base) + while impl.kind in {nnkRefTy, nnkPtrTy}: + impl = getTypeImpl(impl[0]) + foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen) + let body = typeNode[2] + foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen) - else: - error("unhandled kind: " & $typeNode.kind, typeNode) + else: + error("unhandled kind: " & $typeNode.kind, typeNode) -macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - let typeSym = getTypeInst(dst) - let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen") - result = newStmtList() - result.add quote do: - let `originalJsonPathLen` = len(`jsonPath`) - if typeSym.kind in {nnkTupleTy, nnkTupleConstr}: - # both, `dst` and `typeSym` don't have good lineinfo. But nothing - # else is available here. - detectIncompatibleType(typeSym, dst) - foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen) - else: - foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen) + macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + let typeSym = getTypeInst(dst) + let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen") + result = newStmtList() + result.add quote do: + let `originalJsonPathLen` = len(`jsonPath`) + if typeSym.kind in {nnkTupleTy, nnkTupleConstr}: + # both, `dst` and `typeSym` don't have good lineinfo. But nothing + # else is available here. + detectIncompatibleType(typeSym, dst) + foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen) + else: + foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen) -proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - assignObjectImpl(dst, jsonNode, jsonPath) + proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + assignObjectImpl(dst, jsonNode, jsonPath) -proc to*[T](node: JsonNode, t: typedesc[T]): T = - ## `Unmarshals`:idx: the specified node into the object type specified. - ## - ## Known limitations: - ## - ## * Heterogeneous arrays are not supported. - ## * Sets in object variants are not supported. - ## * Not nil annotations are not supported. - ## - runnableExamples: - let jsonNode = parseJson(""" - { - "person": { - "name": "Nimmer", - "age": 21 - }, - "list": [1, 2, 3, 4] - } - """) + proc to*[T](node: JsonNode, t: typedesc[T]): T = + ## `Unmarshals`:idx: the specified node into the object type specified. + ## + ## Known limitations: + ## + ## * Heterogeneous arrays are not supported. + ## * Sets in object variants are not supported. + ## * Not nil annotations are not supported. + ## + runnableExamples: + let jsonNode = parseJson(""" + { + "person": { + "name": "Nimmer", + "age": 21 + }, + "list": [1, 2, 3, 4] + } + """) - type - Person = object - name: string - age: int + type + Person = object + name: string + age: int - Data = object - person: Person - list: seq[int] + Data = object + person: Person + list: seq[int] - var data = to(jsonNode, Data) - doAssert data.person.name == "Nimmer" - doAssert data.person.age == 21 - doAssert data.list == @[1, 2, 3, 4] + var data = to(jsonNode, Data) + doAssert data.person.name == "Nimmer" + doAssert data.person.age == 21 + doAssert data.list == @[1, 2, 3, 4] - var jsonPath = "" - result = default(T) - initFromJson(result, node, jsonPath) + var jsonPath = "" + initFromJson(result, node, jsonPath) when false: import os diff --git a/lib/pure/lenientops.nim b/lib/pure/lenientops.nim index a8fc78e39..f73df5e5f 100644 --- a/lib/pure/lenientops.nim +++ b/lib/pure/lenientops.nim @@ -8,49 +8,49 @@ # ## This module offers implementations of common binary operations -## like `+`, `-`, `*`, `/` and comparison operations, +## like ``+``, ``-``, ``*``, ``/`` and comparison operations, ## which work for mixed float/int operands. ## All operations convert the integer operand into the ## type of the float operand. For numerical expressions, the return ## type is always the type of the float involved in the expression, ## i.e., there is no auto conversion from float32 to float64. ## -## **Note:** In general, auto-converting from int to float loses +## Note: In general, auto-converting from int to float loses ## information, which is why these operators live in a separate ## module. Use with care. ## ## Regarding binary comparison, this module only provides unequal operators. -## The equality operator `==` is omitted, because depending on the use case +## The equality operator ``==`` is omitted, because depending on the use case ## either casting to float or rounding to int might be preferred, and users ## should make an explicit choice. -func `+`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} = +proc `+`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} = F(i) + f -func `+`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} = +proc `+`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} = f + F(i) -func `-`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} = +proc `-`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} = F(i) - f -func `-`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} = +proc `-`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} = f - F(i) -func `*`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} = +proc `*`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} = F(i) * f -func `*`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} = +proc `*`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} = f * F(i) -func `/`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} = +proc `/`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} = F(i) / f -func `/`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} = +proc `/`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} = f / F(i) -func `<`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.inline.} = +proc `<`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.noSideEffect, inline.} = F(i) < f -func `<`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.inline.} = +proc `<`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.noSideEffect, inline.} = f < F(i) -func `<=`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.inline.} = +proc `<=`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.noSideEffect, inline.} = F(i) <= f -func `<=`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.inline.} = +proc `<=`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.noSideEffect, inline.} = f <= F(i) # Note that we must not defined `>=` and `>`, because system.nim already has a diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index 336a57ec1..27225ab8d 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -1,6 +1,6 @@ # # -# Nim's Runtime Library +# The Nim Compiler # (c) Copyright 2009 Andreas Rumpf # # See the file "copying.txt", included in this @@ -14,9 +14,6 @@ import strutils, streams -when defined(nimPreviewSlimSystem): - import std/assertions - const EndOfFile* = '\0' ## end of file marker NewLines* = {'\c', '\L'} @@ -36,7 +33,7 @@ type lineNumber*: int ## the current line number sentinel: int lineStart: int # index of last line start in buffer - offsetBase*: int # use `offsetBase + bufpos` to get the offset + offsetBase*: int # use ``offsetBase + bufpos`` to get the offset refillChars: set[char] proc close*(L: var BaseLexer) = diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index 46b59b750..06987d395 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -18,7 +18,7 @@ ## To get started, first create a logger: ## ## .. code-block:: -## import std/logging +## import logging ## ## var logger = newConsoleLogger() ## @@ -45,10 +45,11 @@ ## ``levelThreshold`` field and the global log filter. The latter can be changed ## with the `setLogFilter proc<#setLogFilter,Level>`_. ## -## .. warning:: -## For loggers that log to a console or to files, only error and fatal -## messages will cause their output buffers to be flushed immediately by default. -## set ``flushThreshold`` when creating the logger to change this. +## **Warning:** +## * For loggers that log to a console or to files, only error and fatal +## messages will cause their output buffers to be flushed immediately. +## Use the `flushFile proc `_ to flush the buffer +## manually if needed. ## ## Handlers ## -------- @@ -59,7 +60,7 @@ ## in the following example: ## ## .. code-block:: -## import std/logging +## import logging ## ## var consoleLog = newConsoleLogger() ## var fileLog = newFileLogger("errors.log", levelThreshold=lvlError) @@ -117,7 +118,7 @@ ## The following example illustrates how to use format strings: ## ## .. code-block:: -## import std/logging +## import logging ## ## var logger = newConsoleLogger(fmtStr="[$time] - $levelname: ") ## logger.log(lvlInfo, "this is a message") @@ -145,9 +146,6 @@ import strutils, times when not defined(js): import os -when defined(nimPreviewSlimSystem): - import std/syncio - type Level* = enum ## \ ## Enumeration of logging levels. @@ -199,15 +197,6 @@ const ## If a different format string is preferred, refer to the ## `documentation about format strings<#basic-usage-format-strings>`_ ## for more information, including a list of available variables. - defaultFlushThreshold = when NimMajor >= 2: - when defined(nimV1LogFlushBehavior): lvlError else: lvlAll - else: - when defined(nimFlushAllLogs): lvlAll else: lvlError - ## The threshold above which log messages to file-like loggers - ## are automatically flushed. - ## - ## By default, only error and fatal messages are logged, - ## but defining ``-d:nimFlushAllLogs`` will make all levels be flushed type Logger* = ref object of RootObj @@ -236,8 +225,6 @@ type ## * `FileLogger<#FileLogger>`_ ## * `RollingFileLogger<#RollingFileLogger>`_ useStderr*: bool ## If true, writes to stderr; otherwise, writes to stdout - flushThreshold*: Level ## Only messages that are at or above this - ## threshold will be flushed immediately when not defined(js): type @@ -253,8 +240,6 @@ when not defined(js): ## * `ConsoleLogger<#ConsoleLogger>`_ ## * `RollingFileLogger<#RollingFileLogger>`_ file*: File ## The wrapped file - flushThreshold*: Level ## Only messages that are at or above this - ## threshold will be flushed immediately RollingFileLogger* = ref object of FileLogger ## A logger that writes log messages to a file while performing log @@ -360,8 +345,8 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = ## `setLogFilter proc<#setLogFilter,Level>`_. ## ## **Note:** Only error and fatal messages will cause the output buffer - ## to be flushed immediately by default. Set ``flushThreshold`` when creating - ## the logger to change this. + ## to be flushed immediately. Use the `flushFile proc + ## `_ to flush the buffer manually if needed. ## ## See also: ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_ @@ -379,7 +364,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 = ln.cstring + let cln: cstring = ln case level of lvlDebug: {.emit: "console.debug(`cln`);".} of lvlInfo: {.emit: "console.info(`cln`);".} @@ -392,12 +377,12 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = if logger.useStderr: handle = stderr writeLine(handle, ln) - if level >= logger.flushThreshold: flushFile(handle) + if level in {lvlError, lvlFatal}: flushFile(handle) except IOError: discard proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr, - useStderr = false, flushThreshold = defaultFlushThreshold): ConsoleLogger = + useStderr = false): ConsoleLogger = ## Creates a new `ConsoleLogger<#ConsoleLogger>`_. ## ## By default, log messages are written to ``stdout``. If ``useStderr`` is @@ -421,7 +406,6 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr, new result result.fmtStr = fmtStr result.levelThreshold = levelThreshold - result.flushThreshold = flushThreshold result.useStderr = useStderr when not defined(js): @@ -437,8 +421,8 @@ when not defined(js): ## ## **Notes:** ## * Only error and fatal messages will cause the output buffer - ## to be flushed immediately by default. Set ``flushThreshold`` when creating - ## the logger to change this. + ## to be flushed immediately. Use the `flushFile proc + ## `_ to flush the buffer manually if needed. ## * This method is not available for the JavaScript backend. ## ## See also: @@ -456,7 +440,7 @@ when not defined(js): ## fileLog.log(lvlError, "error code is: ", 404) if level >= logging.level and level >= logger.levelThreshold: writeLine(logger.file, substituteLog(logger.fmtStr, level, args)) - if level >= logger.flushThreshold: flushFile(logger.file) + if level in {lvlError, lvlFatal}: flushFile(logger.file) proc defaultFilename*(): string = ## Returns the filename that is used by default when naming log files. @@ -467,8 +451,7 @@ when not defined(js): proc newFileLogger*(file: File, levelThreshold = lvlAll, - fmtStr = defaultFmtStr, - flushThreshold = defaultFlushThreshold): FileLogger = + fmtStr = defaultFmtStr): FileLogger = ## Creates a new `FileLogger<#FileLogger>`_ that uses the given file handle. ## ## **Note:** This proc is not available for the JavaScript backend. @@ -492,15 +475,13 @@ when not defined(js): new(result) result.file = file result.levelThreshold = levelThreshold - result.flushThreshold = flushThreshold result.fmtStr = fmtStr proc newFileLogger*(filename = defaultFilename(), mode: FileMode = fmAppend, levelThreshold = lvlAll, fmtStr = defaultFmtStr, - bufSize: int = -1, - flushThreshold = defaultFlushThreshold): FileLogger = + bufSize: int = -1): FileLogger = ## Creates a new `FileLogger<#FileLogger>`_ that logs to a file with the ## given filename. ## @@ -524,7 +505,7 @@ when not defined(js): ## var formatLog = newFileLogger("formatted.log", fmtStr=verboseFmtStr) ## var errorLog = newFileLogger("errors.log", levelThreshold=lvlError) let file = open(filename, mode, bufSize = bufSize) - newFileLogger(file, levelThreshold, fmtStr, flushThreshold) + newFileLogger(file, levelThreshold, fmtStr) # ------ @@ -556,8 +537,7 @@ when not defined(js): levelThreshold = lvlAll, fmtStr = defaultFmtStr, maxLines: Positive = 1000, - bufSize: int = -1, - flushThreshold = defaultFlushThreshold): RollingFileLogger = + bufSize: int = -1): RollingFileLogger = ## Creates a new `RollingFileLogger<#RollingFileLogger>`_. ## ## Once the current log file being written to contains ``maxLines`` lines, @@ -593,7 +573,6 @@ when not defined(js): result.curLine = 0 result.baseName = filename result.baseMode = mode - result.flushThreshold = flushThreshold result.logFiles = countFiles(filename) @@ -620,8 +599,8 @@ when not defined(js): ## ## **Notes:** ## * Only error and fatal messages will cause the output buffer - ## to be flushed immediately by default. Set ``flushThreshold`` when creating - ## the logger to change this. + ## to be flushed immediately. Use the `flushFile proc + ## `_ to flush the buffer manually if needed. ## * This method is not available for the JavaScript backend. ## ## See also: @@ -647,7 +626,7 @@ when not defined(js): bufSize = logger.bufSize) writeLine(logger.file, substituteLog(logger.fmtStr, level, args)) - if level >= logger.flushThreshold: flushFile(logger.file) + if level in {lvlError, lvlFatal}: flushFile(logger.file) logger.curLine.inc # -------- @@ -815,9 +794,9 @@ template fatal*(args: varargs[string, `$`]) = proc addHandler*(handler: Logger) = ## Adds a logger to the list of registered handlers. ## - ## .. warning:: The list of handlers is a thread-local variable. If the given - ## handler will be used in multiple threads, this proc should be called in - ## each of those threads. + ## **Warning:** The list of handlers is a thread-local variable. If the given + ## handler will be used in multiple threads, this proc should be called in + ## each of those threads. ## ## See also: ## * `getHandlers proc<#getHandlers>`_ @@ -841,10 +820,10 @@ proc setLogFilter*(lvl: Level) = ## individual logger's ``levelThreshold``. By default, all messages are ## logged. ## - ## .. warning:: The global log filter is a thread-local variable. If logging - ## is being performed in multiple threads, this proc should be called in each - ## thread unless it is intended that different threads should log at different - ## logging levels. + ## **Warning:** The global log filter is a thread-local variable. If logging + ## is being performed in multiple threads, this proc should be called in each + ## thread unless it is intended that different threads should log at different + ## logging levels. ## ## See also: ## * `getLogFilter proc<#getLogFilter>`_ diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index 848d7e3fb..e74e68b05 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -10,7 +10,7 @@ ## This module contains procs for `serialization`:idx: and `deserialization`:idx: ## of arbitrary Nim data structures. The serialization format uses `JSON`:idx:. ## -## **Restriction:** For objects, their type is **not** serialized. This means +## **Restriction**: For objects their type is **not** serialized. This means ## essentially that it does not work if the object has some other runtime ## type than its compiletime type. ## @@ -18,24 +18,31 @@ ## Basic usage ## =========== ## -runnableExamples: - type - A = object of RootObj - B = object of A - f: int - - let a: ref A = new(B) - assert $$a[] == "{}" # not "{f: 0}" - - # unmarshal - let c = to[B]("""{"f": 2}""") - assert typeof(c) is B - assert c.f == 2 - - # marshal - assert $$c == """{"f": 2}""" - -## **Note:** The `to` and `$$` operations are available at compile-time! +## .. code-block:: nim +## +## type +## A = object of RootObj +## B = object of A +## f: int +## +## var +## a: ref A +## b: ref B +## +## new(b) +## a = b +## echo($$a[]) # produces "{}", not "{f: 0}" +## +## # unmarshal +## let c = to[B]("""{"f": 2}""") +## assert typeof(c) is B +## assert c.f == 2 +## +## # marshal +## let s = $$c +## assert s == """{"f": 2}""" +## +## **Note**: The ``to`` and ``$$`` operations are available at compile-time! ## ## ## See also @@ -56,9 +63,6 @@ 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 @@ -166,10 +170,7 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) = of akSequence: case p.kind of jsonNull: - when defined(nimSeqsV2): - invokeNewSeq(a, 0) - else: - setPointer(a, nil) + setPointer(a, nil) next(p) of jsonArrayStart: next(p) @@ -236,10 +237,7 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) = of akString: case p.kind of jsonNull: - when defined(nimSeqsV2): - setString(a, "") - else: - setPointer(a, nil) + setPointer(a, nil) next(p) of jsonString: setString(a, p.str) @@ -281,8 +279,7 @@ proc loadAny(s: Stream, a: Any, t: var Table[BiggestInt, pointer]) = proc load*[T](s: Stream, data: var T) = ## Loads `data` from the stream `s`. Raises `IOError` in case of an error. runnableExamples: - import std/streams - + import marshal, streams var s = newStringStream("[1, 3, 5]") var a: array[3, int] load(s, a) @@ -291,11 +288,10 @@ proc load*[T](s: Stream, data: var T) = var tab = initTable[BiggestInt, pointer]() loadAny(s, toAny(data), tab) -proc store*[T](s: Stream, data: sink T) = +proc store*[T](s: Stream, data: T) = ## Stores `data` into the stream `s`. Raises `IOError` in case of an error. runnableExamples: - import std/streams - + import marshal, streams var s = newStringStream("") var a = [1, 3, 5] store(s, a) @@ -304,20 +300,13 @@ proc store*[T](s: Stream, data: sink T) = var stored = initIntSet() var d: T - when defined(gcArc) or defined(gcOrc): - d = data - else: - shallowCopy(d, data) + 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: sink T): string = +proc `$$`*[T](x: T): string = ## Returns a string representation of `x` (serialization, marshalling). ## - ## **Note:** to serialize `x` to JSON use `%x` from the `json` module - ## or `jsonutils.toJson(x)`. + ## **Note:** to serialize `x` to JSON use `$(%x)` from the ``json`` module. runnableExamples: type Foo = object @@ -328,24 +317,15 @@ proc `$$`*[T](x: sink T): string = let y = $$x assert y == """{"id": 1, "bar": "baz"}""" - when nimvm: - result = loadVM(T, x) - else: - var stored = initIntSet() - var d: T - when defined(gcArc) or defined(gcOrc): - d = x - else: - shallowCopy(d, x) - var s = newStringStream() - storeAny(s, toAny(d), stored) - result = s.data - -proc toVM[T](typ: typedesc[T], data: string): T = - discard "the implementation is in the compiler/vmops" + var stored = initIntSet() + var d: T + shallowCopy(d, x) + var s = newStringStream() + storeAny(s, toAny(d), stored) + result = s.data proc to*[T](data: string): T = - ## Reads data and transforms it to a type `T` (deserialization, unmarshalling). + ## Reads data and transforms it to a type ``T`` (deserialization, unmarshalling). runnableExamples: type Foo = object @@ -359,8 +339,79 @@ proc to*[T](data: string): T = assert z.id == 1 assert z.bar == "baz" - when nimvm: - result = toVM(T, data) - else: - var tab = initTable[BiggestInt, pointer]() - loadAny(newStringStream(data), toAny(result), tab) + var tab = initTable[BiggestInt, pointer]() + loadAny(newStringStream(data), toAny(result), tab) + + +when not defined(testing) and isMainModule: + template testit(x: untyped) = echo($$to[typeof(x)]($$x)) + + var x: array[0..4, array[0..4, string]] = [ + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"]] + testit(x) + var test2: tuple[name: string, s: uint] = ("tuple test", 56u) + testit(test2) + + type + TE = enum + blah, blah2 + + TestObj = object + test, asd: int + case test2: TE + of blah: + help: string + else: + nil + + PNode = ref Node + Node = object + next, prev: PNode + data: string + + proc buildList(): PNode = + new(result) + new(result.next) + new(result.prev) + result.data = "middle" + result.next.data = "next" + result.prev.data = "prev" + result.next.next = result.prev + result.next.prev = result + result.prev.next = result + result.prev.prev = result.next + + var test3: TestObj + test3.test = 42 + test3 = TestObj(test2: blah) + testit(test3) + + var test4: ref tuple[a, b: string] + new(test4) + test4.a = "ref string test: A" + test4.b = "ref string test: B" + testit(test4) + + var test5 = @[(0, 1), (2, 3), (4, 5)] + testit(test5) + + var test6: set[char] = {'A'..'Z', '_'} + testit(test6) + + var test7 = buildList() + echo($$test7) + testit(test7) + + type + A {.inheritable.} = object + B = object of A + f: int + + var + a: ref A + b: ref B + new(b) + a = b + echo($$a[]) # produces "{}", not "{f: 0}" diff --git a/lib/pure/math.nim b/lib/pure/math.nim index bea655a0e..a6a3676b9 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -12,45 +12,44 @@ ## Basic math routines for Nim. ## ## Note that the trigonometric functions naturally operate on radians. -## The helper functions `degToRad <#degToRad,T>`_ and `radToDeg <#radToDeg,T>`_ +## The helper functions `degToRad<#degToRad,T>`_ and `radToDeg<#radToDeg,T>`_ ## provide conversion between radians and degrees. - -runnableExamples: - from std/fenv import epsilon - from std/random import rand - - proc generateGaussianNoise(mu: float = 0.0, sigma: float = 1.0): (float, float) = - # Generates values from a normal distribution. - # Translated from https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform#Implementation. - var u1: float - var u2: float - while true: - u1 = rand(1.0) - u2 = rand(1.0) - if u1 > epsilon(float): break - let mag = sigma * sqrt(-2 * ln(u1)) - let z0 = mag * cos(2 * PI * u2) + mu - let z1 = mag * sin(2 * PI * u2) + mu - (z0, z1) - - echo generateGaussianNoise() - +## +## .. code-block:: +## +## import math +## from sequtils import map +## +## let a = [0.0, PI/6, PI/4, PI/3, PI/2] +## +## echo a.map(sin) +## # @[0.0, 0.499…, 0.707…, 0.866…, 1.0] +## +## echo a.map(tan) +## # @[0.0, 0.577…, 0.999…, 1.732…, 1.633…e+16] +## +## echo cos(degToRad(180.0)) +## # -1.0 +## +## echo sqrt(-1.0) +## # nan (use `complex` module) +## ## This module is available for the `JavaScript target ## `_. ## -## See also -## ======== -## * `complex module `_ for complex numbers and their +## **See also:** +## * `complex module`_ for complex numbers and their ## mathematical operations -## * `rationals module `_ for rational numbers and their +## * `rationals module`_ for rational numbers and their ## mathematical operations -## * `fenv module `_ for handling of floating-point rounding +## * `fenv module`_ for handling of floating-point rounding ## and exceptions (overflow, zero-divide, etc.) -## * `random module `_ for a fast and tiny random number generator -## * `stats module `_ for statistical analysis -## * `strformat module `_ for formatting floats for printing -## * `system module `_ for some very basic and trivial math operators -## (`shr`, `shl`, `xor`, `clamp`, etc.) +## * `random module`_ for fast and tiny random number generator +## * `mersenne module`_ for Mersenne twister random number generator +## * `stats module`_ for statistical analysis +## * `strformat module`_ for formatting floats for print +## * `system module`_ Some very basic and trivial math operators +## are on system directly, to name a few ``shr``, ``shl``, ``xor``, ``clamp``, etc. import std/private/since @@ -59,10 +58,6 @@ import std/private/since import bitops, fenv -when defined(nimPreviewSlimSystem): - import std/assertions - - when defined(c) or defined(cpp): proc c_isnan(x: float): bool {.importc: "isnan", header: "".} # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. @@ -70,23 +65,15 @@ when defined(c) or defined(cpp): proc c_copysign(x, y: cfloat): cfloat {.importc: "copysignf", header: "".} proc c_copysign(x, y: cdouble): cdouble {.importc: "copysign", header: "".} - proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "".} - - # don't export `c_frexp` in the future and remove `c_frexp2`. - func c_frexp2(x: cfloat, exponent: var cint): cfloat {. - importc: "frexpf", header: "".} - func c_frexp2(x: cdouble, exponent: var cint): cdouble {. - importc: "frexp", header: "".} - func binom*(n, k: int): int = - ## Computes the [binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient). + ## Computes the `binomial coefficient `_. runnableExamples: + doAssert binom(6, 2) == binom(6, 4) doAssert binom(6, 2) == 15 doAssert binom(-6, 2) == 1 doAssert binom(6, 0) == 1 - if k <= 0: return 1 - if 2 * k > n: return binom(n, n - k) + if 2*k > n: return binom(n, n-k) result = n for i in countup(2, k): result = (result * (n + 1 - i)) div i @@ -97,16 +84,15 @@ func createFactTable[N: static[int]]: array[N, int] = result[i] = result[i - 1] * i func fac*(n: int): int = - ## Computes the [factorial](https://en.wikipedia.org/wiki/Factorial) of - ## a non-negative integer `n`. + ## Computes the `factorial `_ of + ## a non-negative integer ``n``. ## - ## **See also:** + ## See also: ## * `prod func <#prod,openArray[T]>`_ runnableExamples: - doAssert fac(0) == 1 + doAssert fac(3) == 6 doAssert fac(4) == 24 doAssert fac(10) == 3628800 - const factTable = when sizeof(int) == 2: createFactTable[5]() @@ -120,47 +106,49 @@ func fac*(n: int): int = {.push checks: off, line_dir: off, stack_trace: off.} -when defined(posix) and not defined(genode): +when defined(Posix) and not defined(genode): {.passl: "-lm".} const - PI* = 3.1415926535897932384626433 ## The circle constant PI (Ludolph's number). - TAU* = 2.0 * PI ## The circle constant TAU (= 2 * PI). - E* = 2.71828182845904523536028747 ## Euler's number. + PI* = 3.1415926535897932384626433 ## The circle constant PI (Ludolph's number) + TAU* = 2.0 * PI ## The circle constant TAU (= 2 * PI) + E* = 2.71828182845904523536028747 ## Euler's number MaxFloat64Precision* = 16 ## Maximum number of meaningful digits ## after the decimal point for Nim's - ## `float64` type. + ## ``float64`` type. MaxFloat32Precision* = 8 ## Maximum number of meaningful digits ## after the decimal point for Nim's - ## `float32` type. + ## ``float32`` type. MaxFloatPrecision* = MaxFloat64Precision ## Maximum number of ## meaningful digits ## after the decimal point - ## for Nim's `float` type. + ## for Nim's ``float`` type. MinFloatNormal* = 2.225073858507201e-308 ## Smallest normal number for Nim's - ## `float` type (= 2^-1022). - RadPerDeg = PI / 180.0 ## Number of radians per degree. + ## ``float`` type. (= 2^-1022). + RadPerDeg = PI / 180.0 ## Number of radians per degree type FloatClass* = enum ## Describes the class a floating point value belongs to. - ## This is the type that is returned by the + ## This is the type that is returned by ## `classify func <#classify,float>`_. fcNormal, ## value is an ordinary nonzero floating point value fcSubnormal, ## value is a subnormal (a very small) floating point value fcZero, ## value is zero fcNegZero, ## value is the negative zero - fcNan, ## value is Not a Number (NaN) + fcNan, ## value is Not-A-Number (NAN) fcInf, ## value is positive infinity fcNegInf ## value is negative infinity func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = ## Returns whether `x` is a `NaN`, more efficiently than via `classify(x) == fcNan`. - ## Works even with `--passc:-ffast-math`. + ## Works even with: `--passc:-ffast-math`. runnableExamples: doAssert NaN.isNaN doAssert not Inf.isNaN + doAssert isNaN(Inf - Inf) doAssert not isNaN(3.1415926) + doAssert not isNaN(0'f32) template fn: untyped = result = x != x when nimvm: fn() @@ -168,88 +156,59 @@ func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = when defined(js): fn() else: result = c_isnan(x) -when defined(js): - import std/private/jsutils - - proc toBitsImpl(x: float): array[2, uint32] = - let buffer = newArrayBuffer(8) - let a = newFloat64Array(buffer) - let b = newUint32Array(buffer) - a[0] = x - {.emit: "`result` = `b`;".} - # result = cast[array[2, uint32]](b) - - proc jsSetSign(x: float, sgn: bool): float = - let buffer = newArrayBuffer(8) - let a = newFloat64Array(buffer) - let b = newUint32Array(buffer) - a[0] = x - asm """ - function updateBit(num, bitPos, bitVal) { - return (num & ~(1 << bitPos)) | (bitVal << bitPos); - } - `b`[1] = updateBit(`b`[1], 31, `sgn`); - `result` = `a`[0] - """ - -proc signbit*(x: SomeFloat): bool {.inline, since: (1, 5, 1).} = - ## Returns true if `x` is negative, false otherwise. - runnableExamples: - doAssert not signbit(0.0) - doAssert signbit(-0.0) - doAssert signbit(-0.1) - doAssert not signbit(0.1) - - when defined(js): - let uintBuffer = toBitsImpl(x) - result = (uintBuffer[1] shr 31) != 0 - else: - result = c_signbit(x) != 0 - func copySign*[T: SomeFloat](x, y: T): T {.inline, since: (1, 5, 1).} = ## Returns a value with the magnitude of `x` and the sign of `y`; - ## this works even if x or y are NaN, infinity or zero, all of which can carry a sign. + ## this works even if x or y are NaN or zero, both of which can carry a sign. runnableExamples: - doAssert copySign(10.0, 1.0) == 10.0 - doAssert copySign(10.0, -1.0) == -10.0 - doAssert copySign(-Inf, -0.0) == -Inf - doAssert copySign(NaN, 1.0).isNaN + doAssert copySign(1.0, -0.0) == -1.0 + doAssert copySign(0.0, -0.0) == -0.0 + doAssert copySign(-1.0, 0.0) == 1.0 + doAssert copySign(10.0, 0.0) == 10.0 + + doAssert copySign(Inf, -1.0) == -Inf + doAssert copySign(-Inf, 1.0) == Inf + doAssert copySign(-1.0, NaN) == 1.0 + doAssert copySign(10.0, NaN) == 10.0 + + doAssert copySign(NaN, 0.0).isNaN + doAssert copySign(NaN, -0.0).isNaN + + # fails in VM and JS backend doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0 - # TODO: use signbit for examples - when defined(js): - let uintBuffer = toBitsImpl(y) - let sgn = (uintBuffer[1] shr 31) != 0 - result = jsSetSign(x, sgn) + # TODO use signbit for examples + template impl() = + if y > 0.0 or (y == 0.0 and 1.0 / y > 0.0): + result = abs(x) + elif y <= 0.0: + result = -abs(x) + else: # must be NaN + result = abs(x) + + when defined(js): impl() else: - when nimvm: # not exact but we have a vmops for recent enough nim - if y > 0.0 or (y == 0.0 and 1.0 / y > 0.0): - result = abs(x) - elif y <= 0.0: - result = -abs(x) - else: # must be NaN - result = abs(x) + when nimvm: impl() else: result = c_copysign(x, y) func classify*(x: float): FloatClass = ## Classifies a floating point value. ## - ## Returns `x`'s class as specified by the `FloatClass enum<#FloatClass>`_. - ## Doesn't work with `--passc:-ffast-math`. + ## Returns ``x``'s class as specified by `FloatClass enum<#FloatClass>`_. + ## Doesn't work with: `--passc:-ffast-math`. runnableExamples: doAssert classify(0.3) == fcNormal doAssert classify(0.0) == fcZero - doAssert classify(0.3 / 0.0) == fcInf - doAssert classify(-0.3 / 0.0) == fcNegInf + doAssert classify(0.3/0.0) == fcInf + doAssert classify(-0.3/0.0) == fcNegInf doAssert classify(5.0e-324) == fcSubnormal # JavaScript and most C compilers have no classify: if x == 0.0: - if 1.0 / x == Inf: + if 1.0/x == Inf: return fcZero else: return fcNegZero - if x * 0.5 == x: + if x*0.5 == x: if x > 0.0: return fcInf else: return fcNegInf if x != x: return fcNan @@ -259,13 +218,13 @@ func classify*(x: float): FloatClass = func almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. since: (1, 5), inline.} = - ## Checks if two float values are almost equal, using the - ## [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon). + ## Checks if two float values are almost equal, using + ## `machine epsilon `_. ## ## `unitsInLastPlace` is the max number of - ## [units in the last place](https://en.wikipedia.org/wiki/Unit_in_the_last_place) + ## `units in last place `_ ## difference tolerated when comparing two numbers. The larger the value, the - ## more error is allowed. A `0` value means that two numbers must be exactly the + ## more error is allowed. A ``0`` value means that two numbers must be exactly the ## same to be considered equal. ## ## The machine epsilon has to be scaled to the magnitude of the values used @@ -274,9 +233,14 @@ func almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. ## # taken from: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon runnableExamples: - doAssert almostEqual(PI, 3.14159265358979) + doAssert almostEqual(3.141592653589793, 3.1415926535897936) + doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32) doAssert almostEqual(Inf, Inf) - doAssert not almostEqual(NaN, NaN) + doAssert almostEqual(-Inf, -Inf) + doAssert almostEqual(Inf, -Inf) == false + doAssert almostEqual(-Inf, Inf) == false + doAssert almostEqual(Inf, NaN) == false + doAssert almostEqual(NaN, NaN) == false if x == y: # short circuit exact equality -- needed to catch two infinities of @@ -287,33 +251,31 @@ func almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. diff < minimumPositiveValue(T) func isPowerOfTwo*(x: int): bool = - ## Returns `true`, if `x` is a power of two, `false` otherwise. + ## Returns ``true``, if ``x`` is a power of two, ``false`` otherwise. ## ## Zero and negative numbers are not a power of two. ## - ## **See also:** - ## * `nextPowerOfTwo func <#nextPowerOfTwo,int>`_ + ## See also: + ## * `nextPowerOfTwo func<#nextPowerOfTwo,int>`_ runnableExamples: - doAssert isPowerOfTwo(16) - doAssert not isPowerOfTwo(5) - doAssert not isPowerOfTwo(0) - doAssert not isPowerOfTwo(-16) - + doAssert isPowerOfTwo(16) == true + doAssert isPowerOfTwo(5) == false + doAssert isPowerOfTwo(0) == false + doAssert isPowerOfTwo(-16) == false return (x > 0) and ((x and (x - 1)) == 0) func nextPowerOfTwo*(x: int): int = - ## Returns `x` rounded up to the nearest power of two. + ## Returns ``x`` rounded up to the nearest power of two. ## ## Zero and negative numbers get rounded up to 1. ## - ## **See also:** - ## * `isPowerOfTwo func <#isPowerOfTwo,int>`_ + ## See also: + ## * `isPowerOfTwo func<#isPowerOfTwo,int>`_ runnableExamples: doAssert nextPowerOfTwo(16) == 16 doAssert nextPowerOfTwo(5) == 8 doAssert nextPowerOfTwo(0) == 1 doAssert nextPowerOfTwo(-16) == 1 - result = x - 1 when defined(cpu64): result = result or (result shr 32) @@ -327,103 +289,99 @@ func nextPowerOfTwo*(x: int): int = result += 1 + ord(x <= 0) func sum*[T](x: openArray[T]): T = - ## Computes the sum of the elements in `x`. + ## Computes the sum of the elements in ``x``. ## - ## If `x` is empty, 0 is returned. + ## If ``x`` is empty, 0 is returned. ## - ## **See also:** + ## See also: ## * `prod func <#prod,openArray[T]>`_ ## * `cumsum func <#cumsum,openArray[T]>`_ ## * `cumsummed func <#cumsummed,openArray[T]>`_ runnableExamples: doAssert sum([1, 2, 3, 4]) == 10 - doAssert sum([-4, 3, 5]) == 4 - + doAssert sum([-1.5, 2.7, -0.1]) == 1.1 for i in items(x): result = result + i func prod*[T](x: openArray[T]): T = - ## Computes the product of the elements in `x`. + ## Computes the product of the elements in ``x``. ## - ## If `x` is empty, 1 is returned. + ## If ``x`` is empty, 1 is returned. ## - ## **See also:** + ## See also: ## * `sum func <#sum,openArray[T]>`_ ## * `fac func <#fac,int>`_ runnableExamples: doAssert prod([1, 2, 3, 4]) == 24 doAssert prod([-4, 3, 5]) == -60 - - result = T(1) + result = 1.T for i in items(x): result = result * i func cumsummed*[T](x: openArray[T]): seq[T] = - ## Returns the cumulative (aka prefix) summation of `x`. + ## Return cumulative (aka prefix) summation of ``x``. ## - ## If `x` is empty, `@[]` is returned. - ## - ## **See also:** + ## See also: ## * `sum func <#sum,openArray[T]>`_ ## * `cumsum func <#cumsum,openArray[T]>`_ for the in-place version runnableExamples: - doAssert cumsummed([1, 2, 3, 4]) == @[1, 3, 6, 10] - - let xLen = x.len - if xLen == 0: - return @[] - result.setLen(xLen) + let a = [1, 2, 3, 4] + doAssert cumsummed(a) == @[1, 3, 6, 10] + result.setLen(x.len) result[0] = x[0] - for i in 1 ..< xLen: result[i] = result[i - 1] + x[i] + for i in 1 ..< x.len: result[i] = result[i-1] + x[i] func cumsum*[T](x: var openArray[T]) = - ## Transforms `x` in-place (must be declared as `var`) into its + ## Transforms ``x`` in-place (must be declared as `var`) into its ## cumulative (aka prefix) summation. ## - ## **See also:** + ## See also: ## * `sum func <#sum,openArray[T]>`_ ## * `cumsummed func <#cumsummed,openArray[T]>`_ for a version which - ## returns a cumsummed sequence + ## returns cumsummed sequence runnableExamples: var a = [1, 2, 3, 4] cumsum(a) doAssert a == @[1, 3, 6, 10] - - for i in 1 ..< x.len: x[i] = x[i - 1] + x[i] + for i in 1 ..< x.len: x[i] = x[i-1] + x[i] when not defined(js): # C func sqrt*(x: float32): float32 {.importc: "sqrtf", header: "".} - func sqrt*(x: float64): float64 {.importc: "sqrt", header: "".} = - ## Computes the square root of `x`. + func sqrt*(x: float64): float64 {.importc: "sqrt", header: "".} + ## Computes the square root of ``x``. ## - ## **See also:** - ## * `cbrt func <#cbrt,float64>`_ for the cube root - runnableExamples: - doAssert almostEqual(sqrt(4.0), 2.0) - doAssert almostEqual(sqrt(1.44), 1.2) + ## See also: + ## * `cbrt func <#cbrt,float64>`_ for cubic root + ## + ## .. code-block:: nim + ## echo sqrt(4.0) ## 2.0 + ## echo sqrt(1.44) ## 1.2 + ## echo sqrt(-4.0) ## nan func cbrt*(x: float32): float32 {.importc: "cbrtf", header: "".} - func cbrt*(x: float64): float64 {.importc: "cbrt", header: "".} = - ## Computes the cube root of `x`. + func cbrt*(x: float64): float64 {.importc: "cbrt", header: "".} + ## Computes the cubic root of ``x``. ## - ## **See also:** - ## * `sqrt func <#sqrt,float64>`_ for the square root - runnableExamples: - doAssert almostEqual(cbrt(8.0), 2.0) - doAssert almostEqual(cbrt(2.197), 1.3) - doAssert almostEqual(cbrt(-27.0), -3.0) + ## See also: + ## * `sqrt func <#sqrt,float64>`_ for square root + ## + ## .. code-block:: nim + ## echo cbrt(8.0) ## 2.0 + ## echo cbrt(2.197) ## 1.3 + ## echo cbrt(-27.0) ## -3.0 func ln*(x: float32): float32 {.importc: "logf", header: "".} - func ln*(x: float64): float64 {.importc: "log", header: "".} = - ## Computes the [natural logarithm](https://en.wikipedia.org/wiki/Natural_logarithm) - ## of `x`. + func ln*(x: float64): float64 {.importc: "log", header: "".} + ## Computes the `natural logarithm `_ + ## of ``x``. ## - ## **See also:** + ## See also: ## * `log func <#log,T,T>`_ ## * `log10 func <#log10,float64>`_ ## * `log2 func <#log2,float64>`_ ## * `exp func <#exp,float64>`_ - runnableExamples: - doAssert almostEqual(ln(exp(4.0)), 4.0) - doAssert almostEqual(ln(1.0), 0.0) - doAssert almostEqual(ln(0.0), -Inf) - doAssert ln(-7.0).isNaN + ## + ## .. code-block:: nim + ## echo ln(exp(4.0)) ## 4.0 + ## echo ln(1.0)) ## 0.0 + ## echo ln(0.0) ## -inf + ## echo ln(-7.0) ## nan else: # JS func sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.} func sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.} @@ -435,155 +393,199 @@ else: # JS func ln*(x: float64): float64 {.importc: "Math.log", nodecl.} func log*[T: SomeFloat](x, base: T): T = - ## Computes the logarithm of `x` to base `base`. + ## Computes the logarithm of ``x`` to base ``base``. ## - ## **See also:** + ## See also: ## * `ln func <#ln,float64>`_ ## * `log10 func <#log10,float64>`_ ## * `log2 func <#log2,float64>`_ - runnableExamples: - doAssert almostEqual(log(9.0, 3.0), 2.0) - doAssert almostEqual(log(0.0, 2.0), -Inf) - doAssert log(-7.0, 4.0).isNaN - doAssert log(8.0, -2.0).isNaN - + ## * `exp func <#exp,float64>`_ + ## + ## .. code-block:: nim + ## echo log(9.0, 3.0) ## 2.0 + ## echo log(32.0, 2.0) ## 5.0 + ## echo log(0.0, 2.0) ## -inf + ## echo log(-7.0, 4.0) ## nan + ## echo log(8.0, -2.0) ## nan ln(x) / ln(base) when not defined(js): # C func log10*(x: float32): float32 {.importc: "log10f", header: "".} - func log10*(x: float64): float64 {.importc: "log10", header: "".} = - ## Computes the common logarithm (base 10) of `x`. + func log10*(x: float64): float64 {.importc: "log10", header: "".} + ## Computes the common logarithm (base 10) of ``x``. ## - ## **See also:** + ## See also: ## * `ln func <#ln,float64>`_ ## * `log func <#log,T,T>`_ ## * `log2 func <#log2,float64>`_ - runnableExamples: - doAssert almostEqual(log10(100.0) , 2.0) - doAssert almostEqual(log10(0.0), -Inf) - doAssert log10(-100.0).isNaN + ## * `exp func <#exp,float64>`_ + ## + ## .. code-block:: nim + ## echo log10(100.0) ## 2.0 + ## echo log10(0.0) ## nan + ## echo log10(-100.0) ## -inf func exp*(x: float32): float32 {.importc: "expf", header: "".} - func exp*(x: float64): float64 {.importc: "exp", header: "".} = - ## Computes the exponential function of `x` (`e^x`). + func exp*(x: float64): float64 {.importc: "exp", header: "".} + ## Computes the exponential function of ``x`` (e^x). ## - ## **See also:** + ## See also: ## * `ln func <#ln,float64>`_ - runnableExamples: - doAssert almostEqual(exp(1.0), E) - doAssert almostEqual(ln(exp(4.0)), 4.0) - doAssert almostEqual(exp(0.0), 1.0) + ## * `log func <#log,T,T>`_ + ## * `log10 func <#log10,float64>`_ + ## * `log2 func <#log2,float64>`_ + ## + ## .. code-block:: nim + ## echo exp(1.0) ## 2.718281828459045 + ## echo ln(exp(4.0)) ## 4.0 + ## echo exp(0.0) ## 1.0 + ## echo exp(-1.0) ## 0.3678794411714423 func sin*(x: float32): float32 {.importc: "sinf", header: "".} - func sin*(x: float64): float64 {.importc: "sin", header: "".} = - ## Computes the sine of `x`. + func sin*(x: float64): float64 {.importc: "sin", header: "".} + ## Computes the sine of ``x``. ## - ## **See also:** - ## * `arcsin func <#arcsin,float64>`_ - runnableExamples: - doAssert almostEqual(sin(PI / 6), 0.5) - doAssert almostEqual(sin(degToRad(90.0)), 1.0) - func cos*(x: float32): float32 {.importc: "cosf", header: "".} - func cos*(x: float64): float64 {.importc: "cos", header: "".} = - ## Computes the cosine of `x`. - ## - ## **See also:** - ## * `arccos func <#arccos,float64>`_ - runnableExamples: - doAssert almostEqual(cos(2 * PI), 1.0) - doAssert almostEqual(cos(degToRad(60.0)), 0.5) - func tan*(x: float32): float32 {.importc: "tanf", header: "".} - func tan*(x: float64): float64 {.importc: "tan", header: "".} = - ## Computes the tangent of `x`. - ## - ## **See also:** - ## * `arctan func <#arctan,float64>`_ - runnableExamples: - doAssert almostEqual(tan(degToRad(45.0)), 1.0) - doAssert almostEqual(tan(PI / 4), 1.0) - func sinh*(x: float32): float32 {.importc: "sinhf", header: "".} - func sinh*(x: float64): float64 {.importc: "sinh", header: "".} = - ## Computes the [hyperbolic sine](https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions) of `x`. - ## - ## **See also:** - ## * `arcsinh func <#arcsinh,float64>`_ - runnableExamples: - doAssert almostEqual(sinh(0.0), 0.0) - doAssert almostEqual(sinh(1.0), 1.175201193643801) - func cosh*(x: float32): float32 {.importc: "coshf", header: "".} - func cosh*(x: float64): float64 {.importc: "cosh", header: "".} = - ## Computes the [hyperbolic cosine](https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions) of `x`. - ## - ## **See also:** - ## * `arccosh func <#arccosh,float64>`_ - runnableExamples: - doAssert almostEqual(cosh(0.0), 1.0) - doAssert almostEqual(cosh(1.0), 1.543080634815244) - func tanh*(x: float32): float32 {.importc: "tanhf", header: "".} - func tanh*(x: float64): float64 {.importc: "tanh", header: "".} = - ## Computes the [hyperbolic tangent](https://en.wikipedia.org/wiki/Hyperbolic_function#Definitions) of `x`. - ## - ## **See also:** - ## * `arctanh func <#arctanh,float64>`_ - runnableExamples: - doAssert almostEqual(tanh(0.0), 0.0) - doAssert almostEqual(tanh(1.0), 0.7615941559557649) - func arcsin*(x: float32): float32 {.importc: "asinf", header: "".} - func arcsin*(x: float64): float64 {.importc: "asin", header: "".} = - ## Computes the arc sine of `x`. - ## - ## **See also:** - ## * `sin func <#sin,float64>`_ - runnableExamples: - doAssert almostEqual(radToDeg(arcsin(0.0)), 0.0) - doAssert almostEqual(radToDeg(arcsin(1.0)), 90.0) - func arccos*(x: float32): float32 {.importc: "acosf", header: "".} - func arccos*(x: float64): float64 {.importc: "acos", header: "".} = - ## Computes the arc cosine of `x`. - ## - ## **See also:** + ## See also: ## * `cos func <#cos,float64>`_ - runnableExamples: - doAssert almostEqual(radToDeg(arccos(0.0)), 90.0) - doAssert almostEqual(radToDeg(arccos(1.0)), 0.0) - func arctan*(x: float32): float32 {.importc: "atanf", header: "".} - func arctan*(x: float64): float64 {.importc: "atan", header: "".} = - ## Calculate the arc tangent of `x`. + ## * `tan func <#tan,float64>`_ + ## * `arcsin func <#arcsin,float64>`_ + ## * `sinh func <#sinh,float64>`_ ## - ## **See also:** + ## .. code-block:: nim + ## echo sin(PI / 6) ## 0.4999999999999999 + ## echo sin(degToRad(90.0)) ## 1.0 + func cos*(x: float32): float32 {.importc: "cosf", header: "".} + func cos*(x: float64): float64 {.importc: "cos", header: "".} + ## Computes the cosine of ``x``. + ## + ## See also: + ## * `sin func <#sin,float64>`_ + ## * `tan func <#tan,float64>`_ + ## * `arccos func <#arccos,float64>`_ + ## * `cosh func <#cosh,float64>`_ + ## + ## .. code-block:: nim + ## echo cos(2 * PI) ## 1.0 + ## echo cos(degToRad(60.0)) ## 0.5000000000000001 + func tan*(x: float32): float32 {.importc: "tanf", header: "".} + func tan*(x: float64): float64 {.importc: "tan", header: "".} + ## Computes the tangent of ``x``. + ## + ## See also: + ## * `sin func <#sin,float64>`_ + ## * `cos func <#cos,float64>`_ + ## * `arctan func <#arctan,float64>`_ + ## * `tanh func <#tanh,float64>`_ + ## + ## .. code-block:: nim + ## echo tan(degToRad(45.0)) ## 0.9999999999999999 + ## echo tan(PI / 4) ## 0.9999999999999999 + func sinh*(x: float32): float32 {.importc: "sinhf", header: "".} + func sinh*(x: float64): float64 {.importc: "sinh", header: "".} + ## Computes the `hyperbolic sine `_ of ``x``. + ## + ## See also: + ## * `cosh func <#cosh,float64>`_ + ## * `tanh func <#tanh,float64>`_ + ## * `arcsinh func <#arcsinh,float64>`_ + ## * `sin func <#sin,float64>`_ + ## + ## .. code-block:: nim + ## echo sinh(0.0) ## 0.0 + ## echo sinh(1.0) ## 1.175201193643801 + ## echo sinh(degToRad(90.0)) ## 2.301298902307295 + func cosh*(x: float32): float32 {.importc: "coshf", header: "".} + func cosh*(x: float64): float64 {.importc: "cosh", header: "".} + ## Computes the `hyperbolic cosine `_ of ``x``. + ## + ## See also: + ## * `sinh func <#sinh,float64>`_ + ## * `tanh func <#tanh,float64>`_ + ## * `arccosh func <#arccosh,float64>`_ + ## * `cos func <#cos,float64>`_ + ## + ## .. code-block:: nim + ## echo cosh(0.0) ## 1.0 + ## echo cosh(1.0) ## 1.543080634815244 + ## echo cosh(degToRad(90.0)) ## 2.509178478658057 + func tanh*(x: float32): float32 {.importc: "tanhf", header: "".} + func tanh*(x: float64): float64 {.importc: "tanh", header: "".} + ## Computes the `hyperbolic tangent `_ of ``x``. + ## + ## See also: + ## * `sinh func <#sinh,float64>`_ + ## * `cosh func <#cosh,float64>`_ + ## * `arctanh func <#arctanh,float64>`_ + ## * `tan func <#tan,float64>`_ + ## + ## .. code-block:: nim + ## echo tanh(0.0) ## 0.0 + ## echo tanh(1.0) ## 0.7615941559557649 + ## echo tanh(degToRad(90.0)) ## 0.9171523356672744 + + func arccos*(x: float32): float32 {.importc: "acosf", header: "".} + func arccos*(x: float64): float64 {.importc: "acos", header: "".} + ## Computes the arc cosine of ``x``. + ## + ## See also: + ## * `arcsin func <#arcsin,float64>`_ + ## * `arctan func <#arctan,float64>`_ + ## * `arctan2 func <#arctan2,float64,float64>`_ + ## * `cos func <#cos,float64>`_ + ## + ## .. code-block:: nim + ## echo radToDeg(arccos(0.0)) ## 90.0 + ## echo radToDeg(arccos(1.0)) ## 0.0 + func arcsin*(x: float32): float32 {.importc: "asinf", header: "".} + func arcsin*(x: float64): float64 {.importc: "asin", header: "".} + ## Computes the arc sine of ``x``. + ## + ## See also: + ## * `arccos func <#arccos,float64>`_ + ## * `arctan func <#arctan,float64>`_ + ## * `arctan2 func <#arctan2,float64,float64>`_ + ## * `sin func <#sin,float64>`_ + ## + ## .. code-block:: nim + ## echo radToDeg(arcsin(0.0)) ## 0.0 + ## echo radToDeg(arcsin(1.0)) ## 90.0 + func arctan*(x: float32): float32 {.importc: "atanf", header: "".} + func arctan*(x: float64): float64 {.importc: "atan", header: "".} + ## Calculate the arc tangent of ``x``. + ## + ## See also: + ## * `arcsin func <#arcsin,float64>`_ + ## * `arccos func <#arccos,float64>`_ ## * `arctan2 func <#arctan2,float64,float64>`_ ## * `tan func <#tan,float64>`_ - runnableExamples: - doAssert almostEqual(arctan(1.0), 0.7853981633974483) - doAssert almostEqual(radToDeg(arctan(1.0)), 45.0) - func arctan2*(y, x: float32): float32 {.importc: "atan2f", header: "".} - func arctan2*(y, x: float64): float64 {.importc: "atan2", header: "".} = - ## Calculate the arc tangent of `y/x`. + ## + ## .. code-block:: nim + ## echo arctan(1.0) ## 0.7853981633974483 + ## echo radToDeg(arctan(1.0)) ## 45.0 + func arctan2*(y, x: float32): float32 {.importc: "atan2f", + header: "".} + func arctan2*(y, x: float64): float64 {.importc: "atan2", header: "".} + ## Calculate the arc tangent of ``y`` / ``x``. ## ## It produces correct results even when the resulting angle is near - ## `PI/2` or `-PI/2` (`x` near 0). + ## pi/2 or -pi/2 (``x`` near 0). ## - ## **See also:** + ## See also: + ## * `arcsin func <#arcsin,float64>`_ + ## * `arccos func <#arccos,float64>`_ ## * `arctan func <#arctan,float64>`_ - runnableExamples: - doAssert almostEqual(arctan2(1.0, 0.0), PI / 2.0) - doAssert almostEqual(radToDeg(arctan2(1.0, 0.0)), 90.0) + ## * `tan func <#tan,float64>`_ + ## + ## .. code-block:: nim + ## echo arctan2(1.0, 0.0) ## 1.570796326794897 + ## echo radToDeg(arctan2(1.0, 0.0)) ## 90.0 func arcsinh*(x: float32): float32 {.importc: "asinhf", header: "".} func arcsinh*(x: float64): float64 {.importc: "asinh", header: "".} - ## Computes the inverse hyperbolic sine of `x`. - ## - ## **See also:** - ## * `sinh func <#sinh,float64>`_ + ## Computes the inverse hyperbolic sine of ``x``. func arccosh*(x: float32): float32 {.importc: "acoshf", header: "".} func arccosh*(x: float64): float64 {.importc: "acosh", header: "".} - ## Computes the inverse hyperbolic cosine of `x`. - ## - ## **See also:** - ## * `cosh func <#cosh,float64>`_ + ## Computes the inverse hyperbolic cosine of ``x``. func arctanh*(x: float32): float32 {.importc: "atanhf", header: "".} func arctanh*(x: float64): float64 {.importc: "atanh", header: "".} - ## Computes the inverse hyperbolic tangent of `x`. - ## - ## **See also:** - ## * `tanh func <#tanh,float64>`_ + ## Computes the inverse hyperbolic tangent of ``x``. else: # JS func log10*(x: float32): float32 {.importc: "Math.log10", nodecl.} @@ -613,116 +615,127 @@ else: # JS func arctanh*[T: float32|float64](x: T): T {.importc: "Math.atanh", nodecl.} func cot*[T: float32|float64](x: T): T = 1.0 / tan(x) - ## Computes the cotangent of `x` (`1/tan(x)`). + ## Computes the cotangent of ``x`` (1 / tan(x)). func sec*[T: float32|float64](x: T): T = 1.0 / cos(x) - ## Computes the secant of `x` (`1/cos(x)`). + ## Computes the secant of ``x`` (1 / cos(x)). func csc*[T: float32|float64](x: T): T = 1.0 / sin(x) - ## Computes the cosecant of `x` (`1/sin(x)`). + ## Computes the cosecant of ``x`` (1 / sin(x)). func coth*[T: float32|float64](x: T): T = 1.0 / tanh(x) - ## Computes the hyperbolic cotangent of `x` (`1/tanh(x)`). + ## Computes the hyperbolic cotangent of ``x`` (1 / tanh(x)). func sech*[T: float32|float64](x: T): T = 1.0 / cosh(x) - ## Computes the hyperbolic secant of `x` (`1/cosh(x)`). + ## Computes the hyperbolic secant of ``x`` (1 / cosh(x)). func csch*[T: float32|float64](x: T): T = 1.0 / sinh(x) - ## Computes the hyperbolic cosecant of `x` (`1/sinh(x)`). + ## Computes the hyperbolic cosecant of ``x`` (1 / sinh(x)). func arccot*[T: float32|float64](x: T): T = arctan(1.0 / x) - ## Computes the inverse cotangent of `x` (`arctan(1/x)`). + ## Computes the inverse cotangent of ``x``. func arcsec*[T: float32|float64](x: T): T = arccos(1.0 / x) - ## Computes the inverse secant of `x` (`arccos(1/x)`). + ## Computes the inverse secant of ``x``. func arccsc*[T: float32|float64](x: T): T = arcsin(1.0 / x) - ## Computes the inverse cosecant of `x` (`arcsin(1/x)`). + ## Computes the inverse cosecant of ``x``. func arccoth*[T: float32|float64](x: T): T = arctanh(1.0 / x) - ## Computes the inverse hyperbolic cotangent of `x` (`arctanh(1/x)`). + ## Computes the inverse hyperbolic cotangent of ``x``. func arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x) - ## Computes the inverse hyperbolic secant of `x` (`arccosh(1/x)`). + ## Computes the inverse hyperbolic secant of ``x``. func arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x) - ## Computes the inverse hyperbolic cosecant of `x` (`arcsinh(1/x)`). + ## Computes the inverse hyperbolic cosecant of ``x``. const windowsCC89 = defined(windows) and defined(bcc) when not defined(js): # C func hypot*(x, y: float32): float32 {.importc: "hypotf", header: "".} - func hypot*(x, y: float64): float64 {.importc: "hypot", header: "".} = - ## Computes the length of the hypotenuse of a right-angle triangle with - ## `x` as its base and `y` as its height. Equivalent to `sqrt(x*x + y*y)`. - runnableExamples: - doAssert almostEqual(hypot(3.0, 4.0), 5.0) + func hypot*(x, y: float64): float64 {.importc: "hypot", header: "".} + ## Computes the hypotenuse of a right-angle triangle with ``x`` and + ## ``y`` as its base and height. Equivalent to ``sqrt(x*x + y*y)``. + ## + ## .. code-block:: nim + ## echo hypot(4.0, 3.0) ## 5.0 func pow*(x, y: float32): float32 {.importc: "powf", header: "".} - func pow*(x, y: float64): float64 {.importc: "pow", header: "".} = - ## Computes `x` raised to the power of `y`. + func pow*(x, y: float64): float64 {.importc: "pow", header: "".} + ## Computes x to power raised of y. ## - ## To compute the power between integers (e.g. 2^6), - ## use the `^ func <#^,T,Natural>`_. + ## To compute power between integers (e.g. 2^6), use `^ func<#^,T,Natural>`_. ## - ## **See also:** - ## * `^ func <#^,T,Natural>`_ + ## See also: + ## * `^ func<#^,T,Natural>`_ ## * `sqrt func <#sqrt,float64>`_ ## * `cbrt func <#cbrt,float64>`_ - runnableExamples: - doAssert almostEqual(pow(100, 1.5), 1000.0) - doAssert almostEqual(pow(16.0, 0.5), 4.0) + ## + ## .. code-block:: nim + ## echo pow(100, 1.5) ## 1000.0 + ## echo pow(16.0, 0.5) ## 4.0 # TODO: add C89 version on windows when not windowsCC89: func erf*(x: float32): float32 {.importc: "erff", header: "".} func erf*(x: float64): float64 {.importc: "erf", header: "".} - ## Computes the [error function](https://en.wikipedia.org/wiki/Error_function) for `x`. + ## Computes the `error function `_ for ``x``. ## - ## **Note:** Not available for the JS backend. + ## Note: Not available for JS backend. func erfc*(x: float32): float32 {.importc: "erfcf", header: "".} func erfc*(x: float64): float64 {.importc: "erfc", header: "".} - ## Computes the [complementary error function](https://en.wikipedia.org/wiki/Error_function#Complementary_error_function) for `x`. + ## Computes the `complementary error function `_ for ``x``. ## - ## **Note:** Not available for the JS backend. + ## Note: Not available for JS backend. func gamma*(x: float32): float32 {.importc: "tgammaf", header: "".} - func gamma*(x: float64): float64 {.importc: "tgamma", header: "".} = - ## Computes the [gamma function](https://en.wikipedia.org/wiki/Gamma_function) for `x`. + func gamma*(x: float64): float64 {.importc: "tgamma", header: "".} + ## Computes the `gamma function `_ for ``x``. ## - ## **Note:** Not available for the JS backend. + ## Note: Not available for JS backend. ## - ## **See also:** - ## * `lgamma func <#lgamma,float64>`_ for the natural logarithm of the gamma function - runnableExamples: - doAssert almostEqual(gamma(1.0), 1.0) - doAssert almostEqual(gamma(4.0), 6.0) - doAssert almostEqual(gamma(11.0), 3628800.0) + ## See also: + ## * `lgamma func <#lgamma,float64>`_ for a natural log of gamma function + ## + ## .. code-block:: Nim + ## echo gamma(1.0) # 1.0 + ## echo gamma(4.0) # 6.0 + ## echo gamma(11.0) # 3628800.0 + ## echo gamma(-1.0) # nan func lgamma*(x: float32): float32 {.importc: "lgammaf", header: "".} - func lgamma*(x: float64): float64 {.importc: "lgamma", header: "".} = - ## Computes the natural logarithm of the gamma function for `x`. + func lgamma*(x: float64): float64 {.importc: "lgamma", header: "".} + ## Computes the natural log of the gamma function for ``x``. ## - ## **Note:** Not available for the JS backend. + ## Note: Not available for JS backend. ## - ## **See also:** + ## See also: ## * `gamma func <#gamma,float64>`_ for gamma function + ## + ## .. code-block:: Nim + ## echo lgamma(1.0) # 1.0 + ## echo lgamma(4.0) # 1.791759469228055 + ## echo lgamma(11.0) # 15.10441257307552 + ## echo lgamma(-1.0) # inf func floor*(x: float32): float32 {.importc: "floorf", header: "".} - func floor*(x: float64): float64 {.importc: "floor", header: "".} = - ## Computes the floor function (i.e. the largest integer not greater than `x`). + func floor*(x: float64): float64 {.importc: "floor", header: "".} + ## Computes the floor function (i.e., the largest integer not greater than ``x``). ## - ## **See also:** + ## See also: ## * `ceil func <#ceil,float64>`_ ## * `round func <#round,float64>`_ ## * `trunc func <#trunc,float64>`_ - runnableExamples: - doAssert floor(2.1) == 2.0 - doAssert floor(2.9) == 2.0 - doAssert floor(-3.5) == -4.0 + ## + ## .. code-block:: nim + ## echo floor(2.1) ## 2.0 + ## echo floor(2.9) ## 2.0 + ## echo floor(-3.5) ## -4.0 func ceil*(x: float32): float32 {.importc: "ceilf", header: "".} - func ceil*(x: float64): float64 {.importc: "ceil", header: "".} = - ## Computes the ceiling function (i.e. the smallest integer not smaller - ## than `x`). + func ceil*(x: float64): float64 {.importc: "ceil", header: "".} + ## Computes the ceiling function (i.e., the smallest integer not smaller + ## than ``x``). ## - ## **See also:** + ## See also: ## * `floor func <#floor,float64>`_ ## * `round func <#round,float64>`_ ## * `trunc func <#trunc,float64>`_ - runnableExamples: - doAssert ceil(2.1) == 3.0 - doAssert ceil(2.9) == 3.0 - doAssert ceil(-2.1) == -2.0 + ## + ## .. code-block:: nim + ## echo ceil(2.1) ## 3.0 + ## echo ceil(2.9) ## 3.0 + ## echo ceil(-2.1) ## -2.0 when windowsCC89: # MSVC 2010 don't have trunc/truncf @@ -742,8 +755,8 @@ when not defined(js): # C let e = (x shr shift) and mask - bias # Keep the top 12+e bits, the integer part; clear the rest. - if e < 64 - 12: - x = x and (not (1'u64 shl (64'u64 - 12'u64 - e) - 1'u64)) + if e < 64-12: + x = x and (not (1'u64 shl (64'u64-12'u64-e) - 1'u64)) result = cast[float64](x) @@ -762,8 +775,8 @@ when not defined(js): # C let e = (x shr shift) and mask - bias # Keep the top 9+e bits, the integer part; clear the rest. - if e < 32 - 9: - x = x and (not (1'u32 shl (32'u32 - 9'u32 - e) - 1'u32)) + if e < 32-9: + x = x and (not (1'u32 shl (32'u32-9'u32-e) - 1'u32)) result = cast[float32](x) @@ -781,46 +794,49 @@ when not defined(js): # C result = if x < 0.0: ceil(x - T(0.5)) else: floor(x + T(0.5)) else: func round*(x: float32): float32 {.importc: "roundf", header: "".} - func round*(x: float64): float64 {.importc: "round", header: "".} = + func round*(x: float64): float64 {.importc: "round", header: "".} ## Rounds a float to zero decimal places. ## ## Used internally by the `round func <#round,T,int>`_ ## when the specified number of places is 0. ## - ## **See also:** + ## See also: ## * `round func <#round,T,int>`_ for rounding to the specific ## number of decimal places ## * `floor func <#floor,float64>`_ ## * `ceil func <#ceil,float64>`_ ## * `trunc func <#trunc,float64>`_ - runnableExamples: - doAssert round(3.4) == 3.0 - doAssert round(3.5) == 4.0 - doAssert round(4.5) == 5.0 + ## + ## .. code-block:: nim + ## echo round(3.4) ## 3.0 + ## echo round(3.5) ## 4.0 + ## echo round(4.5) ## 5.0 func trunc*(x: float32): float32 {.importc: "truncf", header: "".} - func trunc*(x: float64): float64 {.importc: "trunc", header: "".} = - ## Truncates `x` to the decimal point. + func trunc*(x: float64): float64 {.importc: "trunc", header: "".} + ## Truncates ``x`` to the decimal point. ## - ## **See also:** + ## See also: ## * `floor func <#floor,float64>`_ ## * `ceil func <#ceil,float64>`_ ## * `round func <#round,float64>`_ - runnableExamples: - doAssert trunc(PI) == 3.0 - doAssert trunc(-1.85) == -1.0 + ## + ## .. code-block:: nim + ## echo trunc(PI) # 3.0 + ## echo trunc(-1.85) # -1.0 func `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "".} - func `mod`*(x, y: float64): float64 {.importc: "fmod", header: "".} = - ## Computes the modulo operation for float values (the remainder of `x` divided by `y`). + func `mod`*(x, y: float64): float64 {.importc: "fmod", header: "".} + ## Computes the modulo operation for float values (the remainder of ``x`` divided by ``y``). ## - ## **See also:** - ## * `floorMod func <#floorMod,T,T>`_ for Python-like (`%` operator) behavior - runnableExamples: - doAssert 6.5 mod 2.5 == 1.5 - doAssert -6.5 mod 2.5 == -1.5 - doAssert 6.5 mod -2.5 == 1.5 - doAssert -6.5 mod -2.5 == -1.5 + ## See also: + ## * `floorMod func <#floorMod,T,T>`_ for Python-like (% operator) behavior + ## + ## .. code-block:: nim + ## ( 6.5 mod 2.5) == 1.5 + ## (-6.5 mod 2.5) == -1.5 + ## ( 6.5 mod -2.5) == 1.5 + ## (-6.5 mod -2.5) == -1.5 else: # JS func hypot*(x, y: float32): float32 {.importc: "Math.hypot", varargs, nodecl.} @@ -845,80 +861,81 @@ else: # JS func trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.} func trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.} - func `mod`*(x, y: float32): float32 {.importjs: "(# % #)".} - func `mod`*(x, y: float64): float64 {.importjs: "(# % #)".} = - ## Computes the modulo operation for float values (the remainder of `x` divided by `y`). - runnableExamples: - doAssert 6.5 mod 2.5 == 1.5 - doAssert -6.5 mod 2.5 == -1.5 - doAssert 6.5 mod -2.5 == 1.5 - doAssert -6.5 mod -2.5 == -1.5 + func `mod`*(x, y: float32): float32 {.importcpp: "# % #".} + func `mod`*(x, y: float64): float64 {.importcpp: "# % #".} + ## Computes the modulo operation for float values (the remainder of ``x`` divided by ``y``). + ## + ## .. code-block:: nim + ## ( 6.5 mod 2.5) == 1.5 + ## (-6.5 mod 2.5) == -1.5 + ## ( 6.5 mod -2.5) == 1.5 + ## (-6.5 mod -2.5) == -1.5 func round*[T: float32|float64](x: T, places: int): T = ## Decimal rounding on a binary floating point number. ## ## This function is NOT reliable. Floating point numbers cannot hold - ## non integer decimals precisely. If `places` is 0 (or omitted), + ## non integer decimals precisely. If ``places`` is 0 (or omitted), ## round to the nearest integral value following normal mathematical - ## rounding rules (e.g. `round(54.5) -> 55.0`). If `places` is + ## rounding rules (e.g. ``round(54.5) -> 55.0``). If ``places`` is ## greater than 0, round to the given number of decimal places, - ## e.g. `round(54.346, 2) -> 54.350000000000001421…`. If `places` is negative, round - ## to the left of the decimal place, e.g. `round(537.345, -1) -> 540.0`. - runnableExamples: - doAssert round(PI, 2) == 3.14 - doAssert round(PI, 4) == 3.1416 - + ## e.g. ``round(54.346, 2) -> 54.350000000000001421…``. If ``places`` is negative, round + ## to the left of the decimal place, e.g. ``round(537.345, -1) -> + ## 540.0`` + ## + ## .. code-block:: Nim + ## echo round(PI, 2) ## 3.14 + ## echo round(PI, 4) ## 3.1416 if places == 0: result = round(x) else: - var mult = pow(10.0, T(places)) - result = round(x * mult) / mult + var mult = pow(10.0, places.T) + result = round(x*mult)/mult func floorDiv*[T: SomeInteger](x, y: T): T = - ## Floor division is conceptually defined as `floor(x / y)`. + ## Floor division is conceptually defined as ``floor(x / y)``. ## ## This is different from the `system.div `_ - ## operator, which is defined as `trunc(x / y)`. - ## That is, `div` rounds towards `0` and `floorDiv` rounds down. + ## operator, which is defined as ``trunc(x / y)``. + ## That is, ``div`` rounds towards ``0`` and ``floorDiv`` rounds down. ## - ## **See also:** + ## See also: ## * `system.div proc `_ for integer division - ## * `floorMod func <#floorMod,T,T>`_ for Python-like (`%` operator) behavior - runnableExamples: - doAssert floorDiv( 13, 3) == 4 - doAssert floorDiv(-13, 3) == -5 - doAssert floorDiv( 13, -3) == -5 - doAssert floorDiv(-13, -3) == 4 - + ## * `floorMod func <#floorMod,T,T>`_ for Python-like (% operator) behavior + ## + ## .. code-block:: nim + ## echo floorDiv( 13, 3) # 4 + ## echo floorDiv(-13, 3) # -5 + ## echo floorDiv( 13, -3) # -5 + ## echo floorDiv(-13, -3) # 4 result = x div y let r = x mod y if (r > 0 and y < 0) or (r < 0 and y > 0): result.dec 1 func floorMod*[T: SomeNumber](x, y: T): T = - ## Floor modulo is conceptually defined as `x - (floorDiv(x, y) * y)`. + ## Floor modulus is conceptually defined as ``x - (floorDiv(x, y) * y)``. ## - ## This func behaves the same as the `%` operator in Python. + ## This func behaves the same as the ``%`` operator in Python. ## - ## **See also:** + ## See also: ## * `mod func <#mod,float64,float64>`_ ## * `floorDiv func <#floorDiv,T,T>`_ - runnableExamples: - doAssert floorMod( 13, 3) == 1 - doAssert floorMod(-13, 3) == 2 - doAssert floorMod( 13, -3) == -2 - doAssert floorMod(-13, -3) == -1 - + ## + ## .. code-block:: nim + ## echo floorMod( 13, 3) # 1 + ## echo floorMod(-13, 3) # 2 + ## echo floorMod( 13, -3) # -2 + ## echo floorMod(-13, -3) # -1 result = x mod y if (result > 0 and y < 0) or (result < 0 and y > 0): result += y func euclDiv*[T: SomeInteger](x, y: T): T {.since: (1, 5, 1).} = ## Returns euclidean division of `x` by `y`. runnableExamples: - doAssert euclDiv(13, 3) == 4 - doAssert euclDiv(-13, 3) == -5 - doAssert euclDiv(13, -3) == -4 - doAssert euclDiv(-13, -3) == 5 - + assert euclDiv(13, 3) == 4 + assert euclDiv(-13, 3) == -5 + assert euclDiv(13, -3) == -4 + assert euclDiv(-13, -3) == 5 result = x div y if x mod y < 0: if y > 0: @@ -930,129 +947,45 @@ func euclMod*[T: SomeNumber](x, y: T): T {.since: (1, 5, 1).} = ## Returns euclidean modulo of `x` by `y`. ## `euclMod(x, y)` is non-negative. runnableExamples: - doAssert euclMod(13, 3) == 1 - doAssert euclMod(-13, 3) == 2 - doAssert euclMod(13, -3) == 1 - doAssert euclMod(-13, -3) == 2 - + assert euclMod(13, 3) == 1 + assert euclMod(-13, 3) == 2 + assert euclMod(13, -3) == 1 + assert euclMod(-13, -3) == 2 result = x mod y if result < 0: result += abs(y) -func ceilDiv*[T: SomeInteger](x, y: T): T {.inline, since: (1, 5, 1).} = - ## Ceil division is conceptually defined as `ceil(x / y)`. - ## - ## Assumes `x >= 0` and `y > 0` (and `x + y - 1 <= high(T)` if T is SomeUnsignedInt). - ## - ## This is different from the `system.div `_ - ## operator, which works like `trunc(x / y)`. - ## That is, `div` rounds towards `0` and `ceilDiv` rounds up. - ## - ## This function has the above input limitation, because that allows the - ## compiler to generate faster code and it is rarely used with - ## negative values or unsigned integers close to `high(T)/2`. - ## If you need a `ceilDiv` that works with any input, see: - ## https://github.com/demotomohiro/divmath. - ## - ## **See also:** - ## * `system.div proc `_ for integer division - ## * `floorDiv func <#floorDiv,T,T>`_ for integer division which rounds down. - runnableExamples: - assert ceilDiv(12, 3) == 4 - assert ceilDiv(13, 3) == 5 - - when sizeof(T) == 8: - type UT = uint64 - elif sizeof(T) == 4: - type UT = uint32 - elif sizeof(T) == 2: - type UT = uint16 - elif sizeof(T) == 1: - type UT = uint8 - else: - {.fatal: "Unsupported int type".} - - assert x >= 0 and y > 0 - when T is SomeUnsignedInt: - assert x + y - 1 >= x - - # If the divisor is const, the backend C/C++ compiler generates code without a `div` - # instruction, as it is slow on most CPUs. - # If the divisor is a power of 2 and a const unsigned integer type, the - # compiler generates faster code. - # If the divisor is const and a signed integer, generated code becomes slower - # than the code with unsigned integers, because division with signed integers - # need to works for both positive and negative value without `idiv`/`sdiv`. - # That is why this code convert parameters to unsigned. - # This post contains a comparison of the performance of signed/unsigned integers: - # https://github.com/nim-lang/Nim/pull/18596#issuecomment-894420984. - # If signed integer arguments were not converted to unsigned integers, - # `ceilDiv` wouldn't work for any positive signed integer value, because - # `x + (y - 1)` can overflow. - ((x.UT + (y.UT - 1.UT)) div y.UT).T - -func frexp*[T: float32|float64](x: T): tuple[frac: T, exp: int] {.inline.} = - ## Splits `x` into a normalized fraction `frac` and an integral power of 2 `exp`, - ## such that `abs(frac) in 0.5..<1` and `x == frac * 2 ^ exp`, except for special - ## cases shown below. - runnableExamples: - doAssert frexp(8.0) == (0.5, 4) - doAssert frexp(-8.0) == (-0.5, 4) - doAssert frexp(0.0) == (0.0, 0) - - # special cases: - when sizeof(int) == 8: - doAssert frexp(-0.0).frac.signbit # signbit preserved for +-0 - doAssert frexp(Inf).frac == Inf # +- Inf preserved - doAssert frexp(NaN).frac.isNaN - - when not defined(js): - var exp: cint - result.frac = c_frexp2(x, exp) - result.exp = exp - else: - if x == 0.0: - # reuse signbit implementation - let uintBuffer = toBitsImpl(x) - if (uintBuffer[1] shr 31) != 0: - # x is -0.0 - result = (-0.0, 0) - else: - result = (0.0, 0) - elif x < 0.0: - result = frexp(-x) - result.frac = -result.frac - else: - var ex = trunc(log2(x)) - result.exp = int(ex) - result.frac = x / pow(2.0, ex) - if abs(result.frac) >= 1: - inc(result.exp) - result.frac = result.frac / 2 - if result.exp == 1024 and result.frac == 0.0: - result.frac = 0.99999999999999988898 - -func frexp*[T: float32|float64](x: T, exponent: var int): T {.inline.} = - ## Overload of `frexp` that calls `(result, exponent) = frexp(x)`. - runnableExamples: - var x: int - doAssert frexp(5.0, x) == 0.625 - doAssert x == 3 - - (result, exponent) = frexp(x) - - when not defined(js): + func c_frexp*(x: float32, exponent: var int32): float32 {. + importc: "frexp", header: "".} + func c_frexp*(x: float64, exponent: var int32): float64 {. + importc: "frexp", header: "".} + func frexp*[T, U](x: T, exponent: var U): T = + ## Split a number into mantissa and exponent. + ## + ## ``frexp`` calculates the mantissa m (a float greater than or equal to 0.5 + ## and less than 1) and the integer value n such that ``x`` (the original + ## float value) equals ``m * 2**n``. frexp stores n in `exponent` and returns + ## m. + ## + runnableExamples: + var x: int + doAssert frexp(5.0, x) == 0.625 + doAssert x == 3 + var exp: int32 + result = c_frexp(x, exp) + exponent = exp + when windowsCC89: # taken from Go-lang Math.Log2 const ln2 = 0.693147180559945309417232121458176568075500134360255254120680009 template log2Impl[T](x: T): T = - var exp: int + var exp: int32 var frac = frexp(x, exp) # Make sure exact powers of two give an exact answer. # Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1. if frac == 0.5: return T(exp - 1) - log10(frac) * (1 / ln2) + T(exp) + log10(frac)*(1/ln2) + T(exp) func log2*(x: float32): float32 = log2Impl(x) func log2*(x: float64): float64 = log2Impl(x) @@ -1061,31 +994,50 @@ when not defined(js): else: func log2*(x: float32): float32 {.importc: "log2f", header: "".} - func log2*(x: float64): float64 {.importc: "log2", header: "".} = - ## Computes the binary logarithm (base 2) of `x`. + func log2*(x: float64): float64 {.importc: "log2", header: "".} + ## Computes the binary logarithm (base 2) of ``x``. ## - ## **See also:** + ## See also: ## * `log func <#log,T,T>`_ ## * `log10 func <#log10,float64>`_ ## * `ln func <#ln,float64>`_ - runnableExamples: - doAssert almostEqual(log2(8.0), 3.0) - doAssert almostEqual(log2(1.0), 0.0) - doAssert almostEqual(log2(0.0), -Inf) - doAssert log2(-2.0).isNaN + ## * `exp func <#exp,float64>`_ + ## + ## .. code-block:: Nim + ## echo log2(8.0) # 3.0 + ## echo log2(1.0) # 0.0 + ## echo log2(0.0) # -inf + ## echo log2(-2.0) # nan + +else: + func frexp*[T: float32|float64](x: T, exponent: var int): T = + if x == 0.0: + exponent = 0 + result = 0.0 + elif x < 0.0: + result = -frexp(-x, exponent) + else: + var ex = trunc(log2(x)) + exponent = int(ex) + result = x / pow(2.0, ex) + if abs(result) >= 1: + inc(exponent) + result = result / 2 + if exponent == 1024 and result == 0.0: + result = 0.99999999999999988898 func splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] = - ## Breaks `x` into an integer and a fractional part. + ## Breaks ``x`` into an integer and a fractional part. ## - ## Returns a tuple containing `intpart` and `floatpart`, representing - ## the integer part and the fractional part, respectively. + ## Returns a tuple containing ``intpart`` and ``floatpart`` representing + ## the integer part and the fractional part respectively. ## - ## Both parts have the same sign as `x`. Analogous to the `modf` + ## Both parts have the same sign as ``x``. Analogous to the ``modf`` ## function in C. + ## runnableExamples: doAssert splitDecimal(5.25) == (intpart: 5.0, floatpart: 0.25) doAssert splitDecimal(-2.73) == (intpart: -2.0, floatpart: -0.73) - var absolute: T absolute = abs(x) @@ -1097,57 +1049,60 @@ func splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] = func degToRad*[T: float32|float64](d: T): T {.inline.} = - ## Converts from degrees to radians. + ## Convert from degrees to radians. ## - ## **See also:** + ## See also: ## * `radToDeg func <#radToDeg,T>`_ + ## runnableExamples: - doAssert almostEqual(degToRad(180.0), PI) - - result = d * T(RadPerDeg) + doAssert degToRad(180.0) == 3.141592653589793 + result = T(d) * RadPerDeg func radToDeg*[T: float32|float64](d: T): T {.inline.} = - ## Converts from radians to degrees. + ## Convert from radians to degrees. ## - ## **See also:** + ## See also: ## * `degToRad func <#degToRad,T>`_ + ## runnableExamples: - doAssert almostEqual(radToDeg(2 * PI), 360.0) - - result = d / T(RadPerDeg) + doAssert radToDeg(2 * PI) == 360.0 + result = T(d) / RadPerDeg func sgn*[T: SomeNumber](x: T): int {.inline.} = ## Sign function. ## ## Returns: - ## * `-1` for negative numbers and `NegInf`, - ## * `1` for positive numbers and `Inf`, - ## * `0` for positive zero, negative zero and `NaN` + ## * `-1` for negative numbers and ``NegInf``, + ## * `1` for positive numbers and ``Inf``, + ## * `0` for positive zero, negative zero and ``NaN`` + ## runnableExamples: doAssert sgn(5) == 1 doAssert sgn(0) == 0 doAssert sgn(-4.1) == -1 - ord(T(0) < x) - ord(x < T(0)) {.pop.} {.pop.} func `^`*[T: SomeNumber](x: T, y: Natural): T = - ## Computes `x` to the power of `y`. + ## Computes ``x`` to the power ``y``. ## - ## The exponent `y` must be non-negative, use - ## `pow <#pow,float64,float64>`_ for negative exponents. + ## Exponent ``y`` must be non-negative, use + ## `pow func <#pow,float64,float64>`_ for negative exponents. ## - ## **See also:** + ## See also: ## * `pow func <#pow,float64,float64>`_ for negative exponent or ## floats ## * `sqrt func <#sqrt,float64>`_ ## * `cbrt func <#cbrt,float64>`_ + ## runnableExamples: - doAssert -3 ^ 0 == 1 - doAssert -3 ^ 1 == -3 - doAssert -3 ^ 2 == 9 + assert -3.0^0 == 1.0 + assert -3^1 == -3 + assert -3^2 == 9 + assert -3.0^3 == -27.0 + assert -3.0^4 == 81.0 case y of 0: result = 1 @@ -1166,18 +1121,17 @@ func `^`*[T: SomeNumber](x: T, y: Natural): T = x *= x func gcd*[T](x, y: T): T = - ## Computes the greatest common (positive) divisor of `x` and `y`. + ## Computes the greatest common (positive) divisor of ``x`` and ``y``. ## ## Note that for floats, the result cannot always be interpreted as - ## "greatest decimal `z` such that `z*N == x and z*M == y` - ## where N and M are positive integers". + ## "greatest decimal `z` such that ``z*N == x and z*M == y`` + ## where N and M are positive integers." ## - ## **See also:** - ## * `gcd func <#gcd,SomeInteger,SomeInteger>`_ for an integer version + ## See also: + ## * `gcd func <#gcd,SomeInteger,SomeInteger>`_ for integer version ## * `lcm func <#lcm,T,T>`_ runnableExamples: doAssert gcd(13.5, 9.0) == 4.5 - var (x, y) = (x, y) while y != 0: x = x mod y @@ -1185,16 +1139,15 @@ func gcd*[T](x, y: T): T = abs x func gcd*(x, y: SomeInteger): SomeInteger = - ## Computes the greatest common (positive) divisor of `x` and `y`, - ## using the binary GCD (aka Stein's) algorithm. + ## Computes the greatest common (positive) divisor of ``x`` and ``y``, + ## using binary GCD (aka Stein's) algorithm. ## - ## **See also:** - ## * `gcd func <#gcd,T,T>`_ for a float version + ## See also: + ## * `gcd func <#gcd,T,T>`_ for floats version ## * `lcm func <#lcm,T,T>`_ runnableExamples: doAssert gcd(12, 8) == 4 doAssert gcd(17, 63) == 1 - when x is SomeSignedInt: var x = abs(x) else: @@ -1219,48 +1172,38 @@ func gcd*(x, y: SomeInteger): SomeInteger = y shl shift func gcd*[T](x: openArray[T]): T {.since: (1, 1).} = - ## Computes the greatest common (positive) divisor of the elements of `x`. + ## Computes the greatest common (positive) divisor of the elements of ``x``. ## - ## **See also:** - ## * `gcd func <#gcd,T,T>`_ for a version with two arguments + ## See also: + ## * `gcd func <#gcd,T,T>`_ for integer version runnableExamples: doAssert gcd(@[13.5, 9.0]) == 4.5 - result = x[0] - for i in 1 ..< x.len: + var i = 1 + while i < x.len: result = gcd(result, x[i]) + inc(i) func lcm*[T](x, y: T): T = - ## Computes the least common multiple of `x` and `y`. + ## Computes the least common multiple of ``x`` and ``y``. ## - ## **See also:** + ## See also: ## * `gcd func <#gcd,T,T>`_ runnableExamples: doAssert lcm(24, 30) == 120 doAssert lcm(13, 39) == 39 - x div gcd(x, y) * y -func clamp*[T](val: T, bounds: Slice[T]): T {.since: (1, 5), inline.} = - ## Like `system.clamp`, but takes a slice, so you can easily clamp within a range. - runnableExamples: - assert clamp(10, 1 .. 5) == 5 - assert clamp(1, 1 .. 3) == 1 - type A = enum a0, a1, a2, a3, a4, a5 - assert a1.clamp(a2..a4) == a2 - assert clamp((3, 0), (1, 0) .. (2, 9)) == (2, 9) - doAssertRaises(AssertionDefect): discard clamp(1, 3..2) # invalid bounds - assert bounds.a <= bounds.b, $(bounds.a, bounds.b) - clamp(val, bounds.a, bounds.b) - func lcm*[T](x: openArray[T]): T {.since: (1, 1).} = - ## Computes the least common multiple of the elements of `x`. + ## Computes the least common multiple of the elements of ``x``. ## - ## **See also:** - ## * `lcm func <#lcm,T,T>`_ for a version with two arguments + ## See also: + ## * `gcd func <#gcd,T,T>`_ for integer version runnableExamples: doAssert lcm(@[24, 30]) == 120 - result = x[0] - for i in 1 ..< x.len: + var i = 1 + while i < x.len: result = lcm(result, x[i]) + inc(i) + diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index cd4d1e6b8..ba944ba81 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -7,14 +7,11 @@ # distribution, for details about the copyright. # -## Module for computing [MD5 checksums](https://en.wikipedia.org/wiki/MD5). +## Module for computing `MD5 checksums `_. ## -## This module also works at compile time and in JavaScript. -## -## See also -## ======== -## * `base64 module`_ for a Base64 encoder and decoder -## * `std/sha1 module `_ for the SHA-1 checksum algorithm +## **See also:** +## * `base64 module`_ implements a base64 encoder and decoder +## * `std/sha1 module `_ for a sha1 encoder and decoder ## * `hashes module`_ for efficient computations of hash values ## for diverse Nim types @@ -25,8 +22,8 @@ type MD5State = array[0..3, uint32] MD5Block = array[0..15, uint32] MD5CBits = array[0..7, uint8] - MD5Digest* = array[0..15, uint8] - ## MD5 checksum of a string, obtained with the `toMD5 proc <#toMD5,string>`_. + MD5Digest* = array[0..15, uint8] ## \ + ## MD5 checksum of a string, obtained with `toMD5 proc <#toMD5,string>`_. MD5Buffer = array[0..63, uint8] MD5Context* {.final.} = object state: MD5State @@ -34,16 +31,15 @@ type buffer: MD5Buffer const - padding: array[0..63, uint8] = [ - 0x80'u8, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - ] + padding: cstring = "\x80\0\0\0" & + "\0\0\0\0\0\0\0\0" & + "\0\0\0\0\0\0\0\0" & + "\0\0\0\0\0\0\0\0" & + "\0\0\0\0\0\0\0\0" & + "\0\0\0\0\0\0\0\0" & + "\0\0\0\0\0\0\0\0" & + "\0\0\0\0\0\0\0\0" & + "\0\0\0\0" proc F(x, y, z: uint32): uint32 {.inline.} = result = (x and y) or ((not x) and z) @@ -80,7 +76,7 @@ proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = rot(a, s) a = a + b -proc encode(dest: var MD5Block, src: openArray[uint8]) = +proc encode(dest: var MD5Block, src: cstring) = var j = 0 for i in 0..high(dest): dest[i] = uint32(ord(src[j])) or @@ -98,48 +94,10 @@ proc decode(dest: var openArray[uint8], src: openArray[uint32]) = dest[i+3] = uint8(src[j] shr 24 and 0xff'u32) inc(i, 4) -template slice(s: string, a, b): openArray[uint8] = - when nimvm: - # toOpenArray is not implemented in VM - var s2 = newSeq[uint8](s.len) - for i in 0 ..< s2.len: - s2[i] = uint8(s[i]) - s2 - else: - s.toOpenArrayByte(a, b) - -template slice(s: cstring, a, b): openArray[uint8] = - when nimvm: - # toOpenArray is not implemented in VM - slice($s, a, b) - else: - when defined(js): - # toOpenArrayByte for cstring is not implemented in JS - slice($s, a, b) - else: - s.toOpenArrayByte(a, b) - -template slice(s: openArray[uint8], a, b): openArray[uint8] = - when nimvm: - s[a .. b] - else: - s.toOpenArray(a, b) - -const useMem = declared(copyMem) - -template memOrNot(withMem, withoutMem): untyped = - when nimvm: - withoutMem - else: - when useMem: - withMem - else: - withoutMem - -proc transform(buffer: openArray[uint8], state: var MD5State) = +proc transform(buffer: pointer, state: var MD5State) = var myBlock: MD5Block - encode(myBlock, buffer) + encode(myBlock, cast[cstring](buffer)) var a = state[0] var b = state[1] var c = state[2] @@ -214,23 +172,15 @@ proc transform(buffer: openArray[uint8], state: var MD5State) = state[3] = state[3] + d proc md5Init*(c: var MD5Context) {.raises: [], tags: [], gcsafe.} -proc md5Update*(c: var MD5Context, input: openArray[uint8]) {.raises: [], +proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [], tags: [], gcsafe.} proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [], gcsafe.} -proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [], - tags: [], gcsafe.} = - ## Updates the `MD5Context` with the `input` data of length `len`. - ## - ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this - ## function explicitly. - md5Update(c, input.slice(0, len - 1)) - proc toMD5*(s: string): MD5Digest = ## Computes the `MD5Digest` value for a string `s`. ## - ## **See also:** + ## See also: ## * `getMD5 proc <#getMD5,string>`_ which returns a string representation ## of the `MD5Digest` ## * `$ proc <#$,MD5Digest>`_ for converting MD5Digest to string @@ -239,7 +189,7 @@ proc toMD5*(s: string): MD5Digest = var c: MD5Context md5Init(c) - md5Update(c, s.slice(0, s.len - 1)) + md5Update(c, cstring(s), len(s)) md5Final(c, result) proc `$`*(d: MD5Digest): string = @@ -252,8 +202,10 @@ proc `$`*(d: MD5Digest): string = proc getMD5*(s: string): string = ## Computes an MD5 value of `s` and returns its string representation. + ## .. note:: + ## available at compile time ## - ## **See also:** + ## See also: ## * `toMD5 proc <#toMD5,string>`_ which returns the `MD5Digest` of a string runnableExamples: assert getMD5("abc") == "900150983cd24fb0d6963f7d28e17f72" @@ -262,7 +214,7 @@ proc getMD5*(s: string): string = c: MD5Context d: MD5Digest md5Init(c) - md5Update(c, s.slice(0, s.len - 1)) + md5Update(c, cstring(s), len(s)) md5Final(c, d) result = $d @@ -273,16 +225,10 @@ proc `==`*(D1, D2: MD5Digest): bool = return true -proc clearBuffer(c: var MD5Context) {.inline.} = - memOrNot: - zeroMem(addr(c.buffer), sizeof(MD5Buffer)) - do: - reset(c.buffer) - proc md5Init*(c: var MD5Context) = - ## Initializes an `MD5Context`. + ## Initializes a `MD5Context`. ## - ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this + ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this ## function explicitly. c.state[0] = 0x67452301'u32 c.state[1] = 0xEFCDAB89'u32 @@ -290,44 +236,34 @@ proc md5Init*(c: var MD5Context) = c.state[3] = 0x10325476'u32 c.count[0] = 0'u32 c.count[1] = 0'u32 - clearBuffer(c) + zeroMem(addr(c.buffer), sizeof(MD5Buffer)) -proc writeBuffer(c: var MD5Context, index: int, - input: openArray[uint8], inputIndex, len: int) {.inline.} = - memOrNot: - copyMem(addr(c.buffer[index]), unsafeAddr(input[inputIndex]), len) - do: - # cannot use system.`[]=` for arrays and openarrays as - # it can raise RangeDefect which gets tracked - for i in 0..`_, there's no need to call this + ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this ## function explicitly. + var input = input var Index = int((c.count[0] shr 3) and 0x3F) - c.count[0] = c.count[0] + (uint32(input.len) shl 3) - if c.count[0] < (uint32(input.len) shl 3): c.count[1] = c.count[1] + 1'u32 - c.count[1] = c.count[1] + (uint32(input.len) shr 29) + c.count[0] = c.count[0] + (uint32(len) shl 3) + if c.count[0] < (uint32(len) shl 3): c.count[1] = c.count[1] + 1'u32 + c.count[1] = c.count[1] + (uint32(len) shr 29) var PartLen = 64 - Index - if input.len >= PartLen: - writeBuffer(c, Index, input, 0, PartLen) - transform(c.buffer, c.state) + if len >= PartLen: + copyMem(addr(c.buffer[Index]), input, PartLen) + transform(addr(c.buffer), c.state) var i = PartLen - while i + 63 < input.len: - transform(input.slice(i, i + 63), c.state) + while i + 63 < len: + transform(addr(input[i]), c.state) inc(i, 64) - if i < input.len: - writeBuffer(c, 0, input, i, input.len - i) - elif input.len > 0: - writeBuffer(c, Index, input, 0, input.len) + copyMem(addr(c.buffer[0]), addr(input[i]), len-i) + else: + copyMem(addr(c.buffer[Index]), addr(input[0]), len) proc md5Final*(c: var MD5Context, digest: var MD5Digest) = ## Finishes the `MD5Context` and stores the result in `digest`. ## - ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this + ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this ## function explicitly. var Bits: MD5CBits @@ -336,10 +272,10 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) = var Index = int((c.count[0] shr 3) and 0x3F) if Index < 56: PadLen = 56 - Index else: PadLen = 120 - Index - md5Update(c, padding.slice(0, PadLen - 1)) - md5Update(c, Bits) + md5Update(c, padding, PadLen) + md5Update(c, cast[cstring](addr(Bits)), 8) decode(digest, c.state) - clearBuffer(c) + zeroMem(addr(c), sizeof(MD5Context)) when defined(nimHasStyleChecks): diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 74065bc2e..5e6571e11 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -17,49 +17,17 @@ when defined(windows): import winlean - when defined(nimPreviewSlimSystem): - import std/widestrs elif defined(posix): import posix else: {.error: "the memfiles module is not supported on your operating system!".} -import streams -import std/oserrors - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - +import os, streams proc newEIO(msg: string): ref IOError = new(result) result.msg = msg -proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode = - ## Set the size of open file pointed to by `fh` to `newFileSize` if != -1. - ## Space is only allocated if that is cheaper than writing to the file. This - ## routine returns the last OSErrorCode found rather than raising to support - ## old rollback/clean-up code style. [ Should maybe move to std/osfiles. ] - if newFileSize == -1: - return - when defined(windows): - var sizeHigh = int32(newFileSize shr 32) - let sizeLow = int32(newFileSize and 0xffffffff) - let status = setFilePointer(fh, sizeLow, addr(sizeHigh), FILE_BEGIN) - let lastErr = osLastError() - if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or - setEndOfFile(fh) == 0: - result = lastErr - else: - var e: cint # posix_fallocate truncates up when needed. - when declared(posix_fallocate): - while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR): - discard - if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1: - result = osLastError() # fallback arguable; Most portable, but allows SEGV - elif e != 0: - result = osLastError() - type MemFile* = object ## represents a memory mapped file mem*: pointer ## a pointer to the memory mapped file. The pointer @@ -80,8 +48,8 @@ proc mapMem*(m: var MemFile, mode: FileMode = fmRead, mappedSize = -1, offset = 0, mapFlags = cint(-1)): pointer = ## returns a pointer to a mapped portion of MemFile `m` ## - ## `mappedSize` of `-1` maps to the whole file, and - ## `offset` must be multiples of the PAGE SIZE of your OS + ## ``mappedSize`` of ``-1`` maps to the whole file, and + ## ``offset`` must be multiples of the PAGE SIZE of your OS if mode == fmAppend: raise newEIO("The append mode is not supported.") @@ -115,12 +83,12 @@ proc mapMem*(m: var MemFile, mode: FileMode = fmRead, proc unmapMem*(f: var MemFile, p: pointer, size: int) = - ## unmaps the memory region `(p, allocated. - if (let e = setFileSize(f.fHandle.FileHandle, newFileSize); - e != 0.OSErrorCode): raiseOSError(e) - f.mapHandle = createFileMappingW(f.fHandle, nil, PAGE_READWRITE, 0,0,nil) - if f.mapHandle == 0: # Re-do map - raiseOSError(osLastError()) - if (let m = mapViewOfFileEx(f.mapHandle, FILE_MAP_READ or FILE_MAP_WRITE, - 0, 0, WinSizeT(newFileSize), nil); m != nil): - f.mem = m +when defined(posix) or defined(nimdoc): + proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} = + ## resize and re-map the file underlying an ``allowRemap MemFile``. + ## **Note**: this assumes the entire file is mapped read-write at offset zero. + ## Also, the value of ``.mem`` will probably change. + ## **Note**: This is not (yet) available on Windows. + when defined(posix): + if f.handle == -1: + raise newException(IOError, + "Cannot resize MemFile opened with allowRemap=false") + if ftruncate(f.handle, newFileSize) == -1: + 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): + pointer {.importc: "mremap", header: "".} + let newAddr = mremap(f.mem, csize(f.size), csize(newFileSize), cint(1)) + if newAddr == cast[pointer](MAP_FAILED): + raiseOSError(osLastError()) + else: + if munmap(f.mem, f.size) != 0: + raiseOSError(osLastError()) + let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE, + f.flags, f.handle, 0) + if newAddr == cast[pointer](MAP_FAILED): + raiseOSError(osLastError()) + f.mem = newAddr f.size = newFileSize - else: - raiseOSError(osLastError()) - elif defined(posix): - if f.handle == -1: - raise newException(IOError, - "Cannot resize MemFile opened with allowRemap=false") - if newFileSize != f.size: - if (let e = setFileSize(f.handle.FileHandle, newFileSize); - e != 0.OSErrorCode): raiseOSError(e) - 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_t; flags: cint): - pointer {.importc: "mremap", header: "".} - let newAddr = mremap(f.mem, csize_t(f.size), csize_t(newFileSize), 1.cint) - if newAddr == cast[pointer](MAP_FAILED): - raiseOSError(osLastError()) - else: - if munmap(f.mem, f.size) != 0: - raiseOSError(osLastError()) - let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE, - f.flags, f.handle, 0) - if newAddr == cast[pointer](MAP_FAILED): - raiseOSError(osLastError()) - f.mem = newAddr - f.size = newFileSize proc close*(f: var MemFile) = ## closes the memory mapped file `f`. All changes are written back to the @@ -420,7 +377,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. @@ -451,7 +408,7 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline ## inc(count) ## echo count - proc c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. + proc c_memchr(cstr: pointer, c: char, n: csize): pointer {. importc: "memchr", header: "".} proc `-!`(p, q: pointer): int {.inline.} = return cast[int](p) -% cast[int](q) var ms: MemSlice @@ -459,7 +416,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, csize_t(remaining)) + ending = c_memchr(ms.data, delim, remaining) if ending == nil: # unterminated final slice ms.size = remaining # Weird case..check eat? yield ms @@ -471,29 +428,29 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline ms.data = cast[pointer](cast[int](ending) +% 1) # skip delim remaining = mfile.size - (ms.data -! mfile.mem) -iterator lines*(mfile: MemFile, buf: var string, delim = '\l', - eat = '\r'): string {.inline.} = +iterator lines*(mfile: MemFile, buf: var TaintedString, delim = '\l', + eat = '\r'): TaintedString {.inline.} = ## Replace contents of passed buffer with each new line, like - ## `readLine(File) `_. + ## `readLine(File) `_. ## `delim`, `eat`, and delimiting logic is exactly as for `memSlices ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned. ## ## Example: ## ## .. code-block:: nim - ## var buffer: string = "" + ## var buffer: TaintedString = "" ## for line in lines(memfiles.open("foo"), buffer): ## echo line for ms in memSlices(mfile, delim, eat): - setLen(buf, ms.size) + setLen(buf.string, ms.size) if ms.size > 0: - copyMem(addr buf[0], ms.data, ms.size) + copyMem(addr string(buf)[0], ms.data, ms.size) yield buf -iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): string {.inline.} = +iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): TaintedString {.inline.} = ## Return each line in a file as a Nim string, like - ## `lines(File) `_. + ## `lines(File) `_. ## `delim`, `eat`, and delimiting logic is exactly as for `memSlices ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned. ## @@ -503,7 +460,7 @@ iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): string {.inline.} = ## for line in lines(memfiles.open("foo")): ## echo line - var buf = newStringOfCap(80) + var buf = TaintedString(newStringOfCap(80)) for line in lines(mfile, buf, delim, eat): yield buf @@ -531,8 +488,8 @@ proc mmsSetPosition(s: Stream, pos: int) = proc mmsGetPosition(s: Stream): int = MemMapFileStream(s).pos proc mmsPeekData(s: Stream, buffer: pointer, bufLen: int): int = - let startAddress = cast[int](MemMapFileStream(s).mf.mem) - let p = cast[int](MemMapFileStream(s).pos) + let startAddress = cast[ByteAddress](MemMapFileStream(s).mf.mem) + let p = cast[ByteAddress](MemMapFileStream(s).pos) let l = min(bufLen, MemMapFileStream(s).mf.size - p) moveMem(buffer, cast[pointer](startAddress + p), l) result = l @@ -547,8 +504,8 @@ proc mmsWriteData(s: Stream, buffer: pointer, bufLen: int) = let size = MemMapFileStream(s).mf.size if MemMapFileStream(s).pos + bufLen > size: raise newEIO("cannot write to stream") - let p = cast[int](MemMapFileStream(s).mf.mem) + - cast[int](MemMapFileStream(s).pos) + let p = cast[ByteAddress](MemMapFileStream(s).mf.mem) + + cast[ByteAddress](MemMapFileStream(s).pos) moveMem(cast[pointer](p), buffer, bufLen) inc(MemMapFileStream(s).pos, bufLen) @@ -557,7 +514,7 @@ proc newMemMapFileStream*(filename: string, mode: FileMode = fmRead, ## creates a new stream from the file named `filename` with the mode `mode`. ## Raises ## `OSError` if the file cannot be opened. See the `system ## `_ module for a list of available FileMode enums. - ## `fileSize` can only be set if the file does not exist and is opened + ## ``fileSize`` can only be set if the file does not exist and is opened ## with write access (e.g., with fmReadWrite). var mf: MemFile = open(filename, mode, newFileSize = fileSize) new(result) diff --git a/lib/deprecated/pure/mersenne.nim b/lib/pure/mersenne.nim similarity index 67% rename from lib/deprecated/pure/mersenne.nim rename to lib/pure/mersenne.nim index 37c5085b1..812893508 100644 --- a/lib/deprecated/pure/mersenne.nim +++ b/lib/pure/mersenne.nim @@ -2,28 +2,17 @@ # # Nim's Runtime Library # (c) Copyright 2015 Nim Contributors +# # See the file "copying.txt", included in this # distribution, for details about the copyright. -## The [Mersenne Twister](https://en.wikipedia.org/wiki/Mersenne_Twister) -## random number generator. -## .. note:: The procs in this module work at compile-time. +# -{.deprecated: "use `std/random` instead".} - -runnableExamples: - var rand = newMersenneTwister(uint32.high) ## must be "var" - doAssert rand.getNum() != rand.getNum() ## pseudorandom number -## See also -## ======== -## * `random module`_ for Nim's standard random number generator type MersenneTwister* = object - ## The Mersenne Twister. mt: array[0..623, uint32] index: int proc newMersenneTwister*(seed: uint32): MersenneTwister = - ## Creates a new `MersenneTwister` with seed `seed`. result.index = 0 result.mt[0] = seed for i in 1'u32 .. 623'u32: @@ -31,7 +20,6 @@ proc newMersenneTwister*(seed: uint32): MersenneTwister = (result.mt[i-1] shr 30'u32)) + i) proc generateNumbers(m: var MersenneTwister) = - for i in 0..623: var y = (m.mt[i] and 0x80000000'u32) + (m.mt[(i+1) mod 624] and 0x7fffffff'u32) @@ -40,12 +28,28 @@ proc generateNumbers(m: var MersenneTwister) = m.mt[i] = m.mt[i] xor 0x9908b0df'u32 proc getNum*(m: var MersenneTwister): uint32 = - ## Returns the next pseudorandom `uint32`. + ## Returns the next pseudo random number ranging from 0 to high(uint32) if m.index == 0: generateNumbers(m) result = m.mt[m.index] m.index = (m.index + 1) mod m.mt.len + result = result xor (result shr 11'u32) result = result xor ((result shl 7'u32) and 0x9d2c5680'u32) result = result xor ((result shl 15'u32) and 0xefc60000'u32) result = result xor (result shr 18'u32) + + +runnableExamples: + static: + block: + var rando: MersenneTwister = newMersenneTwister(uint32.high) ## Must be "var". + doAssert rando.getNum() != rando.getNum() ## Pseudo random number. Works at compile-time. + + +# Test +when not defined(testing) and isMainModule: + var mt = newMersenneTwister(2525) + + for i in 0..99: + echo mt.getNum diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index 346fb39ee..880ccdff1 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -8,34 +8,12 @@ # ## This module implements a mimetypes database - -runnableExamples: - var m = newMimetypes() - doAssert m.getMimetype("mp4") == "video/mp4" - doAssert m.getExt("text/html") == "html" - ## Values can be uppercase too. - doAssert m.getMimetype("MP4") == "video/mp4" - doAssert m.getExt("TEXT/HTML") == "html" - ## If values are invalid then `default` is returned. - doAssert m.getMimetype("INVALID") == "text/plain" - doAssert m.getExt("INVALID/NONEXISTENT") == "txt" - doAssert m.getMimetype("") == "text/plain" - doAssert m.getExt("") == "txt" - ## Register new Mimetypes. - m.register(ext = "fakext", mimetype = "text/fakelang") - doAssert m.getMimetype("fakext") == "text/fakelang" - doAssert m.getMimetype("FaKeXT") == "text/fakelang" - -import tables +import strtabs, std/private/since from strutils import startsWith, toLowerAscii, strip -when defined(nimPreviewSlimSystem): - import std/assertions - - type MimeDB* = object - mimes: OrderedTable[string, string] + mimes: StringTableRef const mimes* = { "123": "application/vnd.lotus-1-2-3", @@ -1907,13 +1885,12 @@ const mimes* = { func newMimetypes*(): MimeDB = ## Creates a new Mimetypes database. The database will contain the most ## common mimetypes. - {.cast(noSideEffect).}: - result.mimes = mimes.toOrderedTable() + result.mimes = mimes.newStringTable() func getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string = - ## Gets mimetype which corresponds to `ext`. Returns `default` if `ext` - ## could not be found. `ext` can start with an optional dot which is ignored. - ## `ext` is lowercased before querying `mimedb`. + ## Gets mimetype which corresponds to ``ext``. Returns ``default`` if ``ext`` + ## could not be found. ``ext`` can start with an optional dot which is ignored. + ## ``ext`` is lowercased before querying ``mimedb``. if ext.startsWith("."): result = mimedb.mimes.getOrDefault(ext.toLowerAscii.substr(1)) else: @@ -1922,9 +1899,9 @@ func getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string = return default func getExt*(mimedb: MimeDB, mimetype: string, default = "txt"): string = - ## Gets extension which corresponds to `mimetype`. Returns `default` if - ## `mimetype` could not be found. Extensions are returned without the - ## leading dot. `mimetype` is lowercased before querying `mimedb`. + ## Gets extension which corresponds to ``mimetype``. Returns ``default`` if + ## ``mimetype`` could not be found. Extensions are returned without the + ## leading dot. ``mimetype`` is lowercased before querying ``mimedb``. result = default let mimeLowered = mimetype.toLowerAscii() for e, m in mimedb.mimes: @@ -1933,9 +1910,55 @@ func getExt*(mimedb: MimeDB, mimetype: string, default = "txt"): string = break func register*(mimedb: var MimeDB, ext: string, mimetype: string) = - ## Adds `mimetype` to the `mimedb`. - ## `mimetype` and `ext` are lowercased before registering on `mimedb`. + ## Adds ``mimetype`` to the ``mimedb``. + ## ``mimetype`` and ``ext`` are lowercased before registering on ``mimedb``. assert ext.strip.len > 0, "ext argument can not be empty string" assert mimetype.strip.len > 0, "mimetype argument can not be empty string" {.noSideEffect.}: mimedb.mimes[ext.toLowerAscii()] = mimetype.toLowerAscii() + + +since (1, 5): + func mimesLongest(): array[2, int] {.compiletime.} = + runnableExamples: + static: + doAssert mimesLongest() >= (ext: 24, mime: 73) + var currentKeyLength, currentValLength: int + for item in mimes: + currentKeyLength = item[0].len + currentValLength = item[1].len + if currentKeyLength > result[0]: result[0] = currentKeyLength + if currentValLength > result[1]: result[1] = currentValLength + + const + ctValue = mimesLongest() # Use 2 const instead of func, save tuple unpack. + mimesExtMaxLen*: int = ctValue[0] ## \ + ## The length of the longest "ext" from `mimes`, + ## this is useful for optimizations with `newStringOfCap` and `newString`. + mimesMaxLen*: int = ctValue[1] ## \ + ## The length of the longest "mime" from `mimes`, + ## this is useful for optimizations with `newStringOfCap` and `newString`. + ## + ## See also: + ## * `newStringOfCap `_ + ## * `newString `_ + + +runnableExamples: + static: + block: + var m = newMimetypes() + doAssert m.getMimetype("mp4") == "video/mp4" + doAssert m.getExt("text/html") == "html" + ## Values can be uppercase too. + doAssert m.getMimetype("MP4") == "video/mp4" + doAssert m.getExt("TEXT/HTML") == "html" + ## If values are invalid then ``default`` is returned. + doAssert m.getMimetype("INVALID") == "text/plain" + doAssert m.getExt("INVALID/NONEXISTENT") == "txt" + doAssert m.getMimetype("") == "text/plain" + doAssert m.getExt("") == "txt" + ## Register new Mimetypes. + m.register(ext = "fakext", mimetype = "text/fakelang") + doAssert m.getMimetype("fakext") == "text/fakelang" + doAssert m.getMimetype("FaKeXT") == "text/fakelang" diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index b3465ef14..f1445fca5 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -8,23 +8,18 @@ # ## This module implements a low-level cross-platform sockets interface. Look -## at the `net` module for the higher-level version. +## at the ``net`` module for the higher-level version. # TODO: Clean up the exports a bit and everything else in general. import os, options import std/private/since -import std/strbasics -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] when hostOS == "solaris": {.passl: "-lsocket -lnsl".} -const useWinVersion = defined(windows) or defined(nimdoc) -const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or - defined(nuttx) +const useWinVersion = defined(Windows) or defined(nimdoc) when useWinVersion: import winlean @@ -39,12 +34,9 @@ else: export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen, Sockaddr_in6, Sockaddr_storage, - recv, `==`, connect, send, accept, recvfrom, sendto, + inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto, freeAddrInfo -when not useNimNetLite: - export inet_ntoa - export SO_ERROR, SOL_SOCKET, @@ -120,19 +112,19 @@ else: nativeAfUnix = posix.AF_UNIX proc `==`*(a, b: Port): bool {.borrow.} - ## `==` for ports. + ## ``==`` for ports. proc `$`*(p: Port): string {.borrow.} ## Returns the port number as a string proc toInt*(domain: Domain): cint - ## Converts the Domain enum to a platform-dependent `cint`. + ## Converts the Domain enum to a platform-dependent ``cint``. proc toInt*(typ: SockType): cint - ## Converts the SockType enum to a platform-dependent `cint`. + ## Converts the SockType enum to a platform-dependent ``cint``. proc toInt*(p: Protocol): cint - ## Converts the Protocol enum to a platform-dependent `cint`. + ## Converts the Protocol enum to a platform-dependent ``cint``. when not useWinVersion: proc toInt(domain: Domain): cint = @@ -143,8 +135,8 @@ when not useWinVersion: of AF_INET6: result = posix.AF_INET6.cint proc toKnownDomain*(family: cint): Option[Domain] = - ## Converts the platform-dependent `cint` to the Domain or none(), - ## if the `cint` is not known. + ## Converts the platform-dependent ``cint`` to the Domain or none(), + ## if the ``cint`` is not known. result = if family == posix.AF_UNSPEC: some(Domain.AF_UNSPEC) elif family == posix.AF_UNIX: some(Domain.AF_UNIX) elif family == posix.AF_INET: some(Domain.AF_INET) @@ -170,11 +162,11 @@ when not useWinVersion: else: proc toInt(domain: Domain): cint = - result = cast[cint](uint32(ord(domain))) + result = toU32(ord(domain)).cint proc toKnownDomain*(family: cint): Option[Domain] = - ## Converts the platform-dependent `cint` to the Domain or none(), - ## if the `cint` is not known. + ## Converts the platform-dependent ``cint`` to the Domain or none(), + ## if the ``cint`` is not known. result = if family == winlean.AF_UNSPEC: some(Domain.AF_UNSPEC) elif family == winlean.AF_INET: some(Domain.AF_INET) elif family == winlean.AF_INET6: some(Domain.AF_INET6) @@ -210,14 +202,14 @@ proc toSockType*(protocol: Protocol): SockType = SOCK_RAW proc getProtoByName*(name: string): int {.since: (1, 3, 5).} = - ## Returns a protocol code from the database that matches the protocol `name`. + ## Returns a protocol code from the database that matches the protocol ``name``. when useWinVersion: let protoent = winlean.getprotobyname(name.cstring) else: let protoent = posix.getprotobyname(name.cstring) - + if protoent == nil: - raise newException(OSError, "protocol not found: " & name) + raise newException(OSError, "protocol not found") result = protoent.p_proto.int @@ -275,8 +267,8 @@ proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ ReadIOEffect].} = - ## Marks `socket` as accepting connections. - ## `Backlog` specifies the maximum length of the + ## Marks ``socket`` as accepting connections. + ## ``Backlog`` specifies the maximum length of the ## queue of pending connections. when useWinVersion: result = winlean.listen(socket, cint(backlog)) @@ -288,7 +280,7 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, protocol: Protocol = IPPROTO_TCP): ptr AddrInfo = ## ## - ## .. warning:: The resulting `ptr AddrInfo` must be freed using `freeAddrInfo`! + ## **Warning**: The resulting ``ptr AddrInfo`` must be freed using ``freeAddrInfo``! var hints: AddrInfo result = nil hints.ai_family = toInt(domain) @@ -303,9 +295,9 @@ 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.cstring, addr(hints), result) + var gaiResult = getaddrinfo(address, socketPort, addr(hints), result) if gaiResult != 0'i32: - when useWinVersion or defined(freertos) or defined(nuttx): + when useWinVersion or defined(freertos): raiseOSError(osLastError()) else: raiseOSError(osLastError(), $gai_strerror(gaiResult)) @@ -339,6 +331,125 @@ template htons*(x: uint16): untyped = ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. nativesockets.ntohs(x) +proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = + ## Searches the database from the beginning and finds the first entry for + ## which the service name specified by ``name`` matches the s_name member + ## and the protocol name specified by ``proto`` matches the s_proto member. + ## + ## On posix this will search through the ``/etc/services`` file. + when useWinVersion: + var s = winlean.getservbyname(name, proto) + else: + var s = posix.getservbyname(name, proto) + if s == nil: raiseOSError(osLastError(), "Service not found.") + result.name = $s.s_name + result.aliases = cstringArrayToSeq(s.s_aliases) + result.port = Port(s.s_port) + result.proto = $s.s_proto + +proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = + ## Searches the database from the beginning and finds the first entry for + ## which the port specified by ``port`` matches the s_port member and the + ## protocol name specified by ``proto`` matches the s_proto member. + ## + ## On posix this will search through the ``/etc/services`` file. + when useWinVersion: + var s = winlean.getservbyport(ze(int16(port)).cint, proto) + else: + var s = posix.getservbyport(ze(int16(port)).cint, proto) + if s == nil: raiseOSError(osLastError(), "Service not found.") + result.name = $s.s_name + result.aliases = cstringArrayToSeq(s.s_aliases) + result.port = Port(s.s_port) + result.proto = $s.s_proto + +proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = + ## This function will lookup the hostname of an IP Address. + var myaddr: InAddr + myaddr.s_addr = inet_addr(ip) + + when useWinVersion: + var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, + cint(AF_INET)) + if s == nil: raiseOSError(osLastError()) + else: + var s = + when defined(android4): + posix.gethostbyaddr(cast[cstring](addr(myaddr)), sizeof(myaddr).cint, + cint(posix.AF_INET)) + else: + posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).SockLen, + cint(posix.AF_INET)) + if s == nil: + raiseOSError(osLastError(), $hstrerror(h_errno)) + + result.name = $s.h_name + result.aliases = cstringArrayToSeq(s.h_aliases) + when useWinVersion: + result.addrtype = Domain(s.h_addrtype) + else: + if s.h_addrtype == posix.AF_INET: + result.addrtype = AF_INET + elif s.h_addrtype == posix.AF_INET6: + result.addrtype = AF_INET6 + else: + raiseOSError(osLastError(), "unknown h_addrtype") + if result.addrtype == AF_INET: + result.addrList = @[] + var i = 0 + while not isNil(s.h_addr_list[i]): + var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i]) + result.addrList.add($inet_ntoa(inaddrPtr[])) + inc(i) + else: + result.addrList = cstringArrayToSeq(s.h_addr_list) + result.length = int(s.h_length) + +proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = + ## This function will lookup the IP address of a hostname. + when useWinVersion: + var s = winlean.gethostbyname(name) + else: + var s = posix.gethostbyname(name) + if s == nil: raiseOSError(osLastError()) + result.name = $s.h_name + result.aliases = cstringArrayToSeq(s.h_aliases) + when useWinVersion: + result.addrtype = Domain(s.h_addrtype) + else: + if s.h_addrtype == posix.AF_INET: + result.addrtype = AF_INET + elif s.h_addrtype == posix.AF_INET6: + result.addrtype = AF_INET6 + else: + raiseOSError(osLastError(), "unknown h_addrtype") + if result.addrtype == AF_INET: + result.addrList = @[] + var i = 0 + while not isNil(s.h_addr_list[i]): + var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i]) + result.addrList.add($inet_ntoa(inaddrPtr[])) + inc(i) + else: + result.addrList = cstringArrayToSeq(s.h_addr_list) + result.length = int(s.h_length) + +proc getHostname*(): string {.tags: [ReadIOEffect].} = + ## Returns the local hostname (not the FQDN) + # https://tools.ietf.org/html/rfc1035#section-2.3.1 + # https://tools.ietf.org/html/rfc2181#section-11 + const size = 64 + result = newString(size) + when useWinVersion: + let success = winlean.gethostname(result, size) + else: + # Posix + let success = posix.gethostname(result, size) + if success != 0.cint: + raiseOSError(osLastError()) + let x = len(cstring(result)) + result.setLen(x) + proc getSockDomain*(socket: SocketHandle): Domain = ## Returns the socket's domain (AF_INET or AF_INET6). var name: Sockaddr_in6 @@ -352,354 +463,161 @@ proc getSockDomain*(socket: SocketHandle): Domain = else: raise newException(IOError, "Unknown socket family in getSockDomain") -when not useNimNetLite: - proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = - ## Searches the database from the beginning and finds the first entry for - ## which the service name specified by `name` matches the s_name member - ## and the protocol name specified by `proto` matches the s_proto member. - ## - ## On posix this will search through the `/etc/services` file. - when useWinVersion: - var s = winlean.getservbyname(name, proto) +proc getAddrString*(sockAddr: ptr SockAddr): string = + ## Returns the string representation of address within sockAddr + if sockAddr.sa_family.cint == nativeAfInet: + result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr) + elif sockAddr.sa_family.cint == nativeAfInet6: + let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN + else: 46 # it's actually 46 in both cases + result = newString(addrLen) + let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr + when not useWinVersion: + if posix.inet_ntop(posix.AF_INET6, addr6, addr result[0], + result.len.int32) == nil: + raiseOSError(osLastError()) + if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0: + result = result.substr("::ffff:".len) else: - var s = posix.getservbyname(name, proto) - if s == nil: raiseOSError(osLastError(), "Service not found.") - result.name = $s.s_name - result.aliases = cstringArrayToSeq(s.s_aliases) - result.port = Port(s.s_port) - result.proto = $s.s_proto + if winlean.inet_ntop(winlean.AF_INET6, addr6, addr result[0], + result.len.int32) == nil: + raiseOSError(osLastError()) + setLen(result, len(cstring(result))) + else: + when defined(posix) and not defined(nimdoc): + if sockAddr.sa_family.cint == nativeAfUnix: + return "unix" + raise newException(IOError, "Unknown socket family in getAddrString") - proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = - ## Searches the database from the beginning and finds the first entry for - ## which the port specified by `port` matches the s_port member and the - ## protocol name specified by `proto` matches the s_proto member. - ## - ## On posix this will search through the `/etc/services` file. - when useWinVersion: - var s = winlean.getservbyport(uint16(port).cint, proto) +proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) = + ## Stores in ``strAddress`` the string representation of the address inside + ## ``sockAddr`` + ## + ## **Note** + ## * ``strAddress`` must be initialized to 46 in length. + assert(46 == len(strAddress), + "`strAddress` was not initialized correctly. 46 != `len(strAddress)`") + if sockAddr.sa_family.cint == nativeAfInet: + let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr + when not useWinVersion: + if posix.inet_ntop(posix.AF_INET, addr4, addr strAddress[0], + strAddress.len.int32) == nil: + raiseOSError(osLastError()) else: - 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) - result.port = Port(s.s_port) - result.proto = $s.s_proto + if winlean.inet_ntop(winlean.AF_INET, addr4, addr strAddress[0], + strAddress.len.int32) == nil: + raiseOSError(osLastError()) + elif sockAddr.sa_family.cint == nativeAfInet6: + let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr + when not useWinVersion: + if posix.inet_ntop(posix.AF_INET6, addr6, addr strAddress[0], + strAddress.len.int32) == nil: + raiseOSError(osLastError()) + if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0: + strAddress = strAddress.substr("::ffff:".len) + else: + if winlean.inet_ntop(winlean.AF_INET6, addr6, addr strAddress[0], + strAddress.len.int32) == nil: + raiseOSError(osLastError()) + else: + raise newException(IOError, "Unknown socket family in getAddrString") + setLen(strAddress, len(cstring(strAddress))) - proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = - ## This function will lookup the hostname of an IP Address. - var myaddr: InAddr - myaddr.s_addr = inet_addr(ip) +when defined(posix) and not defined(nimdoc): + proc makeUnixAddr*(path: string): Sockaddr_un = + result.sun_family = AF_UNIX.TSa_Family + if path.len >= Sockaddr_un_path_length: + raise newException(ValueError, "socket path too long") + copyMem(addr result.sun_path, path.cstring, path.len + 1) - when useWinVersion: - var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, - cint(AF_INET)) - if s == nil: raiseOSError(osLastError()) - else: - var s = - when defined(android4): - posix.gethostbyaddr(cast[cstring](addr(myaddr)), sizeof(myaddr).cint, - cint(posix.AF_INET)) - else: - posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).SockLen, - cint(posix.AF_INET)) - if s == nil: - raiseOSError(osLastError(), $hstrerror(h_errno)) +proc getSockName*(socket: SocketHandle): Port = + ## Returns the socket's associated port number. + var name: Sockaddr_in + when useWinVersion: + name.sin_family = uint16(ord(AF_INET)) + else: + name.sin_family = TSa_Family(posix.AF_INET) + #name.sin_port = htons(cint16(port)) + #name.sin_addr.s_addr = htonl(INADDR_ANY) + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = Port(nativesockets.ntohs(name.sin_port)) - result.name = $s.h_name - result.aliases = cstringArrayToSeq(s.h_aliases) - when useWinVersion: - result.addrtype = Domain(s.h_addrtype) - else: - if s.h_addrtype == posix.AF_INET: - result.addrtype = AF_INET - elif s.h_addrtype == posix.AF_INET6: - result.addrtype = AF_INET6 - else: - raiseOSError(osLastError(), "unknown h_addrtype") - if result.addrtype == AF_INET: - result.addrList = @[] - var i = 0 - while not isNil(s.h_addr_list[i]): - var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i]) - result.addrList.add($inet_ntoa(inaddrPtr[])) - inc(i) - else: - result.addrList = cstringArrayToSeq(s.h_addr_list) - result.length = int(s.h_length) - - proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = - ## This function will lookup the IP address of a hostname. - when useWinVersion: - var s = winlean.gethostbyname(name) - else: - var s = posix.gethostbyname(name) - if s == nil: raiseOSError(osLastError()) - result.name = $s.h_name - result.aliases = cstringArrayToSeq(s.h_aliases) - when useWinVersion: - result.addrtype = Domain(s.h_addrtype) - else: - if s.h_addrtype == posix.AF_INET: - result.addrtype = AF_INET - elif s.h_addrtype == posix.AF_INET6: - result.addrtype = AF_INET6 - else: - raiseOSError(osLastError(), "unknown h_addrtype") - if result.addrtype == AF_INET: - result.addrList = @[] - var i = 0 - while not isNil(s.h_addr_list[i]): - var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i]) - result.addrList.add($inet_ntoa(inaddrPtr[])) - inc(i) - else: - result.addrList = cstringArrayToSeq(s.h_addr_list) - result.length = int(s.h_length) - - proc getHostname*(): string {.tags: [ReadIOEffect].} = - ## Returns the local hostname (not the FQDN) - # https://tools.ietf.org/html/rfc1035#section-2.3.1 - # https://tools.ietf.org/html/rfc2181#section-11 - const size = 256 - result = newString(size) - when useWinVersion: - let success = winlean.gethostname(result.cstring, size) - else: - # Posix - let success = posix.gethostname(result.cstring, size) - if success != 0.cint: - raiseOSError(osLastError()) - let x = len(cstring(result)) - result.setLen(x) - - proc getAddrString*(sockAddr: ptr SockAddr): string = - ## Returns the string representation of address within sockAddr - if sockAddr.sa_family.cint == nativeAfInet: - result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr) - elif sockAddr.sa_family.cint == nativeAfInet6: - let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int - else: 46 # it's actually 46 in both cases - result = newString(addrLen) - let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr - when not useWinVersion: - if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr result[0]), - result.len.int32) == nil: - raiseOSError(osLastError()) - if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0: - result.setSlice("::ffff:".len..= Sockaddr_un_path_length: - raise newException(ValueError, "socket path too long") - copyMem(addr result.sun_path, path.cstring, path.len + 1) - - proc getSockName*(socket: SocketHandle): Port = - ## Returns the socket's associated port number. +proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = + ## Returns the socket's local address and port number. + ## + ## Similar to POSIX's `getsockname`:idx:. + case domain + of AF_INET: var name: Sockaddr_in when useWinVersion: name.sin_family = uint16(ord(AF_INET)) else: name.sin_family = TSa_Family(posix.AF_INET) - #name.sin_port = htons(cint16(port)) - #name.sin_addr.s_addr = htonl(INADDR_ANY) var namelen = sizeof(name).SockLen if getsockname(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: + addr(namelen)) == -1'i32: raiseOSError(osLastError()) - result = Port(nativesockets.ntohs(name.sin_port)) - - proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = - ## Returns the socket's local address and port number. - ## - ## Similar to POSIX's `getsockname`:idx:. - case domain - of AF_INET: - var name: Sockaddr_in - when useWinVersion: - name.sin_family = uint16(ord(AF_INET)) - else: - name.sin_family = TSa_Family(posix.AF_INET) - var namelen = sizeof(name).SockLen - if getsockname(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - result = ($inet_ntoa(name.sin_addr), - Port(nativesockets.ntohs(name.sin_port))) - of AF_INET6: - var name: Sockaddr_in6 - when useWinVersion: - name.sin6_family = uint16(ord(AF_INET6)) - else: - name.sin6_family = TSa_Family(posix.AF_INET6) - var namelen = sizeof(name).SockLen - if getsockname(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - # Cannot use INET6_ADDRSTRLEN here, because it's a C define. - result[0] = newString(64) - if inet_ntop(name.sin6_family.cint, - addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil: - raiseOSError(osLastError()) - setLen(result[0], result[0].cstring.len) - result[1] = Port(nativesockets.ntohs(name.sin6_port)) + result = ($inet_ntoa(name.sin_addr), + Port(nativesockets.ntohs(name.sin_port))) + of AF_INET6: + var name: Sockaddr_in6 + when useWinVersion: + name.sin6_family = uint16(ord(AF_INET6)) else: - raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") - - proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = - ## Returns the socket's peer address and port number. - ## - ## Similar to POSIX's `getpeername`:idx: - case domain - of AF_INET: - var name: Sockaddr_in - when useWinVersion: - name.sin_family = uint16(ord(AF_INET)) - else: - name.sin_family = TSa_Family(posix.AF_INET) - var namelen = sizeof(name).SockLen - if getpeername(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - result = ($inet_ntoa(name.sin_addr), - Port(nativesockets.ntohs(name.sin_port))) - of AF_INET6: - var name: Sockaddr_in6 - when useWinVersion: - name.sin6_family = uint16(ord(AF_INET6)) - else: - name.sin6_family = TSa_Family(posix.AF_INET6) - var namelen = sizeof(name).SockLen - if getpeername(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - # Cannot use INET6_ADDRSTRLEN here, because it's a C define. - result[0] = newString(64) - if inet_ntop(name.sin6_family.cint, - addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil: - raiseOSError(osLastError()) - setLen(result[0], result[0].cstring.len) - result[1] = Port(nativesockets.ntohs(name.sin6_port)) - else: - raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") - -when useNimNetLite: - - when useWinVersion: - const - INET_ADDRSTRLEN = 16 - INET6_ADDRSTRLEN = 46 # it's actually 46 in both cases - - proc sockAddrToStr(sa: ptr SockAddr): string {.noinit.} = - let af_family = sa.sa_family - var nl, v4Slice: cint - var si_addr: ptr InAddr - - if af_family == AF_INET.TSa_Family: - nl = INET_ADDRSTRLEN - si_addr = cast[ptr Sockaddr_in](sa).sin_addr.addr() - elif af_family == AF_INET6.TSa_Family: - nl = INET6_ADDRSTRLEN - let si6_addr = cast[ptr Sockaddr_in6](sa).sin6_addr.addr() - si_addr = cast[ptr InAddr](si6_addr) # let's us reuse logic below - when defined(posix) and not defined(nimdoc) and not defined(zephyr): - if posix.IN6_IS_ADDR_V4MAPPED(si6_addr) != 0: - v4Slice = "::ffff:".len() - else: - when defined(posix) and not defined(nimdoc): - if af_family.cint == nativeAfUnix: - return "unix" - return "" - - result = newString(nl) - let namePtr = result.cstring() - if namePtr == inet_ntop(af_family.cint, si_addr, namePtr, nl): - result.setLen(len(namePtr)) - if v4Slice > 0: result.setSlice(v4Slice.int ..< nl.int) - else: - return "" - - proc sockAddrToStr(sa: var Sockaddr_in | var Sockaddr_in6): string = - result = sockAddrToStr(cast[ptr SockAddr](unsafeAddr(sa))) - - proc getAddrString*(sockAddr: ptr SockAddr): string = - result = sockAddrToStr(sockAddr) - if result.len() == 0: + name.sin6_family = TSa_Family(posix.AF_INET6) + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: raiseOSError(osLastError()) + # Cannot use INET6_ADDRSTRLEN here, because it's a C define. + result[0] = newString(64) + if inet_ntop(name.sin6_family.cint, + addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil: + raiseOSError(osLastError()) + setLen(result[0], result[0].cstring.len) + result[1] = Port(nativesockets.ntohs(name.sin6_port)) + else: + raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") - proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) {.noinit.} = - strAddress = getAddrString(sockAddr) - - proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = - ## Returns the socket's local address and port number. - ## - ## Similar to POSIX's `getsockname`:idx:. - template sockGetNameOrRaiseError(socket: untyped, name: untyped) = - var namelen = sizeof(socket).SockLen - if getsockname(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - - case domain - of AF_INET: - var name = Sockaddr_in(sin_family: TSa_Family(posix.AF_INET)) - sockGetNameOrRaiseError(socket, name) - result = (sockAddrToStr(name), - Port(nativesockets.ntohs(name.sin_port))) - of AF_INET6: - var name = Sockaddr_in6(sin6_family: TSa_Family(posix.AF_INET6)) - sockGetNameOrRaiseError(socket, name) - result = (sockAddrToStr(name), - Port(nativesockets.ntohs(name.sin6_port))) +proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = + ## Returns the socket's peer address and port number. + ## + ## Similar to POSIX's `getpeername`:idx: + case domain + of AF_INET: + var name: Sockaddr_in + when useWinVersion: + name.sin_family = uint16(ord(AF_INET)) else: - raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") - + name.sin_family = TSa_Family(posix.AF_INET) + var namelen = sizeof(name).SockLen + if getpeername(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = ($inet_ntoa(name.sin_addr), + Port(nativesockets.ntohs(name.sin_port))) + of AF_INET6: + var name: Sockaddr_in6 + when useWinVersion: + name.sin6_family = uint16(ord(AF_INET6)) + else: + name.sin6_family = TSa_Family(posix.AF_INET6) + var namelen = sizeof(name).SockLen + if getpeername(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + # Cannot use INET6_ADDRSTRLEN here, because it's a C define. + result[0] = newString(64) + if inet_ntop(name.sin6_family.cint, + addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil: + raiseOSError(osLastError()) + setLen(result[0], result[0].cstring.len) + result[1] = Port(nativesockets.ntohs(name.sin6_port)) + else: + raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {. tags: [ReadIOEffect].} = @@ -764,12 +682,12 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) = setLen(s, L) proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int = - ## When a socket in `readfds` is ready to be read from then a non-zero + ## When a socket in ``readfds`` is ready to be read from then a non-zero ## value will be returned specifying the count of the sockets which can be ## read from. The sockets which cannot be read from will also be removed - ## from `readfds`. + ## from ``readfds``. ## - ## `timeout` is specified in milliseconds and `-1` can be specified for + ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for ## an unlimited time. var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout) @@ -786,12 +704,12 @@ proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int = proc selectWrite*(writefds: var seq[SocketHandle], timeout = 500): int {.tags: [ReadIOEffect].} = - ## When a socket in `writefds` is ready to be written to then a non-zero + ## When a socket in ``writefds`` is ready to be written to then a non-zero ## value will be returned specifying the count of the sockets which can be ## written to. The sockets which cannot be written to will also be removed - ## from `writefds`. + ## from ``writefds``. ## - ## `timeout` is specified in milliseconds and `-1` can be specified for + ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for ## an unlimited time. var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout) @@ -813,14 +731,14 @@ proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (Socke ## child processes. ## ## Returns (osInvalidSocket, "") if an error occurred. - var sockAddress: SockAddr + var sockAddress: Sockaddr_in var addrLen = sizeof(sockAddress).SockLen var sock = when (defined(linux) or defined(bsd)) and not defined(nimdoc): - accept4(fd, addr(sockAddress), addr(addrLen), + accept4(fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen), if inheritable: 0 else: SOCK_CLOEXEC) else: - accept(fd, addr(sockAddress), addr(addrLen)) + accept(fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) when declared(setInheritable) and not (defined(linux) or defined(bsd)): if not setInheritable(sock, inheritable): close sock @@ -828,12 +746,8 @@ proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (Socke if sock == osInvalidSocket: return (osInvalidSocket, "") else: - when useNimNetLite: - var name = sockAddrToStr(addr sockAddress) - return (sock, name) - else: - return (sock, $inet_ntoa(cast[Sockaddr_in](sockAddress).sin_addr)) + return (sock, $inet_ntoa(sockAddress.sin_addr)) -when defined(windows): +when defined(Windows): var wsa: WSAData if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError()) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index d3a767eed..7b92a5e0d 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -9,99 +9,80 @@ ## This module implements a high-level cross-platform sockets interface. ## The procedures implemented in this module are primarily for blocking sockets. -## For asynchronous non-blocking sockets use the `asyncnet` module together -## with the `asyncdispatch` module. +## For asynchronous non-blocking sockets use the ``asyncnet`` module together +## with the ``asyncdispatch`` module. ## ## The first thing you will always need to do in order to start using sockets, -## is to create a new instance of the `Socket` type using the `newSocket` +## is to create a new instance of the ``Socket`` type using the ``newSocket`` ## procedure. ## ## SSL ## ==== ## ## In order to use the SSL procedures defined in this module, you will need to -## compile your application with the `-d:ssl` flag. See the +## compile your application with the ``-d:ssl`` flag. See the ## `newContext`_ ## procedure for additional details. ## -## -## SSL on Windows -## ============== -## -## On Windows the SSL library checks for valid certificates. -## It uses the `cacert.pem` file for this purpose which was extracted -## from `https://curl.se/ca/cacert.pem`. Besides -## the OpenSSL DLLs (e.g. libssl-1_1-x64.dll, libcrypto-1_1-x64.dll) you -## also need to ship `cacert.pem` with your `.exe` file. -## -## ## Examples ## ======== ## ## Connecting to a server ## ---------------------- ## -## After you create a socket with the `newSocket` procedure, you can easily +## After you create a socket with the ``newSocket`` procedure, you can easily ## connect it to a server running at a known hostname (or IP address) and port. ## To do so over TCP, use the example below. - -runnableExamples("-r:off"): - let socket = newSocket() - socket.connect("google.com", Port(80)) - -## For SSL, use the following example: - -runnableExamples("-r:off -d:ssl"): - let socket = newSocket() - let ctx = newContext() - wrapSocket(ctx, socket) - socket.connect("google.com", Port(443)) - +## +## .. code-block:: Nim +## var socket = newSocket() +## socket.connect("google.com", Port(80)) +## +## For SSL, use the following example (and make sure to compile with ``-d:ssl``): +## +## .. code-block:: Nim +## var socket = newSocket() +## var ctx = newContext() +## wrapSocket(ctx, socket) +## socket.connect("google.com", Port(443)) +## ## UDP is a connectionless protocol, so UDP sockets don't have to explicitly ## call the `connect `_ procedure. They can ## simply start sending data immediately. - -runnableExamples("-r:off"): - let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) - socket.sendTo("192.168.0.1", Port(27960), "status\n") - -runnableExamples("-r:off"): - let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) - let ip = parseIpAddress("192.168.0.1") - doAssert socket.sendTo(ip, Port(27960), "status\c\l") == 8 - +## +## .. code-block:: Nim +## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) +## socket.sendTo("192.168.0.1", Port(27960), "status\n") +## ## Creating a server ## ----------------- ## -## After you create a socket with the `newSocket` procedure, you can create a -## TCP server by calling the `bindAddr` and `listen` procedures. - -runnableExamples("-r:off"): - let socket = newSocket() - socket.bindAddr(Port(1234)) - socket.listen() - - # You can then begin accepting connections using the `accept` procedure. - var client: Socket - var address = "" - while true: - socket.acceptAddr(client, address) - echo "Client connected from: ", address +## After you create a socket with the ``newSocket`` procedure, you can create a +## TCP server by calling the ``bindAddr`` and ``listen`` procedures. +## +## .. code-block:: Nim +## var socket = newSocket() +## socket.bindAddr(Port(1234)) +## socket.listen() +## +## You can then begin accepting connections using the ``accept`` procedure. +## +## .. code-block:: Nim +## var client: Socket +## var address = "" +## while true: +## socket.acceptAddr(client, address) +## echo("Client connected from: ", address) import std/private/since -when defined(nimPreviewSlimSystem): - import std/assertions - -import nativesockets -import os, strutils, times, sets, options, std/monotimes +import nativesockets, os, strutils, times, sets, options, std/monotimes +from ssl_certs import scanSSLCertificates import ssl_config export nativesockets.Port, nativesockets.`$`, nativesockets.`==` export Domain, SockType, Protocol -const useWinVersion = defined(windows) or defined(nimdoc) -const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or - defined(nuttx) +const useWinVersion = defined(Windows) or defined(nimdoc) const defineSsl = defined(ssl) or defined(nimdoc) when useWinVersion: @@ -109,8 +90,6 @@ when useWinVersion: when defineSsl: import openssl - when not defined(nimDisableCertificateValidation): - from ssl_certs import scanSSLCertificates # Note: The enumerations are mapped to Window's constants. @@ -207,38 +186,14 @@ 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.} + flags: set[SocketFlag] = {}): void {.gcsafe.} proc isDisconnectionError*(flags: set[SocketFlag], lastError: OSErrorCode): bool = - ## Determines whether `lastError` is a disconnection error. Only does this - ## if flags contains `SafeDisconn`. + ## Determines whether ``lastError`` is a disconnection error. Only does this + ## if flags contains ``SafeDisconn``. when useWinVersion: SocketFlag.SafeDisconn in flags and (lastError.int32 == WSAECONNRESET or @@ -316,20 +271,14 @@ proc parseIPv4Address(addressStr: string): IpAddress = byteCount = 0 currentByte: uint16 = 0 separatorValid = false - leadingZero = false result = IpAddress(family: IpAddressFamily.IPv4) for i in 0 .. high(addressStr): if addressStr[i] in strutils.Digits: # Character is a number - if leadingZero: - raise newException(ValueError, - "Invalid IP address. Octal numbers are not allowed") currentByte = currentByte * 10 + cast[uint16](ord(addressStr[i]) - ord('0')) - if currentByte == 0'u16: - leadingZero = true - elif currentByte > 255'u16: + if currentByte > 255'u16: raise newException(ValueError, "Invalid IP Address. Value is out of range") separatorValid = true @@ -341,7 +290,6 @@ proc parseIPv4Address(addressStr: string): IpAddress = currentByte = 0 byteCount.inc separatorValid = false - leadingZero = false else: raise newException(ValueError, "Invalid IP Address. Address contains an invalid character") @@ -430,16 +378,10 @@ proc parseIPv6Address(addressStr: string): IpAddress = result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) groupCount.inc() else: # Must parse IPv4 address - var leadingZero = false for i, c in addressStr[v4StartPos..high(addressStr)]: if c in strutils.Digits: # Character is a number - if leadingZero: - raise newException(ValueError, - "Invalid IP address. Octal numbers not allowed") currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) - if currentShort == 0'u32: - leadingZero = true - elif currentShort > 255'u32: + if currentShort > 255'u32: raise newException(ValueError, "Invalid IP Address. Value is out of range") separatorValid = true @@ -450,7 +392,6 @@ proc parseIPv6Address(addressStr: string): IpAddress = currentShort = 0 byteCount.inc() separatorValid = false - leadingZero = false else: # Invalid character raise newException(ValueError, "Invalid IP Address. Address contains an invalid character") @@ -480,12 +421,7 @@ proc parseIPv6Address(addressStr: string): IpAddress = proc parseIpAddress*(addressStr: string): IpAddress = ## Parses an IP address - ## - ## Raises ValueError on error. - ## - ## For IPv4 addresses, only the strict form as - ## defined in RFC 6943 is considered valid, see - ## https://datatracker.ietf.org/doc/html/rfc6943#section-3.1.1. + ## Raises ValueError on error if addressStr.len == 0: raise newException(ValueError, "IP Address string is empty") if addressStr.contains(':'): @@ -510,14 +446,14 @@ proc toSockAddr*(address: IpAddress, port: Port, sa: var Sockaddr_storage, of IpAddressFamily.IPv4: sl = sizeof(Sockaddr_in).SockLen let s = cast[ptr Sockaddr_in](addr sa) - s.sin_family = typeof(s.sin_family)(toInt(AF_INET)) + s.sin_family = type(s.sin_family)(toInt(AF_INET)) s.sin_port = port copyMem(addr s.sin_addr, unsafeAddr address.address_v4[0], sizeof(s.sin_addr)) of IpAddressFamily.IPv6: sl = sizeof(Sockaddr_in6).SockLen let s = cast[ptr Sockaddr_in6](addr sa) - s.sin6_family = typeof(s.sin6_family)(toInt(AF_INET6)) + s.sin6_family = type(s.sin6_family)(toInt(AF_INET6)) s.sin6_port = port copyMem(addr s.sin6_addr, unsafeAddr address.address_v6[0], sizeof(s.sin6_addr)) @@ -547,20 +483,13 @@ proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6, fromSockAddrAux(cast[ptr Sockaddr_storage](unsafeAddr sa), sl, address, port) when defineSsl: - # 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() + 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 = "") {.raises: [SslError].}= + proc raiseSSLError*(s = "") = ## Raises a new SSL error. if s != "": raise newException(SslError, s) @@ -622,16 +551,17 @@ when defineSsl: proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, certFile = "", keyFile = "", cipherList = CiphersIntermediate, - caDir = "", caFile = "", ciphersuites = CiphersModern): SslContext = + caDir = "", caFile = ""): SslContext = ## Creates an SSL context. ## - ## Protocol version is currently ignored by default and TLS is used. - ## With `-d:openssl10`, only SSLv23 and TLSv1 may be used. + ## 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. ## ## There are three options for verify mode: - ## `CVerifyNone`: certificates are not verified; - ## `CVerifyPeer`: certificates are verified; - ## `CVerifyPeerUseEnvVars`: certificates are verified and the optional + ## ``CVerifyNone``: certificates are not verified; + ## ``CVerifyPeer``: certificates are verified; + ## ``CVerifyPeerUseEnvVars``: certificates are verified and the optional ## environment variables SSL_CERT_FILE and SSL_CERT_DIR are also used to ## locate certificates ## @@ -640,46 +570,38 @@ when defineSsl: ## ## CA certificates will be loaded, in the following order, from: ## - ## - caFile, caDir, parameters, if set - ## - if `verifyMode` is set to `CVerifyPeerUseEnvVars`, - ## the SSL_CERT_FILE and SSL_CERT_DIR environment variables are used - ## - a set of files and directories from the `ssl_certs `_ file. + ## - caFile, caDir, parameters, if set + ## - if `verifyMode` is set to ``CVerifyPeerUseEnvVars``, + ## the SSL_CERT_FILE and SSL_CERT_DIR environment variables are used + ## - a set of files and directories from the `ssl_certs `_ file. ## ## The last two parameters specify the certificate file path and the key file ## path, a server socket will most likely not work without these. ## ## Certificates can be generated using the following command: - ## - `openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout mykey.pem -out mycert.pem` + ## - ``openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout mykey.pem -out mycert.pem`` ## 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 mtd: PSSL_METHOD - when defined(openssl10): - case protVersion - of protSSLv23: - mtd = SSLv23_method() - 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: - mtd = TLSv1_method() - else: - 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") + ## - ``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()) if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1: raiseSSLError() when not defined(openssl10) and not defined(libressl): let sslVersion = getOpenSSLVersion() - if sslVersion >= 0x010101000 and sslVersion != 0x020000000: + if sslVersion >= 0x010101000 and not sslVersion == 0x020000000: # In OpenSSL >= 1.1.1, TLSv1.3 cipher suites can only be configured via # this API. - if newCTX.SSL_CTX_set_ciphersuites(ciphersuites) != 1: + if newCTX.SSL_CTX_set_ciphersuites(cipherList) != 1: raiseSSLError() # Automatically the best ECDH curve for client exchange. Without this, ECDH # ciphers will be ignored by the server. @@ -689,7 +611,7 @@ when defineSsl: if newCTX.SSL_CTX_set_ecdh_auto(1) != 1: raiseSSLError() - when defined(nimDisableCertificateValidation): + when defined(nimDisableCertificateValidation) or defined(windows): newCTX.SSL_CTX_set_verify(SSL_VERIFY_NONE, nil) else: case verifyMode @@ -704,26 +626,19 @@ when defineSsl: discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY) newCTX.loadCertificates(certFile, keyFile) - const VerifySuccess = 1 # SSL_CTX_load_verify_locations returns 1 on success. - - when not defined(nimDisableCertificateValidation): + when not defined(nimDisableCertificateValidation) and not defined(windows): if verifyMode != CVerifyNone: # Use the caDir and caFile parameters if set if caDir != "" or caFile != "": - if newCTX.SSL_CTX_load_verify_locations(if caFile == "": nil else: caFile.cstring, if caDir == "": nil else: caDir.cstring) != VerifySuccess: + if newCTX.SSL_CTX_load_verify_locations(caFile, caDir) != 0: raise newException(IOError, "Failed to load SSL/TLS CA certificate(s).") else: # Scan for certs in known locations. For CVerifyPeerUseEnvVars also scan # the SSL_CERT_FILE and SSL_CERT_DIR env vars var found = false - let useEnvVars = (if verifyMode == CVerifyPeerUseEnvVars: true else: false) - for fn in scanSSLCertificates(useEnvVars = useEnvVars): - 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: + for fn in scanSSLCertificates(): + if newCTX.SSL_CTX_load_verify_locations(fn, "") == 0: found = true break if not found: @@ -742,7 +657,7 @@ when defineSsl: # That means we can assume that the next internal index is the length of # extra data indexes. for i in ctx.referencedData: - GC_unref(getExtraData(ctx, i)) + GC_unref(getExtraData(ctx, i).RootRef) ctx.context.SSL_CTX_free() proc `pskIdentityHint=`*(ctx: SslContext, hint: string) = @@ -756,16 +671,17 @@ when defineSsl: return ctx.getExtraInternal().clientGetPskFunc proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; - max_identity_len: cuint; psk: ptr uint8; + max_identity_len: cuint; psk: ptr cuchar; max_psk_len: cuint): cuint {.cdecl.} = let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) let hintString = if hint == nil: "" else: $hint let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString) - if pskString.len.cuint > max_psk_len: + if psk.len.cuint > max_psk_len: return 0 if identityString.len.cuint >= max_identity_len: return 0 - copyMem(identity, identityString.cstring, identityString.len + 1) # with the last zero byte + + copyMem(identity, identityString.cstring, pskString.len + 1) # with the last zero byte copyMem(psk, pskString.cstring, pskString.len) return pskString.len.cuint @@ -782,11 +698,11 @@ when defineSsl: proc serverGetPskFunc*(ctx: SslContext): SslServerGetPskFunc = return ctx.getExtraInternal().serverGetPskFunc - proc pskServerCallback(ssl: SslCtx; identity: cstring; psk: ptr uint8; + proc pskServerCallback(ssl: SslCtx; identity: cstring; psk: ptr cuchar; max_psk_len: cint): cuint {.cdecl.} = let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) let pskString = (ctx.serverGetPskFunc)($identity) - if pskString.len.cint > max_psk_len: + if psk.len.cint > max_psk_len: return 0 copyMem(psk, pskString.cstring, pskString.len) @@ -807,7 +723,7 @@ when defineSsl: proc wrapSocket*(ctx: SslContext, socket: Socket) = ## Wraps a socket in an SSL context. This function effectively turns - ## `socket` into an SSL socket. + ## ``socket`` into an SSL socket. ## ## This must be called on an unconnected socket; an SSL session will ## be started when the socket is connected. @@ -829,35 +745,30 @@ when defineSsl: if SSL_set_fd(socket.sslHandle, socket.fd) != 1: raiseSSLError() - proc checkCertName(socket: Socket, hostname: string) {.raises: [SslError], tags:[RootEffect].} = + proc checkCertName(socket: Socket, hostname: string) = ## 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 - try: - let certificate = socket.sslHandle.SSL_get_peer_certificate() - if certificate.isNil: - raiseSSLError("No SSL certificate found.") + 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") + const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT = 0x1.cuint + const size = 1024 + var peername: string = newString(size) + let match = certificate.X509_check_host(hostname.cstring, hostname.len.cint, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, peername) + if match != 1: + raiseSSLError("SSL Certificate check failed.") proc wrapConnectedSocket*(ctx: SslContext, socket: Socket, handshake: SslHandshakeType, hostname: string = "") = ## Wraps a connected socket in an SSL context. This function effectively - ## turns `socket` into an SSL socket. - ## `hostname` should be specified so that the client knows which hostname + ## turns ``socket`` into an SSL socket. + ## ``hostname`` should be specified so that the client knows which hostname ## the server certificate should be validated against. ## ## This should be called on a connected socket, and will perform @@ -877,7 +788,6 @@ 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: @@ -887,7 +797,7 @@ when defineSsl: proc getPeerCertificates*(sslHandle: SslPtr): seq[Certificate] {.since: (1, 1).} = ## Returns the certificate chain received by the peer we are connected to - ## through the OpenSSL connection represented by `sslHandle`. + ## through the OpenSSL connection represented by ``sslHandle``. ## The handshake must have been completed and the certificate chain must ## have been verified successfully or else an empty sequence is returned. ## The chain is ordered from leaf certificate to root certificate. @@ -930,9 +840,9 @@ when defineSsl: if sidCtx.len > 32: raiseSSLError("sessionIdContext must be shorter than 32 characters") SSL_CTX_set_session_id_context(ctx.context, sidCtx, sidCtx.len) - + proc getSocketError*(socket: Socket): OSErrorCode = - ## Checks `osLastError` for a valid error. If it has been reset it uses + ## Checks ``osLastError`` for a valid error. If it has been reset it uses ## the last error stored in the socket object. result = osLastError() if result == 0.OSErrorCode: @@ -943,15 +853,15 @@ proc getSocketError*(socket: Socket): OSErrorCode = proc socketError*(socket: Socket, err: int = -1, async = false, lastError = (-1).OSErrorCode, flags: set[SocketFlag] = {}) = - ## Raises an OSError based on the error code returned by `SSL_get_error` - ## (for SSL sockets) and `osLastError` otherwise. + ## Raises an OSError based on the error code returned by ``SSL_get_error`` + ## (for SSL sockets) and ``osLastError`` otherwise. ## - ## If `async` is `true` no error will be thrown in the case when the + ## If ``async`` is ``true`` no error will be thrown in the case when the ## error was caused by no data being available to be read. ## - ## If `err` is not lower than 0 no exception will be raised. + ## If ``err`` is not lower than 0 no exception will be raised. ## - ## If `flags` contains `SafeDisconn`, no exception will be raised + ## If ``flags`` contains ``SafeDisconn``, no exception will be raised ## when the error was caused by a peer disconnection. when defineSsl: if socket.isSsl: @@ -1006,8 +916,8 @@ proc socketError*(socket: Socket, err: int = -1, async = false, else: raiseOSError(lastE) proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = - ## Marks `socket` as accepting connections. - ## `Backlog` specifies the maximum length of the + ## Marks ``socket`` as accepting connections. + ## ``Backlog`` specifies the maximum length of the ## queue of pending connections. ## ## Raises an OSError error upon failure. @@ -1016,9 +926,9 @@ proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = proc bindAddr*(socket: Socket, port = Port(0), address = "") {. tags: [ReadIOEffect].} = - ## Binds `address`:`port` to the socket. + ## Binds ``address``:``port`` to the socket. ## - ## If `address` is "" then ADDR_ANY will be bound. + ## If ``address`` is "" then ADDR_ANY will be bound. var realaddr = address if realaddr == "": case socket.domain @@ -1030,18 +940,16 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. var aiList = getAddrInfo(realaddr, port, socket.domain) if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: - freeAddrInfo(aiList) - var address2: string - address2.addQuoted address - raiseOSError(osLastError(), "address: $# port: $#" % [address2, $port]) - freeAddrInfo(aiList) + freeaddrinfo(aiList) + raiseOSError(osLastError()) + freeaddrinfo(aiList) proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, flags = {SocketFlag.SafeDisconn}, inheritable = defined(nimInheritHandles)) {. - tags: [ReadIOEffect], gcsafe.} = + tags: [ReadIOEffect], gcsafe, locks: 0.} = ## Blocks until a connection is being made from a client. When a connection - ## is made sets `client` to the client socket and `address` to the address + ## is made sets ``client`` to the client socket and ``address`` to the address ## of the connecting client. ## This function will raise OSError if an error occurs. ## @@ -1052,8 +960,8 @@ proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, ## inheritable by child processes by default. This can be changed via ## the `inheritable` parameter. ## - ## The `accept` call may result in an error if the connecting socket - ## disconnects during the duration of the `accept`. If the `SafeDisconn` + ## The ``accept`` call may result in an error if the connecting socket + ## disconnects during the duration of the ``accept``. If the ``SafeDisconn`` ## flag is specified then this error will not be raised and instead ## accept will be called again. if client.isNil: @@ -1089,16 +997,16 @@ when false: #defineSsl: ## This procedure should only be used for non-blocking **SSL** sockets. ## It will immediately return with one of the following values: ## - ## `AcceptSuccess` will be returned when a client has been successfully + ## ``AcceptSuccess`` will be returned when a client has been successfully ## accepted and the handshake has been successfully performed between - ## `server` and the newly connected client. + ## ``server`` and the newly connected client. ## - ## `AcceptNoHandshake` will be returned when a client has been accepted + ## ``AcceptNoHandshake`` will be returned when a client has been accepted ## but no handshake could be performed. This can happen when the client ## connects but does not yet initiate a handshake. In this case - ## `acceptAddrSSL` should be called again with the same parameters. + ## ``acceptAddrSSL`` should be called again with the same parameters. ## - ## `AcceptNoClient` will be returned when no client is currently attempting + ## ``AcceptNoClient`` will be returned when no client is currently attempting ## to connect. template doHandshake(): untyped = when defineSsl: @@ -1139,15 +1047,15 @@ proc accept*(server: Socket, client: var owned(Socket), flags = {SocketFlag.SafeDisconn}, inheritable = defined(nimInheritHandles)) {.tags: [ReadIOEffect].} = - ## Equivalent to `acceptAddr` but doesn't return the address, only the + ## Equivalent to ``acceptAddr`` but doesn't return the address, only the ## socket. ## ## The SocketHandle associated with the resulting client will not be ## inheritable by child processes by default. This can be changed via ## the `inheritable` parameter. ## - ## The `accept` call may result in an error if the connecting socket - ## disconnects during the duration of the `accept`. If the `SafeDisconn` + ## The ``accept`` call may result in an error if the connecting socket + ## disconnects during the duration of the ``accept``. If the ``SafeDisconn`` ## flag is specified then this error will not be raised and instead ## accept will be called again. var addrDummy = "" @@ -1273,7 +1181,7 @@ else: from winlean import TCP_NODELAY proc toCInt*(opt: SOBool): cint = - ## Converts a `SOBool` into its Socket Option cint representation. + ## Converts a ``SOBool`` into its Socket Option cint representation. case opt of OptAcceptConn: SO_ACCEPTCONN of OptBroadcast: SO_BROADCAST @@ -1287,7 +1195,7 @@ proc toCInt*(opt: SOBool): cint = proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {. tags: [ReadIOEffect].} = - ## Retrieves option `opt` as a boolean value. + ## Retrieves option ``opt`` as a boolean value. var res = getSockOptInt(socket.fd, cint(level), toCInt(opt)) result = res != 0 @@ -1297,24 +1205,25 @@ proc getLocalAddr*(socket: Socket): (string, Port) = ## This is high-level interface for `getsockname`:idx:. getLocalAddr(socket.fd, socket.domain) -when not useNimNetLite: - proc getPeerAddr*(socket: Socket): (string, Port) = - ## Get the socket's peer address and port number. - ## - ## This is high-level interface for `getpeername`:idx:. - getPeerAddr(socket.fd, socket.domain) +proc getPeerAddr*(socket: Socket): (string, Port) = + ## Get the socket's peer address and port number. + ## + ## This is high-level interface for `getpeername`:idx:. + getPeerAddr(socket.fd, socket.domain) proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.tags: [WriteIOEffect].} = - ## Sets option `opt` to a boolean value specified by `value`. - runnableExamples("-r:off"): - let socket = newSocket() - socket.setSockOpt(OptReusePort, true) - socket.setSockOpt(OptNoDelay, true, level = IPPROTO_TCP.cint) + ## Sets option ``opt`` to a boolean value specified by ``value``. + ## + ## .. code-block:: Nim + ## var socket = newSocket() + ## socket.setSockOpt(OptReusePort, true) + ## socket.setSockOpt(OptNoDelay, true, level=IPPROTO_TCP.toInt) + ## var valuei = cint(if value: 1 else: 0) setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei) -when defined(nimdoc) or (defined(posix) and not useNimNetLite): +when defined(posix) or defined(nimdoc): proc connectUnix*(socket: Socket, path: string) = ## Connects to Unix socket on `path`. ## This only works on Unix-style systems: Mac OS X, BSD and Linux @@ -1333,12 +1242,12 @@ when defined(nimdoc) or (defined(posix) and not useNimNetLite): (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32: raiseOSError(osLastError()) -when defineSsl: +when defined(ssl): proc gotHandshake*(socket: Socket): bool = - ## Determines whether a handshake has occurred between a client (`socket`) - ## and the server that `socket` is connected to. + ## Determines whether a handshake has occurred between a client (``socket``) + ## and the server that ``socket`` is connected to. ## - ## Throws SslError if `socket` is not an SSL socket. + ## Throws SslError if ``socket`` is not an SSL socket. if socket.isSsl: return not socket.sslNoHandshake else: @@ -1354,6 +1263,14 @@ 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 @@ -1398,7 +1315,7 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ ## Receives data from a socket. ## ## **Note**: This is a low-level function, you may be interested in the higher - ## level versions of this function which are also named `recv`. + ## level versions of this function which are also named ``recv``. if size == 0: return if socket.isBuffered: if socket.bufLen == 0: @@ -1441,11 +1358,11 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ proc waitFor(socket: Socket, waited: var Duration, timeout, size: int, funcName: string): int {.tags: [TimeEffect].} = ## determines the amount of characters that can be read. Result will never - ## be larger than `size`. For unbuffered sockets this will be `1`. - ## For buffered sockets it can be as big as `BufferSize`. + ## be larger than ``size``. For unbuffered sockets this will be ``1``. + ## For buffered sockets it can be as big as ``BufferSize``. ## ## If this function does not determine that there is data on the socket - ## within `timeout` ms, a TimeoutError error will be raised. + ## within ``timeout`` ms, a TimeoutError error will be raised. result = 1 if size <= 0: assert false if timeout == -1: return size @@ -1467,9 +1384,7 @@ proc waitFor(socket: Socket, waited: var Duration, timeout, size: int, return min(sslPending, size) var startTime = getMonoTime() - let selRet = if socket.hasDataBuffered: 1 - else: - timeoutRead(socket.fd, (timeout - waited.inMilliseconds).int) + let selRet = select(socket, (timeout - waited.inMilliseconds).int) if selRet < 0: raiseOSError(osLastError()) if selRet != 1: raise newException(TimeoutError, "Call to '" & funcName & "' timed out.") @@ -1477,7 +1392,7 @@ proc waitFor(socket: Socket, waited: var Duration, timeout, size: int, proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {. tags: [ReadIOEffect, TimeEffect].} = - ## overload with a `timeout` parameter in milliseconds. + ## overload with a ``timeout`` parameter in milliseconds. var waited: Duration # duration already waited var read = 0 @@ -1495,12 +1410,12 @@ proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {. proc recv*(socket: Socket, data: var string, size: int, timeout = -1, flags = {SocketFlag.SafeDisconn}): int = - ## Higher-level version of `recv`. + ## Higher-level version of ``recv``. ## - ## Reads **up to** `size` bytes from `socket` into `data`. + ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. ## ## For buffered sockets this function will attempt to read all the requested - ## data. It will read this data in `BufferSize` chunks. + ## data. It will read this data in ``BufferSize`` chunks. ## ## For unbuffered sockets this function makes no effort to read ## all the data requested. It will return as much data as the operating system @@ -1514,7 +1429,9 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1, ## A timeout may be specified in milliseconds, if enough data is not received ## within the time specified a TimeoutError exception will be raised. ## - ## .. warning:: Only the `SafeDisconn` flag is currently supported. + ## **Note**: ``data`` must be initialised. + ## + ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. data.setLen(size) result = if timeout == -1: @@ -1530,18 +1447,18 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1, proc recv*(socket: Socket, size: int, timeout = -1, flags = {SocketFlag.SafeDisconn}): string {.inline.} = - ## Higher-level version of `recv` which returns a string. + ## Higher-level version of ``recv`` which returns a string. ## - ## Reads **up to** `size` bytes from `socket` into the result. + ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. ## ## For buffered sockets this function will attempt to read all the requested - ## data. It will read this data in `BufferSize` chunks. + ## data. It will read this data in ``BufferSize`` chunks. ## ## For unbuffered sockets this function makes no effort to read ## all the data requested. It will return as much data as the operating system ## gives it. ## - ## When `""` is returned the socket's connection has been closed. + ## When ``""`` is returned the socket's connection has been closed. ## ## This function will throw an OSError exception when an error occurs. ## @@ -1549,7 +1466,7 @@ proc recv*(socket: Socket, size: int, timeout = -1, ## within the time specified a TimeoutError exception will be raised. ## ## - ## .. warning:: Only the `SafeDisconn` flag is currently supported. + ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. result = newString(size) discard recv(socket, result, size, timeout, flags) @@ -1573,47 +1490,46 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} = return result = recv(socket.fd, addr(c), 1, MSG_PEEK) -proc readLine*(socket: Socket, line: var string, timeout = -1, +proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {. tags: [ReadIOEffect, TimeEffect].} = - ## Reads a line of data from `socket`. + ## Reads a line of data from ``socket``. ## - ## If a full line is read `\r\L` is not - ## added to `line`, however if solely `\r\L` is read then `line` + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## will be set to it. ## - ## If the socket is disconnected, `line` will be set to `""`. + ## If the socket is disconnected, ``line`` will be set to ``""``. ## ## An OSError exception will be raised in the case of a socket error. ## ## A timeout can be specified in milliseconds, if data is not received within ## the specified time a TimeoutError exception will be raised. ## - ## The `maxLength` parameter determines the maximum amount of characters + ## The ``maxLength`` parameter determines the maximum amount of characters ## that can be read. The result is truncated after that. ## - ## .. warning:: Only the `SafeDisconn` flag is currently supported. + ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. template addNLIfEmpty() = if line.len == 0: - line.add("\c\L") + line.string.add("\c\L") template raiseSockError() {.dirty.} = let lastError = getSocketError(socket) if flags.isDisconnectionError(lastError): - setLen(line, 0) + setLen(line.string, 0) socket.socketError(n, lastError = lastError, flags = flags) - return var waited: Duration - setLen(line, 0) + setLen(line.string, 0) while true: var c: char discard waitFor(socket, waited, timeout, 1, "readLine") var n = recv(socket, addr(c), 1) if n < 0: raiseSockError() - elif n == 0: setLen(line, 0); return + elif n == 0: setLen(line.string, 0); return if c == '\r': discard waitFor(socket, waited, timeout, 1, "readLine") n = peekChar(socket, c) @@ -1625,84 +1541,77 @@ proc readLine*(socket: Socket, line: var string, timeout = -1, elif c == '\L': addNLIfEmpty() return - add(line, c) + add(line.string, c) # Verify that this isn't a DOS attack: #3847. - if line.len > maxLength: break + if line.string.len > maxLength: break proc recvLine*(socket: Socket, timeout = -1, flags = {SocketFlag.SafeDisconn}, - maxLength = MaxLineLength): string = - ## Reads a line of data from `socket`. + maxLength = MaxLineLength): TaintedString = + ## Reads a line of data from ``socket``. ## - ## If a full line is read `\r\L` is not - ## added to the result, however if solely `\r\L` is read then the result + ## If a full line is read ``\r\L`` is not + ## added to the result, however if solely ``\r\L`` is read then the result ## will be set to it. ## - ## If the socket is disconnected, the result will be set to `""`. + ## If the socket is disconnected, the result will be set to ``""``. ## ## An OSError exception will be raised in the case of a socket error. ## ## A timeout can be specified in milliseconds, if data is not received within ## the specified time a TimeoutError exception will be raised. ## - ## The `maxLength` parameter determines the maximum amount of characters + ## The ``maxLength`` parameter determines the maximum amount of characters ## that can be read. The result is truncated after that. ## - ## .. warning:: Only the `SafeDisconn` flag is currently supported. - result = "" + ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. + result = "".TaintedString readLine(socket, result, timeout, flags, maxLength) -proc recvFrom*[T: string | IpAddress](socket: Socket, data: var string, length: int, - address: var T, port: var Port, flags = 0'i32): int {. +proc recvFrom*(socket: Socket, data: var string, length: int, + address: var string, port: var Port, flags = 0'i32): int {. tags: [ReadIOEffect].} = - ## Receives data from `socket`. This function should normally be used with - ## connection-less sockets (UDP sockets). The source address of the data - ## packet is stored in the `address` argument as either a string or an IpAddress. + ## Receives data from ``socket``. This function should normally be used with + ## connection-less sockets (UDP sockets). ## ## If an error occurs an OSError exception will be raised. Otherwise the return ## value will be the length of data received. ## - ## .. warning:: This function does not yet have a buffered implementation, - ## so when `socket` is buffered the non-buffered implementation will be - ## used. Therefore if `socket` contains something in its buffer this - ## function will make no effort to return it. - template adaptRecvFromToDomain(sockAddress: untyped, domain: Domain) = - var addrLen = SockLen(sizeof(sockAddress)) + ## **Warning:** This function does not yet have a buffered implementation, + ## so when ``socket`` is buffered the non-buffered implementation will be + ## used. Therefore if ``socket`` contains something in its buffer this + ## function will make no effort to return it. + template adaptRecvFromToDomain(domain: Domain) = + var addrLen = sizeof(sockAddress).SockLen result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) if result != -1: data.setLen(result) - - when typeof(address) is string: - address = getAddrString(cast[ptr SockAddr](addr(sockAddress))) - when domain == AF_INET6: - port = ntohs(sockAddress.sin6_port).Port - else: - port = ntohs(sockAddress.sin_port).Port + address = getAddrString(cast[ptr SockAddr](addr(sockAddress))) + when domain == AF_INET6: + port = ntohs(sockAddress.sin6_port).Port else: - data.setLen(result) - sockAddress.fromSockAddr(addrLen, address, port) + port = ntohs(sockAddress.sin_port).Port else: raiseOSError(osLastError()) assert(socket.protocol != IPPROTO_TCP, "Cannot `recvFrom` on a TCP socket") # TODO: Buffered sockets data.setLen(length) - case socket.domain of AF_INET6: var sockAddress: Sockaddr_in6 - adaptRecvFromToDomain(sockAddress, AF_INET6) + adaptRecvFromToDomain(AF_INET6) of AF_INET: var sockAddress: Sockaddr_in - adaptRecvFromToDomain(sockAddress, AF_INET) + adaptRecvFromToDomain(AF_INET) else: raise newException(ValueError, "Unknown socket address family") proc skip*(socket: Socket, size: int, timeout = -1) = - ## Skips `size` amount of bytes. + ## Skips ``size`` amount of bytes. ## ## An optional timeout can be specified in milliseconds, if skipping the ## bytes takes longer than specified a TimeoutError exception will be raised. @@ -1720,7 +1629,7 @@ proc send*(socket: Socket, data: pointer, size: int): int {. tags: [WriteIOEffect].} = ## Sends data to a socket. ## - ## **Note**: This is a low-level version of `send`. You likely should use + ## **Note**: This is a low-level version of ``send``. You likely should use ## the version below. assert(not socket.isClosed, "Cannot `send` on a closed socket") when defineSsl: @@ -1736,53 +1645,31 @@ proc send*(socket: Socket, data: pointer, size: int): int {. result = send(socket.fd, data, size, int32(MSG_NOSIGNAL)) proc send*(socket: Socket, data: string, - flags = {SocketFlag.SafeDisconn}, maxRetries = 100) {.tags: [WriteIOEffect].} = - ## Sends data to a socket. Will try to send all the data by handling interrupts - ## and incomplete writes up to `maxRetries`. - var written = 0 - var attempts = 0 - while data.len - written > 0: - let sent = send(socket, cstring(data), data.len) + flags = {SocketFlag.SafeDisconn}) {.tags: [WriteIOEffect].} = + ## sends data to a socket. + let sent = send(socket, cstring(data), data.len) + if sent < 0: + let lastError = osLastError() + socketError(socket, lastError = lastError, flags = flags) - if sent < 0: - let lastError = osLastError() - let isBlockingErr = - when defined(nimdoc): - false - elif useWinVersion: - lastError.int32 == WSAEINTR or - lastError.int32 == WSAEWOULDBLOCK - else: - lastError.int32 == EINTR or - lastError.int32 == EWOULDBLOCK or - lastError.int32 == EAGAIN - - if not isBlockingErr: - let lastError = osLastError() - socketError(socket, lastError = lastError, flags = flags) - else: - attempts.inc() - if attempts > maxRetries: - raiseOSError(osLastError(), "Could not send all data.") - else: - written.inc(sent) + if sent != data.len: + raiseOSError(osLastError(), "Could not send all data.") template `&=`*(socket: Socket; data: typed) = ## an alias for 'send'. send(socket, data) proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} = - ## Safe alternative to `send`. Does not raise an OSError when an error occurs, - ## and instead returns `false` on failure. + ## Safe alternative to ``send``. Does not raise an OSError when an error occurs, + ## and instead returns ``false`` on failure. result = send(socket, cstring(data), data.len) == data.len proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, size: int, af: Domain = AF_INET, flags = 0'i32) {. tags: [WriteIOEffect].} = - ## This proc sends `data` to the specified `address`, + ## This proc sends ``data`` to the specified ``address``, ## which may be an IP address or a hostname, if a hostname is specified - ## this function will try each IP of that hostname. This function - ## should normally be used with connection-less sockets (UDP sockets). + ## this function will try each IP of that hostname. ## ## If an error occurs an OSError exception will be raised. ## @@ -1806,51 +1693,25 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, it = it.ai_next let osError = osLastError() - freeAddrInfo(aiList) + freeaddrinfo(aiList) if not success: raiseOSError(osError) proc sendTo*(socket: Socket, address: string, port: Port, data: string) {.tags: [WriteIOEffect].} = - ## This proc sends `data` to the specified `address`, + ## This proc sends ``data`` to the specified ``address``, ## which may be an IP address or a hostname, if a hostname is specified ## this function will try each IP of that hostname. ## - ## Generally for use with connection-less (UDP) sockets. - ## ## If an error occurs an OSError exception will be raised. ## - ## This is the high-level version of the above `sendTo` function. + ## This is the high-level version of the above ``sendTo`` function. socket.sendTo(address, port, cstring(data), data.len, socket.domain) -proc sendTo*(socket: Socket, address: IpAddress, port: Port, - data: string, flags = 0'i32): int {. - discardable, tags: [WriteIOEffect].} = - ## This proc sends `data` to the specified `IpAddress` and returns - ## the number of bytes written. - ## - ## Generally for use with connection-less (UDP) sockets. - ## - ## If an error occurs an OSError exception will be raised. - ## - ## This is the high-level version of the above `sendTo` function. - assert(socket.protocol != IPPROTO_TCP, "Cannot `sendTo` on a TCP socket") - assert(not socket.isClosed, "Cannot `sendTo` on a closed socket") - - var sa: Sockaddr_storage - var sl: SockLen - toSockAddr(address, port, sa, sl) - result = sendto(socket.fd, cstring(data), data.len().cint, flags.cint, - cast[ptr SockAddr](addr sa), sl) - - if result == -1'i32: - let osError = osLastError() - raiseOSError(osError) - proc isSsl*(socket: Socket): bool = - ## Determines whether `socket` is a SSL socket. + ## Determines whether ``socket`` is a SSL socket. when defineSsl: result = socket.isSsl else: @@ -1859,16 +1720,6 @@ proc isSsl*(socket: Socket): bool = proc getFd*(socket: Socket): SocketHandle = return socket.fd ## Returns the socket's file descriptor -when defined(zephyr) or defined(nimNetSocketExtras): # Remove in future - proc getDomain*(socket: Socket): Domain = return socket.domain - ## Returns the socket's domain - - proc getType*(socket: Socket): SockType = return socket.sockType - ## Returns the socket's type - - proc getProtocol*(socket: Socket): Protocol = return socket.protocol - ## Returns the socket's protocol - when defined(nimHasStyleChecks): {.push styleChecks: off.} @@ -1920,18 +1771,14 @@ proc `==`*(lhs, rhs: IpAddress): bool = proc `$`*(address: IpAddress): string = ## Converts an IpAddress into the textual representation + result = "" case address.family of IpAddressFamily.IPv4: - result = newStringOfCap(15) - result.addInt address.address_v4[0] - result.add '.' - result.addInt address.address_v4[1] - result.add '.' - result.addInt address.address_v4[2] - result.add '.' - result.addInt address.address_v4[3] + for i in 0 .. 3: + if i != 0: + result.add('.') + result.add($address.address_v4[i]) of IpAddressFamily.IPv6: - result = newStringOfCap(39) var currentZeroStart = -1 currentZeroCount = 0 @@ -1989,9 +1836,9 @@ proc `$`*(address: IpAddress): string = proc dial*(address: string, port: Port, protocol = IPPROTO_TCP, buffered = true): owned(Socket) {.tags: [ReadIOEffect, WriteIOEffect].} = - ## Establishes connection to the specified `address`:`port` pair via the + ## Establishes connection to the specified ``address``:``port`` pair via the ## specified protocol. The procedure iterates through possible - ## resolutions of the `address` until it succeeds, meaning that it + ## resolutions of the ``address`` until it succeeds, meaning that it ## seamlessly works with both IPv4 and IPv6. ## Returns Socket ready to send or receive data. let sockType = protocol.toSockType() @@ -2025,7 +1872,7 @@ proc dial*(address: string, port: Port, # network system problem (e.g. not enough FDs), and not an unreachable # address. let err = osLastError() - freeAddrInfo(aiList) + freeaddrinfo(aiList) closeUnusedFds() raiseOSError(err) fdPerDomain[ord(domain)] = lastFd @@ -2034,24 +1881,24 @@ proc dial*(address: string, port: Port, break lastError = osLastError() it = it.ai_next - freeAddrInfo(aiList) + freeaddrinfo(aiList) closeUnusedFds(ord(domain)) if success: - result = newSocket(lastFd, domain, sockType, protocol, buffered) + result = newSocket(lastFd, domain, sockType, protocol) elif lastError != 0.OSErrorCode: raiseOSError(lastError) else: raise newException(IOError, "Couldn't resolve address: " & address) proc connect*(socket: Socket, address: string, - 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 + port = Port(0)) {.tags: [ReadIOEffect].} = + ## 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 ## not do it. ## - ## If `socket` is an SSL socket a handshake will be automatically performed. + ## If ``socket`` is an SSL socket a handshake will be automatically performed. var aiList = getAddrInfo(address, port, socket.domain) # try all possibilities: var success = false @@ -2064,7 +1911,7 @@ proc connect*(socket: Socket, address: string, else: lastError = osLastError() it = it.ai_next - freeAddrInfo(aiList) + freeaddrinfo(aiList) if not success: raiseOSError(lastError) when defineSsl: @@ -2084,13 +1931,13 @@ proc connect*(socket: Socket, address: string, proc connectAsync(socket: Socket, name: string, port = Port(0), af: Domain = AF_INET) {.tags: [ReadIOEffect].} = - ## A variant of `connect` for non-blocking sockets. + ## A variant of ``connect`` for non-blocking sockets. ## ## This procedure will immediately return, it will not block until a connection ## is made. It is up to the caller to make sure the connection has been established - ## by checking (using `select`) whether the socket is writeable. + ## by checking (using ``select``) whether the socket is writeable. ## - ## **Note**: For SSL sockets, the `handshake` procedure must be called + ## **Note**: For SSL sockets, the ``handshake`` procedure must be called ## whenever the socket successfully connects to a server. var aiList = getAddrInfo(name, port, af) # try all possibilities: @@ -2116,19 +1963,24 @@ proc connectAsync(socket: Socket, name: string, port = Port(0), it = it.ai_next - freeAddrInfo(aiList) + freeaddrinfo(aiList) if not success: raiseOSError(lastError) proc connect*(socket: Socket, address: string, port = Port(0), - timeout: int) {.tags: [ReadIOEffect, WriteIOEffect, RootEffect].} = - ## Connects to server as specified by `address` on port specified by `port`. + timeout: int) {.tags: [ReadIOEffect, WriteIOEffect].} = + ## Connects to server as specified by ``address`` on port specified by ``port``. ## - ## The `timeout` parameter specifies the time in milliseconds to allow for + ## The ``timeout`` parameter specifies the time in milliseconds to allow for ## the connection to the server to be made. + ## + ## **Warning:** This procedure appears to be broken for SSL connections as of + ## Nim v1.0.2. Consider using the other `connect` procedure. See + ## https://github.com/nim-lang/Nim/issues/15215 for more info. socket.fd.setBlocking(false) socket.connectAsync(address, port, socket.domain) - if timeoutWrite(socket.fd, timeout) != 1: + var s = @[socket.fd] + if selectWrite(s, timeout) != 1: raise newException(TimeoutError, "Call to 'connect' timed out.") else: let res = getSockOptInt(socket.fd, SOL_SOCKET, SO_ERROR) @@ -2137,18 +1989,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), when defineSsl and not defined(nimdoc): if socket.isSsl: socket.fd.setBlocking(true) - # RFC3546 for SNI specifies that IP addresses are not allowed. - if not isIpAddress(address): - # Discard result in case OpenSSL version doesn't support SNI, or we're - # not using TLSv1+ - discard SSL_set_tlsext_host_name(socket.sslHandle, address) - - ErrClearError() - let ret = SSL_connect(socket.sslHandle) - socketError(socket, ret) - when not defined(nimDisableCertificateValidation): - if not isIpAddress(address): - socket.checkCertName(address) + doAssert socket.gotHandshake() socket.fd.setBlocking(true) proc getPrimaryIPAddr*(dest = parseIpAddress("8.8.8.8")): IpAddress = @@ -2159,8 +2000,10 @@ proc getPrimaryIPAddr*(dest = parseIpAddress("8.8.8.8")): IpAddress = ## ## Supports IPv4 and v6. ## Raises OSError if external networking is not set up. - runnableExamples("-r:off"): - echo getPrimaryIPAddr() # "192.168.1.2" + ## + ## .. code-block:: Nim + ## echo $getPrimaryIPAddr() # "192.168.1.2" + let socket = if dest.family == IpAddressFamily.IPv4: newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim index 9849e42db..57ea466e5 100644 --- a/lib/pure/nimprof.nim +++ b/lib/pure/nimprof.nim @@ -8,23 +8,20 @@ # ## Profiling support for Nim. This is an embedded profiler that requires -## `--profiler:on`. You only need to import this module to get a profiling -## report at program exit. See `Embedded Stack Trace Profiler `_ -## for usage. +## ``--profiler:on``. You only need to import this module to get a profiling +## report at program exit. when not defined(profiler) and not defined(memProfiler): {.error: "Profiling support is turned off! Enable profiling by passing `--profiler:on --stackTrace:on` to the compiler (see the Nim Compiler User Guide for more options).".} -{.used.} +when defined(nimHasUsed): + {.used.} # We don't want to profile the profiling code ... {.push profiler: off.} import hashes, algorithm, strutils, tables, sets -when defined(nimPreviewSlimSystem): - import std/[syncio, sysatomics] - when not defined(memProfiler): include "system/timers" @@ -124,13 +121,13 @@ when defined(memProfiler): var gTicker {.threadvar.}: int - proc requestedHook(): bool {.nimcall.} = + proc requestedHook(): bool {.nimcall, locks: 0.} = if gTicker == 0: gTicker = SamplingInterval result = true dec gTicker - proc hook(st: StackTrace, size: int) {.nimcall.} = + proc hook(st: StackTrace, size: int) {.nimcall, locks: 0.} = when defined(ignoreAllocationSize): hookAux(st, 1) else: @@ -142,7 +139,7 @@ else: gTicker: int # we use an additional counter to # avoid calling 'getTicks' too frequently - proc requestedHook(): bool {.nimcall.} = + proc requestedHook(): bool {.nimcall, locks: 0.} = if interval == 0: result = true elif gTicker == 0: gTicker = 500 @@ -151,7 +148,7 @@ else: else: dec gTicker - proc hook(st: StackTrace) {.nimcall.} = + proc hook(st: StackTrace) {.nimcall, locks: 0.} = #echo "profiling! ", interval if interval == 0: hookAux(st, 1) @@ -225,9 +222,8 @@ proc enableProfiling*() = system.profilingRequestedHook = requestedHook when declared(system.StackTrace): - import std/exitprocs system.profilingRequestedHook = requestedHook system.profilerHook = hook - addExitProc(writeProfile) + addQuitProc(writeProfile) {.pop.} diff --git a/lib/pure/nimtracker.nim b/lib/pure/nimtracker.nim new file mode 100644 index 000000000..a66dfc2ea --- /dev/null +++ b/lib/pure/nimtracker.nim @@ -0,0 +1,88 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Memory tracking support for Nim. + +when not defined(memTracker) and not isMainModule: + {.error: "Memory tracking support is turned off!".} + +{.push memtracker: off.} +# we import the low level wrapper and are careful not to use Nim's +# memory manager for anything here. +import sqlite3 + +var + dbHandle: PSqlite3 + insertStmt {.threadvar.}: Pstmt + +const insertQuery = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)" + +template sbind(x: int; value) = + when value is cstring: + let ret = insertStmt.bindText(x, value, value.len.int32, SQLITE_TRANSIENT) + if ret != SQLITE_OK: + quit "could not bind value" + else: + let ret = insertStmt.bindInt64(x, value) + if ret != SQLITE_OK: + quit "could not bind value" + +when defined(memTracker): + proc logEntries(log: TrackLog) {.nimcall, locks: 0, tags: [], gcsafe.} = + if insertStmt.isNil: + if prepare_v2(dbHandle, insertQuery, + insertQuery.len, insertStmt, nil) != SQLITE_OK: + quit "could not bind query to insertStmt " & $sqlite3.errmsg(dbHandle) + for i in 0..log.count-1: + var success = false + let e = log.data[i] + discard sqlite3.reset(insertStmt) + discard clearBindings(insertStmt) + sbind 1, e.op + sbind(2, cast[int](e.address)) + sbind 3, e.size + sbind 4, e.file + sbind 5, e.line + if step(insertStmt) == SQLITE_DONE: + success = true + if not success: + quit "could not write to database! " & $sqlite3.errmsg(dbHandle) + +proc execQuery(q: string) = + var s: Pstmt + if prepare_v2(dbHandle, q, q.len.int32, s, nil) == SQLITE_OK: + discard step(s) + if finalize(s) != SQLITE_OK: + quit "could not finalize " & $sqlite3.errmsg(dbHandle) + else: + quit "could not prepare statement " & $sqlite3.errmsg(dbHandle) + +proc setupDb() = + execQuery """create table if not exists tracking( + id integer primary key, + op varchar not null, + address integer not null, + size integer not null, + file varchar not null, + line integer not null + )""" + execQuery "delete from tracking" + +if sqlite3.open("memtrack.db", dbHandle) == SQLITE_OK: + setupDb() + const query = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)" + if prepare_v2(dbHandle, insertQuery, + insertQuery.len, insertStmt, nil) == SQLITE_OK: + when defined(memTracker): + setTrackLogger logEntries + else: + quit "could not prepare statement B " & $sqlite3.errmsg(dbHandle) +else: + quit "could not setup sqlite " & $sqlite3.errmsg(dbHandle) +{.pop.} diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index ad8eeefd7..d3a7c4fb6 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -9,91 +9,92 @@ ## 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. +## produce a globally distributed unique ID. This implementation was extracted +## from the Mongodb interface and it thus binary compatible with a Mongo OID. ## -## This implementation calls `initRand()` for the first call of -## `genOid`. +## This implementation calls ``math.randomize()`` for the first call of +## ``genOid``. -import hashes, times, endians, random -from std/private/decode_helpers import handleHexChar - -when defined(nimPreviewSlimSystem): - import std/sysatomics +import hashes, times, endians type - Oid* = object ## An OID. - time: int64 - fuzz: int32 - count: int32 + Oid* = object ## an OID + time: int32 ## + fuzz: int32 ## + count: int32 ## -proc `==`*(oid1: Oid, oid2: Oid): bool {.inline.} = - ## Compares two OIDs for equality. - result = (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and +proc `==`*(oid1: Oid, oid2: Oid): bool = + ## Compare two Mongo Object IDs for equality + return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count) proc hash*(oid: Oid): Hash = - ## Generates the hash of an OID for use in hashtables. + ## Generate hash of Oid for use in hashtables var h: Hash = 0 h = h !& hash(oid.time) h = h !& hash(oid.fuzz) h = h !& hash(oid.count) result = !$h -proc hexbyte*(hex: char): int {.inline.} = - result = handleHexChar(hex) +proc hexbyte*(hex: char): int = + case hex + of '0'..'9': result = (ord(hex) - ord('0')) + of 'a'..'f': result = (ord(hex) - ord('a') + 10) + of 'A'..'F': result = (ord(hex) - ord('A') + 10) + else: discard proc parseOid*(str: cstring): Oid = - ## Parses an OID. - var bytes = cast[cstring](cast[pointer](cast[int](addr(result.time)) + 4)) + ## parses an OID. + var bytes = cast[cstring](addr(result.time)) var i = 0 while i < 12: bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1])) inc(i) -proc `$`*(oid: Oid): string = - ## Converts an OID to a string. +proc oidToString*(oid: Oid, str: cstring) = const hex = "0123456789abcdef" - - result.setLen 24 - + # work around a compiler bug: + var str = str var o = oid - var bytes = cast[cstring](cast[pointer](cast[int](addr(o)) + 4)) + var bytes = cast[cstring](addr(o)) var i = 0 while i < 12: let b = bytes[i].ord - result[2 * i] = hex[(b and 0xF0) shr 4] - result[2 * i + 1] = hex[b and 0xF] + str[2 * i] = hex[(b and 0xF0) shr 4] + str[2 * i + 1] = hex[b and 0xF] inc(i) + str[24] = '\0' -let - t = getTime().toUnix +proc `$`*(oid: Oid): string = + result = newString(24) + oidToString(oid, result) + +proc rand(): cint {.importc: "rand", header: "", nodecl.} +proc srand(seed: cint) {.importc: "srand", header: "", nodecl.} + +var t = getTime().toUnix.int32 +srand(t) var - seed = initRand(t) - incr: int = seed.rand(int.high) + incr: int = rand() + fuzz: int32 = rand() -let fuzz = cast[int32](seed.rand(high(int))) +proc genOid*(): Oid = + ## generates a new OID. + t = getTime().toUnix.int32 + var i = int32(atomicInc(incr)) - -template genOid(result: var Oid, incr: var int, fuzz: int32) = - var time = getTime().toUnix - var i = cast[int32](atomicInc(incr)) - - bigEndian64(addr result.time, addr(time)) + bigEndian32(addr result.time, addr(t)) result.fuzz = fuzz bigEndian32(addr result.count, addr(i)) -proc genOid*(): Oid = - ## Generates a new OID. - runnableExamples: - doAssert ($genOid()).len == 24 - runnableExamples("-r:off"): - echo $genOid() # for example, "5fc7f546ddbbc84800006aaf" - genOid(result, incr, fuzz) - proc generatedTime*(oid: Oid): Time = - ## Returns the generated timestamp of the OID. - var tmp: int64 + ## returns the generated timestamp of the OID. + var tmp: int32 var dummy = oid.time - bigEndian64(addr(tmp), addr(dummy)) + bigEndian32(addr(tmp), addr(dummy)) result = fromUnix(tmp) + +when not defined(testing) and isMainModule: + let xo = genOid() + echo xo.generatedTime diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 9dc4e096b..195da1008 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -7,24 +7,22 @@ # distribution, for details about the copyright. # -##[ -This module implements types which encapsulate an optional value. - -A value of type `Option[T]` either contains a value `x` (represented as -`some(x)`) or is empty (`none(T)`). - -This can be useful when you have a value that can be present or not. The -absence of a value is often represented by `nil`, but that is not always -available, nor is it always a good solution. - - -Basic usage -=========== - -Let's start with an example: a procedure that finds the index of a character -in a string. -]## - +## This module implements types which encapsulate an optional value. +## +## A value of type `Option[T]` either contains a value `x` (represented as +## `some(x)`) or is empty (`none(T)`). +## +## This can be useful when you have a value that can be present or not. The +## absence of a value is often represented by `nil`, but it is not always +## available, nor is it always a good solution. +## +## +## Basic usage +## =========== +## +## Let's start with an example: a procedure that finds the index of a character +## in a string. +## runnableExamples: proc find(haystack: string, needle: char): Option[int] = for i, c in haystack: @@ -36,49 +34,21 @@ runnableExamples: let found = "abc".find('c') assert found.isSome and found.get() == 2 -##[ -The `get` operation demonstrated above returns the underlying value, or -raises `UnpackDefect` if there is no value. Note that `UnpackDefect` -inherits from `system.Defect` and should therefore never be caught. -Instead, rely on checking if the option contains a value with the -`isSome <#isSome,Option[T]>`_ and `isNone <#isNone,Option[T]>`_ procs. +## The `get` operation demonstrated above returns the underlying value, or +## raises `UnpackDefect` if there is no value. Note that `UnpackDefect` +## inherits from `system.Defect`, and should therefore never be caught. +## Instead, rely on checking if the option contains a value with +## `isSome <#isSome,Option[T]>`_ and `isNone <#isNone,Option[T]>`_ procs. +## +## How to deal with an absence of a value: - -Pattern matching -================ - -.. note:: This requires the [fusion](https://github.com/nim-lang/fusion) package. - -[fusion/matching](https://nim-lang.github.io/fusion/src/fusion/matching.html) -supports pattern matching on `Option`s, with the `Some()` and -`None()` patterns. - -.. code-block:: nim - {.experimental: "caseStmtMacros".} - - import fusion/matching - - case some(42) - of Some(@a): - assert a == 42 - of None(): - assert false - - assertMatch(some(some(none(int))), Some(Some(None()))) -]## -# xxx pending https://github.com/timotheecour/Nim/issues/376 use `runnableExamples` and `whichModule` - -when defined(nimHasEffectsOf): - {.experimental: "strictEffects".} -else: - {.pragma: effectsOf.} +runnableExamples: + let result = none(int) + # It has no value: + assert(result.isNone) import typetraits -when defined(nimPreviewSlimSystem): - import std/assertions - - when (NimMajor, NimMinor) >= (1, 1): type SomePointer = ref | ptr | pointer | proc @@ -88,9 +58,7 @@ else: type Option*[T] = object - ## An optional type that may or may not contain a value of type `T`. - ## When `T` is a a pointer type (`ptr`, `pointer`, `ref` or `proc`), - ## `none(T)` is represented as `nil`. + ## An optional type that stores its value and state separately in a boolean. when T is SomePointer: val: T else: @@ -100,41 +68,45 @@ type UnpackDefect* = object of Defect UnpackError* {.deprecated: "See corresponding Defect".} = UnpackDefect -proc option*[T](val: sink T): Option[T] {.inline.} = - ## Can be used to convert a pointer type (`ptr`, `pointer`, `ref` or `proc`) to an option type. - ## It converts `nil` to `none(T)`. When `T` is no pointer type, this is equivalent to `some(val)`. +proc option*[T](val: T): Option[T] {.inline.} = + ## Can be used to convert a pointer type (`ptr` or `ref` or `proc`) to an option type. + ## It converts `nil` to `None`. ## - ## **See also:** - ## * `some proc <#some,T>`_ - ## * `none proc <#none,typedesc>`_ + ## See also: + ## * `some <#some,T>`_ + ## * `none <#none,typedesc>`_ runnableExamples: type Foo = ref object a: int b: string - - assert option[Foo](nil).isNone - assert option(42).isSome + var c: Foo + assert c.isNil + var d = option(c) + assert d.isNone result.val = val when T isnot SomePointer: result.has = true -proc some*[T](val: sink T): Option[T] {.inline.} = +proc some*[T](val: T): Option[T] {.inline.} = ## Returns an `Option` that has the value `val`. ## - ## **See also:** - ## * `option proc <#option,T>`_ - ## * `none proc <#none,typedesc>`_ - ## * `isSome proc <#isSome,Option[T]>`_ + ## See also: + ## * `option <#option,T>`_ + ## * `none <#none,typedesc>`_ + ## * `isSome <#isSome,Option[T]>`_ runnableExamples: - let a = some("abc") - - assert a.isSome + var + a = some("abc") + b = some(42) + assert $type(a) == "Option[system.string]" + assert b.isSome assert a.get == "abc" + assert $b == "Some(42)" when T is SomePointer: - assert not val.isNil + assert(not val.isNil) result.val = val else: result.has = true @@ -143,29 +115,30 @@ proc some*[T](val: sink T): Option[T] {.inline.} = proc none*(T: typedesc): Option[T] {.inline.} = ## Returns an `Option` for this type that has no value. ## - ## **See also:** - ## * `option proc <#option,T>`_ - ## * `some proc <#some,T>`_ - ## * `isNone proc <#isNone,Option[T]>`_ + ## See also: + ## * `option <#option,T>`_ + ## * `some <#some,T>`_ + ## * `isNone <#isNone,Option[T]>`_ runnableExamples: - assert none(int).isNone + var a = none(int) + assert a.isNone + assert $type(a) == "Option[system.int]" # the default is the none type discard proc none*[T]: Option[T] {.inline.} = - ## Alias for `none(T) <#none,typedesc>`_. + ## Alias for `none(T) proc <#none,typedesc>`_. none(T) proc isSome*[T](self: Option[T]): bool {.inline.} = ## Checks if an `Option` contains a value. - ## - ## **See also:** - ## * `isNone proc <#isNone,Option[T]>`_ - ## * `some proc <#some,T>`_ runnableExamples: - assert some(42).isSome - assert not none(string).isSome + var + a = some(42) + b = none(string) + assert a.isSome + assert not b.isSome when T is SomePointer: not self.val.isNil @@ -174,40 +147,44 @@ proc isSome*[T](self: Option[T]): bool {.inline.} = proc isNone*[T](self: Option[T]): bool {.inline.} = ## Checks if an `Option` is empty. - ## - ## **See also:** - ## * `isSome proc <#isSome,Option[T]>`_ - ## * `none proc <#none,typedesc>`_ runnableExamples: - assert not some(42).isNone - assert none(string).isNone - + var + a = some(42) + b = none(string) + assert not a.isNone + assert b.isNone when T is SomePointer: self.val.isNil else: not self.has proc get*[T](self: Option[T]): lent T {.inline.} = - ## Returns the content of an `Option`. If it has no value, - ## an `UnpackDefect` exception is raised. + ## Returns contents of an `Option`. If it is `None`, then an exception is + ## thrown. ## - ## **See also:** - ## * `get proc <#get,Option[T],T>`_ with a default return value + ## See also: + ## * `get proc <#get,Option[T],T>`_ with the default return value runnableExamples: - assert some(42).get == 42 + let + a = some(42) + b = none(string) + assert a.get == 42 doAssertRaises(UnpackDefect): - echo none(string).get + echo b.get if self.isNone: raise newException(UnpackDefect, "Can't obtain a value from a `none`") result = self.val proc get*[T](self: Option[T], otherwise: T): T {.inline.} = - ## Returns the content of the `Option` or `otherwise` if - ## the `Option` has no value. + ## Returns the contents of the `Option` or an `otherwise` value if + ## the `Option` is `None`. runnableExamples: - assert some(42).get(9999) == 42 - assert none(int).get(9999) == 9999 + var + a = some(42) + b = none(int) + assert a.get(9999) == 42 + assert b.get(9999) == 9999 if self.isSome: self.val @@ -215,14 +192,13 @@ proc get*[T](self: Option[T], otherwise: T): T {.inline.} = otherwise proc get*[T](self: var Option[T]): var T {.inline.} = - ## Returns the content of the `var Option` mutably. If it has no value, - ## an `UnpackDefect` exception is raised. + ## Returns contents of the `var Option`. If it is `None`, then an exception + ## is thrown. runnableExamples: - var + let a = some(42) b = none(string) - inc(a.get) - assert a.get == 43 + assert a.get == 42 doAssertRaises(UnpackDefect): echo b.get @@ -230,68 +206,77 @@ 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, effectsOf: callback.} = +proc map*[T](self: Option[T], callback: proc (input: T)) {.inline.} = ## Applies a `callback` function to the value of the `Option`, if it has one. ## - ## **See also:** + ## See also: ## * `map proc <#map,Option[T],proc(T)_2>`_ for a version with a callback ## which returns a value + ## * `filter proc <#filter,Option[T],proc(T)>`_ runnableExamples: var d = 0 proc saveDouble(x: int) = - d = 2 * x + d = 2*x - none(int).map(saveDouble) + let + a = some(42) + b = none(int) + + b.map(saveDouble) assert d == 0 - some(42).map(saveDouble) + a.map(saveDouble) assert d == 84 if self.isSome: callback(self.val) -proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] {.inline, effectsOf: callback.} = +proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] {.inline.} = ## Applies a `callback` function to the value of the `Option` and returns an ## `Option` containing the new value. ## - ## If the `Option` has no value, `none(R)` will be returned. + ## If the `Option` is `None`, `None` of the return type of the `callback` + ## will be returned. ## - ## **See also:** - ## * `map proc <#map,Option[T],proc(T)>`_ - ## * `flatMap proc <#flatMap,Option[T],proc(T)>`_ for a version with a - ## callback that returns an `Option` + ## See also: + ## * `flatMap proc <#flatMap,Option[A],proc(A)>`_ for a version with a + ## callback which returns an `Option` + ## * `filter proc <#filter,Option[T],proc(T)>`_ runnableExamples: + var + a = some(42) + b = none(int) + proc isEven(x: int): bool = x mod 2 == 0 - assert some(42).map(isEven) == some(true) - assert none(int).map(isEven) == none(bool) + assert $(a.map(isEven)) == "Some(true)" + assert $(b.map(isEven)) == "None[bool]" if self.isSome: some[R](callback(self.val)) else: none(R) -proc flatten*[T](self: Option[Option[T]]): Option[T] {.inline.} = +proc flatten*[A](self: Option[Option[A]]): Option[A] {.inline.} = ## Remove one level of structure in a nested `Option`. - ## - ## **See also:** - ## * `flatMap proc <#flatMap,Option[T],proc(T)>`_ runnableExamples: - assert flatten(some(some(42))) == some(42) - assert flatten(none(Option[int])) == none(int) + let a = some(some(42)) + assert $flatten(a) == "Some(42)" if self.isSome: self.val else: - none(T) + none(A) -proc flatMap*[T, R](self: Option[T], - 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. +proc flatMap*[A, B](self: Option[A], + callback: proc (input: A): Option[B]): Option[B] {.inline.} = + ## Applies a `callback` function to the value of the `Option` and returns an + ## `Option` containing the new value. ## - ## If the `Option` has no value, `none(R)` will be returned. + ## If the `Option` is `None`, `None` of the return type of the `callback` + ## will be returned. ## - ## This is similar to `map`, with the difference that the `callback` returns an + ## Similar to `map`, with the difference that the `callback` returns an ## `Option`, not a raw value. This allows multiple procs with a ## signature of `A -> Option[B]` to be chained together. ## @@ -301,40 +286,47 @@ proc flatMap*[T, R](self: Option[T], runnableExamples: proc doublePositives(x: int): Option[int] = if x > 0: - some(2 * x) + return some(2*x) else: - none(int) - - assert some(42).flatMap(doublePositives) == some(84) - assert none(int).flatMap(doublePositives) == none(int) - assert some(-11).flatMap(doublePositives) == none(int) + return none(int) + let + a = some(42) + b = none(int) + c = some(-11) + assert a.flatMap(doublePositives) == some(84) + assert b.flatMap(doublePositives) == none(int) + assert c.flatMap(doublePositives) == none(int) map(self, callback).flatten() -proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] {.inline, effectsOf: callback.} = +proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] {.inline.} = ## Applies a `callback` to the value of the `Option`. ## - ## If the `callback` returns `true`, the option is returned as `some`. - ## If it returns `false`, it is returned as `none`. + ## If the `callback` returns `true`, the option is returned as `Some`. + ## If it returns `false`, it is returned as `None`. ## - ## **See also:** + ## See also: + ## * `map proc <#map,Option[T],proc(T)_2>`_ ## * `flatMap proc <#flatMap,Option[A],proc(A)>`_ runnableExamples: proc isEven(x: int): bool = x mod 2 == 0 - - assert some(42).filter(isEven) == some(42) - assert none(int).filter(isEven) == none(int) - assert some(-11).filter(isEven) == none(int) + let + a = some(42) + b = none(int) + c = some(-11) + assert a.filter(isEven) == some(42) + assert b.filter(isEven) == none(int) + assert c.filter(isEven) == none(int) if self.isSome and not callback(self.val): none(T) else: self -proc `==`*[T](a, b: Option[T]): bool {.inline.} = - ## Returns `true` if both `Option`s are `none`, - ## or if they are both `some` and have equal values. +proc `==`*(a, b: Option): bool {.inline.} = + ## Returns `true` if both `Option`s are `None`, + ## or if they are both `Some` and have equal values. runnableExamples: let a = some(42) @@ -346,35 +338,27 @@ proc `==`*[T](a, b: Option[T]): bool {.inline.} = assert b == d assert not (a == b) - when T is SomePointer: - a.val == b.val - else: - (a.isSome and b.isSome and a.val == b.val) or (a.isNone and b.isNone) + (a.isSome and b.isSome and a.val == b.val) or (not a.isSome and not b.isSome) proc `$`*[T](self: Option[T]): string = ## Get the string representation of the `Option`. - runnableExamples: - assert $some(42) == "some(42)" - assert $none(int) == "none(int)" - + ## + ## If the `Option` has a value, the result will be `Some(x)` where `x` + ## is the string representation of the contained value. + ## If the `Option` does not have a value, the result will be `None[T]` + ## where `T` is the name of the type contained in the `Option`. if self.isSome: - when defined(nimLagacyOptionsDollar): - result = "Some(" - else: - result = "some(" + result = "Some(" result.addQuoted self.val result.add ")" else: - when defined(nimLagacyOptionsDollar): - result = "None[" & name(T) & "]" - else: - result = "none(" & name(T) & ")" + result = "None[" & name(T) & "]" proc unsafeGet*[T](self: Option[T]): lent T {.inline.}= - ## Returns the value of a `some`. The behavior is undefined for `none`. + ## Returns the value of a `some`. Behavior is undefined for `none`. ## - ## **Note:** Use this only when you are **absolutely sure** the value is present - ## (e.g. after checking with `isSome <#isSome,Option[T]>`_). - ## Generally, using the `get proc <#get,Option[T]>`_ is preferred. + ## **Note:** Use it only when you are **absolutely sure** the value is present + ## (e.g. after checking `isSome <#isSome,Option[T]>`_). + ## Generally, using `get proc <#get,Option[T]>`_ is preferred. assert self.isSome result = self.val diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 0569e6cab..77499deea 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -8,61 +8,52 @@ # ## This module contains basic operating system facilities like -## retrieving environment variables, working with directories, -## running shell commands, etc. - -## .. importdoc:: symlinks.nim, appdirs.nim, dirs.nim, ospaths2.nim - -runnableExamples: - let myFile = "/path/to/my/file.nim" - assert splitPath(myFile) == (head: "/path/to/my", tail: "file.nim") - when defined(posix): - assert parentDir(myFile) == "/path/to/my" - assert splitFile(myFile) == (dir: "/path/to/my", name: "file", ext: ".nim") - assert myFile.changeFileExt("c") == "/path/to/my/file.c" +## retrieving environment variables, reading command line arguments, +## working with directories, running shell commands, etc. +## +## .. code-block:: +## import os +## +## let myFile = "/path/to/my/file.nim" +## +## let pathSplit = splitPath(myFile) +## assert pathSplit.head == "/path/to/my" +## assert pathSplit.tail == "file.nim" +## +## assert parentDir(myFile) == "/path/to/my" +## +## let fileSplit = splitFile(myFile) +## assert fileSplit.dir == "/path/to/my" +## assert fileSplit.name == "file" +## assert fileSplit.ext == ".nim" +## +## assert myFile.changeFileExt("c") == "/path/to/my/file.c" +## +## ## **See also:** ## * `osproc module `_ for process communication beyond -## `execShellCmd proc`_ +## `execShellCmd proc <#execShellCmd,string>`_ +## * `parseopt module `_ for command-line parser beyond +## `parseCmdLine proc <#parseCmdLine,string>`_ ## * `uri module `_ ## * `distros module `_ ## * `dynlib module `_ ## * `streams module `_ -import std/private/ospaths2 -export ospaths2 -import std/private/osfiles -export osfiles - -import std/private/osdirs -export osdirs - -import std/private/ossymlinks -export ossymlinks - -import std/private/osappdirs -export osappdirs - -import std/private/oscommon - -include system/inclrtl +include "system/inclrtl" import std/private/since -import std/cmdline -export cmdline - -import strutils, pathnorm - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions, widestrs] +import + strutils, pathnorm const weirdTarget = defined(nimscript) or defined(js) since (1, 1): const invalidFilenameChars* = {'/', '\\', ':', '*', '?', '"', '<', '>', '|', '^', '\0'} ## \ - ## Characters that may produce invalid filenames across Linux, Windows and Mac. - ## You can check if your filename contains any of these chars and strip them for safety. + ## Characters that may produce invalid filenames across Linux, Windows, Mac, etc. + ## You can check if your filename contains these char and strip them for safety. ## Mac bans ``':'``, Linux bans ``'/'``, Windows bans all others. invalidFilenames* = [ "CON", "PRN", "AUX", "NUL", @@ -84,7 +75,7 @@ elif defined(posix): else: {.error: "OS module not ported to your operating system!".} -when weirdTarget: +when weirdTarget and defined(nimErrorProcCanHaveBody): {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".} else: {.pragma: noWeirdTarget.} @@ -97,31 +88,889 @@ elif defined(js): else: {.pragma: noNimJs.} +proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.} -import std/oserrors -export oserrors -import std/envvars -export envvars +type + ReadEnvEffect* = object of ReadIOEffect ## Effect that denotes a read + ## from an environment variable. + WriteEnvEffect* = object of WriteIOEffect ## Effect that denotes a write + ## to an environment variable. -import std/private/osseps -export osseps + ReadDirEffect* = object of ReadIOEffect ## Effect that denotes a read + ## operation from the directory + ## structure. + WriteDirEffect* = object of WriteIOEffect ## Effect that denotes a write + ## operation to + ## the directory structure. + OSErrorCode* = distinct int32 ## Specifies an OS Error Code. +include "includes/osseps" + +proc absolutePathInternal(path: string): string {.gcsafe.} + +proc normalizePathEnd(path: var string, trailingSep = false) = + ## Ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on + ## ``trailingSep``, and taking care of edge cases: it preservers whether + ## a path is absolute or relative, and makes sure trailing sep is `DirSep`, + ## not `AltSep`. Trailing `/.` are compressed, see examples. + if path.len == 0: return + var i = path.len + while i >= 1: + if path[i-1] in {DirSep, AltSep}: dec(i) + elif path[i-1] == '.' and i >= 2 and path[i-2] in {DirSep, AltSep}: dec(i) + else: break + if trailingSep: + # foo// => foo + path.setLen(i) + # foo => foo/ + path.add DirSep + elif i > 0: + # foo// => foo + path.setLen(i) + else: + # // => / (empty case was already taken care of) + path = $DirSep + +proc normalizePathEnd(path: string, trailingSep = false): string = + ## outplace overload + runnableExamples: + when defined(posix): + assert normalizePathEnd("/lib//.//", trailingSep = true) == "/lib/" + assert normalizePathEnd("lib/./.", trailingSep = false) == "lib" + assert normalizePathEnd(".//./.", trailingSep = false) == "." + assert normalizePathEnd("", trailingSep = true) == "" # not / ! + assert normalizePathEnd("/", trailingSep = false) == "/" # not "" ! + result = path + result.normalizePathEnd(trailingSep) + +since((1, 1)): + export normalizePathEnd + +template endsWith(a: string, b: set[char]): bool = + a.len > 0 and a[^1] in b + +proc joinPathImpl(result: var string, state: var int, tail: string) = + let trailingSep = tail.endsWith({DirSep, AltSep}) or tail.len == 0 and result.endsWith({DirSep, AltSep}) + normalizePathEnd(result, trailingSep=false) + addNormalizePath(tail, result, state, DirSep) + normalizePathEnd(result, trailingSep=trailingSep) + +proc joinPath*(head, tail: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Joins two directory names to one. + ## + ## returns normalized path concatenation of `head` and `tail`, preserving + ## whether or not `tail` has a trailing slash (or, if tail if empty, whether + ## head has one). + ## + ## See also: + ## * `joinPath(varargs) proc <#joinPath,varargs[string]>`_ + ## * `/ proc <#/,string,string>`_ + ## * `splitPath proc <#splitPath,string>`_ + ## * `uri.combine proc `_ + ## * `uri./ proc `_ + runnableExamples: + when defined(posix): + assert joinPath("usr", "lib") == "usr/lib" + assert joinPath("usr", "lib/") == "usr/lib/" + assert joinPath("usr", "") == "usr" + assert joinPath("usr/", "") == "usr/" + assert joinPath("", "") == "" + assert joinPath("", "lib") == "lib" + assert joinPath("", "/lib") == "/lib" + assert joinPath("usr/", "/lib") == "usr/lib" + assert joinPath("usr/lib", "../bin") == "usr/bin" + + result = newStringOfCap(head.len + tail.len) + var state = 0 + joinPathImpl(result, state, head) + joinPathImpl(result, state, tail) + when false: + if len(head) == 0: + result = tail + elif head[len(head)-1] in {DirSep, AltSep}: + if tail.len > 0 and tail[0] in {DirSep, AltSep}: + result = head & substr(tail, 1) + else: + result = head & tail + else: + if tail.len > 0 and tail[0] in {DirSep, AltSep}: + result = head & tail + else: + result = head & DirSep & tail + +proc joinPath*(parts: varargs[string]): string {.noSideEffect, + rtl, extern: "nos$1OpenArray".} = + ## The same as `joinPath(head, tail) proc <#joinPath,string,string>`_, + ## but works with any number of directory parts. + ## + ## You need to pass at least one element or the proc + ## will assert in debug builds and crash on release builds. + ## + ## See also: + ## * `joinPath(head, tail) proc <#joinPath,string,string>`_ + ## * `/ proc <#/,string,string>`_ + ## * `/../ proc <#/../,string,string>`_ + ## * `splitPath proc <#splitPath,string>`_ + runnableExamples: + when defined(posix): + assert joinPath("a") == "a" + assert joinPath("a", "b", "c") == "a/b/c" + assert joinPath("usr/lib", "../../var", "log") == "var/log" + + var estimatedLen = 0 + for p in parts: estimatedLen += p.len + result = newStringOfCap(estimatedLen) + var state = 0 + for i in 0..high(parts): + joinPathImpl(result, state, parts[i]) + +proc `/`*(head, tail: string): string {.noSideEffect.} = + ## The same as `joinPath(head, tail) proc <#joinPath,string,string>`_. + ## + ## See also: + ## * `/../ proc <#/../,string,string>`_ + ## * `joinPath(head, tail) proc <#joinPath,string,string>`_ + ## * `joinPath(varargs) proc <#joinPath,varargs[string]>`_ + ## * `splitPath proc <#splitPath,string>`_ + ## * `uri.combine proc `_ + ## * `uri./ proc `_ + runnableExamples: + when defined(posix): + assert "usr" / "" == "usr" + assert "" / "lib" == "lib" + assert "" / "/lib" == "/lib" + assert "usr/" / "/lib/" == "usr/lib/" + assert "usr" / "lib" / "../bin" == "usr/bin" + + return joinPath(head, tail) + +proc splitPath*(path: string): tuple[head, tail: string] {. + noSideEffect, rtl, extern: "nos$1".} = + ## Splits a directory into `(head, tail)` tuple, so that + ## ``head / tail == path`` (except for edge cases like "/usr"). + ## + ## See also: + ## * `joinPath(head, tail) proc <#joinPath,string,string>`_ + ## * `joinPath(varargs) proc <#joinPath,varargs[string]>`_ + ## * `/ proc <#/,string,string>`_ + ## * `/../ proc <#/../,string,string>`_ + ## * `relativePath proc <#relativePath,string,string>`_ + runnableExamples: + assert splitPath("usr/local/bin") == ("usr/local", "bin") + assert splitPath("usr/local/bin/") == ("usr/local/bin", "") + assert splitPath("/bin/") == ("/bin", "") + when (NimMajor, NimMinor) <= (1, 0): + assert splitPath("/bin") == ("", "bin") + else: + assert splitPath("/bin") == ("/", "bin") + assert splitPath("bin") == ("", "bin") + assert splitPath("") == ("", "") + + var sepPos = -1 + for i in countdown(len(path)-1, 0): + if path[i] in {DirSep, AltSep}: + sepPos = i + break + if sepPos >= 0: + result.head = substr(path, 0, + when (NimMajor, NimMinor) <= (1, 0): + sepPos-1 + else: + if likely(sepPos >= 1): sepPos-1 else: 0 + ) + result.tail = substr(path, sepPos+1) + else: + result.head = "" + result.tail = path + +proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raises: [].} = + ## Checks whether a given `path` is absolute. + ## + ## On Windows, network paths are considered absolute too. + runnableExamples: + assert not "".isAbsolute + assert not ".".isAbsolute + when defined(posix): + assert "/".isAbsolute + assert not "a/".isAbsolute + assert "/a/".isAbsolute + + if len(path) == 0: return false + + when doslikeFileSystem: + var len = len(path) + result = (path[0] in {'/', '\\'}) or + (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':') + elif defined(macos): + # according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path + result = path[0] != ':' + elif defined(RISCOS): + result = path[0] == '$' + elif defined(posix) or defined(js): + # `or defined(js)` wouldn't be needed pending https://github.com/nim-lang/Nim/issues/13469 + # This works around the problem for posix, but windows is still broken with nim js -d:nodejs + result = path[0] == '/' + else: + doAssert false # if ever hits here, adapt as needed + +when FileSystemCaseSensitive: + template `!=?`(a, b: char): bool = a != b +else: + template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b) + +when doslikeFileSystem: + proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: []} = + ## An absolute path from the root of the current drive (e.g. "\foo") + path.len > 0 and + (path[0] == AltSep or + (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. + ## + ## Detail of windows path formats: + ## https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats + + 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 + 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 + +proc relativePath*(path, base: string, sep = DirSep): string {. + rtl, extern: "nos$1".} = + ## Converts `path` to a path relative to `base`. + ## + ## The `sep` (default: `DirSep <#DirSep>`_) is used for the path normalizations, + ## this can be useful to ensure the relative path only contains `'/'` + ## so that it can be used for URL constructions. + ## + ## On windows, if a root of `path` and a root of `base` are different, + ## returns `path` as is because it is impossible to make a relative path. + ## That means an absolute path can be returned. + ## + ## See also: + ## * `splitPath proc <#splitPath,string>`_ + ## * `parentDir proc <#parentDir,string>`_ + ## * `tailDir proc <#tailDir,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" + assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim" + assert relativePath("", "/users/moo", '/') == "" + assert relativePath("foo", ".", '/') == "foo" + assert relativePath("foo", "foo", '/') == "." + + if path.len == 0: return "" + var base = if base == ".": "" else: base + var path = path + path.normalizePathAux + base.normalizePathAux + let a1 = isAbsolute(path) + let a2 = isAbsolute(base) + if a1 and not a2: + base = absolutePathInternal(base) + elif a2 and not a1: + path = absolutePathInternal(path) + + when doslikeFileSystem: + if isAbsolute(path) and isAbsolute(base): + if not sameRoot(path, base): + return path + + var f = default PathIter + var b = default PathIter + var ff = (0, -1) + var bb = (0, -1) # (int, int) + result = newStringOfCap(path.len) + # skip the common prefix: + while f.hasNext(path) and b.hasNext(base): + ff = next(f, path) + bb = next(b, base) + let diff = ff[1] - ff[0] + if diff != bb[1] - bb[0]: break + var same = true + for i in 0..diff: + if path[i + ff[0]] !=? base[i + bb[0]]: + same = false + break + if not same: break + ff = (0, -1) + bb = (0, -1) + # for i in 0..diff: + # result.add base[i + bb[0]] + + # /foo/bar/xxx/ -- base + # /foo/bar/baz -- path path + # ../baz + # every directory that is in 'base', needs to add '..' + while true: + if bb[1] >= bb[0]: + if result.len > 0 and result[^1] != sep: + result.add sep + result.add ".." + if not b.hasNext(base): break + bb = b.next(base) + + # add the rest of 'path': + while true: + if ff[1] >= ff[0]: + if result.len > 0 and result[^1] != sep: + result.add sep + for i in 0..ff[1] - ff[0]: + result.add path[i + ff[0]] + if not f.hasNext(path): break + ff = f.next(path) + + when not defined(nimOldRelativePathBehavior): + if result.len == 0: result.add "." + +proc isRelativeTo*(path: string, base: string): bool {.since: (1, 1).} = + ## Returns true if `path` is relative to `base`. + runnableExamples: + doAssert isRelativeTo("./foo//bar", "foo") + doAssert isRelativeTo("foo/bar", ".") + doAssert isRelativeTo("/foo/bar.nim", "/foo/bar.nim") + doAssert not isRelativeTo("foo/bar.nims", "foo/bar.nim") + let path = path.normalizePath + let base = base.normalizePath + let ret = relativePath(path, base) + result = path.len > 0 and not ret.startsWith ".." + +proc parentDirPos(path: string): int = + var q = 1 + if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2 + for i in countdown(len(path)-q, 0): + if path[i] in {DirSep, AltSep}: return i + result = -1 + +proc parentDir*(path: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Returns the parent directory of `path`. + ## + ## This is similar to ``splitPath(path).head`` when ``path`` doesn't end + ## in a dir separator, but also takes care of path normalizations. + ## The remainder can be obtained with `lastPathPart(path) proc + ## <#lastPathPart,string>`_. + ## + ## See also: + ## * `relativePath proc <#relativePath,string,string>`_ + ## * `splitPath proc <#splitPath,string>`_ + ## * `tailDir proc <#tailDir,string>`_ + ## * `parentDirs iterator <#parentDirs.i,string>`_ + runnableExamples: + assert parentDir("") == "" + when defined(posix): + assert parentDir("/usr/local/bin") == "/usr/local" + assert parentDir("foo/bar//") == "foo" + assert parentDir("//foo//bar//.") == "/foo" + assert parentDir("./foo") == "." + assert parentDir("/./foo//./") == "/" + assert parentDir("a//./") == "." + assert parentDir("a/b/c/..") == "a" + result = pathnorm.normalizePath(path) + var sepPos = parentDirPos(result) + if sepPos >= 0: + result = substr(result, 0, sepPos) + normalizePathEnd(result) + elif result == ".." or result == "." or result.len == 0 or result[^1] in {DirSep, AltSep}: + # `.` => `..` and .. => `../..`(etc) would be a sensible alternative + # `/` => `/` (as done with splitFile) would be a sensible alternative + result = "" + else: + result = "." + +proc tailDir*(path: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Returns the tail part of `path`. + ## + ## See also: + ## * `relativePath proc <#relativePath,string,string>`_ + ## * `splitPath proc <#splitPath,string>`_ + ## * `parentDir proc <#parentDir,string>`_ + runnableExamples: + assert tailDir("/bin") == "bin" + assert tailDir("bin") == "" + assert tailDir("bin/") == "" + assert tailDir("/usr/local/bin") == "usr/local/bin" + assert tailDir("//usr//local//bin//") == "usr//local//bin//" + assert tailDir("./usr/local/bin") == "usr/local/bin" + assert tailDir("usr/local/bin") == "local/bin" + + var i = 0 + while i < len(path): + if path[i] in {DirSep, AltSep}: + while i < len(path) and path[i] in {DirSep, AltSep}: inc i + return substr(path, i) + inc i + result = "" + +proc isRootDir*(path: string): bool {. + noSideEffect, rtl, extern: "nos$1".} = + ## Checks whether a given `path` is a root directory. + runnableExamples: + assert isRootDir("") + assert isRootDir(".") + assert isRootDir("/") + assert isRootDir("a") + assert not isRootDir("/a") + assert not isRootDir("a/b/c") + + result = parentDirPos(path) < 0 + +iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string = + ## Walks over all parent directories of a given `path`. + ## + ## If `fromRoot` is true (default: false), the traversal will start from + ## the file system root directory. + ## If `inclusive` is true (default), the original argument will be included + ## in the traversal. + ## + ## Relative paths won't be expanded by this iterator. Instead, it will traverse + ## only the directories appearing in the relative path. + ## + ## See also: + ## * `parentDir proc <#parentDir,string>`_ + ## + ## **Examples:** + ## + ## .. code-block:: + ## let g = "a/b/c" + ## + ## for p in g.parentDirs: + ## echo p + ## # a/b/c + ## # a/b + ## # a + ## + ## for p in g.parentDirs(fromRoot=true): + ## echo p + ## # a/ + ## # a/b/ + ## # a/b/c + ## + ## for p in g.parentDirs(inclusive=false): + ## echo p + ## # a/b + ## # a + + if not fromRoot: + var current = path + if inclusive: yield path + while true: + if current.isRootDir: break + current = current.parentDir + yield current + else: + for i in countup(0, 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}): + yield path.substr(0, i) + + if inclusive: yield path + +proc `/../`*(head, tail: string): string {.noSideEffect.} = + ## The same as ``parentDir(head) / tail``, unless there is no parent + ## directory. Then ``head / tail`` is performed instead. + ## + ## See also: + ## * `/ proc <#/,string,string>`_ + ## * `parentDir proc <#parentDir,string>`_ + runnableExamples: + when defined(posix): + assert "a/b/c" /../ "d/e" == "a/b/d/e" + assert "a" /../ "d/e" == "a/d/e" + + let sepPos = parentDirPos(head) + if sepPos >= 0: + result = substr(head, 0, sepPos-1) / tail + else: + result = head / tail + +proc normExt(ext: string): string = + if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here + else: result = ExtSep & ext + +proc searchExtPos*(path: string): int = + ## Returns index of the `'.'` char in `path` if it signifies the beginning + ## of extension. Returns -1 otherwise. + ## + ## See also: + ## * `splitFile proc <#splitFile,string>`_ + ## * `extractFilename proc <#extractFilename,string>`_ + ## * `lastPathPart proc <#lastPathPart,string>`_ + ## * `changeFileExt proc <#changeFileExt,string,string>`_ + ## * `addFileExt proc <#addFileExt,string,string>`_ + runnableExamples: + assert searchExtPos("a/b/c") == -1 + assert searchExtPos("c.nim") == 1 + assert searchExtPos("a/b/c.nim") == 5 + assert searchExtPos("a.b.c.nim") == 5 + + # BUGFIX: do not search until 0! .DS_Store is no file extension! + result = -1 + for i in countdown(len(path)-1, 1): + if path[i] == ExtSep: + result = i + break + elif path[i] in {DirSep, AltSep}: + break # do not skip over path + +proc splitFile*(path: string): tuple[dir, name, ext: string] {. + noSideEffect, rtl, extern: "nos$1".} = + ## Splits a filename into `(dir, name, extension)` tuple. + ## + ## `dir` does not end in `DirSep <#DirSep>`_ unless it's `/`. + ## `extension` includes the leading dot. + ## + ## If `path` has no extension, `ext` is the empty string. + ## If `path` has no directory component, `dir` is the empty string. + ## If `path` has no filename component, `name` and `ext` are empty strings. + ## + ## See also: + ## * `searchExtPos proc <#searchExtPos,string>`_ + ## * `extractFilename proc <#extractFilename,string>`_ + ## * `lastPathPart proc <#lastPathPart,string>`_ + ## * `changeFileExt proc <#changeFileExt,string,string>`_ + ## * `addFileExt proc <#addFileExt,string,string>`_ + runnableExamples: + var (dir, name, ext) = splitFile("usr/local/nimc.html") + assert dir == "usr/local" + assert name == "nimc" + assert ext == ".html" + (dir, name, ext) = splitFile("/usr/local/os") + assert dir == "/usr/local" + assert name == "os" + assert ext == "" + (dir, name, ext) = splitFile("/usr/local/") + assert dir == "/usr/local" + assert name == "" + assert ext == "" + (dir, name, ext) = splitFile("/tmp.txt") + assert dir == "/" + assert name == "tmp" + assert ext == ".txt" + + var namePos = 0 + var dotPos = 0 + for i in countdown(len(path) - 1, 0): + 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) + namePos = i + 1 + if dotPos > i: + result.name = substr(path, namePos, dotPos - 1) + result.ext = substr(path, dotPos) + else: + result.name = substr(path, namePos) + break + elif path[i] == ExtSep and i > 0 and i < len(path) - 1 and + path[i - 1] notin {DirSep, AltSep} and + path[i + 1] != ExtSep and dotPos == 0: + dotPos = i + +proc extractFilename*(path: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Extracts the filename of a given `path`. + ## + ## This is the same as ``name & ext`` from `splitFile(path) proc + ## <#splitFile,string>`_. + ## + ## See also: + ## * `searchExtPos proc <#searchExtPos,string>`_ + ## * `splitFile proc <#splitFile,string>`_ + ## * `lastPathPart proc <#lastPathPart,string>`_ + ## * `changeFileExt proc <#changeFileExt,string,string>`_ + ## * `addFileExt proc <#addFileExt,string,string>`_ + runnableExamples: + assert extractFilename("foo/bar/") == "" + assert extractFilename("foo/bar") == "bar" + assert extractFilename("foo/bar.baz") == "bar.baz" + + if path.len == 0 or path[path.len-1] in {DirSep, AltSep}: + result = "" + else: + result = splitPath(path).tail + +proc lastPathPart*(path: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Like `extractFilename proc <#extractFilename,string>`_, but ignores + ## trailing dir separator; aka: `baseName`:idx: in some other languages. + ## + ## See also: + ## * `searchExtPos proc <#searchExtPos,string>`_ + ## * `splitFile proc <#splitFile,string>`_ + ## * `extractFilename proc <#extractFilename,string>`_ + ## * `changeFileExt proc <#changeFileExt,string,string>`_ + ## * `addFileExt proc <#addFileExt,string,string>`_ + runnableExamples: + assert lastPathPart("foo/bar/") == "bar" + assert lastPathPart("foo/bar") == "bar" + + let path = path.normalizePathEnd(trailingSep = false) + result = extractFilename(path) + +proc changeFileExt*(filename, ext: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Changes the file extension to `ext`. + ## + ## If the `filename` has no extension, `ext` will be added. + ## If `ext` == "" then any extension is removed. + ## + ## `Ext` should be given without the leading `'.'`, because some + ## filesystems may use a different character. (Although I know + ## of none such beast.) + ## + ## See also: + ## * `searchExtPos proc <#searchExtPos,string>`_ + ## * `splitFile proc <#splitFile,string>`_ + ## * `extractFilename proc <#extractFilename,string>`_ + ## * `lastPathPart proc <#lastPathPart,string>`_ + ## * `addFileExt proc <#addFileExt,string,string>`_ + runnableExamples: + assert changeFileExt("foo.bar", "baz") == "foo.baz" + assert changeFileExt("foo.bar", "") == "foo" + assert changeFileExt("foo", "baz") == "foo.baz" + + var extPos = searchExtPos(filename) + if extPos < 0: result = filename & normExt(ext) + else: result = substr(filename, 0, extPos-1) & normExt(ext) + +proc addFileExt*(filename, ext: string): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Adds the file extension `ext` to `filename`, unless + ## `filename` already has an extension. + ## + ## `Ext` should be given without the leading `'.'`, because some + ## filesystems may use a different character. + ## (Although I know of none such beast.) + ## + ## See also: + ## * `searchExtPos proc <#searchExtPos,string>`_ + ## * `splitFile proc <#splitFile,string>`_ + ## * `extractFilename proc <#extractFilename,string>`_ + ## * `lastPathPart proc <#lastPathPart,string>`_ + ## * `changeFileExt proc <#changeFileExt,string,string>`_ + runnableExamples: + assert addFileExt("foo.bar", "baz") == "foo.bar" + assert addFileExt("foo.bar", "") == "foo.bar" + assert addFileExt("foo", "baz") == "foo.baz" + + var extPos = searchExtPos(filename) + if extPos < 0: result = filename & normExt(ext) + else: result = filename + +proc cmpPaths*(pathA, pathB: string): int {. + noSideEffect, rtl, extern: "nos$1".} = + ## Compares two paths. + ## + ## On a case-sensitive filesystem this is done + ## case-sensitively otherwise case-insensitively. Returns: + ## + ## | 0 if pathA == pathB + ## | < 0 if pathA < pathB + ## | > 0 if pathA > pathB + runnableExamples: + when defined(macosx): + assert cmpPaths("foo", "Foo") == 0 + elif defined(posix): + assert cmpPaths("foo", "Foo") > 0 + + let a = normalizePath(pathA) + let b = normalizePath(pathB) + if FileSystemCaseSensitive: + result = cmp(a, b) + else: + when defined(nimscript): + result = cmpic(a, b) + elif defined(nimdoc): discard + else: + result = cmpIgnoreCase(a, b) + +proc unixToNativePath*(path: string, drive=""): string {. + noSideEffect, rtl, extern: "nos$1".} = + ## Converts an UNIX-like path to a native one. + ## + ## On an UNIX system this does nothing. Else it converts + ## `'/'`, `'.'`, `'..'` to the appropriate things. + ## + ## On systems with a concept of "drives", `drive` is used to determine + ## which drive label to use during absolute path conversion. + ## `drive` defaults to the drive of the current working directory, and is + ## ignored on systems that do not have a concept of "drives". + when defined(unix): + result = path + else: + if path.len == 0: return "" + + var start: int + if path[0] == '/': + # an absolute path + when doslikeFileSystem: + if drive != "": + result = drive & ":" & DirSep + else: + result = $DirSep + elif defined(macos): + result = "" # must not start with ':' + else: + result = $DirSep + start = 1 + elif path[0] == '.' and (path.len == 1 or path[1] == '/'): + # current directory + result = $CurDir + start = when doslikeFileSystem: 1 else: 2 + else: + result = "" + start = 0 + + var i = start + while i < len(path): # ../../../ --> :::: + if i+2 < path.len and path[i] == '.' and path[i+1] == '.' and path[i+2] == '/': + # parent directory + when defined(macos): + if result[high(result)] == ':': + add result, ':' + else: + add result, ParDir + else: + add result, ParDir & DirSep + inc(i, 3) + elif path[i] == '/': + add result, DirSep + inc(i) + else: + add result, path[i] + inc(i) + +include "includes/oserr" +when not defined(nimscript): + include "includes/osenv" + +proc getHomeDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the home directory of the current user. + ## + ## This proc is wrapped by the `expandTilde proc <#expandTilde,string>`_ + ## for the convenience of processing paths coming from user configuration files. + ## + ## See also: + ## * `getConfigDir proc <#getConfigDir>`_ + ## * `getTempDir proc <#getTempDir>`_ + ## * `expandTilde proc <#expandTilde,string>`_ + ## * `getCurrentDir proc <#getCurrentDir>`_ + ## * `setCurrentDir proc <#setCurrentDir,string>`_ + runnableExamples: + assert getHomeDir() == expandTilde("~") + + when defined(windows): return string(getEnv("USERPROFILE")) & "\\" + else: return string(getEnv("HOME")) & "/" + +proc getConfigDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the config directory of the current user for applications. + ## + ## On non-Windows OSs, this proc conforms to the XDG Base Directory + ## spec. Thus, this proc returns the value of the `XDG_CONFIG_HOME` environment + ## variable if it is set, otherwise it returns the default configuration + ## directory ("~/.config/"). + ## + ## An OS-dependent trailing slash is always present at the end of the + ## returned string: `\\` on Windows and `/` on all other OSs. + ## + ## See also: + ## * `getHomeDir proc <#getHomeDir>`_ + ## * `getTempDir proc <#getTempDir>`_ + ## * `expandTilde proc <#expandTilde,string>`_ + ## * `getCurrentDir proc <#getCurrentDir>`_ + ## * `setCurrentDir proc <#setCurrentDir,string>`_ + when defined(windows): + result = getEnv("APPDATA").string + else: + result = getEnv("XDG_CONFIG_HOME", getEnv("HOME").string / ".config").string + result.normalizePathEnd(trailingSep = true) + +proc getTempDir*(): string {.rtl, extern: "nos$1", + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the temporary directory of the current user for applications to + ## save temporary files in. + ## + ## **Please do not use this**: On Android, it currently + ## returns ``getHomeDir()``, and on other Unix based systems it can cause + ## security problems too. That said, you can override this implementation + ## by adding ``-d:tempDir=mytempname`` to your compiler invocation. + ## + ## See also: + ## * `getHomeDir proc <#getHomeDir>`_ + ## * `getConfigDir proc <#getConfigDir>`_ + ## * `expandTilde proc <#expandTilde,string>`_ + ## * `getCurrentDir proc <#getCurrentDir>`_ + ## * `setCurrentDir proc <#setCurrentDir,string>`_ + const tempDirDefault = "/tmp" + result = tempDirDefault + when defined(tempDir): + const tempDir {.strdefine.}: string = tempDirDefault + result = tempDir + elif defined(windows): result = string(getEnv("TEMP")) + elif defined(android): result = getHomeDir() + else: + if existsEnv("TMPDIR"): result = string(getEnv("TMPDIR")) + normalizePathEnd(result, trailingSep=true) proc expandTilde*(path: string): string {. tags: [ReadEnvEffect, ReadIOEffect].} = ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing - ## ``~`` with `getHomeDir()`_ (otherwise returns ``path`` unmodified). + ## ``~`` with `getHomeDir() <#getHomeDir>`_ (otherwise returns ``path`` unmodified). ## - ## Windows: this is still supported despite the Windows platform not having this + ## Windows: this is still supported despite Windows platform not having this ## convention; also, both ``~/`` and ``~\`` are handled. ## ## See also: - ## * `getHomeDir proc`_ - ## * `getConfigDir proc`_ - ## * `getTempDir proc`_ - ## * `getCurrentDir proc`_ - ## * `setCurrentDir proc`_ + ## * `getHomeDir proc <#getHomeDir>`_ + ## * `getConfigDir proc <#getConfigDir>`_ + ## * `getTempDir proc <#getTempDir>`_ + ## * `getCurrentDir proc <#getCurrentDir>`_ + ## * `setCurrentDir proc <#setCurrentDir,string>`_ runnableExamples: assert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg" assert expandTilde("~/foo/bar") == getHomeDir() / "foo/bar" @@ -137,12 +986,15 @@ proc expandTilde*(path: string): string {. # TODO: handle `~bob` and `~bob/` which means home of bob result = path +# TODO: consider whether quoteShellPosix, quoteShellWindows, quoteShell, quoteShellCommand +# belong in `strutils` instead; they are not specific to paths proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = ## Quote `s`, so it can be safely passed to Windows API. ## ## Based on Python's `subprocess.list2cmdline`. ## See `this link `_ ## for more details. + let needQuote = {' ', '\t'} in s or s.len == 0 result = "" var backslashBuff = "" @@ -153,8 +1005,8 @@ proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1" if c == '\\': backslashBuff.add(c) elif c == '\"': - for i in 0.. 0: - result.add(backslashBuff) if needQuote: - result.add(backslashBuff) result.add("\"") - proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = ## Quote ``s``, so it can be safely passed to POSIX shell. + ## Based on Python's `pipes.quote`. const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@', '0'..'9', 'A'..'Z', 'a'..'z'} if s.len == 0: - result = "''" - elif s.allCharsInSet(safeUnixChars): - result = s + return "''" + + let safe = s.allCharsInSet(safeUnixChars) + + if safe: + return s else: - result = "'" & s.replace("'", "'\"'\"'") & "'" + return "'" & s.replace("'", "'\"'\"'") & "'" when defined(windows) or defined(posix) or defined(nintendoswitch): proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = ## Quote ``s``, so it can be safely passed to shell. ## - ## When on Windows, it calls `quoteShellWindows proc`_. - ## Otherwise, calls `quoteShellPosix proc`_. + ## When on Windows, it calls `quoteShellWindows proc + ## <#quoteShellWindows,string>`_. Otherwise, calls `quoteShellPosix proc + ## <#quoteShellPosix,string>`_. when defined(windows): - result = quoteShellWindows(s) + return quoteShellWindows(s) else: - result = quoteShellPosix(s) + return quoteShellPosix(s) proc quoteShellCommand*(args: openArray[string]): string = ## Concatenates and quotes shell arguments `args`. @@ -207,15 +1060,119 @@ when defined(windows) or defined(posix) or defined(nintendoswitch): result.add quoteShell(args[i]) when not weirdTarget: + proc c_rename(oldname, newname: cstring): cint {. + importc: "rename", header: "".} proc c_system(cmd: cstring): cint {. importc: "system", header: "".} + proc c_strlen(a: cstring): cint {. + importc: "strlen", header: "", noSideEffect.} + proc c_free(p: pointer) {. + importc: "free", header: "".} - when not defined(windows): - proc c_free(p: pointer) {. - importc: "free", header: "".} +when defined(windows) and not weirdTarget: + when useWinUnicode: + template wrapUnary(varname, winApiProc, arg: untyped) = + var varname = winApiProc(newWideCString(arg)) + + template wrapBinary(varname, winApiProc, arg, arg2: untyped) = + var varname = winApiProc(newWideCString(arg), arg2) + proc findFirstFile(a: string, b: var WIN32_FIND_DATA): Handle = + result = findFirstFileW(newWideCString(a), b) + template findNextFile(a, b: untyped): untyped = findNextFileW(a, b) + template getCommandLine(): untyped = getCommandLineW() + + template getFilename(f: untyped): untyped = + $cast[WideCString](addr(f.cFileName[0])) + else: + template findFirstFile(a, b: untyped): untyped = findFirstFileA(a, b) + template findNextFile(a, b: untyped): untyped = findNextFileA(a, b) + template getCommandLine(): untyped = getCommandLineA() + + template getFilename(f: untyped): untyped = $cstring(addr f.cFileName) + + proc skipFindData(f: WIN32_FIND_DATA): bool {.inline.} = + # Note - takes advantage of null delimiter in the cstring + const dot = ord('.') + result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or + f.cFileName[1].int == dot and f.cFileName[2].int == 0) + +proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", + tags: [ReadDirEffect], noNimJs.} = + ## Returns true if `filename` exists and is a regular file or symlink. + ## + ## Directories, device files, named pipes and sockets return false. + ## + ## See also: + ## * `dirExists proc <#dirExists,string>`_ + ## * `symlinkExists proc <#symlinkExists,string>`_ + when defined(windows): + when useWinUnicode: + wrapUnary(a, getFileAttributesW, filename) + else: + var a = getFileAttributesA(filename) + if a != -1'i32: + result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32 + else: + var res: Stat + return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) + +proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], + noNimJs.} = + ## Returns true if the directory `dir` exists. If `dir` is a file, false + ## is returned. Follows symlinks. + ## + ## See also: + ## * `fileExists proc <#fileExists,string>`_ + ## * `symlinkExists proc <#symlinkExists,string>`_ + when defined(windows): + when useWinUnicode: + wrapUnary(a, getFileAttributesW, dir) + else: + var a = getFileAttributesA(dir) + if a != -1'i32: + result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 + else: + var res: Stat + return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) + +proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", + tags: [ReadDirEffect], + noWeirdTarget.} = + ## Returns true if the symlink `link` exists. Will return true + ## regardless of whether the link points to a directory or file. + ## + ## See also: + ## * `fileExists proc <#fileExists,string>`_ + ## * `dirExists proc <#dirExists,string>`_ + when defined(windows): + when useWinUnicode: + wrapUnary(a, getFileAttributesW, link) + else: + var a = getFileAttributesA(link) + if a != -1'i32: + result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32 + else: + var res: Stat + return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode) + + +when not defined(nimscript): + when not defined(js): # `noNimJs` doesn't work with templates, this should improve. + template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = + fileExists(args) + template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} = + dirExists(args) + # {.deprecated: [existsFile: fileExists].} # pending bug #14819; this would avoid above mentioned issue + +when not defined(windows) and not weirdTarget: + proc checkSymlink(path: string): bool = + var rawInfo: Stat + if lstat(path, rawInfo) < 0'i32: result = false + else: result = S_ISLNK(rawInfo.st_mode) const + maxSymlinkLen = 1024 ExeExts* = ## Platform specific file extension for executables. ## On Windows ``["exe", "cmd", "bat"]``, on Posix ``[""]``. when defined(windows): ["exe", "cmd", "bat"] else: [""] @@ -227,7 +1184,7 @@ proc findExe*(exe: string, followSymlinks: bool = true; ## in directories listed in the ``PATH`` environment variable. ## ## Returns `""` if the `exe` cannot be found. `exe` - ## is added the `ExeExts`_ file extensions if it has none. + ## is added the `ExeExts <#ExeExts>`_ file extensions if it has none. ## ## If the system supports symlinks it also resolves them until it ## meets the actual file. This behavior can be disabled if desired @@ -242,7 +1199,7 @@ proc findExe*(exe: string, followSymlinks: bool = true; if '/' in exe: checkCurrentDir() else: checkCurrentDir() - let path = getEnv("PATH") + let path = string(getEnv("PATH")) for candidate in split(path, PathSep): if candidate.len == 0: continue when defined(windows): @@ -254,16 +1211,16 @@ proc findExe*(exe: string, followSymlinks: bool = true; for ext in extensions: var x = addFileExt(x, ext) if fileExists(x): - when not (defined(windows) or defined(nintendoswitch)): + when not defined(windows): while followSymlinks: # doubles as if here - if x.symlinkExists: + if x.checkSymlink: var r = newString(maxSymlinkLen) - var len = readlink(x.cstring, r.cstring, maxSymlinkLen) + var len = readlink(x, r, maxSymlinkLen) if len < 0: raiseOSError(osLastError(), exe) if len > maxSymlinkLen: r = newString(len+1) - len = readlink(x.cstring, r.cstring, len) + len = readlink(x, r, len) setLen(r, len) if isAbsolute(r): x = r @@ -282,9 +1239,9 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1", ## Returns the `file`'s last modification time. ## ## See also: - ## * `getLastAccessTime proc`_ - ## * `getCreationTime proc`_ - ## * `fileNewer proc`_ + ## * `getLastAccessTime proc <#getLastAccessTime,string>`_ + ## * `getCreationTime proc <#getCreationTime,string>`_ + ## * `fileNewer proc <#fileNewer,string,string>`_ when defined(posix): var res: Stat if stat(file, res) < 0'i32: raiseOSError(osLastError(), file) @@ -300,9 +1257,9 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeir ## Returns the `file`'s last read or write access time. ## ## See also: - ## * `getLastModificationTime proc`_ - ## * `getCreationTime proc`_ - ## * `fileNewer proc`_ + ## * `getLastModificationTime proc <#getLastModificationTime,string>`_ + ## * `getCreationTime proc <#getCreationTime,string>`_ + ## * `fileNewer proc <#fileNewer,string,string>`_ when defined(posix): var res: Stat if stat(file, res) < 0'i32: raiseOSError(osLastError(), file) @@ -322,9 +1279,9 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdT ## `here `_ for details. ## ## See also: - ## * `getLastModificationTime proc`_ - ## * `getLastAccessTime proc`_ - ## * `fileNewer proc`_ + ## * `getLastModificationTime proc <#getLastModificationTime,string>`_ + ## * `getLastAccessTime proc <#getLastAccessTime,string>`_ + ## * `fileNewer proc <#fileNewer,string,string>`_ when defined(posix): var res: Stat if stat(file, res) < 0'i32: raiseOSError(osLastError(), file) @@ -341,9 +1298,9 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noWeirdTarget.} = ## modification time is later than `b`'s. ## ## See also: - ## * `getLastModificationTime proc`_ - ## * `getLastAccessTime proc`_ - ## * `getCreationTime proc`_ + ## * `getLastModificationTime proc <#getLastModificationTime,string>`_ + ## * `getLastAccessTime proc <#getLastAccessTime,string>`_ + ## * `getCreationTime proc <#getCreationTime,string>`_ when defined(posix): # If we don't have access to nanosecond resolution, use '>=' when not StatHasNanoseconds: @@ -353,39 +1310,524 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noWeirdTarget.} = else: result = getLastModificationTime(a) > getLastModificationTime(b) +when not defined(nimscript): + proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = + ## Returns the `current working directory`:idx: i.e. where the built + ## binary is run. + ## + ## So the path returned by this proc is determined at run time. + ## + ## See also: + ## * `getHomeDir proc <#getHomeDir>`_ + ## * `getConfigDir proc <#getConfigDir>`_ + ## * `getTempDir proc <#getTempDir>`_ + ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `currentSourcePath template `_ + ## * `getProjectPath proc `_ + when defined(nodejs): + var ret: cstring + {.emit: "`ret` = process.cwd();".} + return $ret + elif defined(js): + doAssert false, "use -d:nodejs to have `getCurrentDir` defined" + elif defined(windows): + var bufsize = MAX_PATH.int32 + when useWinUnicode: + var res = newWideCString("", bufsize) + while true: + var L = getCurrentDirectoryW(bufsize, res) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + res = newWideCString("", L) + bufsize = L + else: + result = res$L + break + else: + result = newString(bufsize) + while true: + var L = getCurrentDirectoryA(bufsize, result) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + result = newString(L) + bufsize = L + else: + setLen(result, L) + break + else: + var bufsize = 1024 # should be enough + result = newString(bufsize) + while true: + if getcwd(result, bufsize) != nil: + setLen(result, c_strlen(result)) + break + else: + let err = osLastError() + if err.int32 == ERANGE: + bufsize = bufsize shl 1 + doAssert(bufsize >= 0) + result = newString(bufsize) + else: + raiseOSError(osLastError()) -proc isAdmin*: bool {.noWeirdTarget.} = - ## Returns whether the caller's process is a member of the Administrators local - ## group (on Windows) or a root (on POSIX), via `geteuid() == 0`. - when defined(windows): - # Rewrite of the example from Microsoft Docs: - # https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-checktokenmembership#examples - # and corresponding PostgreSQL function: - # https://doxygen.postgresql.org/win32security_8c.html#ae6b61e106fa5d6c5d077a9d14ee80569 - var ntAuthority = SID_IDENTIFIER_AUTHORITY(value: SECURITY_NT_AUTHORITY) - var administratorsGroup: PSID - if not isSuccess(allocateAndInitializeSid(addr ntAuthority, - BYTE(2), - SECURITY_BUILTIN_DOMAIN_RID, - DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, - addr administratorsGroup)): - raiseOSError(osLastError(), "could not get SID for Administrators group") - - try: - var b: WINBOOL - if not isSuccess(checkTokenMembership(0, administratorsGroup, addr b)): - raiseOSError(osLastError(), "could not check access token membership") - - result = isSuccess(b) - finally: - if freeSid(administratorsGroup) != nil: - raiseOSError(osLastError(), "failed to free SID for Administrators group") - +proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} = + ## Sets the `current working directory`:idx:; `OSError` + ## is raised if `newDir` cannot been set. + ## + ## See also: + ## * `getHomeDir proc <#getHomeDir>`_ + ## * `getConfigDir proc <#getConfigDir>`_ + ## * `getTempDir proc <#getTempDir>`_ + ## * `getCurrentDir proc <#getCurrentDir>`_ + when defined(Windows): + when useWinUnicode: + if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32: + raiseOSError(osLastError(), newDir) + else: + if setCurrentDirectoryA(newDir) == 0'i32: raiseOSError(osLastError(), newDir) else: - result = geteuid() == 0 + if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir) +proc absolutePath*(path: string, root = getCurrentDir()): string = + ## Returns the absolute path of `path`, rooted at `root` (which must be absolute; + ## default: current directory). + ## If `path` is absolute, return it, ignoring `root`. + ## + ## See also: + ## * `normalizedPath proc <#normalizedPath,string>`_ + ## * `normalizePath proc <#normalizePath,string>`_ + runnableExamples: + assert absolutePath("a") == getCurrentDir() / "a" + + if isAbsolute(path): path + else: + if not root.isAbsolute: + raise newException(ValueError, "The specified root is not absolute: " & root) + joinPath(root, path) + +proc absolutePathInternal(path: string): string = + absolutePath(path, getCurrentDir()) + +proc normalizeExe*(file: var string) {.since: (1, 3, 5).} = + ## on posix, prepends `./` if `file` doesn't contain `/` and is not `"", ".", ".."`. + runnableExamples: + import sugar + when defined(posix): + doAssert "foo".dup(normalizeExe) == "./foo" + doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" + doAssert "".dup(normalizeExe) == "" + when defined(posix): + if file.len > 0 and DirSep notin file and file != "." and file != "..": + file = "./" & file + +proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} = + ## Normalize a path. + ## + ## Consecutive directory separators are collapsed, including an initial double slash. + ## + ## On relative paths, double dot (`..`) sequences are collapsed if possible. + ## On absolute paths they are always collapsed. + ## + ## Warning: URL-encoded and Unicode attempts at directory traversal are not detected. + ## Triple dot is not handled. + ## + ## See also: + ## * `absolutePath proc <#absolutePath,string>`_ + ## * `normalizedPath proc <#normalizedPath,string>`_ for outplace version + ## * `normalizeExe proc <#normalizeExe,string>`_ + runnableExamples: + when defined(posix): + var a = "a///b//..//c///d" + a.normalizePath() + assert a == "a/c/d" + + path = pathnorm.normalizePath(path) + when false: + let isAbs = isAbsolute(path) + var stack: seq[string] = @[] + for p in split(path, {DirSep}): + case p + of "", ".": + continue + of "..": + if stack.len == 0: + if isAbs: + discard # collapse all double dots on absoluta paths + else: + stack.add(p) + elif stack[^1] == "..": + stack.add(p) + else: + discard stack.pop() + else: + stack.add(p) + + if isAbs: + path = DirSep & join(stack, $DirSep) + elif stack.len > 0: + path = join(stack, $DirSep) + else: + path = "." + +proc normalizePathAux(path: var string) = normalizePath(path) + +proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [].} = + ## Returns a normalized path for the current OS. + ## + ## See also: + ## * `absolutePath proc <#absolutePath,string>`_ + ## * `normalizePath proc <#normalizePath,string>`_ for the in-place version + runnableExamples: + when defined(posix): + assert normalizedPath("a///b//..//c///d") == "a/c/d" + result = pathnorm.normalizePath(path) + +when defined(Windows) and not weirdTarget: + proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle = + var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL + if not followSymlink: + flags = flags or FILE_FLAG_OPEN_REPARSE_POINT + let access = if writeAccess: GENERIC_WRITE else: 0'i32 + + when useWinUnicode: + result = createFileW( + newWideCString(path), access, + FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, + nil, OPEN_EXISTING, flags, 0 + ) + else: + result = createFileA( + path, access, + FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, + nil, OPEN_EXISTING, flags, 0 + ) + +proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", + tags: [ReadDirEffect], noWeirdTarget.} = + ## Returns true if both pathname arguments refer to the same physical + ## file or directory. + ## + ## Raises `OSError` if any of the files does not + ## exist or information about it can not be obtained. + ## + ## This proc will return true if given two alternative hard-linked or + ## sym-linked paths to the same file or directory. + ## + ## See also: + ## * `sameFileContent proc <#sameFileContent,string,string>`_ + when defined(Windows): + var success = true + var f1 = openHandle(path1) + var f2 = openHandle(path2) + + var lastErr: OSErrorCode + if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE: + var fi1, fi2: BY_HANDLE_FILE_INFORMATION + + if getFileInformationByHandle(f1, addr(fi1)) != 0 and + getFileInformationByHandle(f2, addr(fi2)) != 0: + result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and + fi1.nFileIndexHigh == fi2.nFileIndexHigh and + fi1.nFileIndexLow == fi2.nFileIndexLow + else: + lastErr = osLastError() + success = false + else: + lastErr = osLastError() + success = false + + discard closeHandle(f1) + discard closeHandle(f2) + + if not success: raiseOSError(lastErr, $(path1, path2)) + else: + var a, b: Stat + if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32: + raiseOSError(osLastError(), $(path1, path2)) + else: + result = a.st_dev == b.st_dev and a.st_ino == b.st_ino + +type + FilePermission* = enum ## File access permission, modelled after UNIX. + ## + ## See also: + ## * `getFilePermissions <#getFilePermissions,string>`_ + ## * `setFilePermissions <#setFilePermissions,string,set[FilePermission]>`_ + ## * `FileInfo object <#FileInfo>`_ + fpUserExec, ## execute access for the file owner + fpUserWrite, ## write access for the file owner + fpUserRead, ## read access for the file owner + fpGroupExec, ## execute access for the group + fpGroupWrite, ## write access for the group + fpGroupRead, ## read access for the group + fpOthersExec, ## execute access for others + fpOthersWrite, ## write access for others + fpOthersRead ## read access for others + +proc getFilePermissions*(filename: string): set[FilePermission] {. + rtl, extern: "nos$1", tags: [ReadDirEffect], noWeirdTarget.} = + ## Retrieves file permissions for `filename`. + ## + ## `OSError` is raised in case of an error. + ## On Windows, only the ``readonly`` flag is checked, every other + ## permission is available in any case. + ## + ## See also: + ## * `setFilePermissions proc <#setFilePermissions,string,set[FilePermission]>`_ + ## * `FilePermission enum <#FilePermission>`_ + when defined(posix): + var a: Stat + if stat(filename, a) < 0'i32: raiseOSError(osLastError(), filename) + result = {} + if (a.st_mode and S_IRUSR.Mode) != 0.Mode: result.incl(fpUserRead) + if (a.st_mode and S_IWUSR.Mode) != 0.Mode: result.incl(fpUserWrite) + if (a.st_mode and S_IXUSR.Mode) != 0.Mode: result.incl(fpUserExec) + + if (a.st_mode and S_IRGRP.Mode) != 0.Mode: result.incl(fpGroupRead) + if (a.st_mode and S_IWGRP.Mode) != 0.Mode: result.incl(fpGroupWrite) + if (a.st_mode and S_IXGRP.Mode) != 0.Mode: result.incl(fpGroupExec) + + if (a.st_mode and S_IROTH.Mode) != 0.Mode: result.incl(fpOthersRead) + if (a.st_mode and S_IWOTH.Mode) != 0.Mode: result.incl(fpOthersWrite) + if (a.st_mode and S_IXOTH.Mode) != 0.Mode: result.incl(fpOthersExec) + else: + when useWinUnicode: + wrapUnary(res, getFileAttributesW, filename) + else: + var res = getFileAttributesA(filename) + if res == -1'i32: raiseOSError(osLastError(), filename) + if (res and FILE_ATTRIBUTE_READONLY) != 0'i32: + result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, + fpOthersExec, fpOthersRead} + else: + result = {fpUserExec..fpOthersRead} + +proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {. + rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = + ## Sets the file permissions for `filename`. + ## + ## `OSError` is raised in case of an error. + ## On Windows, only the ``readonly`` flag is changed, depending on + ## ``fpUserWrite`` permission. + ## + ## See also: + ## * `getFilePermissions <#getFilePermissions,string>`_ + ## * `FilePermission enum <#FilePermission>`_ + when defined(posix): + var p = 0.Mode + if fpUserRead in permissions: p = p or S_IRUSR.Mode + if fpUserWrite in permissions: p = p or S_IWUSR.Mode + if fpUserExec in permissions: p = p or S_IXUSR.Mode + + if fpGroupRead in permissions: p = p or S_IRGRP.Mode + if fpGroupWrite in permissions: p = p or S_IWGRP.Mode + if fpGroupExec in permissions: p = p or S_IXGRP.Mode + + if fpOthersRead in permissions: p = p or S_IROTH.Mode + if fpOthersWrite in permissions: p = p or S_IWOTH.Mode + if fpOthersExec in permissions: p = p or S_IXOTH.Mode + + if chmod(filename, cast[Mode](p)) != 0: raiseOSError(osLastError(), $(filename, permissions)) + else: + when useWinUnicode: + wrapUnary(res, getFileAttributesW, filename) + else: + var res = getFileAttributesA(filename) + if res == -1'i32: raiseOSError(osLastError(), filename) + if fpUserWrite in permissions: + res = res and not FILE_ATTRIBUTE_READONLY + else: + res = res or FILE_ATTRIBUTE_READONLY + when useWinUnicode: + wrapBinary(res2, setFileAttributesW, filename, res) + else: + var res2 = setFileAttributesA(filename, res) + if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions)) + +proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", + tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} = + ## Copies a file from `source` to `dest`, where `dest.parentDir` must exist. + ## + ## If this fails, `OSError` is raised. + ## + ## On the Windows platform this proc will + ## copy the source file's attributes into dest. + ## + ## On other platforms you need + ## to use `getFilePermissions <#getFilePermissions,string>`_ and + ## `setFilePermissions <#setFilePermissions,string,set[FilePermission]>`_ procs + ## to copy them by hand (or use the convenience `copyFileWithPermissions + ## proc <#copyFileWithPermissions,string,string>`_), + ## otherwise `dest` will inherit the default permissions of a newly + ## created file for the user. + ## + ## If `dest` already exists, the file attributes + ## will be preserved and the content overwritten. + ## + ## See also: + ## * `copyDir proc <#copyDir,string,string>`_ + ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ + ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ + ## * `removeFile proc <#removeFile,string>`_ + ## * `moveFile proc <#moveFile,string,string>`_ + + when defined(Windows): + when useWinUnicode: + let s = newWideCString(source) + let d = newWideCString(dest) + if copyFileW(s, d, 0'i32) == 0'i32: raiseOSError(osLastError(), $(source, dest)) + else: + if copyFileA(source, dest, 0'i32) == 0'i32: raiseOSError(osLastError(), $(source, dest)) + else: + # generic version of copyFile which works for any platform: + const bufSize = 8000 # better for memory manager + var d, s: File + if not open(s, source): raiseOSError(osLastError(), source) + if not open(d, dest, fmWrite): + close(s) + raiseOSError(osLastError(), dest) + var buf = alloc(bufSize) + while true: + var bytesread = readBuffer(s, buf, bufSize) + if bytesread > 0: + var byteswritten = writeBuffer(d, buf, bytesread) + if bytesread != byteswritten: + dealloc(buf) + close(s) + close(d) + raiseOSError(osLastError(), dest) + if bytesread != bufSize: break + dealloc(buf) + close(s) + flushFile(d) + close(d) + +proc copyFileToDir*(source, dir: string) {.noWeirdTarget, since: (1,3,7).} = + ## Copies a file `source` into directory `dir`, which must exist. + if dir.len == 0: # treating "" as "." is error prone + raise newException(ValueError, "dest is empty") + copyFile(source, dir / source.lastPathPart) + +when not declared(ENOENT) and not defined(Windows): + when NoFakeVars: + when not defined(haiku): + const ENOENT = cint(2) # 2 on most systems including Solaris + else: + const ENOENT = cint(-2147459069) + else: + var ENOENT {.importc, header: "".}: cint + +when defined(Windows) and not weirdTarget: + when useWinUnicode: + template deleteFile(file: untyped): untyped = deleteFileW(file) + template setFileAttributes(file, attrs: untyped): untyped = + setFileAttributesW(file, attrs) + else: + template deleteFile(file: untyped): untyped = deleteFileA(file) + template setFileAttributes(file, attrs: untyped): untyped = + setFileAttributesA(file, attrs) + +proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = + ## Removes the `file`. + ## + ## If this fails, returns `false`. This does not fail + ## if the file never existed in the first place. + ## + ## On Windows, ignores the read-only attribute. + ## + ## See also: + ## * `copyFile proc <#copyFile,string,string>`_ + ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ + ## * `removeFile proc <#removeFile,string>`_ + ## * `moveFile proc <#moveFile,string,string>`_ + result = true + when defined(Windows): + when useWinUnicode: + let f = newWideCString(file) + else: + let f = file + if deleteFile(f) == 0: + result = false + let err = getLastError() + if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND: + result = true + elif err == ERROR_ACCESS_DENIED and + setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and + deleteFile(f) != 0: + result = true + else: + if unlink(file) != 0'i32 and errno != ENOENT: + result = false + +proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = + ## Removes the `file`. + ## + ## If this fails, `OSError` is raised. This does not fail + ## if the file never existed in the first place. + ## + ## On Windows, ignores the read-only attribute. + ## + ## See also: + ## * `removeDir proc <#removeDir,string>`_ + ## * `copyFile proc <#copyFile,string,string>`_ + ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ + ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ + ## * `moveFile proc <#moveFile,string,string>`_ + if not tryRemoveFile(file): + raiseOSError(osLastError(), file) + +proc tryMoveFSObject(source, dest: string): bool {.noWeirdTarget.} = + ## Moves a file or directory from `source` to `dest`. + ## + ## Returns false in case of `EXDEV` error. + ## In case of other errors `OSError` is raised. + ## Returns true in case of success. + when defined(Windows): + when useWinUnicode: + let s = newWideCString(source) + let d = newWideCString(dest) + if moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) == 0'i32: raiseOSError(osLastError(), $(source, dest)) + else: + if moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) == 0'i32: raiseOSError(osLastError(), $(source, dest)) + else: + if c_rename(source, dest) != 0'i32: + let err = osLastError() + if err == EXDEV.OSErrorCode: + return false + else: + # see whether `strerror(errno)` is redundant with what raiseOSError already shows + raiseOSError(err, $(source, dest, strerror(errno))) + return true + +proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", + tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} = + ## Moves a file from `source` to `dest`. + ## + ## If this fails, `OSError` is raised. + ## If `dest` already exists, it will be overwritten. + ## + ## Can be used to `rename files`:idx:. + ## + ## See also: + ## * `moveDir proc <#moveDir,string,string>`_ + ## * `copyFile proc <#copyFile,string,string>`_ + ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ + ## * `removeFile proc <#removeFile,string>`_ + ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ + + if not tryMoveFSObject(source, dest): + when not defined(windows): + # Fallback to copy & del + copyFile(source, dest) + try: + removeFile(source) + except: + discard tryRemoveFile(dest) + raise + proc exitStatusLikeShell*(status: cint): cint = ## Converts exit code from `c_system` into a shell exit code. when defined(posix) and not weirdTarget: @@ -415,6 +1857,105 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", ## discard execShellCmd("ls -la") result = exitStatusLikeShell(c_system(command)) +# Templates for filtering directories and files +when defined(windows) and not weirdTarget: + template isDir(f: WIN32_FIND_DATA): bool = + (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 + template isFile(f: WIN32_FIND_DATA): bool = + not isDir(f) +else: + template isDir(f: string): bool {.dirty.} = + dirExists(f) + template isFile(f: string): bool {.dirty.} = + fileExists(f) + +template defaultWalkFilter(item): bool = + ## Walk filter used to return true on both + ## files and directories + true + +template walkCommon(pattern: string, filter) = + ## Common code for getting the files and directories with the + ## specified `pattern` + when defined(windows): + var + f: WIN32_FIND_DATA + res: int + res = findFirstFile(pattern, f) + if res != -1: + defer: findClose(res) + let dotPos = searchExtPos(pattern) + while true: + if not skipFindData(f) and filter(f): + # Windows bug/gotcha: 't*.nim' matches 'tfoo.nims' -.- so we check + # that the file extensions have the same length ... + let ff = getFilename(f) + let idx = ff.len - pattern.len + dotPos + if dotPos < 0 or idx >= ff.len or (idx >= 0 and ff[idx] == '.') or + (dotPos >= 0 and dotPos+1 < pattern.len and pattern[dotPos+1] == '*'): + yield splitFile(pattern).dir / extractFilename(ff) + if findNextFile(res, f) == 0'i32: + let errCode = getLastError() + if errCode == ERROR_NO_MORE_FILES: break + else: raiseOSError(errCode.OSErrorCode) + else: # here we use glob + var + f: Glob + res: int + f.gl_offs = 0 + f.gl_pathc = 0 + f.gl_pathv = nil + res = glob(pattern, 0, nil, addr(f)) + defer: globfree(addr(f)) + if res == 0: + for i in 0.. f.gl_pathc - 1: + assert(f.gl_pathv[i] != nil) + let path = $f.gl_pathv[i] + if filter(path): + yield path + +iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = + ## Iterate over all the files and directories that match the `pattern`. + ## + ## On POSIX this uses the `glob`:idx: call. + ## `pattern` is OS dependent, but at least the `"\*.ext"` + ## notation is supported. + ## + ## See also: + ## * `walkFiles iterator <#walkFiles.i,string>`_ + ## * `walkDirs iterator <#walkDirs.i,string>`_ + ## * `walkDir iterator <#walkDir.i,string>`_ + ## * `walkDirRec iterator <#walkDirRec.i,string>`_ + walkCommon(pattern, defaultWalkFilter) + +iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = + ## Iterate over all the files that match the `pattern`. + ## + ## On POSIX this uses the `glob`:idx: call. + ## `pattern` is OS dependent, but at least the `"\*.ext"` + ## notation is supported. + ## + ## See also: + ## * `walkPattern iterator <#walkPattern.i,string>`_ + ## * `walkDirs iterator <#walkDirs.i,string>`_ + ## * `walkDir iterator <#walkDir.i,string>`_ + ## * `walkDirRec iterator <#walkDirRec.i,string>`_ + walkCommon(pattern, isFile) + +iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = + ## Iterate over all the directories that match the `pattern`. + ## + ## On POSIX this uses the `glob`:idx: call. + ## `pattern` is OS dependent, but at least the `"\*.ext"` + ## notation is supported. + ## + ## See also: + ## * `walkPattern iterator <#walkPattern.i,string>`_ + ## * `walkFiles iterator <#walkFiles.i,string>`_ + ## * `walkDir iterator <#walkDir.i,string>`_ + ## * `walkDirRec iterator <#walkDirRec.i,string>`_ + walkCommon(pattern, isDir) + proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", tags: [ReadDirEffect], noWeirdTarget.} = ## Returns the full (`absolute`:idx:) path of an existing file `filename`. @@ -422,18 +1963,32 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", ## Raises `OSError` in case of an error. Follows symlinks. when defined(windows): var bufsize = MAX_PATH.int32 - var unused: WideCString = nil - var res = newWideCString("", bufsize) - while true: - var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused) - if L == 0'i32: - raiseOSError(osLastError(), filename) - elif L > bufsize: - res = newWideCString("", L) - bufsize = L - else: - result = res$L - break + when useWinUnicode: + var unused: WideCString = nil + var res = newWideCString("", bufsize) + while true: + var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused) + if L == 0'i32: + raiseOSError(osLastError(), filename) + elif L > bufsize: + res = newWideCString("", L) + bufsize = L + else: + result = res$L + break + else: + var unused: cstring = nil + result = newString(bufsize) + while true: + var L = getFullPathNameA(filename, bufsize, result, unused) + if L == 0'i32: + raiseOSError(osLastError(), filename) + elif L > bufsize: + result = newString(L) + bufsize = L + else: + setLen(result, L) + break # getFullPathName doesn't do case corrections, so we have to use this convoluted # way of retrieving the true filename for x in walkFiles(result): @@ -451,32 +2006,506 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", result = $r c_free(cast[pointer](r)) +type + PathComponent* = enum ## Enumeration specifying a path component. + ## + ## See also: + ## * `walkDirRec iterator <#walkDirRec.i,string>`_ + ## * `FileInfo object <#FileInfo>`_ + pcFile, ## path refers to a file + pcLinkToFile, ## path refers to a symbolic link to a file + pcDir, ## path refers to a directory + pcLinkToDir ## path refers to a symbolic link to a directory + proc getCurrentCompilerExe*(): string {.compileTime.} = discard - ## This is `getAppFilename()`_ at compile time. + ## This is `getAppFilename() <#getAppFilename>`_ at compile time. ## ## Can be used to retrieve the currently executing ## Nim compiler from a Nim or nimscript program, or the nimble binary ## inside a nimble program (likewise with other binaries built from ## compiler API). +when defined(posix) and not weirdTarget: + proc getSymlinkFileKind(path: string): PathComponent = + # Helper function. + var s: Stat + assert(path != "") + if stat(path, s) == 0'i32 and S_ISDIR(s.st_mode): + result = pcLinkToDir + else: + result = pcLinkToFile + +proc staticWalkDir(dir: string; relative: bool): seq[ + tuple[kind: PathComponent, path: string]] = + discard + +iterator walkDir*(dir: string; relative = false, checkDir = false): + tuple[kind: PathComponent, path: string] {.tags: [ReadDirEffect].} = + ## Walks over the directory `dir` and yields for each directory or file in + ## `dir`. The component type and full path for each item are returned. + ## + ## Walking is not recursive. If ``relative`` is true (default: false) + ## the resulting path is shortened to be relative to ``dir``. + ## Example: This directory structure:: + ## dirA / dirB / fileB1.txt + ## / dirC + ## / fileA1.txt + ## / fileA2.txt + ## + ## and this code: + ## + ## .. code-block:: Nim + ## for kind, path in walkDir("dirA"): + ## echo(path) + ## + ## produce this output (but not necessarily in this order!):: + ## dirA/dirB + ## dirA/dirC + ## dirA/fileA1.txt + ## dirA/fileA2.txt + ## + ## See also: + ## * `walkPattern iterator <#walkPattern.i,string>`_ + ## * `walkFiles iterator <#walkFiles.i,string>`_ + ## * `walkDirs iterator <#walkDirs.i,string>`_ + ## * `walkDirRec iterator <#walkDirRec.i,string>`_ + + when nimvm: + for k, v in items(staticWalkDir(dir, relative)): + yield (k, v) + else: + when weirdTarget: + for k, v in items(staticWalkDir(dir, relative)): + yield (k, v) + elif defined(windows): + var f: WIN32_FIND_DATA + var h = findFirstFile(dir / "*", f) + if h == -1: + if checkDir: + raiseOSError(osLastError(), dir) + else: + defer: findClose(h) + while true: + var k = pcFile + if not skipFindData(f): + if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: + k = pcDir + if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: + k = succ(k) + let xx = if relative: extractFilename(getFilename(f)) + else: dir / extractFilename(getFilename(f)) + yield (k, xx) + if findNextFile(h, f) == 0'i32: + let errCode = getLastError() + if errCode == ERROR_NO_MORE_FILES: break + else: raiseOSError(errCode.OSErrorCode) + else: + var d = opendir(dir) + if d == nil: + if checkDir: + raiseOSError(osLastError(), dir) + else: + defer: discard closedir(d) + while true: + var x = readdir(d) + if x == nil: break + when defined(nimNoArrayToCstringConversion): + var y = $cstring(addr x.d_name) + else: + var y = $x.d_name.cstring + if y != "." and y != "..": + var s: Stat + let path = dir / y + if not relative: + y = path + var k = pcFile + + template kSetGeneric() = # pure Posix component `k` resolution + if lstat(path, s) < 0'i32: continue # don't yield + elif S_ISDIR(s.st_mode): + k = pcDir + elif S_ISLNK(s.st_mode): + k = getSymlinkFileKind(path) + + when defined(linux) or defined(macosx) or + defined(bsd) or defined(genode) or defined(nintendoswitch): + case x.d_type + of DT_DIR: k = pcDir + of DT_LNK: + if dirExists(path): k = pcLinkToDir + else: k = pcLinkToFile + of DT_UNKNOWN: + kSetGeneric() + else: # e.g. DT_REG etc + discard # leave it as pcFile + else: # assuming that field `d_type` is not present + kSetGeneric() + + yield (k, y) + +iterator walkDirRec*(dir: string, + yieldFilter = {pcFile}, followFilter = {pcDir}, + relative = false, checkDir = false): string {.tags: [ReadDirEffect].} = + ## Recursively walks over the directory `dir` and yields for each file + ## or directory in `dir`. + ## + ## If ``relative`` is true (default: false) the resulting path is + ## shortened to be relative to ``dir``, otherwise the full path is returned. + ## + ## **Warning**: + ## Modifying the directory structure while the iterator + ## is traversing may result in undefined behavior! + ## + ## 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: + ## * `walkPattern iterator <#walkPattern.i,string>`_ + ## * `walkFiles iterator <#walkFiles.i,string>`_ + ## * `walkDirs iterator <#walkDirs.i,string>`_ + ## * `walkDir iterator <#walkDir.i,string>`_ + + var stack = @[""] + var checkDir = checkDir + while stack.len > 0: + let d = stack.pop() + for k, p in walkDir(dir / d, relative = true, checkDir = checkDir): + let rel = d / p + if k in {pcDir, pcLinkToDir} and k in followFilter: + stack.add rel + if k in yieldFilter: + yield if relative: rel else: dir / rel + checkDir = false + # We only check top-level dir, otherwise if a subdir is invalid (eg. wrong + # permissions), it'll abort iteration and there would be no way to + # continue iteration. + # Future work can provide a way to customize this and do error reporting. + +proc rawRemoveDir(dir: string) {.noWeirdTarget.} = + when defined(windows): + when useWinUnicode: + wrapUnary(res, removeDirectoryW, dir) + else: + var res = removeDirectoryA(dir) + let lastError = osLastError() + if res == 0'i32 and lastError.int32 != 3'i32 and + lastError.int32 != 18'i32 and lastError.int32 != 2'i32: + raiseOSError(lastError, dir) + else: + if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError(), dir) + +proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [ + WriteDirEffect, ReadDirEffect], benign, noWeirdTarget.} = + ## Removes the directory `dir` including all subdirectories and files + ## in `dir` (recursively). + ## + ## If this fails, `OSError` is raised. This does not fail if the directory never + ## existed in the first place, unless `checkDir` = true + ## + ## See also: + ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ + ## * `removeFile proc <#removeFile,string>`_ + ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ + ## * `createDir proc <#createDir,string>`_ + ## * `copyDir proc <#copyDir,string,string>`_ + ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ + ## * `moveDir proc <#moveDir,string,string>`_ + for kind, path in walkDir(dir, checkDir = checkDir): + case kind + of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path) + of pcDir: removeDir(path, true) + # for subdirectories there is no benefit in `checkDir = false` + # (unless perhaps for edge case of concurrent processes also deleting + # the same files) + rawRemoveDir(dir) + +proc rawCreateDir(dir: string): bool {.noWeirdTarget.} = + # Try to create one directory (not the whole path). + # returns `true` for success, `false` if the path has previously existed + # + # This is a thin wrapper over mkDir (or alternatives on other systems), + # so in case of a pre-existing path we don't check that it is a directory. + when defined(solaris): + let res = mkdir(dir, 0o777) + if res == 0'i32: + result = true + elif errno in {EEXIST, ENOSYS}: + result = false + else: + raiseOSError(osLastError(), dir) + elif defined(haiku): + let res = mkdir(dir, 0o777) + if res == 0'i32: + result = true + elif errno == EEXIST or errno == EROFS: + result = false + else: + raiseOSError(osLastError(), dir) + elif defined(posix): + let res = mkdir(dir, 0o777) + if res == 0'i32: + result = true + elif errno == EEXIST: + result = false + else: + #echo res + raiseOSError(osLastError(), dir) + else: + when useWinUnicode: + wrapUnary(res, createDirectoryW, dir) + else: + let res = createDirectoryA(dir) + + if res != 0'i32: + result = true + elif getLastError() == 183'i32: + result = false + else: + raiseOSError(osLastError(), dir) + +proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", + tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} = + ## Check if a `directory`:idx: `dir` exists, and create it otherwise. + ## + ## Does not create parent directories (fails if parent does not exist). + ## Returns `true` if the directory already exists, and `false` + ## otherwise. + ## + ## See also: + ## * `removeDir proc <#removeDir,string>`_ + ## * `createDir proc <#createDir,string>`_ + ## * `copyDir proc <#copyDir,string,string>`_ + ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ + ## * `moveDir proc <#moveDir,string,string>`_ + result = not rawCreateDir(dir) + if result: + # path already exists - need to check that it is indeed a directory + if not dirExists(dir): + raise newException(IOError, "Failed to create '" & dir & "'") + +proc createDir*(dir: string) {.rtl, extern: "nos$1", + tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} = + ## Creates the `directory`:idx: `dir`. + ## + ## The directory may contain several subdirectories that do not exist yet. + ## The full path is created. If this fails, `OSError` is raised. + ## + ## It does **not** fail if the directory already exists because for + ## most usages this does not indicate an error. + ## + ## See also: + ## * `removeDir proc <#removeDir,string>`_ + ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ + ## * `copyDir proc <#copyDir,string,string>`_ + ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ + ## * `moveDir proc <#moveDir,string,string>`_ + 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) + +proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", + tags: [WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = + ## Copies a directory from `source` to `dest`. + ## + ## If this fails, `OSError` is raised. + ## + ## On the Windows platform this proc will copy the attributes from + ## `source` into `dest`. + ## + ## On other platforms created files and directories will inherit the + ## default permissions of a newly created file/directory for the user. + ## Use `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ + ## to preserve attributes recursively on these platforms. + ## + ## See also: + ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ + ## * `copyFile proc <#copyFile,string,string>`_ + ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ + ## * `removeDir proc <#removeDir,string>`_ + ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ + ## * `createDir proc <#createDir,string>`_ + ## * `moveDir proc <#moveDir,string,string>`_ + createDir(dest) + for kind, path in walkDir(source): + var noSource = splitPath(path).tail + case kind + of pcFile: + copyFile(path, dest / noSource) + of pcDir: + copyDir(path, dest / noSource) + else: discard + +proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} = + ## Moves a directory from `source` to `dest`. + ## + ## If this fails, `OSError` is raised. + ## + ## See also: + ## * `moveFile proc <#moveFile,string,string>`_ + ## * `copyDir proc <#copyDir,string,string>`_ + ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ + ## * `removeDir proc <#removeDir,string>`_ + ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ + ## * `createDir proc <#createDir,string>`_ + if not tryMoveFSObject(source, dest): + when not defined(windows): + # Fallback to copy & del + copyDir(source, dest) + removeDir(source) + +proc createSymlink*(src, dest: string) {.noWeirdTarget.} = + ## Create a symbolic link at `dest` which points to the item specified + ## 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). + ## + ## See also: + ## * `createHardlink proc <#createHardlink,string,string>`_ + ## * `expandSymlink proc <#expandSymlink,string>`_ + + when defined(Windows): + # 2 is the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE. This allows + # anyone with developer mode on to create a link + let flag = dirExists(src).int32 or 2 + when useWinUnicode: + var wSrc = newWideCString(src) + var wDst = newWideCString(dest) + if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0: + raiseOSError(osLastError(), $(src, dest)) + else: + if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0: + raiseOSError(osLastError(), $(src, dest)) + else: + if symlink(src, dest) != 0: + raiseOSError(osLastError(), $(src, dest)) + proc createHardlink*(src, dest: string) {.noWeirdTarget.} = ## Create a hard link at `dest` which points to the item specified ## by `src`. ## - ## .. warning:: Some OS's restrict the creation of hard links to - ## root users (administrators). + ## **Warning**: Some OS's restrict the creation of hard links to + ## root users (administrators). ## ## See also: - ## * `createSymlink proc`_ - when defined(windows): - var wSrc = newWideCString(src) - var wDst = newWideCString(dest) - if createHardLinkW(wDst, wSrc, nil) == 0: - raiseOSError(osLastError(), $(src, dest)) + ## * `createSymlink proc <#createSymlink,string,string>`_ + when defined(Windows): + when useWinUnicode: + var wSrc = newWideCString(src) + var wDst = newWideCString(dest) + if createHardLinkW(wDst, wSrc, nil) == 0: + raiseOSError(osLastError(), $(src, dest)) + else: + if createHardLinkA(dest, src, nil) == 0: + raiseOSError(osLastError(), $(src, dest)) else: if link(src, dest) != 0: raiseOSError(osLastError(), $(src, dest)) +proc copyFileWithPermissions*(source, dest: string, + ignorePermissionErrors = true) {.noWeirdTarget.} = + ## Copies a file from `source` to `dest` preserving file permissions. + ## + ## This is a wrapper proc around `copyFile <#copyFile,string,string>`_, + ## `getFilePermissions <#getFilePermissions,string>`_ and + ## `setFilePermissions<#setFilePermissions,string,set[FilePermission]>`_ + ## procs on non-Windows platforms. + ## + ## On Windows this proc is just a wrapper for `copyFile proc + ## <#copyFile,string,string>`_ since that proc already copies attributes. + ## + ## On non-Windows systems permissions are copied after the file itself has + ## been copied, which won't happen atomically and could lead to a race + ## condition. If `ignorePermissionErrors` is true (default), errors while + ## reading/setting file attributes will be ignored, otherwise will raise + ## `OSError`. + ## + ## See also: + ## * `copyFile proc <#copyFile,string,string>`_ + ## * `copyDir proc <#copyDir,string,string>`_ + ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ + ## * `removeFile proc <#removeFile,string>`_ + ## * `moveFile proc <#moveFile,string,string>`_ + ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ + copyFile(source, dest) + when not defined(Windows): + try: + setFilePermissions(dest, getFilePermissions(source)) + except: + if not ignorePermissionErrors: + raise + +proc copyDirWithPermissions*(source, dest: string, + ignorePermissionErrors = true) {.rtl, extern: "nos$1", + tags: [WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = + ## Copies a directory from `source` to `dest` preserving file permissions. + ## + ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir + ## <#copyDir,string,string>`_ and `copyFileWithPermissions + ## <#copyFileWithPermissions,string,string>`_ procs + ## on non-Windows platforms. + ## + ## On Windows this proc is just a wrapper for `copyDir proc + ## <#copyDir,string,string>`_ since that proc already copies attributes. + ## + ## On non-Windows systems permissions are copied after the file or directory + ## itself has been copied, which won't happen atomically and could lead to a + ## race condition. If `ignorePermissionErrors` is true (default), errors while + ## reading/setting file attributes will be ignored, otherwise will raise + ## `OSError`. + ## + ## See also: + ## * `copyDir proc <#copyDir,string,string>`_ + ## * `copyFile proc <#copyFile,string,string>`_ + ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ + ## * `removeDir proc <#removeDir,string>`_ + ## * `moveDir proc <#moveDir,string,string>`_ + ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ + ## * `createDir proc <#createDir,string>`_ + createDir(dest) + when not defined(Windows): + try: + setFilePermissions(dest, getFilePermissions(source)) + except: + if not ignorePermissionErrors: + raise + for kind, path in walkDir(source): + var noSource = splitPath(path).tail + case kind + of pcFile: + copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors) + of pcDir: + copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors) + else: discard + proc inclFilePermissions*(filename: string, permissions: set[FilePermission]) {. rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noWeirdTarget.} = @@ -495,11 +2524,280 @@ proc exclFilePermissions*(filename: string, ## setFilePermissions(filename, getFilePermissions(filename)-permissions) setFilePermissions(filename, getFilePermissions(filename)-permissions) +proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = + ## Returns a string representing the path to which the symbolic link points. + ## + ## On Windows this is a noop, ``symlinkPath`` is simply returned. + ## + ## See also: + ## * `createSymlink proc <#createSymlink,string,string>`_ + when defined(windows): + result = symlinkPath + else: + result = newString(maxSymlinkLen) + var len = readlink(symlinkPath, result, maxSymlinkLen) + if len < 0: + raiseOSError(osLastError(), symlinkPath) + if len > maxSymlinkLen: + result = newString(len+1) + len = readlink(symlinkPath, result, len) + setLen(result, len) + +proc parseCmdLine*(c: string): seq[string] {. + noSideEffect, rtl, extern: "nos$1".} = + ## Splits a `command line`:idx: into several components. + ## + ## **Note**: This proc is only occasionally useful, better use the + ## `parseopt module `_. + ## + ## On Windows, it uses the `following parsing rules + ## `_: + ## + ## * Arguments are delimited by white space, which is either a space or a tab. + ## * The caret character (^) is not recognized as an escape character or + ## delimiter. The character is handled completely by the command-line parser + ## in the operating system before being passed to the argv array in the + ## program. + ## * A string surrounded by double quotation marks ("string") is interpreted + ## as a single argument, regardless of white space contained within. A + ## quoted string can be embedded in an argument. + ## * A double quotation mark preceded by a backslash (\") is interpreted as a + ## literal double quotation mark character ("). + ## * Backslashes are interpreted literally, unless they immediately precede + ## a double quotation mark. + ## * If an even number of backslashes is followed by a double quotation mark, + ## one backslash is placed in the argv array for every pair of backslashes, + ## and the double quotation mark is interpreted as a string delimiter. + ## * If an odd number of backslashes is followed by a double quotation mark, + ## one backslash is placed in the argv array for every pair of backslashes, + ## and the double quotation mark is "escaped" by the remaining backslash, + ## causing a literal double quotation mark (") to be placed in argv. + ## + ## On Posix systems, it uses the following parsing rules: + ## Components are separated by whitespace unless the whitespace + ## occurs within ``"`` or ``'`` quotes. + ## + ## See also: + ## * `parseopt module `_ + ## * `paramCount proc <#paramCount>`_ + ## * `paramStr proc <#paramStr,int>`_ + ## * `commandLineParams proc <#commandLineParams>`_ + + result = @[] + var i = 0 + var a = "" + while true: + setLen(a, 0) + # eat all delimiting whitespace + while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i) + if i >= c.len: break + when defined(windows): + # parse a single argument according to the above rules: + var inQuote = false + while i < c.len: + case c[i] + of '\\': + var j = i + while j < c.len and c[j] == '\\': inc(j) + if j < c.len and c[j] == '"': + for k in 1..(j-i) div 2: a.add('\\') + if (j-i) mod 2 == 0: + i = j + else: + a.add('"') + i = j+1 + else: + a.add(c[i]) + inc(i) + of '"': + inc(i) + if not inQuote: inQuote = true + elif i < c.len and c[i] == '"': + a.add(c[i]) + inc(i) + else: + inQuote = false + break + of ' ', '\t': + if not inQuote: break + a.add(c[i]) + inc(i) + else: + a.add(c[i]) + inc(i) + else: + case c[i] + of '\'', '\"': + var delim = c[i] + inc(i) # skip ' or " + while i < c.len and c[i] != delim: + add a, c[i] + inc(i) + if i < c.len: inc(i) + else: + while i < c.len and c[i] > ' ': + add(a, c[i]) + inc(i) + add(result, a) + +when defined(nimdoc): + # Common forward declaration docstring block for parameter retrieval procs. + proc paramCount*(): int {.tags: [ReadIOEffect].} = + ## Returns the number of `command line arguments`:idx: given to the + ## application. + ## + ## Unlike `argc`:idx: in C, if your binary was called without parameters this + ## will return zero. + ## You can query each individual parameter with `paramStr proc <#paramStr,int>`_ + ## or retrieve all of them in one go with `commandLineParams proc + ## <#commandLineParams>`_. + ## + ## **Availability**: When generating a dynamic library (see `--app:lib`) on + ## Posix this proc is not defined. + ## Test for availability using `declared() `_. + ## + ## See also: + ## * `parseopt module `_ + ## * `parseCmdLine proc <#parseCmdLine,string>`_ + ## * `paramStr proc <#paramStr,int>`_ + ## * `commandLineParams proc <#commandLineParams>`_ + ## + ## **Examples:** + ## + ## .. code-block:: nim + ## when declared(paramCount): + ## # Use paramCount() here + ## else: + ## # Do something else! + + proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = + ## Returns the `i`-th `command line argument`:idx: given to the application. + ## + ## `i` should be in the range `1..paramCount()`, the `IndexDefect` + ## exception will be raised for invalid values. Instead of iterating over + ## `paramCount() <#paramCount>`_ with this proc you can call the + ## convenience `commandLineParams() <#commandLineParams>`_. + ## + ## Similarly to `argv`:idx: in C, + ## it is possible to call ``paramStr(0)`` but this will return OS specific + ## contents (usually the name of the invoked executable). You should avoid + ## this and call `getAppFilename() <#getAppFilename>`_ instead. + ## + ## **Availability**: When generating a dynamic library (see `--app:lib`) on + ## Posix this proc is not defined. + ## Test for availability using `declared() `_. + ## + ## See also: + ## * `parseopt module `_ + ## * `parseCmdLine proc <#parseCmdLine,string>`_ + ## * `paramCount proc <#paramCount>`_ + ## * `commandLineParams proc <#commandLineParams>`_ + ## * `getAppFilename proc <#getAppFilename>`_ + ## + ## **Examples:** + ## + ## .. code-block:: nim + ## when declared(paramStr): + ## # Use paramStr() here + ## else: + ## # Do something else! + +elif defined(nimscript): discard +elif defined(nintendoswitch) or weirdTarget: + proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = + raise newException(OSError, "paramStr is not implemented on Nintendo Switch") + + proc paramCount*(): int {.tags: [ReadIOEffect].} = + raise newException(OSError, "paramCount is not implemented on Nintendo Switch") + +elif defined(windows): + # Since we support GUI applications with Nim, we sometimes generate + # a WinMain entry proc. But a WinMain proc has no access to the parsed + # command line arguments. The way to get them differs. Thus we parse them + # ourselves. This has the additional benefit that the program's behaviour + # is always the same -- independent of the used C compiler. + var + ownArgv {.threadvar.}: seq[string] + ownParsedArgv {.threadvar.}: bool + + proc paramCount*(): int {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = + # Docstring in nimdoc block. + if not ownParsedArgv: + ownArgv = parseCmdLine($getCommandLine()) + ownParsedArgv = true + result = ownArgv.len-1 + + proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1", + tags: [ReadIOEffect].} = + # Docstring in nimdoc block. + if not ownParsedArgv: + ownArgv = parseCmdLine($getCommandLine()) + ownParsedArgv = true + if i < ownArgv.len and i >= 0: return TaintedString(ownArgv[i]) + raise newException(IndexDefect, formatErrorIndexBound(i, ownArgv.len-1)) + +elif defined(genode): + proc paramStr*(i: int): TaintedString = + raise newException(OSError, "paramStr is not implemented on Genode") + + proc paramCount*(): int = + raise newException(OSError, "paramCount is not implemented on Genode") + +elif not defined(createNimRtl) and + not(defined(posix) and appType == "lib"): + # On Posix, there is no portable way to get the command line from a DLL. + var + cmdCount {.importc: "cmdCount".}: cint + cmdLine {.importc: "cmdLine".}: cstringArray + + proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = + # Docstring in nimdoc block. + if i < cmdCount and i >= 0: return TaintedString($cmdLine[i]) + raise newException(IndexDefect, formatErrorIndexBound(i, cmdCount-1)) + + proc paramCount*(): int {.tags: [ReadIOEffect].} = + # Docstring in nimdoc block. + result = cmdCount-1 + +when declared(paramCount) or defined(nimdoc): + proc commandLineParams*(): seq[TaintedString] = + ## Convenience proc which returns the command line parameters. + ## + ## This returns **only** the parameters. If you want to get the application + ## executable filename, call `getAppFilename() <#getAppFilename>`_. + ## + ## **Availability**: On Posix there is no portable way to get the command + ## line from a DLL and thus the proc isn't defined in this environment. You + ## can test for its availability with `declared() + ## `_. + ## + ## See also: + ## * `parseopt module `_ + ## * `parseCmdLine proc <#parseCmdLine,string>`_ + ## * `paramCount proc <#paramCount>`_ + ## * `paramStr proc <#paramStr,int>`_ + ## * `getAppFilename proc <#getAppFilename>`_ + ## + ## **Examples:** + ## + ## .. code-block:: nim + ## when declared(commandLineParams): + ## # Use commandLineParams() here + ## else: + ## # Do something else! + result = @[] + for i in 1..paramCount(): + result.add(paramStr(i)) +else: + proc commandLineParams*(): seq[TaintedString] {.error: + "commandLineParams() unsupported by dynamic libraries".} = + discard + when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netbsd)): proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t, newp: pointer, newplen: csize_t): cint {.importc: "sysctl",header: """#include - #include """.} + #include """} const CTL_KERN = 1 KERN_PROC = 14 @@ -539,10 +2837,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.cstring, maxSymlinkLen) + var len = readlink(procPath, result, maxSymlinkLen) if len > maxSymlinkLen: result = newString(len+1) - len = readlink(procPath, result.cstring, len) + len = readlink(procPath, result, len) setLen(result, len) when not weirdTarget and defined(openbsd): @@ -553,7 +2851,7 @@ when not weirdTarget and defined(openbsd): # POSIX guaranties that this contains the executable # as it has been executed by the calling process - let exePath = paramStr(0) + let exePath = string(paramStr(0)) if len(exePath) == 0: return "" @@ -572,7 +2870,7 @@ when not weirdTarget and defined(openbsd): return expandFilename(result) # search in path - for p in split(getEnv("PATH"), {PathSep}): + for p in split(string(getEnv("PATH")), {PathSep}): var x = joinPath(p, exePath) if fileExists(x): return expandFilename(x) @@ -582,12 +2880,12 @@ when not weirdTarget and defined(openbsd): when not (defined(windows) or defined(macosx) or weirdTarget): proc getApplHeuristic(): string = when declared(paramStr): - result = paramStr(0) + result = string(paramStr(0)) # POSIX guaranties that this contains the executable # as it has been executed by the calling process if len(result) > 0 and result[0] != DirSep: # not an absolute path? # iterate over any path in the $PATH environment variable - for p in split(getEnv("PATH"), {PathSep}): + for p in split(string(getEnv("PATH")), {PathSep}): var x = joinPath(p, result) if fileExists(x): return x else: @@ -611,7 +2909,7 @@ when defined(haiku): B_FIND_PATH_IMAGE_PATH = 1000 proc find_path(codePointer: pointer, baseDirectory: cint, subPath: cstring, - pathBuffer: cstring, bufferSize: csize_t): int32 + pathBuffer: cstring, bufferSize: csize): int32 {.importc, header: "".} proc getApplHaiku(): string = @@ -628,8 +2926,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW ## This proc will resolve symlinks. ## ## See also: - ## * `getAppDir proc`_ - ## * `getCurrentCompilerExe proc`_ + ## * `getAppDir proc <#getAppDir>`_ + ## * `getCurrentCompilerExe proc <#getCurrentCompilerExe>`_ # Linux: /proc//exe # Solaris: @@ -637,23 +2935,37 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW # /proc//path/a.out (complete pathname) when defined(windows): var bufsize = int32(MAX_PATH) - var buf = newWideCString("", bufsize) - while true: - var L = getModuleFileNameW(0, buf, bufsize) - if L == 0'i32: - result = "" # error! - break - elif L > bufsize: - buf = newWideCString("", L) - bufsize = L - else: - result = buf$L - break + when useWinUnicode: + var buf = newWideCString("", bufsize) + while true: + var L = getModuleFileNameW(0, buf, bufsize) + if L == 0'i32: + result = "" # error! + break + elif L > bufsize: + buf = newWideCString("", L) + bufsize = L + else: + result = buf$L + break + else: + result = newString(bufsize) + while true: + var L = getModuleFileNameA(0, result, bufsize) + if L == 0'i32: + result = "" # error! + break + elif L > bufsize: + result = newString(L) + bufsize = L + else: + setLen(result, L) + break elif defined(macosx): var size = cuint32(0) getExecPath1(nil, size) result = newString(int(size)) - if getExecPath2(result.cstring, size): + if getExecPath2(result, size): result = "" # error! if result.len > 0: result = result.expandFilename @@ -662,7 +2974,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW result = getApplAux("/proc/self/exe") elif defined(solaris): result = getApplAux("/proc/" & $getpid() & "/path/a.out") - elif defined(genode): + elif defined(genode) or defined(nintendoswitch): raiseOSError(OSErrorCode(-1), "POSIX command line not supported") elif defined(freebsd) or defined(dragonfly) or defined(netbsd): result = getApplFreebsd() @@ -670,8 +2982,6 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW result = getApplHaiku() elif defined(openbsd): result = getApplOpenBsd() - elif defined(nintendoswitch): - result = "" # little heuristic that may work on other POSIX-like systems: if result.len == 0: @@ -681,7 +2991,7 @@ proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdT ## Returns the directory of the application's executable. ## ## See also: - ## * `getAppFilename proc`_ + ## * `getAppFilename proc <#getAppFilename>`_ result = splitFile(getAppFilename()).dir proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTarget.} = @@ -705,12 +3015,13 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", result = rdFileSize(a) findClose(resA) else: - var rawInfo: Stat - if stat(file, rawInfo) < 0'i32: - raiseOSError(osLastError(), file) - rawInfo.st_size + var f: File + if open(f, file): + result = getFileSize(f) + close(f) + else: raiseOSError(osLastError(), file) -when defined(windows) or weirdTarget: +when defined(Windows) or weirdTarget: type DeviceId* = int32 FileId* = int64 @@ -724,9 +3035,9 @@ type ## Contains information associated with a file object. ## ## See also: - ## * `getFileInfo(handle) proc`_ - ## * `getFileInfo(file) proc`_ - ## * `getFileInfo(path, followSymlink) proc`_ + ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_ + ## * `getFileInfo(file) proc <#getFileInfo,File>`_ + ## * `getFileInfo(path) proc <#getFileInfo,string>`_ id*: tuple[device: DeviceId, file: FileId] ## Device and file id. kind*: PathComponent ## Kind of file object - directory, symlink, etc. size*: BiggestInt ## Size of file. @@ -737,22 +3048,13 @@ type creationTime*: times.Time ## Time file was created. Not supported on all systems! blockSize*: int ## Preferred I/O block size for this object. ## In some filesystems, this may vary from file to file. - isSpecial*: bool ## Is file special? (on Unix some "files" - ## can be special=non-regular like FIFOs, - ## devices); for directories `isSpecial` - ## is always `false`, for symlinks it is - ## the same as for the link's target. template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = ## Transforms the native file info structure into the one nim uses. ## 'rawInfo' is either a 'BY_HANDLE_FILE_INFORMATION' structure on Windows, ## or a 'Stat' structure on posix - when defined(windows): - template merge(a, b): untyped = - int64( - (uint64(cast[uint32](a))) or - (uint64(cast[uint32](b)) shl 32) - ) + when defined(Windows): + template merge(a, b): untyped = a or (b shl 32) formalInfo.id.device = rawInfo.dwVolumeSerialNumber formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh) formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh) @@ -760,7 +3062,7 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime)) formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime)) formalInfo.creationTime = fromWinTime(rdFileTime(rawInfo.ftCreationTime)) - formalInfo.blockSize = 8192 # xxx use Windows API instead of hardcoding + formalInfo.blockSize = 8192 # xxx use windows API instead of hardcoding # Retrieve basic permissions if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: @@ -802,14 +3104,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = checkAndIncludeMode(S_IWOTH, fpOthersWrite) checkAndIncludeMode(S_IXOTH, fpOthersExec) - (formalInfo.kind, formalInfo.isSpecial) = + formalInfo.kind = if S_ISDIR(rawInfo.st_mode): - (pcDir, false) + pcDir elif S_ISLNK(rawInfo.st_mode): assert(path != "") # symlinks can't occur for file handles getSymlinkFileKind(path) else: - (pcFile, not S_ISREG(rawInfo.st_mode)) + pcFile when defined(js): when not declared(FileHandle): @@ -825,11 +3127,11 @@ proc getFileInfo*(handle: FileHandle): FileInfo {.noWeirdTarget.} = ## is invalid, `OSError` is raised. ## ## See also: - ## * `getFileInfo(file) proc`_ - ## * `getFileInfo(path, followSymlink) proc`_ + ## * `getFileInfo(file) proc <#getFileInfo,File>`_ + ## * `getFileInfo(path) proc <#getFileInfo,string>`_ # Done: ID, Kind, Size, Permissions, Link Count - when defined(windows): + when defined(Windows): var rawInfo: BY_HANDLE_FILE_INFORMATION # We have to use the super special '_get_osfhandle' call (wrapped above) # To transform the C file descriptor to a native file handle. @@ -847,8 +3149,8 @@ proc getFileInfo*(file: File): FileInfo {.noWeirdTarget.} = ## Retrieves file information for the file object. ## ## See also: - ## * `getFileInfo(handle) proc`_ - ## * `getFileInfo(path, followSymlink) proc`_ + ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_ + ## * `getFileInfo(path) proc <#getFileInfo,string>`_ if file.isNil: raise newException(IOError, "File is nil") result = getFileInfo(file.getFileHandle()) @@ -857,22 +3159,21 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noWeirdTarget. ## Retrieves file information for the file object pointed to by `path`. ## ## Due to intrinsic differences between operating systems, the information - ## contained by the returned `FileInfo object`_ will be slightly + ## contained by the returned `FileInfo object <#FileInfo>`_ will be slightly ## different across platforms, and in some cases, incomplete or inaccurate. ## ## When `followSymlink` is true (default), symlinks are followed and the ## information retrieved is information related to the symlink's target. - ## Otherwise, information on the symlink itself is retrieved (however, - ## field `isSpecial` is still determined from the target on Unix). + ## Otherwise, information on the symlink itself is retrieved. ## ## If the information cannot be retrieved, such as when the path doesn't ## exist, or when permission restrictions prevent the program from retrieving ## file information, `OSError` is raised. ## ## See also: - ## * `getFileInfo(handle) proc`_ - ## * `getFileInfo(file) proc`_ - when defined(windows): + ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_ + ## * `getFileInfo(file) proc <#getFileInfo,File>`_ + when defined(Windows): var handle = openHandle(path, followSymlink) rawInfo: BY_HANDLE_FILE_INFORMATION @@ -898,7 +3199,7 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1", ## binary content. ## ## See also: - ## * `sameFile proc`_ + ## * `sameFile proc <#sameFile,string,string>`_ var a, b: File if not open(a, path1): return false @@ -944,8 +3245,11 @@ proc isHidden*(path: string): bool {.noWeirdTarget.} = assert not "".isHidden assert ".foo/".isHidden - when defined(windows): - wrapUnary(attributes, getFileAttributesW, path) + when defined(Windows): + when useWinUnicode: + wrapUnary(attributes, getFileAttributesW, path) + else: + var attributes = getFileAttributesA(path) if attributes != -1'i32: result = (attributes and FILE_ATTRIBUTE_HIDDEN) != 0'i32 else: @@ -981,45 +3285,28 @@ proc setLastModificationTime*(file: string, t: times.Time) {.noWeirdTarget.} = discard h.closeHandle if res == 0'i32: raiseOSError(osLastError(), file) - func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1, 1).} = - ## Returns `true` if `filename` is valid for crossplatform use. + ## Returns true if ``filename`` is valid for crossplatform use. ## ## This is useful if you want to copy or save files across Windows, Linux, Mac, etc. - ## It uses `invalidFilenameChars`, `invalidFilenames` and `maxLen` to verify the specified `filename`. + ## You can pass full paths as argument too, but func only checks filenames. + ## It uses ``invalidFilenameChars``, ``invalidFilenames`` and ``maxLen`` to verify the specified ``filename``. ## - ## See also: + ## .. code-block:: nim + ## assert not isValidFilename(" foo") ## Leading white space + ## assert not isValidFilename("foo ") ## Trailing white space + ## assert not isValidFilename("foo.") ## Ends with Dot + ## assert not isValidFilename("con.txt") ## "CON" is invalid (Windows) + ## assert not isValidFilename("OwO:UwU") ## ":" is invalid (Mac) + ## assert not isValidFilename("aux.bat") ## "AUX" is invalid (Windows) ## - ## * https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception - ## * https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file - ## * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx - ## - ## .. warning:: This only checks filenames, not whole paths - ## (because basically you can mount anything as a path on Linux). - runnableExamples: - assert not isValidFilename(" foo") # Leading white space - assert not isValidFilename("foo ") # Trailing white space - assert not isValidFilename("foo.") # Ends with dot - assert not isValidFilename("con.txt") # "CON" is invalid (Windows) - assert not isValidFilename("OwO:UwU") # ":" is invalid (Mac) - assert not isValidFilename("aux.bat") # "AUX" is invalid (Windows) - assert not isValidFilename("") # Empty string - assert not isValidFilename("foo/") # Filename is empty - + # https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception + # https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx result = true let f = filename.splitFile() - if unlikely(f.name.len + f.ext.len > maxLen or f.name.len == 0 or + if unlikely(f.name.len + f.ext.len > maxLen or f.name[0] == ' ' or f.name[^1] == ' ' or f.name[^1] == '.' or find(f.name, invalidFilenameChars) != -1): return false for invalid in invalidFilenames: if cmpIgnoreCase(f.name, invalid) == 0: return false - - -# deprecated declarations -when not defined(nimscript): - when not defined(js): # `noNimJs` doesn't work with templates, this should improve. - template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = - fileExists(args) - template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} = - dirExists(args) - # {.deprecated: [existsFile: fileExists].} # pending bug #14819; this would avoid above mentioned issue diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 915337f12..bc447eb67 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -31,12 +31,6 @@ else: when defined(linux) and defined(useClone): import linux -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - when defined(windows): - import std/widestrs - - type ProcessOption* = enum ## Options that can be passed to `startProcess proc ## <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_. @@ -72,17 +66,23 @@ type Process* = ref ProcessObj ## Represents an operating system process. +const poDemon* {.deprecated.} = poDaemon ## Nim versions before 0.20 + ## used the wrong spelling ("demon"). + ## Now `ProcessOption` uses the correct spelling ("daemon"), + ## and this is needed just for backward compatibility. + proc execProcess*(command: string, workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut, poUsePath, poEvalCommand}): - string {.rtl, extern: "nosp$1", raises: [OSError, IOError], + TaintedString {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadIOEffect, RootEffect].} ## A convenience procedure that executes ``command`` with ``startProcess`` ## and returns its output as a string. ## - ## .. warning:: This function uses `poEvalCommand` by default for backwards - ## compatibility. Make sure to pass options explicitly. + ## **WARNING:** This function uses `poEvalCommand` by default for backwards + ## compatibility. + ## Make sure to pass options explicitly. ## ## See also: ## * `startProcess proc @@ -120,7 +120,7 @@ proc execCmd*(command: string): int {.rtl, extern: "nosp$1", proc startProcess*(command: string, workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut}): - owned(Process) {.rtl, extern: "nosp$1", raises: [OSError, IOError], + owned(Process) {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect, RootEffect].} ## Starts a process. `Command` is the executable file, `workingDir` is the ## process's working directory. If ``workingDir == ""`` the current directory @@ -152,12 +152,12 @@ proc startProcess*(command: string, workingDir: string = "", ## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_ ## * `execCmd proc <#execCmd,string>`_ -proc close*(p: Process) {.rtl, extern: "nosp$1", raises: [IOError, OSError], tags: [WriteIOEffect].} +proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [WriteIOEffect].} ## When the process has finished executing, cleanup related handles. ## - ## .. warning:: If the process has not finished executing, this will forcibly - ## terminate the process. Doing so may result in zombie processes and - ## `pty leaks `_. + ## **WARNING:** If the process has not finished executing, this will forcibly + ## terminate the process. Doing so may result in zombie processes and + ## `pty leaks `_. proc suspend*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## Suspends the process `p`. @@ -201,7 +201,7 @@ proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## * `terminate proc <#terminate,Process>`_ ## * `posix_utils.sendSignal(pid: Pid, signal: int) `_ -proc running*(p: Process): bool {.rtl, extern: "nosp$1", raises: [OSError], tags: [].} +proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].} ## Returns true if the process `p` is still running. Returns immediately. proc processID*(p: Process): int {.rtl, extern: "nosp$1".} = @@ -212,20 +212,16 @@ proc processID*(p: Process): int {.rtl, extern: "nosp$1".} = return p.id proc waitForExit*(p: Process, timeout: int = -1): int {.rtl, - extern: "nosp$1", raises: [OSError, ValueError], tags: [].} + extern: "nosp$1", tags: [].} ## Waits for the process to finish and returns `p`'s error code. ## - ## .. warning:: Be careful when using `waitForExit` for processes created without - ## `poParentStreams` because they may fill output buffers, causing deadlock. + ## **WARNING**: Be careful when using `waitForExit` for processes created without + ## `poParentStreams` because they may fill output buffers, causing deadlock. ## ## On posix, if the process has exited because of a signal, 128 + signal ## number will be returned. - ## - ## .. warning:: When working with `timeout` parameters, remember that the value is - ## typically expressed in milliseconds, and ensure that the correct unit of time - ## is used to avoid unexpected behavior. -proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", raises: [OSError], tags: [].} +proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", tags: [].} ## Return `-1` if the process is still running. Otherwise the process' exit code. ## ## On posix, if the process has exited because of a signal, 128 + signal @@ -234,22 +230,22 @@ proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", raises: [OSError], proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## Returns ``p``'s input stream for writing to. ## - ## .. warning:: The returned `Stream` should not be closed manually as it - ## is closed when closing the Process ``p``. + ## **WARNING**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. ## ## See also: ## * `outputStream proc <#outputStream,Process>`_ ## * `errorStream proc <#errorStream,Process>`_ -proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", raises: [IOError, OSError], tags: [].} +proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## Returns ``p``'s output stream for reading from. ## ## You cannot perform peek/write/setOption operations to this stream. ## Use `peekableOutputStream proc <#peekableOutputStream,Process>`_ ## if you need to peek stream. ## - ## .. warning:: The returned `Stream` should not be closed manually as it - ## is closed when closing the Process ``p``. + ## **WARNING**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. ## ## See also: ## * `inputStream proc <#inputStream,Process>`_ @@ -262,8 +258,8 @@ proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## Use `peekableErrorStream proc <#peekableErrorStream,Process>`_ ## if you need to peek stream. ## - ## .. warning:: The returned `Stream` should not be closed manually as it - ## is closed when closing the Process ``p``. + ## **WARNING**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. ## ## See also: ## * `inputStream proc <#inputStream,Process>`_ @@ -274,8 +270,8 @@ proc peekableOutputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [] ## ## You can peek returned stream. ## - ## .. warning:: The returned `Stream` should not be closed manually as it - ## is closed when closing the Process ``p``. + ## **WARNING**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. ## ## See also: ## * `outputStream proc <#outputStream,Process>`_ @@ -286,19 +282,19 @@ proc peekableErrorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [], ## ## You can run peek operation to returned stream. ## - ## .. warning:: The returned `Stream` should not be closed manually as it - ## is closed when closing the Process ``p``. + ## **WARNING**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. ## ## See also: ## * `errorStream proc <#errorStream,Process>`_ ## * `peekableOutputStream proc <#peekableOutputStream,Process>`_ -proc inputHandle*(p: Process): FileHandle {.rtl, raises: [], extern: "nosp$1", +proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", tags: [].} = ## Returns ``p``'s input file handle for writing to. ## - ## .. warning:: The returned `FileHandle` should not be closed manually as - ## it is closed when closing the Process ``p``. + ## **WARNING**: The returned `FileHandle` should not be closed manually as + ## it is closed when closing the Process ``p``. ## ## See also: ## * `outputHandle proc <#outputHandle,Process>`_ @@ -306,11 +302,11 @@ proc inputHandle*(p: Process): FileHandle {.rtl, raises: [], extern: "nosp$1", result = p.inHandle proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", - raises: [], tags: [].} = + tags: [].} = ## Returns ``p``'s output file handle for reading from. ## - ## .. warning:: The returned `FileHandle` should not be closed manually as - ## it is closed when closing the Process ``p``. + ## **WARNING**: The returned `FileHandle` should not be closed manually as + ## it is closed when closing the Process ``p``. ## ## See also: ## * `inputHandle proc <#inputHandle,Process>`_ @@ -318,34 +314,29 @@ proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", result = p.outHandle proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", - raises: [], tags: [].} = + tags: [].} = ## Returns ``p``'s error file handle for reading from. ## - ## .. warning:: The returned `FileHandle` should not be closed manually as - ## it is closed when closing the Process ``p``. + ## **WARNING**: The returned `FileHandle` should not be closed manually as + ## it is closed when closing the Process ``p``. ## ## See also: ## * `inputHandle proc <#inputHandle,Process>`_ ## * `outputHandle proc <#outputHandle,Process>`_ result = p.errHandle -proc countProcessors*(): int {.rtl, extern: "nosp$1", raises: [].} = +proc countProcessors*(): int {.rtl, extern: "nosp$1".} = ## Returns the number of the processors/cores the machine has. ## Returns 0 if it cannot be detected. ## It is implemented just calling `cpuinfo.countProcessors`. result = cpuinfo.countProcessors() -when not defined(nimHasEffectsOf): - {.pragma: effectsOf.} - proc execProcesses*(cmds: openArray[string], options = {poStdErrToStdOut, poParentStreams}, n = countProcessors(), beforeRunEvent: proc(idx: int) = nil, afterRunEvent: proc(idx: int, p: Process) = nil): int {.rtl, extern: "nosp$1", - raises: [ValueError, OSError, IOError], - tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect], - effectsOf: [beforeRunEvent, afterRunEvent].} = + tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect].} = ## Executes the commands `cmds` in parallel. ## Creates `n` processes that execute in parallel. ## @@ -457,7 +448,7 @@ proc execProcesses*(cmds: openArray[string], if afterRunEvent != nil: afterRunEvent(i, p) close(p) -iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raises: [OSError, IOError, ValueError], tags: [ReadIOEffect].} = +iterator lines*(p: Process): string {.since: (1, 3), tags: [ReadIOEffect].} = ## Convenience iterator for working with `startProcess` to read data from a ## background process. ## @@ -480,14 +471,13 @@ iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raise ## p.close var outp = p.outputStream var line = newStringOfCap(120) - while outp.readLine(line): - if keepNewLines: - line.add("\n") - yield line - discard waitForExit(p) + while true: + if outp.readLine(line): + yield line + else: + if p.peekExitCode != -1: break -proc readLines*(p: Process): (seq[string], int) {.since: (1, 3), - raises: [OSError, IOError, ValueError], tags: [ReadIOEffect].} = +proc readLines*(p: Process): (seq[string], int) {.since: (1, 3).} = ## Convenience function for working with `startProcess` to read data from a ## background process. ## @@ -514,26 +504,25 @@ when not defined(useNimRtl): args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut, poUsePath, poEvalCommand}): - string = + TaintedString = var p = startProcess(command, workingDir = workingDir, args = args, env = env, options = options) var outp = outputStream(p) - result = "" - var line = newStringOfCap(120) - # consider `p.lines(keepNewLines=true)` to circumvent `running` busy-wait + result = TaintedString"" + var line = newStringOfCap(120).TaintedString while true: # FIXME: converts CR-LF to LF. if outp.readLine(line): - result.add(line) - result.add("\n") + result.string.add(line.string) + result.string.add("\n") elif not running(p): break close(p) template streamAccess(p) = assert poParentStreams notin p.options, "API usage error: stream access not allowed when you use poParentStreams" -when defined(windows) and not defined(useNimRtl): +when defined(Windows) and not defined(useNimRtl): # We need to implement a handle stream for Windows: type FileHandleStream = ref object of StreamObj @@ -574,8 +563,12 @@ when defined(windows) and not defined(useNimRtl): if a == 0: raiseOSError(osLastError()) proc newFileHandleStream(handle: Handle): owned FileHandleStream = - result = FileHandleStream(handle: handle, closeImpl: hsClose, atEndImpl: hsAtEnd, - readDataImpl: hsReadData, writeDataImpl: hsWriteData) + new(result) + result.handle = handle + result.closeImpl = hsClose + result.atEndImpl = hsAtEnd + result.readDataImpl = hsReadData + result.writeDataImpl = hsWriteData proc buildCommandLine(a: string, args: openArray[string]): string = result = quoteShell(a) @@ -718,15 +711,22 @@ when defined(windows) and not defined(useNimRtl): if len(workingDir) > 0: wd = workingDir if env != nil: e = buildEnv(env) if poEchoCmd in options: echo($cmdl) - var tmp = newWideCString(cmdl) - var ee = - if e.str.isNil: newWideCString(cstring(nil)) - else: newWideCString(e.str, e.len) - var wwd = newWideCString(wd) - var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT - if poDaemon in options: flags = flags or CREATE_NO_WINDOW - success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags, - ee, wwd, si, procInfo) + when useWinUnicode: + var tmp = newWideCString(cmdl) + var ee = + if e.str.isNil: newWideCString(cstring(nil)) + else: newWideCString(e.str, e.len) + var wwd = newWideCString(wd) + var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT + if poDaemon in options: flags = flags or CREATE_NO_WINDOW + success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags, + ee, wwd, si, procInfo) + else: + var ee = + if e.str.isNil: cstring(nil) + else: cstring(e.str) + success = winlean.createProcessA(nil, + cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, ee, wd, si, procInfo) let lastError = osLastError() if poParentStreams notin options: @@ -742,7 +742,7 @@ when defined(windows) and not defined(useNimRtl): const errFileNotFound = 2.int if lastError.int in {errInvalidParameter, errFileNotFound}: raiseOSError(lastError, - "Requested command not found: '" & command & "'. OS error:") + "Requested command not found: '$1'. OS error:" % command) else: raiseOSError(lastError, command) result.fProcessHandle = procInfo.hProcess @@ -867,9 +867,13 @@ when defined(windows) and not defined(useNimRtl): si.hStdError = getStdHandle(STD_ERROR_HANDLE) si.hStdInput = getStdHandle(STD_INPUT_HANDLE) si.hStdOutput = getStdHandle(STD_OUTPUT_HANDLE) - var c = newWideCString(command) - var res = winlean.createProcessW(nil, c, nil, nil, 0, - NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) + when useWinUnicode: + var c = newWideCString(command) + var res = winlean.createProcessW(nil, c, nil, nil, 0, + NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) + else: + var res = winlean.createProcessA(nil, command, nil, nil, 0, + NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) if res == 0: raiseOSError(osLastError()) else: @@ -928,7 +932,7 @@ elif not defined(useNimRtl): result = cast[cstringArray](alloc0((counter + 1) * sizeof(cstring))) var i = 0 for key, val in envPairs(): - var x = key & "=" & val + var x = key.string & "=" & val.string result[i] = cast[cstring](alloc(x.len+1)) copyMem(result[i], addr(x[0]), x.len+1) inc(i) @@ -946,13 +950,13 @@ elif not defined(useNimRtl): not defined(useClone) and not defined(linux) when useProcessAuxSpawn: proc startProcessAuxSpawn(data: StartProcessData): Pid {. - raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} + tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} else: proc startProcessAuxFork(data: StartProcessData): Pid {. - raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} + tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} {.push stacktrace: off, profiler: off.} proc startProcessAfterFork(data: ptr StartProcessData) {. - raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.} + tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.} {.pop.} proc startProcess(command: string, workingDir: string = "", @@ -1079,9 +1083,9 @@ elif not defined(useNimRtl): var pid: Pid if (poUsePath in data.options): - res = posix_spawnp(pid, data.sysCommand.cstring, fops, attr, data.sysArgs, data.sysEnv) + res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) else: - res = posix_spawn(pid, data.sysCommand.cstring, fops, attr, data.sysArgs, data.sysEnv) + res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) discard posix_spawn_file_actions_destroy(fops) discard posix_spawnattr_destroy(attr) @@ -1122,7 +1126,8 @@ elif not defined(useNimRtl): let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error)) if sizeRead == sizeof(error): raiseOSError(osLastError(), - "Could not find command: '" & $data.sysCommand & "'. OS error: " & $strerror(error)) + "Could not find command: '$1'. OS error: $2" % + [$data.sysCommand, $strerror(error)]) return pid @@ -1166,14 +1171,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.cstring, data.sysArgs, data.sysEnv) + discard execve(exe, 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.cstring, data.sysArgs) + discard execvp(data.sysCommand, data.sysArgs) else: - discard execve(data.sysCommand.cstring, data.sysArgs, data.sysEnv) + discard execve(data.sysCommand, data.sysArgs, data.sysEnv) startProcessFail(data) {.pop.} @@ -1229,7 +1234,7 @@ elif not defined(useNimRtl): when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(openbsd) or defined(dragonfly): - import kqueue + import kqueue, times proc waitForExit(p: Process, timeout: int = -1): int = if p.exitFlag: @@ -1515,7 +1520,7 @@ elif not defined(useNimRtl): header: "".} proc execCmd(command: string): int = - when defined(posix): + when defined(linux): let tmp = csystem(command) result = if tmp == -1: tmp else: exitStatusLikeShell(tmp) else: @@ -1567,8 +1572,8 @@ elif not defined(useNimRtl): proc execCmdEx*(command: string, options: set[ProcessOption] = { poStdErrToStdOut, poUsePath}, env: StringTableRef = nil, workingDir = "", input = ""): tuple[ - output: string, - exitCode: int] {.raises: [OSError, IOError], tags: + output: TaintedString, + exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = ## A convenience proc that runs the `command`, and returns its `output` and ## `exitCode`. `env` and `workingDir` params behave as for `startProcess`. @@ -1588,10 +1593,10 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## ## .. code-block:: Nim ## var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") - ## import std/[strutils, strtabs] + ## import strutils, strtabs ## stripLineEnd(result[0]) ## portable way to remove trailing newline, if any ## doAssert result == ("12", 0) - ## doAssert execCmdEx("ls --nonexistent").exitCode != 0 + ## doAssert execCmdEx("ls --nonexistant").exitCode != 0 ## when defined(posix): ## assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) ## assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) @@ -1614,13 +1619,12 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { inputStream(p).write(input) close inputStream(p) - # consider `p.lines(keepNewLines=true)` to avoid exit code test - result = ("", -1) - var line = newStringOfCap(120) + result = (TaintedString"", -1) + var line = newStringOfCap(120).TaintedString while true: if outp.readLine(line): - result[0].add(line) - result[0].add("\n") + result[0].string.add(line.string) + result[0].string.add("\n") else: result[1] = peekExitCode(p) if result[1] != -1: break diff --git a/lib/deprecated/pure/oswalkdir.nim b/lib/pure/oswalkdir.nim similarity index 84% rename from lib/deprecated/pure/oswalkdir.nim rename to lib/pure/oswalkdir.nim index 866f9ed70..7d80b312c 100644 --- a/lib/deprecated/pure/oswalkdir.nim +++ b/lib/pure/oswalkdir.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This module is deprecated, `import os` instead. +## This module is deprecated, ``import os`` instead. {.deprecated: "import os.nim instead".} import os export PathComponent, walkDir, walkDirRec diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 54584a253..79cf522f0 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -19,32 +19,37 @@ ## :literal: ## ## Here is an example of how to use the configuration file parser: -runnableExamples("-r:off"): - import std/[strutils, streams] - - let configFile = "example.ini" - var f = newFileStream(configFile, fmRead) - assert f != nil, "cannot open " & configFile - var p: CfgParser - open(p, f, configFile) - while true: - var e = next(p) - case e.kind - of cfgEof: break - of cfgSectionStart: ## a `[section]` has been parsed - echo "new section: " & e.section - of cfgKeyValuePair: - echo "key-value-pair: " & e.key & ": " & e.value - of cfgOption: - echo "command: " & e.key & ": " & e.value - of cfgError: - echo e.msg - close(p) - -##[ +## +## .. code-block:: nim +## +## import os, parsecfg, strutils, streams +## +## var f = newFileStream(paramStr(1), fmRead) +## if f != nil: +## var p: CfgParser +## open(p, f, paramStr(1)) +## while true: +## var e = next(p) +## case e.kind +## of cfgEof: break +## of cfgSectionStart: ## a ``[section]`` has been parsed +## echo("new section: " & e.section) +## of cfgKeyValuePair: +## echo("key-value-pair: " & e.key & ": " & e.value) +## of cfgOption: +## echo("command: " & e.key & ": " & e.value) +## of cfgError: +## echo(e.msg) +## close(p) +## else: +## echo("cannot open: " & paramStr(1)) +## +## +## Examples +## ======== +## ## Configuration file example -]## - +## -------------------------- ## ## .. code-block:: nim ## @@ -53,68 +58,64 @@ runnableExamples("-r:off"): ## name = "hello" ## --threads:on ## [Author] -## name = "nim-lang" -## website = "nim-lang.org" - -##[ +## name = "lihf8515" +## qq = "10214028" +## email = "lihaifeng@wxm.com" +## ## Creating a configuration file -]## - -runnableExamples: - var dict = newConfig() - dict.setSectionKey("","charset", "utf-8") - dict.setSectionKey("Package", "name", "hello") - dict.setSectionKey("Package", "--threads", "on") - dict.setSectionKey("Author", "name", "nim-lang") - dict.setSectionKey("Author", "website", "nim-lang.org") - assert $dict == """ -charset=utf-8 -[Package] -name=hello ---threads:on -[Author] -name=nim-lang -website=nim-lang.org -""" - -##[ +## ----------------------------- +## .. code-block:: nim +## +## import parsecfg +## var dict=newConfig() +## dict.setSectionKey("","charset","utf-8") +## dict.setSectionKey("Package","name","hello") +## dict.setSectionKey("Package","--threads","on") +## dict.setSectionKey("Author","name","lihf8515") +## dict.setSectionKey("Author","qq","10214028") +## dict.setSectionKey("Author","email","lihaifeng@wxm.com") +## dict.writeConfig("config.ini") +## ## Reading a configuration file -]## - -runnableExamples("-r:off"): - let dict = loadConfig("config.ini") - let charset = dict.getSectionValue("","charset") - let threads = dict.getSectionValue("Package","--threads") - let pname = dict.getSectionValue("Package","name") - let name = dict.getSectionValue("Author","name") - let website = dict.getSectionValue("Author","website") - echo pname & "\n" & name & "\n" & website - -##[ +## ---------------------------- +## .. code-block:: nim +## +## import parsecfg +## var dict = loadConfig("config.ini") +## var charset = dict.getSectionValue("","charset") +## var threads = dict.getSectionValue("Package","--threads") +## var pname = dict.getSectionValue("Package","name") +## var name = dict.getSectionValue("Author","name") +## var qq = dict.getSectionValue("Author","qq") +## var email = dict.getSectionValue("Author","email") +## echo pname & "\n" & name & "\n" & qq & "\n" & email +## ## Modifying a configuration file -]## - -runnableExamples("-r:off"): - var dict = loadConfig("config.ini") - dict.setSectionKey("Author", "name", "nim-lang") - dict.writeConfig("config.ini") - -##[ +## ------------------------------ +## .. code-block:: nim +## +## import parsecfg +## var dict = loadConfig("config.ini") +## dict.setSectionKey("Author","name","lhf") +## dict.writeConfig("config.ini") +## ## Deleting a section key in a configuration file -]## - -runnableExamples("-r:off"): - var dict = loadConfig("config.ini") - dict.delSectionKey("Author", "website") - dict.writeConfig("config.ini") - -##[ +## ---------------------------------------------- +## .. code-block:: nim +## +## import parsecfg +## var dict = loadConfig("config.ini") +## dict.delSectionKey("Author","email") +## dict.writeConfig("config.ini") +## ## Supported INI File structure -]## +## ---------------------------- +## The examples below are supported: +## # taken from https://docs.python.org/3/library/configparser.html#supported-ini-file-structure runnableExamples: - import std/streams + import streams var dict = loadConfig(newStringStream("""[Simple Values] key=value @@ -149,38 +150,33 @@ runnableExamples: ) let section1 = "Simple Values" - assert dict.getSectionValue(section1, "key") == "value" - assert dict.getSectionValue(section1, "spaces in keys") == "allowed" - assert dict.getSectionValue(section1, "spaces in values") == "allowed as well" - assert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously" - assert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values" + doAssert dict.getSectionValue(section1, "key") == "value" + doAssert dict.getSectionValue(section1, "spaces in keys") == "allowed" + doAssert dict.getSectionValue(section1, "spaces in values") == "allowed as well" + doAssert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously" + doAssert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values" let section2 = "All Values Are Strings" - assert dict.getSectionValue(section2, "values like this") == "19990429" - assert dict.getSectionValue(section2, "or this") == "3.14159265359" - assert dict.getSectionValue(section2, "are they treated as numbers") == "no" - assert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings" - assert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true" + doAssert dict.getSectionValue(section2, "values like this") == "19990429" + doAssert dict.getSectionValue(section2, "or this") == "3.14159265359" + doAssert dict.getSectionValue(section2, "are they treated as numbers") == "no" + doAssert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings" + doAssert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true" let section3 = "Seletion A" - assert dict.getSectionValue(section3, + doAssert dict.getSectionValue(section3, "space around section name will be ignored", "not an empty value") == "" let section4 = "Sections Can Be Indented" - assert dict.getSectionValue(section4, "can_values_be_as_well") == "True" - assert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False" - assert dict.getSectionValue(section4, "purpose") == "formatting for readability" + doAssert dict.getSectionValue(section4, "can_values_be_as_well") == "True" + doAssert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False" + doAssert dict.getSectionValue(section4, "purpose") == "formatting for readability" import strutils, lexbase, streams, tables import std/private/decode_helpers -import std/private/since - -when defined(nimPreviewSlimSystem): - import std/syncio include "system/inclrtl" - type CfgEventKind* = enum ## enumeration of all events that may occur when parsing cfgEof, ## end of file reached @@ -499,17 +495,17 @@ proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} = # ---------------- Configuration file related operations ---------------- type - Config* = OrderedTableRef[string, OrderedTableRef[string, string]] + Config* = OrderedTableRef[string, OrderedTableRef[string, string]] proc newConfig*(): Config = ## Creates a new configuration table. ## Useful when wanting to create a configuration file. - result = newOrderedTable[string, OrderedTableRef[string, string]]() + result = newOrderedTable[string, OrderedTableRef[string, string]]() -proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config = +proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config = ## Loads the specified configuration from stream into a new Config instance. ## `filename` parameter is only used for nicer error messages. - var dict = newOrderedTable[string, OrderedTableRef[string, string]]() + var dict = newOrderedTable[string, OrderedTableRef[string, string]]() var curSection = "" ## Current section, ## the default value of the current section is "", ## which means that the current section is a common @@ -539,7 +535,7 @@ proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config = close(p) result = dict -proc loadConfig*(filename: string): Config = +proc loadConfig*(filename: string): Config = ## Loads the specified configuration file into a new Config instance. let file = open(filename, fmRead) let fileStream = newFileStream(file) @@ -567,7 +563,7 @@ proc replace(s: string): string = proc writeConfig*(dict: Config, stream: Stream) = ## Writes the contents of the table to the specified stream. ## - ## .. note:: Comment statement will be ignored. + ## **Note:** Comment statement will be ignored. for section, sectionData in dict.pairs(): if section != "": ## Not general section if not allCharsInSet(section, SymChars): ## Non system character @@ -607,7 +603,7 @@ proc writeConfig*(dict: Config, stream: Stream) = proc `$`*(dict: Config): string = ## Writes the contents of the table to string. ## - ## .. note:: Comment statement will be ignored. + ## **Note:** Comment statement will be ignored. let stream = newStringStream() defer: stream.close() dict.writeConfig(stream) @@ -616,7 +612,7 @@ proc `$`*(dict: Config): string = proc writeConfig*(dict: Config, filename: string) = ## Writes the contents of the table to the specified configuration file. ## - ## .. note:: Comment statement will be ignored. + ## **Note:** Comment statement will be ignored. let file = open(filename, fmWrite) defer: file.close() let fileStream = newFileStream(file) @@ -653,8 +649,3 @@ proc delSectionKey*(dict: var Config, section, key: string) = dict.del(section) else: dict[section].del(key) - -iterator sections*(dict: Config): lent string {.since: (1, 5).} = - ## Iterates through the sections in the `dict`. - for section in dict.keys: - yield section diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index a8d1cfaab..4a7117dc5 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -14,9 +14,9 @@ ## =========== ## ## .. code-block:: nim -## import std/parsecsv -## from std/os import paramStr -## from std/streams import newFileStream +## import parsecsv +## from os import paramStr +## from streams import newFileStream ## ## var s = newFileStream(paramStr(1), fmRead) ## if s == nil: @@ -34,7 +34,7 @@ ## reference for item access with `rowEntry <#rowEntry,CsvParser,string>`_: ## ## .. code-block:: nim -## import std/parsecsv +## import parsecsv ## ## # Prepare a file ## let content = """One,Two,Three,Four @@ -65,10 +65,8 @@ ## * `parsesql module `_ for a SQL parser ## * `other parsers `_ for other parsers -import lexbase, streams - -when defined(nimPreviewSlimSystem): - import std/syncio +import + lexbase, streams type CsvRow* = seq[string] ## A row in a CSV file. @@ -99,10 +97,10 @@ proc raiseEInvalidCsv(filename: string, line, col: int, e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg raise e -proc error(self: CsvParser, pos: int, msg: string) = - raiseEInvalidCsv(self.filename, self.lineNumber, getColNumber(self, pos), msg) +proc error(my: CsvParser, pos: int, msg: string) = + raiseEInvalidCsv(my.filename, my.lineNumber, getColNumber(my, pos), msg) -proc open*(self: var CsvParser, input: Stream, filename: string, +proc open*(my: var CsvParser, input: Stream, filename: string, separator = ',', quote = '"', escape = '\0', skipInitialSpace = false) = ## Initializes the parser with an input stream. `Filename` is only used @@ -122,27 +120,29 @@ proc open*(self: var CsvParser, input: Stream, filename: string, ## * `open proc <#open,CsvParser,string,char,char,char>`_ which creates the ## file stream for you runnableExamples: - import std/streams + import streams var strm = newStringStream("One,Two,Three\n1,2,3\n10,20,30") var parser: CsvParser parser.open(strm, "tmp.csv") parser.close() strm.close() - lexbase.open(self, input) - self.filename = filename - self.sep = separator - self.quote = quote - self.esc = escape - self.skipWhite = skipInitialSpace + lexbase.open(my, input) + my.filename = filename + my.sep = separator + my.quote = quote + my.esc = escape + my.skipWhite = skipInitialSpace + my.row = @[] + my.currRow = 0 -proc open*(self: var CsvParser, filename: string, +proc open*(my: var CsvParser, filename: string, separator = ',', quote = '"', escape = '\0', skipInitialSpace = false) = ## Similar to the `other open proc<#open,CsvParser,Stream,string,char,char,char>`_, ## but creates the file stream for you. runnableExamples: - from std/os import removeFile + from os import removeFile writeFile("tmp.csv", "One,Two,Three\n1,2,3\n10,20,300") var parser: CsvParser parser.open("tmp.csv") @@ -150,60 +150,60 @@ proc open*(self: var CsvParser, filename: string, removeFile("tmp.csv") var s = newFileStream(filename, fmRead) - if s == nil: self.error(0, "cannot open: " & filename) - open(self, s, filename, separator, + if s == nil: my.error(0, "cannot open: " & filename) + open(my, s, filename, separator, quote, escape, skipInitialSpace) -proc parseField(self: var CsvParser, a: var string) = - var pos = self.bufpos - if self.skipWhite: - while self.buf[pos] in {' ', '\t'}: inc(pos) +proc parseField(my: var CsvParser, a: var string) = + var pos = my.bufpos + if my.skipWhite: + while my.buf[pos] in {' ', '\t'}: inc(pos) setLen(a, 0) # reuse memory - if self.buf[pos] == self.quote and self.quote != '\0': + if my.buf[pos] == my.quote and my.quote != '\0': inc(pos) while true: - let c = self.buf[pos] + let c = my.buf[pos] if c == '\0': - self.bufpos = pos # can continue after exception? - error(self, pos, self.quote & " expected") + my.bufpos = pos # can continue after exception? + error(my, pos, my.quote & " expected") break - elif c == self.quote: - if self.esc == '\0' and self.buf[pos + 1] == self.quote: - add(a, self.quote) + elif c == my.quote: + if my.esc == '\0' and my.buf[pos+1] == my.quote: + add(a, my.quote) inc(pos, 2) else: inc(pos) break - elif c == self.esc: - add(a, self.buf[pos + 1]) + elif c == my.esc: + add(a, my.buf[pos+1]) inc(pos, 2) else: case c of '\c': - pos = handleCR(self, pos) + pos = handleCR(my, pos) add(a, "\n") of '\l': - pos = handleLF(self, pos) + pos = handleLF(my, pos) add(a, "\n") else: add(a, c) inc(pos) else: while true: - let c = self.buf[pos] - if c == self.sep: break + let c = my.buf[pos] + if c == my.sep: break if c in {'\c', '\l', '\0'}: break add(a, c) inc(pos) - self.bufpos = pos + my.bufpos = pos -proc processedRows*(self: var CsvParser): int {.inline.} = +proc processedRows*(my: var CsvParser): int = ## Returns number of the processed rows. ## ## But even if `readRow <#readRow,CsvParser,int>`_ arrived at EOF then ## processed rows counter is incremented. runnableExamples: - import std/streams + import streams var strm = newStringStream("One,Two,Three\n1,2,3") var parser: CsvParser @@ -220,16 +220,16 @@ proc processedRows*(self: var CsvParser): int {.inline.} = parser.close() strm.close() - self.currRow + return my.currRow -proc readRow*(self: var CsvParser, columns = 0): bool = +proc readRow*(my: var CsvParser, columns = 0): bool = ## Reads the next row; if `columns` > 0, it expects the row to have ## exactly this many columns. Returns false if the end of the file ## has been encountered else true. ## ## Blank lines are skipped. runnableExamples: - import std/streams + import streams var strm = newStringStream("One,Two,Three\n1,2,3\n\n10,20,30") var parser: CsvParser parser.open(strm, "tmp.csv") @@ -251,52 +251,52 @@ proc readRow*(self: var CsvParser, columns = 0): bool = strm.close() var col = 0 # current column - let oldpos = self.bufpos + let oldpos = my.bufpos # skip initial empty lines #8365 while true: - case self.buf[self.bufpos] - of '\c': self.bufpos = handleCR(self, self.bufpos) - of '\l': self.bufpos = handleLF(self, self.bufpos) + case my.buf[my.bufpos] + of '\c': my.bufpos = handleCR(my, my.bufpos) + of '\l': my.bufpos = handleLF(my, my.bufpos) else: break - while self.buf[self.bufpos] != '\0': - let oldlen = self.row.len - if oldlen < col + 1: - setLen(self.row, col + 1) - self.row[col] = "" - parseField(self, self.row[col]) + while my.buf[my.bufpos] != '\0': + let oldlen = my.row.len + if oldlen < col+1: + setLen(my.row, col+1) + my.row[col] = "" + parseField(my, my.row[col]) inc(col) - if self.buf[self.bufpos] == self.sep: - inc(self.bufpos) + if my.buf[my.bufpos] == my.sep: + inc(my.bufpos) else: - case self.buf[self.bufpos] + case my.buf[my.bufpos] of '\c', '\l': # skip empty lines: while true: - case self.buf[self.bufpos] - of '\c': self.bufpos = handleCR(self, self.bufpos) - of '\l': self.bufpos = handleLF(self, self.bufpos) + case my.buf[my.bufpos] + of '\c': my.bufpos = handleCR(my, my.bufpos) + of '\l': my.bufpos = handleLF(my, my.bufpos) else: break of '\0': discard - else: error(self, self.bufpos, self.sep & " expected") + else: error(my, my.bufpos, my.sep & " expected") break - setLen(self.row, col) + setLen(my.row, col) result = col > 0 if result and col != columns and columns > 0: - error(self, oldpos + 1, $columns & " columns expected, but found " & + error(my, oldpos+1, $columns & " columns expected, but found " & $col & " columns") - inc(self.currRow) + inc(my.currRow) -proc close*(self: var CsvParser) {.inline.} = - ## Closes the parser `self` and its associated input stream. - lexbase.close(self) +proc close*(my: var CsvParser) {.inline.} = + ## Closes the parser `my` and its associated input stream. + lexbase.close(my) -proc readHeaderRow*(self: var CsvParser) = +proc readHeaderRow*(my: var CsvParser) = ## Reads the first row and creates a look-up table for column numbers ## See also: ## * `rowEntry proc <#rowEntry,CsvParser,string>`_ runnableExamples: - import std/streams + import streams var strm = newStringStream("One,Two,Three\n1,2,3") var parser: CsvParser @@ -313,19 +313,19 @@ proc readHeaderRow*(self: var CsvParser) = parser.close() strm.close() - let present = self.readRow() + let present = my.readRow() if present: - self.headers = self.row + my.headers = my.row -proc rowEntry*(self: var CsvParser, entry: string): var string = +proc rowEntry*(my: var CsvParser, entry: string): var string = ## Accesses a specified `entry` from the current row. ## ## Assumes that `readHeaderRow <#readHeaderRow,CsvParser>`_ has already been ## called. - ## + ## ## If specified `entry` does not exist, raises KeyError. runnableExamples: - import std/streams + import streams var strm = newStringStream("One,Two,Three\n1,2,3\n\n10,20,30") var parser: CsvParser parser.open(strm, "tmp.csv") @@ -340,9 +340,9 @@ proc rowEntry*(self: var CsvParser, entry: string): var string = parser.close() strm.close() - let index = self.headers.find(entry) + let index = my.headers.find(entry) if index >= 0: - result = self.row[index] + result = my.row[index] else: raise newException(KeyError, "Entry `" & entry & "` doesn't exist") diff --git a/lib/pure/parsejson.nim b/lib/pure/parsejson.nim index fcbcf8e36..8a0e04298 100644 --- a/lib/pure/parsejson.nim +++ b/lib/pure/parsejson.nim @@ -8,15 +8,12 @@ # ## This module implements a json parser. It is used -## and exported by the `json` standard library +## and exported by the ``json`` standard library ## module, but can also be used in its own right. import strutils, lexbase, streams, unicode import std/private/decode_helpers -when defined(nimPreviewSlimSystem): - import std/assertions - type JsonEventKind* = enum ## enumeration of all events that may occur when parsing jsonError, ## an error occurred during parsing @@ -24,13 +21,13 @@ type jsonString, ## a string literal jsonInt, ## an integer literal jsonFloat, ## a float literal - jsonTrue, ## the value `true` - jsonFalse, ## the value `false` - jsonNull, ## the value `null` - jsonObjectStart, ## start of an object: the `{` token - jsonObjectEnd, ## end of an object: the `}` token - jsonArrayStart, ## start of an array: the `[` token - jsonArrayEnd ## end of an array: the `]` token + jsonTrue, ## the value ``true`` + jsonFalse, ## the value ``false`` + jsonNull, ## the value ``null`` + jsonObjectStart, ## start of an object: the ``{`` token + jsonObjectEnd, ## end of an object: the ``}`` token + jsonArrayStart, ## start of an array: the ``[`` token + jsonArrayEnd ## end of an array: the ``]`` token TokKind* = enum # must be synchronized with TJsonEventKind! tkError, @@ -52,12 +49,12 @@ type errNone, ## no error errInvalidToken, ## invalid token errStringExpected, ## string expected - errColonExpected, ## `:` expected - errCommaExpected, ## `,` expected - errBracketRiExpected, ## `]` expected - errCurlyRiExpected, ## `}` expected - errQuoteExpected, ## `"` or `'` expected - errEOC_Expected, ## `*/` expected + errColonExpected, ## ``:`` expected + errCommaExpected, ## ``,`` expected + errBracketRiExpected, ## ``]`` expected + errCurlyRiExpected, ## ``}`` expected + errQuoteExpected, ## ``"`` or ``'`` expected + errEOC_Expected, ## ``*/`` expected errEofExpected, ## EOF expected errExprExpected ## expr expected @@ -74,7 +71,7 @@ type filename: string rawStringLiterals: bool - JsonKindError* = object of ValueError ## raised by the `to` macro if the + JsonKindError* = object of ValueError ## raised by the ``to`` macro if the ## JSON kind is incorrect. JsonParsingError* = object of ValueError ## is raised for a JSON error @@ -122,18 +119,18 @@ proc close*(my: var JsonParser) {.inline.} = lexbase.close(my) proc str*(my: JsonParser): string {.inline.} = - ## returns the character data for the events: `jsonInt`, `jsonFloat`, - ## `jsonString` + ## returns the character data for the events: ``jsonInt``, ``jsonFloat``, + ## ``jsonString`` assert(my.kind in {jsonInt, jsonFloat, jsonString}) return my.a proc getInt*(my: JsonParser): BiggestInt {.inline.} = - ## returns the number for the event: `jsonInt` + ## returns the number for the event: ``jsonInt`` assert(my.kind == jsonInt) return parseBiggestInt(my.a) proc getFloat*(my: JsonParser): float {.inline.} = - ## returns the number for the event: `jsonFloat` + ## returns the number for the event: ``jsonFloat`` assert(my.kind == jsonFloat) return parseFloat(my.a) @@ -154,7 +151,7 @@ proc getFilename*(my: JsonParser): string {.inline.} = result = my.filename proc errorMsg*(my: JsonParser): string = - ## returns a helpful error message for the event `jsonError` + ## returns a helpful error message for the event ``jsonError`` assert(my.kind == jsonError) result = "$1($2, $3) Error: $4" % [ my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]] @@ -221,7 +218,7 @@ proc parseString(my: var JsonParser): TokKind = add(my.a, 'u') inc(pos, 2) var pos2 = pos - var r = parseEscapedUTF16(cstring(my.buf), pos) + var r = parseEscapedUTF16(my.buf, pos) if r < 0: my.err = errInvalidToken break @@ -231,7 +228,7 @@ proc parseString(my: var JsonParser): TokKind = my.err = errInvalidToken break inc(pos, 2) - var s = parseEscapedUTF16(cstring(my.buf), pos) + var s = parseEscapedUTF16(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 059f8e0f5..94df5ea40 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -14,21 +14,21 @@ ## Supported Syntax ## ================ ## -## The following syntax is supported when arguments for the `shortNoVal` and -## `longNoVal` parameters, which are -## `described later<#nimshortnoval-and-nimlongnoval>`_, are not provided: +## The following syntax is supported when arguments for the ``shortNoVal`` and +## ``longNoVal`` parameters, which are +## `described later<#shortnoval-and-longnoval>`_, are not provided: ## -## 1. Short options: `-abcd`, `-e:5`, `-e=5` -## 2. Long options: `--foo:bar`, `--foo=bar`, `--foo` -## 3. Arguments: everything that does not start with a `-` +## 1. Short options: ``-abcd``, ``-e:5``, ``-e=5`` +## 2. Long options: ``--foo:bar``, ``--foo=bar``, ``--foo`` +## 3. Arguments: everything that does not start with a ``-`` ## ## These three kinds of tokens are enumerated in the ## `CmdLineKind enum<#CmdLineKind>`_. ## ## When option values begin with ':' or '=', they need to be doubled up (as in -## `--delim::`) or alternated (as in `--delim=:`). +## ``--delim::``) or alternated (as in ``--delim=:``). ## -## The `--` option, commonly used to denote that every token that follows is +## The ``--`` option, commonly used to denote that every token that follows is ## an argument, is interpreted as a long option, and its name is the empty ## string. ## @@ -39,17 +39,17 @@ ## created with `initOptParser<#initOptParser,string,set[char],seq[string]>`_, ## and `next<#next,OptParser>`_ advances the parser by one token. ## -## For each token, the parser's `kind`, `key`, and `val` fields give -## information about that token. If the token is a long or short option, `key` -## is the option's name, and `val` is either the option's value, if provided, -## or the empty string. For arguments, the `key` field contains the argument -## itself, and `val` is unused. To check if the end of the command line has -## been reached, check if `kind` is equal to `cmdEnd`. +## For each token, the parser's ``kind``, ``key``, and ``val`` fields give +## information about that token. If the token is a long or short option, ``key`` +## is the option's name, and ``val`` is either the option's value, if provided, +## or the empty string. For arguments, the ``key`` field contains the argument +## itself, and ``val`` is unused. To check if the end of the command line has +## been reached, check if ``kind`` is equal to ``cmdEnd``. ## ## Here is an example: ## ## .. code-block:: -## import std/parseopt +## import parseopt ## ## var p = initOptParser("-ab -e:5 --foo --bar=20 file.txt") ## while true: @@ -75,52 +75,31 @@ ## The `getopt iterator<#getopt.i,OptParser>`_, which is provided for ## convenience, can be used to iterate through all command line options as well. ## -## To set a default value for a variable assigned through `getopt` and accept arguments from the cmd line. -## Assign the default value to a variable before parsing. -## Then set the variable to the new value while parsing. +## ``shortNoVal`` and ``longNoVal`` +## ================================ ## -## Here is an example: -## .. code-block:: -## import std/parseopt -## -## var varName: string = "defaultValue" -## -## for kind, key, val in getopt(): -## case kind -## of cmdArgument: -## discard -## of cmdLongOption, cmdShortOption: -## case key: -## of "varName": # --varName: in the console when executing -## varName = val # do input sanitization in production systems -## of cmdEnd: -## discard -## -## `shortNoVal` and `longNoVal` -## ============================ -## -## The optional `shortNoVal` and `longNoVal` parameters present in +## The optional ``shortNoVal`` and ``longNoVal`` parameters present in ## `initOptParser<#initOptParser,string,set[char],seq[string]>`_ are for ## specifying which short and long options do not accept values. ## -## When `shortNoVal` is non-empty, users are not required to separate short +## When ``shortNoVal`` is non-empty, users are not required to separate short ## options and their values with a ':' or '=' since the parser knows which ## options accept values and which ones do not. This behavior also applies for -## long options if `longNoVal` is non-empty. For short options, `-j4` -## becomes supported syntax, and for long options, `--foo bar` becomes +## long options if ``longNoVal`` is non-empty. For short options, ``-j4`` +## becomes supported syntax, and for long options, ``--foo bar`` becomes ## supported. This is in addition to the `previously mentioned ## syntax<#supported-syntax>`_. Users can still separate options and their ## values with ':' or '=', but that becomes optional. ## ## As more options which do not accept values are added to your program, -## remember to amend `shortNoVal` and `longNoVal` accordingly. +## remember to amend ``shortNoVal`` and ``longNoVal`` accordingly. ## ## The following example illustrates the difference between having an empty -## `shortNoVal` and `longNoVal`, which is the default, and providing +## ``shortNoVal`` and ``longNoVal``, which is the default, and providing ## arguments for those two parameters: ## ## .. code-block:: -## import std/parseopt +## import parseopt ## ## proc printToken(kind: CmdLineKind, key: string, val: string) = ## case kind @@ -185,7 +164,7 @@ type ## ## To initialize it, use the ## `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_. - pos: int + pos*: int inShortState: bool allowWhitespaceAfterColon: bool shortNoVal: set[char] @@ -193,7 +172,7 @@ type cmds: seq[string] idx: int kind*: CmdLineKind ## The detected command line token - key*, val*: string ## Key and value pair; the key is the option + key*, val*: TaintedString ## Key and value pair; the key is the option ## or the argument, and the value is not "" if ## the option was given a value @@ -213,93 +192,101 @@ 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 = - ## Initializes the command line parser. - ## - ## If `cmdline.len == 0`, the real command line as provided by the - ## `os` module is retrieved instead if it is available. If the - ## command line is not available, a `ValueError` will be raised. - ## Behavior of the other parameters remains the same as in - ## `initOptParser(string, ...) - ## <#initOptParser,string,set[char],seq[string]>`_. - ## - ## See also: - ## * `getopt iterator<#getopt.i,seq[string],set[char],seq[string]>`_ - runnableExamples: - var p = initOptParser() - p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"]) - p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"], - shortNoVal = {'l'}, longNoVal = @["left"]) +when declared(os.paramCount): + # we cannot provide this for NimRtl creation on Posix, because we can't + # access the command line arguments then! - result.pos = 0 - result.idx = 0 - result.inShortState = false - result.shortNoVal = shortNoVal - result.longNoVal = longNoVal - result.allowWhitespaceAfterColon = allowWhitespaceAfterColon - if cmdline.len != 0: - result.cmds = newSeq[string](cmdline.len) - for i in 0..`_ for more information on + ## how this affects parsing. + ## + ## See also: + ## * `getopt iterator<#getopt.i,OptParser>`_ + runnableExamples: + var p = initOptParser() + p = initOptParser("--left --debug:3 -l -r:2") + 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: - # 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 = "" + result.cmds = newSeq[string](os.paramCount()) + for i in countup(1, os.paramCount()): + result.cmds[i-1] = os.paramStr(i).string -proc initOptParser*(cmdline = "", shortNoVal: set[char] = {}, - longNoVal: seq[string] = @[]; - allowWhitespaceAfterColon = true): OptParser = - ## Initializes the command line parser. - ## - ## If `cmdline == ""`, the real command line as provided by the - ## `os` module is retrieved instead if it is available. If the - ## command line is not available, a `ValueError` will be raised. - ## - ## `shortNoVal` and `longNoVal` are used to specify which options - ## do not take values. See the `documentation about these - ## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on - ## how this affects parsing. - ## - ## This does not provide a way of passing default values to arguments. - ## - ## See also: - ## * `getopt iterator<#getopt.i,OptParser>`_ - runnableExamples: - var p = initOptParser() - p = initOptParser("--left --debug:3 -l -r:2") - p = initOptParser("--left --debug:3 -l -r:2", - shortNoVal = {'l'}, longNoVal = @["left"]) + result.kind = cmdEnd + result.key = TaintedString"" + result.val = TaintedString"" - initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, allowWhitespaceAfterColon) + proc initOptParser*(cmdline: seq[TaintedString], shortNoVal: set[char] = {}, + longNoVal: seq[string] = @[]; + allowWhitespaceAfterColon = true): OptParser = + ## Initializes the command line parser. + ## + ## If ``cmdline.len == 0``, the real command line as provided by the + ## ``os`` module is retrieved instead. Behavior of the other parameters + ## remains the same as in `initOptParser(string, ...) + ## <#initOptParser,string,set[char],seq[string]>`_. + ## + ## See also: + ## * `getopt iterator<#getopt.i,seq[TaintedString],set[char],seq[string]>`_ + runnableExamples: + var p = initOptParser() + p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"]) + 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.len != 0: + result.cmds = newSeq[string](cmdline.len) + for i in 0..= p.cmds[p.idx].len: @@ -351,7 +338,7 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} = if i < p.cmds[p.idx].len and p.cmds[p.idx][i] == '-': p.kind = cmdLongOption inc(i) - i = parseWord(p.cmds[p.idx], i, p.key, {' ', '\t', ':', '='}) + i = parseWord(p.cmds[p.idx], i, p.key.string, {' ', '\t', ':', '='}) while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i) if i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {':', '='}: inc(i) @@ -362,12 +349,12 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} = inc p.idx i = 0 if p.idx < p.cmds.len: - p.val = p.cmds[p.idx].substr(i) - elif len(p.longNoVal) > 0 and p.key notin p.longNoVal and p.idx+1 < p.cmds.len: - p.val = p.cmds[p.idx+1] + p.val = TaintedString p.cmds[p.idx].substr(i) + elif len(p.longNoVal) > 0 and p.key.string notin p.longNoVal and p.idx+1 < p.cmds.len: + p.val = TaintedString p.cmds[p.idx+1] inc p.idx else: - p.val = "" + p.val = TaintedString"" inc p.idx p.pos = 0 else: @@ -375,29 +362,29 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} = handleShortOption(p, p.cmds[p.idx]) else: p.kind = cmdArgument - p.key = p.cmds[p.idx] + p.key = TaintedString p.cmds[p.idx] inc p.idx p.pos = 0 -when declared(quoteShellCommand): - proc cmdLineRest*(p: OptParser): string {.rtl, extern: "npo$1".} = - ## Retrieves the rest of the command line that has not been parsed yet. - ## - ## See also: - ## * `remainingArgs proc<#remainingArgs,OptParser>`_ - ## - ## **Examples:** - ## - ## .. code-block:: - ## var p = initOptParser("--left -r:2 -- foo.txt bar.txt") - ## while true: - ## p.next() - ## if p.kind == cmdLongOption and p.key == "": # Look for "--" - ## break - ## doAssert p.cmdLineRest == "foo.txt bar.txt" - result = p.cmds[p.idx .. ^1].quoteShellCommand +proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} = + ## Retrieves the rest of the command line that has not been parsed yet. + ## + ## See also: + ## * `remainingArgs proc<#remainingArgs,OptParser>`_ + ## + ## **Examples:** + ## + ## .. code-block:: + ## var p = initOptParser("--left -r:2 -- foo.txt bar.txt") + ## while true: + ## p.next() + ## if p.kind == cmdLongOption and p.key == "": # Look for "--" + ## break + ## else: continue + ## doAssert p.cmdLineRest == "foo.txt bar.txt" + result = p.cmds[p.idx .. ^1].quoteShellCommand.TaintedString -proc remainingArgs*(p: OptParser): seq[string] {.rtl, extern: "npo$1".} = +proc remainingArgs*(p: OptParser): seq[TaintedString] {.rtl, extern: "npo$1".} = ## Retrieves a sequence of the arguments that have not been parsed yet. ## ## See also: @@ -411,17 +398,17 @@ proc remainingArgs*(p: OptParser): seq[string] {.rtl, extern: "npo$1".} = ## p.next() ## if p.kind == cmdLongOption and p.key == "": # Look for "--" ## break + ## else: continue ## doAssert p.remainingArgs == @["foo.txt", "bar.txt"] result = @[] - for i in p.idx..`_. ## - ## There is no need to check for `cmdEnd` while iterating. If using `getopt` - ## with case switching, checking for `cmdEnd` is required. + ## There is no need to check for ``cmdEnd`` while iterating. ## ## See also: ## * `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_ @@ -455,54 +442,54 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, if p.kind == cmdEnd: break yield (p.kind, p.key, p.val) -iterator getopt*(cmdline: seq[string] = @[], - shortNoVal: set[char] = {}, longNoVal: seq[string] = @[]): - tuple[kind: CmdLineKind, key, val: string] = - ## Convenience iterator for iterating over command line arguments. - ## - ## This creates a new `OptParser<#OptParser>`_. If no command line - ## arguments are provided, the real command line as provided by the - ## `os` module is retrieved instead. - ## - ## `shortNoVal` and `longNoVal` are used to specify which options - ## do not take values. See the `documentation about these - ## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on - ## how this affects parsing. - ## - ## There is no need to check for `cmdEnd` while iterating. If using `getopt` - ## with case switching, checking for `cmdEnd` is required. - ## - ## See also: - ## * `initOptParser proc<#initOptParser,seq[string],set[char],seq[string]>`_ - ## - ## **Examples:** - ## - ## .. code-block:: - ## - ## # these are placeholders, of course - ## proc writeHelp() = discard - ## proc writeVersion() = discard - ## - ## var filename: string - ## let params = @["--left", "--debug:3", "-l", "-r:2"] - ## - ## for kind, key, val in getopt(params): - ## 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 written, so we show the help - ## writeHelp() - var p = initOptParser(cmdline, shortNoVal = shortNoVal, - longNoVal = longNoVal) - while true: - next(p) - if p.kind == cmdEnd: break - yield (p.kind, p.key, p.val) +when declared(initOptParser): + iterator getopt*(cmdline: seq[TaintedString] = commandLineParams(), + shortNoVal: set[char] = {}, longNoVal: seq[string] = @[]): + tuple[kind: CmdLineKind, key, val: TaintedString] = + ## Convenience iterator for iterating over command line arguments. + ## + ## This creates a new `OptParser<#OptParser>`_. If no command line + ## arguments are provided, the real command line as provided by the + ## ``os`` module is retrieved instead. + ## + ## ``shortNoVal`` and ``longNoVal`` are used to specify which options + ## do not take values. See the `documentation about these + ## parameters<#shortnoval-and-longnoval>`_ for more information on + ## how this affects parsing. + ## + ## There is no need to check for ``cmdEnd`` while iterating. + ## + ## See also: + ## * `initOptParser proc<#initOptParser,seq[TaintedString],set[char],seq[string]>`_ + ## + ## **Examples:** + ## + ## .. code-block:: + ## + ## # these are placeholders, of course + ## proc writeHelp() = discard + ## proc writeVersion() = discard + ## + ## var filename: string + ## let params = @["--left", "--debug:3", "-l", "-r:2"] + ## + ## for kind, key, val in getopt(params): + ## 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 written, so we show the help + ## writeHelp() + var p = initOptParser(cmdline, shortNoVal = shortNoVal, + longNoVal = longNoVal) + while true: + next(p) + if p.kind == cmdEnd: break + yield (p.kind, p.key, p.val) {.pop.} diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index 074faf64c..23d43dfe0 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## The `parsesql` module implements a high performance SQL file +## The ``parsesql`` module implements a high performance SQL file ## parser. It parses PostgreSQL syntax and the SQL ANSI standard. ## ## Unstable API. @@ -15,9 +15,6 @@ import strutils, lexbase import std/private/decode_helpers -when defined(nimPreviewSlimSystem): - import std/assertions - # ------------------- scanner ------------------------------------------------- type @@ -60,7 +57,7 @@ const reservedKeywords = @[ # statements - "select", "from", "where", "group", "limit", "offset", "having", + "select", "from", "where", "group", "limit", "having", # functions "count", ] @@ -509,7 +506,6 @@ type nkFromItemPair, nkGroup, nkLimit, - nkOffset, nkHaving, nkOrder, nkJoin, @@ -986,7 +982,7 @@ proc parseInsert(p: var SqlParser): SqlNode = parseParIdentList(p, n) result.add n else: - result.add(newNode(nkNone)) + result.add(nil) if isKeyw(p, "default"): getTok(p) eat(p, "values") @@ -1021,7 +1017,7 @@ proc parseUpdate(p: var SqlParser): SqlNode = if isKeyw(p, "where"): result.add(parseWhere(p)) else: - result.add(newNode(nkNone)) + result.add(nil) proc parseDelete(p: var SqlParser): SqlNode = getTok(p) @@ -1033,7 +1029,7 @@ proc parseDelete(p: var SqlParser): SqlNode = if isKeyw(p, "where"): result.add(parseWhere(p)) else: - result.add(newNode(nkNone)) + result.add(nil) proc parseSelect(p: var SqlParser): SqlNode = getTok(p) @@ -1127,11 +1123,6 @@ proc parseSelect(p: var SqlParser): SqlNode = var l = newNode(nkLimit) l.add(parseExpr(p)) result.add(l) - if isKeyw(p, "offset"): - getTok(p) - var o = newNode(nkOffset) - o.add(parseExpr(p)) - result.add(o) proc parseStmt(p: var SqlParser; parent: SqlNode) = if isKeyw(p, "create"): @@ -1188,12 +1179,9 @@ type proc add(s: var SqlWriter, thing: char) = s.buffer.add(thing) -proc prepareAdd(s: var SqlWriter) {.inline.} = +proc add(s: var SqlWriter, thing: string) = if s.buffer.len > 0 and s.buffer[^1] notin {' ', '\L', '(', '.'}: s.buffer.add(" ") - -proc add(s: var SqlWriter, thing: string) = - s.prepareAdd s.buffer.add(thing) proc addKeyw(s: var SqlWriter, thing: string) = @@ -1235,17 +1223,6 @@ proc addMulti(s: var SqlWriter, n: SqlNode, sep = ',', prefix, suffix: char) = proc quoted(s: string): string = "\"" & replace(s, "\"", "\"\"") & "\"" -func escape(result: var string; s: string) = - result.add('\'') - for c in items(s): - case c - of '\0'..'\31': - result.add("\\x") - result.add(toHex(ord(c), 2)) - of '\'': result.add("''") - else: result.add(c) - result.add('\'') - proc ra(n: SqlNode, s: var SqlWriter) = if n == nil: return case n.kind @@ -1258,8 +1235,7 @@ proc ra(n: SqlNode, s: var SqlWriter) = of nkQuotedIdent: s.add(quoted(n.strVal)) of nkStringLit: - s.prepareAdd - s.buffer.escape(n.strVal) + s.add(escape(n.strVal, "'", "'")) of nkBitStringLit: s.add("b'" & n.strVal & "'") of nkHexStringLit: @@ -1394,9 +1370,6 @@ proc ra(n: SqlNode, s: var SqlWriter) = of nkLimit: s.addKeyw("limit") s.addMulti(n) - of nkOffset: - s.addKeyw("offset") - s.addMulti(n) of nkHaving: s.addKeyw("having") s.addMulti(n) @@ -1463,7 +1436,7 @@ proc ra(n: SqlNode, s: var SqlWriter) = s.addKeyw("enum") rs(n, s) -proc renderSql*(n: SqlNode, upperCase = false): string = +proc renderSQL*(n: SqlNode, upperCase = false): string = ## Converts an SQL abstract syntax tree to its string representation. var s: SqlWriter s.buffer = "" @@ -1472,8 +1445,8 @@ proc renderSql*(n: SqlNode, upperCase = false): string = return s.buffer proc `$`*(n: SqlNode): string = - ## an alias for `renderSql`. - renderSql(n) + ## an alias for `renderSQL`. + renderSQL(n) proc treeReprAux(s: SqlNode, level: int, result: var string) = result.add('\n') @@ -1505,7 +1478,7 @@ proc open(p: var SqlParser, input: Stream, filename: string) = p.tok.literal = "" getTok(p) -proc parseSql*(input: Stream, filename: string): SqlNode = +proc parseSQL*(input: Stream, filename: string): SqlNode = ## parses the SQL from `input` into an AST and returns the AST. ## `filename` is only used for error messages. ## Syntax errors raise an `SqlParseError` exception. @@ -1516,8 +1489,8 @@ proc parseSql*(input: Stream, filename: string): SqlNode = finally: close(p) -proc parseSql*(input: string, filename = ""): SqlNode = +proc parseSQL*(input: string, filename = ""): SqlNode = ## parses the SQL from `input` into an AST and returns the AST. ## `filename` is only used for error messages. ## Syntax errors raise an `SqlParseError` exception. - parseSql(newStringStream(input), "") + parseSQL(newStringStream(input), "") diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index c4d59d892..5ceff26a0 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -26,7 +26,7 @@ ## ## .. code-block:: nim ## :test: -## from std/strutils import Digits, parseInt +## from strutils import Digits, parseInt ## ## let ## input1 = "2019 school start" @@ -50,8 +50,6 @@ include "system/inclrtl" -template toOa(s: string): openArray[char] = openArray[char](s) - const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'} IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} @@ -61,7 +59,8 @@ const proc toLower(c: char): char {.inline.} = result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c -proc parseBin*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} = +proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0, + maxLen = 0): int {.noSideEffect.} = ## Parses a binary number and stores its value in ``number``. ## ## Returns the number of the parsed characters or 0 in case of an error. @@ -90,7 +89,7 @@ proc parseBin*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): i var num64: int64 doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40 doAssert num64 == 336784608873 - var i = 0 + var i = start var output = T(0) var foundDigit = false let last = min(s.len, if maxLen == 0: s.len else: i + maxLen) @@ -105,9 +104,10 @@ proc parseBin*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): i inc(i) if foundDigit: number = output - result = i + result = i - start -proc parseOct*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} = +proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0, + maxLen = 0): int {.noSideEffect.} = ## Parses an octal number and stores its value in ``number``. ## ## Returns the number of the parsed characters or 0 in case of an error. @@ -136,7 +136,7 @@ proc parseOct*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): i var num64: int64 doAssert parseOct("2346475523464755", num64) == 16 doAssert num64 == 86216859871725 - var i = 0 + var i = start var output = T(0) var foundDigit = false let last = min(s.len, if maxLen == 0: s.len else: i + maxLen) @@ -151,9 +151,10 @@ proc parseOct*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): i inc(i) if foundDigit: number = output - result = i + result = i - start -proc parseHex*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} = +proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0, + maxLen = 0): int {.noSideEffect.} = ## Parses a hexadecimal number and stores its value in ``number``. ## ## Returns the number of the parsed characters or 0 in case of an error. @@ -183,7 +184,7 @@ proc parseHex*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): i var num64: int64 doAssert parseHex("4E69ED4E69ED", num64) == 12 doAssert num64 == 86216859871725 - var i = 0 + var i = start var output = T(0) var foundDigit = false let last = min(s.len, if maxLen == 0: s.len else: i + maxLen) @@ -205,9 +206,9 @@ proc parseHex*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): i inc(i) if foundDigit: number = output - result = i + result = i - start -proc parseIdent*(s: openArray[char], ident: var string): int = +proc parseIdent*(s: string, ident: var string, start = 0): int = ## Parses an identifier and stores it in ``ident``. Returns ## the number of the parsed characters or 0 in case of an error. ## If error, the value of `ident` is not changed. @@ -219,14 +220,14 @@ proc parseIdent*(s: openArray[char], ident: var string): int = doAssert res == "ello" doAssert parseIdent("Hello World", res, 6) == 5 doAssert res == "World" - var i = 0 + var i = start if i < s.len and s[i] in IdentStartChars: inc(i) while i < s.len and s[i] in IdentChars: inc(i) - ident = substr(s.toOpenArray(0, i-1)) - result = i + ident = substr(s, start, i-1) + result = i-start -proc parseIdent*(s: openArray[char]): string = +proc parseIdent*(s: string, start = 0): string = ## Parses an identifier and returns it or an empty string in ## case of an error. runnableExamples: @@ -235,13 +236,13 @@ proc parseIdent*(s: openArray[char]): string = doAssert parseIdent("Hello World", 5) == "" doAssert parseIdent("Hello World", 6) == "World" result = "" - var i = 0 + var i = start if i < s.len and s[i] in IdentStartChars: inc(i) while i < s.len and s[i] in IdentChars: inc(i) - result = substr(s.toOpenArray(0, i - 1)) + result = substr(s, start, i-1) -proc parseChar*(s: openArray[char], c: var char): int = +proc parseChar*(s: string, c: var char, start = 0): int = ## Parses a single character, stores it in `c` and returns 1. ## In case of error (if start >= s.len) it returns 0 ## and the value of `c` is unchanged. @@ -251,11 +252,11 @@ proc parseChar*(s: openArray[char], c: var char): int = doAssert c == '\0' doAssert "nim".parseChar(c, 0) == 1 doAssert c == 'n' - if s.len > 0: - c = s[0] + if start < s.len: + c = s[start] result = 1 -proc skipWhitespace*(s: openArray[char]): int {.inline.} = +proc skipWhitespace*(s: string, start = 0): int {.inline.} = ## Skips the whitespace starting at ``s[start]``. Returns the number of ## skipped characters. runnableExamples: @@ -264,9 +265,9 @@ proc skipWhitespace*(s: openArray[char]): int {.inline.} = doAssert skipWhitespace("Hello World", 5) == 1 doAssert skipWhitespace("Hello World", 5) == 2 result = 0 - while result < s.len and s[result] in Whitespace: inc(result) + while start+result < s.len and s[start+result] in Whitespace: inc(result) -proc skip*(s, token: openArray[char]): int {.inline.} = +proc skip*(s, token: string, start = 0): int {.inline.} = ## Skips the `token` starting at ``s[start]``. Returns the length of `token` ## or 0 if there was no `token` at ``s[start]``. runnableExamples: @@ -276,22 +277,22 @@ proc skip*(s, token: openArray[char]): int {.inline.} = doAssert skip("CAPlow", "CAP", 0) == 3 doAssert skip("CAPlow", "cap", 0) == 0 result = 0 - while result < s.len and result < token.len and - s[result] == token[result]: + while start+result < s.len and result < token.len and + s[result+start] == token[result]: inc(result) if result != token.len: result = 0 -proc skipIgnoreCase*(s, token: openArray[char]): int = +proc skipIgnoreCase*(s, token: string, start = 0): int = ## Same as `skip` but case is ignored for token matching. runnableExamples: doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3 doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3 result = 0 - while result < s.len and result < token.len and - toLower(s[result]) == toLower(token[result]): inc(result) + while start+result < s.len and result < token.len and + toLower(s[result+start]) == toLower(token[result]): inc(result) if result != token.len: result = 0 -proc skipUntil*(s: openArray[char], until: set[char]): int {.inline.} = +proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} = ## Skips all characters until one char from the set `until` is found ## or the end is reached. ## Returns number of characters skipped. @@ -300,9 +301,9 @@ proc skipUntil*(s: openArray[char], until: set[char]): int {.inline.} = doAssert skipUntil("Hello World", {'W'}, 0) == 6 doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6 result = 0 - while result < s.len and s[result] notin until: inc(result) + while start+result < s.len and s[result+start] notin until: inc(result) -proc skipUntil*(s: openArray[char], until: char): int {.inline.} = +proc skipUntil*(s: string, until: char, start = 0): int {.inline.} = ## Skips all characters until the char `until` is found ## or the end is reached. ## Returns number of characters skipped. @@ -312,23 +313,24 @@ proc skipUntil*(s: openArray[char], until: char): int {.inline.} = doAssert skipUntil("Hello World", 'W', 0) == 6 doAssert skipUntil("Hello World", 'w', 0) == 11 result = 0 - while result < s.len and s[result] != until: inc(result) + while start+result < s.len and s[result+start] != until: inc(result) -proc skipWhile*(s: openArray[char], toSkip: set[char]): int {.inline.} = - ## Skips all characters while one char from the set `toSkip` is found. +proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} = + ## Skips all characters while one char from the set `token` is found. ## Returns number of characters skipped. runnableExamples: doAssert skipWhile("Hello World", {'H', 'e'}) == 2 doAssert skipWhile("Hello World", {'e'}) == 0 doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3 result = 0 - while result < s.len and s[result] in toSkip: inc(result) + while start+result < s.len and s[result+start] in toSkip: inc(result) -proc fastSubstr(s: openArray[char]; token: var string; length: int) = +proc fastSubstr(s: string; token: var string; start, length: int) = token.setLen length - for i in 0 ..< length: token[i] = s[i] + for i in 0 ..< length: token[i] = s[i+start] -proc parseUntil*(s: openArray[char], token: var string, until: set[char]): int {.inline.} = +proc parseUntil*(s: string, token: var string, until: set[char], + start = 0): int {.inline.} = ## Parses a token and stores it in ``token``. Returns ## the number of the parsed characters or 0 in case of an error. A token ## consists of the characters notin `until`. @@ -340,13 +342,14 @@ proc parseUntil*(s: openArray[char], token: var string, until: set[char]): int { doAssert myToken == "Hello " doAssert parseUntil("Hello World", myToken, {'W', 'r'}, 3) == 3 doAssert myToken == "lo " - var i = 0 + var i = start while i < s.len and s[i] notin until: inc(i) - result = i - fastSubstr(s, token, result) + result = i-start + fastSubstr(s, token, start, result) #token = substr(s, start, i-1) -proc parseUntil*(s: openArray[char], token: var string, until: char): int {.inline.} = +proc parseUntil*(s: string, token: var string, until: char, + start = 0): int {.inline.} = ## Parses a token and stores it in ``token``. Returns ## the number of the parsed characters or 0 in case of an error. A token ## consists of any character that is not the `until` character. @@ -358,13 +361,14 @@ proc parseUntil*(s: openArray[char], token: var string, until: char): int {.inli doAssert myToken == "Hell" doAssert parseUntil("Hello World", myToken, 'o', 2) == 2 doAssert myToken == "ll" - var i = 0 + var i = start while i < s.len and s[i] != until: inc(i) - result = i - fastSubstr(s, token, result) + result = i-start + fastSubstr(s, token, start, result) #token = substr(s, start, i-1) -proc parseUntil*(s: openArray[char], token: var string, until: string): int {.inline.} = +proc parseUntil*(s: string, token: var string, until: string, + start = 0): int {.inline.} = ## Parses a token and stores it in ``token``. Returns ## the number of the parsed characters or 0 in case of an error. A token ## consists of any character that comes before the `until` token. @@ -378,7 +382,7 @@ proc parseUntil*(s: openArray[char], token: var string, until: string): int {.in if until.len == 0: token.setLen(0) return 0 - var i = 0 + var i = start while i < s.len: if until.len > 0 and s[i] == until[0]: var u = 1 @@ -386,11 +390,12 @@ proc parseUntil*(s: openArray[char], token: var string, until: string): int {.in inc u if u >= until.len: break inc(i) - result = i - fastSubstr(s, token, result) + result = i-start + fastSubstr(s, token, start, result) #token = substr(s, start, i-1) -proc parseWhile*(s: openArray[char], token: var string, validChars: set[char]): int {.inline.} = +proc parseWhile*(s: string, token: var string, validChars: set[char], + start = 0): int {.inline.} = ## Parses a token and stores it in ``token``. Returns ## the number of the parsed characters or 0 in case of an error. A token ## consists of the characters in `validChars`. @@ -400,22 +405,22 @@ proc parseWhile*(s: openArray[char], token: var string, validChars: set[char]): doAssert myToken.len() == 0 doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 6) == 3 doAssert myToken == "Wor" - var i = 0 + var i = start while i < s.len and s[i] in validChars: inc(i) - result = i - fastSubstr(s, token, result) + result = i-start + fastSubstr(s, token, start, result) #token = substr(s, start, i-1) -proc captureBetween*(s: openArray[char], first: char, second = '\0'): string = +proc captureBetween*(s: string, first: char, second = '\0', start = 0): string = ## Finds the first occurrence of ``first``, then returns everything from there ## up to ``second`` (if ``second`` is '\0', then ``first`` is used). runnableExamples: doAssert captureBetween("Hello World", 'e') == "llo World" doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo" - doAssert captureBetween("Hello World".toOpenArray(6, "Hello World".high), 'l') == "d" - var i = skipUntil(s, first) + 1 + doAssert captureBetween("Hello World", 'l', start = 6) == "d" + var i = skipUntil(s, first, start)+1+start result = "" - discard parseUntil(s.toOpenArray(i, s.high), result, if second == '\0': first else: second) + discard s.parseUntil(result, if second == '\0': first else: second, i) proc integerOutOfRangeError() {.noinline.} = raise newException(ValueError, "Parsed integer outside of valid range") @@ -424,10 +429,10 @@ proc integerOutOfRangeError() {.noinline.} = when defined(js): {.push overflowChecks: off.} -proc rawParseInt(s: openArray[char], b: var BiggestInt): int = +proc rawParseInt(s: string, b: var BiggestInt, start = 0): int = var sign: BiggestInt = -1 - i = 0 + i = start if i < s.len: if s[i] == '+': inc(i) elif s[i] == '-': @@ -447,30 +452,30 @@ proc rawParseInt(s: openArray[char], b: var BiggestInt): int = integerOutOfRangeError() else: b = b * sign - result = i + result = i - start when defined(js): {.pop.} # overflowChecks: off -proc parseBiggestInt*(s: openArray[char], number: var BiggestInt): int {. +proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {. rtl, extern: "npuParseBiggestInt", noSideEffect, raises: [ValueError].} = - ## Parses an integer and stores the value into `number`. + ## Parses an integer starting at `start` and stores the value into `number`. ## Result is the number of processed chars or 0 if there is no integer. ## `ValueError` is raised if the parsed integer is out of the valid range. runnableExamples: var res: BiggestInt - doAssert parseBiggestInt("9223372036854775807", res) == 19 + doAssert parseBiggestInt("9223372036854775807", res, 0) == 19 doAssert res == 9223372036854775807 var res = BiggestInt(0) # use 'res' for exception safety (don't write to 'number' in case of an # overflow exception): - result = rawParseInt(s, res) + result = rawParseInt(s, res, start) if result != 0: number = res -proc parseInt*(s: openArray[char], number: var int): int {. +proc parseInt*(s: string, number: var int, start = 0): int {. rtl, extern: "npuParseInt", noSideEffect, raises: [ValueError].} = - ## Parses an integer and stores the value into `number`. + ## Parses an integer starting at `start` and stores the value into `number`. ## Result is the number of processed chars or 0 if there is no integer. ## `ValueError` is raised if the parsed integer is out of the valid range. runnableExamples: @@ -480,14 +485,14 @@ proc parseInt*(s: openArray[char], number: var int): int {. doAssert parseInt("2019", res, 2) == 2 doAssert res == 19 var res = BiggestInt(0) - result = parseBiggestInt(s, res) + result = parseBiggestInt(s, res, start) when sizeof(int) <= 4: if res < low(int) or res > high(int): integerOutOfRangeError() if result != 0: number = int(res) -proc parseSaturatedNatural*(s: openArray[char], b: var int): int {. +proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {. raises: [].} = ## Parses a natural number into ``b``. This cannot raise an overflow ## error. ``high(int)`` is returned for an overflow. @@ -497,7 +502,7 @@ proc parseSaturatedNatural*(s: openArray[char], b: var int): int {. var res = 0 discard parseSaturatedNatural("848", res) doAssert res == 848 - var i = 0 + var i = start if i < s.len and s[i] == '+': inc(i) if i < s.len and s[i] in {'0'..'9'}: b = 0 @@ -509,13 +514,13 @@ proc parseSaturatedNatural*(s: openArray[char], b: var int): int {. b = high(int) inc(i) while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored - result = i + result = i - start -proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int = +proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int = var res = 0.BiggestUInt prev = 0.BiggestUInt - i = 0 + i = start if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}: integerOutOfRangeError() if i < s.len and s[i] == '+': inc(i) # Allow @@ -529,11 +534,11 @@ proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int = inc(i) while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored b = res - result = i + result = i - start -proc parseBiggestUInt*(s: openArray[char], number: var BiggestUInt): int {. +proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {. rtl, extern: "npuParseBiggestUInt", noSideEffect, raises: [ValueError].} = - ## Parses an unsigned integer and stores the value + ## Parses an unsigned integer starting at `start` and stores the value ## into `number`. ## `ValueError` is raised if the parsed integer is out of the valid range. runnableExamples: @@ -545,13 +550,13 @@ proc parseBiggestUInt*(s: openArray[char], number: var BiggestUInt): int {. var res = BiggestUInt(0) # use 'res' for exception safety (don't write to 'number' in case of an # overflow exception): - result = rawParseUInt(s, res) + result = rawParseUInt(s, res, start) if result != 0: number = res -proc parseUInt*(s: openArray[char], number: var uint): int {. +proc parseUInt*(s: string, number: var uint, start = 0): int {. rtl, extern: "npuParseUInt", noSideEffect, raises: [ValueError].} = - ## Parses an unsigned integer and stores the value + ## Parses an unsigned integer starting at `start` and stores the value ## into `number`. ## `ValueError` is raised if the parsed integer is out of the valid range. runnableExamples: @@ -561,22 +566,22 @@ proc parseUInt*(s: openArray[char], number: var uint): int {. doAssert parseUInt("3450", res, 2) == 2 doAssert res == 50 var res = BiggestUInt(0) - result = parseBiggestUInt(s, res) + result = parseBiggestUInt(s, res, start) when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4: if res > 0xFFFF_FFFF'u64: integerOutOfRangeError() if result != 0: number = uint(res) -proc parseBiggestFloat*(s: openArray[char], number: var BiggestFloat): int {. +proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {. magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.} - ## Parses a float and stores the value into `number`. + ## Parses a float starting at `start` and stores the value into `number`. ## Result is the number of processed chars or 0 if a parsing error ## occurred. -proc parseFloat*(s: openArray[char], number: var float): int {. +proc parseFloat*(s: string, number: var float, start = 0): int {. rtl, extern: "npuParseFloat", noSideEffect.} = - ## Parses a float and stores the value into `number`. + ## Parses a float starting at `start` and stores the value into `number`. ## Result is the number of processed chars or 0 if there occurred a parsing ## error. runnableExamples: @@ -588,75 +593,10 @@ proc parseFloat*(s: openArray[char], number: var float): int {. doAssert parseFloat("32.57", res, 3) == 2 doAssert res == 57.00 var bf = BiggestFloat(0.0) - result = parseBiggestFloat(s, bf) + result = parseBiggestFloat(s, bf, start) if result != 0: number = bf -func toLowerAscii(c: char): char = - if c in {'A'..'Z'}: char(uint8(c) xor 0b0010_0000'u8) else: c - -func parseSize*(s: openArray[char], size: var int64, alwaysBin=false): int = - ## Parse a size qualified by binary or metric units into `size`. This format - ## is often called "human readable". Result is the number of processed chars - ## or 0 on parse errors and size is rounded to the nearest integer. Trailing - ## garbage like "/s" in "1k/s" is allowed and detected by `result < s.len`. - ## - ## To simplify use, following non-rare wild conventions, and since fractional - ## data like milli-bytes is so rare, unit matching is case-insensitive but for - ## the 'i' distinguishing binary-metric from metric (which cannot be 'I'). - ## - ## An optional trailing 'B|b' is ignored but processed. I.e., you must still - ## know if units are bytes | bits or infer this fact via the case of s[^1] (if - ## users can even be relied upon to use 'B' for byte and 'b' for bit or have - ## that be s[^1]). - ## - ## If `alwaysBin==true` then scales are always binary-metric, but e.g. "KiB" - ## is still accepted for clarity. If the value would exceed the range of - ## `int64`, `size` saturates to `int64.high`. Supported metric prefix chars - ## include k, m, g, t, p, e, z, y (but z & y saturate unless the number is a - ## small fraction). - ## - ## **See also:** - ## * https://en.wikipedia.org/wiki/Binary_prefix - ## * `formatSize module`_ for formatting - runnableExamples: - var res: int64 # caller must still know if 'b' refers to bytes|bits - doAssert parseSize("10.5 MB", res) == 7 - doAssert res == 10_500_000 # decimal metric Mega prefix - doAssert parseSize("64 mib", res) == 6 - doAssert res == 67108864 # 64 shl 20 - doAssert parseSize("1G/h", res, true) == 2 # '/' stops parse - doAssert res == 1073741824 # 1 shl 30, forced binary metric - const prefix = "b" & "kmgtpezy" # byte|bit & lowCase metric-ish prefixes - const scaleM = [1.0, 1e3, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24] # 10^(3*idx) - const scaleB = [1.0, 1024, 1048576, 1073741824, 1099511627776.0, # 2^(10*idx) - 1125899906842624.0, 1152921504606846976.0, # ldexp? - 1.180591620717411303424e21, 1.208925819614629174706176e24] - var number: float - var scale = 1.0 - result = parseFloat(s, number) - if number < 0: # While parseFloat accepts negatives .. - result = 0 #.. we do not since sizes cannot be < 0 - if result > 0: - let start = result # Save spot to maybe unwind white to EOS - while result < s.len and s[result] in Whitespace: - inc result - if result < s.len: # Illegal starting char => unity - if (let si = prefix.find(s[result].toLowerAscii); si >= 0): - inc result # Now parse the scale - scale = if alwaysBin: scaleB[si] else: scaleM[si] - if result < s.len and s[result] == 'i': - scale = scaleB[si] # Switch from default to binary-metric - inc result - if result < s.len and s[result].toLowerAscii == 'b': - inc result # Skip optional '[bB]' - else: # Unwind result advancement when there.. - result = start #..is no unit to the end of `s`. - var sizeF = number * scale + 0.5 # Saturate to int64.high when too big - size = if sizeF > 9223372036854774784.0: int64.high else: sizeF.int64 -# Above constant=2^63-1024 avoids C UB; github.com/nim-lang/Nim/issues/20102 or -# stackoverflow.com/questions/20923556/math-pow2-63-1-math-pow2-63-512-is-true - type InterpolatedKind* = enum ## Describes for `interpolatedFragments` ## which part of the interpolated string is @@ -666,7 +606,7 @@ type ikVar, ## ``var`` part of the interpolated string ikExpr ## ``expr`` part of the interpolated string -iterator interpolatedFragments*(s: openArray[char]): tuple[kind: InterpolatedKind, +iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind, value: string] = ## Tokenizes the string `s` into substrings for interpolation purposes. ## @@ -701,7 +641,7 @@ iterator interpolatedFragments*(s: openArray[char]): tuple[kind: InterpolatedKin else: discard inc j raise newException(ValueError, - "Expected closing '}': " & substr(s.toOpenArray(i, s.high))) + "Expected closing '}': " & substr(s, i, s.high)) inc i, 2 # skip ${ kind = ikExpr elif j+1 < s.len and s[j+1] in IdentStartChars: @@ -715,370 +655,15 @@ iterator interpolatedFragments*(s: openArray[char]): tuple[kind: InterpolatedKin kind = ikDollar else: raise newException(ValueError, - "Unable to parse a variable name at " & substr(s.toOpenArray(i, s.high))) + "Unable to parse a variable name at " & substr(s, i, s.high)) else: while j < s.len and s[j] != '$': inc j kind = ikStr if j > i: # do not copy the trailing } for ikExpr: - yield (kind, substr(s.toOpenArray(i, j-1-ord(kind == ikExpr)))) + yield (kind, substr(s, i, j-1-ord(kind == ikExpr))) else: break i = j {.pop.} - - -proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0, - maxLen = 0): int {.noSideEffect.} = - ## Parses a binary number and stores its value in ``number``. - ## - ## Returns the number of the parsed characters or 0 in case of an error. - ## If error, the value of ``number`` is not changed. - ## - ## If ``maxLen == 0``, the parsing continues until the first non-bin character - ## or to the end of the string. Otherwise, no more than ``maxLen`` characters - ## are parsed starting from the ``start`` position. - ## - ## It does not check for overflow. If the value represented by the string is - ## too big to fit into ``number``, only the value of last fitting characters - ## will be stored in ``number`` without producing an error. - runnableExamples: - var num: int - doAssert parseBin("0100_1110_0110_1001_1110_1101", num) == 29 - doAssert num == 5138925 - doAssert parseBin("3", num) == 0 - var num8: int8 - doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8) == 32 - doAssert num8 == 0b1110_1101'i8 - doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8, 3, 9) == 9 - doAssert num8 == 0b0100_1110'i8 - var num8u: uint8 - doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8u) == 32 - doAssert num8u == 237 - var num64: int64 - doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40 - doAssert num64 == 336784608873 - parseBin(s.toOpenArray(start, s.high), number, maxLen) - -proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0, - maxLen = 0): int {.noSideEffect.} = - ## Parses an octal number and stores its value in ``number``. - ## - ## Returns the number of the parsed characters or 0 in case of an error. - ## If error, the value of ``number`` is not changed. - ## - ## If ``maxLen == 0``, the parsing continues until the first non-oct character - ## or to the end of the string. Otherwise, no more than ``maxLen`` characters - ## are parsed starting from the ``start`` position. - ## - ## It does not check for overflow. If the value represented by the string is - ## too big to fit into ``number``, only the value of last fitting characters - ## will be stored in ``number`` without producing an error. - runnableExamples: - var num: int - doAssert parseOct("0o23464755", num) == 10 - doAssert num == 5138925 - doAssert parseOct("8", num) == 0 - var num8: int8 - doAssert parseOct("0o_1464_755", num8) == 11 - doAssert num8 == -19 - doAssert parseOct("0o_1464_755", num8, 3, 3) == 3 - doAssert num8 == 102 - var num8u: uint8 - doAssert parseOct("1464755", num8u) == 7 - doAssert num8u == 237 - var num64: int64 - doAssert parseOct("2346475523464755", num64) == 16 - doAssert num64 == 86216859871725 - parseOct(s.toOpenArray(start, s.high), number, maxLen) - -proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0, - maxLen = 0): int {.noSideEffect.} = - ## Parses a hexadecimal number and stores its value in ``number``. - ## - ## Returns the number of the parsed characters or 0 in case of an error. - ## If error, the value of ``number`` is not changed. - ## - ## If ``maxLen == 0``, the parsing continues until the first non-hex character - ## or to the end of the string. Otherwise, no more than ``maxLen`` characters - ## are parsed starting from the ``start`` position. - ## - ## It does not check for overflow. If the value represented by the string is - ## too big to fit into ``number``, only the value of last fitting characters - ## will be stored in ``number`` without producing an error. - runnableExamples: - var num: int - doAssert parseHex("4E_69_ED", num) == 8 - doAssert num == 5138925 - doAssert parseHex("X", num) == 0 - doAssert parseHex("#ABC", num) == 4 - var num8: int8 - doAssert parseHex("0x_4E_69_ED", num8) == 11 - doAssert num8 == 0xED'i8 - doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2 - doAssert num8 == 0x4E'i8 - var num8u: uint8 - doAssert parseHex("0x_4E_69_ED", num8u) == 11 - doAssert num8u == 237 - var num64: int64 - doAssert parseHex("4E69ED4E69ED", num64) == 12 - doAssert num64 == 86216859871725 - parseHex(s.toOpenArray(start, s.high), number, maxLen) - -proc parseIdent*(s: string, ident: var string, start = 0): int = - ## Parses an identifier and stores it in ``ident``. Returns - ## the number of the parsed characters or 0 in case of an error. - ## If error, the value of `ident` is not changed. - runnableExamples: - var res: string - doAssert parseIdent("Hello World", res, 0) == 5 - doAssert res == "Hello" - doAssert parseIdent("Hello World", res, 1) == 4 - doAssert res == "ello" - doAssert parseIdent("Hello World", res, 6) == 5 - doAssert res == "World" - parseIdent(s.toOpenArray(start, s.high), ident) - -proc parseIdent*(s: string, start = 0): string = - ## Parses an identifier and returns it or an empty string in - ## case of an error. - runnableExamples: - doAssert parseIdent("Hello World", 0) == "Hello" - doAssert parseIdent("Hello World", 1) == "ello" - doAssert parseIdent("Hello World", 5) == "" - doAssert parseIdent("Hello World", 6) == "World" - parseIdent(s.toOpenArray(start, s.high)) - -proc parseChar*(s: string, c: var char, start = 0): int = - ## Parses a single character, stores it in `c` and returns 1. - ## In case of error (if start >= s.len) it returns 0 - ## and the value of `c` is unchanged. - runnableExamples: - var c: char - doAssert "nim".parseChar(c, 3) == 0 - doAssert c == '\0' - doAssert "nim".parseChar(c, 0) == 1 - doAssert c == 'n' - parseChar(s.toOpenArray(start, s.high), c) - -proc skipWhitespace*(s: string, start = 0): int {.inline.} = - ## Skips the whitespace starting at ``s[start]``. Returns the number of - ## skipped characters. - runnableExamples: - doAssert skipWhitespace("Hello World", 0) == 0 - doAssert skipWhitespace(" Hello World", 0) == 1 - doAssert skipWhitespace("Hello World", 5) == 1 - doAssert skipWhitespace("Hello World", 5) == 2 - skipWhitespace(s.toOpenArray(start, s.high)) - -proc skip*(s, token: string, start = 0): int {.inline.} = - ## Skips the `token` starting at ``s[start]``. Returns the length of `token` - ## or 0 if there was no `token` at ``s[start]``. - runnableExamples: - doAssert skip("2019-01-22", "2019", 0) == 4 - doAssert skip("2019-01-22", "19", 0) == 0 - doAssert skip("2019-01-22", "19", 2) == 2 - doAssert skip("CAPlow", "CAP", 0) == 3 - doAssert skip("CAPlow", "cap", 0) == 0 - skip(s.toOpenArray(start, s.high), token) - -proc skipIgnoreCase*(s, token: string, start = 0): int = - ## Same as `skip` but case is ignored for token matching. - runnableExamples: - doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3 - doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3 - skipIgnoreCase(s.toOpenArray(start, s.high), token) - -proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} = - ## Skips all characters until one char from the set `until` is found - ## or the end is reached. - ## Returns number of characters skipped. - runnableExamples: - doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1 - doAssert skipUntil("Hello World", {'W'}, 0) == 6 - doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6 - skipUntil(s.toOpenArray(start, s.high), until) - -proc skipUntil*(s: string, until: char, start = 0): int {.inline.} = - ## Skips all characters until the char `until` is found - ## or the end is reached. - ## Returns number of characters skipped. - runnableExamples: - doAssert skipUntil("Hello World", 'o', 0) == 4 - doAssert skipUntil("Hello World", 'o', 4) == 0 - doAssert skipUntil("Hello World", 'W', 0) == 6 - doAssert skipUntil("Hello World", 'w', 0) == 11 - skipUntil(s.toOpenArray(start, s.high), until) - -proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} = - ## Skips all characters while one char from the set `toSkip` is found. - ## Returns number of characters skipped. - runnableExamples: - doAssert skipWhile("Hello World", {'H', 'e'}) == 2 - doAssert skipWhile("Hello World", {'e'}) == 0 - doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3 - skipWhile(s.toOpenArray(start, s.high), toSkip) - -proc parseUntil*(s: string, token: var string, until: set[char], - start = 0): int {.inline.} = - ## Parses a token and stores it in ``token``. Returns - ## the number of the parsed characters or 0 in case of an error. A token - ## consists of the characters notin `until`. - runnableExamples: - var myToken: string - doAssert parseUntil("Hello World", myToken, {'W', 'o', 'r'}) == 4 - doAssert myToken == "Hell" - doAssert parseUntil("Hello World", myToken, {'W', 'r'}) == 6 - doAssert myToken == "Hello " - doAssert parseUntil("Hello World", myToken, {'W', 'r'}, 3) == 3 - doAssert myToken == "lo " - parseUntil(s.toOpenArray(start, s.high), token, until) - -proc parseUntil*(s: string, token: var string, until: char, - start = 0): int {.inline.} = - ## Parses a token and stores it in ``token``. Returns - ## the number of the parsed characters or 0 in case of an error. A token - ## consists of any character that is not the `until` character. - runnableExamples: - var myToken: string - doAssert parseUntil("Hello World", myToken, 'W') == 6 - doAssert myToken == "Hello " - doAssert parseUntil("Hello World", myToken, 'o') == 4 - doAssert myToken == "Hell" - doAssert parseUntil("Hello World", myToken, 'o', 2) == 2 - doAssert myToken == "ll" - parseUntil(s.toOpenArray(start, s.high), token, until) - -proc parseUntil*(s: string, token: var string, until: string, - start = 0): int {.inline.} = - ## Parses a token and stores it in ``token``. Returns - ## the number of the parsed characters or 0 in case of an error. A token - ## consists of any character that comes before the `until` token. - runnableExamples: - var myToken: string - doAssert parseUntil("Hello World", myToken, "Wor") == 6 - doAssert myToken == "Hello " - doAssert parseUntil("Hello World", myToken, "Wor", 2) == 4 - doAssert myToken == "llo " - parseUntil(s.toOpenArray(start, s.high), token, until) - -proc parseWhile*(s: string, token: var string, validChars: set[char], - start = 0): int {.inline.} = - ## Parses a token and stores it in ``token``. Returns - ## the number of the parsed characters or 0 in case of an error. A token - ## consists of the characters in `validChars`. - runnableExamples: - var myToken: string - doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 0) == 0 - doAssert myToken.len() == 0 - doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 6) == 3 - doAssert myToken == "Wor" - parseWhile(s.toOpenArray(start, s.high), token, validChars) - -proc captureBetween*(s: string, first: char, second = '\0', start = 0): string = - ## Finds the first occurrence of ``first``, then returns everything from there - ## up to ``second`` (if ``second`` is '\0', then ``first`` is used). - runnableExamples: - doAssert captureBetween("Hello World", 'e') == "llo World" - doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo" - doAssert captureBetween("Hello World", 'l', start = 6) == "d" - captureBetween(s.toOpenArray(start, s.high), first, second) - -proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.noSideEffect, raises: [ValueError].} = - ## Parses an integer starting at `start` and stores the value into `number`. - ## Result is the number of processed chars or 0 if there is no integer. - ## `ValueError` is raised if the parsed integer is out of the valid range. - runnableExamples: - var res: BiggestInt - doAssert parseBiggestInt("9223372036854775807", res, 0) == 19 - doAssert res == 9223372036854775807 - parseBiggestInt(s.toOpenArray(start, s.high), number) - -proc parseInt*(s: string, number: var int, start = 0): int {.noSideEffect, raises: [ValueError].} = - ## Parses an integer starting at `start` and stores the value into `number`. - ## Result is the number of processed chars or 0 if there is no integer. - ## `ValueError` is raised if the parsed integer is out of the valid range. - runnableExamples: - var res: int - doAssert parseInt("2019", res, 0) == 4 - doAssert res == 2019 - doAssert parseInt("2019", res, 2) == 2 - doAssert res == 19 - parseInt(s.toOpenArray(start, s.high), number) - - -proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {. - raises: [].} = - ## Parses a natural number into ``b``. This cannot raise an overflow - ## error. ``high(int)`` is returned for an overflow. - ## The number of processed character is returned. - ## This is usually what you really want to use instead of `parseInt`:idx:. - runnableExamples: - var res = 0 - discard parseSaturatedNatural("848", res) - doAssert res == 848 - parseSaturatedNatural(s.toOpenArray(start, s.high), b) - - -proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.noSideEffect, raises: [ValueError].} = - ## Parses an unsigned integer starting at `start` and stores the value - ## into `number`. - ## `ValueError` is raised if the parsed integer is out of the valid range. - runnableExamples: - var res: BiggestUInt - doAssert parseBiggestUInt("12", res, 0) == 2 - doAssert res == 12 - doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19 - doAssert res == 1111111111111111111'u64 - parseBiggestUInt(s.toOpenArray(start, s.high), number) - -proc parseUInt*(s: string, number: var uint, start = 0): int {.noSideEffect, raises: [ValueError].} = - ## Parses an unsigned integer starting at `start` and stores the value - ## into `number`. - ## `ValueError` is raised if the parsed integer is out of the valid range. - runnableExamples: - var res: uint - doAssert parseUInt("3450", res) == 4 - doAssert res == 3450 - doAssert parseUInt("3450", res, 2) == 2 - doAssert res == 50 - parseUInt(s.toOpenArray(start, s.high), number) - -proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.noSideEffect.} = - ## Parses a float starting at `start` and stores the value into `number`. - ## Result is the number of processed chars or 0 if a parsing error - ## occurred. - parseFloat(s.toOpenArray(start, s.high), number) - -proc parseFloat*(s: string, number: var float, start = 0): int {.noSideEffect.} = - ## Parses a float starting at `start` and stores the value into `number`. - ## Result is the number of processed chars or 0 if there occurred a parsing - ## error. - runnableExamples: - var res: float - doAssert parseFloat("32", res, 0) == 2 - doAssert res == 32.0 - doAssert parseFloat("32.57", res, 0) == 5 - doAssert res == 32.57 - doAssert parseFloat("32.57", res, 3) == 2 - doAssert res == 57.00 - parseFloat(s.toOpenArray(start, s.high), number) - -iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind, - value: string] = - ## Tokenizes the string `s` into substrings for interpolation purposes. - ## - runnableExamples: - var outp: seq[tuple[kind: InterpolatedKind, value: string]] - for k, v in interpolatedFragments(" $this is ${an example} $$"): - outp.add (k, v) - doAssert outp == @[(ikStr, " "), - (ikVar, "this"), - (ikStr, " is "), - (ikExpr, "an example"), - (ikStr, " "), - (ikDollar, "$")] - for x in s.toOa.interpolatedFragments: - yield x - diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index 884f258f3..79a9cc730 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -149,9 +149,6 @@ an HTML document contains. import strutils, lexbase, streams, unicode -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - # the parser treats ``
    `` as ``

    `` # xmlElementCloseEnd, ## ``/>`` diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim index 4cdc02303..7834f8d95 100644 --- a/lib/pure/pathnorm.nim +++ b/lib/pure/pathnorm.nim @@ -7,14 +7,14 @@ # distribution, for details about the copyright. # -## OS-Path normalization. Used by `os.nim` but also +## OS-Path normalization. Used by ``os.nim`` but also ## generally useful for dealing with paths. ## ## Unstable API. # Yes, this uses import here, not include so that # we don't end up exporting these symbols from pathnorm and os: -import std/private/osseps +import "includes/osseps" type PathIter* = object @@ -29,6 +29,10 @@ 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: @@ -52,23 +56,10 @@ 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 86ad9c0f6..f9868b619 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -16,8 +16,6 @@ ## include "system/inclrtl" -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] const useUnicode = true ## change this to deactivate proper UTF-8 support @@ -85,30 +83,30 @@ type of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkNonTerminal: nt: NonTerminal - of pkBackRef..pkBackRefIgnoreStyle: index: range[-MaxSubpatterns..MaxSubpatterns-1] + of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] else: sons: seq[Peg] NonTerminal* = ref NonTerminalObj -func kind*(p: Peg): PegKind = p.kind +proc kind*(p: Peg): PegKind = p.kind ## Returns the *PegKind* of a given *Peg* object. -func term*(p: Peg): string = p.term +proc term*(p: Peg): string = p.term ## Returns the *string* representation of a given *Peg* variant object ## where present. -func ch*(p: Peg): char = p.ch +proc ch*(p: Peg): char = p.ch ## Returns the *char* representation of a given *Peg* variant object ## where present. -func charChoice*(p: Peg): ref set[char] = p.charChoice +proc charChoice*(p: Peg): ref set[char] = p.charChoice ## Returns the *charChoice* field of a given *Peg* variant object ## where present. -func nt*(p: Peg): NonTerminal = p.nt +proc nt*(p: Peg): NonTerminal = p.nt ## Returns the *NonTerminal* object of a given *Peg* variant object ## where present. -func index*(p: Peg): range[-MaxSubpatterns..MaxSubpatterns-1] = p.index +proc index*(p: Peg): range[0..MaxSubpatterns] = p.index ## Returns the back-reference index of a captured sub-pattern in the ## *Captures* object for a given *Peg* variant object where present. @@ -122,60 +120,59 @@ iterator pairs*(p: Peg): (int, Peg) {.inline.} = for i in 0 ..< p.sons.len: yield (i, p.sons[i]) -func name*(nt: NonTerminal): string = nt.name +proc name*(nt: NonTerminal): string = nt.name ## Gets the name of the symbol represented by the parent *Peg* object variant ## of a given *NonTerminal*. -func line*(nt: NonTerminal): int = nt.line +proc line*(nt: NonTerminal): int = nt.line ## Gets the line number of the definition of the parent *Peg* object variant ## of a given *NonTerminal*. -func col*(nt: NonTerminal): int = nt.col +proc col*(nt: NonTerminal): int = nt.col ## Gets the column number of the definition of the parent *Peg* object variant ## of a given *NonTerminal*. -func flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags +proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags ## Gets the *NonTerminalFlag*-typed flags field of the parent *Peg* variant ## object of a given *NonTerminal*. -func rule*(nt: NonTerminal): Peg = nt.rule +proc rule*(nt: NonTerminal): Peg = nt.rule ## Gets the *Peg* object representing the rule definition of the parent *Peg* ## object variant of a given *NonTerminal*. -func term*(t: string): Peg {.rtl, extern: "npegs$1Str".} = +proc term*(t: string): Peg {.noSideEffect, rtl, extern: "npegs$1Str".} = ## constructs a PEG from a terminal string if t.len != 1: result = Peg(kind: pkTerminal, term: t) else: result = Peg(kind: pkChar, ch: t[0]) -func termIgnoreCase*(t: string): Peg {. - rtl, extern: "npegs$1".} = +proc termIgnoreCase*(t: string): Peg {. + noSideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG from a terminal string; ignore case for matching result = Peg(kind: pkTerminalIgnoreCase, term: t) -func termIgnoreStyle*(t: string): Peg {. - rtl, extern: "npegs$1".} = +proc termIgnoreStyle*(t: string): Peg {. + noSideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG from a terminal string; ignore style for matching result = Peg(kind: pkTerminalIgnoreStyle, term: t) -func term*(t: char): Peg {.rtl, extern: "npegs$1Char".} = +proc term*(t: char): Peg {.noSideEffect, rtl, extern: "npegs$1Char".} = ## constructs a PEG from a terminal char assert t != '\0' result = Peg(kind: pkChar, ch: t) -func charSet*(s: set[char]): Peg {.rtl, extern: "npegs$1".} = +proc charSet*(s: set[char]): Peg {.noSideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG from a character set `s` assert '\0' notin s result = Peg(kind: pkCharChoice) - {.cast(noSideEffect).}: - new(result.charChoice) - result.charChoice[] = s + new(result.charChoice) + result.charChoice[] = s -func len(a: Peg): int {.inline.} = return a.sons.len -func add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s) +proc len(a: Peg): int {.inline.} = return a.sons.len +proc add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s) -func addChoice(dest: var Peg, elem: Peg) = +proc addChoice(dest: var Peg, elem: Peg) = var L = dest.len-1 if L >= 0 and dest.sons[L].kind == pkCharChoice: # caution! Do not introduce false aliasing here! @@ -198,12 +195,12 @@ template multipleOp(k: PegKind, localOpt: untyped) = if result.len == 1: result = result.sons[0] -func `/`*(a: varargs[Peg]): Peg {. - rtl, extern: "npegsOrderedChoice".} = +proc `/`*(a: varargs[Peg]): Peg {. + noSideEffect, rtl, extern: "npegsOrderedChoice".} = ## constructs an ordered choice with the PEGs in `a` multipleOp(pkOrderedChoice, addChoice) -func addSequence(dest: var Peg, elem: Peg) = +proc addSequence(dest: var Peg, elem: Peg) = var L = dest.len-1 if L >= 0 and dest.sons[L].kind == pkTerminal: # caution! Do not introduce false aliasing here! @@ -215,12 +212,12 @@ func addSequence(dest: var Peg, elem: Peg) = else: add(dest, elem) else: add(dest, elem) -func sequence*(a: varargs[Peg]): Peg {. - rtl, extern: "npegs$1".} = +proc sequence*(a: varargs[Peg]): Peg {. + noSideEffect, rtl, extern: "npegs$1".} = ## constructs a sequence with all the PEGs from `a` multipleOp(pkSequence, addSequence) -func `?`*(a: Peg): Peg {.rtl, extern: "npegsOptional".} = +proc `?`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsOptional".} = ## constructs an optional for the PEG `a` if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, pkGreedyRepSet}: @@ -230,7 +227,7 @@ func `?`*(a: Peg): Peg {.rtl, extern: "npegsOptional".} = else: result = Peg(kind: pkOption, sons: @[a]) -func `*`*(a: Peg): Peg {.rtl, extern: "npegsGreedyRep".} = +proc `*`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsGreedyRep".} = ## constructs a "greedy repetition" for the PEG `a` case a.kind of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption: @@ -245,99 +242,96 @@ func `*`*(a: Peg): Peg {.rtl, extern: "npegsGreedyRep".} = else: result = Peg(kind: pkGreedyRep, sons: @[a]) -func `!*`*(a: Peg): Peg {.rtl, extern: "npegsSearch".} = +proc `!*`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsSearch".} = ## constructs a "search" for the PEG `a` result = Peg(kind: pkSearch, sons: @[a]) -func `!*\`*(a: Peg): Peg {.rtl, +proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npgegsCapturedSearch".} = ## constructs a "captured search" for the PEG `a` result = Peg(kind: pkCapturedSearch, sons: @[a]) -func `+`*(a: Peg): Peg {.rtl, extern: "npegsGreedyPosRep".} = +proc `+`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsGreedyPosRep".} = ## constructs a "greedy positive repetition" with the PEG `a` return sequence(a, *a) -func `&`*(a: Peg): Peg {.rtl, extern: "npegsAndPredicate".} = +proc `&`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsAndPredicate".} = ## constructs an "and predicate" with the PEG `a` result = Peg(kind: pkAndPredicate, sons: @[a]) -func `!`*(a: Peg): Peg {.rtl, extern: "npegsNotPredicate".} = +proc `!`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsNotPredicate".} = ## constructs a "not predicate" with the PEG `a` result = Peg(kind: pkNotPredicate, sons: @[a]) -func any*: Peg {.inline.} = +proc any*: Peg {.inline.} = ## constructs the PEG `any character`:idx: (``.``) result = Peg(kind: pkAny) -func anyRune*: Peg {.inline.} = +proc anyRune*: Peg {.inline.} = ## constructs the PEG `any rune`:idx: (``_``) result = Peg(kind: pkAnyRune) -func newLine*: Peg {.inline.} = +proc newLine*: Peg {.inline.} = ## constructs the PEG `newline`:idx: (``\n``) result = Peg(kind: pkNewLine) -func unicodeLetter*: Peg {.inline.} = +proc unicodeLetter*: Peg {.inline.} = ## constructs the PEG ``\letter`` which matches any Unicode letter. result = Peg(kind: pkLetter) -func unicodeLower*: Peg {.inline.} = +proc unicodeLower*: Peg {.inline.} = ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter. result = Peg(kind: pkLower) -func unicodeUpper*: Peg {.inline.} = +proc unicodeUpper*: Peg {.inline.} = ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter. result = Peg(kind: pkUpper) -func unicodeTitle*: Peg {.inline.} = +proc unicodeTitle*: Peg {.inline.} = ## constructs the PEG ``\title`` which matches any Unicode title letter. result = Peg(kind: pkTitle) -func unicodeWhitespace*: Peg {.inline.} = +proc unicodeWhitespace*: Peg {.inline.} = ## constructs the PEG ``\white`` which matches any Unicode ## whitespace character. result = Peg(kind: pkWhitespace) -func startAnchor*: Peg {.inline.} = +proc startAnchor*: Peg {.inline.} = ## constructs the PEG ``^`` which matches the start of the input. result = Peg(kind: pkStartAnchor) -func endAnchor*: Peg {.inline.} = +proc endAnchor*: Peg {.inline.} = ## constructs the PEG ``$`` which matches the end of the input. result = !any() -func capture*(a: Peg = Peg(kind: pkEmpty)): Peg {.rtl, extern: "npegsCapture".} = +proc capture*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsCapture".} = ## constructs a capture with the PEG `a` result = Peg(kind: pkCapture, sons: @[a]) -func backref*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. - rtl, extern: "npegs$1".} = +proc backref*(index: range[1..MaxSubpatterns]): Peg {. + noSideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. `reverse` specifies wether indexing starts from the end of the - ## capture list. - result = Peg(kind: pkBackRef, index: (if reverse: -index else: index - 1)) + ## from 1. + result = Peg(kind: pkBackRef, index: index-1) -func backrefIgnoreCase*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. - rtl, extern: "npegs$1".} = +proc backrefIgnoreCase*(index: range[1..MaxSubpatterns]): Peg {. + noSideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. `reverse` specifies wether indexing starts from the end of the - ## capture list. Ignores case for matching. - result = Peg(kind: pkBackRefIgnoreCase, index: (if reverse: -index else: index - 1)) + ## from 1. Ignores case for matching. + result = Peg(kind: pkBackRefIgnoreCase, index: index-1) -func backrefIgnoreStyle*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. - rtl, extern: "npegs$1".} = +proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns]): Peg {. + noSideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. `reverse` specifies wether indexing starts from the end of the - ## capture list. Ignores style for matching. - result = Peg(kind: pkBackRefIgnoreStyle, index: (if reverse: -index else: index - 1)) + ## from 1. Ignores style for matching. + result = Peg(kind: pkBackRefIgnoreStyle, index: index-1) -func spaceCost(n: Peg): int = +proc spaceCost(n: Peg): int = case n.kind of pkEmpty: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, - pkAny..pkWhitespace, pkGreedyAny, pkBackRef..pkBackRefIgnoreStyle: + pkAny..pkWhitespace, pkGreedyAny: result = 1 of pkNonTerminal: # we cannot inline a rule with a non-terminal @@ -347,8 +341,8 @@ func spaceCost(n: Peg): int = inc(result, spaceCost(n.sons[i])) if result >= InlineThreshold: break -func nonterminal*(n: NonTerminal): Peg {. - rtl, extern: "npegs$1".} = +proc nonterminal*(n: NonTerminal): Peg {. + noSideEffect, rtl, extern: "npegs$1".} = ## constructs a PEG that consists of the nonterminal symbol assert n != nil if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold: @@ -357,8 +351,8 @@ func nonterminal*(n: NonTerminal): Peg {. else: result = Peg(kind: pkNonTerminal, nt: n) -func newNonTerminal*(name: string, line, column: int): NonTerminal {. - rtl, extern: "npegs$1".} = +proc newNonTerminal*(name: string, line, column: int): NonTerminal {. + noSideEffect, rtl, extern: "npegs$1".} = ## constructs a nonterminal symbol result = NonTerminal(name: name, line: line, col: column) @@ -393,7 +387,7 @@ template natural*: Peg = # ------------------------- debugging ----------------------------------------- -func esc(c: char, reserved = {'\0'..'\255'}): string = +proc esc(c: char, reserved = {'\0'..'\255'}): string = case c of '\b': result = "\\b" of '\t': result = "\\t" @@ -409,14 +403,14 @@ func esc(c: char, reserved = {'\0'..'\255'}): string = elif c in reserved: result = '\\' & c else: result = $c -func singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'" +proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'" -func singleQuoteEsc(str: string): string = +proc singleQuoteEsc(str: string): string = result = "'" for c in items(str): add result, esc(c, {'\''}) add result, '\'' -func charSetEscAux(cc: set[char]): string = +proc charSetEscAux(cc: set[char]): string = const reserved = {'^', '-', ']'} result = "" var c1 = 0 @@ -433,13 +427,13 @@ func charSetEscAux(cc: set[char]): string = c1 = c2 inc(c1) -func charSetEsc(cc: set[char]): string = +proc charSetEsc(cc: set[char]): string = if card(cc) >= 128+64: result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']' else: result = '[' & charSetEscAux(cc) & ']' -func toStrAux(r: Peg, res: var string) = +proc toStrAux(r: Peg, res: var string) = case r.kind of pkEmpty: add(res, "()") of pkAny: add(res, '.') @@ -525,7 +519,7 @@ func toStrAux(r: Peg, res: var string) = of pkStartAnchor: add(res, '^') -func `$` *(r: Peg): string {.rtl, extern: "npegsToString".} = +proc `$` *(r: Peg): string {.noSideEffect, rtl, extern: "npegsToString".} = ## converts a PEG to its string representation result = "" toStrAux(r, result) @@ -538,7 +532,7 @@ type ml: int origStart: int -func bounds*(c: Captures, +proc bounds*(c: Captures, i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = ## returns the bounds ``[first..last]`` of the `i`'th capture. result = c.matches[i] @@ -551,26 +545,24 @@ when not useUnicode: inc(i) template runeLenAt(s, i): untyped = 1 - func isAlpha(a: char): bool {.inline.} = return a in {'a'..'z', 'A'..'Z'} - func isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'} - func isLower(a: char): bool {.inline.} = return a in {'a'..'z'} - func isTitle(a: char): bool {.inline.} = return false - func isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'} + proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z', 'A'..'Z'} + proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'} + proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'} + proc isTitle(a: char): bool {.inline.} = return false + proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'} template matchOrParse(mopProc: untyped) = # Used to make the main matcher proc *rawMatch* as well as event parser # procs. For the former, *enter* and *leave* event handler code generators # are provided which just return *discard*. - proc mopProc(s: string, p: Peg, start: int, c: var Captures): int {.gcsafe.} = + proc mopProc(s: string, p: Peg, start: int, c: var Captures): int = proc matchBackRef(s: string, p: Peg, start: int, c: var Captures): int = # Parse handler code must run in an *of* clause of its own for each # *PegKind*, so we encapsulate the identical clause body for # *pkBackRef..pkBackRefIgnoreStyle* here. - var index = p.index - if index < 0: index.inc(c.ml) - if index < 0 or index >= c.ml: return -1 - var (a, b) = c.matches[index] + if p.index >= c.ml: return -1 + var (a, b) = c.matches[p.index] var n: Peg case p.kind of pkBackRef: @@ -830,22 +822,15 @@ template matchOrParse(mopProc: untyped) = leave(pkNotPredicate, s, p, start, result) of pkCapture: enter(pkCapture, s, p, start) - if p.sons.len == 0 or p.sons[0].kind == pkEmpty: - # empty capture removes last match - dec(c.ml) - c.matches[c.ml] = (0, 0) - result = 0 # match of length 0 + var idx = c.ml # reserve a slot for the subpattern + inc(c.ml) + result = mopProc(s, p.sons[0], start, c) + if result >= 0: + if idx < MaxSubpatterns: + c.matches[idx] = (start, start+result-1) + #else: silently ignore the capture else: - var idx = c.ml # reserve a slot for the subpattern - result = mopProc(s, p.sons[0], start, c) - if result >= 0: - 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) + c.ml = idx leave(pkCapture, s, p, start, result) of pkBackRef: enter(pkBackRef, s, p, start) @@ -866,8 +851,8 @@ template matchOrParse(mopProc: untyped) = leave(pkStartAnchor, s, p, start, result) of pkRule, pkList: assert false -func rawMatch*(s: string, p: Peg, start: int, c: var Captures): int - {.rtl, extern: "npegs$1".} = +proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int + {.noSideEffect, rtl, extern: "npegs$1".} = ## low-level matching proc that implements the PEG interpreter. Use this ## for maximum efficiency (every other PEG operation ends up calling this ## proc). @@ -879,11 +864,7 @@ func rawMatch*(s: string, p: Peg, start: int, c: var Captures): int template leave(pk, s, p, start, length) = discard matchOrParse(matchIt) - {.cast(noSideEffect).}: - # This cast is allowed because the `matchOrParse` template is used for - # both matching and parsing, but side effects are only possible when it's - # used by `eventParser`. - result = matchIt(s, p, start, c) + result = matchIt(s, p, start, c) macro mkHandlerTplts(handlers: untyped): untyped = # Transforms the handler spec in *handlers* into handler templates. @@ -910,7 +891,7 @@ macro mkHandlerTplts(handlers: untyped): untyped = # StmtList # # ... - func mkEnter(hdName, body: NimNode): NimNode = + proc mkEnter(hdName, body: NimNode): NimNode = template helper(hdName, body) {.dirty.} = template hdName(s, p, start) = let s {.inject.} = s @@ -960,7 +941,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) = ## evaluates an arithmetic expression defined by a simple PEG: ## ## .. code-block:: nim - ## import std/[strutils, pegs] + ## import strutils, pegs ## ## let ## pegAst = """ @@ -1027,7 +1008,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) = ## Symbols declared in an *enter* handler can be made visible in the ## corresponding *leave* handler by annotating them with an *inject* pragma. proc rawParse(s: string, p: Peg, start: int, c: var Captures): int - {.gensym.} = + {.genSym.} = # binding from *macros* bind strVal @@ -1062,7 +1043,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) = matchOrParse(parseIt) parseIt(s, p, start, c) - proc parser(s: string): int {.gensym.} = + proc parser(s: string): int {.genSym.} = # the proc to be returned var ms: array[MaxSubpatterns, (int, int)] @@ -1079,8 +1060,8 @@ template fillMatches(s, caps, c) = else: caps[k] = "" -func matchLen*(s: string, pattern: Peg, matches: var openArray[string], - start = 0): int {.rtl, extern: "npegs$1Capture".} = +proc matchLen*(s: string, pattern: Peg, matches: var openArray[string], + start = 0): int {.noSideEffect, rtl, extern: "npegs$1Capture".} = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length ## of zero can happen. It's possible that a suffix of `s` remains @@ -1090,8 +1071,8 @@ func matchLen*(s: string, pattern: Peg, matches: var openArray[string], result = rawMatch(s, pattern, start, c) if result >= 0: fillMatches(s, matches, c) -func matchLen*(s: string, pattern: Peg, - start = 0): int {.rtl, extern: "npegs$1".} = +proc matchLen*(s: string, pattern: Peg, + start = 0): int {.noSideEffect, rtl, extern: "npegs$1".} = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length ## of zero can happen. It's possible that a suffix of `s` remains @@ -1100,22 +1081,22 @@ func matchLen*(s: string, pattern: Peg, c.origStart = start result = rawMatch(s, pattern, start, c) -func match*(s: string, pattern: Peg, matches: var openArray[string], - start = 0): bool {.rtl, extern: "npegs$1Capture".} = +proc match*(s: string, pattern: Peg, matches: var openArray[string], + start = 0): bool {.noSideEffect, rtl, extern: "npegs$1Capture".} = ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and ## the captured substrings in the array ``matches``. If it does not ## match, nothing is written into ``matches`` and ``false`` is ## returned. result = matchLen(s, pattern, matches, start) != -1 -func match*(s: string, pattern: Peg, - start = 0): bool {.rtl, extern: "npegs$1".} = +proc match*(s: string, pattern: Peg, + start = 0): bool {.noSideEffect, rtl, extern: "npegs$1".} = ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. result = matchLen(s, pattern, start) != -1 -func find*(s: string, pattern: Peg, matches: var openArray[string], - start = 0): int {.rtl, extern: "npegs$1Capture".} = +proc find*(s: string, pattern: Peg, matches: var openArray[string], + start = 0): int {.noSideEffect, rtl, extern: "npegs$1Capture".} = ## returns the starting position of ``pattern`` in ``s`` and the captured ## substrings in the array ``matches``. If it does not match, nothing ## is written into ``matches`` and -1 is returned. @@ -1129,9 +1110,9 @@ func find*(s: string, pattern: Peg, matches: var openArray[string], return -1 # could also use the pattern here: (!P .)* P -func findBounds*(s: string, pattern: Peg, matches: var openArray[string], +proc findBounds*(s: string, pattern: Peg, matches: var openArray[string], start = 0): tuple[first, last: int] {. - rtl, extern: "npegs$1Capture".} = + noSideEffect, rtl, extern: "npegs$1Capture".} = ## returns the starting position and end position of ``pattern`` in ``s`` ## and the captured ## substrings in the array ``matches``. If it does not match, nothing @@ -1146,8 +1127,8 @@ func findBounds*(s: string, pattern: Peg, matches: var openArray[string], return (i, i+L-1) return (-1, 0) -func find*(s: string, pattern: Peg, - start = 0): int {.rtl, extern: "npegs$1".} = +proc find*(s: string, pattern: Peg, + start = 0): int {.noSideEffect, rtl, extern: "npegs$1".} = ## returns the starting position of ``pattern`` in ``s``. If it does not ## match, -1 is returned. var c: Captures @@ -1170,13 +1151,16 @@ iterator findAll*(s: string, pattern: Peg, start = 0): string = yield substr(s, i, i+L-1) inc(i, L) -func findAll*(s: string, pattern: Peg, start = 0): seq[string] {. - rtl, extern: "npegs$1".} = +proc findAll*(s: string, pattern: Peg, start = 0): seq[string] {. + noSideEffect, rtl, extern: "npegs$1".} = ## returns all matching *substrings* of `s` that match `pattern`. - ## If it does not match, `@[]` is returned. + ## If it does not match, @[] is returned. result = @[] for it in findAll(s, pattern, start): result.add it +when not defined(nimhygiene): + {.pragma: inject.} + template `=~`*(s: string, pattern: Peg): bool = ## This calls ``match`` with an implicit declared ``matches`` array that ## can be used in the scope of the ``=~`` call: @@ -1202,31 +1186,31 @@ template `=~`*(s: string, pattern: Peg): bool = # ------------------------- more string handling ------------------------------ -func contains*(s: string, pattern: Peg, start = 0): bool {. - rtl, extern: "npegs$1".} = +proc contains*(s: string, pattern: Peg, start = 0): bool {. + noSideEffect, rtl, extern: "npegs$1".} = ## same as ``find(s, pattern, start) >= 0`` return find(s, pattern, start) >= 0 -func contains*(s: string, pattern: Peg, matches: var openArray[string], - start = 0): bool {.rtl, extern: "npegs$1Capture".} = +proc contains*(s: string, pattern: Peg, matches: var openArray[string], + start = 0): bool {.noSideEffect, rtl, extern: "npegs$1Capture".} = ## same as ``find(s, pattern, matches, start) >= 0`` return find(s, pattern, matches, start) >= 0 -func startsWith*(s: string, prefix: Peg, start = 0): bool {. - rtl, extern: "npegs$1".} = +proc startsWith*(s: string, prefix: Peg, start = 0): bool {. + noSideEffect, rtl, extern: "npegs$1".} = ## returns true if `s` starts with the pattern `prefix` result = matchLen(s, prefix, start) >= 0 -func endsWith*(s: string, suffix: Peg, start = 0): bool {. - rtl, extern: "npegs$1".} = +proc endsWith*(s: string, suffix: Peg, start = 0): bool {. + noSideEffect, rtl, extern: "npegs$1".} = ## returns true if `s` ends with the pattern `suffix` var c: Captures c.origStart = start for i in start .. s.len-1: if rawMatch(s, suffix, i, c) == s.len - i: return true -func replacef*(s: string, sub: Peg, by: string): string {. - rtl, extern: "npegs$1".} = +proc replacef*(s: string, sub: Peg, by: string): string {. + noSideEffect, rtl, extern: "npegs$1".} = ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## @@ -1254,8 +1238,8 @@ func replacef*(s: string, sub: Peg, by: string): string {. inc(i, x) add(result, substr(s, i)) -func replace*(s: string, sub: Peg, by = ""): string {. - rtl, extern: "npegs$1".} = +proc replace*(s: string, sub: Peg, by = ""): string {. + noSideEffect, rtl, extern: "npegs$1".} = ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed ## in `by`. result = "" @@ -1271,9 +1255,9 @@ func replace*(s: string, sub: Peg, by = ""): string {. inc(i, x) add(result, substr(s, i)) -func parallelReplace*(s: string, subs: varargs[ +proc parallelReplace*(s: string, subs: varargs[ tuple[pattern: Peg, repl: string]]): string {. - rtl, extern: "npegs$1".} = + noSideEffect, rtl, extern: "npegs$1".} = ## Returns a modified copy of `s` with the substitutions in `subs` ## applied in parallel. result = "" @@ -1295,19 +1279,16 @@ func parallelReplace*(s: string, subs: varargs[ # copy the rest: add(result, substr(s, i)) -when not defined(nimHasEffectsOf): - {.pragma: effectsOf.} - -func replace*(s: string, sub: Peg, cb: proc( +proc replace*(s: string, sub: Peg, cb: proc( match: int, cnt: int, caps: openArray[string]): string): string {. - rtl, extern: "npegs$1cb", effectsOf: cb.} = + rtl, extern: "npegs$1cb".} = ## Replaces `sub` in `s` by the resulting strings from the callback. ## The callback proc receives the index of the current match (starting with 0), ## the count of captures and an open array with the captures of each match. Examples: ## ## .. code-block:: nim ## - ## func handleMatches*(m: int, n: int, c: openArray[string]): string = + ## proc handleMatches*(m: int, n: int, c: openArray[string]): string = ## result = "" ## if m > 0: ## result.add ", " @@ -1351,7 +1332,7 @@ when not defined(js): ## error occurs. This is supposed to be used for quick scripting. ## ## **Note**: this proc does not exist while using the JS backend. - var x = readFile(infile) + var x = readFile(infile).string writeFile(outfile, x.parallelReplace(subs)) @@ -1390,8 +1371,8 @@ iterator split*(s: string, sep: Peg): string = if first < last: yield substr(s, first, last-1) -func split*(s: string, sep: Peg): seq[string] {. - rtl, extern: "npegs$1".} = +proc split*(s: string, sep: Peg): seq[string] {. + noSideEffect, rtl, extern: "npegs$1".} = ## Splits the string `s` into substrings. result = @[] for it in split(s, sep): result.add it @@ -1417,7 +1398,6 @@ type tkCurlyLe, ## '{' tkCurlyRi, ## '}' tkCurlyAt, ## '{@}' - tkEmptyCurl, ## '{}' tkArrow, ## '<-' tkBar, ## '/' tkStar, ## '*' @@ -1450,25 +1430,25 @@ type const tokKindToStr: array[TokKind, string] = [ "invalid", "[EOF]", ".", "_", "identifier", "string literal", - "character set", "(", ")", "{", "}", "{@}", "{}", + "character set", "(", ")", "{", "}", "{@}", "<-", "/", "*", "+", "&", "!", "?", "@", "built-in", "escaped", "$", "$", "^" ] -func handleCR(L: var PegLexer, pos: int): int = +proc handleCR(L: var PegLexer, pos: int): int = assert(L.buf[pos] == '\c') inc(L.lineNumber) result = pos+1 if result < L.buf.len and L.buf[result] == '\L': inc(result) L.lineStart = result -func handleLF(L: var PegLexer, pos: int): int = +proc handleLF(L: var PegLexer, pos: int): int = assert(L.buf[pos] == '\L') inc(L.lineNumber) result = pos+1 L.lineStart = result -func init(L: var PegLexer, input, filename: string, line = 1, col = 0) = +proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) = L.buf = input L.bufpos = 0 L.lineNumber = line @@ -1476,18 +1456,18 @@ func init(L: var PegLexer, input, filename: string, line = 1, col = 0) = L.lineStart = 0 L.filename = filename -func getColumn(L: PegLexer): int {.inline.} = +proc getColumn(L: PegLexer): int {.inline.} = result = abs(L.bufpos - L.lineStart) + L.colOffset -func getLine(L: PegLexer): int {.inline.} = +proc getLine(L: PegLexer): int {.inline.} = result = L.lineNumber -func errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = +proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = var line = if line < 0: getLine(L) else: line var col = if col < 0: getColumn(L) else: col result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] -func getEscapedChar(c: var PegLexer, tok: var Token) = +proc getEscapedChar(c: var PegLexer, tok: var Token) = inc(c.bufpos) if c.bufpos >= len(c.buf): tok.kind = tkInvalid @@ -1547,7 +1527,7 @@ func getEscapedChar(c: var PegLexer, tok: var Token) = add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) -func skip(c: var PegLexer) = +proc skip(c: var PegLexer) = var pos = c.bufpos while pos < c.buf.len: case c.buf[pos] @@ -1564,7 +1544,7 @@ func skip(c: var PegLexer) = break # EndOfFile also leaves the loop c.bufpos = pos -func getString(c: var PegLexer, tok: var Token) = +proc getString(c: var PegLexer, tok: var Token) = tok.kind = tkStringLit var pos = c.bufpos + 1 var quote = c.buf[pos-1] @@ -1585,27 +1565,19 @@ func getString(c: var PegLexer, tok: var Token) = inc(pos) c.bufpos = pos -func getDollar(c: var PegLexer, tok: var Token) = +proc getDollar(c: var PegLexer, tok: var Token) = var pos = c.bufpos + 1 - var neg = false - if pos < c.buf.len and c.buf[pos] == '^': - neg = true - inc(pos) if pos < c.buf.len and c.buf[pos] in {'0'..'9'}: tok.kind = tkBackref tok.index = 0 while pos < c.buf.len and c.buf[pos] in {'0'..'9'}: tok.index = tok.index * 10 + ord(c.buf[pos]) - ord('0') inc(pos) - if neg: - tok.index = -tok.index else: - if neg: - dec(pos) tok.kind = tkDollar c.bufpos = pos -func getCharSet(c: var PegLexer, tok: var Token) = +proc getCharSet(c: var PegLexer, tok: var Token) = tok.kind = tkCharSet tok.charset = {} var pos = c.bufpos + 1 @@ -1662,7 +1634,7 @@ func getCharSet(c: var PegLexer, tok: var Token) = c.bufpos = pos if caret: tok.charset = {'\1'..'\xFF'} - tok.charset -func getSymbol(c: var PegLexer, tok: var Token) = +proc getSymbol(c: var PegLexer, tok: var Token) = var pos = c.bufpos while pos < c.buf.len: add(tok.literal, c.buf[pos]) @@ -1671,7 +1643,7 @@ func getSymbol(c: var PegLexer, tok: var Token) = c.bufpos = pos tok.kind = tkIdentifier -func getBuiltin(c: var PegLexer, tok: var Token) = +proc getBuiltin(c: var PegLexer, tok: var Token) = if c.bufpos+1 < c.buf.len and c.buf[c.bufpos+1] in strutils.Letters: inc(c.bufpos) getSymbol(c, tok) @@ -1680,7 +1652,7 @@ func getBuiltin(c: var PegLexer, tok: var Token) = tok.kind = tkEscaped getEscapedChar(c, tok) # may set tok.kind to tkInvalid -func getTok(c: var PegLexer, tok: var Token) = +proc getTok(c: var PegLexer, tok: var Token) = tok.kind = tkInvalid tok.modifier = modNone setLen(tok.literal, 0) @@ -1701,10 +1673,6 @@ func getTok(c: var PegLexer, tok: var Token) = tok.kind = tkCurlyAt inc(c.bufpos, 2) add(tok.literal, "{@}") - elif c.buf[c.bufpos] == '}' and c.bufpos < c.buf.len: - tok.kind = tkEmptyCurl - inc(c.bufpos) - add(tok.literal, "{}") else: tok.kind = tkCurlyLe add(tok.literal, '{') @@ -1740,7 +1708,7 @@ func getTok(c: var PegLexer, tok: var Token) = return if c.buf[c.bufpos] in {'\'', '"'} or c.buf[c.bufpos] == '$' and c.bufpos+1 < c.buf.len and - c.buf[c.bufpos+1] in {'^', '0'..'9'}: + c.buf[c.bufpos+1] in {'0'..'9'}: case tok.literal of "i": tok.modifier = modIgnoreCase of "y": tok.modifier = modIgnoreStyle @@ -1802,7 +1770,7 @@ func getTok(c: var PegLexer, tok: var Token) = add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) -func arrowIsNextTok(c: PegLexer): bool = +proc arrowIsNextTok(c: PegLexer): bool = # the only look ahead we need var pos = c.bufpos while pos < c.buf.len and c.buf[pos] in {'\t', ' '}: inc(pos) @@ -1823,21 +1791,23 @@ type identIsVerbatim: bool skip: Peg -func pegError(p: PegParser, msg: string, line = -1, col = -1) = - var e = (ref EInvalidPeg)(msg: errorStr(p, msg, line, col)) +proc pegError(p: PegParser, msg: string, line = -1, col = -1) = + var e: ref EInvalidPeg + new(e) + e.msg = errorStr(p, msg, line, col) raise e -func getTok(p: var PegParser) = +proc getTok(p: var PegParser) = getTok(p, p.tok) if p.tok.kind == tkInvalid: pegError(p, "'" & p.tok.literal & "' is invalid token") -func eat(p: var PegParser, kind: TokKind) = +proc eat(p: var PegParser, kind: TokKind) = if p.tok.kind == kind: getTok(p) else: pegError(p, tokKindToStr[kind] & " expected") -func parseExpr(p: var PegParser): Peg {.gcsafe.} +proc parseExpr(p: var PegParser): Peg {.gcsafe.} -func getNonTerminal(p: var PegParser, name: string): NonTerminal = +proc getNonTerminal(p: var PegParser, name: string): NonTerminal = for i in 0..high(p.nonterms): result = p.nonterms[i] if cmpIgnoreStyle(result.name, name) == 0: return @@ -1845,22 +1815,19 @@ func getNonTerminal(p: var PegParser, name: string): NonTerminal = result = newNonTerminal(name, getLine(p), getColumn(p)) add(p.nonterms, result) -func modifiedTerm(s: string, m: Modifier): Peg = +proc modifiedTerm(s: string, m: Modifier): Peg = case m of modNone, modVerbatim: result = term(s) of modIgnoreCase: result = termIgnoreCase(s) of modIgnoreStyle: result = termIgnoreStyle(s) -func modifiedBackref(s: int, m: Modifier): Peg = - var - reverse = s < 0 - index = if reverse: -s else: s +proc modifiedBackref(s: int, m: Modifier): Peg = case m - of modNone, modVerbatim: result = backref(index, reverse) - of modIgnoreCase: result = backrefIgnoreCase(index, reverse) - of modIgnoreStyle: result = backrefIgnoreStyle(index, reverse) + of modNone, modVerbatim: result = backref(s) + of modIgnoreCase: result = backrefIgnoreCase(s) + of modIgnoreStyle: result = backrefIgnoreStyle(s) -func builtin(p: var PegParser): Peg = +proc builtin(p: var PegParser): Peg = # do not use "y", "skip" or "i" as these would be ambiguous case p.tok.literal of "n": result = newLine() @@ -1880,11 +1847,11 @@ func builtin(p: var PegParser): Peg = of "white": result = unicodeWhitespace() else: pegError(p, "unknown built-in: " & p.tok.literal) -func token(terminal: Peg, p: PegParser): Peg = +proc token(terminal: Peg, p: PegParser): Peg = if p.skip.kind == pkEmpty: result = terminal else: result = sequence(p.skip, terminal) -func primary(p: var PegParser): Peg = +proc primary(p: var PegParser): Peg = case p.tok.kind of tkAmp: getTok(p) @@ -1908,8 +1875,7 @@ func primary(p: var PegParser): Peg = getTok(p) elif not arrowIsNextTok(p): var nt = getNonTerminal(p, p.tok.literal) - {.cast(noSideEffect).}: - incl(nt.flags, ntUsed) + incl(nt.flags, ntUsed) result = nonterminal(nt).token(p) getTok(p) else: @@ -1933,9 +1899,6 @@ func primary(p: var PegParser): Peg = result = capture(parseExpr(p)).token(p) eat(p, tkCurlyRi) inc(p.captures) - of tkEmptyCurl: - result = capture() - getTok(p) of tkAny: result = any().token(p) getTok(p) @@ -1955,11 +1918,11 @@ func primary(p: var PegParser): Peg = result = startAnchor() getTok(p) of tkBackref: - if abs(p.tok.index) > p.captures or p.tok.index == 0: - pegError(p, "invalid back reference index: " & $p.tok.index) var m = p.tok.modifier if m == modNone: m = p.modifier result = modifiedBackref(p.tok.index, m).token(p) + if p.tok.index < 0 or p.tok.index > p.captures: + pegError(p, "invalid back reference index: " & $p.tok.index) getTok(p) else: pegError(p, "expression expected, but found: " & p.tok.literal) @@ -1977,13 +1940,13 @@ func primary(p: var PegParser): Peg = getTok(p) else: break -func seqExpr(p: var PegParser): Peg = +proc seqExpr(p: var PegParser): Peg = result = primary(p) while true: case p.tok.kind of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe, tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref, - tkHat, tkCurlyAt, tkEmptyCurl: + tkHat, tkCurlyAt: result = sequence(result, primary(p)) of tkIdentifier: if not arrowIsNextTok(p): @@ -1991,29 +1954,27 @@ func seqExpr(p: var PegParser): Peg = else: break else: break -func parseExpr(p: var PegParser): Peg = +proc parseExpr(p: var PegParser): Peg = result = seqExpr(p) while p.tok.kind == tkBar: getTok(p) result = result / seqExpr(p) -func parseRule(p: var PegParser): NonTerminal = +proc parseRule(p: var PegParser): NonTerminal = if p.tok.kind == tkIdentifier and arrowIsNextTok(p): result = getNonTerminal(p, p.tok.literal) if ntDeclared in result.flags: pegError(p, "attempt to redefine: " & result.name) - {.cast(noSideEffect).}: - result.line = getLine(p) - result.col = getColumn(p) + result.line = getLine(p) + result.col = getColumn(p) getTok(p) eat(p, tkArrow) - {.cast(noSideEffect).}: - result.rule = parseExpr(p) - incl(result.flags, ntDeclared) # NOW inlining may be attempted + result.rule = parseExpr(p) + incl(result.flags, ntDeclared) # NOW inlining may be attempted else: pegError(p, "rule expected, but found: " & p.tok.literal) -func rawParse(p: var PegParser): Peg = +proc rawParse(p: var PegParser): Peg = ## parses a rule or a PEG expression while p.tok.kind == tkBuiltin: case p.tok.literal @@ -2043,7 +2004,7 @@ func rawParse(p: var PegParser): Peg = elif ntUsed notin nt.flags and i > 0: pegError(p, "unused rule: " & nt.name, nt.line, nt.col) -func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg = +proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg = ## constructs a Peg object from `pattern`. `filename`, `line`, `col` are ## used for error messages, but they only provide start offsets. `parsePeg` ## keeps track of line and column numbers within `pattern`. @@ -2058,14 +2019,14 @@ func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg = getTok(p) result = rawParse(p) -func peg*(pattern: string): Peg = +proc peg*(pattern: string): Peg = ## constructs a Peg object from the `pattern`. The short name has been - ## chosen to encourage its use as a raw string modifier: + ## chosen to encourage its use as a raw string modifier:: ## - ## peg"{\ident} \s* '=' \s* {.*}" + ## peg"{\ident} \s* '=' \s* {.*}" result = parsePeg(pattern, "pattern") -func escapePeg*(s: string): string = +proc escapePeg*(s: string): string = ## escapes `s` so that it is matched verbatim when used as a peg. result = "" var inQuote = false @@ -2083,3 +2044,147 @@ func escapePeg*(s: string): string = inQuote = true result.add(c) if inQuote: result.add('\'') + +when isMainModule: + proc pegsTest() = + assert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27" + assert match("(a b c)", peg"'(' @ ')'") + assert match("W_HI_Le", peg"\y 'while'") + assert(not match("W_HI_L", peg"\y 'while'")) + assert(not match("W_HI_Le", peg"\y v'while'")) + assert match("W_HI_Le", peg"y'while'") + + assert($ +digits == $peg"\d+") + assert "0158787".match(peg"\d+") + assert "ABC 0232".match(peg"\w+\s+\d+") + assert "ABC".match(peg"\d+ / \w+") + + var accum: seq[string] = @[] + for word in split("00232this02939is39an22example111", peg"\d+"): + accum.add(word) + assert(accum == @["this", "is", "an", "example"]) + + assert matchLen("key", ident) == 3 + + var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) + assert matchLen("key1= cal9", pattern) == 11 + + var ws = newNonTerminal("ws", 1, 1) + ws.rule = *whitespace + + var expr = newNonTerminal("expr", 1, 1) + expr.rule = sequence(capture(ident), *sequence( + nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) + + var c: Captures + var s = "a+b + c +d+e+f" + assert rawMatch(s, expr.rule, 0, c) == len(s) + var a = "" + for i in 0..c.ml-1: + a.add(substr(s, c.matches[i][0], c.matches[i][1])) + assert a == "abcdef" + #echo expr.rule + + #const filename = "lib/devel/peg/grammar.txt" + #var grammar = parsePeg(newFileStream(filename, fmRead), filename) + #echo "a <- [abc]*?".match(grammar) + assert find("_____abc_______", term("abc"), 2) == 5 + assert match("_______ana", peg"A <- 'ana' / . A") + assert match("abcs%%%", peg"A <- ..A / .A / '%'") + + var matches: array[0..MaxSubpatterns-1, string] + if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": + assert matches[0] == "abc" + else: + assert false + + var g2 = peg"""S <- A B / C D + A <- 'a'+ + B <- 'b'+ + C <- 'c'+ + D <- 'd'+ + """ + assert($g2 == "((A B) / (C D))") + assert match("cccccdddddd", g2) + assert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") == + "var1<-keykey; var2<-key2key2") + assert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") == + "$1<-$2$2; $1<-$2$2") + assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") + + if "aaaaaa" =~ peg"'aa' !. / ({'a'})+": + assert matches[0] == "a" + else: + assert false + + if match("abcdefg", peg"c {d} ef {g}", matches, 2): + assert matches[0] == "d" + assert matches[1] == "g" + else: + assert false + + accum = @[] + for x in findAll("abcdef", peg".", 3): + accum.add(x) + assert(accum == @["d", "e", "f"]) + + for x in findAll("abcdef", peg"^{.}", 3): + assert x == "d" + + if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": + assert matches[0] == "f" + assert matches[1] == "a, b" + else: + assert false + + assert match("eine übersicht und außerdem", peg"(\letter \white*)+") + # ß is not a lower cased letter?! + assert match("eine übersicht und auerdem", peg"(\lower \white*)+") + assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") + assert(not match("456678", peg"(\letter)+")) + + assert("var1 = key; var2 = key2".replacef( + peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == + "var1<-keykey;var2<-key2key2") + + assert match("prefix/start", peg"^start$", 7) + + if "foo" =~ peg"{'a'}?.*": + assert matches[0].len == 0 + else: assert false + + if "foo" =~ peg"{''}.*": + assert matches[0] == "" + else: assert false + + if "foo" =~ peg"{'foo'}": + assert matches[0] == "foo" + else: assert false + + let empty_test = peg"^\d*" + let str = "XYZ" + + assert(str.find(empty_test) == 0) + assert(str.match(empty_test)) + + proc handleMatches(m: int, n: int, c: openArray[string]): string = + result = "" + + if m > 0: + result.add ", " + + result.add case n: + of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'" + of 1: toLowerAscii(c[0]) & ": ''" + else: "" + + assert("Var1=key1;var2=Key2; VAR3". + replace(peg"{\ident}('='{\ident})* ';'* \s*", + handleMatches) == "var1: 'key1', var2: 'Key2', var3: ''") + + + doAssert "test1".match(peg"""{@}$""") + doAssert "test2".match(peg"""{(!$ .)*} $""") + pegsTest() + static: + pegsTest() diff --git a/lib/pure/prelude.nim b/lib/pure/prelude.nim deleted file mode 100644 index 9428f29eb..000000000 --- a/lib/pure/prelude.nim +++ /dev/null @@ -1,28 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -when defined(nimdoc) and isMainModule: - from std/compileSettings import nil - when compileSettings.querySetting(compileSettings.SingleValueSetting.projectFull) == currentSourcePath: - ## This is an include file that simply imports common modules for your convenience. - runnableExamples: - include std/prelude - # same as: - # import std/[os, strutils, times, parseutils, hashes, tables, sets, sequtils, parseopt, strformat] - let x = 1 - assert "foo $# $#" % [$x, "bar"] == "foo 1 bar" - assert toSeq(1..3) == @[1, 2, 3] - when not defined(js) or defined(nodejs): - assert getCurrentDir().len > 0 - assert ($now()).startsWith "20" - - # xxx `nim doc -b:js -d:nodejs --doccmd:-d:nodejs lib/pure/prelude.nim` fails for some reason - # specific to `nim doc`, but the code otherwise works with nodejs. - -import std/[os, strutils, times, parseutils, hashes, tables, sets, sequtils, parseopt, strformat] diff --git a/lib/pure/punycode.nim b/lib/pure/punycode.nim new file mode 100644 index 000000000..dfeeea35e --- /dev/null +++ b/lib/pure/punycode.nim @@ -0,0 +1,208 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Implements a representation of Unicode with the limited +## ASCII character subset. + +import strutils +import unicode + +# issue #3045 + +const + Base = 36 + TMin = 1 + TMax = 26 + Skew = 38 + Damp = 700 + InitialBias = 72 + InitialN = 128 + Delimiter = '-' + +type + PunyError* = object of ValueError + +proc decodeDigit(x: char): int {.raises: [PunyError].} = + if '0' <= x and x <= '9': + result = ord(x) - (ord('0') - 26) + elif 'A' <= x and x <= 'Z': + result = ord(x) - ord('A') + elif 'a' <= x and x <= 'z': + result = ord(x) - ord('a') + else: + raise newException(PunyError, "Bad input") + +proc encodeDigit(digit: int): Rune {.raises: [PunyError].} = + if 0 <= digit and digit < 26: + result = Rune(digit + ord('a')) + elif 26 <= digit and digit < 36: + result = Rune(digit + (ord('0') - 26)) + else: + raise newException(PunyError, "internal error in punycode encoding") + +proc isBasic(c: char): bool = ord(c) < 0x80 +proc isBasic(r: Rune): bool = int(r) < 0x80 + +proc adapt(delta, numPoints: int, first: bool): int = + var d = if first: delta div Damp else: delta div 2 + d += d div numPoints + var k = 0 + while d > ((Base-TMin)*TMax) div 2: + d = d div (Base - TMin) + k += Base + result = k + (Base - TMin + 1) * d div (d + Skew) + +proc encode*(prefix, s: string): string {.raises: [PunyError].} = + ## Encode a string that may contain Unicode. + ## Prepend `prefix` to the result + result = prefix + var (d, n, bias) = (0, InitialN, InitialBias) + var (b, remaining) = (0, 0) + for r in s.runes: + if r.isBasic: + # basic Ascii character + inc b + result.add($r) + else: + # special character + inc remaining + + var h = b + if b > 0: + result.add(Delimiter) # we have some Ascii chars + while remaining != 0: + var m: int = high(int32) + for r in s.runes: + if m > int(r) and int(r) >= n: + m = int(r) + d += (m - n) * (h + 1) + if d < 0: + raise newException(PunyError, "invalid label " & s) + n = m + for r in s.runes: + if int(r) < n: + inc d + if d < 0: + raise newException(PunyError, "invalid label " & s) + continue + if int(r) > n: + continue + var q = d + var k = Base + while true: + var t = k - bias + if t < TMin: + t = TMin + elif t > TMax: + t = TMax + if q < t: + break + result.add($encodeDigit(t + (q - t) mod (Base - t))) + q = (q - t) div (Base - t) + k += Base + result.add($encodeDigit(q)) + bias = adapt(d, h + 1, h == b) + d = 0 + inc h + dec remaining + inc d + inc n + +proc encode*(s: string): string {.raises: [PunyError].} = + ## Encode a string that may contain Unicode. Prefix is empty. + result = encode("", s) + +proc decode*(encoded: string): string {.raises: [PunyError].} = + ## Decode a Punycode-encoded string + var + n = InitialN + i = 0 + bias = InitialBias + var d = rfind(encoded, Delimiter) + result = "" + + if d > 0: + # found Delimiter + for j in 0.. (high(int32) - i) div w: + raise newException(PunyError, "Too large a value: " & $digit) + i += digit * w + var t: int + if k <= bias: + t = TMin + elif k >= bias + TMax: + t = TMax + else: + t = k - bias + if digit < t: + break + w *= Base - t + k += Base + bias = adapt(i - oldi, runeLen(result) + 1, oldi == 0) + + if i div (runeLen(result) + 1) > high(int32) - n: + raise newException(PunyError, "Value too large") + + n += i div (runeLen(result) + 1) + i = i mod (runeLen(result) + 1) + insert(result, $Rune(n), i) + inc i + + +runnableExamples: + static: + block: + doAssert encode("") == "" + doAssert encode("a") == "a-" + doAssert encode("A") == "A-" + doAssert encode("3") == "3-" + doAssert encode("-") == "--" + doAssert encode("--") == "---" + doAssert encode("abc") == "abc-" + doAssert encode("London") == "London-" + doAssert encode("Lloyd-Atkinson") == "Lloyd-Atkinson-" + doAssert encode("This has spaces") == "This has spaces-" + doAssert encode("ü") == "tda" + doAssert encode("München") == "Mnchen-3ya" + doAssert encode("Mnchen-3ya") == "Mnchen-3ya-" + doAssert encode("München-Ost") == "Mnchen-Ost-9db" + doAssert encode("Bahnhof München-Ost") == "Bahnhof Mnchen-Ost-u6b" + block: + doAssert decode("") == "" + doAssert decode("a-") == "a" + doAssert decode("A-") == "A" + doAssert decode("3-") == "3" + doAssert decode("--") == "-" + doAssert decode("---") == "--" + doAssert decode("abc-") == "abc" + doAssert decode("London-") == "London" + doAssert decode("Lloyd-Atkinson-") == "Lloyd-Atkinson" + doAssert decode("This has spaces-") == "This has spaces" + doAssert decode("tda") == "ü" + doAssert decode("Mnchen-3ya") == "München" + doAssert decode("Mnchen-3ya-") == "Mnchen-3ya" + doAssert decode("Mnchen-Ost-9db") == "München-Ost" + doAssert decode("Bahnhof Mnchen-Ost-u6b") == "Bahnhof München-Ost" diff --git a/lib/pure/random.nim b/lib/pure/random.nim index c36ab445b..d599727c3 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -7,9 +7,9 @@ # distribution, for details about the copyright. # -## Nim's standard random number generator (RNG). +## Nim's standard random number generator. ## -## Its implementation is based on the `xoroshiro128+` +## Its implementation is based on the ``xoroshiro128+`` ## (xor/rotate/shift/rotate) library. ## * More information: http://xoroshiro.di.unimi.it ## * C implementation: http://xoroshiro.di.unimi.it/xoroshiro128plus.c @@ -19,65 +19,69 @@ ## Basic usage ## =========== ## -runnableExamples: - # Call randomize() once to initialize the default random number generator. - # If this is not called, the same results will occur every time these - # examples are run. - randomize() - - # Pick a number in 0..100. - let num = rand(100) - doAssert num in 0..100 - - # Roll a six-sided die. - let roll = rand(1..6) - doAssert roll in 1..6 - - # Pick a marble from a bag. - let marbles = ["red", "blue", "green", "yellow", "purple"] - let pick = sample(marbles) - doAssert pick in marbles - - # Shuffle some cards. - var cards = ["Ace", "King", "Queen", "Jack", "Ten"] - shuffle(cards) - doAssert cards.len == 5 - -## These examples all use the default RNG. The -## `Rand type <#Rand>`_ represents the state of an RNG. +## To get started, here are some examples: +## +## .. code-block:: +## +## import random +## +## # Call randomize() once to initialize the default random number generator +## # If this is not called, the same results will occur every time these +## # examples are run +## randomize() +## +## # Pick a number between 0 and 100 +## let num = rand(100) +## echo num +## +## # Roll a six-sided die +## let roll = rand(1..6) +## echo roll +## +## # Pick a marble from a bag +## let marbles = ["red", "blue", "green", "yellow", "purple"] +## let pick = sample(marbles) +## echo pick +## +## # Shuffle some cards +## var cards = ["Ace", "King", "Queen", "Jack", "Ten"] +## shuffle(cards) +## echo cards +## +## These examples all use the default random number generator. The +## `Rand type<#Rand>`_ represents the state of a random number generator. ## For convenience, this module contains a default Rand state that corresponds -## to the default RNG. Most procs in this module which do +## to the default random number generator. Most procs in this module which do ## not take in a Rand parameter, including those called in the above examples, ## use the default generator. Those procs are **not** thread-safe. ## ## Note that the default generator always starts in the same state. -## The `randomize proc <#randomize>`_ can be called to initialize the default +## The `randomize proc<#randomize>`_ can be called to initialize the default ## generator with a seed based on the current time, and it only needs to be ## called once before the first usage of procs from this module. If -## `randomize` is not called, the default generator will always produce +## ``randomize`` is not called, then the default generator will always produce ## the same results. ## -## RNGs that are independent of the default one can be created with the -## `initRand proc <#initRand,int64>`_. +## Generators that are independent of the default one can be created with the +## `initRand proc<#initRand,int64>`_. ## ## Again, it is important to remember that this module must **not** be used for ## cryptographic applications. ## ## See also ## ======== -## * `std/sysrand module `_ for a cryptographically secure pseudorandom number generator -## * `math module `_ for basic math routines -## * `stats module `_ for statistical analysis -## * `list of cryptographic and hashing modules `_ +## * `math module`_ for basic math routines +## * `mersenne module`_ for the Mersenne Twister random number +## generator +## * `stats module`_ for statistical analysis +## * `list of cryptographic and hashing modules +## `_ ## in the standard library import algorithm, math import std/private/since -when defined(nimPreviewSlimSystem): - import std/[assertions] - -include system/inclrtl +include "system/inclrtl" {.push debugger: off.} when defined(js): @@ -92,12 +96,12 @@ else: type Rand* = object ## State of a random number generator. ## - ## Create a new Rand state using the `initRand proc <#initRand,int64>`_. + ## Create a new Rand state using the `initRand proc<#initRand,int64>`_. ## ## The module contains a default Rand state for convenience. - ## It corresponds to the default RNG's state. + ## It corresponds to the default random number generator's state. ## The default Rand state always starts with the same values, but the - ## `randomize proc <#randomize>`_ can be used to seed the default generator + ## `randomize proc<#randomize>`_ can be used to seed the default generator ## with a value based on the current time. ## ## Many procs have two variations: one that takes in a Rand parameter and @@ -110,19 +114,10 @@ when defined(js): a0: 0x69B4C98Cu32, a1: 0xFED1DD30u32) # global for backwards compatibility else: - const DefaultRandSeed = Rand( - a0: 0x69B4C98CB8530805u64, - a1: 0xFED1DD3004688D67CAu64) - # racy for multi-threading but good enough for now: - var state = DefaultRandSeed # global for backwards compatibility - -func isValid(r: Rand): bool {.inline.} = - ## Check whether state of `r` is valid. - ## - ## In `xoroshiro128+`, if all bits of `a0` and `a1` are zero, - ## they are always zero after calling `next(r: var Rand)`. - not (r.a0 == 0 and r.a1 == 0) + var state = Rand( + a0: 0x69B4C98CB8530805u64, + a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility since (1, 5): template randState*(): untyped = @@ -134,9 +129,9 @@ proc rotl(x, k: Ui): Ui = result = (x shl k) or (x shr (Ui(64) - k)) proc next*(r: var Rand): uint64 = - ## Computes a random `uint64` number using the given state. + ## Computes a random ``uint64`` number using the given state. ## - ## **See also:** + ## See also: ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer between zero and ## a given upper bound ## * `rand proc<#rand,Rand,range[]>`_ that returns a float @@ -144,11 +139,11 @@ proc next*(r: var Rand): uint64 = ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type ## * `skipRandomNumbers proc<#skipRandomNumbers,Rand>`_ - runnableExamples("-r:off"): + runnableExamples: var r = initRand(2019) - assert r.next() == 13223559681708962501'u64 # implementation defined - assert r.next() == 7229677234260823147'u64 # ditto - + doAssert r.next() == 138_744_656_611_299'u64 + doAssert r.next() == 979_810_537_855_049_344'u64 + doAssert r.next() == 3_628_232_584_225_300_704'u64 let s0 = r.a0 var s1 = r.a1 result = s0 + s1 @@ -159,12 +154,12 @@ proc next*(r: var Rand): uint64 = proc skipRandomNumbers*(s: var Rand) = ## The jump function for the generator. ## - ## This proc is equivalent to `2^64` calls to `next <#next,Rand>`_, and it can - ## be used to generate `2^64` non-overlapping subsequences for parallel + ## This proc is equivalent to 2^64 calls to `next<#next,Rand>`_, and it can + ## be used to generate 2^64 non-overlapping subsequences for parallel ## computations. ## ## When multiple threads are generating random numbers, each thread must - ## own the `Rand <#Rand>`_ state it is using so that the thread can safely + ## own the `Rand<#Rand>`_ state it is using so that the thread can safely ## obtain random numbers. However, if each thread creates its own Rand state, ## the subsequences of random numbers that each thread generates may overlap, ## even if the provided seeds are unique. This is more likely to happen as the @@ -175,39 +170,34 @@ proc skipRandomNumbers*(s: var Rand) = ## Rand state to a thread, call this proc before passing it to the next one. ## By using the Rand state this way, the subsequences of random numbers ## generated in each thread will never overlap as long as no thread generates - ## more than `2^64` random numbers. + ## more than 2^64 random numbers. ## - ## **See also:** + ## The following example below demonstrates this pattern: + ## + ## .. code-block:: + ## # Compile this example with --threads:on + ## import random + ## import threadpool + ## + ## const spawns = 4 + ## const numbers = 100000 + ## + ## proc randomSum(rand: Rand): int = + ## var r = rand + ## for i in 1..numbers: + ## result += rand(1..10) + ## + ## var r = initRand(2019) + ## var vals: array[spawns, FlowVar[int]] + ## for val in vals.mitems: + ## val = spawn(randomSum(r)) + ## r.skipRandomNumbers() + ## + ## for val in vals: + ## echo ^val + ## + ## See also: ## * `next proc<#next,Rand>`_ - runnableExamples("--threads:on"): - import std/random - - const numbers = 100000 - - 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: - s += r.rand(0..10) - vals[params.index] = s - - var r = initRand(2019) - for i in 0..`_ that returns an integer using the default RNG + ## See also: + ## * `rand proc<#rand,int>`_ that returns an integer using the default + ## random number generator ## * `rand proc<#rand,Rand,range[]>`_ that returns a float ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: var r = initRand(123) - if false: - assert r.rand(100) == 96 # implementation defined - # bootstrap: can't use `runnableExamples("-r:off")` - cast[int](rand(r, uint64(max))) - # xxx toUnsigned pending https://github.com/nim-lang/Nim/pull/18445 + doAssert r.rand(100) == 0 + doAssert r.rand(100) == 96 + doAssert r.rand(100) == 66 + if max == 0: return + while true: + let x = next(r) + if x <= randMax - (randMax mod Ui(max)): + return int(x mod (uint64(max)+1u64)) proc rand*(max: int): int {.benign.} = ## Returns a random integer in the range `0..max`. ## - ## If `randomize <#randomize>`_ has not been called, the sequence of random + ## If `randomize<#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## - ## This proc uses the default RNG. Thus, it is **not** thread-safe. + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. ## - ## **See also:** + ## See also: ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer using a ## provided state ## * `rand proc<#rand,float>`_ that returns a float ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type - runnableExamples("-r:off"): + runnableExamples: randomize(123) - assert [rand(100), rand(100)] == [96, 63] # implementation defined - + doAssert rand(100) == 0 + doAssert rand(100) == 96 + doAssert rand(100) == 66 rand(state, max) proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = ## Returns a random floating point number in the range `0.0..max` ## using the given state. ## - ## **See also:** - ## * `rand proc<#rand,float>`_ that returns a float using the default RNG + ## See also: + ## * `rand proc<#rand,float>`_ that returns a float using the default + ## random number generator ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: var r = initRand(234) - let f = r.rand(1.0) # 8.717181376738381e-07 - + let f = r.rand(1.0) + ## f = 8.717181376738381e-07 let x = next(r) when defined(js): result = (float(x) / float(high(uint32))) * max @@ -299,12 +283,13 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = proc rand*(max: float): float {.benign.} = ## Returns a random floating point number in the range `0.0..max`. ## - ## If `randomize <#randomize>`_ has not been called, the sequence of random + ## If `randomize<#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## - ## This proc uses the default RNG. Thus, it is **not** thread-safe. + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. ## - ## **See also:** + ## See also: ## * `rand proc<#rand,Rand,range[]>`_ that returns a float using a ## provided state ## * `rand proc<#rand,int>`_ that returns an integer @@ -313,8 +298,8 @@ proc rand*(max: float): float {.benign.} = ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: randomize(234) - let f = rand(1.0) # 8.717181376738381e-07 - + let f = rand(1.0) + ## f = 8.717181376738381e-07 rand(state, max) proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = @@ -323,36 +308,36 @@ proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = ## ## Allowed types for `T` are integers, floats, and enums without holes. ## - ## **See also:** + ## See also: ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ - ## that accepts a slice and uses the default RNG + ## that accepts a slice and uses the default random number generator ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer ## * `rand proc<#rand,Rand,range[]>`_ that returns a float ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: var r = initRand(345) - assert r.rand(1..5) <= 5 - assert r.rand(-1.1 .. 1.2) >= -1.1 - assert x.a <= x.b + doAssert r.rand(1..6) == 4 + doAssert r.rand(1..6) == 4 + doAssert r.rand(1..6) == 6 + let f = r.rand(-1.0 .. 1.0) + ## f = 0.8741183448756229 when T is SomeFloat: result = rand(r, x.b - x.a) + x.a else: # Integers and Enum types - when defined(js): - result = cast[T](rand(r, cast[uint](x.b) - cast[uint](x.a)) + cast[uint](x.a)) - else: - result = cast[T](rand(r, cast[uint64](x.b) - cast[uint64](x.a)) + cast[uint64](x.a)) + result = T(rand(r, int(x.b) - int(x.a)) + int(x.a)) proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = ## For a slice `a..b`, returns a value in the range `a..b`. ## ## Allowed types for `T` are integers, floats, and enums without holes. ## - ## If `randomize <#randomize>`_ has not been called, the sequence of random + ## If `randomize<#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## - ## This proc uses the default RNG. Thus, it is **not** thread-safe. + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. ## - ## **See also:** + ## See also: ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ ## that accepts a slice and uses a provided state ## * `rand proc<#rand,int>`_ that returns an integer @@ -360,72 +345,56 @@ proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: randomize(345) - assert rand(1..6) <= 6 - + doAssert rand(1..6) == 4 + doAssert rand(1..6) == 4 + doAssert rand(1..6) == 6 result = rand(state, x) -proc rand*[T: Ordinal](r: var Rand; t: typedesc[T]): T {.since: (1, 7, 1).} = - ## Returns a random Ordinal in the range `low(T)..high(T)`. +proc rand*[T: SomeInteger](t: typedesc[T]): T = + ## Returns a random integer in the range `low(T)..high(T)`. ## - ## If `randomize <#randomize>`_ has not been called, the sequence of random + ## If `randomize<#randomize>`_ 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)`. + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. ## - ## If `randomize <#randomize>`_ has not been called, the sequence of random - ## numbers returned from this proc will always be the same. - ## - ## This proc uses the default RNG. Thus, it is **not** thread-safe. - ## - ## **See also:** + ## 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 runnableExamples: randomize(567) - 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) + doAssert rand(int8) == 55 + doAssert rand(int8) == -42 + doAssert rand(int8) == 43 + doAssert rand(uint32) == 578980729'u32 + doAssert rand(uint32) == 4052940463'u32 + doAssert rand(uint32) == 2163872389'u32 + doAssert rand(range[1..16]) == 11 + doAssert rand(range[1..16]) == 4 + doAssert rand(range[1..16]) == 16 + when T is range: + result = rand(state, low(T)..high(T)) + else: + result = cast[T](state.next) proc sample*[T](r: var Rand; s: set[T]): T = - ## Returns a random element from the set `s` using the given state. + ## Returns a random element from the set ``s`` using the given state. ## - ## **See also:** - ## * `sample proc<#sample,set[T]>`_ that uses the default RNG - ## * `sample proc<#sample,Rand,openArray[T]>`_ for `openArray`s + ## See also: + ## * `sample proc<#sample,set[T]>`_ that uses the default random number + ## generator + ## * `sample proc<#sample,Rand,openArray[T]>`_ for openarrays ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that uses a ## cumulative distribution function runnableExamples: var r = initRand(987) let s = {1, 3, 5, 7, 9} - assert r.sample(s) in s - + doAssert r.sample(s) == 5 + doAssert r.sample(s) == 7 + doAssert r.sample(s) == 1 assert card(s) != 0 var i = rand(r, card(s) - 1) for e in s: @@ -433,49 +402,54 @@ proc sample*[T](r: var Rand; s: set[T]): T = dec(i) proc sample*[T](s: set[T]): T = - ## Returns a random element from the set `s`. + ## Returns a random element from the set ``s``. ## - ## If `randomize <#randomize>`_ has not been called, the order of outcomes + ## If `randomize<#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default RNG. Thus, it is **not** thread-safe. + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. ## - ## **See also:** + ## See also: ## * `sample proc<#sample,Rand,set[T]>`_ that uses a provided state - ## * `sample proc<#sample,openArray[T]>`_ for `openArray`s + ## * `sample proc<#sample,openArray[T]>`_ for openarrays ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that uses a ## cumulative distribution function runnableExamples: randomize(987) let s = {1, 3, 5, 7, 9} - assert sample(s) in s - + doAssert sample(s) == 5 + doAssert sample(s) == 7 + doAssert sample(s) == 1 sample(state, s) proc sample*[T](r: var Rand; a: openArray[T]): T = - ## Returns a random element from `a` using the given state. + ## Returns a random element from ``a`` using the given state. ## - ## **See also:** - ## * `sample proc<#sample,openArray[T]>`_ that uses the default RNG + ## See also: + ## * `sample proc<#sample,openArray[T]>`_ that uses the default + ## random number generator ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that uses a ## cumulative distribution function ## * `sample proc<#sample,Rand,set[T]>`_ for sets runnableExamples: let marbles = ["red", "blue", "green", "yellow", "purple"] var r = initRand(456) - assert r.sample(marbles) in marbles - + doAssert r.sample(marbles) == "blue" + doAssert r.sample(marbles) == "yellow" + doAssert r.sample(marbles) == "red" result = a[r.rand(a.low..a.high)] -proc sample*[T](a: openArray[T]): lent T = - ## Returns a random element from `a`. +proc sample*[T](a: openArray[T]): T = + ## Returns a random element from ``a``. ## - ## If `randomize <#randomize>`_ has not been called, the order of outcomes + ## If `randomize<#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default RNG. Thus, it is **not** thread-safe. + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. ## - ## **See also:** + ## See also: ## * `sample proc<#sample,Rand,openArray[T]>`_ that uses a provided state ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that uses a ## cumulative distribution function @@ -483,79 +457,84 @@ proc sample*[T](a: openArray[T]): lent T = runnableExamples: let marbles = ["red", "blue", "green", "yellow", "purple"] randomize(456) - assert sample(marbles) in marbles - + doAssert sample(marbles) == "blue" + doAssert sample(marbles) == "yellow" + doAssert sample(marbles) == "red" result = a[rand(a.low..a.high)] proc sample*[T, U](r: var Rand; a: openArray[T]; cdf: openArray[U]): T = - ## Returns an element from `a` using a cumulative distribution function + ## Returns an element from ``a`` using a cumulative distribution function ## (CDF) and the given state. ## - ## The `cdf` argument does not have to be normalized, and it could contain - ## any type of elements that can be converted to a `float`. It must be - ## the same length as `a`. Each element in `cdf` should be greater than + ## The ``cdf`` argument does not have to be normalized, and it could contain + ## any type of elements that can be converted to a ``float``. It must be + ## the same length as ``a``. Each element in ``cdf`` should be greater than ## or equal to the previous element. ## ## The outcome of the `cumsum`_ proc and the ## return value of the `cumsummed`_ proc, - ## which are both in the math module, can be used as the `cdf` argument. + ## which are both in the math module, can be used as the ``cdf`` argument. ## - ## **See also:** + ## See also: ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that also utilizes - ## a CDF but uses the default RNG + ## a CDF but uses the default random number generator ## * `sample proc<#sample,Rand,openArray[T]>`_ that does not use a CDF ## * `sample proc<#sample,Rand,set[T]>`_ for sets runnableExamples: - from std/math import cumsummed + from math import cumsummed let marbles = ["red", "blue", "green", "yellow", "purple"] let count = [1, 6, 8, 3, 4] let cdf = count.cumsummed var r = initRand(789) - assert r.sample(marbles, cdf) in marbles - + doAssert r.sample(marbles, cdf) == "red" + doAssert r.sample(marbles, cdf) == "green" + doAssert r.sample(marbles, cdf) == "blue" assert(cdf.len == a.len) # Two basic sanity checks. assert(float(cdf[^1]) > 0.0) - # While we could check cdf[i-1] <= cdf[i] for i in 1..cdf.len, that could get - # awfully expensive even in debugging modes. + #While we could check cdf[i-1] <= cdf[i] for i in 1..cdf.len, that could get + #awfully expensive even in debugging modes. let u = r.rand(float(cdf[^1])) a[cdf.upperBound(U(u))] proc sample*[T, U](a: openArray[T]; cdf: openArray[U]): T = - ## Returns an element from `a` using a cumulative distribution function + ## Returns an element from ``a`` using a cumulative distribution function ## (CDF). ## ## This proc works similarly to - ## `sample <#sample,Rand,openArray[T],openArray[U]>`_. + ## `sample[T, U](Rand, openArray[T], openArray[U]) + ## <#sample,Rand,openArray[T],openArray[U]>`_. ## See that proc's documentation for more details. ## - ## If `randomize <#randomize>`_ has not been called, the order of outcomes + ## If `randomize<#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default RNG. Thus, it is **not** thread-safe. + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. ## - ## **See also:** + ## See also: ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that also utilizes ## a CDF but uses a provided state ## * `sample proc<#sample,openArray[T]>`_ that does not use a CDF ## * `sample proc<#sample,set[T]>`_ for sets runnableExamples: - from std/math import cumsummed + from math import cumsummed let marbles = ["red", "blue", "green", "yellow", "purple"] let count = [1, 6, 8, 3, 4] let cdf = count.cumsummed randomize(789) - assert sample(marbles, cdf) in marbles - + doAssert sample(marbles, cdf) == "red" + doAssert sample(marbles, cdf) == "green" + doAssert sample(marbles, cdf) == "blue" state.sample(a, cdf) proc gauss*(r: var Rand; mu = 0.0; sigma = 1.0): float {.since: (1, 3).} = ## Returns a Gaussian random variate, - ## with mean `mu` and standard deviation `sigma` + ## with mean ``mu`` and standard deviation ``sigma`` ## using the given state. # Ratio of uniforms method for normal - # https://www2.econ.osaka-u.ac.jp/~tanizaki/class/2013/econome3/13.pdf + # http://www2.econ.osaka-u.ac.jp/~tanizaki/class/2013/econome3/13.pdf const K = sqrt(2 / E) var a = 0.0 @@ -568,75 +547,70 @@ proc gauss*(r: var Rand; mu = 0.0; sigma = 1.0): float {.since: (1, 3).} = proc gauss*(mu = 0.0, sigma = 1.0): float {.since: (1, 3).} = ## Returns a Gaussian random variate, - ## with mean `mu` and standard deviation `sigma`. + ## with mean ``mu`` and standard deviation ``sigma``. ## - ## If `randomize <#randomize>`_ has not been called, the order of outcomes + ## If `randomize<#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default RNG. Thus, it is **not** thread-safe. + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. result = gauss(state, mu, sigma) proc initRand*(seed: int64): Rand = - ## Initializes a new `Rand <#Rand>`_ state using the given seed. + ## Initializes a new `Rand<#Rand>`_ state using the given seed. ## - ## Providing a specific seed will produce the same results for that seed each time. + ## `seed` must not be zero. Providing a specific seed will produce + ## the same results for that seed each time. ## - ## The resulting state is independent of the default RNG's state. When `seed == 0`, - ## we internally set the seed to an implementation defined non-zero value. + ## The resulting state is independent of the default random number + ## generator's state. ## - ## **See also:** - ## * `initRand proc<#initRand>`_ that uses the current time - ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default RNG - ## * `randomize proc<#randomize>`_ that initializes the default RNG using the current time + ## See also: + ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default + ## random number generator + ## * `randomize proc<#randomize>`_ that initializes the default random + ## number generator using the current time runnableExamples: - from std/times import getTime, toUnix, nanosecond + from times import getTime, toUnix, nanosecond var r1 = initRand(123) + let now = getTime() var r2 = initRand(now.toUnix * 1_000_000_000 + now.nanosecond) - const seedFallback0 = int32.high # arbitrary - let seed = if seed != 0: seed else: seedFallback0 # because 0 is a fixed point + doAssert seed != 0 # 0 causes `rand(int)` to always return 0 for example. result.a0 = Ui(seed shr 16) result.a1 = Ui(seed and 0xffff) - when not defined(nimLegacyRandomInitRand): - # calling `discard next(result)` (even a few times) would still produce - # skewed numbers for the 1st call to `rand()`. - skipRandomNumbers(result) discard next(result) proc randomize*(seed: int64) {.benign.} = ## Initializes the default random number generator with the given seed. ## - ## Providing a specific seed will produce the same results for that seed each time. + ## `seed` must not be zero. Providing a specific seed will produce + ## the same results for that seed each time. ## - ## **See also:** - ## * `initRand proc<#initRand,int64>`_ that initializes a Rand state - ## with a given seed + ## See also: + ## * `initRand proc<#initRand,int64>`_ ## * `randomize proc<#randomize>`_ that uses the current time instead - ## * `initRand proc<#initRand>`_ that initializes a Rand state using - ## the current time runnableExamples: - from std/times import getTime, toUnix, nanosecond + from times import getTime, toUnix, nanosecond randomize(123) let now = getTime() randomize(now.toUnix * 1_000_000_000 + now.nanosecond) - state = initRand(seed) proc shuffle*[T](r: var Rand; x: var openArray[T]) = ## Shuffles a sequence of elements in-place using the given state. ## - ## **See also:** - ## * `shuffle proc<#shuffle,openArray[T]>`_ that uses the default RNG + ## See also: + ## * `shuffle proc<#shuffle,openArray[T]>`_ that uses the default + ## random number generator runnableExamples: var cards = ["Ace", "King", "Queen", "Jack", "Ten"] var r = initRand(678) r.shuffle(cards) - import std/algorithm - assert cards.sorted == @["Ace", "Jack", "King", "Queen", "Ten"] - + doAssert cards == ["King", "Ace", "Queen", "Ten", "Jack"] for i in countdown(x.high, 1): let j = r.rand(i) swap(x[i], x[j]) @@ -644,104 +618,42 @@ proc shuffle*[T](r: var Rand; x: var openArray[T]) = proc shuffle*[T](x: var openArray[T]) = ## Shuffles a sequence of elements in-place. ## - ## If `randomize <#randomize>`_ has not been called, the order of outcomes + ## If `randomize<#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default RNG. Thus, it is **not** thread-safe. + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. ## - ## **See also:** + ## See also: ## * `shuffle proc<#shuffle,Rand,openArray[T]>`_ that uses a provided state runnableExamples: var cards = ["Ace", "King", "Queen", "Jack", "Ten"] randomize(678) shuffle(cards) - import std/algorithm - assert cards.sorted == @["Ace", "Jack", "King", "Queen", "Ten"] - + doAssert cards == ["King", "Ace", "Queen", "Ten", "Jack"] shuffle(state, x) -when not defined(standalone): - when defined(js): - import std/times - else: - when defined(nimscript): - import std/hashes - else: - import std/[hashes, os, sysrand, monotimes] - - when compileOption("threads"): - import locks - var baseSeedLock: Lock - baseSeedLock.initLock - - var baseState: Rand - - proc initRand(): Rand = - ## Initializes a new Rand state. - ## - ## The resulting state is independent of the default RNG's state. - ## - ## **Note:** Does not work for the compile-time VM. - ## - ## See also: - ## * `initRand proc<#initRand,int64>`_ that accepts a seed for a new Rand state - ## * `randomize proc<#randomize>`_ that initializes the default RNG using the current time - ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default RNG - when defined(js): - let time = int64(times.epochTime() * 1000) and 0x7fff_ffff - result = initRand(time) - else: - proc getRandomState(): Rand = - when defined(nimscript): - result = Rand( - a0: CompileTime.hash.Ui, - a1: CompileDate.hash.Ui) - if not result.isValid: - result = DefaultRandSeed - else: - var urand: array[sizeof(Rand), byte] - - for i in 0 .. 7: - if sysrand.urandom(urand): - copyMem(result.addr, urand[0].addr, sizeof(Rand)) - if result.isValid: - break - - if not result.isValid: - # Don't try to get alternative random values from other source like time or process/thread id, - # because such code would be never tested and is a liability for security. - quit("Failed to initializes baseState in random module as sysrand.urandom doesn't work.") - - when compileOption("threads"): - baseSeedLock.withLock: - if not baseState.isValid: - baseState = getRandomState() - result = baseState - baseState.skipRandomNumbers - else: - if not baseState.isValid: - baseState = getRandomState() - result = baseState - baseState.skipRandomNumbers - - since (1, 5, 1): - export initRand +when not defined(nimscript) and not defined(standalone): + import times proc randomize*() {.benign.} = - ## Initializes the default random number generator with a seed based on - ## random number source. + ## Initializes the default random number generator with a value based on + ## the current time. ## ## This proc only needs to be called once, and it should be called before - ## the first usage of procs from this module that use the default RNG. + ## the first usage of procs from this module that use the default random + ## number generator. ## - ## **Note:** Does not work for the compile-time VM. + ## **Note:** Does not work for NimScript. ## - ## **See also:** + ## See also: ## * `randomize proc<#randomize,int64>`_ that accepts a seed - ## * `initRand proc<#initRand>`_ that initializes a Rand state using - ## the current time - ## * `initRand proc<#initRand,int64>`_ that initializes a Rand state - ## with a given seed - state = initRand() + ## * `initRand proc<#initRand,int64>`_ + when defined(js): + let time = int64(times.epochTime() * 1000) and 0x7fff_ffff + randomize(time) + else: + let now = times.getTime() + randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond) {.pop.} diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index aba0f4ce4..2a1a97d3b 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -8,95 +8,56 @@ # -## This module implements rational numbers, consisting of a numerator and -## a denominator. The denominator can not be 0. +## This module implements rational numbers, consisting of a numerator `num` and +## a denominator `den`, both of type int. The denominator can not be 0. -runnableExamples: - let - r1 = 1 // 2 - r2 = -3 // 4 - - doAssert r1 + r2 == -1 // 4 - doAssert r1 - r2 == 5 // 4 - doAssert r1 * r2 == -3 // 8 - doAssert r1 / r2 == -2 // 3 - -import math, hashes -when defined(nimPreviewSlimSystem): - import std/assertions +import math +import hashes type Rational*[T] = object - ## A rational number, consisting of a numerator `num` and a denominator `den`. + ## a rational number, consisting of a numerator and denominator num*, den*: T -func reduce*[T: SomeInteger](x: var Rational[T]) = - ## Reduces the rational number `x`, so that the numerator and denominator - ## have no common divisors other than 1 (and -1). - ## If `x` is 0, raises `DivByZeroDefect`. - ## - ## **Note:** This is called automatically by the various operations on rationals. - runnableExamples: - var r = Rational[int](num: 2, den: 4) # 1/2 - reduce(r) - doAssert r.num == 1 - doAssert r.den == 2 - - let common = gcd(x.num, x.den) - if x.den > 0: - x.num = x.num div common - x.den = x.den div common - elif x.den < 0: - x.num = -x.num div common - x.den = -x.den div common - else: - raise newException(DivByZeroDefect, "division by zero") - func initRational*[T: SomeInteger](num, den: T): Rational[T] = - ## Creates a new rational number with numerator `num` and denominator `den`. - ## `den` must not be 0. - ## - ## **Note:** `den != 0` is not checked when assertions are turned off. - assert(den != 0, "a denominator of zero is invalid") + ## Create a new rational number. + assert(den != 0, "a denominator of zero value is invalid") result.num = num result.den = den - reduce(result) -func `//`*[T](num, den: T): Rational[T] = - ## A friendlier version of `initRational <#initRational,T,T>`_. - runnableExamples: - let x = 1 // 3 + 1 // 5 - doAssert x == 8 // 15 - - initRational[T](num, den) +func `//`*[T](num, den: T): Rational[T] = initRational[T](num, den) + ## A friendlier version of `initRational`. Example usage: + ## + ## .. code-block:: nim + ## var x = 1//3 + 1//5 func `$`*[T](x: Rational[T]): string = - ## Turns a rational number into a string. - runnableExamples: - doAssert $(1 // 2) == "1/2" - + ## Turn a rational number into a string. result = $x.num & "/" & $x.den func toRational*[T: SomeInteger](x: T): Rational[T] = - ## Converts some integer `x` to a rational number. - runnableExamples: - doAssert toRational(42) == 42 // 1 - + ## Convert some integer `x` to a rational number. result.num = x result.den = 1 func toRational*(x: float, n: int = high(int) shr (sizeof(int) div 2 * 8)): Rational[int] = - ## Calculates the best rational approximation of `x`, - ## where the denominator is smaller than `n` - ## (default is the largest possible `int` for maximal resolution). + ## Calculates the best rational numerator and denominator + ## that approximates to `x`, where the denominator is + ## smaller than `n` (default is the largest possible + ## int to give maximum resolution). ## ## The algorithm is based on the theory of continued fractions. + ## + ## .. code-block:: Nim + ## import math, rationals + ## for i in 1..10: + ## let t = (10 ^ (i+3)).int + ## let x = toRational(PI, t) + ## let newPI = x.num / x.den + ## echo x, " ", newPI, " error: ", PI - newPI, " ", t + # David Eppstein / UC Irvine / 8 Aug 1993 # With corrections from Arno Formella, May 2008 - runnableExamples: - let x = 1.2 - doAssert x.toRational.toFloat == x - var m11, m22 = 1 m12, m21 = 0 @@ -108,113 +69,124 @@ func toRational*(x: float, m11 = m12 * ai + m11 m21 = m22 * ai + m21 if x == float(ai): break # division by zero - x = 1 / (x - float(ai)) + x = 1/(x - float(ai)) if x > float(high(int32)): break # representation failure ai = int(x) result = m11 // m21 func toFloat*[T](x: Rational[T]): float = - ## Converts a rational number `x` to a `float`. + ## Convert a rational number `x` to a float. x.num / x.den func toInt*[T](x: Rational[T]): int = - ## Converts a rational number `x` to an `int`. Conversion rounds towards 0 if + ## Convert a rational number `x` to an int. Conversion rounds towards 0 if ## `x` does not contain an integer value. x.num div x.den -func `+`*[T](x, y: Rational[T]): Rational[T] = - ## Adds two rational numbers. +func reduce*[T: SomeInteger](x: var Rational[T]) = + ## Reduce rational `x`. + let common = gcd(x.num, x.den) + if x.den > 0: + x.num = x.num div common + x.den = x.den div common + elif x.den < 0: + x.num = -x.num div common + x.den = -x.den div common + else: + raise newException(DivByZeroDefect, "division by zero") + +func `+` *[T](x, y: Rational[T]): Rational[T] = + ## Add two rational numbers. let common = lcm(x.den, y.den) result.num = common div x.den * x.num + common div y.den * y.num result.den = common reduce(result) -func `+`*[T](x: Rational[T], y: T): Rational[T] = - ## Adds the rational `x` to the int `y`. +func `+` *[T](x: Rational[T], y: T): Rational[T] = + ## Add rational `x` to int `y`. result.num = x.num + y * x.den result.den = x.den -func `+`*[T](x: T, y: Rational[T]): Rational[T] = - ## Adds the int `x` to the rational `y`. +func `+` *[T](x: T, y: Rational[T]): Rational[T] = + ## Add int `x` to rational `y`. result.num = x * y.den + y.num result.den = y.den -func `+=`*[T](x: var Rational[T], y: Rational[T]) = - ## Adds the rational `y` to the rational `x` in-place. +func `+=` *[T](x: var Rational[T], y: Rational[T]) = + ## Add rational `y` to rational `x`. let common = lcm(x.den, y.den) x.num = common div x.den * x.num + common div y.den * y.num x.den = common reduce(x) -func `+=`*[T](x: var Rational[T], y: T) = - ## Adds the int `y` to the rational `x` in-place. +func `+=` *[T](x: var Rational[T], y: T) = + ## Add int `y` to rational `x`. x.num += y * x.den -func `-`*[T](x: Rational[T]): Rational[T] = +func `-` *[T](x: Rational[T]): Rational[T] = ## Unary minus for rational numbers. result.num = -x.num result.den = x.den -func `-`*[T](x, y: Rational[T]): Rational[T] = - ## Subtracts two rational numbers. +func `-` *[T](x, y: Rational[T]): Rational[T] = + ## Subtract two rational numbers. let common = lcm(x.den, y.den) result.num = common div x.den * x.num - common div y.den * y.num result.den = common reduce(result) -func `-`*[T](x: Rational[T], y: T): Rational[T] = - ## Subtracts the int `y` from the rational `x`. +func `-` *[T](x: Rational[T], y: T): Rational[T] = + ## Subtract int `y` from rational `x`. result.num = x.num - y * x.den result.den = x.den -func `-`*[T](x: T, y: Rational[T]): Rational[T] = - ## Subtracts the rational `y` from the int `x`. +func `-` *[T](x: T, y: Rational[T]): Rational[T] = + ## Subtract rational `y` from int `x`. result.num = x * y.den - y.num result.den = y.den -func `-=`*[T](x: var Rational[T], y: Rational[T]) = - ## Subtracts the rational `y` from the rational `x` in-place. +func `-=` *[T](x: var Rational[T], y: Rational[T]) = + ## Subtract rational `y` from rational `x`. let common = lcm(x.den, y.den) x.num = common div x.den * x.num - common div y.den * y.num x.den = common reduce(x) -func `-=`*[T](x: var Rational[T], y: T) = - ## Subtracts the int `y` from the rational `x` in-place. +func `-=` *[T](x: var Rational[T], y: T) = + ## Subtract int `y` from rational `x`. x.num -= y * x.den -func `*`*[T](x, y: Rational[T]): Rational[T] = - ## Multiplies two rational numbers. +func `*` *[T](x, y: Rational[T]): Rational[T] = + ## Multiply two rational numbers. result.num = x.num * y.num result.den = x.den * y.den reduce(result) -func `*`*[T](x: Rational[T], y: T): Rational[T] = - ## Multiplies the rational `x` with the int `y`. +func `*` *[T](x: Rational[T], y: T): Rational[T] = + ## Multiply rational `x` with int `y`. result.num = x.num * y result.den = x.den reduce(result) -func `*`*[T](x: T, y: Rational[T]): Rational[T] = - ## Multiplies the int `x` with the rational `y`. +func `*` *[T](x: T, y: Rational[T]): Rational[T] = + ## Multiply int `x` with rational `y`. result.num = x * y.num result.den = y.den reduce(result) -func `*=`*[T](x: var Rational[T], y: Rational[T]) = - ## Multiplies the rational `x` by `y` in-place. +func `*=` *[T](x: var Rational[T], y: Rational[T]) = + ## Multiply rationals `y` to `x`. x.num *= y.num x.den *= y.den reduce(x) -func `*=`*[T](x: var Rational[T], y: T) = - ## Multiplies the rational `x` by the int `y` in-place. +func `*=` *[T](x: var Rational[T], y: T) = + ## Multiply int `y` to rational `x`. x.num *= y reduce(x) func reciprocal*[T](x: Rational[T]): Rational[T] = - ## Calculates the reciprocal of `x` (`1/x`). - ## If `x` is 0, raises `DivByZeroDefect`. + ## Calculate the reciprocal of `x`. (1/x) if x.num > 0: result.num = x.den result.den = x.num @@ -225,59 +197,48 @@ func reciprocal*[T](x: Rational[T]): Rational[T] = raise newException(DivByZeroDefect, "division by zero") func `/`*[T](x, y: Rational[T]): Rational[T] = - ## Divides the rational `x` by the rational `y`. + ## Divide rationals `x` by `y`. result.num = x.num * y.den result.den = x.den * y.num reduce(result) func `/`*[T](x: Rational[T], y: T): Rational[T] = - ## Divides the rational `x` by the int `y`. + ## Divide rational `x` by int `y`. result.num = x.num result.den = x.den * y reduce(result) func `/`*[T](x: T, y: Rational[T]): Rational[T] = - ## Divides the int `x` by the rational `y`. + ## Divide int `x` by Rational `y`. result.num = x * y.den result.den = y.num reduce(result) func `/=`*[T](x: var Rational[T], y: Rational[T]) = - ## Divides the rational `x` by the rational `y` in-place. + ## Divide rationals `x` by `y` in place. x.num *= y.den x.den *= y.num reduce(x) func `/=`*[T](x: var Rational[T], y: T) = - ## Divides the rational `x` by the int `y` in-place. + ## Divide rational `x` by int `y` in place. x.den *= y reduce(x) func cmp*(x, y: Rational): int = - ## Compares two rationals. Returns - ## * a value less than zero, if `x < y` - ## * a value greater than zero, if `x > y` - ## * zero, if `x == y` + ## Compares two rationals. (x - y).num -func `<`*(x, y: Rational): bool = - ## Returns true if `x` is less than `y`. +func `<` *(x, y: Rational): bool = (x - y).num < 0 -func `<=`*(x, y: Rational): bool = - ## Returns tue if `x` is less than or equal to `y`. +func `<=` *(x, y: Rational): bool = (x - y).num <= 0 -func `==`*(x, y: Rational): bool = - ## Compares two rationals for equality. +func `==` *(x, y: Rational): bool = (x - y).num == 0 func abs*[T](x: Rational[T]): Rational[T] = - ## Returns the absolute value of `x`. - runnableExamples: - doAssert abs(1 // 2) == 1 // 2 - doAssert abs(-1 // 2) == 1 // 2 - result.num = abs x.num result.den = abs x.den @@ -287,29 +248,29 @@ func `div`*[T: SomeInteger](x, y: Rational[T]): T = func `mod`*[T: SomeInteger](x, y: Rational[T]): Rational[T] = ## Computes the rational modulo by truncated division (remainder). - ## This is same as `x - (x div y) * y`. + ## This is same as ``x - (x div y) * y``. result = ((x.num * y.den) mod (y.num * x.den)) // (x.den * y.den) reduce(result) func floorDiv*[T: SomeInteger](x, y: Rational[T]): T = ## Computes the rational floor division. ## - ## Floor division is conceptually defined as `floor(x / y)`. - ## This is different from the `div` operator, which is defined - ## as `trunc(x / y)`. That is, `div` rounds towards 0 and `floorDiv` + ## Floor division is conceptually defined as ``floor(x / y)``. + ## This is different from the ``div`` operator, which is defined + ## as ``trunc(x / y)``. That is, ``div`` rounds towards ``0`` and ``floorDiv`` ## rounds down. floorDiv(x.num * y.den, y.num * x.den) func floorMod*[T: SomeInteger](x, y: Rational[T]): Rational[T] = ## Computes the rational modulo by floor division (modulo). ## - ## This is same as `x - floorDiv(x, y) * y`. - ## This func behaves the same as the `%` operator in Python. + ## This is same as ``x - floorDiv(x, y) * y``. + ## This func behaves the same as the ``%`` operator in python. result = floorMod(x.num * y.den, y.num * x.den) // (x.den * y.den) reduce(result) func hash*[T](x: Rational[T]): Hash = - ## Computes the hash for the rational `x`. + ## Computes hash for rational `x` # reduce first so that hash(x) == hash(y) for x == y var copy = x reduce(copy) diff --git a/lib/pure/reservedmem.nim b/lib/pure/reservedmem.nim index 528b0095c..469ca7efa 100644 --- a/lib/pure/reservedmem.nim +++ b/lib/pure/reservedmem.nim @@ -9,7 +9,7 @@ ## :Authors: Zahary Karadjov ## -## This module provides utilities for reserving portions of the +## This module provides utilities for reserving a portions of the ## address space of a program without consuming physical memory. ## It can be used to implement a dynamically resizable buffer that ## is guaranteed to remain in the same memory location. The buffer @@ -18,10 +18,7 @@ ## ## Unstable API. -from std/oserrors import raiseOSError, osLastError - -when defined(nimPreviewSlimSystem): - import std/assertions +from os import raiseOSError, osLastError template distance*(lhs, rhs: pointer): int = cast[int](rhs) - cast[int](lhs) @@ -45,10 +42,25 @@ type when defined(windows): import winlean - import std/private/win_getsysteminfo + + type + SYSTEM_INFO {.final, pure.} = object + u1: uint32 + dwPageSize: uint32 + lpMinimumApplicationAddress: pointer + lpMaximumApplicationAddress: pointer + dwActiveProcessorMask: ptr uint32 + dwNumberOfProcessors: uint32 + dwProcessorType: uint32 + dwAllocationGranularity: uint32 + wProcessorLevel: uint16 + wProcessorRevision: uint16 + + proc getSystemInfo(lpSystemInfo: ptr SYSTEM_INFO) {.stdcall, + dynlib: "kernel32", importc: "GetSystemInfo".} proc getAllocationGranularity: uint = - var sysInfo: SystemInfo + var sysInfo: SYSTEM_INFO getSystemInfo(addr sysInfo) return uint(sysInfo.dwAllocationGranularity) @@ -64,7 +76,7 @@ when defined(windows): template check(expr) = let r = expr - if r == cast[typeof(r)](0): + if r == cast[type(r)](0): raiseOSError(osLastError()) else: diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index b973fd222..83850e03e 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -8,20 +8,17 @@ # ## This module contains support for a `rope`:idx: data type. -## Ropes can represent very long strings efficiently; in particular, concatenation +## Ropes can represent very long strings efficiently; especially concatenation ## is done in O(1) instead of O(n). They are essentially concatenation ## trees that are only flattened when converting to a native Nim -## string. The empty string is represented by `nil`. Ropes are immutable and +## string. The empty string is represented by ``nil``. Ropes are immutable and ## subtrees can be shared without copying. ## Leaves can be cached for better memory efficiency at the cost of ## runtime efficiency. -include system/inclrtl +include "system/inclrtl" import streams -when defined(nimPreviewSlimSystem): - import std/[syncio, formatfloat, assertions] - {.push debugger: off.} # the user does not want to trace a part # of the standard library! @@ -32,8 +29,8 @@ var cacheEnabled = false type - Rope* {.acyclic.} = ref object - ## A rope data type. The empty rope is represented by `nil`. + Rope* = ref RopeObj ## empty rope is represented by nil + RopeObj {.acyclic.} = object left, right: Rope length: int data: string # not empty if a leaf @@ -48,7 +45,8 @@ type proc len*(a: Rope): int {.rtl, extern: "nro$1".} = ## The rope's length. - if a == nil: 0 else: a.length + if a == nil: result = 0 + else: result = a.length proc newRope(): Rope = new(result) proc newRope(data: string): Rope = @@ -130,9 +128,8 @@ proc insertInCache(s: string, tree: Rope): Rope = proc rope*(s: string = ""): Rope {.rtl, extern: "nro$1Str".} = ## Converts a string to a rope. runnableExamples: - let r = rope("I'm a rope") + var r = rope("I'm a rope") doAssert $r == "I'm a rope" - if s.len == 0: result = nil else: @@ -149,17 +146,15 @@ proc rope*(s: string = ""): Rope {.rtl, extern: "nro$1Str".} = proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = ## Converts an int to a rope. runnableExamples: - let r = rope(429) + var r = rope(429) doAssert $r == "429" - result = rope($i) proc rope*(f: BiggestFloat): Rope {.rtl, extern: "nro$1BiggestFloat".} = ## Converts a float to a rope. runnableExamples: - let r = rope(4.29) + var r = rope(4.29) doAssert $r == "4.29" - result = rope($f) proc enableCache*() {.rtl, extern: "nro$1".} = @@ -175,9 +170,12 @@ proc disableCache*() {.rtl, extern: "nro$1".} = proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = ## The concatenation operator for ropes. runnableExamples: - let r = rope("Hello, ") & rope("Nim!") + var + r1 = rope("Hello, ") + r2 = rope("Nim!") + + let r = r1 & r2 doAssert $r == "Hello, Nim!" - if a == nil: result = b elif b == nil: @@ -191,62 +189,78 @@ proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} = ## The concatenation operator for ropes. runnableExamples: - let r = rope("Hello, ") & "Nim!" + var + r1 = rope("Hello, ") + r2 = "Nim!" + + let r = r1 & r2 doAssert $r == "Hello, Nim!" - result = a & rope(b) proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} = ## The concatenation operator for ropes. runnableExamples: - let r = "Hello, " & rope("Nim!") + var + r1 = "Hello, " + r2 = rope("Nim!") + + let r = r1 & r2 doAssert $r == "Hello, Nim!" - result = rope(a) & b proc `&`*(a: openArray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} = - ## The concatenation operator for an `openArray` of ropes. + ## The concatenation operator for an openarray of ropes. runnableExamples: - let r = &[rope("Hello, "), rope("Nim"), rope("!")] + let s = @[rope("Hello, "), rope("Nim"), rope("!")] + let r = &s doAssert $r == "Hello, Nim!" - for item in a: result = result & item proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} = ## Adds `b` to the rope `a`. runnableExamples: - var r = rope("Hello, ") - r.add(rope("Nim!")) - doAssert $r == "Hello, Nim!" + var + r1 = rope("Hello, ") + r2 = rope("Nim!") + r1.add(r2) + doAssert $r1 == "Hello, Nim!" a = a & b proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} = ## Adds `b` to the rope `a`. runnableExamples: - var r = rope("Hello, ") - r.add("Nim!") - doAssert $r == "Hello, Nim!" + var + r1 = rope("Hello, ") + r2 = "Nim!" + r1.add(r2) + doAssert $r1 == "Hello, Nim!" a = a & b proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} = ## Returns the character at position `i` in the rope `r`. This is quite - ## expensive! Worst-case: O(n). If `i >= r.len or i < 0`, `\0` is returned. + ## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned. runnableExamples: - let r = rope("Hello, Nim!") + let r1 = rope("Hello, Nim!") - doAssert r[0] == 'H' - doAssert r[7] == 'N' - doAssert r[22] == '\0' + doAssert r1[0] == 'H' + doAssert r1[7] == 'N' + doAssert r1[22] == '\0' + + let r2 = rope("Hello") & rope(", Nim!") + + doAssert r2[0] == 'H' + doAssert r2[7] == 'N' + doAssert r2[22] == '\0' var x = r var j = i - if x == nil or i < 0 or i >= r.len: return + if x == nil: return while true: if x != nil and x.data.len > 0: - # leaf - return x.data[j] + if j < x.data.len: return x.data[j] + return '\0' else: if x.left.length > j: x = x.left @@ -262,7 +276,7 @@ iterator leaves*(r: Rope): string = var index = 0 for leave in r.leaves: doAssert leave == s[index] - inc(index) + inc index if r != nil: var stack = @[r] @@ -293,19 +307,16 @@ proc `$`*(r: Rope): string {.rtl, extern: "nroToString".} = result = newStringOfCap(r.len) for s in leaves(r): add(result, s) -proc `%`*(frmt: string, args: openArray[Rope]): Rope {.rtl, extern: "nroFormat".} = - ## `%` substitution operator for ropes. Does not support the `$identifier` - ## nor `${identifier}` notations. +proc `%`*(frmt: string, args: openArray[Rope]): Rope {. + rtl, extern: "nroFormat".} = + ## `%` substitution operator for ropes. Does not support the ``$identifier`` + ## nor ``${identifier}`` notations. runnableExamples: let r1 = "$1 $2 $3" % [rope("Nim"), rope("is"), rope("a great language")] doAssert $r1 == "Nim is a great language" let r2 = "$# $# $#" % [rope("Nim"), rope("is"), rope("a great language")] doAssert $r2 == "Nim is a great language" - - let r3 = "${1} ${2} ${3}" % [rope("Nim"), rope("is"), rope("a great language")] - doAssert $r3 == "Nim is a great language" - var i = 0 var length = len(frmt) result = nil @@ -346,13 +357,13 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {.rtl, extern: "nroFormat". if i - 1 >= start: add(result, substr(frmt, start, i - 1)) -proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {.rtl, extern: "nro$1".} = - ## Shortcut for `add(c, frmt % args)`. +proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {. + rtl, extern: "nro$1".} = + ## Shortcut for ``add(c, frmt % args)``. runnableExamples: var r = rope("Dash: ") r.addf "$1 $2 $3", [rope("Nim"), rope("is"), rope("a great language")] doAssert $r == "Dash: Nim is a great language" - add(c, frmt % args) when not defined(js) and not defined(nimscript): @@ -375,12 +386,14 @@ when not defined(js) and not defined(nimscript): bpos = 0 blen = readBuffer(f, addr(buf[0]), buf.len) if blen == 0: # no more data in file - return false + result = false + return let n = min(blen - bpos, slen - spos) - # TODO: There's gotta be a better way of comparing here... + # TODO There's gotta be a better way of comparing here... if not equalMem(addr(buf[bpos]), - cast[pointer](cast[int](cstring(s)) + spos), n): - return false + cast[pointer](cast[int](cstring(s))+spos), n): + result = false + return spos += n bpos += n diff --git a/lib/pure/segfaults.nim b/lib/pure/segfaults.nim index b0eac2299..2fa9a0b1c 100644 --- a/lib/pure/segfaults.nim +++ b/lib/pure/segfaults.nim @@ -13,8 +13,6 @@ ## ## Tested on these OSes: Linux, Windows, OSX -# xxx possibly broken on arm64, see bug #17178 - {.used.} # do allocate memory upfront: @@ -30,7 +28,7 @@ when defined(windows): const EXCEPTION_ACCESS_VIOLATION = DWORD(0xc0000005'i32) - EXCEPTION_CONTINUE_SEARCH = LONG(0) + EXCEPTION_CONTINUE_SEARCH = Long(0) type PEXCEPTION_RECORD = ptr object diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index b15a25a1d..20bf1b3b0 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -9,11 +9,11 @@ ## This module allows high-level and efficient I/O multiplexing. ## -## Supported OS primitives: `epoll`, `kqueue`, `poll` and -## Windows `select`. +## Supported OS primitives: ``epoll``, ``kqueue``, ``poll`` and +## Windows ``select``. ## ## To use threadsafe version of this module, it needs to be compiled -## with both `-d:threadsafe` and `--threads:on` options. +## with both ``-d:threadsafe`` and ``--threads:on`` options. ## ## Supported features: files, sockets, pipes, timers, processes, signals ## and user events. @@ -25,22 +25,18 @@ ## Solaris (files, sockets, handles and user events). ## Android (files, sockets, handles and user events). ## -## TODO: `/dev/poll`, `event ports` and filesystem events. +## TODO: ``/dev/poll``, ``event ports`` and filesystem events. -import nativesockets -import std/oserrors - -when defined(nimPreviewSlimSystem): - import std/assertions +import os, nativesockets const hasThreadSupport = compileOption("threads") and defined(threadsafe) const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or defined(netbsd) or defined(openbsd) or - defined(dragonfly) or defined(nuttx) or + defined(dragonfly) or (defined(linux) and not defined(android) and not defined(emscripten)) ## This constant is used to determine whether the destination platform is - ## fully supported by `ioselectors` module. + ## fully supported by ``ioselectors`` module. const bsdPlatform = defined(macosx) or defined(freebsd) or defined(netbsd) or defined(openbsd) or @@ -90,62 +86,62 @@ when defined(nimdoc): proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event], data: T) = - ## Registers file/socket descriptor `fd` to selector `s` - ## with events set in `events`. The `data` is application-defined + ## Registers file/socket descriptor ``fd`` to selector ``s`` + ## with events set in ``events``. The ``data`` is application-defined ## data, which will be passed when an event is triggered. proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event]) = - ## Update file/socket descriptor `fd`, registered in selector - ## `s` with new events set `event`. + ## Update file/socket descriptor ``fd``, registered in selector + ## ``s`` with new events set ``event``. proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool, data: T): int {.discardable.} = - ## Registers timer notification with `timeout` (in milliseconds) - ## to selector `s`. + ## Registers timer notification with ``timeout`` (in milliseconds) + ## to selector ``s``. ## - ## If `oneshot` is `true`, timer will be notified only once. + ## If ``oneshot`` is ``true``, timer will be notified only once. ## - ## Set `oneshot` to `false` if you want periodic notifications. + ## Set ``oneshot`` to ``false`` if you want periodic notifications. ## - ## The `data` is application-defined data, which will be passed, when + ## The ``data`` is application-defined data, which will be passed, when ## the timer is triggered. ## ## Returns the file descriptor for the registered timer. proc registerSignal*[T](s: Selector[T], signal: int, data: T): int {.discardable.} = - ## Registers Unix signal notification with `signal` to selector - ## `s`. + ## Registers Unix signal notification with ``signal`` to selector + ## ``s``. ## - ## The `data` is application-defined data, which will be + ## The ``data`` is application-defined data, which will be ## passed when signal raises. ## ## Returns the file descriptor for the registered signal. ## - ## **Note:** This function is not supported on `Windows`. + ## **Note:** This function is not supported on ``Windows``. proc registerProcess*[T](s: Selector[T], pid: int, data: T): int {.discardable.} = ## Registers a process id (pid) notification (when process has - ## exited) in selector `s`. + ## exited) in selector ``s``. ## - ## The `data` is application-defined data, which will be passed when - ## process with `pid` has exited. + ## The ``data`` is application-defined data, which will be passed when + ## process with ``pid`` has exited. ## ## Returns the file descriptor for the registered signal. proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) = - ## Registers selector event `ev` in selector `s`. + ## Registers selector event ``ev`` in selector ``s``. ## - ## The `data` is application-defined data, which will be passed when - ## `ev` happens. + ## The ``data`` is application-defined data, which will be passed when + ## ``ev`` happens. proc registerVnode*[T](s: Selector[T], fd: cint, events: set[Event], data: T) = ## Registers selector BSD/MacOSX specific vnode events for file - ## descriptor `fd` and events `events`. - ## `data` application-defined data, which to be passed, when + ## descriptor ``fd`` and events ``events``. + ## ``data`` application-defined data, which to be passed, when ## vnode event happens. ## ## **Note:** This function is supported only by BSD and MacOSX. @@ -154,77 +150,77 @@ when defined(nimdoc): ## Creates a new user-defined event. proc trigger*(ev: SelectEvent) = - ## Trigger event `ev`. + ## Trigger event ``ev``. proc close*(ev: SelectEvent) = - ## Closes user-defined event `ev`. + ## Closes user-defined event ``ev``. proc unregister*[T](s: Selector[T], ev: SelectEvent) = - ## Unregisters user-defined event `ev` from selector `s`. + ## Unregisters user-defined event ``ev`` from selector ``s``. proc unregister*[T](s: Selector[T], fd: int|SocketHandle|cint) = - ## Unregisters file/socket descriptor `fd` from selector `s`. + ## Unregisters file/socket descriptor ``fd`` from selector ``s``. proc selectInto*[T](s: Selector[T], timeout: int, results: var openArray[ReadyKey]): int = - ## Waits for events registered in selector `s`. + ## Waits for events registered in selector ``s``. ## - ## The `timeout` argument specifies the maximum number of milliseconds + ## The ``timeout`` argument specifies the maximum number of milliseconds ## the function will be blocked for if no events are ready. Specifying a - ## timeout of `-1` causes the function to block indefinitely. - ## All available events will be stored in `results` array. + ## timeout of ``-1`` causes the function to block indefinitely. + ## All available events will be stored in ``results`` array. ## ## Returns number of triggered events. proc select*[T](s: Selector[T], timeout: int): seq[ReadyKey] = - ## Waits for events registered in selector `s`. + ## Waits for events registered in selector ``s``. ## - ## The `timeout` argument specifies the maximum number of milliseconds + ## The ``timeout`` argument specifies the maximum number of milliseconds ## the function will be blocked for if no events are ready. Specifying a - ## timeout of `-1` causes the function to block indefinitely. + ## timeout of ``-1`` causes the function to block indefinitely. ## ## Returns a list of triggered events. proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = - ## Retrieves application-defined `data` associated with descriptor `fd`. - ## If specified descriptor `fd` is not registered, empty/default value + ## Retrieves application-defined ``data`` associated with descriptor ``fd``. + ## If specified descriptor ``fd`` is not registered, empty/default value ## will be returned. proc setData*[T](s: Selector[T], fd: SocketHandle|int, data: var T): bool = - ## Associate application-defined `data` with descriptor `fd`. + ## Associate application-defined ``data`` with descriptor ``fd``. ## - ## Returns `true`, if data was successfully updated, `false` otherwise. + ## Returns ``true``, if data was successfully updated, ``false`` otherwise. template isEmpty*[T](s: Selector[T]): bool = # TODO: Why is this a template? - ## Returns `true`, if there are no registered events or descriptors + ## Returns ``true``, if there are no registered events or descriptors ## in selector. template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body: untyped) = - ## Retrieves the application-data assigned with descriptor `fd` - ## to `value`. This `value` can be modified in the scope of - ## the `withData` call. + ## Retrieves the application-data assigned with descriptor ``fd`` + ## to ``value``. This ``value`` can be modified in the scope of + ## the ``withData`` call. ## ## .. code-block:: nim ## ## s.withData(fd, value) do: - ## # block is executed only if `fd` registered in selector `s` + ## # block is executed only if ``fd`` registered in selector ``s`` ## value.uid = 1000 ## template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1, body2: untyped) = - ## Retrieves the application-data assigned with descriptor `fd` - ## to `value`. This `value` can be modified in the scope of - ## the `withData` call. + ## Retrieves the application-data assigned with descriptor ``fd`` + ## to ``value``. This ``value`` can be modified in the scope of + ## the ``withData`` call. ## ## .. code-block:: nim ## ## s.withData(fd, value) do: - ## # block is executed only if `fd` registered in selector `s`. + ## # block is executed only if ``fd`` registered in selector ``s``. ## value.uid = 1000 ## do: - ## # block is executed if `fd` not registered in selector `s`. + ## # block is executed if ``fd`` not registered in selector ``s``. ## raise ## @@ -234,7 +230,7 @@ when defined(nimdoc): proc getFd*[T](s: Selector[T]): int = ## Retrieves the underlying selector's file descriptor. ## - ## For *poll* and *select* selectors `-1` is returned. + ## For *poll* and *select* selectors ``-1`` is returned. else: import strutils @@ -325,24 +321,7 @@ else: proc verifySelectParams(timeout: int) = # Timeout of -1 means: wait forever # Anything higher is the time to wait in milliseconds. - doAssert(timeout >= -1, "Cannot select with a negative value, got: " & $timeout) - - when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or - defined(solaris) or defined(zephyr) or defined(freertos) or defined(nuttx): - template maxDescriptors*(): int = - ## Returns the maximum number of active file descriptors for the current - ## process. This involves a system call. For now `maxDescriptors` is - ## supported on the following OSes: Windows, Linux, OSX, BSD, Solaris. - when defined(windows): - 16_700_000 - elif defined(zephyr) or defined(freertos): - FD_MAX - else: - var fdLim: RLimit - var res = int(getrlimit(RLIMIT_NOFILE, fdLim)) - if res >= 0: - res = int(fdLim.rlim_cur) - 1 - res + doAssert(timeout >= -1, "Cannot select with a negative value, got " & $timeout) when defined(linux) and not defined(emscripten): include ioselects/ioselectors_epoll @@ -358,9 +337,5 @@ else: include ioselects/ioselectors_select elif defined(freertos) or defined(lwip): include ioselects/ioselectors_select - elif defined(zephyr): - include ioselects/ioselectors_poll - elif defined(nuttx): - include ioselects/ioselectors_epoll else: include ioselects/ioselectors_poll diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim new file mode 100644 index 000000000..66d181d40 --- /dev/null +++ b/lib/pure/smtp.nim @@ -0,0 +1,343 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the SMTP client protocol as specified by RFC 5321, +## this can be used to send mail to any SMTP Server. +## +## This module also implements the protocol used to format messages, +## as specified by RFC 2822. +## +## Example gmail use: +## +## +## .. code-block:: Nim +## var msg = createMessage("Hello from Nim's SMTP", +## "Hello!.\n Is this awesome or what?", +## @["foo@gmail.com"]) +## let smtpConn = newSmtp(useSsl = true, debug=true) +## smtpConn.connect("smtp.gmail.com", Port 465) +## smtpConn.auth("username", "password") +## smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg) +## +## +## Example for startTls use: +## +## +## .. code-block:: Nim +## var msg = createMessage("Hello from Nim's SMTP", +## "Hello!.\n Is this awesome or what?", +## @["foo@gmail.com"]) +## let smtpConn = newSmtp(debug=true) +## smtpConn.connect("smtp.mailtrap.io", Port 2525) +## smtpConn.startTls() +## smtpConn.auth("username", "password") +## smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg) +## +## +## For SSL support this module relies on OpenSSL. If you want to +## enable SSL, compile with ``-d:ssl``. + +import net, strutils, strtabs, base64, os, strutils +import asyncnet, asyncdispatch + +export Port + +type + Message* = object + msgTo: seq[string] + msgCc: seq[string] + msgSubject: string + msgOtherHeaders: StringTableRef + msgBody: string + + ReplyError* = object of IOError + + SmtpBase[SocketType] = ref object + sock: SocketType + address: string + debug: bool + + Smtp* = SmtpBase[Socket] + AsyncSmtp* = SmtpBase[AsyncSocket] + +proc containsNewline(xs: seq[string]): bool = + for x in xs: + if x.contains({'\c', '\L'}): + return true + +proc debugSend*(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} = + ## Sends ``cmd`` on the socket connected to the SMTP server. + ## + ## If the ``smtp`` object was created with ``debug`` enabled, + ## debugSend will invoke ``echo("C:" & cmd)`` before sending. + ## + ## This is a lower level proc and not something that you typically + ## would need to call when using this module. One exception to + ## this is if you are implementing any + ## `SMTP extensions`_. + + if smtp.debug: + echo("C:" & cmd) + await smtp.sock.send(cmd) + +proc debugRecv*(smtp: Smtp | AsyncSmtp): Future[TaintedString] {.multisync.} = + ## Receives a line of data from the socket connected to the + ## SMTP server. + ## + ## If the ``smtp`` object was created with ``debug`` enabled, + ## debugRecv will invoke ``echo("S:" & result.string)`` after + ## the data is received. + ## + ## This is a lower level proc and not something that you typically + ## would need to call when using this module. One exception to + ## this is if you are implementing any + ## `SMTP extensions`_. + ## + ## See `checkReply(reply)<#checkReply,AsyncSmtp,string>`_. + + result = await smtp.sock.recvLine() + if smtp.debug: + echo("S:" & result.string) + +proc quitExcpt(smtp: Smtp, msg: string) = + smtp.debugSend("QUIT") + raise newException(ReplyError, msg) + +const compiledWithSsl = defined(ssl) + +when not defined(ssl): + let defaultSSLContext: SslContext = nil +else: + var defaultSSLContext {.threadvar.}: SslContext + + proc getSSLContext(): SslContext = + if defaultSSLContext == nil: + defaultSSLContext = newContext(verifyMode = CVerifyNone) + result = defaultSSLContext + +proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], + otherHeaders: openArray[tuple[name, value: string]]): Message = + ## Creates a new MIME compliant message. + ## + ## You need to make sure that ``mSubject``, ``mTo`` and ``mCc`` don't contain + ## any newline characters. Failing to do so will raise ``AssertionDefect``. + doAssert(not mSubject.contains({'\c', '\L'}), + "'mSubject' shouldn't contain any newline characters") + doAssert(not (mTo.containsNewline() or mCc.containsNewline()), + "'mTo' and 'mCc' shouldn't contain any newline characters") + + result.msgTo = mTo + result.msgCc = mCc + result.msgSubject = mSubject + result.msgBody = mBody + result.msgOtherHeaders = newStringTable() + for n, v in items(otherHeaders): + result.msgOtherHeaders[n] = v + +proc createMessage*(mSubject, mBody: string, mTo, + mCc: seq[string] = @[]): Message = + ## Alternate version of the above. + ## + ## You need to make sure that ``mSubject``, ``mTo`` and ``mCc`` don't contain + ## any newline characters. Failing to do so will raise ``AssertionDefect``. + doAssert(not mSubject.contains({'\c', '\L'}), + "'mSubject' shouldn't contain any newline characters") + doAssert(not (mTo.containsNewline() or mCc.containsNewline()), + "'mTo' and 'mCc' shouldn't contain any newline characters") + result.msgTo = mTo + result.msgCc = mCc + result.msgSubject = mSubject + result.msgBody = mBody + result.msgOtherHeaders = newStringTable() + +proc `$`*(msg: Message): string = + ## stringify for ``Message``. + result = "" + if msg.msgTo.len() > 0: + result = "TO: " & msg.msgTo.join(", ") & "\c\L" + if msg.msgCc.len() > 0: + result.add("CC: " & msg.msgCc.join(", ") & "\c\L") + # TODO: Folding? i.e when a line is too long, shorten it... + result.add("Subject: " & msg.msgSubject & "\c\L") + for key, value in pairs(msg.msgOtherHeaders): + result.add(key & ": " & value & "\c\L") + + result.add("\c\L") + result.add(msg.msgBody) + +proc newSmtp*(useSsl = false, debug = false, + sslContext: SslContext = nil): Smtp = + ## Creates a new ``Smtp`` instance. + new result + result.debug = debug + result.sock = newSocket() + if useSsl: + when compiledWithSsl: + if sslContext == nil: + getSSLContext().wrapSocket(result.sock) + else: + sslContext.wrapSocket(result.sock) + else: + {.error: "SMTP module compiled without SSL support".} + +proc newAsyncSmtp*(useSsl = false, debug = false, + sslContext: SslContext = nil): AsyncSmtp = + ## Creates a new ``AsyncSmtp`` instance. + new result + result.debug = debug + + result.sock = newAsyncSocket() + if useSsl: + when compiledWithSsl: + if sslContext == nil: + getSSLContext().wrapSocket(result.sock) + else: + sslContext.wrapSocket(result.sock) + else: + {.error: "SMTP module compiled without SSL support".} + +proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] = + var retFuture = newFuture[void]() + var sendFut = smtp.debugSend("QUIT") + sendFut.callback = + proc () = + retFuture.fail(newException(ReplyError, msg)) + return retFuture + +proc checkReply*(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} = + ## Calls `debugRecv<#debugRecv,AsyncSmtp>`_ and checks that the received + ## data starts with ``reply``. If the received data does not start + ## with ``reply``, then a ``QUIT`` command will be sent to the SMTP + ## server and a ``ReplyError`` exception will be raised. + ## + ## This is a lower level proc and not something that you typically + ## would need to call when using this module. One exception to + ## this is if you are implementing any + ## `SMTP extensions`_. + + var line = await smtp.debugRecv() + if not line.startsWith(reply): + await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line) + +proc helo*(smtp: Smtp | AsyncSmtp) {.multisync.} = + # Sends the HELO request + await smtp.debugSend("HELO " & smtp.address & "\c\L") + await smtp.checkReply("250") + +proc connect*(smtp: Smtp | AsyncSmtp, + address: string, port: Port) {.multisync.} = + ## Establishes a connection with a SMTP server. + ## May fail with ReplyError or with a socket error. + smtp.address = address + await smtp.sock.connect(address, port) + await smtp.checkReply("220") + await smtp.helo() + +proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync.} = + ## Put the SMTP connection in TLS (Transport Layer Security) mode. + ## May fail with ReplyError + await smtp.debugSend("STARTTLS\c\L") + await smtp.checkReply("220") + when compiledWithSsl: + if sslContext == nil: + getSSLContext().wrapConnectedSocket(smtp.sock, handshakeAsClient) + else: + sslContext.wrapConnectedSocket(smtp.sock, handshakeAsClient) + await smtp.helo() + else: + {.error: "SMTP module compiled without SSL support".} + +proc auth*(smtp: Smtp | AsyncSmtp, username, password: string) {.multisync.} = + ## Sends an AUTH command to the server to login as the `username` + ## using `password`. + ## May fail with ReplyError. + + await smtp.debugSend("AUTH LOGIN\c\L") + await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:" + # i.e "334 VXNlcm5hbWU6" + await smtp.debugSend(encode(username) & "\c\L") + await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?) + + await smtp.debugSend(encode(password) & "\c\L") + await smtp.checkReply("235") # Check whether the authentication was successful. + +proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string, + toAddrs: seq[string], msg: string) {.multisync.} = + ## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``. + ## Messages may be formed using ``createMessage`` by converting the + ## Message into a string. + ## + ## You need to make sure that ``fromAddr`` and ``toAddrs`` don't contain + ## any newline characters. Failing to do so will raise ``AssertionDefect``. + doAssert(not (toAddrs.containsNewline() or fromAddr.contains({'\c', '\L'})), + "'toAddrs' and 'fromAddr' shouldn't contain any newline characters") + + await smtp.debugSend("MAIL FROM:<" & fromAddr & ">\c\L") + await smtp.checkReply("250") + for address in items(toAddrs): + await smtp.debugSend("RCPT TO:<" & address & ">\c\L") + await smtp.checkReply("250") + + # Send the message + await smtp.debugSend("DATA " & "\c\L") + await smtp.checkReply("354") + await smtp.sock.send(msg & "\c\L") + await smtp.debugSend(".\c\L") + await smtp.checkReply("250") + +proc close*(smtp: Smtp | AsyncSmtp) {.multisync.} = + ## Disconnects from the SMTP server and closes the socket. + await smtp.debugSend("QUIT\c\L") + smtp.sock.close() + +when not defined(testing) and isMainModule: + # To test with a real SMTP service, create a smtp.ini file, e.g.: + # username = "" + # password = "" + # smtphost = "smtp.gmail.com" + # port = 465 + # use_tls = true + # sender = "" + # recipient = "" + + import parsecfg + + proc `[]`(c: Config, key: string): string = c.getSectionValue("", key) + + let + conf = loadConfig("smtp.ini") + msg = createMessage("Hello from Nim's SMTP!", + "Hello!\n Is this awesome or what?", @[conf["recipient"]]) + + assert conf["smtphost"] != "" + + proc async_test() {.async.} = + let client = newAsyncSmtp( + conf["use_tls"].parseBool, + debug = true + ) + await client.connect(conf["smtphost"], conf["port"].parseInt.Port) + await client.auth(conf["username"], conf["password"]) + await client.sendMail(conf["sender"], @[conf["recipient"]], $msg) + await client.close() + echo "async email sent" + + proc sync_test() = + var smtpConn = newSmtp( + conf["use_tls"].parseBool, + debug = true + ) + smtpConn.connect(conf["smtphost"], conf["port"].parseInt.Port) + smtpConn.auth(conf["username"], conf["password"]) + smtpConn.sendMail(conf["sender"], @[conf["recipient"]], $msg) + smtpConn.close() + echo "sync email sent" + + waitFor async_test() + sync_test() diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim index dd09848be..c1003f445 100644 --- a/lib/pure/ssl_certs.nim +++ b/lib/pure/ssl_certs.nim @@ -11,49 +11,16 @@ ## SSL_CERT_DIR environment variables. import os, strutils +from os import existsEnv, getEnv +import strutils + +# SECURITY: this unnecessarily scans through dirs/files regardless of the +# actual host OS/distribution. Hopefully all the paths are writeble only by +# root. # FWIW look for files before scanning entire dirs. -when defined(macosx): - const certificatePaths = [ - "/etc/ssl/cert.pem", - "/System/Library/OpenSSL/certs/cert.pem" - ] -elif defined(linux): - const certificatePaths = [ - # Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo - # NetBSD (security/mozilla-rootcerts) - # SLES10/SLES11, https://golang.org/issue/12139 - "/etc/ssl/certs/ca-certificates.crt", - # OpenSUSE - "/etc/ssl/ca-bundle.pem", - # Red Hat 5+, Fedora, Centos - "/etc/pki/tls/certs/ca-bundle.crt", - # Red Hat 4 - "/usr/share/ssl/certs/ca-bundle.crt", - # Fedora/RHEL - "/etc/pki/tls/certs", - # Android - "/data/data/com.termux/files/usr/etc/tls/cert.pem", - "/system/etc/security/cacerts", - ] -elif defined(bsd): - const certificatePaths = [ - # Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo - # NetBSD (security/mozilla-rootcerts) - # SLES10/SLES11, https://golang.org/issue/12139 - "/etc/ssl/certs/ca-certificates.crt", - # FreeBSD (security/ca-root-nss package) - "/usr/local/share/certs/ca-root-nss.crt", - # OpenBSD, FreeBSD (optional symlink) - "/etc/ssl/cert.pem", - # FreeBSD - "/usr/local/share/certs", - # NetBSD - "/etc/openssl/certs", - ] -else: - const certificatePaths = [ +const certificatePaths = [ # Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo # NetBSD (security/mozilla-rootcerts) # SLES10/SLES11, https://golang.org/issue/12139 @@ -70,6 +37,8 @@ else: "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", # OpenBSD, FreeBSD (optional symlink) "/etc/ssl/cert.pem", + # Mac OS X + "/System/Library/OpenSSL/certs/cert.pem", # Fedora/RHEL "/etc/pki/tls/certs", # Android @@ -78,7 +47,7 @@ else: "/usr/local/share/certs", # NetBSD "/etc/openssl/certs", - ] +] when defined(haiku): const @@ -88,7 +57,7 @@ when defined(haiku): proc find_paths_etc(architecture: cstring, baseDirectory: cint, subPath: cstring, flags: uint32, paths: var ptr UncheckedArray[cstring], - pathCount: var csize_t): int32 + pathCount: var csize): int32 {.importc, header: "".} proc free(p: pointer) {.importc, header: "".} @@ -98,46 +67,27 @@ iterator scanSSLCertificates*(useEnvVars = false): string = ## if `useEnvVars` is true, the SSL_CERT_FILE and SSL_CERT_DIR ## environment variables can be used to override the certificate ## directories to scan or specify a CA certificate file. - if useEnvVars and existsEnv("SSL_CERT_FILE"): + if existsEnv("SSL_CERT_FILE"): yield getEnv("SSL_CERT_FILE") - elif useEnvVars and existsEnv("SSL_CERT_DIR"): + elif existsEnv("SSL_CERT_DIR"): let p = getEnv("SSL_CERT_DIR") for fn in joinPath(p, "*").walkFiles(): yield fn else: - when defined(windows): - const cacert = "cacert.pem" - let pem = getAppDir() / cacert - if fileExists(pem): - yield pem - else: - let path = getEnv("PATH") - for candidate in split(path, PathSep): - if candidate.len != 0: - let x = (if candidate[0] == '"' and candidate[^1] == '"': - substr(candidate, 1, candidate.len-2) else: candidate) / cacert - if fileExists(x): - yield x - elif not defined(haiku): + when not defined(haiku): for p in certificatePaths: if p.endsWith(".pem") or p.endsWith(".crt"): 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_t + size: csize 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 7f797529d..af6cffef2 100644 --- a/lib/pure/stats.nim +++ b/lib/pure/stats.nim @@ -9,77 +9,67 @@ ## Statistical analysis framework for performing ## basic statistical analysis of data. -## The data is analysed in a single pass, when it -## is pushed to a `RunningStat` or `RunningRegress` object. +## The data is analysed in a single pass, when a data value +## is pushed to the ``RunningStat`` or ``RunningRegress`` objects ## -## `RunningStat` calculates for a single data set +## ``RunningStat`` calculates for a single data set ## - n (data count) -## - min (smallest value) -## - max (largest value) +## - min (smallest value) +## - max (largest value) ## - sum ## - mean ## - variance -## - varianceS (sample variance) +## - varianceS (sample var) ## - standardDeviation -## - standardDeviationS (sample standard deviation) +## - standardDeviationS (sample stddev) ## - skewness (the third statistical moment) ## - kurtosis (the fourth statistical moment) ## -## `RunningRegress` calculates for two sets of data -## - n (data count) +## ``RunningRegress`` calculates for two sets of data +## - n ## - slope ## - intercept ## - correlation ## -## Procs are provided to calculate statistics on `openArray`s. +## Procs have been provided to calculate statistics on arrays and sequences. ## ## However, if more than a single statistical calculation is required, it is more -## efficient to push the data once to a `RunningStat` object and then -## call the numerous statistical procs for the `RunningStat` object: - -runnableExamples: - from std/math import almostEqual - - template `~=`(a, b: float): bool = almostEqual(a, b) - - var statistics: RunningStat # must be var - statistics.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0]) - doAssert statistics.n == 8 - doAssert statistics.mean() ~= 2.0 - doAssert statistics.variance() ~= 1.5 - doAssert statistics.varianceS() ~= 1.714285714285715 - doAssert statistics.skewness() ~= 0.8164965809277261 - doAssert statistics.skewnessS() ~= 1.018350154434631 - doAssert statistics.kurtosis() ~= -1.0 - doAssert statistics.kurtosisS() ~= -0.7000000000000008 +## efficient to push the data once to the RunningStat object, and +## call the numerous statistical procs for the RunningStat object. +## +## .. code-block:: Nim +## +## var rs: RunningStat +## rs.push(MySeqOfData) +## rs.mean() +## rs.variance() +## rs.skewness() +## rs.kurtosis() 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.} type - RunningStat* = object ## An accumulator for statistical data. - n*: int ## amount of pushed data + RunningStat* = object ## an accumulator for statistical data + n*: int ## number of pushed data min*, max*, sum*: float ## self-explaining mom1, mom2, mom3, mom4: float ## statistical moments, mom1 is mean - RunningRegress* = object ## An accumulator for regression calculations. - n*: int ## amount of pushed data - x_stats*: RunningStat ## stats for the first set of data - y_stats*: RunningStat ## stats for the second set of data + + RunningRegress* = object ## an accumulator for regression calculations + n*: int ## number of pushed data + x_stats*: RunningStat ## stats for first set of data + y_stats*: RunningStat ## stats for second set of data s_xy: float ## accumulated data for combined xy # ----------- RunningStat -------------------------- - proc clear*(s: var RunningStat) = - ## Resets `s`. + ## reset `s` s.n = 0 - s.min = 0.0 + s.min = toBiggestFloat(int.high) s.max = 0.0 s.sum = 0.0 s.mom1 = 0.0 @@ -88,15 +78,12 @@ proc clear*(s: var RunningStat) = s.mom4 = 0.0 proc push*(s: var RunningStat, x: float) = - ## Pushes a value `x` for processing. - if s.n == 0: - s.min = x - s.max = x - else: - if s.min > x: s.min = x - if s.max < x: s.max = x + ## pushes a value `x` for processing + if s.n == 0: s.min = x inc(s.n) # See Knuth TAOCP vol 2, 3rd edition, page 232 + if s.min > x: s.min = x + if s.max < x: s.max = x s.sum += x let n = toFloat(s.n) let delta = x - s.mom1 @@ -110,63 +97,63 @@ proc push*(s: var RunningStat, x: float) = s.mom1 += delta_n proc push*(s: var RunningStat, x: int) = - ## Pushes a value `x` for processing. + ## pushes a value `x` for processing. ## - ## `x` is simply converted to `float` + ## `x` is simply converted to ``float`` ## and the other push operation is called. s.push(toFloat(x)) proc push*(s: var RunningStat, x: openArray[float|int]) = - ## Pushes all values of `x` for processing. + ## pushes all values of `x` for processing. ## - ## Int values of `x` are simply converted to `float` and + ## Int values of `x` are simply converted to ``float`` and ## the other push operation is called. for val in x: s.push(val) proc mean*(s: RunningStat): float = - ## Computes the current mean of `s`. + ## computes the current mean of `s` result = s.mom1 proc variance*(s: RunningStat): float = - ## Computes the current population variance of `s`. + ## computes the current population variance of `s` result = s.mom2 / toFloat(s.n) proc varianceS*(s: RunningStat): float = - ## Computes the current sample variance of `s`. + ## computes the current sample variance of `s` if s.n > 1: result = s.mom2 / toFloat(s.n - 1) proc standardDeviation*(s: RunningStat): float = - ## Computes the current population standard deviation of `s`. + ## computes the current population standard deviation of `s` result = sqrt(variance(s)) proc standardDeviationS*(s: RunningStat): float = - ## Computes the current sample standard deviation of `s`. + ## computes the current sample standard deviation of `s` result = sqrt(varianceS(s)) proc skewness*(s: RunningStat): float = - ## Computes the current population skewness of `s`. + ## computes the current population skewness of `s` result = sqrt(toFloat(s.n)) * s.mom3 / pow(s.mom2, 1.5) proc skewnessS*(s: RunningStat): float = - ## Computes the current sample skewness of `s`. + ## computes the current sample skewness of `s` let s2 = skewness(s) result = sqrt(toFloat(s.n*(s.n-1)))*s2 / toFloat(s.n-2) proc kurtosis*(s: RunningStat): float = - ## Computes the current population kurtosis of `s`. + ## computes the current population kurtosis of `s` result = toFloat(s.n) * s.mom4 / (s.mom2 * s.mom2) - 3.0 proc kurtosisS*(s: RunningStat): float = - ## Computes the current sample kurtosis of `s`. + ## computes the current sample kurtosis of `s` result = toFloat(s.n-1) / toFloat((s.n-2)*(s.n-3)) * (toFloat(s.n+1)*kurtosis(s) + 6) proc `+`*(a, b: RunningStat): RunningStat = - ## Combines two `RunningStat`s. + ## combine two RunningStats. ## - ## Useful when performing parallel analysis of data series - ## and needing to re-combine parallel result sets. + ## Useful if performing parallel analysis of data series + ## and need to re-combine parallel result sets result.clear() result.n = a.n + b.n @@ -191,11 +178,11 @@ proc `+`*(a, b: RunningStat): RunningStat = result.min = min(a.min, b.min) proc `+=`*(a: var RunningStat, b: RunningStat) {.inline.} = - ## Adds the `RunningStat` `b` to `a`. + ## add a second RunningStats `b` to `a` a = a + b proc `$`*(a: RunningStat): string = - ## Produces a string representation of the `RunningStat`. The exact + ## produces a string representation of the ``RunningStat``. The exact ## format is currently unspecified and subject to change. Currently ## it contains: ## @@ -212,57 +199,56 @@ proc `$`*(a: RunningStat): string = result.add ")" # ---------------------- standalone array/seq stats --------------------- - proc mean*[T](x: openArray[T]): float = - ## Computes the mean of `x`. + ## computes the mean of `x` var rs: RunningStat rs.push(x) result = rs.mean() proc variance*[T](x: openArray[T]): float = - ## Computes the population variance of `x`. + ## computes the population variance of `x` var rs: RunningStat rs.push(x) result = rs.variance() proc varianceS*[T](x: openArray[T]): float = - ## Computes the sample variance of `x`. + ## computes the sample variance of `x` var rs: RunningStat rs.push(x) result = rs.varianceS() proc standardDeviation*[T](x: openArray[T]): float = - ## Computes the population standard deviation of `x`. + ## computes the population standardDeviation of `x` var rs: RunningStat rs.push(x) result = rs.standardDeviation() proc standardDeviationS*[T](x: openArray[T]): float = - ## Computes the sample standard deviation of `x`. + ## computes the sample standardDeviation of `x` var rs: RunningStat rs.push(x) result = rs.standardDeviationS() proc skewness*[T](x: openArray[T]): float = - ## Computes the population skewness of `x`. + ## computes the population skewness of `x` var rs: RunningStat rs.push(x) result = rs.skewness() proc skewnessS*[T](x: openArray[T]): float = - ## Computes the sample skewness of `x`. + ## computes the sample skewness of `x` var rs: RunningStat rs.push(x) result = rs.skewnessS() proc kurtosis*[T](x: openArray[T]): float = - ## Computes the population kurtosis of `x`. + ## computes the population kurtosis of `x` var rs: RunningStat rs.push(x) result = rs.kurtosis() proc kurtosisS*[T](x: openArray[T]): float = - ## Computes the sample kurtosis of `x`. + ## computes the sample kurtosis of `x` var rs: RunningStat rs.push(x) result = rs.kurtosisS() @@ -270,14 +256,14 @@ proc kurtosisS*[T](x: openArray[T]): float = # ---------------------- Running Regression ----------------------------- proc clear*(r: var RunningRegress) = - ## Resets `r`. + ## reset `r` r.x_stats.clear() r.y_stats.clear() r.s_xy = 0.0 r.n = 0 proc push*(r: var RunningRegress, x, y: float) = - ## Pushes two values `x` and `y` for processing. + ## pushes two values `x` and `y` for processing r.s_xy += (r.x_stats.mean() - x)*(r.y_stats.mean() - y) * toFloat(r.n) / toFloat(r.n + 1) r.x_stats.push(x) @@ -285,38 +271,38 @@ proc push*(r: var RunningRegress, x, y: float) = inc(r.n) proc push*(r: var RunningRegress, x, y: int) {.inline.} = - ## Pushes two values `x` and `y` for processing. + ## pushes two values `x` and `y` for processing. ## - ## `x` and `y` are converted to `float` + ## `x` and `y` are converted to ``float`` ## and the other push operation is called. r.push(toFloat(x), toFloat(y)) proc push*(r: var RunningRegress, x, y: openArray[float|int]) = - ## Pushes two sets of values `x` and `y` for processing. + ## pushes two sets of values `x` and `y` for processing. assert(x.len == y.len) for i in 0..`_ -## * `io module `_ for `FileMode enum `_ +## * `io module `_ for `FileMode enum `_ import std/private/since -when defined(nimPreviewSlimSystem): - import std/syncio - export FileMode +const taintMode = compileOption("taintmode") proc newEIO(msg: string): owned(ref IOError) = new(result) @@ -115,7 +113,7 @@ type ## * That these fields here shouldn't be used directly. ## They are accessible so that a stream implementation can override them. closeImpl*: proc (s: Stream) - {.nimcall, raises: [IOError, OSError], tags: [WriteIOEffect], gcsafe.} + {.nimcall, raises: [Exception, IOError, OSError], tags: [WriteIOEffect], gcsafe.} atEndImpl*: proc (s: Stream): bool {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.} setPositionImpl*: proc (s: Stream, pos: int) @@ -126,7 +124,7 @@ type readDataStrImpl*: proc (s: Stream, buffer: var string, slice: Slice[int]): int {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.} - readLineImpl*: proc(s: Stream, line: var string): bool + readLineImpl*: proc(s: Stream, line: var TaintedString): bool {.nimcall, raises: [Defect, IOError, OSError], tags: [ReadIOEffect], gcsafe.} readDataImpl*: proc (s: Stream, buffer: pointer, bufLen: int): int @@ -148,7 +146,7 @@ proc flush*(s: Stream) = ## See also: ## * `close proc <#close,Stream>`_ runnableExamples: - from std/os import removeFile + from os import removeFile var strm = newFileStream("somefile.txt", fmWrite) @@ -174,20 +172,10 @@ proc close*(s: Stream) = ## See also: ## * `flush proc <#flush,Stream>`_ runnableExamples: - block: - let strm = newStringStream("The first line\nthe second line\nthe third line") - ## do something... - strm.close() - - block: - let strm = newFileStream("amissingfile.txt") - # deferring works even if newFileStream fails - defer: strm.close() - if not isNil(strm): - ## do something... - - if not isNil(s) and not isNil(s.closeImpl): - s.closeImpl(s) + var strm = newStringStream("The first line\nthe second line\nthe third line") + ## do something... + strm.close() + if not isNil(s.closeImpl): s.closeImpl(s) proc atEnd*(s: Stream): bool = ## Checks if more data can be read from `s`. Returns ``true`` if all data has @@ -228,7 +216,7 @@ proc getPosition*(s: Stream): int = proc readData*(s: Stream, buffer: pointer, bufLen: int): int = ## Low level proc that reads data into an untyped `buffer` of `bufLen` size. - ## + ## ## **JS note:** `buffer` is treated as a ``ptr string`` and written to between ## ``0..= (1, 3) or not defined(js): proc peekData*(s: Stream, buffer: pointer, bufLen: int): int = ## Low level proc that reads data into an untyped `buffer` of `bufLen` size ## without moving stream position. - ## + ## ## **JS note:** `buffer` is treated as a ``ptr string`` and written to between ## ``0.. len(str): setLen(str, length) +template untaint(s: var TaintedString): var string = + when taintMode: # for VM, bug #12282 + s.string + else: + s + +proc readStrPrivate(s: Stream, length: int, str: var TaintedString) = + if length > len(str): setLen(str.untaint, length) when defined(js): let L = readData(s, addr(str), length) else: - let L = readData(s, cstring(str), length) - if L != len(str): setLen(str, L) + let L = readData(s, cstring(str.string), length) + if L != len(str): setLen(str.untaint, L) -proc readStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} = +proc readStr*(s: Stream, length: int, str: var TaintedString) {.since: (1, 3).} = ## Reads a string of length `length` from the stream `s`. Raises `IOError` if ## an error occurred. readStrPrivate(s, length, str) -proc readStr*(s: Stream, length: int): string = +proc readStr*(s: Stream, length: int): TaintedString = ## Reads a string of length `length` from the stream `s`. Raises `IOError` if ## an error occurred. runnableExamples: @@ -959,23 +950,23 @@ proc readStr*(s: Stream, length: int): string = doAssert strm.readStr(2) == "e" doAssert strm.readStr(2) == "" strm.close() - result = newString(length) + result = newString(length).TaintedString readStrPrivate(s, length, result) -proc peekStrPrivate(s: Stream, length: int, str: var string) = - if length > len(str): setLen(str, length) +proc peekStrPrivate(s: Stream, length: int, str: var TaintedString) = + if length > len(str): setLen(str.untaint, length) when defined(js): let L = peekData(s, addr(str), length) else: - let L = peekData(s, cstring(str), length) - if L != len(str): setLen(str, L) + let L = peekData(s, cstring(str.string), length) + if L != len(str): setLen(str.untaint, L) -proc peekStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} = +proc peekStr*(s: Stream, length: int, str: var TaintedString) {.since: (1, 3).} = ## Peeks a string of length `length` from the stream `s`. Raises `IOError` if ## an error occurred. peekStrPrivate(s, length, str) -proc peekStr*(s: Stream, length: int): string = +proc peekStr*(s: Stream, length: int): TaintedString = ## Peeks a string of length `length` from the stream `s`. Raises `IOError` if ## an error occurred. runnableExamples: @@ -986,10 +977,10 @@ proc peekStr*(s: Stream, length: int): string = doAssert strm.readStr(2) == "ab" doAssert strm.peekStr(2) == "cd" strm.close() - result = newString(length) + result = newString(length).TaintedString peekStrPrivate(s, length, result) -proc readLine*(s: Stream, line: var string): bool = +proc readLine*(s: Stream, line: var TaintedString): bool = ## Reads a line of text from the stream `s` into `line`. `line` must not be ## ``nil``! May throw an IO exception. ## @@ -1001,7 +992,7 @@ proc readLine*(s: Stream, line: var string): bool = ## See also: ## * `readLine(Stream) proc <#readLine,Stream>`_ ## * `peekLine(Stream) proc <#peekLine,Stream>`_ - ## * `peekLine(Stream, string) proc <#peekLine,Stream,string>`_ + ## * `peekLine(Stream, TaintedString) proc <#peekLine,Stream,TaintedString>`_ runnableExamples: var strm = newStringStream("The first line\nthe second line\nthe third line") var line = "" @@ -1019,7 +1010,7 @@ proc readLine*(s: Stream, line: var string): bool = result = s.readLineImpl(s, line) else: # fallback - line.setLen(0) + line.untaint.setLen(0) while true: var c = readChar(s) if c == '\c': @@ -1029,10 +1020,10 @@ proc readLine*(s: Stream, line: var string): bool = elif c == '\0': if line.len > 0: break else: return false - line.add(c) + line.untaint.add(c) result = true -proc peekLine*(s: Stream, line: var string): bool = +proc peekLine*(s: Stream, line: var TaintedString): bool = ## Peeks a line of text from the stream `s` into `line`. `line` must not be ## ``nil``! May throw an IO exception. ## @@ -1043,7 +1034,7 @@ proc peekLine*(s: Stream, line: var string): bool = ## ## See also: ## * `readLine(Stream) proc <#readLine,Stream>`_ - ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_ + ## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_ ## * `peekLine(Stream) proc <#peekLine,Stream>`_ runnableExamples: var strm = newStringStream("The first line\nthe second line\nthe third line") @@ -1063,15 +1054,15 @@ proc peekLine*(s: Stream, line: var string): bool = defer: setPosition(s, pos) result = readLine(s, line) -proc readLine*(s: Stream): string = +proc readLine*(s: Stream): TaintedString = ## Reads a line from a stream `s`. Raises `IOError` if an error occurred. ## ## **Note:** This is not very efficient. ## ## See also: - ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_ + ## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_ ## * `peekLine(Stream) proc <#peekLine,Stream>`_ - ## * `peekLine(Stream, string) proc <#peekLine,Stream,string>`_ + ## * `peekLine(Stream, TaintedString) proc <#peekLine,Stream,TaintedString>`_ runnableExamples: var strm = newStringStream("The first line\nthe second line\nthe third line") doAssert strm.readLine() == "The first line" @@ -1080,7 +1071,7 @@ proc readLine*(s: Stream): string = doAssertRaises(IOError): discard strm.readLine() strm.close() - result = "" + result = TaintedString"" if s.atEnd: raise newEIO("cannot read from stream") while true: @@ -1091,17 +1082,17 @@ proc readLine*(s: Stream): string = if c == '\L' or c == '\0': break else: - result.add(c) + result.untaint.add(c) -proc peekLine*(s: Stream): string = +proc peekLine*(s: Stream): TaintedString = ## Peeks a line from a stream `s`. Raises `IOError` if an error occurred. ## ## **Note:** This is not very efficient. ## ## See also: ## * `readLine(Stream) proc <#readLine,Stream>`_ - ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_ - ## * `peekLine(Stream, string) proc <#peekLine,Stream,string>`_ + ## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_ + ## * `peekLine(Stream, TaintedString) proc <#peekLine,Stream,TaintedString>`_ runnableExamples: var strm = newStringStream("The first line\nthe second line\nthe third line") doAssert strm.peekLine() == "The first line" @@ -1115,13 +1106,13 @@ proc peekLine*(s: Stream): string = defer: setPosition(s, pos) result = readLine(s) -iterator lines*(s: Stream): string = +iterator lines*(s: Stream): TaintedString = ## Iterates over every line in the stream. ## The iteration is based on ``readLine``. ## ## See also: ## * `readLine(Stream) proc <#readLine,Stream>`_ - ## * `readLine(Stream, string) proc <#readLine,Stream,string>`_ + ## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_ runnableExamples: var strm = newStringStream("The first line\nthe second line\nthe third line") var lines: seq[string] @@ -1130,7 +1121,7 @@ iterator lines*(s: Stream): string = doAssert lines == @["The first line", "the second line", "the third line"] strm.close() - var line: string + var line: TaintedString while s.readLine(line): yield line @@ -1167,7 +1158,10 @@ when (NimMajor, NimMinor) < (1, 3) and defined(js): proc ssClose(s: Stream) {.compileTime.} = var s = StringStream(s) - s.data = "" + when defined(nimNoNilSeqs): + s.data = "" + else: + s.data = nil proc newStringStream*(s: string = ""): owned StringStream {.compileTime.} = new(result) @@ -1193,7 +1187,7 @@ when (NimMajor, NimMinor) < (1, 3) and defined(js): if readBytes < bufferSize: break -else: # after 1.3 or JS not defined +else: # after 1.3 or JS not defined proc ssAtEnd(s: Stream): bool = var s = StringStream(s) return s.pos >= s.data.len @@ -1208,11 +1202,6 @@ 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: @@ -1272,9 +1261,12 @@ else: # after 1.3 or JS not defined proc ssClose(s: Stream) = var s = StringStream(s) - s.data = "" + when defined(nimNoNilSeqs): + s.data = "" + else: + s.data = nil - proc newStringStream*(s: sink string = ""): owned StringStream = + proc newStringStream*(s: string = ""): owned StringStream = ## Creates a new stream from the string `s`. ## ## See also: @@ -1293,11 +1285,6 @@ else: # after 1.3 or JS not defined new(result) result.data = s - when nimvm: - discard - else: - when declared(prepareMutation): - prepareMutation(result.data) # Allows us to mutate using `addr` logic like `copyMem`, otherwise it errors. result.pos = 0 result.closeImpl = ssClose result.atEndImpl = ssAtEnd @@ -1346,7 +1333,7 @@ proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) = if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen: raise newEIO("cannot write to stream") -proc fsReadLine(s: Stream, line: var string): bool = +proc fsReadLine(s: Stream, line: var TaintedString): bool = result = readLine(FileStream(s).f, line) proc newFileStream*(f: File): owned FileStream = @@ -1358,7 +1345,7 @@ proc newFileStream*(f: File): owned FileStream = ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream ## from string. ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ is the same - ## as using `open proc `_ + ## as using `open proc `_ ## on Examples. ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a ## file stream from the file name and the mode. @@ -1397,7 +1384,7 @@ proc newFileStream*(filename: string, mode: FileMode = fmRead, ## Creates a new stream from the file named `filename` with the mode `mode`. ## ## If the file cannot be opened, `nil` is returned. See the `io module - ## `_ for a list of available `FileMode enums `_. + ## `_ for a list of available `FileMode enums `_. ## ## **Note:** ## * **This function returns nil in case of failure.** @@ -1414,7 +1401,7 @@ proc newFileStream*(filename: string, mode: FileMode = fmRead, ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a ## file stream from the file name and the mode. runnableExamples: - from std/os import removeFile + from os import removeFile var strm = newFileStream("somefile.txt", fmWrite) if not isNil(strm): strm.writeLine("The first line") @@ -1530,7 +1517,6 @@ when false: of fmReadWrite: flags = O_RDWR or int(O_CREAT) of fmReadWriteExisting: flags = O_RDWR of fmAppend: flags = O_WRONLY or int(O_CREAT) or O_APPEND - static: doAssert false # handle bug #17888 var handle = open(filename, flags) if handle < 0: raise newEOS("posix.open() call failed") result = newFileHandleStream(handle) diff --git a/lib/pure/streamwrapper.nim b/lib/pure/streamwrapper.nim index a6c1901d2..b99982f1b 100644 --- a/lib/pure/streamwrapper.nim +++ b/lib/pure/streamwrapper.nim @@ -13,10 +13,6 @@ import deques, streams -when defined(nimPreviewSlimSystem): - import std/assertions - - type PipeOutStream*[T] = ref object of T # When stream peek operation is called, it reads from base stream @@ -30,12 +26,12 @@ type baseReadLineImpl: typeof(StreamObj.readLineImpl) baseReadDataImpl: typeof(StreamObj.readDataImpl) -proc posReadLine[T](s: Stream, line: var string): bool = +proc posReadLine[T](s: Stream, line: var TaintedString): bool = var s = PipeOutStream[T](s) assert s.baseReadLineImpl != nil let n = s.buffer.len - line.setLen(0) + line.string.setLen(0) for i in 0.. 0 - line.add(c) + line.string.add(c) var line2: string result = s.baseReadLineImpl(s, line2) @@ -93,7 +89,7 @@ proc newPipeOutStream*[T](s: sink (ref T)): owned PipeOutStream[T] = ## Example: ## ## .. code-block:: Nim - ## import std/[osproc, streamwrapper] + ## import osproc, streamwrapper ## var ## p = startProcess(exePath) ## outStream = p.outputStream().newPipeOutStream() diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 216c1ff11..ddf996e48 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -9,265 +9,257 @@ ##[ String `interpolation`:idx: / `format`:idx: inspired by -Python's f-strings. +Python's ``f``-strings. -# `fmt` vs. `&` +``fmt`` vs. ``&`` +================= -You can use either `fmt` or the unary `&` operator for formatting. The +You can use either ``fmt`` or the unary ``&`` operator for formatting. The difference between them is subtle but important. -The `fmt"{expr}"` syntax is more aesthetically pleasing, but it hides a small +The ``fmt"{expr}"`` syntax is more aesthetically pleasing, but it hides a small gotcha. The string is a `generalized raw string literal `_. This has some surprising effects: -]## -runnableExamples: - let msg = "hello" - assert fmt"{msg}\n" == "hello\\n" +.. code-block:: nim -##[ -Because the literal is a raw string literal, the `\n` is not interpreted as + import strformat + let msg = "hello" + doAssert fmt"{msg}\n" == "hello\\n" + +Because the literal is a raw string literal, the ``\n`` is not interpreted as an escape sequence. -There are multiple ways to get around this, including the use of the `&` operator: -]## +There are multiple ways to get around this, including the use of the ``&`` +operator: -runnableExamples: - let msg = "hello" +.. code-block:: nim - assert &"{msg}\n" == "hello\n" + import strformat + let msg = "hello" - assert fmt"{msg}{'\n'}" == "hello\n" - assert fmt("{msg}\n") == "hello\n" - assert "{msg}\n".fmt == "hello\n" + doAssert &"{msg}\n" == "hello\n" + + doAssert fmt"{msg}{'\n'}" == "hello\n" + doAssert fmt("{msg}\n") == "hello\n" + doAssert "{msg}\n".fmt == "hello\n" -##[ The choice of style is up to you. -# Formatting strings -]## +Formatting strings +================== -runnableExamples: - assert &"""{"abc":>4}""" == " abc" - assert &"""{"abc":<4}""" == "abc " +.. code-block:: nim -##[ -# Formatting floats -]## + import strformat -runnableExamples: - assert fmt"{-12345:08}" == "-0012345" - assert fmt"{-1:3}" == " -1" - assert fmt"{-1:03}" == "-01" - assert fmt"{16:#X}" == "0x10" + doAssert &"""{"abc":>4}""" == " abc" + doAssert &"""{"abc":<4}""" == "abc " - assert fmt"{123.456}" == "123.456" - assert fmt"{123.456:>9.3f}" == " 123.456" - assert fmt"{123.456:9.3f}" == " 123.456" - assert fmt"{123.456:9.4f}" == " 123.4560" - assert fmt"{123.456:>9.0f}" == " 123." - assert fmt"{123.456:<9.4f}" == "123.4560 " +Formatting floats +================= - assert fmt"{123.456:e}" == "1.234560e+02" - assert fmt"{123.456:>13e}" == " 1.234560e+02" - assert fmt"{123.456:13e}" == " 1.234560e+02" +.. code-block:: nim -##[ -# Expressions -]## -runnableExamples: - let x = 3.14 - assert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847" - assert fmt"""{(block: - var res: string - for i in 1..15: - res.add (if i mod 15 == 0: "FizzBuzz" - elif i mod 5 == 0: "Buzz" - elif i mod 3 == 0: "Fizz" - else: $i) & " " - res)}""" == "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz " -##[ -# Debugging strings + import strformat + doAssert fmt"{-12345:08}" == "-0012345" + doAssert fmt"{-1:3}" == " -1" + doAssert fmt"{-1:03}" == "-01" + doAssert fmt"{16:#X}" == "0x10" -`fmt"{expr=}"` expands to `fmt"expr={expr}"` namely the text of the expression, + doAssert fmt"{123.456}" == "123.456" + doAssert fmt"{123.456:>9.3f}" == " 123.456" + doAssert fmt"{123.456:9.3f}" == " 123.456" + doAssert fmt"{123.456:9.4f}" == " 123.4560" + doAssert fmt"{123.456:>9.0f}" == " 123." + doAssert fmt"{123.456:<9.4f}" == "123.4560 " + + doAssert fmt"{123.456:e}" == "1.234560e+02" + doAssert fmt"{123.456:>13e}" == " 1.234560e+02" + doAssert fmt"{123.456:13e}" == " 1.234560e+02" + + +Debugging strings +================= + +``fmt"{expr=}"`` expands to ``fmt"expr={expr}"`` namely the text of the expression, an equal sign and the results of evaluated expression. -]## -runnableExamples: - assert fmt"{123.456=}" == "123.456=123.456" - assert fmt"{123.456=:>9.3f}" == "123.456= 123.456" +.. code-block:: nim - let x = "hello" - assert fmt"{x=}" == "x=hello" - assert fmt"{x =}" == "x =hello" + import strformat + doAssert fmt"{123.456=}" == "123.456=123.456" + doAssert fmt"{123.456=:>9.3f}" == "123.456= 123.456" - let y = 3.1415926 - assert fmt"{y=:.2f}" == fmt"y={y:.2f}" - assert fmt"{y=}" == fmt"y={y}" - assert fmt"{y = : <8}" == fmt"y = 3.14159 " + let x = "hello" + doAssert fmt"{x=}" == "x=hello" + doAssert fmt"{x =}" == "x =hello" + + let y = 3.1415926 + doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}" + doAssert fmt"{y=}" == fmt"y={y}" + doAssert fmt"{y = : <8}" == fmt"y = 3.14159 " + + proc hello(a: string, b: float): int = 12 + let a = "hello" + let b = 3.1415926 + doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12" + doAssert fmt"{x.hello(y) = }" == "x.hello(y) = 12" + doAssert fmt"{hello x, y = }" == "hello x, y = 12" - proc hello(a: string, b: float): int = 12 - assert fmt"{hello(x, y) = }" == "hello(x, y) = 12" - assert fmt"{x.hello(y) = }" == "x.hello(y) = 12" - assert fmt"{hello x, y = }" == "hello x, y = 12" -##[ Note that it is space sensitive: -]## -runnableExamples: - let x = "12" - assert fmt"{x=}" == "x=12" - assert fmt"{x =:}" == "x =12" - assert fmt"{x =}" == "x =12" - assert fmt"{x= :}" == "x= 12" - assert fmt"{x= }" == "x= 12" - assert fmt"{x = :}" == "x = 12" - assert fmt"{x = }" == "x = 12" - assert fmt"{x = :}" == "x = 12" - assert fmt"{x = }" == "x = 12" +.. code-block:: nim -##[ -# Implementation details + import strformat + let x = "12" + doAssert fmt"{x=}" == "x=12" + doAssert fmt"{x =:}" == "x =12" + doAssert fmt"{x =}" == "x =12" + doAssert fmt"{x= :}" == "x= 12" + doAssert fmt"{x= }" == "x= 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" -An expression like `&"{key} is {value:arg} {{z}}"` is transformed into: + +Implementation details +====================== + +An expression like ``&"{key} is {value:arg} {{z}}"`` is transformed into: .. code-block:: nim var temp = newStringOfCap(educatedCapGuess) - temp.formatValue(key, "") - temp.add(" is ") - temp.formatValue(value, arg) - temp.add(" {z}") + temp.formatValue key, "" + temp.add " is " + temp.formatValue value, arg + temp.add " {z}" temp Parts of the string that are enclosed in the curly braces are interpreted -as Nim code. To escape a `{` or `}`, double it. +as Nim code, to escape an ``{`` or ``}`` double it. -Within a curly expression, however, `{`, `}`, must be escaped with a backslash. - -To enable evaluating Nim expressions within curlies, colons inside parentheses -do not need to be escaped. -]## - -runnableExamples: - let x = "hello" - assert fmt"""{ "\{(" & x & ")\}" }""" == "{(hello)}" - assert fmt"""{{({ x })}}""" == "{(hello)}" - assert fmt"""{ $(\{x:1,"world":2\}) }""" == """[("hello", 1), ("world", 2)]""" - -##[ -`&` delegates most of the work to an open overloaded set -of `formatValue` procs. The required signature for a type `T` that supports -formatting is usually `proc formatValue(result: var string; x: T; specifier: string)`. +``&`` delegates most of the work to an open overloaded set +of ``formatValue`` procs. The required signature for a type ``T`` that supports +formatting is usually ``proc formatValue(result: var string; x: T; specifier: string)``. The subexpression after the colon -(`arg` in `&"{key} is {value:arg} {{z}}"`) is optional. It will be passed as -the last argument to `formatValue`. When the colon with the subexpression it is +(``arg`` in ``&"{key} is {value:arg} {{z}}"``) is optional. It will be passed as +the last argument to ``formatValue``. When the colon with the subexpression it is left out, an empty string will be taken instead. For strings and numeric types the optional argument is a so-called "standard format specifier". -# Standard format specifiers for strings, integers and floats -The general form of a standard format specifier is: +Standard format specifier for strings, integers and floats +========================================================== - [[fill]align][sign][#][0][minimumwidth][.precision][type] -The square brackets `[]` indicate an optional element. +The general form of a standard format specifier is:: -The optional `align` flag can be one of the following: + [[fill]align][sign][#][0][minimumwidth][.precision][type] -`<` -: Forces the field to be left-aligned within the available +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 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 option has no meaning in this case. -The optional `fill` character defines the character to be used to pad +The optional 'fill' character defines the character to be used to pad the field to the minimum width. The fill character, if present, must be followed by an alignment flag. -The `sign` option is only valid for numeric types, and can be one of the following: +The 'sign' option is only valid for numeric types, and can be one of the following: ================= ==================================================== Sign Meaning ================= ==================================================== -`+` Indicates that a sign should be used for both +``+`` Indicates that a sign should be used for both positive as well as negative numbers. -`-` Indicates that a sign should be used only for +``-`` Indicates that a sign should be used only for negative numbers (this is the default behavior). (space) Indicates that a leading space should be used on positive numbers. ================= ==================================================== -If the `#` character is present, integers use the 'alternate form' for formatting. -This means that binary, octal and hexadecimal output will be prefixed -with `0b`, `0o` and `0x`, respectively. +If the '#' character is present, integers use the 'alternate form' for formatting. +This means that binary, octal, and hexadecimal output will be prefixed +with '0b', '0o', and '0x', respectively. -`width` is a decimal integer defining the minimum field width. If not specified, +'width' is a decimal integer defining the minimum field width. If not specified, then the field width will be determined by the content. -If the width field is preceded by a zero (`0`) character, this enables +If the width field is preceded by a zero ('0') character, this enables zero-padding. -The `precision` is a decimal number indicating how many digits should be displayed +The 'precision' is a decimal number indicating how many digits should be displayed after the decimal point in a floating point conversion. For non-numeric types the field indicates the maximum field size - in other words, how many characters will be used from the field content. The precision is ignored for integer conversions. -Finally, the `type` determines how the data should be presented. +Finally, the 'type' determines how the data should be presented. The available integer presentation types are: + ================= ==================================================== Type Result ================= ==================================================== -`b` Binary. Outputs the number in base 2. -`d` Decimal Integer. Outputs the number in base 10. -`o` Octal format. Outputs the number in base 8. -`x` Hex format. Outputs the number in base 16, using +``b`` Binary. Outputs the number in base 2. +``d`` Decimal Integer. Outputs the number in base 10. +``o`` Octal format. Outputs the number in base 8. +``x`` Hex format. Outputs the number in base 16, using lower-case letters for the digits above 9. -`X` Hex format. Outputs the number in base 16, using +``X`` Hex format. Outputs the number in base 16, using uppercase letters for the digits above 9. -(None) The same as `d`. +(None) the same as 'd' ================= ==================================================== + The available floating point presentation types are: ================= ==================================================== Type Result ================= ==================================================== -`e` Exponent notation. Prints the number in scientific - notation using the letter `e` to indicate the +``e`` Exponent notation. Prints the number in scientific + notation using the letter 'e' to indicate the exponent. -`E` Exponent notation. Same as `e` except it converts +``E`` Exponent notation. Same as 'e' except it converts the number to uppercase. -`f` Fixed point. Displays the number as a fixed-point +``f`` Fixed point. Displays the number as a fixed-point number. -`F` Fixed point. Same as `f` except it converts the +``F`` Fixed point. Same as 'f' except it converts the number to uppercase. -`g` General format. This prints the number as a +``g`` General format. This prints the number as a fixed-point number, unless the number is too - large, in which case it switches to `e` + large, in which case it switches to 'e' exponent notation. -`G` General format. Same as `g` except it switches to `E` +``G`` General format. Same as 'g' except switches to 'E' if the number gets to large. -(None) Similar to `g`, except that it prints at least one +(None) similar to 'g', except that it prints at least one digit after the decimal point. ================= ==================================================== -# Limitations + +Limitations +=========== Because of the well defined order how templates and macros are expanded, strformat cannot expand template arguments: @@ -280,46 +272,45 @@ expanded, strformat cannot expand template arguments: let x = "abc" myTemplate(x) -First the template `myTemplate` is expanded, where every identifier -`arg` is substituted with its argument. The `arg` inside the +First the template ``myTemplate`` is expanded, where every identifier +``arg`` is substituted with its argument. The ``arg`` inside the format string is not seen by this process, because it is part of a quoted string literal. It is not an identifier yet. Then the strformat -macro creates the `arg` identifier from the string literal, an +macro creates the ``arg`` identifier from the string literal. An identifier that cannot be resolved anymore. The workaround for this is to bind the template argument to a new local variable. .. code-block:: nim + template myTemplate(arg: untyped): untyped = block: let arg1 {.inject.} = arg echo "arg is: ", arg1 echo &"--- {arg1} ---" -The use of `{.inject.}` here is necessary again because of template +The use of ``{.inject.}`` here is necessary again because of template expansion order and hygienic templates. But since we generally want to -keep the hygiene of `myTemplate`, and we do not want `arg1` -to be injected into the context where `myTemplate` is expanded, -everything is wrapped in a `block`. +keep the hygienicness of ``myTemplate``, and we do not want ``arg1`` +to be injected into the context where ``myTemplate`` is expanded, +everything is wrapped in a ``block``. -# Future directions +Future directions +================= -A curly expression with commas in it like `{x, argA, argB}` could be -transformed to `formatValue(result, x, argA, argB)` in order to support +A curly expression with commas in it like ``{x, argA, argB}`` could be +transformed to ``formatValue(result, x, argA, argB)`` in order to support formatters that do not need to parse a custom language within a custom -language but instead prefer to use Nim's existing syntax. This would also -help with readability, since there is only so much you can cram into +language but instead prefer to use Nim's existing syntax. This also +helps in readability since there is only so much you can cram into single letter DSLs. + ]## import macros, parseutils, unicode import strutils except format -when defined(nimPreviewSlimSystem): - import std/assertions - - proc mkDigit(v: int, typ: char): string {.inline.} = assert(v < 26) if v < 10: @@ -327,9 +318,10 @@ proc mkDigit(v: int, typ: char): string {.inline.} = else: result = $chr(ord(if typ == 'x': 'a' else: 'A') + v - 10) -proc alignString*(s: string, minimumWidth: int; align = '\0'; fill = ' '): string = - ## Aligns `s` using the `fill` char. - ## This is only of interest if you want to write a custom `format` proc that +proc alignString*(s: string, minimumWidth: int; align = '\0'; + fill = ' '): string = + ## Aligns ``s`` using ``fill`` char. + ## This is only of interest if you want to write a custom ``format`` proc that ## should support the standard format specifiers. if minimumWidth == 0: result = s @@ -351,30 +343,28 @@ type fill*, align*: char ## Desired fill and alignment. sign*: char ## Desired sign. alternateForm*: bool ## Whether to prefix binary, octal and hex numbers - ## with `0b`, `0o`, `0x`. + ## with ``0b``, ``0o``, ``0x``. padWithZero*: bool ## Whether to pad with zeros rather than spaces. minimumWidth*, precision*: int ## Desired minimum width and precision. typ*: char ## Type like 'f', 'g' or 'd'. endPosition*: int ## End position in the format specifier after - ## `parseStandardFormatSpecifier` returned. + ## ``parseStandardFormatSpecifier`` returned. -proc formatInt(n: SomeNumber; radix: int; spec: StandardFormatSpecifier): string = - ## Converts `n` to a string. If `n` is `SomeFloat`, it casts to `int64`. - ## Conversion is done using `radix`. If result's length is less than - ## `minimumWidth`, it aligns result to the right or left (depending on `a`) - ## with the `fill` char. +proc formatInt(n: SomeNumber; radix: int; + spec: StandardFormatSpecifier): string = + ## Converts ``n`` to string. If ``n`` is `SomeFloat`, it casts to `int64`. + ## Conversion is done using ``radix``. If result's length is lesser than + ## ``minimumWidth``, it aligns result to the right or left (depending on ``a``) + ## with ``fill`` char. when n is SomeUnsignedInt: var v = n.uint64 let negative = false else: - let n = n.int64 - let negative = n < 0 - var v = - if negative: - # `uint64(-n)`, but accounts for `n == low(int64)` - uint64(not n) + 1 - else: - uint64(n) + var v = n.int64 + let negative = v.int64 < 0 + if negative: + # FIXME: overflow error for low(int64) + v = v * -1 var xx = "" if spec.alternateForm: @@ -389,9 +379,9 @@ proc formatInt(n: SomeNumber; radix: int; spec: StandardFormatSpecifier): string result = "0" else: result = "" - while v > typeof(v)(0): - let d = v mod typeof(v)(radix) - v = v div typeof(v)(radix) + while v > type(v)(0): + let d = v mod type(v)(radix) + v = v div type(v)(radix) result.add(mkDigit(d.int, spec.typ)) for idx in 0..<(result.len div 2): swap result[idx], result[result.len - idx - 1] @@ -423,13 +413,13 @@ proc formatInt(n: SomeNumber; radix: int; spec: StandardFormatSpecifier): string proc parseStandardFormatSpecifier*(s: string; start = 0; ignoreUnknownSuffix = false): StandardFormatSpecifier = ## An exported helper proc that parses the "standard format specifiers", - ## as specified by the grammar: + ## as specified by the grammar:: ## - ## [[fill]align][sign][#][0][minimumwidth][.precision][type] + ## [[fill]align][sign][#][0][minimumwidth][.precision][type] ## - ## This is only of interest if you want to write a custom `format` proc that - ## should support the standard format specifiers. If `ignoreUnknownSuffix` is true, - ## an unknown suffix after the `type` field is not an error. + ## This is only of interest if you want to write a custom ``format`` proc that + ## should support the standard format specifiers. If ``ignoreUnknownSuffix`` is true, + ## an unknown suffix after the ``type`` field is not an error. const alignChars = {'<', '>', '^'} result.fill = ' ' result.align = '\0' @@ -451,7 +441,7 @@ proc parseStandardFormatSpecifier*(s: string; start = 0; result.alternateForm = true inc i - if i + 1 < s.len and s[i] == '0' and s[i+1] in {'0'..'9'}: + if i+1 < s.len and s[i] == '0' and s[i+1] in {'0'..'9'}: result.padWithZero = true inc i @@ -473,10 +463,10 @@ proc parseStandardFormatSpecifier*(s: string; start = 0; "invalid format string, cannot parse: " & s[i..^1]) proc formatValue*[T: SomeInteger](result: var string; value: T; - specifier: string) = - ## Standard format implementation for `SomeInteger`. It makes little + specifier: string) = + ## Standard format implementation for ``SomeInteger``. It makes little ## sense to call this directly, but it is required to exist - ## by the `&` macro. + ## by the ``&`` macro. if specifier.len == 0: result.add $value return @@ -494,9 +484,9 @@ proc formatValue*[T: SomeInteger](result: var string; value: T; result.add formatInt(value, radix, spec) proc formatValue*(result: var string; value: SomeFloat; specifier: string) = - ## Standard format implementation for `SomeFloat`. It makes little + ## Standard format implementation for ``SomeFloat``. It makes little ## sense to call this directly, but it is required to exist - ## by the `&` macro. + ## by the ``&`` macro. if specifier.len == 0: result.add $value return @@ -551,9 +541,9 @@ proc formatValue*(result: var string; value: SomeFloat; specifier: string) = result.add res proc formatValue*(result: var string; value: string; specifier: string) = - ## Standard format implementation for `string`. It makes little + ## Standard format implementation for ``string``. It makes little ## sense to call this directly, but it is required to exist - ## by the `&` macro. + ## by the ``&`` macro. let spec = parseStandardFormatSpecifier(specifier) var value = value case spec.typ @@ -567,7 +557,8 @@ proc formatValue*(result: var string; value: string; specifier: string) = setLen(value, runeOffset(value, spec.precision)) result.add alignString(value, spec.minimumWidth, spec.align, spec.fill) -proc formatValue[T: not SomeInteger](result: var string; value: T; specifier: string) = +proc formatValue[T: not SomeInteger](result: var string; value: T; + specifier: string) = mixin `$` formatValue(result, $value, specifier) @@ -577,19 +568,19 @@ template formatValue(result: var string; value: char; specifier: string) = template formatValue(result: var string; value: cstring; specifier: string) = result.add value -proc strformatImpl(f: string; openChar, closeChar: char): NimNode = - template missingCloseChar = - error("invalid format string: missing closing character '" & closeChar & "'") - +proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = + if pattern.kind notin {nnkStrLit..nnkTripleStrLit}: + error "string formatting (fmt(), &) only works with string literals", pattern if openChar == ':' or closeChar == ':': error "openChar and closeChar must not be ':'" + let f = pattern.strVal var i = 0 let res = genSym(nskVar, "fmtRes") - result = newNimNode(nnkStmtListExpr) + result = newNimNode(nnkStmtListExpr, lineInfoFrom = pattern) # XXX: https://github.com/nim-lang/Nim/issues/8405 # When compiling with -d:useNimRtl, certain procs such as `count` from the strutils # module are not accessible at compile-time: - let expectedGrowth = when defined(useNimRtl): 0 else: count(f, openChar) * 10 + let expectedGrowth = when defined(useNimRtl): 0 else: count(f, '{') * 10 result.add newVarStmt(res, newCall(bindSym"newStringOfCap", newLit(f.len + expectedGrowth))) var strlit = "" @@ -605,45 +596,28 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode = strlit = "" var subexpr = "" - var inParens = 0 - var inSingleQuotes = false - var inDoubleQuotes = false - template notEscaped:bool = f[i-1]!='\\' - while i < f.len and f[i] != closeChar and (f[i] != ':' or inParens != 0): - case f[i] - of '\\': - if i < f.len-1 and f[i+1] in {openChar,closeChar,':'}: inc i - of '\'': - if not inDoubleQuotes and notEscaped: inSingleQuotes = not inSingleQuotes - of '\"': - if notEscaped: inDoubleQuotes = not inDoubleQuotes - of '(': - if not (inSingleQuotes or inDoubleQuotes): inc inParens - of ')': - if not (inSingleQuotes or inDoubleQuotes): dec inParens - of '=': + while i < f.len and f[i] != closeChar and f[i] != ':': + if f[i] == '=': let start = i inc i i += f.skipWhitespace(i) - if i == f.len: - missingCloseChar if f[i] == closeChar or f[i] == ':': result.add newCall(bindSym"add", res, newLit(subexpr & f[start ..< i])) else: subexpr.add f[start ..< i] - continue - else: discard - subexpr.add f[i] - inc i - - if i == f.len: - missingCloseChar + else: + subexpr.add f[i] + inc i var x: NimNode try: x = parseExpr(subexpr) - except ValueError as e: - error("could not parse `$#` in `$#`.\n$#" % [subexpr, f, e.msg]) + except ValueError: + when declared(getCurrentExceptionMsg): + let msg = getCurrentExceptionMsg() + error("could not parse `" & subexpr & "`.\n" & msg, pattern) + else: + error("could not parse `" & subexpr & "`.\n", pattern) let formatSym = bindSym("formatValue", brOpen) var options = "" if f[i] == ':': @@ -651,17 +625,17 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode = while i < f.len and f[i] != closeChar: options.add f[i] inc i - if i == f.len: - missingCloseChar if f[i] == closeChar: inc i + else: + doAssert false, "invalid format string: missing '}'" result.add newCall(formatSym, res, x, newLit(options)) elif f[i] == closeChar: - if i".fmt('<', '>') == "7" - assert "<<>>".fmt('<', '>') == "<7>" - assert "`x`".fmt('`', '`') == "7" - strformatImpl(pattern, openChar, closeChar) +macro fmt*(pattern: string): untyped = strformatImpl(pattern, '{', '}') + ## An alias for ``&``. -template fmt*(pattern: static string): untyped = - ## Alias for `fmt(pattern, '{', '}')`. - fmt(pattern, '{', '}') - -macro `&`*(pattern: string{lit}): string = - ## `&pattern` is the same as `pattern.fmt`. - ## For a specification of the `&` macro, see the module level documentation. - # pending bug #18275, bug #18278, use `pattern: static string` - # consider deprecating this, it's redundant with `fmt` and `fmt` is strictly - # more flexible, readable (no confusion with the binary `&`), self-documenting, - # not to mention #18275, bug #18278. +macro fmt*(pattern: string; openChar, closeChar: char): untyped = + ## Use ``openChar`` instead of '{' and ``closeChar`` instead of '}' runnableExamples: - let x = 7 - assert &"{x}\n" == "7\n" # regular 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, '{', '}') + let testInt = 123 + doAssert "".fmt('<', '>') == "123" + doAssert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)" + doAssert """ ""{"123+123"}"" """.fmt('"', '"') == " \"{246}\" " + strformatImpl(pattern, openChar.intVal.char, closeChar.intVal.char) diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim index c8cd839be..5060deb78 100644 --- a/lib/pure/strmisc.nim +++ b/lib/pure/strmisc.nim @@ -8,12 +8,12 @@ # ## This module contains various string utility routines that are uncommonly -## used in comparison to the ones in `strutils `_. +## used in comparison to `strutils `_. -import std/strutils +import strutils -func expandTabs*(s: string, tabSize: int = 8): string = - ## Expands tab characters in `s`, replacing them by spaces. +proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect.} = + ## Expand tab characters in `s` replacing them by spaces. ## ## The amount of inserted spaces for each tab character is the difference ## between the current column number and the next tab position. Tab positions @@ -24,7 +24,9 @@ func expandTabs*(s: string, tabSize: int = 8): string = runnableExamples: doAssert expandTabs("\t", 4) == " " doAssert expandTabs("\tfoo\t", 4) == " foo " - doAssert expandTabs("a\tb\n\txy\t", 3) == "a b\n xy " + doAssert expandTabs("\tfoo\tbar", 4) == " foo bar" + doAssert expandTabs("\tfoo\tbar\t", 4) == " foo bar " + doAssert expandTabs("ab\tcd\n\txy\t", 3) == "ab cd\n xy " result = newStringOfCap(s.len + s.len shr 2) var pos = 0 @@ -48,39 +50,37 @@ func expandTabs*(s: string, tabSize: int = 8): string = if c == '\l': pos = 0 -func partition*(s: string, sep: string, - right: bool = false): (string, string, string) = - ## Splits the string at the first (if `right` is false) - ## or last (if `right` is true) occurrence of `sep` into a 3-tuple. +proc partition*(s: string, sep: string, + right: bool = false): (string, string, string) + {.noSideEffect.} = + ## Split the string at the first or last occurrence of `sep` into a 3-tuple ## - ## Returns a 3-tuple of strings, `(beforeSep, sep, afterSep)` or - ## `(s, "", "")` if `sep` is not found and `right` is false or - ## `("", "", s)` if `sep` is not found and `right` is true. - ## - ## **See also:** - ## * `rpartition proc <#rpartition,string,string>`_ + ## Returns a 3 string tuple of (beforeSep, `sep`, afterSep) or + ## (`s`, "", "") if `sep` is not found and `right` is false or + ## ("", "", `s`) if `sep` is not found and `right` is true runnableExamples: - doAssert partition("foo:bar:baz", ":") == ("foo", ":", "bar:baz") - doAssert partition("foo:bar:baz", ":", right = true) == ("foo:bar", ":", "baz") - doAssert partition("foobar", ":") == ("foobar", "", "") - doAssert partition("foobar", ":", right = true) == ("", "", "foobar") + doAssert partition("foo:bar", ":") == ("foo", ":", "bar") + doAssert partition("foobarbar", "bar") == ("foo", "bar", "bar") + doAssert partition("foobarbar", "bank") == ("foobarbar", "", "") + doAssert partition("foobarbar", "foo") == ("", "foo", "barbar") + doAssert partition("foofoobar", "bar") == ("foofoo", "bar", "") let position = if right: s.rfind(sep) else: s.find(sep) if position != -1: return (s[0 ..< position], sep, s[position + sep.len ..< s.len]) return if right: ("", "", s) else: (s, "", "") -func rpartition*(s: string, sep: string): (string, string, string) = - ## Splits the string at the last occurrence of `sep` into a 3-tuple. +proc rpartition*(s: string, sep: string): (string, string, string) + {.noSideEffect.} = + ## Split the string at the last occurrence of `sep` into a 3-tuple ## - ## Returns a 3-tuple of strings, `(beforeSep, sep, afterSep)` or - ## `("", "", s)` if `sep` is not found. This is the same as - ## `partition(s, sep, right = true)`. - ## - ## **See also:** - ## * `partition proc <#partition,string,string,bool>`_ + ## Returns a 3 string tuple of (beforeSep, `sep`, afterSep) or + ## ("", "", `s`) if `sep` is not found runnableExamples: - doAssert rpartition("foo:bar:baz", ":") == ("foo:bar", ":", "baz") - doAssert rpartition("foobar", ":") == ("", "", "foobar") + doAssert rpartition("foo:bar", ":") == ("foo", ":", "bar") + doAssert rpartition("foobarbar", "bar") == ("foobar", "bar", "") + doAssert rpartition("foobarbar", "bank") == ("", "", "foobarbar") + doAssert rpartition("foobarbar", "foo") == ("", "foo", "barbar") + doAssert rpartition("foofoobar", "bar") == ("foofoo", "bar", "") - partition(s, sep, right = true) + return partition(s, sep, right = true) diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 8a1ea125f..9c55bf3e3 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -35,7 +35,7 @@ substrings starting with ``$``. These constructions are available: ``$o`` Matches an octal integer. This uses ``parseutils.parseOct``. ``$i`` Matches a decimal integer. This uses ``parseutils.parseInt``. ``$h`` Matches a hex integer. This uses ``parseutils.parseHex``. -``$f`` Matches a floating-point number. Uses ``parseFloat``. +``$f`` Matches a floating pointer number. Uses ``parseFloat``. ``$w`` Matches an ASCII identifier: ``[A-Za-z_][A-Za-z_0-9]*``. ``$c`` Matches a single ASCII character. ``$s`` Skips optional whitespace. @@ -52,7 +52,7 @@ substrings starting with ``$``. These constructions are available: ================= ======================================================== Even though ``$*`` and ``$+`` look similar to the regular expressions ``.*`` -and ``.+``, they work quite differently. There is no non-deterministic +and ``.+`` they work quite differently, there is no non-deterministic state machine involved and the matches are non-greedy. ``[$*]`` matches ``[xyz]`` via ``parseutils.parseUntil``. @@ -175,7 +175,7 @@ overloaded to handle both single characters and sets of character. .. code-block:: nim - import std/streams + import streams template atom(input: Stream; idx: int; c: char): bool = ## Used in scanp for the matching of atoms (usually chars). @@ -286,10 +286,6 @@ 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) @@ -512,7 +508,7 @@ macro scanTuple*(input: untyped; pattern: static[string]; matcherTypes: varargs[ inc userMatches else: discard inc p - result.add nnkTupleConstr.newTree(newCall(ident("scanf"), input, newStrLitNode(pattern))) + result.add newPar(newCall(ident("scanf"), input, newStrLitNode(pattern))) for arg in arguments: result[^1][0].add arg result[^1].add arg @@ -581,14 +577,13 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = of nnkCallKinds: # *{'A'..'Z'} !! s.add(!_) template buildWhile(input, idx, init, cond, action): untyped = - mixin hasNxt while hasNxt(input, idx): init if not cond: break action # (x) a # bind action a to (x) - if it[0].kind in {nnkPar, nnkTupleConstr} and it.len == 2: + if it[0].kind == nnkPar 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: @@ -693,4 +688,4 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = result.add toIfChain(conds, idx, res, 0) result.add res when defined(debugScanp): - echo repr result + echo repr result \ No newline at end of file diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 11d5015cb..7cf668d19 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -53,15 +53,11 @@ import std/private/since import hashes, strutils -when defined(nimPreviewSlimSystem): - import std/assertions - - when defined(js) or defined(nimscript) or defined(Standalone): {.pragma: rtlFunc.} else: {.pragma: rtlFunc, rtl.} - import std/envvars + import os include "system/inclrtl" @@ -261,7 +257,10 @@ proc newStringTable*(mode: StringTableMode): owned(StringTableRef) {. ## See also: ## * `newStringTable(keyValuePairs) proc ## <#newStringTable,varargs[tuple[string,string]],StringTableMode>`_ - result = StringTableRef(mode: mode, counter: 0, data: newSeq[KeyValuePair](startSize)) + new(result) + result.mode = mode + result.counter = 0 + newSeq(result.data, startSize) proc newStringTable*(keyValuePairs: varargs[string], mode: StringTableMode): owned(StringTableRef) {. @@ -301,10 +300,11 @@ proc raiseFormatException(s: string) = proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string = if hasKey(t, key): return t.getOrDefault(key) + # hm difficult: assume safety in taint mode here. XXX This is dangerous! when defined(js) or defined(nimscript) or defined(Standalone): result = "" else: - if useEnvironment in flags: result = getEnv(key) + if useEnvironment in flags: result = getEnv(key).string else: result = "" if result.len == 0: if useKey in flags: result = '$' & key diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index c458d605e..a25df9e42 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -38,7 +38,7 @@ runnableExamples: ## `method call syntax`_: runnableExamples: - from std/sequtils import map + from sequtils import map let jenny = "867-5309" assert jenny.split('-').map(parseInt) == @[867, 5309] @@ -72,19 +72,16 @@ runnableExamples: import parseutils from math import pow, floor, log10 -from algorithm import fill, reverse +from algorithm import reverse import std/enumutils -from unicode import toLower, toUpper -export toLower, toUpper +when defined(nimVmExportFixed): + from unicode import toLower, toUpper + export toLower, toUpper include "system/inclrtl" import std/private/since -from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl, - startsWithImpl, endsWithImpl - -when defined(nimPreviewSlimSystem): - import std/assertions +from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl, startsWithImpl, endsWithImpl const @@ -95,15 +92,6 @@ 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. @@ -120,9 +108,6 @@ 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. ## @@ -185,7 +170,7 @@ func isLowerAscii*(c: char): bool {.rtl, extern: "nsuIsLowerAsciiChar".} = doAssert isLowerAscii('e') == true doAssert isLowerAscii('E') == false doAssert isLowerAscii('7') == false - return c in LowercaseLetters + return c in {'a'..'z'} func isUpperAscii*(c: char): bool {.rtl, extern: "nsuIsUpperAsciiChar".} = ## Checks whether or not `c` is an upper case character. @@ -199,7 +184,8 @@ func isUpperAscii*(c: char): bool {.rtl, extern: "nsuIsUpperAsciiChar".} = doAssert isUpperAscii('e') == false doAssert isUpperAscii('E') == true doAssert isUpperAscii('7') == false - return c in UppercaseLetters + return c in {'A'..'Z'} + func toLowerAscii*(c: char): char {.rtl, extern: "nsuToLowerAsciiChar".} = ## Returns the lower case version of character `c`. @@ -214,7 +200,7 @@ func toLowerAscii*(c: char): char {.rtl, extern: "nsuToLowerAsciiChar".} = runnableExamples: doAssert toLowerAscii('A') == 'a' doAssert toLowerAscii('e') == 'e' - if c in UppercaseLetters: + if c in {'A'..'Z'}: result = char(uint8(c) xor 0b0010_0000'u8) else: result = c @@ -251,7 +237,7 @@ func toUpperAscii*(c: char): char {.rtl, extern: "nsuToUpperAsciiChar".} = runnableExamples: doAssert toUpperAscii('a') == 'A' doAssert toUpperAscii('E') == 'E' - if c in LowercaseLetters: + if c in {'a'..'z'}: result = char(uint8(c) xor 0b0010_0000'u8) else: result = c @@ -288,20 +274,14 @@ func nimIdentNormalize*(s: string): string = ## ## That means to convert to lower case and remove any '_' on all characters ## except first one. - ## - ## .. Warning:: Backticks (`) are not handled: they remain *as is* and - ## spaces are preserved. See `nimIdentBackticksNormalize - ## `_ for - ## an alternative approach. runnableExamples: doAssert nimIdentNormalize("Foo_bar") == "Foobar" result = newString(s.len) - if s.len == 0: - return - result[0] = s[0] + if s.len > 0: + result[0] = s[0] var j = 1 for i in 1..len(s) - 1: - if s[i] in UppercaseLetters: + if s[i] in {'A'..'Z'}: result[j] = chr(ord(s[i]) + (ord('a') - ord('A'))) inc j elif s[i] != '_': @@ -323,7 +303,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 UppercaseLetters: + if s[i] in {'A'..'Z'}: result[j] = chr(ord(s[i]) + (ord('a') - ord('A'))) inc j elif s[i] != '_': @@ -624,7 +604,7 @@ iterator splitLines*(s: string, keepEol = false): string = ## ## Every `character literal `_ ## newline combination (CR, LF, CR-LF) is supported. The result strings - ## contain no trailing end of line characters unless the parameter `keepEol` + ## contain no trailing end of line characters unless parameter `keepEol` ## is set to `true`. ## ## Example: @@ -944,26 +924,14 @@ func toHex*[T: SomeInteger](x: T, len: Positive): string = doAssert b.toHex(4) == "1001" doAssert toHex(62, 3) == "03E" doAssert toHex(-8, 6) == "FFFFF8" - when defined(js): - toHexImpl(cast[BiggestUInt](x), len, x < 0) - else: - when T is SomeSignedInt: - toHexImpl(cast[BiggestUInt](BiggestInt(x)), len, x < 0) - else: - toHexImpl(BiggestUInt(x), len, x < 0) + toHexImpl(cast[BiggestUInt](x), len, x < 0) func toHex*[T: SomeInteger](x: T): string = ## Shortcut for `toHex(x, T.sizeof * 2)` runnableExamples: doAssert toHex(1984'i64) == "00000000000007C0" doAssert toHex(1984'i16) == "07C0" - when defined(js): - toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0) - else: - when T is SomeSignedInt: - toHexImpl(cast[BiggestUInt](BiggestInt(x)), 2*sizeof(T), x < 0) - else: - toHexImpl(BiggestUInt(x), 2*sizeof(T), x < 0) + toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0) func toHex*(s: string): string {.rtl.} = ## Converts a bytes string to its hexadecimal representation. @@ -1509,40 +1477,12 @@ func dedent*(s: string, count: Natural = indentation(s)): string {.rtl, doAssert x == "Hello\n There\n" unindent(s, count, " ") -func delete*(s: var string, slice: Slice[int]) = - ## Deletes the items `s[slice]`, raising `IndexDefect` if the slice contains - ## elements out of range. +func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete".} = + ## Deletes in `s` (must be declared as `var`) the characters at positions + ## `first .. last` (both ends included). ## - ## This operation moves all elements after `s[slice]` in linear time, and - ## is the string analog to `sequtils.delete`. + ## This modifies `s` itself, it does not return a copy. runnableExamples: - var a = "abcde" - doAssertRaises(IndexDefect): a.delete(4..5) - assert a == "abcde" - a.delete(4..4) - assert a == "abcd" - a.delete(1..2) - assert a == "ad" - a.delete(1..<1) # empty slice - assert a == "ad" - when compileOption("boundChecks"): - if not (slice.a < s.len and slice.a >= 0 and slice.b < s.len): - raise newException(IndexDefect, $(slice: slice, len: s.len)) - if slice.b >= slice.a: - var i = slice.a - var j = slice.b + 1 - var newLen = s.len - j + i - # if j < s.len: moveMem(addr s[i], addr s[j], s.len - j) # pending benchmark - while i < newLen: - s[i] = s[j] - inc(i) - inc(j) - setLen(s, newLen) - -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" a.delete(4, 5) @@ -1563,6 +1503,7 @@ func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete", inc(j) setLen(s, newLen) + func startsWith*(s: string, prefix: char): bool {.inline.} = ## Returns true if `s` starts with character `prefix`. ## @@ -1660,7 +1601,7 @@ func removePrefix*(s: var string, chars: set[char] = Newlines) {.rtl, var start = 0 while start < s.len and s[start] in chars: start += 1 - if start > 0: s.delete(0..start - 1) + if start > 0: s.delete(0, start - 1) func removePrefix*(s: var string, c: char) {.rtl, extern: "nsuRemovePrefixChar".} = @@ -1687,8 +1628,8 @@ func removePrefix*(s: var string, prefix: string) {.rtl, var answers = "yesyes" answers.removePrefix("yes") doAssert answers == "yes" - if s.startsWith(prefix) and prefix.len > 0: - s.delete(0..prefix.len - 1) + if s.startsWith(prefix): + s.delete(0, prefix.len - 1) func removeSuffix*(s: var string, chars: set[char] = Newlines) {.rtl, extern: "nsuRemoveSuffixCharSet".} = @@ -1820,7 +1761,7 @@ func join*(a: openArray[string], sep: string = ""): string {.rtl, else: result = "" -func join*[T: not string](a: openArray[T], sep: string = ""): string = +func join*[T: not string](a: openArray[T], sep: string = ""): string {.rtl.} = ## Converts all elements in the container `a` to strings using `$`, ## and concatenates them with `sep`. runnableExamples: @@ -1833,44 +1774,36 @@ func join*[T: not string](a: openArray[T], sep: string = ""): string = add(result, $x) type - SkipTable* = array[char, int] ## Character table for efficient substring search. + SkipTable* = array[char, int] func initSkipTable*(a: var SkipTable, sub: string) {.rtl, extern: "nsuInitSkipTable".} = - ## 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. + ## Preprocess table `a` for `sub`. let m = len(sub) - fill(a, m) + var i = 0 + while i <= 0xff-7: + a[chr(i + 0)] = m + a[chr(i + 1)] = m + a[chr(i + 2)] = m + a[chr(i + 3)] = m + a[chr(i + 4)] = m + a[chr(i + 5)] = m + a[chr(i + 6)] = m + a[chr(i + 7)] = m + i += 8 for i in 0 ..< m - 1: a[sub[i]] = m - 1 - i -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 {. +func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): 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: @@ -1880,7 +1813,6 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = -1): 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: @@ -1890,6 +1822,7 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = -1): 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 {. @@ -1898,97 +1831,68 @@ when not (defined(js) or defined(nimdoc) or defined(nimscript)): else: const hasCStringBuiltin = false -func find*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, +func find*(s: string, sub: char, start: Natural = 0, last = 0): int {.rtl, extern: "nsuFindChar".} = ## Searches for `sub` in `s` inside range `start..last` (both ends included). - ## If `last` is unspecified or negative, it defaults to `s.high` (the last element). + ## 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`. - ## Subtract `start` from the result for a `start`-origin index. + ## Use `s[start..last].rfind` for a `start`-origin index. ## ## See also: - ## * `rfind func<#rfind,string,char,Natural,int>`_ + ## * `rfind func<#rfind,string,char,Natural>`_ ## * `replace func<#replace,string,char,char>`_ - result = -1 - let last = if last < 0: s.high else: last - - template findImpl = - for i in int(start)..last: - if s[i] == sub: - return i - + let last = if last == 0: s.high else: last when nimvm: - findImpl() + for i in int(start)..last: + if sub == s[i]: return i else: when hasCStringBuiltin: - let length = last-start+1 - if length > 0: - let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](length)) + 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[int](found) -% cast[int](s.cstring) + return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) else: - findImpl() + for i in int(start)..last: + if sub == s[i]: return i + return -1 -func find*(s: string, chars: set[char], start: Natural = 0, last = -1): int {. +func find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {. rtl, extern: "nsuFindCharSet".} = ## Searches for `chars` in `s` inside range `start..last` (both ends included). - ## If `last` is unspecified or negative, it defaults to `s.high` (the last element). + ## If `last` is unspecified, 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`. - ## Subtract `start` from the result for a `start`-origin index. + ## Use `s[start..last].find` for a `start`-origin index. ## ## See also: - ## * `rfind func<#rfind,string,set[char],Natural,int>`_ + ## * `rfind func<#rfind,string,set[char],Natural>`_ ## * `multiReplace func<#multiReplace,string,varargs[]>`_ - result = -1 - let last = if last < 0: s.high else: last + let last = if last == 0: s.high else: last for i in int(start)..last: - if s[i] in chars: - return i + if s[i] in chars: return i + return -1 -when defined(linux): - proc memmem(haystack: pointer, haystacklen: csize_t, - needle: pointer, needlelen: csize_t): pointer {.importc, header: """#define _GNU_SOURCE -#include """.} -elif defined(bsd) or (defined(macosx) and not defined(ios)): - proc memmem(haystack: pointer, haystacklen: csize_t, - needle: pointer, needlelen: csize_t): pointer {.importc, header: "#include ".} - -func find*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, +func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, extern: "nsuFindStr".} = ## Searches for `sub` in `s` inside range `start..last` (both ends included). - ## If `last` is unspecified or negative, it defaults to `s.high` (the last element). + ## 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`. - ## Subtract `start` from the result for a `start`-origin index. + ## Use `s[start..last].find` for a `start`-origin index. ## ## See also: - ## * `rfind func<#rfind,string,string,Natural,int>`_ + ## * `rfind func<#rfind,string,string,Natural>`_ ## * `replace func<#replace,string,string,string>`_ - if sub.len > s.len - start: return -1 + if sub.len > s.len: return -1 if sub.len == 1: return find(s, sub[0], start, last) - - template useSkipTable = - result = find(initSkipTable(sub), s, sub, start, last) - - when nimvm: - useSkipTable() - else: - when declared(memmem): - let subLen = sub.len - if last < 0 and start < s.len and subLen != 0: - let found = memmem(s[start].unsafeAddr, csize_t(s.len - start), sub.cstring, csize_t(subLen)) - result = if not found.isNil: - cast[int](found) -% cast[int](s.cstring) - else: - -1 - else: - useSkipTable() - else: - useSkipTable() + var a {.noinit.}: SkipTable + initSkipTable(a, sub) + result = find(a, s, sub, start, last) func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, extern: "nsuRFindChar".} = @@ -1999,7 +1903,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`. - ## Subtract `start` from the result for a `start`-origin index. + ## Use `s[start..last].find` for a `start`-origin index. ## ## See also: ## * `find func<#find,string,char,Natural,int>`_ @@ -2017,7 +1921,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`. - ## Subtract `start` from the result for a `start`-origin index. + ## Use `s[start..last].rfind` for a `start`-origin index. ## ## See also: ## * `find func<#find,string,set[char],Natural,int>`_ @@ -2035,14 +1939,11 @@ 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`. - ## Subtract `start` from the result for a `start`-origin index. + ## Use `s[start..last].rfind` for a `start`-origin index. ## ## See also: ## * `find func<#find,string,string,Natural,int>`_ if sub.len == 0: - 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 result = 0 @@ -2098,7 +1999,7 @@ func countLines*(s: string): int {.rtl, extern: "nsuCountLines".} = ## Returns the number of lines in the string `s`. ## ## This is the same as `len(splitLines(s))`, but much more efficient - ## because it doesn't modify the string creating temporary objects. Every + ## because it doesn't modify the string creating temporal objects. Every ## `character literal `_ ## newline combination (CR, LF, CR-LF) is supported. ## @@ -2137,7 +2038,7 @@ func contains*(s: string, chars: set[char]): bool = func replace*(s, sub: string, by = ""): string {.rtl, extern: "nsuReplaceStr".} = - ## Replaces every occurrence of the string `sub` in `s` with the string `by`. + ## Replaces `sub` in `s` by the string `by`. ## ## See also: ## * `find func<#find,string,string,Natural,int>`_ @@ -2164,7 +2065,8 @@ func replace*(s, sub: string, by = ""): string {.rtl, # copy the rest: add result, substr(s, i) else: - var a = initSkipTable(sub) + var a {.noinit.}: SkipTable + initSkipTable(a, sub) let last = s.high var i = 0 while true: @@ -2178,8 +2080,7 @@ func replace*(s, sub: string, by = ""): string {.rtl, func replace*(s: string, sub, by: char): string {.rtl, extern: "nsuReplaceChar".} = - ## Replaces every occurrence of the character `sub` in `s` with the character - ## `by`. + ## Replaces `sub` in `s` by the character `by`. ## ## Optimized version of `replace <#replace,string,string,string>`_ for ## characters. @@ -2197,15 +2098,16 @@ func replace*(s: string, sub, by: char): string {.rtl, func replaceWord*(s, sub: string, by = ""): string {.rtl, extern: "nsuReplaceWord".} = - ## Replaces every occurrence of the string `sub` in `s` with the string `by`. + ## Replaces `sub` in `s` by the string `by`. ## ## Each occurrence of `sub` has to be surrounded by word boundaries ## (comparable to `\b` in regular expressions), otherwise it is not ## replaced. if sub.len == 0: return s const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} + var a {.noinit.}: SkipTable result = "" - var a = initSkipTable(sub) + initSkipTable(a, sub) var i = 0 let last = s.high let sublen = sub.len @@ -2271,7 +2173,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: @@ -2285,7 +2187,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) @@ -2296,23 +2198,15 @@ func insertSep*(s: string, sep = '_', digits = 3): string {.rtl, func escape*(s: string, prefix = "\"", suffix = "\""): string {.rtl, extern: "nsuEscape".} = - ## Escapes a string `s`. - ## - ## .. note:: The escaping scheme is different from - ## `system.addEscapedChar`. - ## - ## * replaces `'\0'..'\31'` and `'\127'..'\255'` by `\xHH` where `HH` is its hexadecimal value - ## * replaces ``\`` by `\\` - ## * replaces `'` by `\'` - ## * replaces `"` by `\"` + ## Escapes a string `s`. See `system.addEscapedChar + ## `_ for the escaping scheme. ## ## The resulting string is prefixed with `prefix` and suffixed with `suffix`. ## Both may be empty strings. ## ## See also: - ## * `addEscapedChar proc`_ ## * `unescape func<#unescape,string,string,string>`_ for the opposite - ## operation + ## operation result = newStringOfCap(s.len + s.len shr 2) result.add(prefix) for c in items(s): @@ -2386,7 +2280,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 @@ -2413,63 +2307,66 @@ func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, doAssert x.formatBiggestFloat() == "123.4560000000000" doAssert x.formatBiggestFloat(ffDecimal, 4) == "123.4560" doAssert x.formatBiggestFloat(ffScientific, 2) == "1.23e+02" - when nimvm: - discard "implemented in the vmops" + when defined(js): + var precision = precision + if precision == -1: + # use the same default precision as c_sprintf + precision = 6 + var res: cstring + case format + of ffDefault: + {.emit: "`res` = `f`.toString();".} + of ffDecimal: + {.emit: "`res` = `f`.toFixed(`precision`);".} + of ffScientific: + {.emit: "`res` = `f`.toExponential(`precision`);".} + result = $res + if 1.0 / f == -Inf: + # JavaScript removes the "-" from negative Zero, add it back here + result = "-" & $res + for i in 0 ..< result.len: + # Depending on the locale either dot or comma is produced, + # but nothing else is possible: + if result[i] in {'.', ','}: result[i] = decimalSep else: - when defined(js): - var precision = precision - if precision == -1: - # use the same default precision as c_sprintf - precision = 6 - var res: cstring - case format - of ffDefault: - {.emit: "`res` = `f`.toString();".} - of ffDecimal: - {.emit: "`res` = `f`.toFixed(`precision`);".} - of ffScientific: - {.emit: "`res` = `f`.toExponential(`precision`);".} - result = $res - if 1.0 / f == -Inf: - # JavaScript removes the "-" from negative Zero, add it back here - result = "-" & $res - for i in 0 ..< result.len: - # Depending on the locale either dot or comma is produced, - # but nothing else is possible: - if result[i] in {'.', ','}: result[i] = decimalSep - else: - const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e'] - var - frmtstr {.noinit.}: array[0..5, char] - buf {.noinit.}: array[0..2500, char] - L: cint - frmtstr[0] = '%' - if precision >= 0: - frmtstr[1] = '#' - frmtstr[2] = '.' - frmtstr[3] = '*' - frmtstr[4] = floatFormatToChar[format] - frmtstr[5] = '\0' - L = c_sprintf(cast[cstring](addr buf), cast[cstring](addr frmtstr), precision, f) + const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e'] + var + frmtstr {.noinit.}: array[0..5, char] + buf {.noinit.}: array[0..2500, char] + L: cint + frmtstr[0] = '%' + if precision >= 0: + frmtstr[1] = '#' + frmtstr[2] = '.' + frmtstr[3] = '*' + frmtstr[4] = floatFormatToChar[format] + frmtstr[5] = '\0' + when defined(nimNoArrayToCstringConversion): + L = c_sprintf(addr buf, addr frmtstr, precision, f) else: - frmtstr[1] = floatFormatToChar[format] - frmtstr[2] = '\0' - L = c_sprintf(cast[cstring](addr buf), cast[cstring](addr frmtstr), f) - result = newString(L) - for i in 0 ..< L: - # Depending on the locale either dot or comma is produced, - # but nothing else is possible: - if buf[i] in {'.', ','}: result[i] = decimalSep - else: result[i] = buf[i] - when defined(windows): - # VS pre 2015 violates the C standard: "The exponent always contains at - # least two digits, and only as many more digits as necessary to - # represent the exponent." [C11 §7.21.6.1] - # The following post-processing fixes this behavior. - if result.len > 4 and result[^4] == '+' and result[^3] == '0': - result[^3] = result[^2] - result[^2] = result[^1] - result.setLen(result.len - 1) + L = c_sprintf(buf, frmtstr, precision, f) + else: + frmtstr[1] = floatFormatToChar[format] + frmtstr[2] = '\0' + when defined(nimNoArrayToCstringConversion): + L = c_sprintf(addr buf, addr frmtstr, f) + else: + L = c_sprintf(buf, frmtstr, f) + result = newString(L) + for i in 0 ..< L: + # Depending on the locale either dot or comma is produced, + # but nothing else is possible: + if buf[i] in {'.', ','}: result[i] = decimalSep + else: result[i] = buf[i] + when defined(windows): + # VS pre 2015 violates the C standard: "The exponent always contains at + # least two digits, and only as many more digits as necessary to + # represent the exponent." [C11 §7.21.6.1] + # The following post-processing fixes this behavior. + if result.len > 4 and result[^4] == '+' and result[^3] == '0': + result[^3] = result[^2] + result[^2] = result[^1] + result.setLen(result.len - 1) func formatFloat*(f: float, format: FloatFormatMode = ffDefault, precision: range[-1..32] = 16; decimalSep = '.'): string {. @@ -2509,8 +2406,7 @@ func trimZeros*(x: var string; decimalSep = '.') = var pos = last while pos >= 0 and x[pos] == '0': dec(pos) if pos > sPos: inc(pos) - if last >= pos: - x.delete(pos..last) + x.delete(pos, last) type BinaryPrefixMode* = enum ## The different names for binary prefixes. @@ -2721,8 +2617,8 @@ func findNormalized(x: string, inArray: openArray[string]): int = # security hole... return -1 -func invalidFormatString(formatstr: string) {.noinline.} = - raise newException(ValueError, "invalid format string: " & formatstr) +func invalidFormatString() {.noinline.} = + raise newException(ValueError, "invalid format string") func addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.rtl, extern: "nsuAddf".} = @@ -2734,7 +2630,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(formatstr) + if num > a.high: invalidFormatString() add s, a[num] inc i, 2 inc num @@ -2750,7 +2646,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(formatstr) + if idx < 0 or idx > a.high: invalidFormatString() add s, a[idx] of '{': var j = i+2 @@ -2767,27 +2663,27 @@ 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(formatstr) + if idx < 0 or idx > a.high: invalidFormatString() 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(formatstr) + else: invalidFormatString() 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(formatstr) + else: invalidFormatString() i = j else: - invalidFormatString(formatstr) + invalidFormatString() else: add s, formatstr[i] inc(i) -func `%`*(formatstr: string, a: openArray[string]): string {.rtl, +func `%` *(formatstr: string, a: openArray[string]): string {.rtl, extern: "nsuFormatOpenArray".} = ## Interpolates a format string with the values from `a`. ## @@ -2835,7 +2731,7 @@ func `%`*(formatstr: string, a: openArray[string]): string {.rtl, result = newStringOfCap(formatstr.len + a.len shl 4) addf(result, formatstr, a) -func `%`*(formatstr, a: string): string {.rtl, +func `%` *(formatstr, a: string): string {.rtl, extern: "nsuFormatSingleElem".} = ## This is the same as `formatstr % [a]` (see ## `% func<#%25,string,openArray[string]>`_). @@ -2864,7 +2760,6 @@ func strip*(s: string, leading = true, trailing = true, ## If both are false, the string is returned unchanged. ## ## See also: - ## * `strip proc`_ Inplace version. ## * `stripLineEnd func<#stripLineEnd,string>`_ runnableExamples: let a = " vhellov " @@ -2891,7 +2786,7 @@ func strip*(s: string, leading = true, trailing = true, result = substr(s, first, last) func stripLineEnd*(s: var string) = - ## Strips one of these suffixes from `s` in-place: + ## Returns `s` stripped from one of these suffixes: ## `\r, \n, \r\n, \f, \v` (at most once instance). ## For example, can be useful in conjunction with `osproc.execCmdEx`. ## aka: `chomp`:idx: diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index d8e00d1f8..686729515 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -11,7 +11,7 @@ ## macro system. import std/private/since -import macros +import macros, typetraits proc checkPragma(ex, prag: var NimNode) = since (1, 3): @@ -19,7 +19,7 @@ proc checkPragma(ex, prag: var NimNode) = prag = ex[1] ex = ex[0] -proc createProcType(p, b: NimNode): NimNode = +proc createProcType(p, b: NimNode): NimNode {.compileTime.} = result = newNimNode(nnkProcTy) var formalParams = newNimNode(nnkFormalParams).add(b) @@ -53,25 +53,22 @@ proc createProcType(p, b: NimNode): NimNode = result.add prag macro `=>`*(p, b: untyped): untyped = - ## Syntax sugar for anonymous procedures. It also supports pragmas. - ## - ## .. warning:: Semicolons can not be used to separate procedure arguments. + ## Syntax sugar for anonymous procedures. + ## It also supports pragmas. runnableExamples: - proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2) + proc passTwoAndTwo(f: (int, int) -> int): int = + f(2, 2) - assert passTwoAndTwo((x, y) => x + y) == 4 + doAssert passTwoAndTwo((x, y) => x + y) == 4 type Bot = object - call: (string {.noSideEffect.} -> string) + call: proc (name: string): string {.noSideEffect.} var myBot = Bot() myBot.call = (name: string) {.noSideEffect.} => "Hello " & name & ", I'm a bot." - assert myBot.call("John") == "Hello John, I'm a bot." - - let f = () => (discard) # simplest proc that returns void - f() + doAssert myBot.call("John") == "Hello John, I'm a bot." var params = @[ident"auto"] @@ -88,6 +85,16 @@ macro `=>`*(p, b: untyped): untyped = checkPragma(p, pragma) # check again after -> transform + since (1, 3): + if p.kind in {nnkCall, nnkObjConstr}: + # foo(x, y) => x + y + kind = nnkProcDef + name = p[0] + let newP = newNimNode(nnkPar) + for i in 1..`*(p, b: untyped): untyped = else: error("Incorrect procedure parameter.", c) params.add(identDefs) - of nnkIdent, nnkOpenSymChoice, nnkClosedSymChoice, nnkSym: + of nnkIdent: var identDefs = newNimNode(nnkIdentDefs) - identDefs.add(ident $p) + identDefs.add(p) identDefs.add(ident"auto") identDefs.add(newEmptyNode()) params.add(identDefs) @@ -134,21 +141,17 @@ macro `=>`*(p, b: untyped): untyped = procType = kind) macro `->`*(p, b: untyped): untyped = - ## Syntax sugar for procedure types. It also supports pragmas. + ## Syntax sugar for procedure types. ## - ## .. warning:: Semicolons can not be used to separate procedure arguments. - runnableExamples: - proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2) - # is the same as: - # proc passTwoAndTwo(f: proc (x, y: int): int): int = f(2, 2) - - assert passTwoAndTwo((x, y) => x + y) == 4 - - proc passOne(f: (int {.noSideEffect.} -> int)): int = f(1) - # is the same as: - # proc passOne(f: proc (x: int): int {.noSideEffect.}): int = f(1) - - assert passOne(x {.noSideEffect.} => x + 1) == 2 + ## .. code-block:: nim + ## + ## proc pass2(f: (float, float) -> float): float = + ## f(2, 2) + ## + ## # is the same as: + ## + ## proc pass2(f: proc (x, y: float): float): float = + ## f(2, 2) result = createProcType(p, b) @@ -158,44 +161,19 @@ macro dump*(x: untyped): untyped = ## of the tree representing the expression - as it would appear in ## source code - together with the value of the expression. ## - ## See also: `dumpToString` which is more convenient and useful since - ## it expands intermediate templates/macros, returns a string instead of - ## calling `echo`, and works with statements and expressions. - runnableExamples("-r:off"): - let - x = 10 - y = 20 - dump(x + y) # prints: `x + y = 30` - + ## As an example, + ## + ## .. code-block:: nim + ## let + ## x = 10 + ## y = 20 + ## dump(x + y) + ## + ## will print ``x + y = 30``. let s = x.toStrLit - result = quote do: + let r = quote do: debugEcho `s`, " = ", `x` - -macro dumpToStringImpl(s: static string, x: typed): string = - let s2 = x.toStrLit - if x.typeKind == ntyVoid: - result = quote do: - `s` & ": " & `s2` - else: - result = quote do: - `s` & ": " & `s2` & " = " & $`x` - -macro dumpToString*(x: untyped): string = - ## Returns the content of a statement or expression `x` after semantic analysis, - ## useful for debugging. - runnableExamples: - const a = 1 - let x = 10 - assert dumpToString(a + 2) == "a + 2: 3 = 3" - assert dumpToString(a + x) == "a + x: 1 + x = 11" - template square(x): untyped = x * x - assert dumpToString(square(x)) == "square(x): x * x = 100" - assert not compiles dumpToString(1 + nonexistent) - import std/strutils - assert "failedAssertImpl" in dumpToString(assert true) # example with a statement - result = newCall(bindSym"dumpToStringImpl") - result.add newLit repr(x) - result.add x + return r # TODO: consider exporting this in macros.nim proc freshIdentNodes(ast: NimNode): NimNode = @@ -215,41 +193,34 @@ proc freshIdentNodes(ast: NimNode): NimNode = macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).} = ## Useful when creating a closure in a loop to capture some local loop variables - ## by their current iteration values. - runnableExamples: - import std/strformat - - var myClosure: () -> string - for i in 5..7: - for j in 7..9: - if i * j == 42: - capture i, j: - myClosure = () => fmt"{i} * {j} = 42" - assert myClosure() == "6 * 7 = 42" - + ## by their current iteration values. Example: + ## + ## .. code-block:: Nim + ## import strformat, sequtils, sugar + ## var myClosure : proc() + ## for i in 5..7: + ## for j in 7..9: + ## if i * j == 42: + ## capture i, j: + ## myClosure = proc () = echo fmt"{i} * {j} = 42" + ## myClosure() # output: 6 * 7 == 42 + ## let m = @[proc (s: string): string = "to " & s, proc (s: string): string = "not to " & s] + ## var l = m.mapIt(capture(it, proc (s: string): string = it(s))) + ## let r = l.mapIt(it("be")) + ## echo r[0] & ", or " & r[1] # output: to be, or not to be var params = @[newIdentNode("auto")] let locals = if locals.len == 1 and locals[0].kind == nnkBracket: locals[0] else: locals for arg in locals: - proc getIdent(n: NimNode): NimNode = - case n.kind - of nnkIdent, nnkSym: - let nStr = n.strVal - if nStr == "result": - error("The variable name cannot be `result`!", n) - result = ident(nStr) - of nnkHiddenDeref: result = n[0].getIdent() - else: - error("The argument to be captured `" & n.repr & "` is not a pure identifier. " & - "It is an unsupported `" & $n.kind & "` node.", n) - let argName = getIdent(arg) - params.add(newIdentDefs(argName, freshIdentNodes getTypeInst arg)) + if arg.strVal == "result": + error("The variable name cannot be `result`!", arg) + params.add(newIdentDefs(ident(arg.strVal), freshIdentNodes getTypeInst arg)) result = newNimNode(nnkCall) - result.add(newProc(newEmptyNode(), params, body, nnkLambda)) + result.add(newProc(newEmptyNode(), params, body, nnkProcDef)) for arg in locals: result.add(arg) since (1, 1): - import std/private/underscored_calls + import std / private / underscored_calls macro dup*[T](arg: T, calls: varargs[untyped]): T = ## Turns an `in-place`:idx: algorithm into one that works on @@ -257,39 +228,41 @@ since (1, 1): ## ## This macro also allows for (otherwise in-place) function chaining. ## - ## **Since:** Version 1.2. + ## **Since**: Version 1.2. runnableExamples: - import std/algorithm - - let a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - assert a.dup(sort) == sorted(a) + import algorithm + var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + doAssert a.dup(sort) == sorted(a) # Chaining: var aCopy = a aCopy.insert(10) - assert a.dup(insert(10), sort) == sorted(aCopy) - let s1 = "abc" - let s2 = "xyz" - assert s1 & s2 == s1.dup(&= s2) + doAssert a.dup(insert(10), sort) == sorted(aCopy) - # An underscore (_) can be used to denote the place of the argument you're passing: - assert "".dup(addQuoted(_, "foo")) == "\"foo\"" - # but `_` is optional here since the substitution is in 1st position: - assert "".dup(addQuoted("foo")) == "\"foo\"" + var s1 = "abc" + var s2 = "xyz" + doAssert s1 & s2 == s1.dup(&= s2) proc makePalindrome(s: var string) = for i in countdown(s.len-2, 0): s.add(s[i]) - let c = "xyz" + var c = "xyz" + + # An underscore (_) can be used to denote the place of the argument you're passing: + doAssert "".dup(addQuoted(_, "foo")) == "\"foo\"" + # but `_` is optional here since the substitution is in 1st position: + doAssert "".dup(addQuoted("foo")) == "\"foo\"" # chaining: - let d = dup c: + # b = "xyz" + var d = dup c: makePalindrome # xyzyx sort(_, SortOrder.Descending) # zyyxx makePalindrome # zyyxxxyyz - assert d == "zyyxxxyyz" + + doAssert d == "zyyxxxyyz" result = newNimNode(nnkStmtListExpr, arg) let tmp = genSym(nskVar, "dupResult") @@ -371,58 +344,51 @@ macro collect*(init, body: untyped): untyped {.since: (1, 1).} = # analyse the body, find the deepest expression 'it' and replace it via # 'result.add it' runnableExamples: - import std/[sets, tables] - + import sets, tables let data = @["bird", "word"] - ## seq: let k = collect(newSeq): for i, d in data.pairs: if i mod 2 == 0: d - assert k == @["bird"] + assert k == @["bird"] ## seq with initialSize: let x = collect(newSeqOfCap(4)): for i, d in data.pairs: if i mod 2 == 0: d + assert x == @["bird"] - ## HashSet: - let y = collect(initHashSet()): + let y = initHashSet.collect: for d in data.items: {d} - assert y == data.toHashSet + assert y == data.toHashSet ## Table: let z = collect(initTable(2)): for i, d in data.pairs: {i: d} - assert z == {0: "bird", 1: "word"}.toTable + assert z == {0: "bird", 1: "word"}.toTable result = collectImpl(init, body) 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] + import sets, tables + # Seq: let data = @["bird", "word"] - - # seq: let k = collect: for i, d in data.pairs: if i mod 2 == 0: d - assert k == @["bird"] + assert k == @["bird"] ## HashSet: let n = collect: for d in data.items: {d} - assert n == data.toHashSet + assert n == data.toHashSet ## Table: let m = collect: for i, d in data.pairs: {i: d} - assert m == {0: "bird", 1: "word"}.toTable - result = collectImpl(nil, body) + assert m == {0: "bird", 1: "word"}.toTable + result = collectImpl(nil, body) \ No newline at end of file diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index daf470b09..3bda5b7aa 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -12,51 +12,9 @@ ## sequences and does not depend on any other module, on Windows it uses the ## Windows API. ## Changing the style is permanent even after program termination! Use the -## code `exitprocs.addExitProc(resetAttributes)` to restore the defaults. +## code ``system.addQuitProc(resetAttributes)`` to restore the defaults. ## Similarly, if you hide the cursor, make sure to unhide it with -## `showCursor` before quitting. -## -## Progress bar -## ============ -## -## Basic progress bar example: -runnableExamples("-r:off"): - import std/[os, strutils] - - for i in 0..100: - stdout.styledWriteLine(fgRed, "0% ", fgWhite, '#'.repeat i, if i > 50: fgGreen else: fgYellow, "\t", $i , "%") - sleep 42 - cursorUp 1 - eraseLine() - - stdout.resetAttributes() - -##[ -## Playing with colorful and styled text -]## - -## Procs like `styledWriteLine`, `styledEcho` etc. have a temporary effect on -## text parameters. Style parameters only affect the text parameter right after them. -## After being called, these procs will reset the default style of the terminal. -## While `setBackGroundColor`, `setForeGroundColor` etc. have a lasting -## influence on the terminal, you can use `resetAttributes` to -## reset the default style of the terminal. -runnableExamples("-r:off"): - stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ") - stdout.styledWriteLine(fgRed, "red text ") - stdout.styledWriteLine(fgWhite, bgRed, "white text in red background") - stdout.styledWriteLine(" ordinary text without style ") - - stdout.setBackGroundColor(bgCyan, true) - stdout.setForeGroundColor(fgBlue) - stdout.write("blue text in cyan background") - stdout.resetAttributes() - - # You can specify multiple text parameters. Style parameters - # only affect the text parameter right after them. - styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!" - - stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text") +## ``showCursor`` before quitting. import macros import strformat @@ -66,9 +24,6 @@ import colors when defined(windows): import winlean -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - type PTerminal = ref object trueColorIsSupported: bool @@ -93,8 +48,8 @@ proc getTerminal(): PTerminal {.inline.} = result = gTerm const - fgPrefix = "\e[38;2;" - bgPrefix = "\e[48;2;" + fgPrefix = "\x1b[38;2;" + bgPrefix = "\x1b[48;2;" ansiResetCode* = "\e[0m" stylePrefix = "\e[" @@ -173,7 +128,6 @@ when defined(windows): return 0 proc terminalWidth*(): int = - ## Returns the terminal width in columns. var w: int = 0 w = terminalWidthIoctl([getStdHandle(STD_INPUT_HANDLE), getStdHandle(STD_OUTPUT_HANDLE), @@ -182,7 +136,6 @@ when defined(windows): return 80 proc terminalHeight*(): int = - ## Returns the terminal height in rows. var h: int = 0 h = terminalHeightIoctl([getStdHandle(STD_INPUT_HANDLE), getStdHandle(STD_OUTPUT_HANDLE), @@ -300,7 +253,7 @@ else: discard close(fd) if w > 0: return w var s = getEnv("COLUMNS") #Try standard env var - if len(s) > 0 and parseInt(s, w) > 0 and w > 0: + if len(s) > 0 and parseInt(string(s), w) > 0 and w > 0: return w return 80 #Finally default to venerable value @@ -318,7 +271,7 @@ else: discard close(fd) if h > 0: return h var s = getEnv("LINES") # Try standard env var - if len(s) > 0 and parseInt(s, h) > 0 and h > 0: + if len(s) > 0 and parseInt(string(s), h) > 0 and h > 0: return h return 0 # Could not determine height @@ -379,7 +332,7 @@ when defined(windows): proc setCursorYPos*(f: File, y: int) = ## Sets the terminal's cursor to the y position. ## The x position is not changed. - ## .. warning:: This is not supported on UNIX! + ## **Warning**: This is not supported on UNIX! when defined(windows): let h = conHandle(f) var scrbuf: CONSOLE_SCREEN_BUFFER_INFO @@ -394,9 +347,6 @@ when defined(windows): proc cursorUp*(f: File, count = 1) = ## Moves the cursor up by `count` rows. - runnableExamples("-r:off"): - stdout.cursorUp(2) - write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this when defined(windows): let h = conHandle(f) var p = getCursorPos(h) @@ -407,9 +357,6 @@ proc cursorUp*(f: File, count = 1) = proc cursorDown*(f: File, count = 1) = ## Moves the cursor down by `count` rows. - runnableExamples("-r:off"): - stdout.cursorDown(2) - write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this when defined(windows): let h = conHandle(f) var p = getCursorPos(h) @@ -420,9 +367,6 @@ proc cursorDown*(f: File, count = 1) = proc cursorForward*(f: File, count = 1) = ## Moves the cursor forward by `count` columns. - runnableExamples("-r:off"): - stdout.cursorForward(2) - write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this when defined(windows): let h = conHandle(f) var p = getCursorPos(h) @@ -433,9 +377,6 @@ proc cursorForward*(f: File, count = 1) = proc cursorBackward*(f: File, count = 1) = ## Moves the cursor backward by `count` columns. - runnableExamples("-r:off"): - stdout.cursorBackward(2) - write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this when defined(windows): let h = conHandle(f) var p = getCursorPos(h) @@ -477,9 +418,6 @@ else: proc eraseLine*(f: File) = ## Erases the entire current line. - runnableExamples("-r:off"): - write(stdout, "never mind") - stdout.eraseLine() # nothing will be printed on the screen when defined(windows): let h = conHandle(f) var scrbuf: CONSOLE_SCREEN_BUFFER_INFO @@ -542,7 +480,7 @@ proc resetAttributes*(f: File) = gBG = 0 type - Style* = enum ## Different styles for text output. + Style* = enum ## different styles for text output styleBright = 1, ## bright text styleDim, ## dim text styleItalic, ## italic (or reverse on terminals not supporting) @@ -596,7 +534,7 @@ proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = stdout.write(ansiStyleCode(gBG)) type - ForegroundColor* = enum ## Terminal's foreground colors. + ForegroundColor* = enum ## terminal's foreground colors fgBlack = 30, ## black fgRed, ## red fgGreen, ## green @@ -605,10 +543,10 @@ type fgMagenta, ## magenta fgCyan, ## cyan fgWhite, ## white - fg8Bit, ## 256-color (not supported, see `enableTrueColors` instead.) + fg8Bit, ## 256-color (not supported, see ``enableTrueColors`` instead.) fgDefault ## default terminal foreground color - BackgroundColor* = enum ## Terminal's background colors. + BackgroundColor* = enum ## terminal's background colors bgBlack = 40, ## black bgRed, ## red bgGreen, ## green @@ -617,7 +555,7 @@ type bgMagenta, ## magenta bgCyan, ## cyan bgWhite, ## white - bg8Bit, ## 256-color (not supported, see `enableTrueColors` instead.) + bg8Bit, ## 256-color (not supported, see ``enableTrueColors`` instead.) bgDefault ## default terminal background color when defined(windows): @@ -641,12 +579,12 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright = false) = (FOREGROUND_RED or FOREGROUND_BLUE), (FOREGROUND_BLUE or FOREGROUND_GREEN), (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED), - 0, # fg8Bit not supported, see `enableTrueColors` instead. + 0, # fg8Bit not supported, see ``enableTrueColors`` instead. 0] # unused if fg == fgDefault: - discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](defaultForegroundColor))) + discard setConsoleTextAttribute(h, toU16(old or defaultForegroundColor)) else: - discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](lookup[fg]))) + discard setConsoleTextAttribute(h, toU16(old or lookup[fg])) else: gFG = ord(fg) if bright: inc(gFG, 60) @@ -670,12 +608,12 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright = false) = (BACKGROUND_RED or BACKGROUND_BLUE), (BACKGROUND_BLUE or BACKGROUND_GREEN), (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED), - 0, # bg8Bit not supported, see `enableTrueColors` instead. + 0, # bg8Bit not supported, see ``enableTrueColors`` instead. 0] # unused if bg == bgDefault: - discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](defaultBackgroundColor))) + discard setConsoleTextAttribute(h, toU16(old or defaultBackgroundColor)) else: - discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](lookup[bg]))) + discard setConsoleTextAttribute(h, toU16(old or lookup[bg])) else: gBG = ord(bg) if bright: inc(gBG, 60) @@ -759,14 +697,18 @@ template styledEchoProcessArg(f: File, cmd: TerminalCmd) = term.fgSetColor = cmd == fgColor macro styledWrite*(f: File, m: varargs[typed]): untyped = - ## Similar to `write`, but treating terminal style arguments specially. - ## When some argument is `Style`, `set[Style]`, `ForegroundColor`, - ## `BackgroundColor` or `TerminalCmd` then it is not sent directly to - ## `f`, but instead corresponding terminal style proc is called. - runnableExamples("-r:off"): - stdout.styledWrite(fgRed, "red text ") - stdout.styledWrite(fgGreen, "green text") - + ## Similar to ``write``, but treating terminal style arguments specially. + ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, + ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to + ## ``f``, but instead corresponding terminal style proc is called. + ## + ## Example: + ## + ## .. code-block:: nim + ## + ## stdout.styledWrite(fgRed, "red text ") + ## stdout.styledWrite(fgGreen, "green text") + ## var reset = false result = newNimNode(nnkStmtList) @@ -788,20 +730,24 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped = if reset: result.add(newCall(bindSym"resetAttributes", f)) template styledWriteLine*(f: File, args: varargs[untyped]) = - ## Calls `styledWrite` and appends a newline at the end. - runnableExamples: - proc error(msg: string) = - styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) - + ## Calls ``styledWrite`` and appends a newline at the end. + ## + ## Example: + ## + ## .. code-block:: nim + ## + ## proc error(msg: string) = + ## styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) + ## styledWrite(f, args) write(f, "\n") template styledEcho*(args: varargs[untyped]) = - ## Echoes styles arguments to stdout using `styledWriteLine`. + ## Echoes styles arguments to stdout using ``styledWriteLine``. stdout.styledWriteLine(args) proc getch*(): char = - ## Reads a single character from the terminal, blocking until it is entered. + ## Read a single character from the terminal, blocking until it is entered. ## The character is not printed to the terminal. when defined(windows): let fd = getStdHandle(STD_INPUT_HANDLE) @@ -823,12 +769,14 @@ proc getch*(): char = discard fd.tcSetAttr(TCSADRAIN, addr oldMode) when defined(windows): - proc readPasswordFromStdin*(prompt: string, password: var string): + from unicode import toUTF8, Rune, runeLenAt + + proc readPasswordFromStdin*(prompt: string, password: var TaintedString): bool {.tags: [ReadIOEffect, WriteIOEffect].} = ## Reads a `password` from stdin without printing it. `password` must not - ## be `nil`! Returns `false` if the end of the file has been reached, - ## `true` otherwise. - password.setLen(0) + ## be ``nil``! Returns ``false`` if the end of the file has been reached, + ## ``true`` otherwise. + password.string.setLen(0) stdout.write(prompt) let hi = createFileA("CONIN$", GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0) @@ -849,9 +797,9 @@ when defined(windows): else: import termios - proc readPasswordFromStdin*(prompt: string, password: var string): + proc readPasswordFromStdin*(prompt: string, password: var TaintedString): bool {.tags: [ReadIOEffect, WriteIOEffect].} = - password.setLen(0) + password.string.setLen(0) let fd = stdin.getFileHandle() var cur, old: Termios discard fd.tcGetAttr(cur.addr) @@ -863,9 +811,9 @@ else: stdout.write "\n" discard fd.tcSetAttr(TCSADRAIN, old.addr) -proc readPasswordFromStdin*(prompt = "password: "): string = +proc readPasswordFromStdin*(prompt = "password: "): TaintedString = ## Reads a password from stdin without printing it. - result = "" + result = TaintedString("") discard readPasswordFromStdin(prompt, result) @@ -895,7 +843,7 @@ template setBackgroundColor*(color: Color) = proc resetAttributes*() {.noconv.} = ## Resets all attributes on stdout. ## It is advisable to register this as a quit proc with - ## `exitprocs.addExitProc(resetAttributes)`. + ## ``system.addQuitProc(resetAttributes)``. resetAttributes(stdout) proc isTrueColorSupported*(): bool = @@ -906,7 +854,7 @@ when defined(windows): import os proc enableTrueColors*() = - ## Enables true color. + ## Enable true color. var term = getTerminal() when defined(windows): var @@ -934,12 +882,12 @@ proc enableTrueColors*() = else: term.trueColorIsEnabled = true else: - term.trueColorIsSupported = getEnv("COLORTERM").toLowerAscii() in [ + term.trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in [ "truecolor", "24bit"] term.trueColorIsEnabled = term.trueColorIsSupported proc disableTrueColors*() = - ## Disables true color. + ## Disable true color. var term = getTerminal() when defined(windows): if term.trueColorIsSupported: @@ -956,3 +904,51 @@ proc newTerminal(): owned(PTerminal) = new result when defined(windows): initTerminal(result) + +when not defined(testing) and isMainModule: + assert ansiStyleCode(styleBright) == "\e[1m" + assert ansiStyleCode(styleStrikethrough) == "\e[9m" + #system.addQuitProc(resetAttributes) + write(stdout, "never mind") + stdout.eraseLine() + stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ") + stdout.styledWriteLine("italic text ", {styleItalic}) + stdout.setBackGroundColor(bgCyan, true) + stdout.setForeGroundColor(fgBlue) + stdout.write("blue text in cyan background") + stdout.resetAttributes() + echo "" + stdout.writeLine("ordinary text") + echo "more ordinary text" + styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!" + echo "ordinary text again" + styledEcho styleBright, fgRed, "[FAIL]", resetStyle, fgRed, " Nay :(" + echo "ordinary text again" + setForeGroundColor(fgGreen) + echo "green text" + echo "more green text" + setForeGroundColor(fgBlue) + echo "blue text" + resetAttributes() + echo "ordinary text" + + stdout.styledWriteLine(fgRed, "red text ") + stdout.styledWriteLine(fgWhite, bgRed, "white text in red background") + stdout.styledWriteLine(" ordinary text ") + stdout.styledWriteLine(fgGreen, "green text") + + writeStyled("underscored text", {styleUnderscore}) + stdout.styledWrite(fgRed, " red text ") + writeStyled("bright text ", {styleBright}) + echo "ordinary text" + + stdout.styledWrite(fgRed, "red text ") + stdout.styledWrite(fgWhite, bgRed, "white text in red background") + stdout.styledWrite(" ordinary text ") + stdout.styledWrite(fgGreen, "green text") + echo "" + echo "ordinary text" + stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text") + stdout.styledWriteLine(bgYellow, "text in yellow bg", styleBright, + " bold text in yellow bg", bgDefault, " bold text") + echo "ordinary text" diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 138f2d9ec..35dda131b 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -8,20 +8,20 @@ # ##[ - The `times` module contains routines and types for dealing with time using + The ``times`` module contains routines and types for dealing with time using the `proleptic Gregorian calendar`_. It's also available for the `JavaScript target `_. - Although the `times` module supports nanosecond time resolution, the - resolution used by `getTime()` depends on the platform and backend + Although the ``times`` module supports nanosecond time resolution, the + resolution used by ``getTime()`` depends on the platform and backend (JS is limited to millisecond precision). Examples ======== .. code-block:: nim - import std/[times, os] + import times, os # Simple benchmarking let time = cpuTime() sleep(100) # Replace this with something to be timed @@ -41,8 +41,8 @@ Parsing and Formatting Dates ============================ - The `DateTime` type can be parsed and formatted using the different - `parse` and `format` procedures. + The ``DateTime`` type can be parsed and formatted using the different + ``parse`` and ``format`` procedures. .. code-block:: nim @@ -51,128 +51,124 @@ The different format patterns that are supported are documented below. - =========== ================================================================================= ============================================== - Pattern Description Example - =========== ================================================================================= ============================================== - `d` Numeric value representing the day of the month, | `1/04/2012 -> 1` - it will be either one or two digits long. | `21/04/2012 -> 21` - `dd` Same as above, but is always two digits. | `1/04/2012 -> 01` - | `21/04/2012 -> 21` - `ddd` Three letter string which indicates the day of the week. | `Saturday -> Sat` - | `Monday -> Mon` - `dddd` Full string for the day of the week. | `Saturday -> Saturday` - | `Monday -> Monday` - `h` The hours in one digit if possible. Ranging from 1-12. | `5pm -> 5` - | `2am -> 2` - `hh` The hours in two digits always. If the hour is one digit, 0 is prepended. | `5pm -> 05` - | `11am -> 11` - `H` The hours in one digit if possible, ranging from 0-23. | `5pm -> 17` - | `2am -> 2` - `HH` The hours in two digits always. 0 is prepended if the hour is one digit. | `5pm -> 17` - | `2am -> 02` - `m` The minutes in one digit if possible. | `5:30 -> 30` - | `2:01 -> 1` - `mm` Same as above but always two digits, 0 is prepended if the minute is one digit. | `5:30 -> 30` - | `2:01 -> 01` - `M` The month in one digit if possible. | `September -> 9` - | `December -> 12` - `MM` The month in two digits always. 0 is prepended if the month value is one digit. | `September -> 09` - | `December -> 12` - `MMM` Abbreviated three-letter form of the month. | `September -> Sep` - | `December -> Dec` - `MMMM` Full month string, properly capitalized. | `September -> September` - `s` Seconds as one digit if possible. | `00:00:06 -> 6` - `ss` Same as above but always two digits. 0 is prepended if the second is one digit. | `00:00:06 -> 06` - `t` `A` when time is in the AM. `P` when time is in the PM. | `5pm -> P` - | `2am -> A` - `tt` Same as above, but `AM` and `PM` instead of `A` and `P` respectively. | `5pm -> PM` - | `2am -> AM` - `yy` The last two digits of the year. When parsing, the current century is assumed. | `2012 AD -> 12` - `yyyy` The year, padded to at least four digits. | `2012 AD -> 2012` - Is always positive, even when the year is BC. | `24 AD -> 0024` - When the year is more than four digits, '+' is prepended. | `24 BC -> 00024` - | `12345 AD -> +12345` - `YYYY` The year without any padding. | `2012 AD -> 2012` - Is always positive, even when the year is BC. | `24 AD -> 24` - | `24 BC -> 24` - | `12345 AD -> 12345` - `uuuu` The year, padded to at least four digits. Will be negative when the year is BC. | `2012 AD -> 2012` - When the year is more than four digits, '+' is prepended unless the year is BC. | `24 AD -> 0024` - | `24 BC -> -0023` - | `12345 AD -> +12345` - `UUUU` The year without any padding. Will be negative when the year is BC. | `2012 AD -> 2012` - | `24 AD -> 24` - | `24 BC -> -23` - | `12345 AD -> 12345` - `z` Displays the timezone offset from UTC. | `UTC+7 -> +7` - | `UTC-5 -> -5` - `zz` Same as above but with leading 0. | `UTC+7 -> +07` - | `UTC-5 -> -05` - `zzz` Same as above but with `:mm` where *mm* represents minutes. | `UTC+7 -> +07:00` - | `UTC-5 -> -05:00` - `ZZZ` Same as above but with `mm` where *mm* represents minutes. | `UTC+7 -> +0700` - | `UTC-5 -> -0500` - `zzzz` Same as above but with `:ss` where *ss* represents seconds. | `UTC+7 -> +07:00:00` - | `UTC-5 -> -05:00:00` - `ZZZZ` Same as above but with `ss` where *ss* represents seconds. | `UTC+7 -> +070000` - | `UTC-5 -> -050000` - `g` Era: AD or BC | `300 AD -> AD` - | `300 BC -> BC` - `fff` Milliseconds display | `1000000 nanoseconds -> 1` - `ffffff` Microseconds display | `1000000 nanoseconds -> 1000` - `fffffffff` Nanoseconds display | `1000000 nanoseconds -> 1000000` - =========== ================================================================================= ============================================== + ============= ================================================================================= ================================================ + Pattern Description Example + ============= ================================================================================= ================================================ + ``d`` Numeric value representing the day of the month, | ``1/04/2012 -> 1`` + it will be either one or two digits long. | ``21/04/2012 -> 21`` + ``dd`` Same as above, but is always two digits. | ``1/04/2012 -> 01`` + | ``21/04/2012 -> 21`` + ``ddd`` Three letter string which indicates the day of the week. | ``Saturday -> Sat`` + | ``Monday -> Mon`` + ``dddd`` Full string for the day of the week. | ``Saturday -> Saturday`` + | ``Monday -> Monday`` + ``h`` The hours in one digit if possible. Ranging from 1-12. | ``5pm -> 5`` + | ``2am -> 2`` + ``hh`` The hours in two digits always. If the hour is one digit, 0 is prepended. | ``5pm -> 05`` + | ``11am -> 11`` + ``H`` The hours in one digit if possible, ranging from 0-23. | ``5pm -> 17`` + | ``2am -> 2`` + ``HH`` The hours in two digits always. 0 is prepended if the hour is one digit. | ``5pm -> 17`` + | ``2am -> 02`` + ``m`` The minutes in one digit if possible. | ``5:30 -> 30`` + | ``2:01 -> 1`` + ``mm`` Same as above but always two digits, 0 is prepended if the minute is one digit. | ``5:30 -> 30`` + | ``2:01 -> 01`` + ``M`` The month in one digit if possible. | ``September -> 9`` + | ``December -> 12`` + ``MM`` The month in two digits always. 0 is prepended if the month value is one digit. | ``September -> 09`` + | ``December -> 12`` + ``MMM`` Abbreviated three-letter form of the month. | ``September -> Sep`` + | ``December -> Dec`` + ``MMMM`` Full month string, properly capitalized. | ``September -> September`` + ``s`` Seconds as one digit if possible. | ``00:00:06 -> 6`` + ``ss`` Same as above but always two digits. 0 is prepended if the second is one digit. | ``00:00:06 -> 06`` + ``t`` ``A`` when time is in the AM. ``P`` when time is in the PM. | ``5pm -> P`` + | ``2am -> A`` + ``tt`` Same as above, but ``AM`` and ``PM`` instead of ``A`` and ``P`` respectively. | ``5pm -> PM`` + | ``2am -> AM`` + ``yy`` The last two digits of the year. When parsing, the current century is assumed. | ``2012 AD -> 12`` + ``yyyy`` The year, padded to at least four digits. | ``2012 AD -> 2012`` + Is always positive, even when the year is BC. | ``24 AD -> 0024`` + When the year is more than four digits, '+' is prepended. | ``24 BC -> 00024`` + | ``12345 AD -> +12345`` + ``YYYY`` The year without any padding. | ``2012 AD -> 2012`` + Is always positive, even when the year is BC. | ``24 AD -> 24`` + | ``24 BC -> 24`` + | ``12345 AD -> 12345`` + ``uuuu`` The year, padded to at least four digits. Will be negative when the year is BC. | ``2012 AD -> 2012`` + When the year is more than four digits, '+' is prepended unless the year is BC. | ``24 AD -> 0024`` + | ``24 BC -> -0023`` + | ``12345 AD -> +12345`` + ``UUUU`` The year without any padding. Will be negative when the year is BC. | ``2012 AD -> 2012`` + | ``24 AD -> 24`` + | ``24 BC -> -23`` + | ``12345 AD -> 12345`` + ``z`` Displays the timezone offset from UTC. | ``UTC+7 -> +7`` + | ``UTC-5 -> -5`` + ``zz`` Same as above but with leading 0. | ``UTC+7 -> +07`` + | ``UTC-5 -> -05`` + ``zzz`` Same as above but with ``:mm`` where *mm* represents minutes. | ``UTC+7 -> +07:00`` + | ``UTC-5 -> -05:00`` + ``zzzz`` Same as above but with ``:ss`` where *ss* represents seconds. | ``UTC+7 -> +07:00:00`` + | ``UTC-5 -> -05:00:00`` + ``g`` Era: AD or BC | ``300 AD -> AD`` + | ``300 BC -> BC`` + ``fff`` Milliseconds display | ``1000000 nanoseconds -> 1`` + ``ffffff`` Microseconds display | ``1000000 nanoseconds -> 1000`` + ``fffffffff`` Nanoseconds display | ``1000000 nanoseconds -> 1000000`` + ============= ================================================================================= ================================================ - Other strings can be inserted by putting them in `''`. For example - `hh'->'mm` will give `01->56`. The following characters can be - inserted without quoting them: `:` `-` `(` `)` `/` `[` `]` - `,`. A literal `'` can be specified with `''`. + Other strings can be inserted by putting them in ``''``. For example + ``hh'->'mm`` will give ``01->56``. The following characters can be + inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` + ``,``. A literal ``'`` can be specified with ``''``. However you don't need to necessarily separate format patterns, as an - unambiguous format string like `yyyyMMddhhmmss` is also valid (although + unambiguous format string like ``yyyyMMddhhmmss`` is also valid (although only for years in the range 1..9999). Duration vs TimeInterval ============================ - The `times` module exports two similar types that are both used to + The ``times`` module exports two similar types that are both used to represent some amount of time: `Duration <#Duration>`_ and `TimeInterval <#TimeInterval>`_. This section explains how they differ and when one should be preferred over the - other (short answer: use `Duration` unless support for months and years is + other (short answer: use ``Duration`` unless support for months and years is needed). Duration ---------------------------- - A `Duration` represents a duration of time stored as seconds and - nanoseconds. A `Duration` is always fully normalized, so - `initDuration(hours = 1)` and `initDuration(minutes = 60)` are equivalent. + A ``Duration`` represents a duration of time stored as seconds and + nanoseconds. A ``Duration`` is always fully normalized, so +``initDuration(hours = 1)`` and ``initDuration(minutes = 60)`` are equivalent. - Arithmetic with a `Duration` is very fast, especially when used with the - `Time` type, since it only involves basic arithmetic. Because `Duration` + Arithmetic with a ``Duration`` is very fast, especially when used with the + ``Time`` type, since it only involves basic arithmetic. Because ``Duration`` is more performant and easier to understand it should generally preferred. TimeInterval ---------------------------- - A `TimeInterval` represents an amount of time expressed in calendar + A ``TimeInterval`` represents an amount of time expressed in calendar units, for example "1 year and 2 days". Since some units cannot be normalized (the length of a year is different for leap years for example), - the `TimeInterval` type uses separate fields for every unit. The - `TimeInterval`'s returned from this module generally don't normalize + the ``TimeInterval`` type uses separate fields for every unit. The + ``TimeInterval``'s returned from this module generally don't normalize **anything**, so even units that could be normalized (like seconds, milliseconds and so on) are left untouched. - Arithmetic with a `TimeInterval` can be very slow, because it requires + Arithmetic with a ``TimeInterval`` can be very slow, because it requires timezone information. - Since it's slower and more complex, the `TimeInterval` type should be + Since it's slower and more complex, the ``TimeInterval`` type should be avoided unless the program explicitly needs the features it offers that - `Duration` doesn't have. + ``Duration`` doesn't have. How long is a day? ---------------------------- It should be especially noted that the handling of days differs between - `TimeInterval` and `Duration`. The `Duration` type always treats a day - as exactly 86400 seconds. For `TimeInterval`, it's more complex. + ``TimeInterval`` and ``Duration``. The ``Duration`` type always treats a day + as exactly 86400 seconds. For ``TimeInterval``, it's more complex. As an example, consider the amount of time between these two timestamps, both in the same timezone: @@ -186,8 +182,8 @@ timezones that use daylight savings time. Because of this change, the amount of time that has passed is actually 25 hours. - The `TimeInterval` type uses calendar units, and will say that exactly one - day has passed. The `Duration` type on the other hand normalizes everything + The ``TimeInterval`` type uses calendar units, and will say that exactly one + day has passed. The ``Duration`` type on the other hand normalizes everything to seconds, and will therefore say that 90000 seconds has passed, which is the same as 25 hours. @@ -201,10 +197,6 @@ import strutils, math, options import std/private/since include "system/inclrtl" -when defined(nimPreviewSlimSystem): - import std/assertions - - when defined(js): import jscore @@ -257,9 +249,9 @@ elif defined(windows): proc localtime(a1: var CTime): ptr Tm {.importc, header: "", sideEffect.} type - Month* = enum ## Represents a month. Note that the enum starts at `1`, - ## so `ord(month)` will give the month number in the - ## range `1..12`. + Month* = enum ## Represents a month. Note that the enum starts at ``1``, + ## so ``ord(month)`` will give the month number in the + ## range ``1..12``. mJan = (1, "January") mFeb = "February" mMar = "March" @@ -292,13 +284,6 @@ type YeardayRange* = range[0..365] NanosecondRange* = range[0..999_999_999] - IsoWeekRange* = range[1 .. 53] - ## An ISO 8601 calendar week number. - IsoYear* = distinct int - ## An ISO 8601 calendar year number. - ## - ## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3. - Time* = object ## Represents a point in time. seconds: int64 nanosecond: NanosecondRange @@ -306,7 +291,7 @@ type DateTime* = object of RootObj ## \ ## Represents a time in different parts. Although this type can represent ## leap seconds, they are generally not supported in this module. They are - ## not ignored, but the `DateTime`'s returned by procedures in this + ## not ignored, but the ``DateTime``'s returned by procedures in this ## module will never have a leap second. nanosecond: NanosecondRange second: SecondRange @@ -324,11 +309,8 @@ type Duration* = object ## Represents a fixed duration of time, meaning a duration ## that has constant length independent of the context. ## - ## To create a new `Duration`, use `initDuration + ## To create a new ``Duration``, use `initDuration proc ## <#initDuration,int64,int64,int64,int64,int64,int64,int64,int64>`_. - ## Instead of trying to access the private attributes, use - ## `inSeconds <#inSeconds,Duration>`_ for converting to seconds and - ## `inNanoseconds <#inNanoseconds,Duration>`_ for converting to nanoseconds. seconds: int64 nanosecond: NanosecondRange @@ -337,23 +319,23 @@ type Weeks, Months, Years FixedTimeUnit* = range[Nanoseconds..Weeks] ## \ - ## Subrange of `TimeUnit` that only includes units of fixed duration. - ## These are the units that can be represented by a `Duration`. + ## Subrange of ``TimeUnit`` that only includes units of fixed duration. + ## These are the units that can be represented by a ``Duration``. TimeInterval* = object ## \ ## Represents a non-fixed duration of time. Can be used to add and ## subtract non-fixed time units from a `DateTime <#DateTime>`_ or ## `Time <#Time>`_. ## - ## Create a new `TimeInterval` with `initTimeInterval proc + ## Create a new ``TimeInterval`` with `initTimeInterval proc ## <#initTimeInterval,int,int,int,int,int,int,int,int,int,int>`_. ## - ## Note that `TimeInterval` doesn't represent a fixed duration of time, + ## Note that ``TimeInterval`` doesn't represent a fixed duration of time, ## since the duration of some units depend on the context (e.g a year ## can be either 365 or 366 days long). The non-fixed time units are ## years, months, days and week. ## - ## Note that `TimeInterval`'s returned from the `times` module are + ## Note that ``TimeInterval``'s returned from the ``times`` module are ## never normalized. If you want to normalize a time unit, ## `Duration <#Duration>`_ should be used instead. nanoseconds*: int ## The number of nanoseconds @@ -369,8 +351,8 @@ type Timezone* = ref object ## \ ## Timezone interface for supporting `DateTime <#DateTime>`_\s of arbitrary - ## timezones. The `times` module only supplies implementations for the - ## system's local time and UTC. + ## timezones. The ``times`` module only supplies implementations for the + ## systems local time and UTC. zonedTimeFromTimeImpl: proc (x: Time): ZonedTime {.tags: [], raises: [], benign.} zonedTimeFromAdjTimeImpl: proc (x: Time): ZonedTime @@ -408,16 +390,6 @@ const unitWeights: array[FixedTimeUnit, int64] = [ 7 * secondsInDay * 1e9.int64, ] -when (NimMajor, NimMinor) >= (1, 4): - # Newer versions of Nim don't track defects - {.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError].} - {.pragma: parseRaises, raises: [TimeParseError].} -else: - # Still track when using older versions - {.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError, Defect].} - {.pragma: parseRaises, raises: [TimeParseError, Defect].} - - # # Helper procs # @@ -439,8 +411,8 @@ proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T = ## Normalize a (seconds, nanoseconds) pair and return it as either - ## a `Duration` or `Time`. A normalized `Duration|Time` has a - ## positive nanosecond part in the range `NanosecondRange`. + ## a ``Duration`` or ``Time``. A normalized ``Duration|Time`` has a + ## positive nanosecond part in the range ``NanosecondRange``. result.seconds = seconds + convert(Nanoseconds, Seconds, nanoseconds) var nanosecond = nanoseconds mod convert(Seconds, Nanoseconds, 1) if nanosecond < 0: @@ -449,14 +421,14 @@ proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T = result.nanosecond = nanosecond.int proc isLeapYear*(year: int): bool = - ## Returns true if `year` is a leap year. + ## Returns true if ``year`` is a leap year. runnableExamples: doAssert isLeapYear(2000) doAssert not isLeapYear(1900) year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0) proc getDaysInMonth*(month: Month, year: int): int = - ## Get the number of days in `month` of `year`. + ## Get the number of days in ``month`` of ``year``. # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month runnableExamples: doAssert getDaysInMonth(mFeb, 2000) == 29 @@ -509,7 +481,7 @@ proc fromEpochDay(epochday: int64): proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRange {.tags: [], raises: [], benign.} = ## Returns the day of the year. - ## Equivalent with `dateTime(year, month, monthday, 0, 0, 0, 0).yearday`. + ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).yearday``. runnableExamples: doAssert getDayOfYear(1, mJan, 2000) == 0 doAssert getDayOfYear(10, mJan, 2000) == 9 @@ -529,7 +501,7 @@ proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.tags: [], raises: [], benign.} = ## Returns the day of the week enum from day, month and year. - ## Equivalent with `dateTime(year, month, monthday, 0, 0, 0, 0).weekday`. + ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).weekday``. runnableExamples: doAssert getDayOfWeek(13, mJun, 1990) == dWed doAssert $getDayOfWeek(13, mJun, 1990) == "Wednesday" @@ -544,60 +516,12 @@ proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay result = if wd == 0: dSun else: WeekDay(wd - 1) proc getDaysInYear*(year: int): int = - ## Get the number of days in a `year` + ## Get the number of days in a ``year`` runnableExamples: doAssert getDaysInYear(2000) == 366 doAssert getDaysInYear(2001) == 365 result = 365 + (if isLeapYear(year): 1 else: 0) -proc `==`*(a, b: IsoYear): bool {.borrow.} -proc `$`*(p: IsoYear): string {.borrow.} - -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(2019)) == 52 - assert getWeeksInIsoYear(IsoYear(2020)) == 53 - - var y = int(y) - - # support negative years - y = if y < 0: 400 + y mod 400 else: y - - # source: https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm - let p = (y + (y div 4) - (y div 100) + (y div 400)) mod 7 - let y1 = y - 1 - let p1 = (y1 + (y1 div 4) - (y1 div 100) + (y1 div 400)) mod 7 - if p == 4 or p1 == 3: 53 else: 52 - -proc getIsoWeekAndYear*(dt: DateTime): - tuple[isoweek: IsoWeekRange, isoyear: IsoYear] {.since: (1, 5).} = - ## Returns the ISO 8601 week and year. - ## - ## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3. - runnableExamples: - assert getIsoWeekAndYear(initDateTime(21, mApr, 2018, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2018.IsoYear) - block: - let (w, y) = getIsoWeekAndYear(initDateTime(30, mDec, 2019, 00, 00, 00)) - assert w == 01.IsoWeekRange - assert y == 2020.IsoYear - assert getIsoWeekAndYear(initDateTime(13, mSep, 2020, 00, 00, 00)) == (isoweek: 37.IsoWeekRange, isoyear: 2020.IsoYear) - block: - let (w, y) = getIsoWeekAndYear(initDateTime(2, mJan, 2021, 00, 00, 00)) - assert w.int > 52 - assert w.int < 54 - assert y.int mod 100 == 20 - - # source: https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm - var w = (dt.yearday.int - dt.weekday.int + 10) div 7 - if w < 1: - (isoweek: getWeeksInIsoYear(IsoYear(dt.year - 1)), isoyear: IsoYear(dt.year - 1)) - elif (w > getWeeksInIsoYear(IsoYear(dt.year))): - (isoweek: IsoWeekRange(1), isoyear: IsoYear(dt.year + 1)) - else: - (isoweek: IsoWeekRange(w), isoyear: IsoYear(dt.year)) - proc stringifyUnit(value: int | int64, unit: TimeUnit): string = ## Stringify time unit with it's name, lowercased let strUnit = $unit @@ -694,56 +618,56 @@ template convert(dur: Duration, unit: static[FixedTimeUnit]): int64 = convert(Nanoseconds, unit, dur.nanosecond) proc inWeeks*(dur: Duration): int64 = - ## Converts the duration to the number of whole weeks. + ## Convert the duration to the number of whole weeks. runnableExamples: let dur = initDuration(days = 8) doAssert dur.inWeeks == 1 dur.convert(Weeks) proc inDays*(dur: Duration): int64 = - ## Converts the duration to the number of whole days. + ## Convert the duration to the number of whole days. runnableExamples: let dur = initDuration(hours = -50) doAssert dur.inDays == -2 dur.convert(Days) proc inHours*(dur: Duration): int64 = - ## Converts the duration to the number of whole hours. + ## Convert the duration to the number of whole hours. runnableExamples: let dur = initDuration(minutes = 60, days = 2) doAssert dur.inHours == 49 dur.convert(Hours) proc inMinutes*(dur: Duration): int64 = - ## Converts the duration to the number of whole minutes. + ## Convert the duration to the number of whole minutes. runnableExamples: let dur = initDuration(hours = 2, seconds = 10) doAssert dur.inMinutes == 120 dur.convert(Minutes) proc inSeconds*(dur: Duration): int64 = - ## Converts the duration to the number of whole seconds. + ## Convert the duration to the number of whole seconds. runnableExamples: let dur = initDuration(hours = 2, milliseconds = 10) doAssert dur.inSeconds == 2 * 60 * 60 dur.convert(Seconds) proc inMilliseconds*(dur: Duration): int64 = - ## Converts the duration to the number of whole milliseconds. + ## Convert the duration to the number of whole milliseconds. runnableExamples: let dur = initDuration(seconds = -2) doAssert dur.inMilliseconds == -2000 dur.convert(Milliseconds) proc inMicroseconds*(dur: Duration): int64 = - ## Converts the duration to the number of whole microseconds. + ## Convert the duration to the number of whole microseconds. runnableExamples: let dur = initDuration(seconds = -2) doAssert dur.inMicroseconds == -2000000 dur.convert(Microseconds) proc inNanoseconds*(dur: Duration): int64 = - ## Converts the duration to the number of whole nanoseconds. + ## Convert the duration to the number of whole nanoseconds. runnableExamples: let dur = initDuration(seconds = -2) doAssert dur.inNanoseconds == -2000000000 @@ -753,9 +677,9 @@ proc toParts*(dur: Duration): DurationParts = ## Converts a duration into an array consisting of fixed time units. ## ## Each value in the array gives information about a specific unit of - ## time, for example `result[Days]` gives a count of days. + ## time, for example ``result[Days]`` gives a count of days. ## - ## This procedure is useful for converting `Duration` values to strings. + ## This procedure is useful for converting ``Duration`` values to strings. runnableExamples: var dp = toParts(initDuration(weeks = 2, days = 1)) doAssert dp[Days] == 1 @@ -785,7 +709,7 @@ proc toParts*(dur: Duration): DurationParts = result[unit] = quantity proc `$`*(dur: Duration): string = - ## Human friendly string representation of a `Duration`. + ## Human friendly string representation of a ``Duration``. runnableExamples: doAssert $initDuration(seconds = 2) == "2 seconds" doAssert $initDuration(weeks = 1, days = 2) == "1 week and 2 days" @@ -825,9 +749,9 @@ proc `-`*(a: Duration): Duration {.operator, extern: "ntReverseDuration".} = proc `<`*(a, b: Duration): bool {.operator, extern: "ntLtDuration".} = ## Note that a duration can be negative, - ## so even if `a < b` is true `a` might + ## so even if ``a < b`` is true ``a`` might ## represent a larger absolute duration. - ## Use `abs(a) < abs(b)` to compare the absolute + ## Use ``abs(a) < abs(b)`` to compare the absolute ## duration. runnableExamples: doAssert initDuration(seconds = 1) < initDuration(seconds = 2) @@ -908,20 +832,20 @@ proc initTime*(unix: int64, nanosecond: NanosecondRange): Time = result.nanosecond = nanosecond proc nanosecond*(time: Time): NanosecondRange = - ## Get the fractional part of a `Time` as the number + ## Get the fractional part of a ``Time`` as the number ## of nanoseconds of the second. time.nanosecond proc fromUnix*(unix: int64): Time {.benign, tags: [], raises: [], noSideEffect.} = - ## Convert a unix timestamp (seconds since `1970-01-01T00:00:00Z`) - ## to a `Time`. + ## Convert a unix timestamp (seconds since ``1970-01-01T00:00:00Z``) + ## to a ``Time``. runnableExamples: doAssert $fromUnix(0).utc == "1970-01-01T00:00:00Z" initTime(unix, 0) proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} = - ## Convert `t` to a unix timestamp (seconds since `1970-01-01T00:00:00Z`). + ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``). ## See also `toUnixFloat` for subsecond resolution. runnableExamples: doAssert fromUnix(0).toUnix() == 0 @@ -949,47 +873,40 @@ since((1, 1)): export fromUnixFloat export toUnixFloat - proc fromWinTime*(win: int64): Time = ## Convert a Windows file time (100-nanosecond intervals since - ## `1601-01-01T00:00:00Z`) to a `Time`. + ## ``1601-01-01T00:00:00Z``) to a ``Time``. const hnsecsPerSec = convert(Seconds, Nanoseconds, 1) div 100 let nanos = floorMod(win, hnsecsPerSec) * 100 let seconds = floorDiv(win - epochDiff, hnsecsPerSec) result = initTime(seconds, nanos) proc toWinTime*(t: Time): int64 = - ## Convert `t` to a Windows file time (100-nanosecond intervals - ## since `1601-01-01T00:00:00Z`). + ## Convert ``t`` to a Windows file time (100-nanosecond intervals + ## since ``1601-01-01T00:00:00Z``). result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100 -proc getTimeImpl(typ: typedesc[Time]): Time = - discard "implemented in the vm" - proc getTime*(): Time {.tags: [TimeEffect], benign.} = - ## Gets the current time as a `Time` with up to nanosecond resolution. - when nimvm: - result = getTimeImpl(Time) - else: - when defined(js): - let millis = newDate().getTime() - let seconds = convert(Milliseconds, Seconds, millis) - let nanos = convert(Milliseconds, Nanoseconds, - millis mod convert(Seconds, Milliseconds, 1).int) - result = initTime(seconds, nanos) - elif defined(macosx): - var a {.noinit.}: Timeval - gettimeofday(a) - result = initTime(a.tv_sec.int64, - convert(Microseconds, Nanoseconds, a.tv_usec.int)) - elif defined(posix): - var ts {.noinit.}: Timespec - discard clock_gettime(CLOCK_REALTIME, ts) - result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) - elif defined(windows): - var f {.noinit.}: FILETIME - getSystemTimeAsFileTime(f) - result = fromWinTime(rdFileTime(f)) + ## Gets the current time as a ``Time`` with up to nanosecond resolution. + when defined(js): + let millis = newDate().getTime() + let seconds = convert(Milliseconds, Seconds, millis) + let nanos = convert(Milliseconds, Nanoseconds, + millis mod convert(Seconds, Milliseconds, 1).int) + result = initTime(seconds, nanos) + elif defined(macosx): + var a {.noinit.}: Timeval + gettimeofday(a) + result = initTime(a.tv_sec.int64, + convert(Microseconds, Nanoseconds, a.tv_usec.int)) + elif defined(posix): + var ts {.noinit.}: Timespec + discard clock_gettime(CLOCK_REALTIME, ts) + result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) + elif defined(windows): + var f {.noinit.}: FILETIME + getSystemTimeAsFileTime(f) + result = fromWinTime(rdFileTime(f)) proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} = ## Computes the duration between two points in time. @@ -999,29 +916,29 @@ proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} = subImpl[Duration](a, b) proc `+`*(a: Time, b: Duration): Time {.operator, extern: "ntAddTime".} = - ## Add a duration of time to a `Time`. + ## Add a duration of time to a ``Time``. runnableExamples: doAssert (fromUnix(0) + initDuration(seconds = 1)) == fromUnix(1) addImpl[Time](a, b) proc `-`*(a: Time, b: Duration): Time {.operator, extern: "ntSubTime".} = - ## Subtracts a duration of time from a `Time`. + ## Subtracts a duration of time from a ``Time``. runnableExamples: doAssert (fromUnix(0) - initDuration(seconds = 1)) == fromUnix(-1) subImpl[Time](a, b) proc `<`*(a, b: Time): bool {.operator, extern: "ntLtTime".} = - ## Returns true if `a < b`, that is if `a` happened before `b`. + ## Returns true if ``a < b``, that is if ``a`` happened before ``b``. runnableExamples: doAssert initTime(50, 0) < initTime(99, 0) ltImpl(a, b) proc `<=`*(a, b: Time): bool {.operator, extern: "ntLeTime".} = - ## Returns true if `a <= b`. + ## Returns true if ``a <= b``. lqImpl(a, b) proc `==`*(a, b: Time): bool {.operator, extern: "ntEqTime".} = - ## Returns true if `a == b`, that is if both times represent the same point in time. + ## Returns true if ``a == b``, that is if both times represent the same point in time. eqImpl(a, b) proc `+=`*(t: var Time, b: Duration) = @@ -1034,7 +951,7 @@ proc high*(typ: typedesc[Time]): Time = initTime(high(int64), high(NanosecondRange)) proc low*(typ: typedesc[Time]): Time = - initTime(0, 0) + initTime(low(int64), 0) # # DateTime & Timezone @@ -1107,7 +1024,7 @@ proc isDst*(dt: DateTime): bool {.inline.} = proc timezone*(dt: DateTime): Timezone {.inline.} = ## The timezone represented as an implementation - ## of `Timezone`. + ## of ``Timezone``. assertDateTimeInitialized(dt) dt.timezone @@ -1115,9 +1032,9 @@ proc utcOffset*(dt: DateTime): int {.inline.} = ## The offset in seconds west of UTC, including ## any offset due to DST. Note that the sign of ## this number is the opposite of the one in a - ## formatted offset string like `+01:00` (which + ## formatted offset string like ``+01:00`` (which ## would be equivalent to the UTC offset - ## `-3600`). + ## ``-3600``). assertDateTimeInitialized(dt) dt.utcOffset @@ -1135,18 +1052,18 @@ proc isLeapDay*(dt: DateTime): bool {.since: (1, 1).} = ## Returns whether `t` is a leap day, i.e. Feb 29 in a leap year. This matters ## as it affects time offset calculations. runnableExamples: - let dt = dateTime(2020, mFeb, 29, 00, 00, 00, 00, utc()) + let dt = initDateTime(29, mFeb, 2020, 00, 00, 00, utc()) doAssert dt.isLeapDay doAssert dt+1.years-1.years != dt - let dt2 = dateTime(2020, mFeb, 28, 00, 00, 00, 00, utc()) + let dt2 = initDateTime(28, mFeb, 2020, 00, 00, 00, utc()) doAssert not dt2.isLeapDay doAssert dt2+1.years-1.years == dt2 - doAssertRaises(Exception): discard dateTime(2021, mFeb, 29, 00, 00, 00, 00, utc()) + doAssertRaises(Exception): discard initDateTime(29, mFeb, 2021, 00, 00, 00, utc()) assertDateTimeInitialized dt dt.year.isLeapYear and dt.month == mFeb and dt.monthday == 29 proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = - ## Converts a `DateTime` to a `Time` representing the same point in time. + ## Converts a ``DateTime`` to a ``Time`` representing the same point in time. assertDateTimeInitialized dt let epochDay = toEpochDay(dt.monthday, dt.month, dt.year) var seconds = epochDay * secondsInDay @@ -1157,7 +1074,7 @@ proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} = result = initTime(seconds, dt.nanosecond) proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime = - ## Create a new `DateTime` using `ZonedTime` in the specified timezone. + ## Create a new ``DateTime`` using ``ZonedTime`` in the specified timezone. let adjTime = zt.time - initDuration(seconds = zt.utcOffset) let s = adjTime.seconds let epochday = floorDiv(s, secondsInDay) @@ -1192,11 +1109,11 @@ proc newTimezone*( zonedTimeFromAdjTimeImpl: proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.} ): owned Timezone = - ## Create a new `Timezone`. + ## Create a new ``Timezone``. ## - ## `zonedTimeFromTimeImpl` and `zonedTimeFromAdjTimeImpl` is used - ## as the underlying implementations for `zonedTimeFromTime` and - ## `zonedTimeFromAdjTime`. + ## ``zonedTimeFromTimeImpl`` and ``zonedTimeFromAdjTimeImpl`` is used + ## as the underlying implementations for ``zonedTimeFromTime`` and + ## ``zonedTimeFromAdjTime``. ## ## If possible, the name parameter should match the name used in the ## tz database. If the timezone doesn't exist in the tz database, or if the @@ -1220,21 +1137,21 @@ proc name*(zone: Timezone): string = ## If the timezone doesn't exist in the tz database, or if the timezone ## name is unknown, then any string that describes the timezone ## unambiguously might be used. For example, the string "LOCAL" is used - ## for the system's local timezone. + ## for the systems local timezone. ## ## See also: https://en.wikipedia.org/wiki/Tz_database zone.name proc zonedTimeFromTime*(zone: Timezone, time: Time): ZonedTime = - ## Returns the `ZonedTime` for some point in time. + ## Returns the ``ZonedTime`` for some point in time. zone.zonedTimeFromTimeImpl(time) proc zonedTimeFromAdjTime*(zone: Timezone, adjTime: Time): ZonedTime = - ## Returns the `ZonedTime` for some local time. + ## Returns the ``ZonedTime`` for some local time. ## - ## Note that the `Time` argument does not represent a point in time, it - ## represent a local time! E.g if `adjTime` is `fromUnix(0)`, it should be - ## interpreted as 1970-01-01T00:00:00 in the `zone` timezone, not in UTC. + ## Note that the ``Time`` argument does not represent a point in time, it + ## represent a local time! E.g if ``adjTime`` is ``fromUnix(0)``, it should be + ## interpreted as 1970-01-01T00:00:00 in the ``zone`` timezone, not in UTC. zone.zonedTimeFromAdjTimeImpl(adjTime) proc `$`*(zone: Timezone): string = @@ -1242,7 +1159,7 @@ proc `$`*(zone: Timezone): string = if zone != nil: result = zone.name proc `==`*(zone1, zone2: Timezone): bool = - ## Two `Timezone`'s are considered equal if their name is equal. + ## Two ``Timezone``'s are considered equal if their name is equal. runnableExamples: doAssert local() == local() doAssert local() != utc() @@ -1254,13 +1171,13 @@ proc `==`*(zone1, zone2: Timezone): bool = proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} = - ## Convert `time` into a `DateTime` using `zone` as the timezone. + ## Convert ``time`` into a ``DateTime`` using ``zone`` as the timezone. result = initDateTime(zone.zonedTimeFromTime(time), zone) proc inZone*(dt: DateTime, zone: Timezone): DateTime {.tags: [], raises: [], benign.} = - ## Returns a `DateTime` representing the same point in time as `dt` but - ## using `zone` as the timezone. + ## Returns a ``DateTime`` representing the same point in time as ``dt`` but + ## using ``zone`` as the timezone. assertDateTimeInitialized dt dt.toTime.inZone(zone) @@ -1366,7 +1283,7 @@ var utcInstance {.threadvar.}: Timezone var localInstance {.threadvar.}: Timezone proc utc*(): Timezone = - ## Get the `Timezone` implementation for the UTC timezone. + ## Get the ``Timezone`` implementation for the UTC timezone. runnableExamples: doAssert now().utc.timezone == utc() doAssert utc().name == "Etc/UTC" @@ -1375,7 +1292,7 @@ proc utc*(): Timezone = result = utcInstance proc local*(): Timezone = - ## Get the `Timezone` implementation for the local timezone. + ## Get the ``Timezone`` implementation for the local timezone. runnableExamples: doAssert now().timezone == local() doAssert local().name == "LOCAL" @@ -1385,36 +1302,35 @@ proc local*(): Timezone = result = localInstance proc utc*(dt: DateTime): DateTime = - ## Shorthand for `dt.inZone(utc())`. + ## Shorthand for ``dt.inZone(utc())``. dt.inZone(utc()) proc local*(dt: DateTime): DateTime = - ## Shorthand for `dt.inZone(local())`. + ## Shorthand for ``dt.inZone(local())``. dt.inZone(local()) proc utc*(t: Time): DateTime = - ## Shorthand for `t.inZone(utc())`. + ## Shorthand for ``t.inZone(utc())``. t.inZone(utc()) proc local*(t: Time): DateTime = - ## Shorthand for `t.inZone(local())`. + ## Shorthand for ``t.inZone(local())``. t.inZone(local()) proc now*(): DateTime {.tags: [TimeEffect], benign.} = - ## Get the current time as a `DateTime` in the local timezone. - ## Shorthand for `getTime().local`. + ## Get the current time as a ``DateTime`` in the local timezone. ## - ## .. warning:: Unsuitable for benchmarking, use `monotimes.getMonoTime` or - ## `cpuTime` instead, depending on the use case. + ## Shorthand for ``getTime().local``. getTime().local -proc dateTime*(year: int, month: Month, monthday: MonthdayRange, - hour: HourRange = 0, minute: MinuteRange = 0, second: SecondRange = 0, - nanosecond: NanosecondRange = 0, - zone: Timezone = local()): DateTime = +proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, + hour: HourRange, minute: MinuteRange, second: SecondRange, + nanosecond: NanosecondRange, + zone: Timezone = local()): DateTime = ## Create a new `DateTime <#DateTime>`_ in the specified timezone. runnableExamples: - assert $dateTime(2017, mMar, 30, zone = utc()) == "2017-03-30T00:00:00Z" + let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc()) + doAssert $dt1 == "2017-03-30T00:00:00Z" assertValidDate monthday, month, year let dt = DateTime( @@ -1430,24 +1346,16 @@ proc dateTime*(year: int, month: Month, monthday: MonthdayRange, proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, hour: HourRange, minute: MinuteRange, second: SecondRange, - nanosecond: NanosecondRange, - zone: Timezone = local()): DateTime {.deprecated: "use `dateTime`".} = + zone: Timezone = local()): DateTime = ## Create a new `DateTime <#DateTime>`_ in the specified timezone. - runnableExamples("--warning:deprecated:off"): - assert $initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc()) == "2017-03-30T00:00:00Z" - dateTime(year, month, monthday, hour, minute, second, nanosecond, zone) - -proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, - hour: HourRange, minute: MinuteRange, second: SecondRange, - zone: Timezone = local()): DateTime {.deprecated: "use `dateTime`".} = - ## Create a new `DateTime <#DateTime>`_ in the specified timezone. - runnableExamples("--warning:deprecated:off"): - assert $initDateTime(30, mMar, 2017, 00, 00, 00, utc()) == "2017-03-30T00:00:00Z" - dateTime(year, month, monthday, hour, minute, second, 0, zone) + runnableExamples: + let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + doAssert $dt1 == "2017-03-30T00:00:00Z" + initDateTime(monthday, month, year, hour, minute, second, 0, zone) proc `+`*(dt: DateTime, dur: Duration): DateTime = runnableExamples: - let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) + let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) let dur = initDuration(hours = 5) doAssert $(dt + dur) == "2017-03-30T05:00:00Z" @@ -1455,32 +1363,32 @@ proc `+`*(dt: DateTime, dur: Duration): DateTime = proc `-`*(dt: DateTime, dur: Duration): DateTime = runnableExamples: - let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) + let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) let dur = initDuration(days = 5) doAssert $(dt - dur) == "2017-03-25T00:00:00Z" (dt.toTime - dur).inZone(dt.timezone) proc `-`*(dt1, dt2: DateTime): Duration = - ## Compute the duration between `dt1` and `dt2`. + ## Compute the duration between ``dt1`` and ``dt2``. runnableExamples: - let dt1 = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) - let dt2 = dateTime(2017, mMar, 25, 00, 00, 00, 00, utc()) + let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + let dt2 = initDateTime(25, mMar, 2017, 00, 00, 00, utc()) doAssert dt1 - dt2 == initDuration(days = 5) dt1.toTime - dt2.toTime proc `<`*(a, b: DateTime): bool = - ## Returns true if `a` happened before `b`. + ## Returns true if ``a`` happened before ``b``. return a.toTime < b.toTime proc `<=`*(a, b: DateTime): bool = - ## Returns true if `a` happened before or at the same time as `b`. + ## Returns true if ``a`` happened before or at the same time as ``b``. return a.toTime <= b.toTime proc `==`*(a, b: DateTime): bool = - ## Returns true if `a` and `b` represent the same point in time. + ## Returns true if ``a`` and ``b`` represent the same point in time. if not a.isInitialized: not b.isInitialized elif not b.isInitialized: false else: a.toTime == b.toTime @@ -1492,7 +1400,7 @@ proc `-=`*(a: var DateTime, b: Duration) = a = a - b proc getDateStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current local date as a string of the format `YYYY-MM-DD`. + ## Gets the current local date as a string of the format ``YYYY-MM-DD``. runnableExamples: echo getDateStr(now() - 1.months) assertDateTimeInitialized dt @@ -1500,7 +1408,7 @@ proc getDateStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} '-' & intToStr(dt.monthday, 2) proc getClockStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current local clock time as a string of the format `HH:mm:ss`. + ## Gets the current local clock time as a string of the format ``HH:mm:ss``. runnableExamples: echo getClockStr(now() - 1.hours) assertDateTimeInitialized dt @@ -1561,31 +1469,30 @@ type uuuu UUUU z, zz, zzz, zzzz - ZZZ, ZZZZ g # This is a special value used to mark literal format values. - # See the doc comment for `TimeFormat.patterns`. + # See the doc comment for ``TimeFormat.patterns``. Lit TimeFormat* = object ## Represents a format for parsing and printing ## time types. ## - ## To create a new `TimeFormat` use `initTimeFormat proc + ## To create a new ``TimeFormat`` use `initTimeFormat proc ## <#initTimeFormat,string>`_. patterns: seq[byte] ## \ ## Contains the patterns encoded as bytes. ## Literal values are encoded in a special way. - ## They start with `Lit.byte`, then the length of the literal, then the + ## They start with ``Lit.byte``, then the length of the literal, then the ## raw char values of the literal. For example, the literal `foo` would - ## be encoded as `@[Lit.byte, 3.byte, 'f'.byte, 'o'.byte, 'o'.byte]`. + ## be encoded as ``@[Lit.byte, 3.byte, 'f'.byte, 'o'.byte, 'o'.byte]``. formatStr: string TimeParseError* = object of ValueError ## \ - ## Raised when parsing input using a `TimeFormat` fails. + ## Raised when parsing input using a ``TimeFormat`` fails. TimeFormatParseError* = object of ValueError ## \ - ## Raised when parsing a `TimeFormat` string fails. + ## Raised when parsing a ``TimeFormat`` string fails. const DefaultLocale* = DateTimeLocale( @@ -1601,7 +1508,7 @@ const FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ','} proc `$`*(f: TimeFormat): string = - ## Returns the format string that was used to construct `f`. + ## Returns the format string that was used to construct ``f``. runnableExamples: let f = initTimeFormat("yyyy-MM-dd") doAssert $f == "yyyy-MM-dd" @@ -1714,8 +1621,6 @@ proc stringToPattern(str: string): FormatPattern = of "zz": result = zz of "zzz": result = zzz of "zzzz": result = zzzz - of "ZZZ": result = ZZZ - of "ZZZZ": result = ZZZZ of "g": result = g else: raise newException(TimeFormatParseError, "'" & str & "' is not a valid pattern") @@ -1724,7 +1629,7 @@ proc initTimeFormat*(format: string): TimeFormat = ## Construct a new time format for parsing & formatting time types. ## ## See `Parsing and formatting dates`_ for documentation of the - ## `format` argument. + ## ``format`` argument. runnableExamples: let f = initTimeFormat("yyyy-MM-dd") doAssert "2000-01-01" == "2000-01-01".parse(f).format(f) @@ -1822,7 +1727,7 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string, result.add '+' & $year of UUUU: result.add $dt.year - of z, zz, zzz, zzzz, ZZZ, ZZZZ: + of z, zz, zzz, zzzz: if dt.timezone != nil and dt.timezone.name == "Etc/UTC": result.add 'Z' else: @@ -1833,18 +1738,16 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string, result.add $(absOffset div 3600) of zz: result.add (absOffset div 3600).intToStr(2) - of zzz, ZZZ: + of zzz: let h = (absOffset div 3600).intToStr(2) let m = ((absOffset div 60) mod 60).intToStr(2) - let sep = if pattern == zzz: ":" else: "" - result.add h & sep & m - of zzzz, ZZZZ: + result.add h & ":" & m + of zzzz: let absOffset = abs(dt.utcOffset) let h = (absOffset div 3600).intToStr(2) let m = ((absOffset div 60) mod 60).intToStr(2) let s = (absOffset mod 60).intToStr(2) - let sep = if pattern == zzzz: ":" else: "" - result.add h & sep & m & sep & s + result.add h & ":" & m & ":" & s else: assert false of g: result.add if dt.year < 1: "BC" else: "AD" @@ -1978,7 +1881,7 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, parsed.year = some(year) of UUUU: parsed.year = some(takeInt(1..high(int), allowSign = true)) - of z, zz, zzz, zzzz, ZZZ, ZZZZ: + of z, zz, zzz, zzzz: case input[i] of '+', '-': let sign = if input[i] == '-': 1 else: -1 @@ -1989,24 +1892,21 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, offset = takeInt(1..2) * 3600 of zz: offset = takeInt(2..2) * 3600 - of zzz, ZZZ: + of zzz: offset.inc takeInt(2..2) * 3600 - if pattern == zzz: - if input[i] != ':': - return false - i.inc + if input[i] != ':': + return false + i.inc offset.inc takeInt(2..2) * 60 - of zzzz, ZZZZ: + of zzzz: offset.inc takeInt(2..2) * 3600 - if pattern == zzzz: - if input[i] != ':': - return false - i.inc + if input[i] != ':': + return false + i.inc offset.inc takeInt(2..2) * 60 - if pattern == zzzz: - if input[i] != ':': - return false - i.inc + if input[i] != ':': + return false + i.inc offset.inc takeInt(2..2) else: assert false parsed.utcOffset = some(offset * sign) @@ -2073,18 +1973,18 @@ proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat, if p.utcOffset.isNone: # No timezone parsed - assume timezone is `zone` - result = dateTime(year, month, monthday, hour, minute, second, nanosecond, zone) + result = initDateTime(monthday, month, year, hour, minute, second, nanosecond, zone) else: # Otherwise convert to `zone` - result = (dateTime(year, month, monthday, hour, minute, second, nanosecond, utc()).toTime + + result = (initDateTime(monthday, month, year, hour, minute, second, nanosecond, utc()).toTime + initDuration(seconds = p.utcOffset.get())).inZone(zone) proc format*(dt: DateTime, f: TimeFormat, loc: DateTimeLocale = DefaultLocale): string {.raises: [].} = - ## Format `dt` using the format specified by `f`. + ## Format ``dt`` using the format specified by ``f``. runnableExamples: let f = initTimeFormat("yyyy-MM-dd") - let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc()) + let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) doAssert "2000-01-01" == dt.format(f) assertDateTimeInitialized dt result = "" @@ -2104,56 +2004,61 @@ proc format*(dt: DateTime, f: TimeFormat, proc format*(dt: DateTime, f: string, loc: DateTimeLocale = DefaultLocale): string {.raises: [TimeFormatParseError].} = - ## Shorthand for constructing a `TimeFormat` and using it to format `dt`. + ## Shorthand for constructing a ``TimeFormat`` and using it to format ``dt``. ## ## See `Parsing and formatting dates`_ for documentation of the - ## `format` argument. + ## ``format`` argument. runnableExamples: - let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc()) + let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) doAssert "2000-01-01" == format(dt, "yyyy-MM-dd") let dtFormat = initTimeFormat(f) result = dt.format(dtFormat, loc) proc format*(dt: DateTime, f: static[string]): string {.raises: [].} = - ## Overload that validates `format` at compile time. + ## Overload that validates ``format`` at compile time. const f2 = initTimeFormat(f) result = dt.format(f2) -proc formatValue*(result: var string; value: DateTime | Time, specifier: string) = +proc formatValue*(result: var string; value: DateTime, specifier: string) = ## adapter for strformat. Not intended to be called directly. result.add format(value, if specifier.len == 0: "yyyy-MM-dd'T'HH:mm:sszzz" else: specifier) proc format*(time: Time, f: string, zone: Timezone = local()): string {.raises: [TimeFormatParseError].} = - ## Shorthand for constructing a `TimeFormat` and using it to format - ## `time`. Will use the timezone specified by `zone`. + ## Shorthand for constructing a ``TimeFormat`` and using it to format + ## ``time``. Will use the timezone specified by ``zone``. ## ## See `Parsing and formatting dates`_ for documentation of the - ## `f` argument. + ## ``f`` argument. runnableExamples: - var dt = dateTime(1970, mJan, 01, 00, 00, 00, 00, utc()) + var dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc()) var tm = dt.toTime() doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00" time.inZone(zone).format(f) proc format*(time: Time, f: static[string], zone: Timezone = local()): string {.raises: [].} = - ## Overload that validates `f` at compile time. + ## Overload that validates ``f`` at compile time. const f2 = initTimeFormat(f) result = time.inZone(zone).format(f2) +template formatValue*(result: var string; value: Time, specifier: string) = + ## adapter for ``strformat``. Not intended to be called directly. + result.add format(value, specifier) + proc parse*(input: string, f: TimeFormat, zone: Timezone = local(), - loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} = - ## Parses `input` as a `DateTime` using the format specified by `f`. - ## If no UTC offset was parsed, then `input` is assumed to be specified in - ## the `zone` timezone. If a UTC offset was parsed, the result will be - ## converted to the `zone` timezone. + loc: DateTimeLocale = DefaultLocale): DateTime + {.raises: [TimeParseError, Defect].} = + ## Parses ``input`` as a ``DateTime`` using the format specified by ``f``. + ## If no UTC offset was parsed, then ``input`` is assumed to be specified in + ## the ``zone`` timezone. If a UTC offset was parsed, the result will be + ## converted to the ``zone`` timezone. ## - ## Month and day names from the passed in `loc` are used. + ## Month and day names from the passed in ``loc`` are used. runnableExamples: let f = initTimeFormat("yyyy-MM-dd") - let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc()) + let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) doAssert dt == "2000-01-01".parse(f, utc()) var inpIdx = 0 # Input index var patIdx = 0 # Pattern index @@ -2187,46 +2092,49 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local(), result = toDateTime(parsed, zone, f, input) proc parse*(input, f: string, tz: Timezone = local(), - loc: DateTimeLocale = DefaultLocale): DateTime {.parseFormatRaises.} = - ## Shorthand for constructing a `TimeFormat` and using it to parse - ## `input` as a `DateTime`. + loc: DateTimeLocale = DefaultLocale): DateTime + {.raises: [TimeParseError, TimeFormatParseError, Defect].} = + ## Shorthand for constructing a ``TimeFormat`` and using it to parse + ## ``input`` as a ``DateTime``. ## ## See `Parsing and formatting dates`_ for documentation of the - ## `f` argument. + ## ``f`` argument. runnableExamples: - let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc()) + let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) doAssert dt == parse("2000-01-01", "yyyy-MM-dd", utc()) let dtFormat = initTimeFormat(f) result = input.parse(dtFormat, tz, loc = loc) proc parse*(input: string, f: static[string], zone: Timezone = local(), - loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} = - ## Overload that validates `f` at compile time. + loc: DateTimeLocale = DefaultLocale): + DateTime {.raises: [TimeParseError, Defect].} = + ## Overload that validates ``f`` at compile time. const f2 = initTimeFormat(f) result = input.parse(f2, zone, loc = loc) -proc parseTime*(input, f: string, zone: Timezone): Time {.parseFormatRaises.} = - ## Shorthand for constructing a `TimeFormat` and using it to parse - ## `input` as a `DateTime`, then converting it a `Time`. +proc parseTime*(input, f: string, zone: Timezone): Time + {.raises: [TimeParseError, TimeFormatParseError, Defect].} = + ## Shorthand for constructing a ``TimeFormat`` and using it to parse + ## ``input`` as a ``DateTime``, then converting it a ``Time``. ## ## See `Parsing and formatting dates`_ for documentation of the - ## `format` argument. + ## ``format`` argument. runnableExamples: let tStr = "1970-01-01T00:00:00+00:00" doAssert parseTime(tStr, "yyyy-MM-dd'T'HH:mm:sszzz", utc()) == fromUnix(0) parse(input, f, zone).toTime() proc parseTime*(input: string, f: static[string], zone: Timezone): Time - {.parseRaises.} = - ## Overload that validates `format` at compile time. + {.raises: [TimeParseError, Defect].} = + ## Overload that validates ``format`` at compile time. const f2 = initTimeFormat(f) result = input.parse(f2, zone).toTime() proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = ## Converts a `DateTime` object to a string representation. - ## It uses the format `yyyy-MM-dd'T'HH:mm:sszzz`. + ## It uses the format ``yyyy-MM-dd'T'HH:mm:sszzz``. runnableExamples: - let dt = dateTime(2000, mJan, 01, 12, 00, 00, 00, utc()) + let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc()) doAssert $dt == "2000-01-01T12:00:00Z" doAssert $default(DateTime) == "Uninitialized DateTime" if not dt.isInitialized: @@ -2236,9 +2144,9 @@ proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = proc `$`*(time: Time): string {.tags: [], raises: [], benign.} = ## Converts a `Time` value to a string representation. It will use the local - ## time zone and use the format `yyyy-MM-dd'T'HH:mm:sszzz`. + ## time zone and use the format ``yyyy-MM-dd'T'HH:mm:sszzz``. runnableExamples: - let dt = dateTime(1970, mJan, 01, 00, 00, 00, 00, local()) + let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local()) let tm = dt.toTime() doAssert $tm == "1970-01-01T00:00:00" & format(dt, "zzz") $time.local @@ -2253,14 +2161,14 @@ proc initTimeInterval*(nanoseconds, microseconds, milliseconds, ## Creates a new `TimeInterval <#TimeInterval>`_. ## ## This proc doesn't perform any normalization! For example, - ## `initTimeInterval(hours = 24)` and `initTimeInterval(days = 1)` are + ## ``initTimeInterval(hours = 24)`` and ``initTimeInterval(days = 1)`` are ## not equal. ## - ## You can also use the convenience procedures called `milliseconds`, - ## `seconds`, `minutes`, `hours`, `days`, `months`, and `years`. + ## You can also use the convenience procedures called ``milliseconds``, + ## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``. runnableExamples: let day = initTimeInterval(hours = 24) - let dt = dateTime(2000, mJan, 01, 12, 00, 00, 00, utc()) + let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc()) doAssert $(dt + day) == "2000-01-02T12:00:00Z" doAssert initTimeInterval(hours = 24) != initTimeInterval(days = 1) result.nanoseconds = nanoseconds @@ -2275,7 +2183,7 @@ proc initTimeInterval*(nanoseconds, microseconds, milliseconds, result.years = years proc `+`*(ti1, ti2: TimeInterval): TimeInterval = - ## Adds two `TimeInterval` objects together. + ## Adds two ``TimeInterval`` objects together. result.nanoseconds = ti1.nanoseconds + ti2.nanoseconds result.microseconds = ti1.microseconds + ti2.microseconds result.milliseconds = ti1.milliseconds + ti2.milliseconds @@ -2307,7 +2215,7 @@ proc `-`*(ti: TimeInterval): TimeInterval = ) proc `-`*(ti1, ti2: TimeInterval): TimeInterval = - ## Subtracts TimeInterval `ti1` from `ti2`. + ## Subtracts TimeInterval ``ti1`` from ``ti2``. ## ## Time components are subtracted one-by-one, see output: runnableExamples: @@ -2337,8 +2245,8 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration = hours = interval.hours) proc between*(startDt, endDt: DateTime): TimeInterval = - ## Gives the difference between `startDt` and `endDt` as a - ## `TimeInterval`. The following guarantees about the result is given: + ## Gives the difference between ``startDt`` and ``endDt`` as a + ## ``TimeInterval``. The following guarantees about the result is given: ## ## - All fields will have the same sign. ## - If `startDt.timezone == endDt.timezone`, it is guaranteed that @@ -2346,8 +2254,8 @@ proc between*(startDt, endDt: DateTime): TimeInterval = ## - If `startDt.timezone != endDt.timezone`, then the result will be ## equivalent to `between(startDt.utc, endDt.utc)`. runnableExamples: - var a = dateTime(2015, mMar, 25, 12, 0, 0, 00, utc()) - var b = dateTime(2017, mApr, 1, 15, 0, 15, 00, utc()) + var a = initDateTime(25, mMar, 2015, 12, 0, 0, utc()) + var b = initDateTime(1, mApr, 2017, 15, 0, 15, utc()) var ti = initTimeInterval(years = 2, weeks = 1, hours = 3, seconds = 15) doAssert between(a, b) == ti doAssert between(a, b) == -between(b, a) @@ -2427,8 +2335,8 @@ proc between*(startDt, endDt: DateTime): TimeInterval = startDate = endDate # Handle hours, minutes, seconds, milliseconds, microseconds and nanoseconds - let newStartDt = dateTime(startDate.year, startDate.month.Month, - startDate.monthday, startDt.hour, startDt.minute, startDt.second, + let newStartDt = initDateTime(startDate.monthday, startDate.month.Month, + startDate.year, startDt.hour, startDt.minute, startDt.second, startDt.nanosecond, startDt.timezone) let dur = endDt - newStartDt let parts = toParts(dur) @@ -2442,10 +2350,10 @@ proc between*(startDt, endDt: DateTime): TimeInterval = result.nanoseconds = parts[Nanoseconds].int proc toParts*(ti: TimeInterval): TimeIntervalParts = - ## Converts a `TimeInterval` into an array consisting of its time units, + ## Converts a ``TimeInterval`` into an array consisting of its time units, ## starting with nanoseconds and ending with years. ## - ## This procedure is useful for converting `TimeInterval` values to strings. + ## This procedure is useful for converting ``TimeInterval`` values to strings. ## E.g. then you need to implement custom interval printing runnableExamples: var tp = toParts(initTimeInterval(years = 1, nanoseconds = 123)) @@ -2458,7 +2366,7 @@ proc toParts*(ti: TimeInterval): TimeIntervalParts = index += 1 proc `$`*(ti: TimeInterval): string = - ## Get string representation of `TimeInterval`. + ## Get string representation of ``TimeInterval``. runnableExamples: doAssert $initTimeInterval(years = 1, nanoseconds = 123) == "1 year and 123 nanoseconds" @@ -2473,63 +2381,63 @@ proc `$`*(ti: TimeInterval): string = result = humanizeParts(parts) proc nanoseconds*(nanos: int): TimeInterval {.inline.} = - ## TimeInterval of `nanos` nanoseconds. + ## TimeInterval of ``nanos`` nanoseconds. initTimeInterval(nanoseconds = nanos) proc microseconds*(micros: int): TimeInterval {.inline.} = - ## TimeInterval of `micros` microseconds. + ## TimeInterval of ``micros`` microseconds. initTimeInterval(microseconds = micros) proc milliseconds*(ms: int): TimeInterval {.inline.} = - ## TimeInterval of `ms` milliseconds. + ## TimeInterval of ``ms`` milliseconds. initTimeInterval(milliseconds = ms) proc seconds*(s: int): TimeInterval {.inline.} = - ## TimeInterval of `s` seconds. + ## TimeInterval of ``s`` seconds. ## - ## `echo getTime() + 5.seconds` + ## ``echo getTime() + 5.seconds`` initTimeInterval(seconds = s) proc minutes*(m: int): TimeInterval {.inline.} = - ## TimeInterval of `m` minutes. + ## TimeInterval of ``m`` minutes. ## - ## `echo getTime() + 5.minutes` + ## ``echo getTime() + 5.minutes`` initTimeInterval(minutes = m) proc hours*(h: int): TimeInterval {.inline.} = - ## TimeInterval of `h` hours. + ## TimeInterval of ``h`` hours. ## - ## `echo getTime() + 2.hours` + ## ``echo getTime() + 2.hours`` initTimeInterval(hours = h) proc days*(d: int): TimeInterval {.inline.} = - ## TimeInterval of `d` days. + ## TimeInterval of ``d`` days. ## - ## `echo getTime() + 2.days` + ## ``echo getTime() + 2.days`` initTimeInterval(days = d) proc weeks*(w: int): TimeInterval {.inline.} = - ## TimeInterval of `w` weeks. + ## TimeInterval of ``w`` weeks. ## - ## `echo getTime() + 2.weeks` + ## ``echo getTime() + 2.weeks`` initTimeInterval(weeks = w) proc months*(m: int): TimeInterval {.inline.} = - ## TimeInterval of `m` months. + ## TimeInterval of ``m`` months. ## - ## `echo getTime() + 2.months` + ## ``echo getTime() + 2.months`` initTimeInterval(months = m) proc years*(y: int): TimeInterval {.inline.} = - ## TimeInterval of `y` years. + ## TimeInterval of ``y`` years. ## - ## `echo getTime() + 2.years` + ## ``echo getTime() + 2.years`` initTimeInterval(years = y) proc evaluateInterval(dt: DateTime, interval: TimeInterval): tuple[adjDur, absDur: Duration] = ## Evaluates how many nanoseconds the interval is worth - ## in the context of `dt`. + ## in the context of ``dt``. ## The result in split into an adjusted diff and an absolute diff. var months = interval.years * 12 + interval.months var curYear = dt.year @@ -2568,9 +2476,9 @@ proc evaluateInterval(dt: DateTime, interval: TimeInterval): hours = interval.hours) proc `+`*(dt: DateTime, interval: TimeInterval): DateTime = - ## Adds `interval` to `dt`. Components from `interval` are added - ## in the order of their size, i.e. first the `years` component, then the - ## `months` component and so on. The returned `DateTime` will have the + ## Adds ``interval`` to ``dt``. Components from ``interval`` are added + ## in the order of their size, i.e. first the ``years`` component, then the + ## ``months`` component and so on. The returned ``DateTime`` will have the ## same timezone as the input. ## ## Note that when adding months, monthday overflow is allowed. This means that @@ -2579,7 +2487,7 @@ proc `+`*(dt: DateTime, interval: TimeInterval): DateTime = ## So adding one month to `31 October` will result in `31 November`, which ## will overflow and result in `1 December`. runnableExamples: - let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) + let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) doAssert $(dt + 1.months) == "2017-04-30T00:00:00Z" # This is correct and happens due to monthday overflow. doAssert $(dt - 1.months) == "2017-03-02T00:00:00Z" @@ -2597,12 +2505,12 @@ proc `+`*(dt: DateTime, interval: TimeInterval): DateTime = result = initDateTime(zt, dt.timezone) proc `-`*(dt: DateTime, interval: TimeInterval): DateTime = - ## Subtract `interval` from `dt`. Components from `interval` are - ## subtracted in the order of their size, i.e. first the `years` component, - ## then the `months` component and so on. The returned `DateTime` will + ## Subtract ``interval`` from ``dt``. Components from ``interval`` are + ## subtracted in the order of their size, i.e. first the ``years`` component, + ## then the ``months`` component and so on. The returned ``DateTime`` will ## have the same timezone as the input. runnableExamples: - let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) + let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) doAssert $(dt - 5.days) == "2017-03-25T00:00:00Z" dt + (-interval) @@ -2645,33 +2553,6 @@ proc `+=`*(t: var Time, b: TimeInterval) = proc `-=`*(t: var Time, b: TimeInterval) = t = t - b -# -# Day of year -# - -proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear, - hour: HourRange, minute: MinuteRange, second: SecondRange, - nanosecond: NanosecondRange, - zone: Timezone = local()): DateTime {.since: (1, 5).} = - ## Create a new `DateTime <#DateTime>`_ from a weekday and an ISO 8601 week number and year - ## in the specified timezone. - ## - ## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3. - runnableExamples: - assert initDateTime(21, mApr, 2018, 00, 00, 00) == initDateTime(dSat, 16, 2018.IsoYear, 00, 00, 00) - assert initDateTime(30, mDec, 2019, 00, 00, 00) == initDateTime(dMon, 01, 2020.IsoYear, 00, 00, 00) - assert initDateTime(13, mSep, 2020, 00, 00, 00) == initDateTime(dSun, 37, 2020.IsoYear, 00, 00, 00) - assert initDateTime(2, mJan, 2021, 00, 00, 00) == initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00) - - # source https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm - let d = isoweek * 7 + weekday.int - initDateTime(4, mJan, isoyear.int, 00, 00, 00).weekday.int - 4 - initDateTime(1, mJan, isoyear.int, hour, minute, second, nanosecond, zone) + initTimeInterval(days=d) - -proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear, - hour: HourRange, minute: MinuteRange, second: SecondRange, - zone: Timezone = local()): DateTime {.since: (1, 5).} = - initDateTime(weekday, isoweek, isoyear, hour, minute, second, 0, zone) - # # Other # @@ -2681,10 +2562,7 @@ proc epochTime*(): float {.tags: [TimeEffect].} = ## because sub-second resolution is likely to be supported (depending ## on the hardware/OS). ## - ## `getTime` should generally be preferred over this proc. - ## - ## .. warning:: Unsuitable for benchmarking (but still better than `now`), - ## use `monotimes.getMonoTime` or `cpuTime` instead, depending on the use case. + ## ``getTime`` should generally be preferred over this proc. when defined(macosx): var a {.noinit.}: Timeval gettimeofday(a) @@ -2719,11 +2597,11 @@ when not defined(js): proc cpuTime*(): float {.tags: [TimeEffect].} = ## Gets time spent that the CPU spent to run the current process in - ## seconds. This may be more useful for benchmarking than `epochTime`. + ## seconds. This may be more useful for benchmarking than ``epochTime``. ## However, it may measure the real time instead (depending on the OS). ## The value of the result has no meaning. ## To generate useful timing values, take the difference between - ## the results of two `cpuTime` calls: + ## the results of two ``cpuTime`` calls: runnableExamples: var t0 = cpuTime() # some useless work here (calculate fibonacci) diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index c20f9e645..7b6c94cbd 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -15,187 +15,103 @@ import std/private/since export system.`$` # for backward compatibility -when defined(nimPreviewSlimSystem): - import std/assertions - - -type HoleyEnum* = (not Ordinal) and enum ## Enum with holes. -type OrdinalEnum* = Ordinal and enum ## Enum without holes. - -runnableExamples: - type A = enum a0 = 2, a1 = 4, a2 - type B = enum b0 = 2, b1, b2 - assert A is enum - assert A is HoleyEnum - assert A isnot OrdinalEnum - assert B isnot HoleyEnum - assert B is OrdinalEnum - assert int isnot HoleyEnum - type C[T] = enum h0 = 2, h1 = 4 - assert C[float] is HoleyEnum - -proc name*(t: typedesc): string {.magic: "TypeTrait".} = - ## Returns the name of `t`. +proc name*(t: typedesc): string {.magic: "TypeTrait".} + ## Returns the name of the given type. ## - ## Alias for `system.\`$\`(t) `_ since Nim v0.20. - runnableExamples: - doAssert name(int) == "int" - doAssert name(seq[string]) == "seq[string]" + ## Alias for system.`$`(t) since Nim v0.20. proc arity*(t: typedesc): int {.magic: "TypeTrait".} = - ## Returns the arity of `t`. This is the number of "type" - ## components or the number of generic parameters a given type `t` has. + ## Returns the arity of the given type. This is the number of "type" + ## components or the number of generic parameters a given type ``t`` has. runnableExamples: - doAssert arity(int) == 0 - doAssert arity(seq[string]) == 1 - doAssert arity(array[3, int]) == 2 - doAssert arity((int, int, float, string)) == 4 + assert arity(seq[string]) == 1 + assert arity(array[3, int]) == 2 + assert arity((int, int, float, string)) == 4 -proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".} = +proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".} ## Accepts an instantiated generic type and returns its ## uninstantiated form. + ## + ## For example: + ## * `seq[int].genericHead` will be just `seq` + ## * `seq[int].genericHead[float]` will be `seq[float]` + ## ## A compile-time error will be produced if the supplied type ## is not generic. ## - ## **See also:** - ## * `stripGenericParams proc <#stripGenericParams,typedesc>`_ - runnableExamples: - type - Foo[T] = object - FooInst = Foo[int] - Foo2 = genericHead(FooInst) + ## See also: + ## * `stripGenericParams <#stripGenericParams,typedesc>`_ + ## + ## Example: + ## + ## .. code-block:: nim + ## type + ## Functor[A] = concept f + ## type MatchedGenericType = genericHead(typeof(f)) + ## # `f` will be a value of a type such as `Option[T]` + ## # `MatchedGenericType` will become the `Option` type - doAssert Foo2 is Foo and Foo is Foo2 - doAssert genericHead(Foo[seq[string]]) is Foo - doAssert not compiles(genericHead(int)) - type Generic = concept f - type _ = genericHead(typeof(f)) - - proc bar(a: Generic): typeof(a) = a - - doAssert bar(Foo[string].default) == Foo[string]() - doAssert not compiles bar(string.default) - - when false: # these don't work yet - doAssert genericHead(Foo[int])[float] is Foo[float] - doAssert seq[int].genericHead is seq - -proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} = +proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} ## This trait is similar to `genericHead <#genericHead,typedesc>`_, but - ## instead of producing an error for non-generic types, it will just return + ## instead of producing error for non-generic types, it will just return ## them unmodified. - runnableExamples: - type Foo[T] = object - - doAssert stripGenericParams(Foo[string]) is Foo - doAssert stripGenericParams(int) is int proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".} - ## Returns true if `t` is safe to use for `copyMem`:idx:. + ## This trait returns true if the type ``t`` is safe to use for + ## `copyMem`:idx:. ## ## Other languages name a type like these `blob`:idx:. -proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".} = - ## Returns true for named tuples, false for any other type. - runnableExamples: - doAssert not isNamedTuple(int) - doAssert not isNamedTuple((string, int)) - doAssert isNamedTuple(tuple[name: string, age: int]) +proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".} + ## Return true for named tuples, false for any other type. -template pointerBase*[T](_: typedesc[ptr T | ref T]): typedesc = - ## Returns `T` for `ref T | ptr T`. - runnableExamples: - assert (ref int).pointerBase is int - type A = ptr seq[float] - assert A.pointerBase is seq[float] - assert (ref A).pointerBase is A # not seq[float] - assert (var s = "abc"; s[0].addr).typeof.pointerBase is char - T - -proc distinctBase*(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} = - ## Returns the base type for distinct types, or the type itself otherwise. - ## If `recursive` is false, only the immediate distinct base will be returned. - ## - ## **See also:** - ## * `distinctBase template <#distinctBase.t,T,static[bool]>`_ - runnableExamples: - type MyInt = distinct int - type MyOtherInt = distinct MyInt - doAssert distinctBase(MyInt) is int - doAssert distinctBase(MyOtherInt) is int - doAssert distinctBase(MyOtherInt, false) is MyInt - doAssert distinctBase(int) is int +proc distinctBase*(T: typedesc): typedesc {.magic: "TypeTrait".} + ## Returns base type for distinct types, works only for distinct types. + ## compile time error otherwise since (1, 1): - template distinctBase*[T](a: T, recursive: static bool = true): untyped = - ## Overload of `distinctBase <#distinctBase,typedesc,static[bool]>`_ for values. + template distinctBase*[T](a: T): untyped = + ## overload for values runnableExamples: type MyInt = distinct int - type MyOtherInt = distinct MyInt doAssert 12.MyInt.distinctBase == 12 - doAssert 12.MyOtherInt.distinctBase == 12 - doAssert 12.MyOtherInt.distinctBase(false) is MyInt - doAssert 12.distinctBase == 12 - when T is distinct: - distinctBase(typeof(a), recursive)(a) - else: # avoids hint ConvFromXtoItselfNotNeeded - a + distinctBase(type(a))(a) - proc tupleLen*(T: typedesc[tuple]): int {.magic: "TypeTrait".} = - ## Returns the number of elements of the tuple type `T`. - ## - ## **See also:** - ## * `tupleLen template <#tupleLen.t>`_ - runnableExamples: - doAssert tupleLen((int, int, float, string)) == 4 - doAssert tupleLen(tuple[name: string, age: int]) == 2 + proc tupleLen*(T: typedesc[tuple]): int {.magic: "TypeTrait".} + ## Return number of elements of `T` template tupleLen*(t: tuple): int = - ## Returns the number of elements of the tuple `t`. - ## - ## **See also:** - ## * `tupleLen proc <#tupleLen,typedesc>`_ - runnableExamples: - doAssert tupleLen((1, 2)) == 2 - - tupleLen(typeof(t)) + ## Return number of elements of `t` + tupleLen(type(t)) template get*(T: typedesc[tuple], i: static int): untyped = - ## Returns the `i`-th element of `T`. + ## Return `i`\th element of `T` # Note: `[]` currently gives: `Error: no generic parameters allowed for ...` - runnableExamples: - doAssert get((int, int, float, string), 2) is float - - typeof(default(T)[i]) + type(default(T)[i]) type StaticParam*[value: static type] = object - ## Used to wrap a static value in `genericParams <#genericParams.t,typedesc>`_. + ## used to wrap a static value in `genericParams` since (1, 3, 5): template elementType*(a: untyped): typedesc = - ## Returns the element type of `a`, which can be any iterable (over which you - ## can iterate). + ## return element type of `a`, which can be any iterable (over which you + ## can iterate) runnableExamples: iterator myiter(n: int): auto = - for i in 0 ..< n: - yield i - + for i in 0..`_ include "system/inclrtl" -import std/strbasics -template toOa(s: string): auto = s.toOpenArray(0, s.high) - -proc substr(s: openArray[char] , first, last: int): string = - # Copied substr from system - let first = max(first, 0) - let L = max(min(last, high(s)) - first + 1, 0) - result = newString(L) - for i in 0 .. L-1: - result[i] = s[i+first] type RuneImpl = int32 # underlying type of Rune @@ -42,7 +32,7 @@ type template ones(n: untyped): untyped = ((1 shl n)-1) -proc runeLen*(s: openArray[char]): int {.rtl, extern: "nuc$1".} = +proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} = ## Returns the number of runes of the string ``s``. runnableExamples: let a = "añyóng" @@ -61,7 +51,7 @@ proc runeLen*(s: openArray[char]): int {.rtl, extern: "nuc$1".} = else: inc i inc(result) -proc runeLenAt*(s: openArray[char], i: Natural): int = +proc runeLenAt*(s: string, i: Natural): int = ## Returns the number of bytes the rune starting at ``s[i]`` takes. ## ## See also: @@ -81,7 +71,7 @@ proc runeLenAt*(s: openArray[char], i: Natural): int = const replRune = Rune(0xFFFD) -template fastRuneAt*(s: openArray[char] or string, i: int, result: untyped, doInc = true) = +template fastRuneAt*(s: string, i: int, result: untyped, doInc = true) = ## Returns the rune ``s[i]`` in ``result``. ## ## If ``doInc == true`` (default), ``i`` is incremented by the number @@ -159,7 +149,7 @@ template fastRuneAt*(s: openArray[char] or string, i: int, result: untyped, doIn result = Rune(uint(s[i])) when doInc: inc(i) -proc runeAt*(s: openArray[char], i: Natural): Rune = +proc runeAt*(s: string, i: Natural): Rune = ## Returns the rune in ``s`` at **byte index** ``i``. ## ## See also: @@ -173,7 +163,7 @@ proc runeAt*(s: openArray[char], i: Natural): Rune = doAssert a.runeAt(3) == "y".runeAt(0) fastRuneAt(s, i, result, false) -proc validateUtf8*(s: openArray[char]): int = +proc validateUtf8*(s: string): int = ## Returns the position of the invalid byte in ``s`` if the string ``s`` does ## not hold valid UTF-8 data. Otherwise ``-1`` is returned. ## @@ -310,7 +300,7 @@ proc `$`*(runes: seq[Rune]): string = for rune in runes: result.add rune -proc runeOffset*(s: openArray[char], pos: Natural, start: Natural = 0): int = +proc runeOffset*(s: string, pos: Natural, start: Natural = 0): int = ## Returns the byte position of rune ## at position ``pos`` in ``s`` with an optional start byte position. ## Returns the special value -1 if it runs out of the string. @@ -337,13 +327,13 @@ proc runeOffset*(s: openArray[char], pos: Natural, start: Natural = 0): int = inc i return o -proc runeReverseOffset*(s: openArray[char], rev: Positive): (int, int) = +proc runeReverseOffset*(s: string, rev: Positive): (int, int) = ## Returns a tuple with the byte offset of the ## rune at position ``rev`` in ``s``, counting ## from the end (starting with 1) and the total ## number of runes in the string. ## - ## Returns a negative value for offset if there are too few runes in + ## Returns a negative value for offset if there are to few runes in ## the string to satisfy the request. ## ## **Beware:** This can lead to unoptimized code and slow execution! @@ -356,16 +346,18 @@ proc runeReverseOffset*(s: openArray[char], rev: Positive): (int, int) = a = rev.int o = 0 x = 0 - let times = 2*rev.int-s.runeLen # transformed from rev.int - a < s.runeLen - rev.int while o < s.len: let r = runeLenAt(s, o) o += r - if a > times: + if a < 0: x += r dec a - result = if a > 0: (-a, rev.int-a) else: (x, -a+rev.int) -proc runeAtPos*(s: openArray[char], pos: int): Rune = + if a > 0: + return (-a, rev.int-a) + return (x, -a+rev.int) + +proc runeAtPos*(s: string, pos: int): Rune = ## Returns the rune at position ``pos``. ## ## **Beware:** This can lead to unoptimized code and slow execution! @@ -378,7 +370,7 @@ proc runeAtPos*(s: openArray[char], pos: int): Rune = ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_ fastRuneAt(s, runeOffset(s, pos), result, false) -proc runeStrAtPos*(s: openArray[char], pos: Natural): string = +proc runeStrAtPos*(s: string, pos: Natural): string = ## Returns the rune at position ``pos`` as UTF8 String. ## ## **Beware:** This can lead to unoptimized code and slow execution! @@ -390,9 +382,9 @@ proc runeStrAtPos*(s: openArray[char], pos: Natural): string = ## * `runeAtPos proc <#runeAtPos,string,int>`_ ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_ let o = runeOffset(s, pos) - substr(s.toOpenArray(o, (o+runeLenAt(s, o)-1))) + s[o .. (o+runeLenAt(s, o)-1)] -proc runeSubStr*(s: openArray[char], pos: int, len: int = int.high): string = +proc runeSubStr*(s: string, pos: int, len: int = int.high): string = ## Returns the UTF-8 substring starting at code point ``pos`` ## with ``len`` code points. ## @@ -411,7 +403,7 @@ proc runeSubStr*(s: openArray[char], pos: int, len: int = int.high): string = if pos < 0: let (o, rl) = runeReverseOffset(s, -pos) if len >= rl: - result = s.substr(o, s.high) + result = s.substr(o, s.len-1) elif len < 0: let e = rl + len if e < 0: @@ -636,7 +628,7 @@ template runeCheck(s, runeProc) = fastRuneAt(s, i, rune, doInc = true) result = runeProc(rune) and result -proc isAlpha*(s: openArray[char]): bool {.noSideEffect, +proc isAlpha*(s: string): bool {.noSideEffect, rtl, extern: "nuc$1Str".} = ## Returns true if ``s`` contains all alphabetic runes. runnableExamples: @@ -644,7 +636,7 @@ proc isAlpha*(s: openArray[char]): bool {.noSideEffect, doAssert a.isAlpha runeCheck(s, isAlpha) -proc isSpace*(s: openArray[char]): bool {.noSideEffect, +proc isSpace*(s: string): bool {.noSideEffect, rtl, extern: "nuc$1Str".} = ## Returns true if ``s`` contains all whitespace runes. runnableExamples: @@ -665,21 +657,21 @@ template convertRune(s, runeProc) = rune = runeProc(rune) fastToUTF8Copy(rune, result, resultIndex, doInc = true) -proc toUpper*(s: openArray[char]): string {.noSideEffect, +proc toUpper*(s: string): string {.noSideEffect, rtl, extern: "nuc$1Str".} = ## Converts ``s`` into upper-case runes. runnableExamples: doAssert toUpper("abγ") == "ABΓ" convertRune(s, toUpper) -proc toLower*(s: openArray[char]): string {.noSideEffect, +proc toLower*(s: string): string {.noSideEffect, rtl, extern: "nuc$1Str".} = ## Converts ``s`` into lower-case runes. runnableExamples: doAssert toLower("ABΓ") == "abγ" convertRune(s, toLower) -proc swapCase*(s: openArray[char]): string {.noSideEffect, +proc swapCase*(s: string): string {.noSideEffect, rtl, extern: "nuc$1".} = ## Swaps the case of runes in ``s``. ## @@ -701,7 +693,7 @@ proc swapCase*(s: openArray[char]): string {.noSideEffect, rune = rune.toUpper() fastToUTF8Copy(rune, result, resultIndex, doInc = true) -proc capitalize*(s: openArray[char]): string {.noSideEffect, +proc capitalize*(s: string): string {.noSideEffect, rtl, extern: "nuc$1".} = ## Converts the first character of ``s`` into an upper-case rune. runnableExamples: @@ -713,13 +705,10 @@ proc capitalize*(s: openArray[char]): string {.noSideEffect, rune: Rune i = 0 fastRuneAt(s, i, rune, doInc = true) - result = $toUpper(rune) & substr(s.toOpenArray(i, s.high)) + result = $toUpper(rune) & substr(s, i) -when not defined(nimHasEffectsOf): - {.pragma: effectsOf.} - -proc translate*(s: openArray[char], replacements: proc(key: string): string): string {. - rtl, extern: "nuc$1", effectsOf: replacements.} = +proc translate*(s: string, replacements: proc(key: string): string): string {. + rtl, extern: "nuc$1".} = ## Translates words in a string using the ``replacements`` proc to substitute ## words inside ``s`` with their replacements. ## @@ -753,7 +742,7 @@ proc translate*(s: openArray[char], replacements: proc(key: string): string): st if whiteSpace and inWord: # If we've reached the end of a word - let word = substr(s.toOpenArray(wordStart, lastIndex - 1)) + let word = s[wordStart ..< lastIndex] result.add(replacements(word)) result.add($rune) inWord = false @@ -768,10 +757,10 @@ proc translate*(s: openArray[char], replacements: proc(key: string): string): st if wordStart < len(s) and inWord: # Get the trailing word at the end - let word = substr(s.toOpenArray(wordStart, s.high)) + let word = s[wordStart .. ^1] result.add(replacements(word)) -proc title*(s: openArray[char]): string {.noSideEffect, +proc title*(s: string): string {.noSideEffect, rtl, extern: "nuc$1".} = ## Converts ``s`` to a unicode title. ## @@ -797,7 +786,7 @@ proc title*(s: openArray[char]): string {.noSideEffect, fastToUTF8Copy(rune, result, resultIndex, doInc = true) -iterator runes*(s: openArray[char]): Rune = +iterator runes*(s: string): Rune = ## Iterates over any rune of the string ``s`` returning runes. var i = 0 @@ -806,7 +795,7 @@ iterator runes*(s: openArray[char]): Rune = fastRuneAt(s, i, result, true) yield result -iterator utf8*(s: openArray[char]): string = +iterator utf8*(s: string): string = ## Iterates over any rune of the string ``s`` returning utf8 values. ## ## See also: @@ -817,10 +806,10 @@ iterator utf8*(s: openArray[char]): string = var o = 0 while o < s.len: let n = runeLenAt(s, o) - yield substr(s.toOpenArray(o, (o+n-1))) + yield s[o .. (o+n-1)] o += n -proc toRunes*(s: openArray[char]): seq[Rune] = +proc toRunes*(s: string): seq[Rune] = ## Obtains a sequence containing the Runes in ``s``. ## ## See also: @@ -833,7 +822,7 @@ proc toRunes*(s: openArray[char]): seq[Rune] = for r in s.runes: result.add(r) -proc cmpRunesIgnoreCase*(a, b: openArray[char]): int {.rtl, extern: "nuc$1".} = +proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1".} = ## Compares two UTF-8 strings and ignores the case. Returns: ## ## | 0 if a == b @@ -850,7 +839,7 @@ proc cmpRunesIgnoreCase*(a, b: openArray[char]): int {.rtl, extern: "nuc$1".} = if result != 0: return result = a.len - b.len -proc reversed*(s: openArray[char]): string = +proc reversed*(s: string): string = ## Returns the reverse of ``s``, interpreting it as runes. ## ## Unicode combining characters are correctly interpreted as well. @@ -885,9 +874,9 @@ proc reversed*(s: openArray[char]): string = reverseUntil(len(s)) -proc graphemeLen*(s: openArray[char]; i: Natural): Natural = +proc graphemeLen*(s: string; i: Natural): Natural = ## The number of bytes belonging to byte index ``s[i]``, - ## including following combining code units. + ## including following combining code unit. runnableExamples: let a = "añyóng" doAssert a.graphemeLen(1) == 2 ## ñ @@ -904,7 +893,7 @@ proc graphemeLen*(s: openArray[char]; i: Natural): Natural = if not isCombining(r2): break result = j-i -proc lastRune*(s: openArray[char]; last: int): (Rune, int) = +proc lastRune*(s: string; last: int): (Rune, int) = ## Length of the last rune in ``s[0..last]``. Returns the rune and its length ## in bytes. if s[last] <= chr(127): @@ -933,12 +922,12 @@ proc size*(r: Rune): int {.noSideEffect.} = else: result = 1 # --------- Private templates for different split separators ----------- -proc stringHasSep(s: openArray[char], index: int, seps: openArray[Rune]): bool = +proc stringHasSep(s: string, index: int, seps: openArray[Rune]): bool = var rune: Rune fastRuneAt(s, index, rune, false) return seps.contains(rune) -proc stringHasSep(s: openArray[char], index: int, sep: Rune): bool = +proc stringHasSep(s: string, index: int, sep: Rune): bool = var rune: Rune fastRuneAt(s, index, rune, false) return sep == rune @@ -956,38 +945,56 @@ template splitCommon(s, sep, maxsplit: untyped) = while last < sLen and not stringHasSep(s, last, sep): inc(last, runeLenAt(s, last)) if splits == 0: last = sLen - yield substr(s.toOpenArray(first, (last - 1))) + yield s[first .. (last - 1)] if splits == 0: break dec(splits) inc(last, if last < sLen: runeLenAt(s, last) else: 1) -iterator split*(s: openArray[char], seps: openArray[Rune] = unicodeSpaces, +iterator split*(s: string, seps: openArray[Rune] = unicodeSpaces, maxsplit: int = -1): string = ## Splits the unicode string ``s`` into substrings using a group of separators. ## ## Substrings are separated by a substring containing only ``seps``. - runnableExamples: - import std/sequtils - - assert toSeq("hÃllo\lthis\lis an\texample\l是".split) == - @["hÃllo", "this", "is", "an", "example", "是"] - - # And the following code splits the same string using a sequence of Runes. - assert toSeq(split("añyóng:hÃllo;是$example", ";:$".toRunes)) == - @["añyóng", "hÃllo", "是", "example"] - - # example with a `Rune` separator and unused one `;`: - assert toSeq(split("ab是de:f:", ";:是".toRunes)) == @["ab", "de", "f", ""] - - # Another example that splits a string containing a date. - let date = "2012-11-20T22:08:08.398990" - - assert toSeq(split(date, " -:T".toRunes)) == - @["2012", "11", "20", "22", "08", "08.398990"] - + ## + ## .. code-block:: nim + ## for word in split("this\lis an\texample"): + ## writeLine(stdout, word) + ## + ## ...generates this output: + ## + ## .. code-block:: + ## "this" + ## "is" + ## "an" + ## "example" + ## + ## And the following code: + ## + ## .. code-block:: nim + ## for word in split("this:is;an$example", {';', ':', '$'}): + ## writeLine(stdout, word) + ## + ## ...produces the same output as the first example. The code: + ## + ## .. code-block:: nim + ## let date = "2012-11-20T22:08:08.398990" + ## let separators = {' ', '-', ':', 'T'} + ## for number in split(date, separators): + ## writeLine(stdout, number) + ## + ## ...results in: + ## + ## .. code-block:: + ## "2012" + ## "11" + ## "20" + ## "22" + ## "08" + ## "08.398990" + ## splitCommon(s, seps, maxsplit) -iterator splitWhitespace*(s: openArray[char]): string = +iterator splitWhitespace*(s: string): string = ## Splits a unicode string at whitespace runes. splitCommon(s, unicodeSpaces, -1) @@ -995,36 +1002,51 @@ template accResult(iter: untyped) = result = @[] for x in iter: add(result, x) -proc splitWhitespace*(s: openArray[char]): seq[string] {.noSideEffect, +proc splitWhitespace*(s: string): seq[string] {.noSideEffect, rtl, extern: "ncuSplitWhitespace".} = ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_ ## iterator, but is a proc that returns a sequence of substrings. accResult(splitWhitespace(s)) -iterator split*(s: openArray[char], sep: Rune, maxsplit: int = -1): string = +iterator split*(s: string, sep: Rune, maxsplit: int = -1): string = ## Splits the unicode string ``s`` into substrings using a single separator. + ## ## Substrings are separated by the rune ``sep``. - runnableExamples: - import std/sequtils - - assert toSeq(split(";;hÃllo;this;is;an;;example;;;是", ";".runeAt(0))) == - @["", "", "hÃllo", "this", "is", "an", "", "example", "", "", "是"] - + ## The code: + ## + ## .. code-block:: nim + ## for word in split(";;this;is;an;;example;;;", ';'): + ## writeLine(stdout, word) + ## + ## Results in: + ## + ## .. code-block:: + ## "" + ## "" + ## "this" + ## "is" + ## "an" + ## "" + ## "example" + ## "" + ## "" + ## "" + ## splitCommon(s, sep, maxsplit) -proc split*(s: openArray[char], seps: openArray[Rune] = unicodeSpaces, maxsplit: int = -1): +proc split*(s: string, seps: openArray[Rune] = unicodeSpaces, maxsplit: int = -1): seq[string] {.noSideEffect, rtl, extern: "nucSplitRunes".} = ## The same as the `split iterator <#split.i,string,openArray[Rune],int>`_, ## but is a proc that returns a sequence of substrings. accResult(split(s, seps, maxsplit)) -proc split*(s: openArray[char], sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffect, +proc split*(s: string, sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffect, rtl, extern: "nucSplitRune".} = ## The same as the `split iterator <#split.i,string,Rune,int>`_, but is a proc ## that returns a sequence of substrings. accResult(split(s, sep, maxsplit)) -proc strip*(s: openArray[char], leading = true, trailing = true, +proc strip*(s: string, leading = true, trailing = true, runes: openArray[Rune] = unicodeSpaces): string {.noSideEffect, rtl, extern: "nucStrip".} = ## Strips leading or trailing ``runes`` from ``s`` and returns @@ -1079,7 +1101,7 @@ proc strip*(s: openArray[char], leading = true, trailing = true, let newLen = eI - sI + 1 result = newStringOfCap(newLen) if newLen > 0: - result.add substr(s.toOpenArray(sI, eI)) + result.add s[sI .. eI] proc repeat*(c: Rune, count: Natural): string {.noSideEffect, rtl, extern: "nucRepeatRune".} = @@ -1095,7 +1117,7 @@ proc repeat*(c: Rune, count: Natural): string {.noSideEffect, for i in 0 ..< count: result.add s -proc align*(s: openArray[char], count: Natural, padding = ' '.Rune): string {. +proc align*(s: string, count: Natural, padding = ' '.Rune): string {. noSideEffect, rtl, extern: "nucAlignString".} = ## Aligns a unicode string ``s`` with ``padding``, so that it has a rune-length ## of ``count``. @@ -1120,9 +1142,9 @@ proc align*(s: openArray[char], count: Natural, padding = ' '.Rune): string {. for i in 0 ..< spaces: result.add padStr result.add s else: - result = s.substr + result = s -proc alignLeft*(s: openArray[char], count: Natural, padding = ' '.Rune): string {. +proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {. noSideEffect.} = ## Left-aligns a unicode string ``s`` with ``padding``, so that it has a ## rune-length of ``count``. @@ -1146,365 +1168,4 @@ proc alignLeft*(s: openArray[char], count: Natural, padding = ' '.Rune): string for i in sLen ..< count: result.add padStr else: - result = s.substr - - -proc runeLen*(s: string): int {.inline.} = - ## Returns the number of runes of the string ``s``. - runnableExamples: - let a = "añyóng" - doAssert a.runeLen == 6 - ## note: a.len == 8 - runeLen(toOa(s)) - -proc runeLenAt*(s: string, i: Natural): int {.inline.} = - ## Returns the number of bytes the rune starting at ``s[i]`` takes. - ## - ## See also: - ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_ - runnableExamples: - let a = "añyóng" - doAssert a.runeLenAt(0) == 1 - doAssert a.runeLenAt(1) == 2 - runeLenAt(toOa(s), i) - -proc runeAt*(s: string, i: Natural): Rune {.inline.} = - ## Returns the rune in ``s`` at **byte index** ``i``. - ## - ## See also: - ## * `runeAtPos proc <#runeAtPos,string,int>`_ - ## * `runeStrAtPos proc <#runeStrAtPos,string,Natural>`_ - ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_ - runnableExamples: - let a = "añyóng" - doAssert a.runeAt(1) == "ñ".runeAt(0) - doAssert a.runeAt(2) == "ñ".runeAt(1) - doAssert a.runeAt(3) == "y".runeAt(0) - fastRuneAt(s, i, result, false) - -proc validateUtf8*(s: string): int {.inline.} = - ## Returns the position of the invalid byte in ``s`` if the string ``s`` does - ## not hold valid UTF-8 data. Otherwise ``-1`` is returned. - ## - ## See also: - ## * `toUTF8 proc <#toUTF8,Rune>`_ - ## * `$ proc <#$,Rune>`_ alias for `toUTF8` - ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_ - validateUtf8(toOa(s)) - -proc runeOffset*(s: string, pos: Natural, start: Natural = 0): int {.inline.} = - ## Returns the byte position of rune - ## at position ``pos`` in ``s`` with an optional start byte position. - ## Returns the special value -1 if it runs out of the string. - ## - ## **Beware:** This can lead to unoptimized code and slow execution! - ## Most problems can be solved more efficiently by using an iterator - ## or conversion to a seq of Rune. - ## - ## See also: - ## * `runeReverseOffset proc <#runeReverseOffset,string,Positive>`_ - runnableExamples: - let a = "añyóng" - doAssert a.runeOffset(1) == 1 - doAssert a.runeOffset(3) == 4 - doAssert a.runeOffset(4) == 6 - runeOffset(toOa(s), pos, start) - -proc runeReverseOffset*(s: string, rev: Positive): (int, int) {.inline.} = - ## Returns a tuple with the byte offset of the - ## rune at position ``rev`` in ``s``, counting - ## from the end (starting with 1) and the total - ## number of runes in the string. - ## - ## Returns a negative value for offset if there are too few runes in - ## the string to satisfy the request. - ## - ## **Beware:** This can lead to unoptimized code and slow execution! - ## Most problems can be solved more efficiently by using an iterator - ## or conversion to a seq of Rune. - ## - ## See also: - ## * `runeOffset proc <#runeOffset,string,Natural,Natural>`_ - runeReverseOffset(toOa(s), rev) - -proc runeAtPos*(s: string, pos: int): Rune {.inline.} = - ## Returns the rune at position ``pos``. - ## - ## **Beware:** This can lead to unoptimized code and slow execution! - ## Most problems can be solved more efficiently by using an iterator - ## or conversion to a seq of Rune. - ## - ## See also: - ## * `runeAt proc <#runeAt,string,Natural>`_ - ## * `runeStrAtPos proc <#runeStrAtPos,string,Natural>`_ - ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_ - fastRuneAt(toOa(s), runeOffset(s, pos), result, false) - -proc runeStrAtPos*(s: string, pos: Natural): string {.inline.} = - ## Returns the rune at position ``pos`` as UTF8 String. - ## - ## **Beware:** This can lead to unoptimized code and slow execution! - ## Most problems can be solved more efficiently by using an iterator - ## or conversion to a seq of Rune. - ## - ## See also: - ## * `runeAt proc <#runeAt,string,Natural>`_ - ## * `runeAtPos proc <#runeAtPos,string,int>`_ - ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_ - let o = runeOffset(s, pos) - substr(s.toOpenArray(o, (o+runeLenAt(s, o)-1))) - -proc runeSubStr*(s: string, pos: int, len: int = int.high): string {.inline.} = - ## Returns the UTF-8 substring starting at code point ``pos`` - ## with ``len`` code points. - ## - ## If ``pos`` or ``len`` is negative they count from - ## the end of the string. If ``len`` is not given it means the longest - ## possible string. - runnableExamples: - let s = "Hänsel ««: 10,00€" - doAssert(runeSubStr(s, 0, 2) == "Hä") - doAssert(runeSubStr(s, 10, 1) == ":") - doAssert(runeSubStr(s, -6) == "10,00€") - doAssert(runeSubStr(s, 10) == ": 10,00€") - doAssert(runeSubStr(s, 12, 5) == "10,00") - doAssert(runeSubStr(s, -6, 3) == "10,") - runeSubStr(toOa(s), pos, len) - - -proc isAlpha*(s: string): bool {.noSideEffect, inline.} = - ## Returns true if ``s`` contains all alphabetic runes. - runnableExamples: - let a = "añyóng" - doAssert a.isAlpha - isAlpha(toOa(s)) - -proc isSpace*(s: string): bool {.noSideEffect, inline.} = - ## Returns true if ``s`` contains all whitespace runes. - runnableExamples: - let a = "\t\l \v\r\f" - doAssert a.isSpace - isSpace(toOa(s)) - - -proc toUpper*(s: string): string {.noSideEffect, inline.} = - ## Converts ``s`` into upper-case runes. - runnableExamples: - doAssert toUpper("abγ") == "ABΓ" - toUpper(toOa(s)) - -proc toLower*(s: string): string {.noSideEffect, inline.} = - ## Converts ``s`` into lower-case runes. - runnableExamples: - doAssert toLower("ABΓ") == "abγ" - toLower(toOa(s)) - -proc swapCase*(s: string): string {.noSideEffect, inline.} = - ## Swaps the case of runes in ``s``. - ## - ## Returns a new string such that the cases of all runes - ## are swapped if possible. - runnableExamples: - doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA" - swapCase(toOa(s)) - -proc capitalize*(s: string): string {.noSideEffect.} = - ## Converts the first character of ``s`` into an upper-case rune. - runnableExamples: - doAssert capitalize("βeta") == "Βeta" - capitalize(toOa(s)) - - -proc translate*(s: string, replacements: proc(key: string): string): string {.effectsOf: replacements, inline.} = - ## Translates words in a string using the ``replacements`` proc to substitute - ## words inside ``s`` with their replacements. - ## - ## ``replacements`` is any proc that takes a word and returns - ## a new word to fill it's place. - runnableExamples: - proc wordToNumber(s: string): string = - case s - of "one": "1" - of "two": "2" - else: s - let a = "one two three four" - doAssert a.translate(wordToNumber) == "1 2 three four" - translate(toOa(s), replacements) - -proc title*(s: string): string {.noSideEffect, inline.} = - ## Converts ``s`` to a unicode title. - ## - ## Returns a new string such that the first character - ## in each word inside ``s`` is capitalized. - runnableExamples: - doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma" - title(toOa(s)) - - -iterator runes*(s: string): Rune = - ## Iterates over any rune of the string ``s`` returning runes. - for rune in runes(toOa(s)): - yield rune - -iterator utf8*(s: string): string = - ## Iterates over any rune of the string ``s`` returning utf8 values. - ## - ## See also: - ## * `validateUtf8 proc <#validateUtf8,string>`_ - ## * `toUTF8 proc <#toUTF8,Rune>`_ - ## * `$ proc <#$,Rune>`_ alias for `toUTF8` - ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_ - for str in utf8(toOa(s)): - yield str - -proc toRunes*(s: string): seq[Rune] {.inline.} = - ## Obtains a sequence containing the Runes in ``s``. - ## - ## See also: - ## * `$ proc <#$,Rune>`_ for a reverse operation - runnableExamples: - let a = toRunes("aáä") - doAssert a == @["a".runeAt(0), "á".runeAt(0), "ä".runeAt(0)] - toRunes(toOa(s)) - -proc cmpRunesIgnoreCase*(a, b: string): int {.inline.} = - ## Compares two UTF-8 strings and ignores the case. Returns: - ## - ## | 0 if a == b - ## | < 0 if a < b - ## | > 0 if a > b - cmpRunesIgnoreCase(a.toOa(), b.toOa()) - -proc reversed*(s: string): string {.inline.} = - ## Returns the reverse of ``s``, interpreting it as runes. - ## - ## Unicode combining characters are correctly interpreted as well. - runnableExamples: - assert reversed("Reverse this!") == "!siht esreveR" - assert reversed("先秦兩漢") == "漢兩秦先" - assert reversed("as⃝df̅") == "f̅ds⃝a" - assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞" - reversed(toOa(s)) - -proc graphemeLen*(s: string; i: Natural): Natural {.inline.} = - ## The number of bytes belonging to byte index ``s[i]``, - ## including following combining code unit. - runnableExamples: - let a = "añyóng" - doAssert a.graphemeLen(1) == 2 ## ñ - doAssert a.graphemeLen(2) == 1 - doAssert a.graphemeLen(4) == 2 ## ó - graphemeLen(toOa(s), i) - -proc lastRune*(s: string; last: int): (Rune, int) {.inline.} = - ## Length of the last rune in ``s[0..last]``. Returns the rune and its length - ## in bytes. - lastRune(toOa(s), last) - -iterator split*(s: string, seps: openArray[Rune] = unicodeSpaces, - maxsplit: int = -1): string = - ## Splits the unicode string ``s`` into substrings using a group of separators. - ## - ## Substrings are separated by a substring containing only ``seps``. - runnableExamples: - import std/sequtils - - assert toSeq("hÃllo\lthis\lis an\texample\l是".split) == - @["hÃllo", "this", "is", "an", "example", "是"] - - # And the following code splits the same string using a sequence of Runes. - assert toSeq(split("añyóng:hÃllo;是$example", ";:$".toRunes)) == - @["añyóng", "hÃllo", "是", "example"] - - # example with a `Rune` separator and unused one `;`: - assert toSeq(split("ab是de:f:", ";:是".toRunes)) == @["ab", "de", "f", ""] - - # Another example that splits a string containing a date. - let date = "2012-11-20T22:08:08.398990" - - assert toSeq(split(date, " -:T".toRunes)) == - @["2012", "11", "20", "22", "08", "08.398990"] - - splitCommon(toOa(s), seps, maxsplit) - -iterator splitWhitespace*(s: string): string = - ## Splits a unicode string at whitespace runes. - splitCommon(s.toOa(), unicodeSpaces, -1) - - -proc splitWhitespace*(s: string): seq[string] {.noSideEffect, inline.}= - ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_ - ## iterator, but is a proc that returns a sequence of substrings. - accResult(splitWhitespace(toOa(s))) - -iterator split*(s: string, sep: Rune, maxsplit: int = -1): string = - ## Splits the unicode string ``s`` into substrings using a single separator. - ## Substrings are separated by the rune ``sep``. - runnableExamples: - import std/sequtils - - assert toSeq(split(";;hÃllo;this;is;an;;example;;;是", ";".runeAt(0))) == - @["", "", "hÃllo", "this", "is", "an", "", "example", "", "", "是"] - - splitCommon(toOa(s), sep, maxsplit) - -proc split*(s: string, seps: openArray[Rune] = unicodeSpaces, maxsplit: int = -1): - seq[string] {.noSideEffect, inline.} = - ## The same as the `split iterator <#split.i,string,openArray[Rune],int>`_, - ## but is a proc that returns a sequence of substrings. - accResult(split(toOa(s), seps, maxsplit)) - -proc split*(s: string, sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffect, inline.} = - ## The same as the `split iterator <#split.i,string,Rune,int>`_, but is a proc - ## that returns a sequence of substrings. - accResult(split(toOa(s), sep, maxsplit)) - -proc strip*(s: string, leading = true, trailing = true, - runes: openArray[Rune] = unicodeSpaces): string {.noSideEffect, inline.} = - ## Strips leading or trailing ``runes`` from ``s`` and returns - ## the resulting string. - ## - ## If ``leading`` is true (default), leading ``runes`` are stripped. - ## If ``trailing`` is true (default), trailing ``runes`` are stripped. - ## If both are false, the string is returned unchanged. - runnableExamples: - let a = "\táñyóng " - doAssert a.strip == "áñyóng" - doAssert a.strip(leading = false) == "\táñyóng" - doAssert a.strip(trailing = false) == "áñyóng " - strip(toOa(s), leading, trailing, runes) - - -proc align*(s: string, count: Natural, padding = ' '.Rune): string {.noSideEffect, inline.} = - ## Aligns a unicode string ``s`` with ``padding``, so that it has a rune-length - ## of ``count``. - ## - ## ``padding`` characters (by default spaces) are added before ``s`` resulting in - ## right alignment. If ``s.runelen >= count``, no spaces are added and ``s`` is - ## returned unchanged. If you need to left align a string use the `alignLeft - ## proc <#alignLeft,string,Natural>`_. - runnableExamples: - assert align("abc", 4) == " abc" - assert align("a", 0) == "a" - assert align("1232", 6) == " 1232" - assert align("1232", 6, '#'.Rune) == "##1232" - assert align("Åge", 5) == " Åge" - assert align("×", 4, '_'.Rune) == "___×" - align(toOa(s), count, padding) - -proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {.noSideEffect, inline.} = - ## Left-aligns a unicode string ``s`` with ``padding``, so that it has a - ## rune-length of ``count``. - ## - ## ``padding`` characters (by default spaces) are added after ``s`` resulting in - ## left alignment. If ``s.runelen >= count``, no spaces are added and ``s`` is - ## returned unchanged. If you need to right align a string use the `align - ## proc <#align,string,Natural>`_. - runnableExamples: - assert alignLeft("abc", 4) == "abc " - assert alignLeft("a", 0) == "a" - assert alignLeft("1232", 6) == "1232 " - assert alignLeft("1232", 6, '#'.Rune) == "1232##" - assert alignLeft("Åge", 5) == "Åge " - assert alignLeft("×", 4, '_'.Rune) == "×___" - alignLeft(toOa(s), count, padding) + result = s diff --git a/lib/pure/unidecode/gen.py b/lib/pure/unidecode/gen.py index 2fb69f7b2..f0647ea6c 100644 --- a/lib/pure/unidecode/gen.py +++ b/lib/pure/unidecode/gen.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#! usr/bin/env python3 # -*- coding: utf-8 -*- # Generates the unidecode.dat module @@ -11,7 +11,7 @@ try: except ImportError: pass -def main(): +def main2(): f = open("unidecode.dat", "wb+") for x in range(128, 0xffff + 1): u = eval("u'\\u%04x'" % x) @@ -19,12 +19,12 @@ def main(): val = unidecode(u) # f.write("%x | " % x) - if x == 0x2028: # U+2028 = LINE SEPARATOR + if x==0x2028: # U+2028 = LINE SEPARATOR val = "" - elif x == 0x2029: # U+2029 = PARAGRAPH SEPARATOR + elif x==0x2029: # U+2028 = PARAGRAPH SEPARATOR val = "" f.write("%s\n" % val) f.close() -main() +main2() \ No newline at end of file diff --git a/lib/pure/unidecode/unidecode.nim b/lib/pure/unidecode/unidecode.nim index 9affc53f6..166608aeb 100644 --- a/lib/pure/unidecode/unidecode.nim +++ b/lib/pure/unidecode/unidecode.nim @@ -7,31 +7,30 @@ # distribution, for details about the copyright. # -## This module is based on Python's [Unidecode](https://pypi.org/project/Unidecode/) -## module by Tomaz Solc, which in turn is based on the -## [Text::Unidecode](https://metacpan.org/pod/Text::Unidecode) -## Perl module by Sean M. Burke. +## This module is based on Python's Unidecode module by Tomaz Solc, +## which in turn is based on the ``Text::Unidecode`` Perl module by +## Sean M. Burke +## (http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm ). ## -## It provides a `unidecode proc <#unidecode,string>`_ that does -## Unicode to ASCII transliterations: It finds the sequence of ASCII characters -## that is the closest approximation to the Unicode string. +## It provides a single proc that does Unicode to ASCII transliterations: +## It finds the sequence of ASCII characters that is the closest approximation +## to the Unicode string. ## ## For example, the closest to string "Äußerst" in ASCII is "Ausserst". Some ## information is lost in this transformation, of course, since several Unicode -## strings can be transformed to the same ASCII representation. So this is a -## strictly one-way transformation. However, a human reader will probably -## still be able to guess from the context, what the original string was. +## strings can be transformed in the same ASCII representation. So this is a +## strictly one-way transformation. However a human reader will probably +## still be able to guess what original string was meant from the context. ## -## This module needs the data file `unidecode.dat` to work: This file is -## embedded as a resource into your application by default. You can also -## define the symbol `--define:noUnidecodeTable` during compile time and -## use the `loadUnidecodeTable proc <#loadUnidecodeTable>`_ to initialize -## this module. +## This module needs the data file "unidecode.dat" to work: This file is +## embedded as a resource into your application by default. But you an also +## define the symbol ``--define:noUnidecodeTable`` during compile time and +## use the `loadUnidecodeTable` proc to initialize this module. -import std/unicode +import unicode when not defined(noUnidecodeTable): - import std/strutils + import strutils const translationTable = splitLines(slurp"unidecode/unidecode.dat") else: @@ -39,26 +38,25 @@ else: var translationTable: seq[string] proc loadUnidecodeTable*(datafile = "unidecode.dat") = - ## Loads the datafile that `unidecode <#unidecode,string>`_ needs to work. - ## This is only required if the module was compiled with the - ## `--define:noUnidecodeTable` switch. This needs to be called by the - ## main thread before any thread can make a call to `unidecode`. + ## loads the datafile that `unidecode` to work. This is only required if + ## the module was compiled with the ``--define:noUnidecodeTable`` switch. + ## This needs to be called by the main thread before any thread can make a + ## call to `unidecode`. when defined(noUnidecodeTable): newSeq(translationTable, 0xffff) var i = 0 for line in lines(datafile): - translationTable[i] = line + translationTable[i] = line.string inc(i) proc unidecode*(s: string): string = ## Finds the sequence of ASCII characters that is the closest approximation ## to the UTF-8 string `s`. runnableExamples: - doAssert unidecode("北京") == "Bei Jing " - doAssert unidecode("Äußerst") == "Ausserst" + assert unidecode("北京") == "Bei Jing " result = "" for r in runes(s): var c = int(r) if c <=% 127: add(result, chr(c)) - elif c <% translationTable.len: add(result, translationTable[c - 128]) + elif c <% translationTable.len: add(result, translationTable[c-128]) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 9dafa8f03..c387e14e1 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -108,10 +108,8 @@ import std/private/since import std/exitprocs -when defined(nimPreviewSlimSystem): - import std/assertions - -import macros, strutils, streams, times, sets, sequtils +import + macros, strutils, streams, times, sets, sequtils when declared(stdout): import os @@ -221,7 +219,7 @@ proc resetOutputFormatters* {.since: (1, 1).} = formatters = @[] proc newConsoleOutputFormatter*(outputLevel: OutputLevel = outputLevelDefault, - colorOutput = true): ConsoleOutputFormatter = + colorOutput = true): ConsoleOutputFormatter = ConsoleOutputFormatter( outputLevel: outputLevel, colorOutput: colorOutput @@ -249,7 +247,7 @@ proc colorOutput(): bool = deprecateEnvVarHere() result = false -proc defaultConsoleFormatter*(): ConsoleOutputFormatter = +proc defaultConsoleFormatter*(): ConsoleOutputFormatter = var colorOutput = colorOutput() var outputLevel = nimUnittestOutputLevel.parseEnum[:OutputLevel] when declared(stdout): @@ -318,7 +316,7 @@ proc xmlEscape(s: string): string = else: result.add(c) -proc newJUnitOutputFormatter*(stream: Stream): JUnitOutputFormatter = +proc newJUnitOutputFormatter*(stream: Stream): JUnitOutputFormatter = ## Creates a formatter that writes report to the specified stream in ## JUnit format. ## The ``stream`` is NOT closed automatically when the test are finished, @@ -433,6 +431,8 @@ proc matchFilter(suiteName, testName, filter: string): bool = return glob(suiteName, suiteAndTestFilters[0]) and glob(testName, suiteAndTestFilters[1]) +when defined(testing): export matchFilter + proc shouldRun(currentSuiteName, testName: string): bool = ## Check if a test should be run by matching suiteName and testName against ## test filters. @@ -515,9 +515,7 @@ template suite*(name, body) {.dirty.} = finally: suiteEnded() -proc exceptionTypeName(e: ref Exception): string {.inline.} = - if e == nil: "" - else: $e.name +template exceptionTypeName(e: typed): string = $e.name template test*(name, body) {.dirty.} = ## Define a single test case identified by `name`. @@ -544,24 +542,18 @@ template test*(name, body) {.dirty.} = for formatter in formatters: formatter.testStarted(name) - {.warning[BareExcept]:off.} try: when declared(testSetupIMPLFlag): testSetupIMPL() when declared(testTeardownIMPLFlag): defer: testTeardownIMPL() - {.warning[BareExcept]:on.} body - {.warning[BareExcept]:off.} except: let e = getCurrentException() let eTypeDesc = "[" & exceptionTypeName(e) & "]" checkpoint("Unhandled exception: " & getCurrentExceptionMsg() & " " & eTypeDesc) - if e == nil: # foreign - fail() - else: - var stackTrace {.inject.} = e.getStackTrace() - fail() + var stackTrace {.inject.} = e.getStackTrace() + fail() finally: if testStatusIMPL == TestStatus.FAILED: @@ -573,7 +565,6 @@ template test*(name, body) {.dirty.} = ) testEnded(testResult) checkpoints = @[] - {.warning[BareExcept]:on.} proc checkpoint*(msg: string) = ## Set a checkpoint identified by `msg`. Upon test failure all @@ -641,17 +632,19 @@ macro check*(conditions: untyped): untyped = ## Verify if a statement or a list of statements is true. ## A helpful error message and set checkpoints are printed out on ## failure (if ``outputLevel`` is not ``PRINT_NONE``). - runnableExamples: - import std/strutils - - check("AKB48".toLowerAscii() == "akb48") - - let teams = {'A', 'K', 'B', '4', '8'} - - check: - "AKB48".toLowerAscii() == "akb48" - 'C' notin teams - + ## Example: + ## + ## .. code-block:: nim + ## + ## import strutils + ## + ## check("AKB48".toLowerAscii() == "akb48") + ## + ## let teams = {'A', 'K', 'B', '4', '8'} + ## + ## check: + ## "AKB48".toLowerAscii() == "akb48" + ## 'C' in teams let checked = callsite()[1] template asgn(a: untyped, value: typed) = @@ -688,7 +681,7 @@ macro check*(conditions: untyped): untyped = result.printOuts.add getAst(print(argStr, callVar)) if exp[i].kind == nnkExprEqExpr: # ExprEqExpr - # Ident "v" + # Ident !"v" # IntLit 2 result.check[i] = exp[i][1] if exp[i].typeKind notin {ntyTypeDesc}: @@ -709,9 +702,7 @@ macro check*(conditions: untyped): untyped = result = quote do: block: `assigns` - if `check`: - discard - else: + if not `check`: checkpoint(`lineinfo` & ": Check failed: " & `callLit`) `printOuts` fail() @@ -727,9 +718,7 @@ macro check*(conditions: untyped): untyped = let callLit = checked.toStrLit result = quote do: - if `checked`: - discard - else: + if not `checked`: checkpoint(`lineinfo` & ": Check failed: " & `callLit`) fail() @@ -747,19 +736,22 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped = ## Test if `body` raises an exception found in the passed `exceptions`. ## The test passes if the raised exception is part of the acceptable ## exceptions. Otherwise, it fails. - runnableExamples: - import std/[math, random, strutils] - proc defectiveRobot() = - randomize() - case rand(1..4) - of 1: raise newException(OSError, "CANNOT COMPUTE!") - of 2: discard parseInt("Hello World!") - of 3: raise newException(IOError, "I can't do that Dave.") - else: assert 2 + 2 == 5 - - expect IOError, OSError, ValueError, AssertionDefect: - defectiveRobot() - + ## Example: + ## + ## .. code-block:: nim + ## + ## import math, random + ## proc defectiveRobot() = + ## randomize() + ## case rand(1..4) + ## of 1: raise newException(OSError, "CANNOT COMPUTE!") + ## of 2: discard parseInt("Hello World!") + ## of 3: raise newException(IOError, "I can't do that Dave.") + ## else: assert 2 + 2 == 5 + ## + ## expect IOError, OSError, ValueError, AssertionDefect: + ## defectiveRobot() + let exp = callsite() template expectBody(errorTypes, lineInfoLit, body): NimNode {.dirty.} = try: body @@ -771,11 +763,13 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped = checkpoint(lineInfoLit & ": Expect Failed, unexpected exception was thrown.") fail() - var errorTypes = newNimNode(nnkBracket) - for exp in exceptions: - errorTypes.add(exp) + var body = exp[exp.len - 1] - result = getAst(expectBody(errorTypes, errorTypes.lineInfo, body)) + var errorTypes = newNimNode(nnkBracket) + for i in countup(1, exp.len - 2): + errorTypes.add(exp[i]) + + result = getAst(expectBody(errorTypes, exp.lineInfo, body)) proc disableParamFiltering* = ## disables filtering tests with the command line params diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index ebc8b90ef..7f553be1a 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -14,33 +14,40 @@ ## as a locator, a name, or both. The term "Uniform Resource Locator" ## (URL) refers to the subset of URIs. ## -## # Basic usage +## Basic usage +## =========== +## +## Combine URIs +## ------------- +## .. code-block:: +## import uri +## let host = parseUri("https://nim-lang.org") +## let blog = "/blog.html" +## let bloguri = host / blog +## assert $host == "https://nim-lang.org" +## assert $bloguri == "https://nim-lang.org/blog.html" +## +## Access URI item +## --------------- +## .. code-block:: +## import uri +## let res = parseUri("sftp://127.0.0.1:4343") +## if isAbsolute(res): +## assert res.port == "4343" +## else: +## echo "Wrong format" +## +## Data URI Base64 +## --------------- +## +## .. code-block::nim +## doAssert getDataUri("Hello World", "text/plain") == "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ=" +## doAssert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt" +import std/private/since -## ## Combine URIs -runnableExamples: - let host = parseUri("https://nim-lang.org") - assert $host == "https://nim-lang.org" - assert $(host / "/blog.html") == "https://nim-lang.org/blog.html" - assert $(host / "blog2.html") == "https://nim-lang.org/blog2.html" - -## ## Access URI item -runnableExamples: - let res = parseUri("sftp://127.0.0.1:4343") - assert isAbsolute(res) - assert res.port == "4343" - -## ## Data URI Base64 -runnableExamples: - assert getDataUri("Hello World", "text/plain") == "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ=" - assert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt" - - -import std/[strutils, parseutils, base64] -import std/private/[since, decode_helpers] - -when defined(nimPreviewSlimSystem): - import std/assertions +import strutils, parseutils, base64 +import std/private/decode_helpers type @@ -50,26 +57,26 @@ type scheme*, username*, password*: string hostname*, port*, path*, query*, anchor*: string opaque*: bool - isIpv6*: bool + isIpv6: bool # not expose it for compatibility. UriParseError* = object of ValueError proc uriParseError*(msg: string) {.noreturn.} = - ## Raises a `UriParseError` exception with message `msg`. + ## Raises a ``UriParseError`` exception with message `msg`. raise newException(UriParseError, msg) func encodeUrl*(s: string, usePlus = true): string = ## Encodes a URL according to RFC3986. ## ## This means that characters in the set - ## `{'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~'}` are + ## ``{'a'..'z', 'A'..'Z', '0'..'9', '-', '.', '_', '~'}`` are ## carried over to the result. - ## All other characters are encoded as `%xx` where `xx` + ## All other characters are encoded as ``%xx`` where ``xx`` ## denotes its hexadecimal value. ## - ## As a special rule, when the value of `usePlus` is true, - ## spaces are encoded as `+` instead of `%20`. + ## As a special rule, when the value of ``usePlus`` is true, + ## spaces are encoded as ``+`` instead of ``%20``. ## ## **See also:** ## * `decodeUrl func<#decodeUrl,string>`_ @@ -91,12 +98,12 @@ func encodeUrl*(s: string, usePlus = true): string = func decodeUrl*(s: string, decodePlus = true): string = ## Decodes a URL according to RFC3986. ## - ## This means that any `%xx` (where `xx` denotes a hexadecimal - ## value) are converted to the character with ordinal number `xx`, + ## This means that any ``%xx`` (where ``xx`` denotes a hexadecimal + ## value) are converted to the character with ordinal number ``xx``, ## and every other character is carried over. - ## If `xx` is not a valid hexadecimal value, it is left intact. + ## If ``xx`` is not a valid hexadecimal value, it is left intact. ## - ## As a special rule, when the value of `decodePlus` is true, `+` + ## As a special rule, when the value of ``decodePlus`` is true, ``+`` ## characters are converted to a space. ## ## **See also:** @@ -126,15 +133,15 @@ func decodeUrl*(s: string, decodePlus = true): string = setLen(result, j) func encodeQuery*(query: openArray[(string, string)], usePlus = true, - omitEq = true, sep = '&'): string = + omitEq = true): 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 + ## 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 the `sep` character. + ## The pairs are joined together by a ``&`` character. ## - ## The `usePlus` parameter is passed down to the `encodeUrl` function that + ## The ``usePlus`` parameter is passed down to the `encodeUrl` function that ## is used for the URL encoding of the string values. ## ## **See also:** @@ -143,10 +150,9 @@ 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 'sep' - if result.len > 0: result.add(sep) + # Encode the `key = value` pairs and separate them with a '&' + if result.len > 0: result.add('&') let (key, val) = elem result.add(encodeUrl(key, usePlus)) # Omit the '=' if the value string is empty @@ -154,28 +160,23 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true, result.add('=') result.add(encodeUrl(val, usePlus)) -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 - ## value, which was the behavior in Nim < 1.5.1. +iterator decodeQuery*(data: string): tuple[key, value: TaintedString] = + ## Reads and decodes query string ``data`` and yields the (key, value) pairs the + ## data consists of. 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"), ("", ""), ("", "")] + import std/sugar + let s = collect(newSeq): + for k, v in decodeQuery("foo=1&bar=2"): (k, v) + doAssert s == @[("foo", "1"), ("bar", "2")] - proc parseData(data: string, i: int, field: var string, sep: char): int = + proc parseData(data: string, i: int, field: var string): int = result = i while result < data.len: - let c = data[result] - case c + case data[result] of '%': add(field, decodePercent(data, result)) of '+': add(field, ' ') - of '&': break - else: - if c == sep: break - else: add(field, data[result]) + of '=', '&': break + else: add(field, data[result]) inc(result) var i = 0 @@ -184,20 +185,16 @@ iterator decodeQuery*(data: string, sep = '&'): tuple[key, value: string] = # decode everything in one pass: while i < data.len: setLen(name, 0) # reuse memory - i = parseData(data, i, name, '=') + i = parseData(data, i, name) setLen(value, 0) # reuse memory if i < data.len and data[i] == '=': inc(i) # skip '=' - when defined(nimLegacyParseQueryStrict): - i = parseData(data, i, value, '=') - else: - i = parseData(data, i, value, sep) - yield (name, value) + i = parseData(data, i, value) + yield (name.TaintedString, value.TaintedString) if i < data.len: - when defined(nimLegacyParseQueryStrict): - if data[i] != '&': - uriParseError("'&' expected at index '$#' for '$#'" % [$i, data]) - inc(i) + if data[i] == '&': inc(i) + else: + uriParseError("'&' expected at index '$#' for '$#'" % [$i, data]) func parseAuthority(authority: string, result: var Uri) = var i = 0 @@ -229,6 +226,7 @@ func parseAuthority(authority: string, result: var Uri) = i.inc func parsePath(uri: string, i: var int, result: var Uri) = + i.inc parseUntil(uri, result.path, {'?', '#'}, i) # The 'mailto' scheme's PATH actually contains the hostname/username @@ -244,9 +242,21 @@ func parsePath(uri: string, i: var int, result: var Uri) = i.inc # Skip '#' i.inc parseUntil(uri, result.anchor, {}, i) -func initUri*(isIpv6 = false): Uri = - ## Initializes a URI with `scheme`, `username`, `password`, - ## `hostname`, `port`, `path`, `query`, `anchor` and `isIpv6`. +func initUri*(): Uri = + ## Initializes a URI with ``scheme``, ``username``, ``password``, + ## ``hostname``, ``port``, ``path``, ``query`` and ``anchor``. + ## + ## **See also:** + ## * `Uri type <#Uri>`_ for available fields in the URI type + runnableExamples: + var uri2: Uri + assert initUri() == uri2 + result = Uri(scheme: "", username: "", password: "", hostname: "", port: "", + path: "", query: "", anchor: "") + +func initUri*(isIpv6: bool): Uri {.since: (1, 3, 5).} = + ## Initializes a URI with ``scheme``, ``username``, ``password``, + ## ``hostname``, ``port``, ``path``, ``query``, ``anchor`` and ``isIpv6``. ## ## **See also:** ## * `Uri type <#Uri>`_ for available fields in the URI type @@ -283,9 +293,9 @@ func parseUri*(uri: string, result: var Uri) = var i = 0 # Check if this is a reference URI (relative URI) - let doubleSlash = uri.len > 1 and uri[0] == '/' and uri[1] == '/' + let doubleSlash = uri.len > 1 and uri[1] == '/' if i < uri.len and uri[i] == '/': - # Make sure `uri` doesn't begin with '//'. + # Make sure ``uri`` doesn't begin with '//'. if not doubleSlash: parsePath(uri, i, result) return @@ -328,17 +338,9 @@ func parseUri*(uri: string): Uri = parseUri(uri, result) func removeDotSegments(path: string): string = - ## Collapses `..` and `.` in `path` in a similar way as done in `os.normalizedPath` - ## Caution: this is buggy. - runnableExamples: - assert removeDotSegments("a1/a2/../a3/a4/a5/./a6/a7/.//./") == "a1/a3/a4/a5/a6/a7/" - assert removeDotSegments("http://www.ai.") == "http://www.ai." - # xxx adapt or reuse `pathnorm.normalizePath(path, '/')` to make this more reliable, but - # taking into account url specificities such as not collapsing leading `//` in scheme - # `https://`. see `turi` for failing tests. if path.len == 0: return "" var collection: seq[string] = @[] - let endsWithSlash = path.endsWith '/' + let endsWithSlash = path[path.len-1] == '/' var i = 0 var currentSegment = "" while i < path.len: @@ -352,7 +354,7 @@ func removeDotSegments(path: string): string = discard collection.pop() i.inc 3 continue - elif i + 1 < path.len and path[i+1] == '/': + elif path[i+1] == '/': i.inc 2 continue currentSegment.add path[i] @@ -444,8 +446,10 @@ func combine*(uris: varargs[Uri]): Uri = func isAbsolute*(uri: Uri): bool = ## Returns true if URI is absolute, false otherwise. runnableExamples: - assert parseUri("https://nim-lang.org").isAbsolute - assert not parseUri("nim-lang").isAbsolute + let foo = parseUri("https://nim-lang.org") + assert isAbsolute(foo) == true + let bar = parseUri("nim-lang") + assert isAbsolute(bar) == false return uri.scheme != "" and (uri.hostname != "" or uri.path != "") func `/`*(x: Uri, path: string): Uri = @@ -493,62 +497,44 @@ func `?`*(u: Uri, query: openArray[(string, string)]): Uri = func `$`*(u: Uri): string = ## Returns the string representation of the specified URI object. runnableExamples: - assert $parseUri("https://nim-lang.org") == "https://nim-lang.org" - # Get the len of all the parts. - let schemeLen = u.scheme.len - let usernameLen = u.username.len - let passwordLen = u.password.len - let hostnameLen = u.hostname.len - let portLen = u.port.len - let pathLen = u.path.len - let queryLen = u.query.len - let anchorLen = u.anchor.len - # Prepare a string that fits all the parts and all punctuation chars. - # 12 is the max len required by all possible punctuation chars. - result = newStringOfCap( - schemeLen + usernameLen + passwordLen + hostnameLen + portLen + pathLen + queryLen + anchorLen + 12 - ) - # Insert to result. - if schemeLen > 0: - result.add u.scheme - result.add ':' - if not u.opaque: - result.add '/' - result.add '/' - if usernameLen > 0: - result.add u.username - if passwordLen > 0: - result.add ':' - result.add u.password - result.add '@' + let foo = parseUri("https://nim-lang.org") + assert $foo == "https://nim-lang.org" + result = "" + if u.scheme.len > 0: + result.add(u.scheme) + if u.opaque: + result.add(":") + else: + result.add("://") + if u.username.len > 0: + result.add(u.username) + if u.password.len > 0: + result.add(":") + result.add(u.password) + result.add("@") if u.hostname.endsWith('/'): if u.isIpv6: - result.add '[' - result.add u.hostname[0 .. ^2] - result.add ']' + result.add("[" & u.hostname[0 .. ^2] & "]") else: - result.add u.hostname[0 .. ^2] + result.add(u.hostname[0 .. ^2]) else: if u.isIpv6: - result.add '[' - result.add u.hostname - result.add ']' + result.add("[" & u.hostname & "]") else: - result.add u.hostname - if portLen > 0: - result.add ':' - result.add u.port - if pathLen > 0: - if hostnameLen > 0 and u.path[0] != '/': - result.add '/' - result.add u.path - if queryLen > 0: - result.add '?' - result.add u.query - if anchorLen > 0: - result.add '#' - result.add u.anchor - + result.add(u.hostname) + if u.port.len > 0: + result.add(":") + result.add(u.port) + if u.path.len > 0: + if u.hostname.len > 0 and u.path[0] != '/': + result.add('/') + result.add(u.path) + if u.query.len > 0: + result.add("?") + result.add(u.query) + if u.anchor.len > 0: + result.add("#") + result.add(u.anchor) proc getDataUri*(data, mime: string, encoding = "utf-8"): string {.since: (1, 3).} = ## Convenience proc for `base64.encode` returns a standard Base64 Data URI (RFC-2397) @@ -557,14 +543,6 @@ proc getDataUri*(data, mime: string, encoding = "utf-8"): string {.since: (1, 3) ## * `mimetypes `_ for `mime` argument ## * https://tools.ietf.org/html/rfc2397 ## * https://en.wikipedia.org/wiki/Data_URI_scheme - runnableExamples: static: assert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt" + runnableExamples: static: doAssert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt" assert encoding.len > 0 and mime.len > 0 # Must *not* be URL-Safe, see RFC-2397 - let base64encoded: string = base64.encode(data) - # ("data:".len + ";charset=".len + ";base64,".len) == 22 - result = newStringOfCap(22 + mime.len + encoding.len + base64encoded.len) - result.add "data:" - result.add mime - result.add ";charset=" - result.add encoding - result.add ";base64," - result.add base64encoded + result = "data:" & mime & ";charset=" & encoding & ";base64," & base64.encode(data) diff --git a/lib/pure/volatile.nim b/lib/pure/volatile.nim index f30d899df..7fdf40e4b 100644 --- a/lib/pure/volatile.nim +++ b/lib/pure/volatile.nim @@ -20,7 +20,7 @@ template volatileLoad*[T](src: ptr T): T = src[] else: var res: T - {.emit: [res, " = (*(", typeof(src[]), " volatile*)", src, ");"].} + {.emit: [res, " = (*(", type(src[]), " volatile*)", src, ");"].} res template volatileStore*[T](dest: ptr T, val: T) = @@ -33,4 +33,4 @@ template volatileStore*[T](dest: ptr T, val: T) = when defined(js): dest[] = val else: - {.emit: ["*((", typeof(dest[]), " volatile*)(", dest, ")) = ", val, ";"].} + {.emit: ["*((", type(dest[]), " volatile*)(", dest, ")) = ", val, ";"].} diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 6785fa66e..3d9c288ed 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -11,9 +11,6 @@ import streams, parsexml, strtabs, xmltree -when defined(nimPreviewSlimSystem): - import std/syncio - type XmlError* = object of ValueError ## Exception that is raised ## for invalid XML. diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 82513bc98..62a62eb4b 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -31,11 +31,7 @@ runnableExamples: ## * `htmlgen module `_ for html code generator import std/private/since -import macros, strtabs, strutils, sequtils - -when defined(nimPreviewSlimSystem): - import std/assertions - +import macros, strtabs, strutils type XmlNode* = ref XmlNodeObj ## An XML tree consisting of XML nodes. @@ -70,19 +66,11 @@ const xmlHeader* = "\n" ## Header to use for complete XML output. -template expect(node: XmlNode, kind: set[XmlNodeKind]) = - ## Check the node's kind is within a set of values - assert node.k in kind, "Got " & $node.k - -template expect(node: XmlNode, kind: XmlNodeKind) = - ## Check the node's kind equals a value - assert node.k == kind, "Got " & $node.k - proc newXmlNode(kind: XmlNodeKind): XmlNode = ## Creates a new ``XmlNode``. result = XmlNode(k: kind) -proc newElement*(tag: sink string): XmlNode = +proc newElement*(tag: string): XmlNode = ## Creates a new ``XmlNode`` of kind ``xnElement`` with the given `tag`. ## ## See also: @@ -101,7 +89,7 @@ proc newElement*(tag: sink string): XmlNode = result.s = @[] # init attributes lazily to save memory -proc newText*(text: sink string): XmlNode = +proc newText*(text: string): XmlNode = ## Creates a new ``XmlNode`` of kind ``xnText`` with the text `text`. runnableExamples: var b = newText("my text") @@ -111,13 +99,13 @@ proc newText*(text: sink string): XmlNode = result = newXmlNode(xnText) result.fText = text -proc newVerbatimText*(text: sink string): XmlNode {.since: (1, 3).} = +proc newVerbatimText*(text: string): XmlNode {.since: (1, 3).} = ## Creates a new ``XmlNode`` of kind ``xnVerbatimText`` with the text `text`. ## **Since**: Version 1.3. result = newXmlNode(xnVerbatimText) result.fText = text -proc newComment*(comment: sink string): XmlNode = +proc newComment*(comment: string): XmlNode = ## Creates a new ``XmlNode`` of kind ``xnComment`` with the text `comment`. runnableExamples: var c = newComment("my comment") @@ -127,7 +115,7 @@ proc newComment*(comment: sink string): XmlNode = result = newXmlNode(xnComment) result.fText = comment -proc newCData*(cdata: sink string): XmlNode = +proc newCData*(cdata: string): XmlNode = ## Creates a new ``XmlNode`` of kind ``xnCData`` with the text `cdata`. runnableExamples: var d = newCData("my cdata") @@ -147,7 +135,7 @@ proc newEntity*(entity: string): XmlNode = result = newXmlNode(xnEntity) result.fText = entity -proc newXmlTree*(tag: sink string, children: openArray[XmlNode], +proc newXmlTree*(tag: string, children: openArray[XmlNode], attributes: XmlAttributes = nil): XmlNode = ## Creates a new XML tree with `tag`, `children` and `attributes`. ## @@ -163,7 +151,7 @@ proc newXmlTree*(tag: sink string, children: openArray[XmlNode], h.add newEntity("some entity") let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes let k = newXmlTree("treeTag", [g, h], att) - + doAssert $k == """ some text &some entity; @@ -175,7 +163,7 @@ proc newXmlTree*(tag: sink string, children: openArray[XmlNode], for i in 0..children.len-1: result.s[i] = children[i] result.fAttr = attributes -proc text*(n: XmlNode): lent string {.inline.} = +proc text*(n: XmlNode): string {.inline.} = ## Gets the associated text with the node `n`. ## ## `n` can be a CDATA, Text, comment, or entity node. @@ -190,10 +178,10 @@ proc text*(n: XmlNode): lent string {.inline.} = assert $c == "" assert c.text == "my comment" - n.expect {xnText, xnComment, xnCData, xnEntity} + assert n.k in {xnText, xnComment, xnCData, xnEntity} result = n.fText -proc `text=`*(n: XmlNode, text: sink string) {.inline.} = +proc `text=`*(n: XmlNode, text: string){.inline.} = ## Sets the associated text with the node `n`. ## ## `n` can be a CDATA, Text, comment, or entity node. @@ -208,10 +196,10 @@ proc `text=`*(n: XmlNode, text: sink string) {.inline.} = e.text = "a new entity text" assert $e == "&a new entity text;" - n.expect {xnText, xnComment, xnCData, xnEntity} + assert n.k in {xnText, xnComment, xnCData, xnEntity} n.fText = text -proc tag*(n: XmlNode): lent string {.inline.} = +proc tag*(n: XmlNode): string {.inline.} = ## Gets the tag name of `n`. ## ## `n` has to be an ``xnElement`` node. @@ -229,10 +217,10 @@ proc tag*(n: XmlNode): lent string {.inline.} = """ assert a.tag == "firstTag" - n.expect xnElement + assert n.k == xnElement result = n.fTag -proc `tag=`*(n: XmlNode, tag: sink string) {.inline.} = +proc `tag=`*(n: XmlNode, tag: string) {.inline.} = ## Sets the tag name of `n`. ## ## `n` has to be an ``xnElement`` node. @@ -252,7 +240,7 @@ proc `tag=`*(n: XmlNode, tag: sink string) {.inline.} = """ - n.expect xnElement + assert n.k == xnElement n.fTag = tag proc rawText*(n: XmlNode): string {.inline.} = @@ -306,60 +294,26 @@ proc innerText*(n: XmlNode): string = proc add*(father, son: XmlNode) {.inline.} = ## Adds the child `son` to `father`. - ## `father` must be of `xnElement` type ## ## See also: - ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ - ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ ## * `delete proc <#delete,XmlNode,Natural>`_ - ## * `delete proc <#delete.XmlNode,Slice[int]>`_ - ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ - ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ runnableExamples: var f = newElement("myTag") f.add newText("my text") f.add newElement("sonTag") f.add newEntity("my entity") assert $f == "my text&my entity;" - - father.expect xnElement add(father.s, son) -proc add*(father: XmlNode, sons: openArray[XmlNode]) {.inline.} = - ## Adds the children `sons` to `father`. - ## `father` must be of `xnElement` type - ## - ## See also: - ## * `add proc <#add,XmlNode,XmlNode>`_ - ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ - ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ - ## * `delete proc <#delete,XmlNode,Natural>`_ - ## * `delete proc <#delete.XmlNode,Slice[int]>`_ - ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ - ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ - runnableExamples: - var f = newElement("myTag") - f.add(@[newText("my text"), newElement("sonTag"), newEntity("my entity")]) - assert $f == "my text&my entity;" - - father.expect xnElement - add(father.s, sons) - - proc insert*(father, son: XmlNode, index: int) {.inline.} = ## Inserts the child `son` to a given position in `father`. ## - ## `father` must be of `xnElement` kind. + ## `father` and `son` must be of `xnElement` kind. ## ## See also: - ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ ## * `add proc <#add,XmlNode,XmlNode>`_ - ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ ## * `delete proc <#delete,XmlNode,Natural>`_ - ## * `delete proc <#delete.XmlNode,Slice[int]>`_ - ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ - ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ runnableExamples: var f = newElement("myTag") f.add newElement("first") @@ -369,52 +323,18 @@ proc insert*(father, son: XmlNode, index: int) {.inline.} = """ - father.expect xnElement + assert father.k == xnElement and son.k == xnElement if len(father.s) > index: insert(father.s, son, index) else: insert(father.s, son, len(father.s)) -proc insert*(father: XmlNode, sons: openArray[XmlNode], index: int) {.inline.} = - ## Inserts the children openArray[`sons`] to a given position in `father`. - ## - ## `father` must be of `xnElement` kind. - ## - ## See also: - ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ - ## * `add proc <#add,XmlNode,XmlNode>`_ - ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ - ## * `delete proc <#delete,XmlNode,Natural>`_ - ## * `delete proc <#delete.XmlNode,Slice[int]>`_ - ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ - ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ - runnableExamples: - var f = newElement("myTag") - f.add newElement("first") - f.insert([newElement("second"), newElement("third")], 0) - assert $f == """ - - - -""" - - father.expect xnElement - if len(father.s) > index: - insert(father.s, sons, index) - else: - insert(father.s, sons, len(father.s)) - proc delete*(n: XmlNode, i: Natural) = ## Deletes the `i`'th child of `n`. ## ## See also: - ## * `delete proc <#delete.XmlNode,Slice[int]>`_ ## * `add proc <#add,XmlNode,XmlNode>`_ - ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ - ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ - ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ - ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ runnableExamples: var f = newElement("myTag") f.add newElement("first") @@ -424,88 +344,9 @@ proc delete*(n: XmlNode, i: Natural) = """ - n.expect xnElement + assert n.k == xnElement n.s.delete(i) -proc delete*(n: XmlNode, slice: Slice[int]) = - ## Deletes the items `n[slice]` of `n`. - ## - ## See also: - ## * `delete proc <#delete.XmlNode,int>`_ - ## * `add proc <#add,XmlNode,XmlNode>`_ - ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ - ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ - ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ - ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ - ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ - runnableExamples: - var f = newElement("myTag") - f.add newElement("first") - f.insert([newElement("second"), newElement("third")], 0) - f.delete(0..1) - assert $f == """ - -""" - - n.expect xnElement - n.s.delete(slice) - -proc replace*(n: XmlNode, i: Natural, replacement: openArray[XmlNode]) = - ## Replaces the `i`'th child of `n` with `replacement` openArray. - ## - ## `n` must be of `xnElement` kind. - ## - ## See also: - ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ - ## * `add proc <#add,XmlNode,XmlNode>`_ - ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ - ## * `delete proc <#delete,XmlNode,Natural>`_ - ## * `delete proc <#delete.XmlNode,Slice[int]>`_ - ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ - ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ - runnableExamples: - var f = newElement("myTag") - f.add newElement("first") - f.insert(newElement("second"), 0) - f.replace(0, @[newElement("third"), newElement("fourth")]) - assert $f == """ - - - -""" - - n.expect xnElement - n.s.delete(i) - n.s.insert(replacement, i) - -proc replace*(n: XmlNode, slice: Slice[int], replacement: openArray[XmlNode]) = - ## Deletes the items `n[slice]` of `n`. - ## - ## `n` must be of `xnElement` kind. - ## - ## See also: - ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ - ## * `add proc <#add,XmlNode,XmlNode>`_ - ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ - ## * `delete proc <#delete,XmlNode,Natural>`_ - ## * `delete proc <#delete.XmlNode,Slice[int]>`_ - ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ - ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ - runnableExamples: - var f = newElement("myTag") - f.add newElement("first") - f.insert([newElement("second"), newElement("fifth")], 0) - f.replace(0..1, @[newElement("third"), newElement("fourth")]) - assert $f == """ - - - -""" - - n.expect xnElement - n.s.delete(slice) - n.s.insert(replacement, slice.a) - proc len*(n: XmlNode): int {.inline.} = ## Returns the number of `n`'s children. runnableExamples: @@ -533,12 +374,12 @@ proc `[]`*(n: XmlNode, i: int): XmlNode {.inline.} = assert $f[1] == "" assert $f[0] == "" - n.expect xnElement + assert n.k == xnElement result = n.s[i] proc `[]`*(n: var XmlNode, i: int): var XmlNode {.inline.} = ## Returns the `i`'th child of `n` so that it can be modified. - n.expect xnElement + assert n.k == xnElement result = n.s[i] proc clear*(n: var XmlNode) = @@ -548,13 +389,13 @@ proc clear*(n: var XmlNode) = var g = newElement("myTag") g.add newText("some text") g.add newComment("this is comment") - + var h = newElement("secondTag") h.add newEntity("some entity") - + let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes var k = newXmlTree("treeTag", [g, h], att) - + doAssert $k == """ some text &some entity; @@ -590,12 +431,12 @@ iterator items*(n: XmlNode): XmlNode {.inline.} = # # &some entity; - n.expect xnElement + assert n.k == xnElement for i in 0 .. n.len-1: yield n[i] iterator mitems*(n: var XmlNode): var XmlNode {.inline.} = ## Iterates over all direct children of `n` so that they can be modified. - n.expect xnElement + assert n.k == xnElement for i in 0 .. n.len-1: yield n[i] proc toXmlAttributes*(keyValuePairs: varargs[tuple[key, @@ -606,7 +447,7 @@ proc toXmlAttributes*(keyValuePairs: varargs[tuple[key, let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes var j = newElement("myTag") j.attrs = att - + doAssert $j == """""" newStringTable(keyValuePairs) @@ -627,7 +468,7 @@ proc attrs*(n: XmlNode): XmlAttributes {.inline.} = j.attrs = att assert j.attrs == att - n.expect xnElement + assert n.k == xnElement result = n.fAttr proc `attrs=`*(n: XmlNode, attr: XmlAttributes) {.inline.} = @@ -644,7 +485,7 @@ proc `attrs=`*(n: XmlNode, attr: XmlAttributes) {.inline.} = j.attrs = att assert j.attrs == att - n.expect xnElement + assert n.k == xnElement n.fAttr = attr proc attrsLen*(n: XmlNode): int {.inline.} = @@ -661,7 +502,7 @@ proc attrsLen*(n: XmlNode): int {.inline.} = j.attrs = att assert j.attrsLen == 2 - n.expect xnElement + assert n.k == xnElement if not isNil(n.fAttr): result = len(n.fAttr) proc attr*(n: XmlNode, name: string): string = @@ -679,7 +520,7 @@ proc attr*(n: XmlNode, name: string): string = assert j.attr("key1") == "first value" assert j.attr("key2") == "second value" - n.expect xnElement + assert n.kind == xnElement if n.attrs == nil: return "" return n.attrs.getOrDefault(name) @@ -711,15 +552,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) @@ -731,8 +572,20 @@ proc addIndent(result: var string, indent: int, addNewLines: bool) = for i in 1 .. indent: result.add(' ') -proc addImpl(result: var string, n: XmlNode, indent = 0, indWidth = 2, - addNewLines = true, lastNodeIsText = false) = +proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2, + addNewLines = true) = + ## Adds the textual representation of `n` to string `result`. + runnableExamples: + var + a = newElement("firstTag") + b = newText("my text") + c = newComment("my comment") + s = "" + s.add(c) + s.add(a) + s.add(b) + assert s == "my text" + proc noWhitespace(n: XmlNode): bool = for i in 0 ..< n.len: if n[i].kind in {xnText, xnEntity}: return true @@ -752,7 +605,7 @@ proc addImpl(result: var string, n: XmlNode, indent = 0, indWidth = 2, case n.k of xnElement: - if indent > 0 and not lastNodeIsText: + if indent > 0: result.addIndent(indent, addNewLines) let @@ -781,10 +634,8 @@ proc addImpl(result: var string, n: XmlNode, indent = 0, indWidth = 2, else: indent+indWidth result.add('>') - var lastNodeIsText = false for i in 0 ..< n.len: - result.addImpl(n[i], indentNext, indWidth, addNewLines, lastNodeIsText) - lastNodeIsText = n[i].kind == xnText + result.add(n[i], indentNext, indWidth, addNewLines) if not n.noWhitespace(): result.addIndent(indent, addNewLines) @@ -809,21 +660,6 @@ proc addImpl(result: var string, n: XmlNode, indent = 0, indWidth = 2, result.add(n.fText) result.add(';') -proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2, - addNewLines = true) {.inline.} = - ## Adds the textual representation of `n` to string `result`. - runnableExamples: - var - a = newElement("firstTag") - b = newText("my text") - c = newComment("my comment") - s = "" - s.add(c) - s.add(a) - s.add(b) - assert s == "my text" - result.addImpl(n, indent, indWidth, addNewLines) - proc `$`*(n: XmlNode): string = ## Converts `n` into its string representation. ## @@ -842,7 +678,7 @@ proc child*(n: XmlNode, name: string): XmlNode = f.add newElement("thirdSon") assert $(f.child("secondSon")) == "" - n.expect xnElement + assert n.kind == xnElement for i in items(n): if i.kind == xnElement: if i.tag == name: @@ -878,7 +714,7 @@ proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode], a.findAll("BAD", s, caseInsensitive = true) assert $s == "@[c text, d text]" - n.expect xnElement + assert n.k == xnElement for child in n.items(): if child.k != xnElement: continue @@ -908,7 +744,7 @@ proc findAll*(n: XmlNode, tag: string, caseInsensitive = false): seq[XmlNode] = newSeq(result, 0) findAll(n, tag, result, caseInsensitive) -proc xmlConstructor(a: NimNode): NimNode = +proc xmlConstructor(a: NimNode): NimNode {.compileTime.} = if a.kind == nnkCall: result = newCall("newXmlTree", toStrLit(a[0])) var attrs = newNimNode(nnkBracket, a) @@ -938,8 +774,8 @@ macro `<>`*(x: untyped): untyped = ## .. code-block:: nim ## <>a(href="http://nim-lang.org", newText("Nim rules.")) ## - ## Produces an XML tree for: + ## Produces an XML tree for:: ## - ## Nim rules. + ## Nim rules. ## result = xmlConstructor(x) diff --git a/lib/std/appdirs.nim b/lib/std/appdirs.nim deleted file mode 100644 index c945ba8ec..000000000 --- a/lib/std/appdirs.nim +++ /dev/null @@ -1,77 +0,0 @@ -## This module implements helpers for determining special directories used by apps. - -## .. importdoc:: paths.nim - -from std/private/osappdirs import nil -import std/paths -import std/envvars - -proc getHomeDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} = - ## Returns the home directory of the current user. - ## - ## This proc is wrapped by the `expandTilde proc`_ - ## for the convenience of processing paths coming from user configuration files. - ## - ## See also: - ## * `getConfigDir proc`_ - ## * `getTempDir proc`_ - result = Path(osappdirs.getHomeDir()) - -proc getConfigDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} = - ## Returns the config directory of the current user for applications. - ## - ## On non-Windows OSs, this proc conforms to the XDG Base Directory - ## spec. Thus, this proc returns the value of the `XDG_CONFIG_HOME` environment - ## variable if it is set, otherwise it returns the default configuration - ## directory ("~/.config/"). - ## - ## An OS-dependent trailing slash is always present at the end of the - ## returned string: `\\` on Windows and `/` on all other OSs. - ## - ## See also: - ## * `getHomeDir proc`_ - ## * `getTempDir proc`_ - result = Path(osappdirs.getConfigDir()) - -proc getCacheDir*(): Path {.inline.} = - ## Returns the cache directory of the current user for applications. - ## - ## This makes use of the following environment variables: - ## - ## * On Windows: `getEnv("LOCALAPPDATA")` - ## - ## * On macOS: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")` - ## - ## * On other platforms: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")` - ## - ## **See also:** - ## * `getHomeDir proc`_ - ## * `getTempDir proc`_ - ## * `getConfigDir proc`_ - # follows https://crates.io/crates/platform-dirs - result = Path(osappdirs.getCacheDir()) - -proc getCacheDir*(app: Path): Path {.inline.} = - ## Returns the cache directory for an application `app`. - ## - ## * On Windows, this uses: `getCacheDir() / app / "cache"` - ## * On other platforms, this uses: `getCacheDir() / app` - result = Path(osappdirs.getCacheDir(app.string)) - -proc getTempDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} = - ## Returns the temporary directory of the current user for applications to - ## save temporary files in. - ## - ## On Windows, it calls [GetTempPath](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw). - ## On Posix based platforms, it will check `TMPDIR`, `TEMP`, `TMP` and `TEMPDIR` environment variables in order. - ## On all platforms, `/tmp` will be returned if the procs fails. - ## - ## You can override this implementation - ## by adding `-d:tempDir=mytempname` to your compiler invocation. - ## - ## .. Note:: This proc does not check whether the returned path exists. - ## - ## See also: - ## * `getHomeDir proc`_ - ## * `getConfigDir proc`_ - result = Path(osappdirs.getTempDir()) diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim deleted file mode 100644 index a249d7751..000000000 --- a/lib/std/assertions.nim +++ /dev/null @@ -1,127 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2022 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -when not defined(nimPreviewSlimSystem) and not declared(sysFatal): - include "system/rawquits" - include "system/fatal" - -## This module implements assertion handling. - -import std/private/miscdollars -# --------------------------------------------------------------------------- -# helpers - -type InstantiationInfo = tuple[filename: string, line: int, column: int] - -proc `$`(info: InstantiationInfo): string = - # The +1 is needed here - # instead of overriding `$` (and changing its meaning), consider explicit name. - result = "" - result.toLocation(info.filename, info.line, info.column + 1) - -# --------------------------------------------------------------------------- - - -proc raiseAssert*(msg: string) {.noinline, noreturn, nosinks.} = - ## Raises an `AssertionDefect` with `msg`. - when defined(nimPreviewSlimSystem): - raise newException(AssertionDefect, msg) - else: - sysFatal(AssertionDefect, msg) - -proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = - ## Raises an `AssertionDefect` with `msg`, but this is hidden - ## from the effect system. Called when an assertion failed. - # trick the compiler to not list `AssertionDefect` when called - # by `assert`. - # xxx simplify this pending bootstrap >= 1.4.0, after which cast not needed - # anymore since `Defect` can't be raised. - type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect, tags: [].} - cast[Hide](raiseAssert)(msg) - -template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) = - when enabled: - const - loc = instantiationInfo(fullPaths = compileOption("excessiveStackTrace")) - ploc = $loc - bind instantiationInfo - mixin failedAssertImpl - {.line: loc.}: - if not cond: - failedAssertImpl(ploc & " `" & expr & "` " & msg) - -template assert*(cond: untyped, msg = "") = - ## Raises `AssertionDefect` with `msg` if `cond` is false. Note - ## that `AssertionDefect` is hidden from the effect system, so it doesn't - ## produce `{.raises: [AssertionDefect].}`. This exception is only supposed - ## to be caught by unit testing frameworks. - ## - ## No code will be generated for `assert` when passing `-d:danger` (implied by `--assertions:off`). - ## See `command line switches `_. - runnableExamples: assert 1 == 1 - runnableExamples("--assertions:off"): - assert 1 == 2 # no code generated, no failure here - runnableExamples("-d:danger"): assert 1 == 2 # ditto - assertImpl(cond, msg, astToStr(cond), compileOption("assertions")) - -template doAssert*(cond: untyped, msg = "") = - ## Similar to `assert <#assert.t,untyped,string>`_ but is always turned on regardless of `--assertions`. - runnableExamples: - doAssert 1 == 1 # generates code even when built with `-d:danger` or `--assertions:off` - assertImpl(cond, msg, astToStr(cond), true) - -template onFailedAssert*(msg, code: untyped): untyped {.dirty.} = - ## Sets an assertion failure handler that will intercept any assert - ## statements following `onFailedAssert` in the current scope. - runnableExamples: - type MyError = object of CatchableError - lineinfo: tuple[filename: string, line: int, column: int] - # block-wide policy to change the failed assert exception type in order to - # include a lineinfo - onFailedAssert(msg): - raise (ref MyError)(msg: msg, lineinfo: instantiationInfo(-2)) - doAssertRaises(MyError): doAssert false - when not defined(nimHasTemplateRedefinitionPragma): - {.pragma: redefine.} - template failedAssertImpl(msgIMPL: string): untyped {.dirty, redefine.} = - let msg = msgIMPL - code - -template doAssertRaises*(exception: typedesc, code: untyped) = - ## Raises `AssertionDefect` if specified `code` does not raise `exception`. - runnableExamples: - doAssertRaises(ValueError): raise newException(ValueError, "Hello World") - doAssertRaises(CatchableError): raise newException(ValueError, "Hello World") - doAssertRaises(AssertionDefect): doAssert false - var wrong = false - const begin = "expected raising '" & astToStr(exception) & "', instead" - const msgEnd = " by: " & astToStr(code) - template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd) - {.warning[BareExcept]:off.} - when Exception is exception: - try: - if true: - code - wrong = true - except Exception as e: discard - except: raisedForeign() - else: - try: - if true: - code - wrong = true - except exception: - discard - except Exception as e: - mixin `$` # alternatively, we could define $cstring in this module - raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd) - except: raisedForeign() - {.warning[BareExcept]:on.} - if wrong: - raiseAssert(begin & " nothing was raised" & msgEnd) diff --git a/lib/std/cmdline.nim b/lib/std/cmdline.nim deleted file mode 100644 index 6788dacde..000000000 --- a/lib/std/cmdline.nim +++ /dev/null @@ -1,310 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2022 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module contains system facilities for reading command -## line parameters. - -## **See also:** -## * `parseopt module `_ for command-line parser beyond -## `parseCmdLine proc`_ - - -include system/inclrtl - -when defined(nimPreviewSlimSystem): - import std/widestrs - -when defined(nodejs): - from std/private/oscommon import ReadDirEffect - - -const weirdTarget = defined(nimscript) or defined(js) - - -when weirdTarget: - discard -elif defined(windows): - import winlean -elif defined(posix): - import posix -else: - {.error: "The cmdline module has not been implemented for the target platform.".} - - -# Needed by windows in order to obtain the command line for targets -# other than command line targets -when defined(windows) and not weirdTarget: - template getCommandLine*(): untyped = getCommandLineW() - - -proc parseCmdLine*(c: string): seq[string] {. - noSideEffect, rtl, extern: "nos$1".} = - ## Splits a `command line`:idx: into several components. - ## - ## **Note**: This proc is only occasionally useful, better use the - ## `parseopt module `_. - ## - ## On Windows, it uses the `following parsing rules - ## `_: - ## - ## * Arguments are delimited by white space, which is either a space or a tab. - ## * The caret character (^) is not recognized as an escape character or - ## delimiter. The character is handled completely by the command-line parser - ## in the operating system before being passed to the argv array in the - ## program. - ## * A string surrounded by double quotation marks ("string") is interpreted - ## as a single argument, regardless of white space contained within. A - ## quoted string can be embedded in an argument. - ## * A double quotation mark preceded by a backslash (\") is interpreted as a - ## literal double quotation mark character ("). - ## * Backslashes are interpreted literally, unless they immediately precede - ## a double quotation mark. - ## * If an even number of backslashes is followed by a double quotation mark, - ## one backslash is placed in the argv array for every pair of backslashes, - ## and the double quotation mark is interpreted as a string delimiter. - ## * If an odd number of backslashes is followed by a double quotation mark, - ## one backslash is placed in the argv array for every pair of backslashes, - ## and the double quotation mark is "escaped" by the remaining backslash, - ## causing a literal double quotation mark (") to be placed in argv. - ## - ## On Posix systems, it uses the following parsing rules: - ## Components are separated by whitespace unless the whitespace - ## occurs within ``"`` or ``'`` quotes. - ## - ## See also: - ## * `parseopt module `_ - ## * `paramCount proc`_ - ## * `paramStr proc`_ - ## * `commandLineParams proc`_ - - result = @[] - var i = 0 - var a = "" - while true: - setLen(a, 0) - # eat all delimiting whitespace - while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i) - if i >= c.len: break - when defined(windows): - # parse a single argument according to the above rules: - var inQuote = false - while i < c.len: - case c[i] - of '\\': - var j = i - while j < c.len and c[j] == '\\': inc(j) - if j < c.len and c[j] == '"': - for k in 1..(j-i) div 2: a.add('\\') - if (j-i) mod 2 == 0: - i = j - else: - a.add('"') - i = j+1 - else: - a.add(c[i]) - inc(i) - of '"': - inc(i) - if not inQuote: inQuote = true - elif i < c.len and c[i] == '"': - a.add(c[i]) - inc(i) - else: - inQuote = false - break - of ' ', '\t': - if not inQuote: break - a.add(c[i]) - inc(i) - else: - a.add(c[i]) - inc(i) - else: - case c[i] - of '\'', '\"': - var delim = c[i] - inc(i) # skip ' or " - while i < c.len and c[i] != delim: - add a, c[i] - inc(i) - if i < c.len: inc(i) - else: - while i < c.len and c[i] > ' ': - add(a, c[i]) - inc(i) - add(result, a) - -when defined(nimdoc): - # Common forward declaration docstring block for parameter retrieval procs. - proc paramCount*(): int {.tags: [ReadIOEffect].} = - ## Returns the number of `command line arguments`:idx: given to the - ## application. - ## - ## Unlike `argc`:idx: in C, if your binary was called without parameters this - ## will return zero. - ## You can query each individual parameter with `paramStr proc`_ - ## or retrieve all of them in one go with `commandLineParams proc`_. - ## - ## **Availability**: When generating a dynamic library (see `--app:lib`) on - ## Posix this proc is not defined. - ## Test for availability using `declared() `_. - ## - ## See also: - ## * `parseopt module `_ - ## * `parseCmdLine proc`_ - ## * `paramStr proc`_ - ## * `commandLineParams proc`_ - ## - ## **Examples:** - ## - ## .. code-block:: nim - ## when declared(paramCount): - ## # Use paramCount() here - ## else: - ## # Do something else! - - proc paramStr*(i: int): string {.tags: [ReadIOEffect].} = - ## Returns the `i`-th `command line argument`:idx: given to the application. - ## - ## `i` should be in the range `1..paramCount()`, the `IndexDefect` - ## exception will be raised for invalid values. Instead of iterating - ## over `paramCount()`_ with this proc you can - ## call the convenience `commandLineParams()`_. - ## - ## Similarly to `argv`:idx: in C, - ## it is possible to call `paramStr(0)` but this will return OS specific - ## contents (usually the name of the invoked executable). You should avoid - ## this and call `getAppFilename()`_ instead. - ## - ## **Availability**: When generating a dynamic library (see `--app:lib`) on - ## Posix this proc is not defined. - ## Test for availability using `declared() `_. - ## - ## See also: - ## * `parseopt module `_ - ## * `parseCmdLine proc`_ - ## * `paramCount proc`_ - ## * `commandLineParams proc`_ - ## * `getAppFilename proc`_ - ## - ## **Examples:** - ## - ## .. code-block:: nim - ## when declared(paramStr): - ## # Use paramStr() here - ## else: - ## # Do something else! - -elif defined(nimscript): discard -elif defined(nodejs): - type Argv = object of JsRoot - let argv {.importjs: "process.argv".} : Argv - proc len(argv: Argv): int {.importjs: "#.length".} - proc `[]`(argv: Argv, i: int): cstring {.importjs: "#[#]".} - - proc paramCount*(): int {.tags: [ReadDirEffect].} = - result = argv.len - 2 - - proc paramStr*(i: int): string {.tags: [ReadIOEffect].} = - let i = i + 1 - if i < argv.len and i >= 0: - result = $argv[i] - else: - raise newException(IndexDefect, formatErrorIndexBound(i - 1, argv.len - 2)) -elif defined(windows): - # Since we support GUI applications with Nim, we sometimes generate - # a WinMain entry proc. But a WinMain proc has no access to the parsed - # command line arguments. The way to get them differs. Thus we parse them - # ourselves. This has the additional benefit that the program's behaviour - # is always the same -- independent of the used C compiler. - var - ownArgv {.threadvar.}: seq[string] - ownParsedArgv {.threadvar.}: bool - - proc paramCount*(): int {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = - # Docstring in nimdoc block. - if not ownParsedArgv: - ownArgv = parseCmdLine($getCommandLine()) - ownParsedArgv = true - result = ownArgv.len-1 - - proc paramStr*(i: int): string {.rtl, extern: "nos$1", - tags: [ReadIOEffect].} = - # Docstring in nimdoc block. - if not ownParsedArgv: - ownArgv = parseCmdLine($getCommandLine()) - ownParsedArgv = true - if i < ownArgv.len and i >= 0: - result = ownArgv[i] - else: - raise newException(IndexDefect, formatErrorIndexBound(i, ownArgv.len-1)) - -elif defined(genode): - proc paramStr*(i: int): string = - raise newException(OSError, "paramStr is not implemented on Genode") - - proc paramCount*(): int = - raise newException(OSError, "paramCount is not implemented on Genode") -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") - - proc paramCount*(): int {.tags: [ReadIOEffect].} = - raise newException(OSError, "paramCount is not implemented on current platform") -elif not defined(createNimRtl) and - not(defined(posix) and appType == "lib"): - # On Posix, there is no portable way to get the command line from a DLL. - var - cmdCount {.importc: "cmdCount".}: cint - cmdLine {.importc: "cmdLine".}: cstringArray - - proc paramStr*(i: int): string {.tags: [ReadIOEffect].} = - # Docstring in nimdoc block. - if i < cmdCount and i >= 0: - result = $cmdLine[i] - else: - raise newException(IndexDefect, formatErrorIndexBound(i, cmdCount-1)) - - proc paramCount*(): int {.tags: [ReadIOEffect].} = - # Docstring in nimdoc block. - result = cmdCount-1 - -when declared(paramCount) or defined(nimdoc): - proc commandLineParams*(): seq[string] = - ## Convenience proc which returns the command line parameters. - ## - ## This returns **only** the parameters. If you want to get the application - ## executable filename, call `getAppFilename()`_. - ## - ## **Availability**: On Posix there is no portable way to get the command - ## line from a DLL and thus the proc isn't defined in this environment. You - ## can test for its availability with `declared() - ## `_. - ## - ## See also: - ## * `parseopt module `_ - ## * `parseCmdLine proc`_ - ## * `paramCount proc`_ - ## * `paramStr proc`_ - ## * `getAppFilename proc`_ - ## - ## **Examples:** - ## - ## .. code-block:: nim - ## when declared(commandLineParams): - ## # Use commandLineParams() here - ## else: - ## # Do something else! - result = @[] - for i in 1..paramCount(): - result.add(paramStr(i)) -else: - proc commandLineParams*(): seq[string] {.error: - "commandLineParams() unsupported by dynamic libraries".} = - discard diff --git a/lib/std/compilesettings.nim b/lib/std/compilesettings.nim index 6d8bd22f4..b9b13175d 100644 --- a/lib/std/compilesettings.nim +++ b/lib/std/compilesettings.nim @@ -1,6 +1,6 @@ # # -# Nim's Runtime Library +# The Nim Compiler # (c) Copyright 2020 Nim Contributors # # See the file "copying.txt", included in this @@ -8,7 +8,7 @@ # ## This module allows querying the compiler about -## diverse configuration settings. See also `compileOption`. +## diverse configuration settings. # Note: Only add new enum values at the end to ensure binary compatibility with # other Nim compiler versions! @@ -31,9 +31,6 @@ type ccompilerPath ## the path to the C/C++ compiler backend ## the backend (eg: c|cpp|objc|js); both `nim doc --backend:js` ## and `nim js` would imply backend=js - libPath ## the absolute path to the stdlib library, i.e. nim's `--lib`, since 1.5.1 - gc {.deprecated.} ## gc selected - mm ## memory management selected MultipleValueSetting* {.pure.} = enum ## \ ## settings resulting in a seq of string values @@ -45,22 +42,15 @@ type clibs ## libraries passed to the C/C++ compiler proc querySetting*(setting: SingleValueSetting): string {. - compileTime, noSideEffect.} = - ## Can be used to get a string compile-time option. + compileTime, noSideEffect.} = discard + ## Can be used to get a string compile-time option. Example: ## - ## See also: - ## * `compileOption `_ for `on|off` options - ## * `compileOption `_ for enum options - ## - runnableExamples: - const nimcache = querySetting(SingleValueSetting.nimcacheDir) + ## .. code-block:: Nim + ## const nimcache = querySetting(SingleValueSetting.nimcacheDir) proc querySettingSeq*(setting: MultipleValueSetting): seq[string] {. - compileTime, noSideEffect.} = - ## Can be used to get a multi-string compile-time option. + compileTime, noSideEffect.} = discard + ## Can be used to get a multi-string compile-time option. Example: ## - ## See also: - ## * `compileOption `_ for `on|off` options - ## * `compileOption `_ for enum options - runnableExamples: - const nimblePaths = querySettingSeq(MultipleValueSetting.nimblePaths) + ## .. code-block:: Nim + ## const nimblePaths = compileSettingSeq(MultipleValueSetting.nimblePaths) diff --git a/lib/std/decls.nim b/lib/std/decls.nim index 3f774cd08..dd7d19da7 100644 --- a/lib/std/decls.nim +++ b/lib/std/decls.nim @@ -1,31 +1,19 @@ -## This module implements syntax sugar for some declarations. +# see `semLowerLetVarCustomPragma` for compiler support that enables these +# lowerings -import macros - -macro byaddr*(sect) = - ## Allows a syntax for l-value references, being an exact analog to - ## `auto& a = ex;` in C++. - ## - ## .. warning:: This makes use of 2 experimental features, namely nullary - ## templates instantiated as symbols and variable macro pragmas. - ## For this reason, its behavior is not stable. The current implementation - ## allows redefinition, but this is not an intended consequence. +template byaddr*(lhs, typ, ex) = + ## Allows a syntax for lvalue reference, exact analog to + ## `auto& a = ex;` in C++ runnableExamples: - var s = @[10, 11, 12] + var s = @[10,11,12] var a {.byaddr.} = s[0] - a += 100 - assert s == @[110, 11, 12] - assert a is int + a+=100 + doAssert s == @[110,11,12] + doAssert a is int var b {.byaddr.}: int = s[0] - assert a.addr == b.addr - expectLen sect, 1 - let def = sect[0] - let - lhs = def[0] - typ = def[1] - ex = def[2] - addrTyp = if typ.kind == nnkEmpty: typ else: newTree(nnkPtrTy, typ) - result = quote do: - let tmp: `addrTyp` = addr(`ex`) - template `lhs`: untyped = tmp[] - result.copyLineInfo(def) + doAssert a.addr == b.addr + when typ is typeof(nil): + let tmp = addr(ex) + else: + let tmp: ptr typ = addr(ex) + template lhs: untyped = tmp[] diff --git a/lib/std/dirs.nim b/lib/std/dirs.nim deleted file mode 100644 index df6107c51..000000000 --- a/lib/std/dirs.nim +++ /dev/null @@ -1,135 +0,0 @@ -## This module implements directory handling. - -from paths import Path, ReadDirEffect, WriteDirEffect - -from std/private/osdirs import dirExists, createDir, existsOrCreateDir, removeDir, - moveDir, walkDir, setCurrentDir, - walkDirRec, PathComponent - -export PathComponent - -proc dirExists*(dir: Path): bool {.inline, tags: [ReadDirEffect].} = - ## Returns true if the directory `dir` exists. If `dir` is a file, false - ## is returned. Follows symlinks. - result = dirExists(dir.string) - -proc createDir*(dir: Path) {.inline, tags: [WriteDirEffect, ReadDirEffect].} = - ## Creates the `directory`:idx: `dir`. - ## - ## The directory may contain several subdirectories that do not exist yet. - ## The full path is created. If this fails, `OSError` is raised. - ## - ## It does **not** fail if the directory already exists because for - ## most usages this does not indicate an error. - ## - ## See also: - ## * `removeDir proc`_ - ## * `existsOrCreateDir proc`_ - ## * `moveDir proc`_ - createDir(dir.string) - -proc existsOrCreateDir*(dir: Path): bool {.inline, tags: [WriteDirEffect, ReadDirEffect].} = - ## Checks if a `directory`:idx: `dir` exists, and creates it otherwise. - ## - ## Does not create parent directories (raises `OSError` if parent directories do not exist). - ## Returns `true` if the directory already exists, and `false` otherwise. - ## - ## See also: - ## * `removeDir proc`_ - ## * `createDir proc`_ - ## * `moveDir proc`_ - result = existsOrCreateDir(dir.string) - -proc removeDir*(dir: Path, checkDir = false - ) {.inline, tags: [WriteDirEffect, ReadDirEffect].} = - ## Removes the directory `dir` including all subdirectories and files - ## in `dir` (recursively). - ## - ## If this fails, `OSError` is raised. This does not fail if the directory never - ## existed in the first place, unless `checkDir` = true. - ## - ## See also: - ## * `removeFile proc `_ - ## * `existsOrCreateDir proc`_ - ## * `createDir proc`_ - ## * `moveDir proc`_ - removeDir(dir.string, checkDir) - -proc moveDir*(source, dest: Path) {.inline, tags: [ReadIOEffect, WriteIOEffect].} = - ## Moves a directory from `source` to `dest`. - ## - ## Symlinks are not followed: if `source` contains symlinks, they themself are - ## moved, not their target. - ## - ## If this fails, `OSError` is raised. - ## - ## See also: - ## * `moveFile proc `_ - ## * `removeDir proc`_ - ## * `existsOrCreateDir proc`_ - ## * `createDir proc`_ - moveDir(source.string, dest.string) - -iterator walkDir*(dir: Path; relative = false, checkDir = false, - skipSpecial = false): - tuple[kind: PathComponent, path: Path] {.tags: [ReadDirEffect].} = - ## Walks over the directory `dir` and yields for each directory or file in - ## `dir`. The component type and full path for each item are returned. - ## - ## Walking is not recursive. - ## * If `relative` is true (default: false) - ## the resulting path is shortened to be relative to ``dir``, - ## otherwise the full path is returned. - ## * If `checkDir` is true, `OSError` is raised when `dir` - ## doesn't exist. - ## * If `skipSpecial` is true, then (besides all directories) only *regular* - ## files (**without** special "file" objects like FIFOs, device files, - ## etc) will be yielded on Unix. - for (k, p) in walkDir(dir.string, relative, checkDir, skipSpecial): - yield (k, Path(p)) - -iterator walkDirRec*(dir: Path, - yieldFilter = {pcFile}, followFilter = {pcDir}, - relative = false, checkDir = false, skipSpecial = false): - Path {.tags: [ReadDirEffect].} = - ## Recursively walks over the directory `dir` and yields for each file - ## or directory in `dir`. - ## - ## Options `relative`, `checkdir`, `skipSpecial` are explained in - ## [walkDir iterator] description. - ## - ## .. warning:: Modifying the directory structure while the iterator - ## is traversing may result in undefined behavior! - ## - ## 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: - ## * `walkDir iterator`_ - for p in walkDirRec(dir.string, yieldFilter, followFilter, relative, - checkDir, skipSpecial): - yield Path(p) - -proc setCurrentDir*(newDir: Path) {.inline, tags: [].} = - ## Sets the `current working directory`:idx:; `OSError` - ## is raised if `newDir` cannot been set. - ## - ## See also: - ## * `getCurrentDir proc `_ - osdirs.setCurrentDir(newDir.string) diff --git a/lib/std/editdistance.nim b/lib/std/editdistance.nim index 9f29c5c05..2c9203d64 100644 --- a/lib/std/editdistance.nim +++ b/lib/std/editdistance.nim @@ -13,24 +13,24 @@ import unicode proc editDistance*(a, b: string): int {.noSideEffect.} = - ## Returns the **unicode-rune** edit distance between `a` and `b`. + ## Returns the **unicode-rune** edit distance between ``a`` and ``b``. ## ## This uses the `Levenshtein`:idx: distance algorithm with only a linear ## memory overhead. runnableExamples: static: doAssert editdistance("Kitten", "Bitten") == 1 if len(a) > len(b): - # make `b` the longer string + # make ``b`` the longer string return editDistance(b, a) # strip common prefix var - iStart = 0 ## The character starting index of the first rune in both strings `a` and `b` + iStart = 0 ## The character starting index of the first rune in both strings ``a`` and ``b`` iNextA = 0 iNextB = 0 runeA, runeB: Rune - lenRunesA = 0 ## The number of relevant runes in string `a`. - lenRunesB = 0 ## The number of relevant runes in string `b`. + lenRunesA = 0 ## The number of relevant runes in string ``a``. + lenRunesB = 0 ## The number of relevant runes in string ``b``. block commonPrefix: - # `a` is the shorter string + # ``a`` is the shorter string while iStart < len(a): iNextA = iStart a.fastRuneAt(iNextA, runeA, doInc = true) @@ -44,9 +44,9 @@ proc editDistance*(a, b: string): int {.noSideEffect.} = var # we know that we are either at the start of the strings # or that the current value of runeA is not equal to runeB - # => start search for common suffix after the current rune (`i_next_*`) - iEndA = iNextA ## The exclusive upper index bound of string `a`. - iEndB = iNextB ## The exclusive upper index bound of string `b`. + # => start search for common suffix after the current rune (``i_next_*``) + iEndA = iNextA ## The exclusive upper index bound of string ``a``. + iEndB = iNextB ## The exclusive upper index bound of string ``b``. iCurrentA = iNextA iCurrentB = iNextB block commonSuffix: @@ -69,8 +69,8 @@ proc editDistance*(a, b: string): int {.noSideEffect.} = addRunesB = 0 iCurrentA = iNextA iCurrentB = iNextB - if iCurrentA >= len(a): # `a` exhausted - if iCurrentB < len(b): # `b` not exhausted + if iCurrentA >= len(a): # ``a`` exhausted + if iCurrentB < len(b): # ``b`` not exhausted iEndA = iCurrentA iEndB = iCurrentB inc(lenRunesA, addRunesA) @@ -79,7 +79,7 @@ proc editDistance*(a, b: string): int {.noSideEffect.} = b.fastRuneAt(iEndB, runeB) inc(lenRunesB) if iEndB >= len(b): break - elif iCurrentB >= len(b): # `b` exhausted and `a` not exhausted + elif iCurrentB >= len(b): # ``b`` exhausted and ``a`` not exhausted iEndA = iCurrentA iEndB = iCurrentB inc(lenRunesA, addRunesA) diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim index fb057a669..0f0a24492 100644 --- a/lib/std/effecttraits.nim +++ b/lib/std/effecttraits.nim @@ -11,53 +11,44 @@ ## for Nim's macro system. ## **Since**: Version 1.4. ## -## One can test for the existence of this standard module -## via `defined(nimHasEffectTraitsModule)`. +## One can test for the existance of this standard module +## via ``defined(nimHasEffectTraitsModule)``. 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" proc getRaisesList*(fn: NimNode): NimNode = - ## Extracts the `.raises` 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. + ## Extracts the ``.raises`` 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 = getRaisesListImpl(fn) proc getTagsList*(fn: NimNode): NimNode = - ## Extracts the `.tags` 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. + ## Extracts the ``.tags`` 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 = 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 - ## implies that the macro that calls this proc should accept `typed` - ## arguments and not `untyped` arguments. + ## Return true if the func/proc/etc ``fn`` is `gcsafe`. + ## ``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 = isGcSafeImpl(fn) proc hasNoSideEffects*(fn: NimNode): bool = - ## Return true if the func/proc/etc `fn` has `noSideEffect`. - ## `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. + ## Return true if the func/proc/etc ``fn`` has `noSideEffect`. + ## ``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 = hasNoSideEffectsImpl(fn) diff --git a/lib/std/enumerate.nim b/lib/std/enumerate.nim index 4f0161b7c..a8f0e1ba7 100644 --- a/lib/std/enumerate.nim +++ b/lib/std/enumerate.nim @@ -21,7 +21,7 @@ macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} = ## The default starting count `0` can be manually overridden if needed. runnableExamples: let a = [10, 20, 30] - var b: seq[(int, int)] = @[] + var b: seq[(int, int)] for i, x in enumerate(a): b.add((i, x)) assert b == @[(0, 10), (1, 20), (2, 30)] diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index 59fb112ed..704e42de5 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -8,19 +8,12 @@ # import macros -from typetraits import OrdinalEnum, HoleyEnum -when defined(nimPreviewSlimSystem): - import std/assertions - - -# xxx `genEnumCaseStmt` needs tests and runnableExamples - -macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, +macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, userMin, userMax: static[int], normalizer: static[proc(s :string): string]): untyped = - # Generates a case stmt, which assigns the correct enum field given + # generates a case stmt, which assigns the correct enum field given # a normalized string comparison to the `argSym` input. - # string normalization is done using passed normalizer. + # string normalization is done using passed normalizer. # NOTE: for an enum with fields Foo, Bar, ... we cannot generate # `of "Foo".nimIdentNormalize: Foo`. # This will fail, if the enum is not defined at top level (e.g. in a block). @@ -40,10 +33,6 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, case f.kind of nnkEmpty: continue # skip first node of `enumTy` of nnkSym, nnkIdent: fStr = f.strVal - of nnkAccQuoted: - fStr = "" - for ch in f: - fStr.add ch.strVal of nnkEnumFieldDef: case f[1].kind of nnkStrLit: fStr = f[1].strVal @@ -53,13 +42,8 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, of nnkIntLit: fStr = f[0].strVal fNum = f[1].intVal - else: - let fAst = f[0].getImpl - if fAst.kind == nnkStrLit: - fStr = fAst.strVal - else: - error("Invalid tuple syntax!", f[1]) - else: error("Invalid node for enum type `" & $f.kind & "`!", f) + else: error("Invalid tuple syntax!", f[1]) + else: error("Invalid node for enum type!", f) # add field if string not already added if fNum >= userMin and fNum <= userMax: fStr = normalizer(fStr) @@ -77,118 +61,4 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, result.add nnkElse.newTree(raiseStmt) else: expectKind(default, nnkSym) - result.add nnkElse.newTree(default) - -macro enumFullRange(a: typed): untyped = - newNimNode(nnkCurly).add(a.getType[1][1..^1]) - -macro enumNames(a: typed): untyped = - # this could be exported too; in particular this could be useful for enum with holes. - result = newNimNode(nnkBracket) - for ai in a.getType[1][1..^1]: - assert ai.kind == nnkSym - result.add newLit ai.strVal - -iterator items*[T: HoleyEnum](E: typedesc[T]): T = - ## Iterates over an enum with holes. - runnableExamples: - type - A = enum - a0 = 2 - a1 = 4 - a2 - B[T] = enum - b0 = 2 - b1 = 4 - from std/sequtils import toSeq - assert A.toSeq == [a0, a1, a2] - assert B[float].toSeq == [B[float].b0, B[float].b1] - for a in enumFullRange(E): yield a - -func span(T: typedesc[HoleyEnum]): int = - (T.high.ord - T.low.ord) + 1 - -const invalidSlot = uint8.high - -proc genLookup[T: typedesc[HoleyEnum]](_: T): auto = - const n = span(T) - var i = 0 - assert n <= invalidSlot.int - var ret {.noinit.}: array[n, uint8] - for ai in mitems(ret): ai = invalidSlot - for ai in items(T): - ret[ai.ord - T.low.ord] = uint8(i) - inc(i) - return ret - -func symbolRankImpl[T](a: T): int {.inline.} = - const n = T.span - const thres = 255 # must be <= `invalidSlot`, but this should be tuned. - when n <= thres: - const lookup = genLookup(T) - let lookup2 {.global.} = lookup # xxx improve pending https://github.com/timotheecour/Nim/issues/553 - #[ - This could be optimized using a hash adapted to `T` (possible since it's known at CT) - to get better key distribution before indexing into the lookup table table. - ]# - {.noSideEffect.}: # because it's immutable - let ret = lookup2[ord(a) - T.low.ord] - if ret != invalidSlot: return ret.int - else: - var i = 0 - # we could also generate a case statement as optimization - for ai in items(T): - if ai == a: return i - inc(i) - raise newException(IndexDefect, $ord(a) & " invalid for " & $T) - -template symbolRank*[T: enum](a: T): int = - ## Returns the index in which `a` is listed in `T`. - ## - ## The cost for a `HoleyEnum` is implementation defined, currently optimized - ## for small enums, otherwise is `O(T.enumLen)`. - runnableExamples: - type - A = enum # HoleyEnum - a0 = -3 - a1 = 10 - a2 - a3 = (20, "f3Alt") - B = enum # OrdinalEnum - b0 - b1 - b2 - C = enum # OrdinalEnum - c0 = 10 - c1 - c2 - assert a2.symbolRank == 2 - assert b2.symbolRank == 2 - assert c2.symbolRank == 2 - assert c2.ord == 12 - assert a2.ord == 11 - var invalid = 7.A - doAssertRaises(IndexDefect): discard invalid.symbolRank - when T is Ordinal: ord(a) - T.low.ord.static - else: symbolRankImpl(a) - -func symbolName*[T: enum](a: T): string = - ## Returns the symbol name of an enum. - ## - ## This uses `symbolRank`. - runnableExamples: - type B = enum - b0 = (10, "kb0") - b1 = "kb1" - b2 - let b = B.low - assert b.symbolName == "b0" - assert $b == "kb0" - static: assert B.high.symbolName == "b2" - type C = enum # HoleyEnum - c0 = -3 - c1 = 4 - c2 = 20 - assert c1.symbolName == "c1" - const names = enumNames(T) - names[a.symbolRank] + result.add nnkElse.newTree(default) \ No newline at end of file diff --git a/lib/std/envvars.nim b/lib/std/envvars.nim deleted file mode 100644 index ab5c9f06e..000000000 --- a/lib/std/envvars.nim +++ /dev/null @@ -1,221 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2022 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - - -## The `std/envvars` module implements environment variable handling. -import std/oserrors - -type - ReadEnvEffect* = object of ReadIOEffect ## Effect that denotes a read - ## from an environment variable. - WriteEnvEffect* = object of WriteIOEffect ## Effect that denotes a write - ## to an environment variable. - - -when not defined(nimscript): - when defined(nodejs): - proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = - var ret = default.cstring - let key2 = key.cstring - {.emit: "const value = process.env[`key2`];".} - {.emit: "if (value !== undefined) { `ret` = value };".} - result = $ret - - proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = - var key2 = key.cstring - var ret: bool - {.emit: "`ret` = `key2` in process.env;".} - result = ret - - proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = - var key2 = key.cstring - var val2 = val.cstring - {.emit: "process.env[`key2`] = `val2`;".} - - proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = - var key2 = key.cstring - {.emit: "delete process.env[`key2`];".} - - iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = - var num: int - var keys: RootObj - {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".} - for i in 0..".} - from std/private/win_setenv import setEnvImpl - import winlean - when defined(nimPreviewSlimSystem): - import std/widestrs - - type wchar_t {.importc: "wchar_t", header: "".} = int16 - proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", - header: "".} - proc getEnvImpl(env: cstring): WideCString = - let r: WideCString = env.newWideCString - cast[WideCString](c_wgetenv(cast[ptr wchar_t](r))) - 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`. - ## - ## If the variable does not exist, `""` is returned. To distinguish - ## whether a variable exists or it's value is just `""`, call - ## `existsEnv(key) proc`_. - ## - ## See also: - ## * `existsEnv proc`_ - ## * `putEnv proc`_ - ## * `delEnv proc`_ - ## * `envPairs iterator`_ - runnableExamples: - assert getEnv("unknownEnv") == "" - assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" - - let env = getEnvImpl(key) - if env == nil: - result = default - else: - result = $env - - proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = - ## Checks whether the environment variable named `key` exists. - ## Returns true if it exists, false otherwise. - ## - ## See also: - ## * `getEnv proc`_ - ## * `putEnv proc`_ - ## * `delEnv proc`_ - ## * `envPairs iterator`_ - runnableExamples: - assert not existsEnv("unknownEnv") - - result = getEnvImpl(key) != nil - - proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = - ## Sets the value of the `environment variable`:idx: named `key` to `val`. - ## If an error occurs, `OSError` is raised. - ## - ## See also: - ## * `getEnv proc`_ - ## * `existsEnv proc`_ - ## * `delEnv proc`_ - ## * `envPairs iterator`_ - when defined(windows): - if key.len == 0 or '=' in key: - raise newException(OSError, "invalid key, got: " & $(key, val)) - if setEnvImpl(key, val, 1'i32) != 0'i32: - raiseOSError(osLastError(), $(key, val)) - else: - if c_setenv(key, val, 1'i32) != 0'i32: - raiseOSError(osLastError(), $(key, val)) - - proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = - ## Deletes the `environment variable`:idx: named `key`. - ## If an error occurs, `OSError` is raised. - ## - ## See also:ven - ## * `getEnv proc`_ - ## * `existsEnv proc`_ - ## * `putEnv proc`_ - ## * `envPairs iterator`_ - template bail = raiseOSError(osLastError(), key) - when defined(windows): - #[ - # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160 - > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string - note that nil is not legal - ]# - if key.len == 0 or '=' in key: - raise newException(OSError, "invalid key, got: " & key) - let envToDel = key & "=" - if c_putenv(cstring envToDel) != 0'i32: bail - else: - if c_unsetenv(key) != 0'i32: bail - - when defined(windows): - when defined(cpp): - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", - header: "".} - else: - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importc: "wcschr", - header: "".} - elif defined(macosx) and not defined(ios) and not defined(emscripten): - # From the manual: - # Shared libraries and bundles don't have direct access to environ, - # which is only available to the loader ld(1) when a complete program - # is being linked. - # The environment routines can still be used, but if direct access to - # environ is needed, the _NSGetEnviron() routine, defined in - # , can be used to retrieve the address of environ - # at runtime. - proc NSGetEnviron(): ptr cstringArray {.importc: "_NSGetEnviron", - header: "".} - elif defined(haiku): - var gEnv {.importc: "environ", header: "".}: cstringArray - else: - var gEnv {.importc: "environ".}: cstringArray - - iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = - when defined(windows): - let env = getEnvironmentStringsW() - var e = env - if e != nil: - while true: - let eend = strEnd(e) - let kv = $e - let p = find(kv, '=') - yield (substr(kv, 0, p-1), substr(kv, p+1)) - e = cast[WideCString](cast[ByteAddress](eend)+2) - if int(eend[1]) == 0: break - discard freeEnvironmentStringsW(env) - else: - var i = 0 - when defined(macosx) and not defined(ios) and not defined(emscripten): - var gEnv = NSGetEnviron()[] - while gEnv[i] != nil: - let kv = $gEnv[i] - inc(i) - let p = find(kv, '=') - yield (substr(kv, 0, p-1), substr(kv, p+1)) - -proc envPairsImplSeq(): seq[tuple[key, value: string]] = discard # vmops - -iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = - ## Iterate over all `environments variables`:idx:. - ## - ## In the first component of the tuple is the name of the current variable stored, - ## in the second its value. - ## - ## Works in native backends, nodejs and vm, like the following APIs: - ## * `getEnv proc`_ - ## * `existsEnv proc`_ - ## * `putEnv proc`_ - ## * `delEnv proc`_ - when nimvm: - for ai in envPairsImplSeq(): yield ai - else: - when defined(nimscript): discard - else: - for ai in envPairsImpl(): yield ai diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim index 48b4fca7f..c6537f7f8 100644 --- a/lib/std/exitprocs.nim +++ b/lib/std/exitprocs.nim @@ -7,8 +7,6 @@ # distribution, for details about the copyright. # -## This module allows adding hooks to program exit. - import locks type @@ -45,7 +43,6 @@ proc callClosures() {.noconv.} = case fun.kind of kClosure: fun.fun1() of kNoconv: fun.fun2() - gFuns.setLen(0) template fun() = if gFuns.len == 0: diff --git a/lib/std/files.nim b/lib/std/files.nim deleted file mode 100644 index 138bb5234..000000000 --- a/lib/std/files.nim +++ /dev/null @@ -1,43 +0,0 @@ -## This module implements file handling. - -from paths import Path, ReadDirEffect, WriteDirEffect - -from std/private/osfiles import fileExists, removeFile, - moveFile - - -proc fileExists*(filename: Path): bool {.inline, tags: [ReadDirEffect].} = - ## Returns true if `filename` exists and is a regular file or symlink. - ## - ## Directories, device files, named pipes and sockets return false. - result = fileExists(filename.string) - -proc removeFile*(file: Path) {.inline, tags: [WriteDirEffect].} = - ## Removes the `file`. - ## - ## If this fails, `OSError` is raised. This does not fail - ## if the file never existed in the first place. - ## - ## On Windows, ignores the read-only attribute. - ## - ## See also: - ## * `removeDir proc `_ - ## * `moveFile proc`_ - removeFile(file.string) - -proc moveFile*(source, dest: Path) {.inline, - tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect].} = - ## Moves a file from `source` to `dest`. - ## - ## Symlinks are not followed: if `source` is a symlink, it is itself moved, - ## not its target. - ## - ## If this fails, `OSError` is raised. - ## If `dest` already exists, it will be overwritten. - ## - ## Can be used to `rename files`:idx:. - ## - ## See also: - ## * `moveDir proc `_ - ## * `removeFile proc`_ - moveFile(source.string, dest.string) diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim deleted file mode 100644 index b216d1fd0..000000000 --- a/lib/std/formatfloat.nim +++ /dev/null @@ -1,142 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2022 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module implements formatting floats as strings. - -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 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).int - buf[result] = '\0' - -proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int = - result = float32ToChars(buf, value, forceTrailingDotZero=true).int - 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 = c_sprintf(cast[cstring](addr buf), "%.16g", value).int - 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(cast[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(cast[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/genasts.nim b/lib/std/genasts.nim deleted file mode 100644 index 05b2823ef..000000000 --- a/lib/std/genasts.nim +++ /dev/null @@ -1,89 +0,0 @@ -## This module implements AST generation using captured variables for macros. - -import macros - -type GenAstOpt* = enum - kDirtyTemplate, - # When set, uses a dirty template in implementation of `genAst`. This - # is occasionally useful as workaround for issues such as #8220, see - # `strformat limitations `_ for details. - # Default is unset, to avoid hijacking of uncaptured local symbols by - # symbols in caller scope. - kNoNewLit, - # don't call call newLit automatically in `genAst` capture parameters - -macro genAstOpt*(options: static set[GenAstOpt], args: varargs[untyped]): untyped = - ## Accepts a list of captured variables `a=b` or `a` and a block and returns the - ## AST that represents it. Local `{.inject.}` symbols (e.g. procs) are captured - ## unless `kDirtyTemplate in options`. - runnableExamples: - # This example shows how one could write a simplified version of `unittest.check`. - import std/[macros, strutils] - macro check2(cond: bool): untyped = - assert cond.kind == nnkInfix, "$# not implemented" % $cond.kind - result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]): - # each local symbol we access must be explicitly captured - if not cond: - doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs] - let a = 3 - check2 a*2 == a+3 - if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4' - - runnableExamples: - # This example goes in more details about the capture semantics. - macro fun(a: string, b: static bool): untyped = - let c = 'z' - var d = 11 # implicitly {.gensym.} and needs to be captured for use in `genAst`. - proc localFun(): auto = 12 # implicitly {.inject.}, doesn't need to be captured. - genAst(a, b, c = true): - # `a`, `b` are captured explicitly, `c` is a local definition masking `c = 'z'`. - const b2 = b # macro static param `b` is forwarded here as a static param. - # `echo d` would give: `var not init` because `d` is not captured. - (a & a, b, c, localFun()) # localFun can be called without capture. - assert fun("ab", false) == ("abab", false, true, 12) - - let params = newTree(nnkFormalParams, newEmptyNode()) - let pragmas = - if kDirtyTemplate in options: - nnkPragma.newTree(ident"dirty") - else: - newEmptyNode() - - template newLitMaybe(a): untyped = - when (a is type) or (typeof(a) is (proc | iterator | func | NimNode)): - a # `proc` actually also covers template, macro - else: newLit(a) - - # using `_` as workaround, see https://github.com/nim-lang/Nim/issues/2465#issuecomment-511076669 - let name = genSym(nskTemplate, "_fun") - let call = newCall(name) - for a in args[0..^2]: - var varName: NimNode - var varVal: NimNode - case a.kind - of nnkExprEqExpr: - varName = a[0] - varVal = a[1] - of nnkIdent: - varName = a - varVal = a - else: error("invalid argument kind: " & $a.kind, a) - if kNoNewLit notin options: varVal = newCall(bindSym"newLitMaybe", varVal) - - params.add newTree(nnkIdentDefs, varName, newEmptyNode(), newEmptyNode()) - call.add varVal - - result = newStmtList() - result.add nnkTemplateDef.newTree( - name, - newEmptyNode(), - newEmptyNode(), - params, - pragmas, - newEmptyNode(), - args[^1]) - result.add newCall(bindSym"getAst", call) - -template genAst*(args: varargs[untyped]): untyped = - ## Convenience wrapper around `genAstOpt`. - genAstOpt({}, args) diff --git a/lib/std/importutils.nim b/lib/std/importutils.nim deleted file mode 100644 index d2da76ea8..000000000 --- a/lib/std/importutils.nim +++ /dev/null @@ -1,44 +0,0 @@ -##[ -Utilities related to import and symbol resolution. - -Experimental API, subject to change. -]## - -#[ -Possible future APIs: -* module symbols (https://github.com/nim-lang/Nim/pull/9560) -* whichModule (subsumes canImport / moduleExists) (https://github.com/timotheecour/Nim/issues/376) -* getCurrentPkgDir (https://github.com/nim-lang/Nim/pull/10530) -* import from a computed string + related APIs (https://github.com/nim-lang/Nim/pull/10527) -]# - -when defined(nimImportutilsExample): - type - Foo = object - f0: int # private - Goo*[T] = object - g0: int # private - proc initFoo*(): auto = Foo() - -proc privateAccess*(t: typedesc) {.magic: "PrivateAccess".} = - ## Enables access to private fields of `t` in current scope. - runnableExamples("-d:nimImportutilsExample"): - # here we're importing a module containing: - # type - # Foo = object - # f0: int # private - # Goo*[T] = object - # g0: int # private - # proc initFoo*(): auto = Foo() - var f = initFoo() - block: - assert not compiles(f.f0) - privateAccess(f.type) - f.f0 = 1 # accessible in this scope - block: - assert f.f0 == 1 # still in scope - assert not compiles(f.f0) - - # this also works with generics - privateAccess(Goo) - assert Goo[float](g0: 1).g0 == 1 diff --git a/lib/std/isolation.nim b/lib/std/isolation.nim index 7d6ac6092..7ca5f0080 100644 --- a/lib/std/isolation.nim +++ b/lib/std/isolation.nim @@ -10,15 +10,12 @@ ## This module implements the `Isolated[T]` type for ## safe construction of isolated subgraphs that can be ## passed efficiently to different channels and threads. -## -## .. warning:: This module is experimental and its interface may change. -## type Isolated*[T] = object ## Isolated data can only be moved, not copied. value: T -proc `=copy`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.} +proc `=`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.} proc `=sink`*[T](dest: var Isolated[T]; src: Isolated[T]) {.inline.} = # delegate to value's sink operation @@ -28,22 +25,7 @@ proc `=destroy`*[T](dest: var Isolated[T]) {.inline.} = # delegate to value's destroy operation `=destroy`(dest.value) -func isolate*[T](value: sink T): Isolated[T] {.magic: "Isolate".} = - ## Creates an isolated subgraph from the expression `value`. - ## Isolation is checked at compile time. - ## +func isolate*[T](value: sink T): Isolated[T] {.magic: "Isolate".} + ## Create an isolated subgraph from the expression `value`. ## Please read https://github.com/nim-lang/RFCs/issues/244 ## for more details. - Isolated[T](value: value) - -func unsafeIsolate*[T](value: sink T): Isolated[T] = - ## Creates an isolated subgraph from the expression `value`. - ## - ## .. warning:: The proc doesn't check whether `value` is isolated. - ## - Isolated[T](value: value) - -func extract*[T](src: var Isolated[T]): T = - ## Returns the internal value of `src`. - ## The value is moved from `src`. - result = move(src.value) diff --git a/lib/std/jsbigints.nim b/lib/std/jsbigints.nim deleted file mode 100644 index 04578fc87..000000000 --- a/lib/std/jsbigints.nim +++ /dev/null @@ -1,230 +0,0 @@ -## Arbitrary precision integers. -## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt -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. - -func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} = - ## Constructor for `JsBigInt`. - runnableExamples: - doAssert big(1234567890) == big"1234567890" - doAssert 0b1111100111.big == 0o1747.big and 0o1747.big == 999.big - when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard - -func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} = - ## Constructor for `JsBigInt`. - runnableExamples: - doAssert -1'big == 1'big - 2'big - # supports decimal, binary, octal, hex: - doAssert -12'big == big"-12" - doAssert 12'big == 12.big - doAssert 0b101'big == 0b101.big - doAssert 0o701'big == 0o701.big - doAssert 0xdeadbeaf'big == 0xdeadbeaf.big - doAssert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big - doAssert not compiles(static(12'big)) - when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard - -func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} = - ## Alias for `'big` - when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard - -func toCstring*(this: JsBigInt; radix: 2..36): cstring {.importjs: "#.toString(#)".} = - ## Converts from `JsBigInt` to `cstring` representation. - ## * `radix` Base to use for representing numeric values. - ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString - runnableExamples: - doAssert big"2147483647".toCstring(2) == "1111111111111111111111111111111".cstring - -func toCstring*(this: JsBigInt): cstring {.importjs: "#.toString()".} - ## Converts from `JsBigInt` to `cstring` representation. - ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString - -func `$`*(this: JsBigInt): string = - ## Returns a `string` representation of `JsBigInt`. - runnableExamples: doAssert $big"1024" == "1024n" - $toCstring(this) & 'n' - -func wrapToInt*(this: JsBigInt; bits: Natural): JsBigInt {.importjs: - "(() => { const i = #, b = #; return BigInt.asIntN(b, i) })()".} = - ## Wraps `this` to a signed `JsBigInt` of `bits` bits in `-2 ^ (bits - 1)` .. `2 ^ (bits - 1) - 1`. - ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN - runnableExamples: - doAssert (big("3") + big("2") ** big("66")).wrapToInt(13) == big("3") - -func wrapToUint*(this: JsBigInt; bits: Natural): JsBigInt {.importjs: - "(() => { const i = #, b = #; return BigInt.asUintN(b, i) })()".} = - ## Wraps `this` to an unsigned `JsBigInt` of `bits` bits in 0 .. `2 ^ bits - 1`. - ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN - runnableExamples: - doAssert (big("3") + big("2") ** big("66")).wrapToUint(66) == big("3") - -func toNumber*(this: JsBigInt): BiggestInt {.importjs: "Number(#)".} = - ## Does not do any bounds check and may or may not return an inexact representation. - runnableExamples: - doAssert toNumber(big"2147483647") == 2147483647.BiggestInt - -func `+`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} = - runnableExamples: - doAssert (big"9" + big"1") == big"10" - -func `-`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} = - runnableExamples: - doAssert (big"9" - big"1") == big"8" - -func `*`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} = - runnableExamples: - doAssert (big"42" * big"9") == big"378" - -func `div`*(x, y: JsBigInt): JsBigInt {.importjs: "(# / #)".} = - ## Same as `div` but for `JsBigInt`(uses JavaScript `BigInt() / BigInt()`). - runnableExamples: - doAssert big"13" div big"3" == big"4" - doAssert big"-13" div big"3" == big"-4" - doAssert big"13" div big"-3" == big"-4" - doAssert big"-13" div big"-3" == big"4" - -func `mod`*(x, y: JsBigInt): JsBigInt {.importjs: "(# % #)".} = - ## Same as `mod` but for `JsBigInt` (uses JavaScript `BigInt() % BigInt()`). - runnableExamples: - doAssert big"13" mod big"3" == big"1" - doAssert big"-13" mod big"3" == big"-1" - doAssert big"13" mod big"-3" == big"1" - doAssert big"-13" mod big"-3" == big"-1" - -func `<`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} = - runnableExamples: - doAssert big"2" < big"9" - -func `<=`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} = - runnableExamples: - doAssert big"1" <= big"5" - -func `==`*(x, y: JsBigInt): bool {.importjs: "(# == #)".} = - runnableExamples: - doAssert big"42" == big"42" - -func `**`*(x, y: JsBigInt): JsBigInt {.importjs: "((#) $1 #)".} = - # (#) needed, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812 - runnableExamples: - doAssert big"2" ** big"64" == big"18446744073709551616" - doAssert big"-2" ** big"3" == big"-8" - doAssert -big"2" ** big"2" == big"4" # parsed as: (-2n) ** 2n - doAssert big"0" ** big"0" == big"1" # edge case - var ok = false - try: discard big"2" ** big"-1" # raises foreign `RangeError` - except: ok = true - doAssert ok - # pending https://github.com/nim-lang/Nim/pull/15940, simplify to: - # doAssertRaises: discard big"2" ** big"-1" # raises foreign `RangeError` - -func `and`*(x, y: JsBigInt): JsBigInt {.importjs: "(# & #)".} = - runnableExamples: - doAssert (big"555" and big"2") == big"2" - -func `or`*(x, y: JsBigInt): JsBigInt {.importjs: "(# | #)".} = - runnableExamples: - doAssert (big"555" or big"2") == big"555" - -func `xor`*(x, y: JsBigInt): JsBigInt {.importjs: "(# ^ #)".} = - runnableExamples: - doAssert (big"555" xor big"2") == big"553" - -func `shl`*(a, b: JsBigInt): JsBigInt {.importjs: "(# << #)".} = - runnableExamples: - doAssert (big"999" shl big"2") == big"3996" - -func `shr`*(a, b: JsBigInt): JsBigInt {.importjs: "(# >> #)".} = - runnableExamples: - doAssert (big"999" shr big"2") == big"249" - -func `-`*(this: JsBigInt): JsBigInt {.importjs: "($1#)".} = - runnableExamples: - doAssert -(big"10101010101") == big"-10101010101" - -func inc*(this: var JsBigInt) {.importjs: "(++[#][0][0])".} = - runnableExamples: - var big1: JsBigInt = big"1" - inc big1 - doAssert big1 == big"2" - -func dec*(this: var JsBigInt) {.importjs: "(--[#][0][0])".} = - runnableExamples: - var big1: JsBigInt = big"2" - dec big1 - doAssert big1 == big"1" - -func inc*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] += #)".} = - runnableExamples: - var big1: JsBigInt = big"1" - inc big1, big"2" - doAssert big1 == big"3" - -func dec*(this: var JsBigInt; amount: JsBigInt) {.importjs: "([#][0][0] -= #)".} = - runnableExamples: - var big1: JsBigInt = big"1" - dec big1, big"2" - doAssert big1 == big"-1" - -func `+=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} = - runnableExamples: - var big1: JsBigInt = big"1" - big1 += big"2" - doAssert big1 == big"3" - -func `-=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} = - runnableExamples: - var big1: JsBigInt = big"1" - big1 -= big"2" - doAssert big1 == big"-1" - -func `*=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} = - runnableExamples: - var big1: JsBigInt = big"2" - big1 *= big"4" - doAssert big1 == big"8" - -func `/=`*(x: var JsBigInt; y: JsBigInt) {.importjs: "([#][0][0] $1 #)".} = - ## Same as `x = x div y`. - runnableExamples: - var big1: JsBigInt = big"11" - big1 /= big"2" - doAssert big1 == big"5" - -proc `+`*(_: JsBigInt): JsBigInt {.error: - "See https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs".} # Can not be used by design - ## **Do NOT use.** https://github.com/tc39/proposal-bigint/blob/master/ADVANCED.md#dont-break-asmjs - -proc low*(_: typedesc[JsBigInt]): JsBigInt {.error: - "Arbitrary precision integers do not have a known low.".} ## **Do NOT use.** - -proc high*(_: typedesc[JsBigInt]): JsBigInt {.error: - "Arbitrary precision integers do not have a known high.".} ## **Do NOT use.** - - -runnableExamples: - block: - let big1: JsBigInt = big"2147483647" - let big2: JsBigInt = big"666" - doAssert JsBigInt isnot int - doAssert big1 != big2 - doAssert big1 > big2 - doAssert big1 >= big2 - doAssert big2 < big1 - doAssert big2 <= big1 - doAssert not(big1 == big2) - let z = JsBigInt.default - doAssert $z == "0n" - block: - var a: seq[JsBigInt] - a.setLen 2 - doAssert a == @[big"0", big"0"] - doAssert a[^1] == big"0" - var b: JsBigInt - doAssert b == big"0" - doAssert b == JsBigInt.default diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim deleted file mode 100644 index 219594619..000000000 --- a/lib/std/jsfetch.nim +++ /dev/null @@ -1,202 +0,0 @@ -## - Fetch for the JavaScript target: https://developer.mozilla.org/docs/Web/API/Fetch_API -when not defined(js): - {.fatal: "Module jsfetch is designed to be used with the JavaScript backend.".} - -import std/[asyncjs, jsformdata, jsheaders] -export jsformdata, jsheaders -from std/httpcore import HttpMethod -from std/jsffi import JsObject - -type - FetchOptions* = ref object of JsRoot ## Options for Fetch API. - keepalive*: bool - metod* {.importjs: "method".}: cstring - body*, integrity*, referrer*, mode*, credentials*, cache*, redirect*, referrerPolicy*: cstring - headers*: Headers - - FetchModes* = enum ## Mode options. - fmCors = "cors" - fmNoCors = "no-cors" - fmSameOrigin = "same-origin" - - FetchCredentials* = enum ## Credential options. See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials - fcInclude = "include" - fcSameOrigin = "same-origin" - fcOmit = "omit" - - FetchCaches* = enum ## https://developer.mozilla.org/docs/Web/API/Request/cache - fchDefault = "default" - fchNoStore = "no-store" - fchReload = "reload" - fchNoCache = "no-cache" - fchForceCache = "force-cache" - - FetchRedirects* = enum ## Redirects options. - frFollow = "follow" - frError = "error" - frManual = "manual" - - FetchReferrerPolicies* = enum ## Referrer Policy options. - frpNoReferrer = "no-referrer" - frpNoReferrerWhenDowngrade = "no-referrer-when-downgrade" - frpOrigin = "origin" - frpOriginWhenCrossOrigin = "origin-when-cross-origin" - frpUnsafeUrl = "unsafe-url" - - Response* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Response - bodyUsed*, ok*, redirected*: bool - typ* {.importjs: "type".}: cstring - url*, statusText*: cstring - status*: cint - headers*: Headers - body*: cstring - - Request* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Request - bodyUsed*, ok*, redirected*: bool - typ* {.importjs: "type".}: cstring - url*, statusText*: cstring - status*: cint - headers*: Headers - body*: cstring - -func newResponse*(body: cstring | FormData): Response {.importjs: "(new Response(#))".} - ## Constructor for `Response`. This does *not* call `fetch()`. Same as `new Response()`. - -func newRequest*(url: cstring): Request {.importjs: "(new Request(#))".} - ## Constructor for `Request`. This does *not* call `fetch()`. Same as `new Request()`. - -func newRequest*(url: cstring; fetchOptions: FetchOptions): Request {.importjs: "(new Request(#, #))".} - ## Constructor for `Request` with `fetchOptions`. Same as `fetch(url, fetchOptions)`. - -func clone*(self: Response | Request): Response {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Response/clone - -proc text*(self: Response): Future[cstring] {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Response/text - -proc json*(self: Response): Future[JsObject] {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Response/json - -proc formData*(self: Response): Future[FormData] {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Response/formData - -proc unsafeNewFetchOptions*(metod, body, mode, credentials, cache, referrerPolicy: cstring; - keepalive: bool; redirect = "follow".cstring; referrer = "client".cstring; integrity = "".cstring; headers: Headers = newHeaders()): FetchOptions {.importjs: - "{method: #, body: #, mode: #, credentials: #, cache: #, referrerPolicy: #, keepalive: #, redirect: #, referrer: #, integrity: #, headers: #}".} - ## .. warning:: Unsafe `newfetchOptions`. - -func newfetchOptions*(metod = HttpGet; body: cstring = nil; - mode = fmCors; credentials = fcSameOrigin; cache = fchDefault; referrerPolicy = frpNoReferrerWhenDowngrade; - keepalive = false; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring, - headers: Headers = newHeaders()): FetchOptions = - ## Constructor for `FetchOptions`. - result = FetchOptions( - 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 - of HttpPost: "POST".cstring - of HttpPut: "PUT".cstring - of HttpDelete: "DELETE".cstring - of HttpPatch: "PATCH".cstring - else: "GET".cstring - ) - ) - -proc fetch*(url: cstring | Request): Future[Response] {.importjs: "$1(#)".} - ## `fetch()` API, simple `GET` only, returns a `Future[Response]`. - -proc fetch*(url: cstring | Request; options: FetchOptions): Future[Response] {.importjs: "$1(#, #)".} - ## `fetch()` API that takes a `FetchOptions`, returns a `Future[Response]`. - -func toCstring*(self: Request | Response | FetchOptions): cstring {.importjs: "JSON.stringify(#)".} - -func `$`*(self: Request | Response | FetchOptions): string = $toCstring(self) - - -runnableExamples("-r:off"): - import std/[asyncjs, jsconsole, jsformdata, jsheaders] - from std/httpcore import HttpMethod - from std/jsffi import JsObject - from std/sugar import `=>` - - block: - let options0: FetchOptions = unsafeNewFetchOptions( - metod = "POST".cstring, - body = """{"key": "value"}""".cstring, - mode = "no-cors".cstring, - credentials = "omit".cstring, - cache = "no-cache".cstring, - referrerPolicy = "no-referrer".cstring, - keepalive = false, - redirect = "follow".cstring, - referrer = "client".cstring, - integrity = "".cstring, - headers = newHeaders() - ) - assert options0.keepalive == false - assert options0.metod == "POST".cstring - assert options0.body == """{"key": "value"}""".cstring - assert options0.mode == "no-cors".cstring - assert options0.credentials == "omit".cstring - assert options0.cache == "no-cache".cstring - assert options0.referrerPolicy == "no-referrer".cstring - assert options0.redirect == "follow".cstring - assert options0.referrer == "client".cstring - assert options0.integrity == "".cstring - assert options0.headers.len == 0 - - block: - let options1: FetchOptions = newFetchOptions( - metod = HttpPost, - body = """{"key": "value"}""".cstring, - mode = fmNoCors, - credentials = fcOmit, - cache = fchNoCache, - referrerPolicy = frpNoReferrer, - keepalive = false, - redirect = frFollow, - referrer = "client".cstring, - integrity = "".cstring, - headers = newHeaders() - ) - assert options1.keepalive == false - assert options1.metod == $HttpPost - assert options1.body == """{"key": "value"}""".cstring - assert options1.mode == $fmNoCors - assert options1.credentials == $fcOmit - assert options1.cache == $fchNoCache - assert options1.referrerPolicy == $frpNoReferrer - assert options1.redirect == $frFollow - assert options1.referrer == "client".cstring - assert options1.integrity == "".cstring - assert options1.headers.len == 0 - - block: - let response: Response = newResponse(body = "-. .. --".cstring) - let request: Request = newRequest(url = "http://nim-lang.org".cstring) - - if not defined(nodejs): - block: - proc doFetch(): Future[Response] {.async.} = - fetch "https://httpbin.org/get".cstring - - proc example() {.async.} = - let response: Response = await doFetch() - assert response.ok - assert response.status == 200.cint - assert response.headers is Headers - assert response.body is cstring - - discard example() - - block: - proc example2 {.async.} = - await fetch("https://api.github.com/users/torvalds".cstring) - .then((response: Response) => response.json()) - .then((json: JsObject) => console.log(json)) - .catch((err: Error) => console.log("Request Failed", err)) - - discard example2() diff --git a/lib/std/jsformdata.nim b/lib/std/jsformdata.nim deleted file mode 100644 index 61dcc39a3..000000000 --- a/lib/std/jsformdata.nim +++ /dev/null @@ -1,69 +0,0 @@ -## - `FormData` for the JavaScript target: https://developer.mozilla.org/en-US/docs/Web/API/FormData -when not defined(js): - {.fatal: "Module jsformdata is designed to be used with the JavaScript backend.".} - -from std/dom import Blob - -type FormData* = ref object of JsRoot ## FormData API. - -func newFormData*(): FormData {.importjs: "new FormData()".} - -func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob) {.importjs: "#.append(#, #)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/append - ## - ## .. hint:: Duplicate keys are allowed and order is preserved. - -func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob; filename: cstring) {.importjs: "#.append(#, #, #)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/append - ## - ## .. hint:: Duplicate keys are allowed and order is preserved. - -func delete*(self: FormData; name: cstring) {.importjs: "#.$1(#)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/delete - ## - ## .. warning:: Deletes *all items* with the same key name. - -func getAll*(self: FormData; name: cstring): seq[cstring] {.importjs: "#.$1(#)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/getAll - -func hasKey*(self: FormData; name: cstring): bool {.importjs: "#.has(#)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/has - -func keys*(self: FormData): seq[cstring] {.importjs: "Array.from(#.$1())".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/keys - -func values*(self: FormData): seq[cstring] {.importjs: "Array.from(#.$1())".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/values - -func pairs*(self: FormData): seq[tuple[key, val: cstring]] {.importjs: "Array.from(#.entries())".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries - -func put*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob; filename: cstring) {.importjs: "#.set(#, #, #)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/set - -func `[]=`*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob) {.importjs: "#.set(#, #)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/set - -func `[]`*(self: FormData; name: cstring): cstring {.importjs: "#.get(#)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/get - -func clear*(self: FormData) {.importjs: - "(() => { const frmdt = #; Array.from(frmdt.keys()).forEach((key) => frmdt.delete(key)) })()".} - ## Convenience func to delete all items from `FormData`. - -func toCstring*(self: FormData): cstring {.importjs: "JSON.stringify(#)".} - -func `$`*(self: FormData): string = $toCstring(self) - -func len*(self: FormData): int {.importjs: "Array.from(#.entries()).length".} - - -runnableExamples("-r:off"): - let data: FormData = newFormData() - data["key0"] = "value0".cstring - data.add("key1".cstring, "value1".cstring) - data.delete("key1") - assert data.hasKey("key0") - assert data["key0"] == "value0".cstring - data.clear() - assert data.len == 0 diff --git a/lib/std/jsheaders.nim b/lib/std/jsheaders.nim deleted file mode 100644 index 6fd3b3468..000000000 --- a/lib/std/jsheaders.nim +++ /dev/null @@ -1,83 +0,0 @@ -## - HTTP Headers for the JavaScript target: https://developer.mozilla.org/en-US/docs/Web/API/Headers -when not defined(js): - {.fatal: "Module jsheaders is designed to be used with the JavaScript backend.".} - -type Headers* = ref object of JsRoot ## HTTP Headers API. - -func newHeaders*(): Headers {.importjs: "new Headers()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Headers - -func add*(self: Headers; key: cstring; value: cstring) {.importjs: "#.append(#, #)".} - ## Allows duplicated keys. - ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/append - -func delete*(self: Headers; key: cstring) {.importjs: "#.$1(#)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/delete - ## - ## .. warning:: Delete *all* items with `key` from the headers, including duplicated keys. - -func hasKey*(self: Headers; key: cstring): bool {.importjs: "#.has(#)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/has - -func keys*(self: Headers): seq[cstring] {.importjs: "Array.from(#.$1())".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/keys - -func values*(self: Headers): seq[cstring] {.importjs: "Array.from(#.$1())".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/values - -func entries*(self: Headers): seq[tuple[key, value: cstring]] {.importjs: "Array.from(#.$1())".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/entries - -func `[]`*(self: Headers; key: cstring): cstring {.importjs: "#.get(#)".} - ## Get *all* items with `key` from the headers, including duplicated values. - ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/get - -func `[]=`*(self: Headers; key: cstring; value: cstring) {.importjs: "#.set(#, #)".} - ## Do *not* allow duplicated keys, overwrites duplicated keys. - ## https://developer.mozilla.org/en-US/docs/Web/API/Headers/set - -func clear*(self: Headers) {.importjs: - "(() => { const header = #; Array.from(header.keys()).forEach((key) => header.delete(key)) })()".} - ## Convenience func to delete all items from `Headers`. - -func toCstring*(self: Headers): cstring {.importjs: "JSON.stringify(Array.from(#.entries()))".} - ## Returns a `cstring` representation of `Headers`. - -func `$`*(self: Headers): string = $toCstring(self) - -func len*(self: Headers): int {.importjs: "Array.from(#.entries()).length".} - - -runnableExamples("-r:off"): - - block: - let header: Headers = newHeaders() - header.add("key", "value") - assert header.hasKey("key") - assert header.keys() == @["key".cstring] - assert header.values() == @["value".cstring] - assert header["key"] == "value".cstring - header["other"] = "another".cstring - assert header["other"] == "another".cstring - assert header.entries() == @[("key".cstring, "value".cstring), ("other".cstring, "another".cstring)] - assert header.toCstring() == """[["key","value"],["other","another"]]""".cstring - header.delete("other") - assert header.entries() == @[("key".cstring, "value".cstring)] - header.clear() - assert header.entries() == @[] - assert header.len == 0 - - block: - let header: Headers = newHeaders() - header.add("key", "a") - header.add("key", "b") ## Duplicated. - header.add("key", "c") ## Duplicated. - assert header["key"] == "a, b, c".cstring - header["key"] = "value".cstring - assert header["key"] == "value".cstring - - block: - let header: Headers = newHeaders() - header["key"] = "a" - header["key"] = "b" ## Overwrites. - assert header["key"] == "b".cstring diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 17d08e02e..4dca024c1 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -1,5 +1,5 @@ ##[ -This module implements a hookable (de)serialization for arbitrary types using JSON. +This module implements a hookable (de)serialization for arbitrary types. Design goal: avoid importing modules where a custom serialization is needed; see strtabs.fromJsonHook,toJsonHook for an example. ]## @@ -11,12 +11,9 @@ runnableExamples: z1: int8 let a = (1.5'f32, (b: "b2", a: "a2"), 'x', @[Foo(t: true, z1: -3), nil], [{"name": "John"}.newStringTable]) let j = a.toJson - assert j.jsonTo(typeof(a)).toJson == j - assert $[NaN, Inf, -Inf, 0.0, -0.0, 1.0, 1e-2].toJson == """["nan","inf","-inf",0.0,-0.0,1.0,0.01]""" - assert 0.0.toJson.kind == JFloat - assert Inf.toJson.kind == JString + doAssert j.jsonTo(type(a)).toJson == j -import json, strutils, tables, sets, strtabs, options, strformat +import std/[json,strutils,tables,sets,strtabs,options] #[ Future directions: @@ -30,18 +27,10 @@ add a way to customize serialization, for e.g.: objects. ]# -import macros -from enumutils import symbolName -from typetraits import OrdinalEnum, tupleLen - -when defined(nimPreviewSlimSystem): - import std/assertions - - -proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} +import std/macros type - Joptions* = object # xxx rename FromJsonOptions + Joptions* = object ## Options controlling the behavior of `fromJson`. allowExtraKeys*: bool ## If `true` Nim's object to which the JSON is parsed is not required to @@ -50,25 +39,10 @@ type ## If `true` Nim's object to which JSON is parsed is allowed to have ## fields without corresponding JSON keys. # in future work: a key rename could be added - EnumMode* = enum - joptEnumOrd - joptEnumSymbol - joptEnumString - JsonNodeMode* = enum ## controls `toJson` for JsonNode types - joptJsonNodeAsRef ## returns the ref as is - joptJsonNodeAsCopy ## returns a deep copy of the JsonNode - joptJsonNodeAsObject ## treats JsonNode as a regular ref object - ToJsonOptions* = object - enumMode*: EnumMode - jsonNodeMode*: JsonNodeMode - # xxx charMode, etc -proc initToJsonOptions*(): ToJsonOptions = - ## initializes `ToJsonOptions` with sane options. - ToJsonOptions(enumMode: joptEnumOrd, jsonNodeMode: joptJsonNodeAsRef) - -proc distinctBase(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} -template distinctBase[T](a: T, recursive: static bool = true): untyped = distinctBase(typeof(a), recursive)(a) +proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} +proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} +template distinctBase[T](a: T): untyped = distinctBase(type(a))(a) macro getDiscriminants(a: typedesc): seq[string] = ## return the discriminant keys @@ -117,14 +91,14 @@ macro initCaseObject(T: typedesc, fun: untyped): untyped = `fun`(`key2`, typedesc[`typ`]) result.add newTree(nnkExprColonExpr, key, val) -proc raiseJsonException(condStr: string, msg: string) {.noinline.} = - # just pick 1 exception type for simplicity; other choices would be: - # JsonError, JsonParser, JsonKindError - raise newException(ValueError, condStr & " failed: " & msg) +proc checkJsonImpl(cond: bool, condStr: string, msg = "") = + if not cond: + # just pick 1 exception type for simplicity; other choices would be: + # JsonError, JsonParser, JsonKindError + raise newException(ValueError, msg) template checkJson(cond: untyped, msg = "") = - if not cond: - raiseJsonException(astToStr(cond), msg) + checkJsonImpl(cond, astToStr(cond), msg) proc hasField[T](obj: T, field: string): bool = for k, _ in fieldPairs(obj): @@ -132,7 +106,7 @@ proc hasField[T](obj: T, field: string): bool = return true return false -macro accessField(obj: typed, name: static string): untyped = +macro accessField(obj: typed, name: static string): untyped = newDotExpr(obj, ident(name)) template fromJsonFields(newObj, oldObj, json, discKeys, opt) = @@ -145,7 +119,7 @@ template fromJsonFields(newObj, oldObj, json, discKeys, opt) = when key notin discKeys: if json.hasKey key: numMatched.inc - fromJson(val, json[key], opt) + fromJson(val, json[key]) elif opt.allowMissingKeys: # if there are no discriminant keys the `oldObj` must always have the # same keys as the new one. Otherwise we must check, because they could @@ -154,7 +128,7 @@ template fromJsonFields(newObj, oldObj, json, discKeys, opt) = if discKeys.len == 0 or hasField(oldObj, key): val = accessField(oldObj, key) else: - checkJson false, "key '$1' for $2 not in $3" % [key, $T, json.pretty()] + checkJson false, $($T, key, json) else: if json.hasKey key: numMatched.inc @@ -172,8 +146,8 @@ template fromJsonFields(newObj, oldObj, json, discKeys, opt) = json.len == numMatched else: json.len == num and num == numMatched - - checkJson ok, "There were $1 keys (expecting $2) for $3 with $4" % [$json.len, $num, $T, json.pretty()] + + checkJson ok, $(json.len, num, numMatched, $T, json) proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) @@ -206,16 +180,14 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = adding "json path" leading to `b` can be added in future work. ]# checkJson b != nil, $($T, b) - when compiles(fromJsonHook(a, b, opt)): fromJsonHook(a, b, opt) - elif compiles(fromJsonHook(a, b)): fromJsonHook(a, b) + when compiles(fromJsonHook(a, b)): fromJsonHook(a, b) elif T is bool: a = to(b,T) elif T is enum: case b.kind of JInt: a = T(b.getBiggestInt()) of JString: a = parseEnum[T](b.getStr()) - else: checkJson false, fmt"Expecting int/string for {$T} got {b.pretty()}" - elif T is uint|uint64: a = T(to(b, uint64)) - elif T is Ordinal: a = cast[T](to(b, int)) + else: checkJson false, $($T, " ", b) + elif T is Ordinal: a = T(to(b, int)) elif T is pointer: a = cast[pointer](to(b, int)) elif T is distinct: when nimvm: @@ -224,11 +196,6 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = else: a.distinctBase.fromJson(b) elif T is string|SomeNumber: a = to(b,T) - elif T is cstring: - case b.kind - of JNull: a = nil - of JString: a = b.str - else: checkJson false, fmt"Expecting null/string for {$T} got {b.pretty()}" elif T is JsonNode: a = b elif T is ref | ptr: if b.kind == JNull: a = nil @@ -236,15 +203,11 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = a = T() fromJson(a[], b, opt) elif T is array: - checkJson a.len == b.len, fmt"Json array size doesn't match for {$T}" + checkJson a.len == b.len, $(a.len, b.len, $T) var i = 0 for ai in mitems(a): fromJson(ai, b[i], opt) i.inc - elif T is set: - type E = typeof(for ai in a: ai) - for val in b.getElems: - incl a, jsonTo(val, E) elif T is seq: a.setLen b.len for i, val in b.getElems: @@ -272,87 +235,52 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = fromJsonFields(a, nil, b, seq[string].default, opt) else: checkJson b.kind == JArray, $(b.kind) # we could customize whether to allow JNull - - when compiles(tupleLen(T)): - let tupleSize = tupleLen(T) - else: - # Tuple len isn't in csources_v1 so using tupleLen would fail. - # Else branch basically never runs (tupleLen added in 1.1 and jsonutils in 1.4), but here for consistency - var tupleSize = 0 - for val in fields(a): - tupleSize.inc - - checkJson b.len == tupleSize, fmt"Json doesn't match expected length of {tupleSize}, got {b.pretty()}" var i = 0 for val in fields(a): fromJson(val, b[i], opt) i.inc + checkJson b.len == i, $(b.len, i, $T, b) # could customize else: # checkJson not appropriate here static: doAssert false, "not yet implemented: " & $T -proc jsonTo*(b: JsonNode, T: typedesc, opt = Joptions()): T = +proc jsonTo*(b: JsonNode, T: typedesc): T = ## reverse of `toJson` - fromJson(result, b, opt) + fromJson(result, b) -proc toJson*[T](a: T, opt = initToJsonOptions()): JsonNode = +proc toJson*[T](a: T): JsonNode = ## serializes `a` to json; uses `toJsonHook(a: T)` if it's in scope to ## customize serialization, see strtabs.toJsonHook for an example. - ## - ## .. note:: With `-d:nimPreviewJsonutilsHoleyEnum`, `toJson` now can - ## serialize/deserialize holey enums as regular enums (via `ord`) instead of as strings. - ## It is expected that this behavior becomes the new default in upcoming versions. - when compiles(toJsonHook(a, opt)): result = toJsonHook(a, opt) - elif compiles(toJsonHook(a)): result = toJsonHook(a) + when compiles(toJsonHook(a)): result = toJsonHook(a) elif T is object | tuple: when T is object or isNamedTuple(T): result = newJObject() - for k, v in a.fieldPairs: result[k] = toJson(v, opt) + for k, v in a.fieldPairs: result[k] = toJson(v) else: result = newJArray() - for v in a.fields: result.add toJson(v, opt) + for v in a.fields: result.add toJson(v) elif T is ref | ptr: - template impl = - if system.`==`(a, nil): result = newJNull() - else: result = toJson(a[], opt) - when T is JsonNode: - case opt.jsonNodeMode - of joptJsonNodeAsRef: result = a - of joptJsonNodeAsCopy: result = copy(a) - of joptJsonNodeAsObject: impl() - else: impl() - elif T is array | seq | set: + if system.`==`(a, nil): result = newJNull() + else: result = toJson(a[]) + elif T is array | seq: result = newJArray() - for ai in a: result.add toJson(ai, opt) - elif T is pointer: result = toJson(cast[int](a), opt) + for ai in a: result.add toJson(ai) + elif T is pointer: result = toJson(cast[int](a)) # edge case: `a == nil` could've also led to `newJNull()`, but this results # in simpler code for `toJson` and `fromJson`. - elif T is distinct: result = toJson(a.distinctBase, opt) + elif T is distinct: result = toJson(a.distinctBase) elif T is bool: result = %(a) - elif T is SomeInteger: result = %a - elif T is enum: - case opt.enumMode - of joptEnumOrd: - when T is Ordinal or defined(nimPreviewJsonutilsHoleyEnum): %(a.ord) - else: toJson($a, opt) - of joptEnumSymbol: - when T is OrdinalEnum: - toJson(symbolName(a), opt) - else: - toJson($a, opt) - of joptEnumString: toJson($a, opt) elif T is Ordinal: result = %(a.ord) - elif T is cstring: (if a == nil: result = newJNull() else: result = % $a) else: result = %a -proc fromJsonHook*[K: string|cstring, V](t: var (Table[K, V] | OrderedTable[K, V]), - jsonNode: JsonNode, opt = Joptions()) = +proc fromJsonHook*[K, V](t: var (Table[K, V] | OrderedTable[K, V]), + jsonNode: JsonNode) = ## Enables `fromJson` for `Table` and `OrderedTable` types. - ## + ## ## See also: ## * `toJsonHook proc<#toJsonHook>`_ runnableExamples: - import std/[tables, json] + import tables, json var foo: tuple[t: Table[string, int], ot: OrderedTable[string, int]] fromJson(foo, parseJson(""" {"t":{"two":2,"one":1},"ot":{"one":1,"three":3}}""")) @@ -364,37 +292,31 @@ proc fromJsonHook*[K: string|cstring, V](t: var (Table[K, V] | OrderedTable[K, V "type is `" & $jsonNode.kind & "`." clear(t) for k, v in jsonNode: - t[k] = jsonTo(v, V, opt) + t[k] = jsonTo(v, V) -proc toJsonHook*[K: string|cstring, V](t: (Table[K, V] | OrderedTable[K, V]), opt = initToJsonOptions()): JsonNode = +proc toJsonHook*[K, V](t: (Table[K, V] | OrderedTable[K, V])): JsonNode = ## Enables `toJson` for `Table` and `OrderedTable` types. ## ## See also: ## * `fromJsonHook proc<#fromJsonHook,,JsonNode>`_ - # pending PR #9217 use: toSeq(a) instead of `collect` in `runnableExamples`. runnableExamples: - import std/[tables, json, sugar] + import tables, json let foo = ( t: [("two", 2)].toTable, ot: [("one", 1), ("three", 3)].toOrderedTable) assert $toJson(foo) == """{"t":{"two":2},"ot":{"one":1,"three":3}}""" - # if keys are not string|cstring, you can use this: - let a = {10: "foo", 11: "bar"}.newOrderedTable - let a2 = collect: (for k,v in a: (k,v)) - assert $toJson(a2) == """[[10,"foo"],[11,"bar"]]""" result = newJObject() for k, v in pairs(t): - # not sure if $k has overhead for string - result[(when K is string: k else: $k)] = toJson(v, opt) + result[k] = toJson(v) -proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode, opt = Joptions()) = +proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode) = ## Enables `fromJson` for `HashSet` and `OrderedSet` types. - ## + ## ## See also: ## * `toJsonHook proc<#toJsonHook,SomeSet[A]>`_ runnableExamples: - import std/[sets, json] + import sets, json var foo: tuple[hs: HashSet[string], os: OrderedSet[string]] fromJson(foo, parseJson(""" {"hs": ["hash", "set"], "os": ["ordered", "set"]}""")) @@ -406,29 +328,29 @@ proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode, opt = Joptions()) = "type is `" & $jsonNode.kind & "`." clear(s) for v in jsonNode: - incl(s, jsonTo(v, A, opt)) + incl(s, jsonTo(v, A)) -proc toJsonHook*[A](s: SomeSet[A], opt = initToJsonOptions()): JsonNode = +proc toJsonHook*[A](s: SomeSet[A]): JsonNode = ## Enables `toJson` for `HashSet` and `OrderedSet` types. ## ## See also: ## * `fromJsonHook proc<#fromJsonHook,SomeSet[A],JsonNode>`_ runnableExamples: - import std/[sets, json] + import sets, json let foo = (hs: ["hash"].toHashSet, os: ["ordered", "set"].toOrderedSet) assert $toJson(foo) == """{"hs":["hash"],"os":["ordered","set"]}""" result = newJArray() for k in s: - add(result, toJson(k, opt)) + add(result, toJson(k)) -proc fromJsonHook*[T](self: var Option[T], jsonNode: JsonNode, opt = Joptions()) = +proc fromJsonHook*[T](self: var Option[T], jsonNode: JsonNode) = ## Enables `fromJson` for `Option` types. - ## + ## ## See also: ## * `toJsonHook proc<#toJsonHook,Option[T]>`_ runnableExamples: - import std/[options, json] + import options, json var opt: Option[string] fromJsonHook(opt, parseJson("\"test\"")) assert get(opt) == "test" @@ -436,34 +358,34 @@ proc fromJsonHook*[T](self: var Option[T], jsonNode: JsonNode, opt = Joptions()) assert isNone(opt) if jsonNode.kind != JNull: - self = some(jsonTo(jsonNode, T, opt)) + self = some(jsonTo(jsonNode, T)) else: self = none[T]() -proc toJsonHook*[T](self: Option[T], opt = initToJsonOptions()): JsonNode = +proc toJsonHook*[T](self: Option[T]): JsonNode = ## Enables `toJson` for `Option` types. ## ## See also: ## * `fromJsonHook proc<#fromJsonHook,Option[T],JsonNode>`_ runnableExamples: - import std/[options, json] + import options, json let optSome = some("test") assert $toJson(optSome) == "\"test\"" let optNone = none[string]() assert $toJson(optNone) == "null" if isSome(self): - toJson(get(self), opt) + toJson(get(self)) else: newJNull() proc fromJsonHook*(a: var StringTableRef, b: JsonNode) = ## Enables `fromJson` for `StringTableRef` type. - ## + ## ## See also: ## * `toJsonHook proc<#toJsonHook,StringTableRef>`_ runnableExamples: - import std/[strtabs, json] + import strtabs, json var t = newStringTable(modeCaseSensitive) let jsonStr = """{"mode": 0, "table": {"name": "John", "surname": "Doe"}}""" fromJsonHook(t, parseJson(jsonStr)) @@ -477,11 +399,11 @@ proc fromJsonHook*(a: var StringTableRef, b: JsonNode) = proc toJsonHook*(a: StringTableRef): JsonNode = ## Enables `toJson` for `StringTableRef` type. - ## + ## ## See also: ## * `fromJsonHook proc<#fromJsonHook,StringTableRef,JsonNode>`_ runnableExamples: - import std/[strtabs, json] + import strtabs, json let t = newStringTable("name", "John", "surname", "Doe", modeCaseSensitive) let jsonStr = """{"mode": "modeCaseSensitive", "table": {"name": "John", "surname": "Doe"}}""" diff --git a/lib/std/logic.nim b/lib/std/logic.nim index 84640d380..3cc871a6e 100644 --- a/lib/std/logic.nim +++ b/lib/std/logic.nim @@ -1,5 +1,5 @@ ## This module provides further logic operators like 'forall' and 'exists' -## They are only supported in `.ensures` etc pragmas. +## They are only supported in ``.ensures`` etc pragmas. proc `->`*(a, b: bool): bool {.magic: "Implies".} proc `<->`*(a, b: bool): bool {.magic: "Iff".} diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim index 5c67a5d4c..c56bf77b3 100644 --- a/lib/std/monotimes.nim +++ b/lib/std/monotimes.nim @@ -8,32 +8,31 @@ # ##[ -The `std/monotimes` module implements monotonic timestamps. A monotonic -timestamp represents the time that has passed since some system defined -point in time. The monotonic timestamps are guaranteed not to decrease, -meaning that that the following is guaranteed to work: -]## + The ``std/monotimes`` module implements monotonic timestamps. A monotonic + timestamp represents the time that has passed since some system defined + point in time. The monotonic timestamps are guaranteed to always increase, + meaning that that the following is guaranteed to work: -runnableExamples: - let a = getMonoTime() - let b = getMonoTime() - assert a <= b + .. code-block:: nim + let a = getMonoTime() + # ... do some work + let b = getMonoTime() + assert a <= b -##[ -This is not guaranteed for the `times.Time` type! This means that the -`MonoTime` should be used when measuring durations of time with -high precision. + This is not guaranteed for the `times.Time` type! This means that the + `MonoTime` should be used when measuring durations of time with + high precision. -However, since `MonoTime` represents the time that has passed since some -unknown time origin, it cannot be converted to a human readable timestamp. -If this is required, the `times.Time` type should be used instead. + However, since `MonoTime` represents the time that has passed since some + unknown time origin, it cannot be converted to a human readable timestamp. + If this is required, the `times.Time` type should be used instead. -The `MonoTime` type stores the timestamp in nanosecond resolution, but note -that the actual supported time resolution differs for different systems. + The `MonoTime` type stores the timestamp in nanosecond resolution, but note + that the actual supported time resolution differs for different systems. -See also -======== -* `times module `_ + See also + ======== + * `times module `_ ]## import times @@ -54,16 +53,18 @@ when defined(macosx): when defined(js): proc getJsTicks: float = - ## Returns ticks in the unit seconds. - when defined(nodejs): - {.emit: """ - let process = require('process'); - let time = process.hrtime(); - `result` = time[0] + time[1] / 1000000000; - """.} - else: - proc jsNow(): float {.importjs: "window.performance.now()".} - result = jsNow() / 1000 + ## Returns ticks in the unit seconds + {.emit: """ + var isNode = typeof module !== 'undefined' && module.exports + + if (isNode) { + var process = require('process'); + var time = process.hrtime() + return time[0] + time[1] / 1000000000; + } else { + return window.performance.now() / 1000; + } + """.} # Workaround for #6752. {.push overflowChecks: off.} @@ -73,13 +74,9 @@ when defined(js): system.`+`(a, b) {.pop.} -elif defined(posix) and not defined(osx): +elif defined(posix): import posix -when defined(zephyr): - proc k_uptime_ticks(): int64 {.importc: "k_uptime_ticks", header: "".} - proc k_ticks_to_ns_floor64(ticks: int64): int64 {.importc: "k_ticks_to_ns_floor64", header: "".} - elif defined(windows): proc QueryPerformanceCounter(res: var uint64) {. importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".} @@ -87,11 +84,11 @@ elif defined(windows): importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".} proc getMonoTime*(): MonoTime {.tags: [TimeEffect].} = - ## Returns the current `MonoTime` timestamp. + ## Get the current `MonoTime` timestamp. ## ## When compiled with the JS backend and executed in a browser, - ## this proc calls `window.performance.now()`. - ## See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) + ## this proc calls `window.performance.now()`, which is not supported by + ## older browsers. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) ## for more information. when defined(js): let ticks = getJsTicks() @@ -102,9 +99,6 @@ proc getMonoTime*(): MonoTime {.tags: [TimeEffect].} = mach_timebase_info(machAbsoluteTimeFreq) result = MonoTime(ticks: ticks * machAbsoluteTimeFreq.numer div machAbsoluteTimeFreq.denom) - elif defined(zephyr): - let ticks = k_ticks_to_ns_floor64(k_uptime_ticks()) - result = MonoTime(ticks: ticks) elif defined(posix): var ts: Timespec discard clock_gettime(CLOCK_MONOTONIC, ts) diff --git a/lib/std/objectdollar.nim b/lib/std/objectdollar.nim deleted file mode 100644 index 86ce9afc8..000000000 --- a/lib/std/objectdollar.nim +++ /dev/null @@ -1,13 +0,0 @@ -## This module implements a generic `$` operator to convert objects to strings. - -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/std/oserrors.nim b/lib/std/oserrors.nim deleted file mode 100644 index a641a7f47..000000000 --- a/lib/std/oserrors.nim +++ /dev/null @@ -1,117 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2022 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - - -## The `std/oserrors` module implements OS error reporting. - -type - OSErrorCode* = distinct int32 ## Specifies an OS Error Code. - -when not defined(nimscript): - when defined(windows): - import winlean - when defined(nimPreviewSlimSystem): - import std/widestrs - else: - var errno {.importc, header: "".}: cint - - proc c_strerror(errnum: cint): cstring {. - importc: "strerror", header: "".} - -proc `==`*(err1, err2: OSErrorCode): bool {.borrow.} -proc `$`*(err: OSErrorCode): string {.borrow.} - -proc osErrorMsg*(errorCode: OSErrorCode): string = - ## Converts an OS error code into a human readable string. - ## - ## The error code can be retrieved using the `osLastError proc`_. - ## - ## If conversion fails, or `errorCode` is `0` then `""` will be - ## returned. - ## - ## See also: - ## * `raiseOSError proc`_ - ## * `osLastError proc`_ - runnableExamples: - when defined(linux): - assert osErrorMsg(OSErrorCode(0)) == "" - assert osErrorMsg(OSErrorCode(1)) == "Operation not permitted" - assert osErrorMsg(OSErrorCode(2)) == "No such file or directory" - - result = "" - when defined(nimscript): - discard - elif defined(windows): - if errorCode != OSErrorCode(0'i32): - var msgbuf: WideCString - if formatMessageW(0x00000100 or 0x00001000 or 0x00000200, - nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: - result = $msgbuf - if msgbuf != nil: localFree(cast[pointer](msgbuf)) - else: - if errorCode != OSErrorCode(0'i32): - result = $c_strerror(errorCode.int32) - -proc newOSError*( - errorCode: OSErrorCode, additionalInfo = "" -): owned(ref OSError) {.noinline.} = - ## Creates a new `OSError exception `_. - ## - ## The `errorCode` will determine the - ## message, `osErrorMsg proc`_ will be used - ## to get this message. - ## - ## The error code can be retrieved using the `osLastError proc`_. - ## - ## If the error code is `0` or an error message could not be retrieved, - ## the message `unknown OS error` will be used. - ## - ## See also: - ## * `osErrorMsg proc`_ - ## * `osLastError proc`_ - result = (ref OSError)(errorCode: errorCode.int32, msg: osErrorMsg(errorCode)) - if additionalInfo.len > 0: - if result.msg.len > 0 and result.msg[^1] != '\n': result.msg.add '\n' - result.msg.add "Additional info: " - result.msg.add additionalInfo - # don't add trailing `.` etc, which negatively impacts "jump to file" in IDEs. - if result.msg == "": - result.msg = "unknown OS error" - -proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} = - ## Raises an `OSError exception `_. - ## - ## Read the description of the `newOSError proc`_ to learn - ## how the exception object is created. - raise newOSError(errorCode, additionalInfo) - -{.push stackTrace:off.} -proc osLastError*(): OSErrorCode {.sideEffect.} = - ## Retrieves the last operating system error code. - ## - ## This procedure is useful in the event when an OS call fails. In that case - ## this procedure will return the error code describing the reason why the - ## OS call failed. The `OSErrorMsg` procedure can then be used to convert - ## this code into a string. - ## - ## .. warning:: The behaviour of this procedure varies between Windows and POSIX systems. - ## On Windows some OS calls can reset the error code to `0` causing this - ## procedure to return `0`. It is therefore advised to call this procedure - ## immediately after an OS call fails. On POSIX systems this is not a problem. - ## - ## See also: - ## * `osErrorMsg proc`_ - ## * `raiseOSError proc`_ - when defined(nimscript): - discard - elif defined(windows): - result = cast[OSErrorCode](getLastError()) - else: - result = OSErrorCode(errno) -{.pop.} diff --git a/lib/std/outparams.nim b/lib/std/outparams.nim deleted file mode 100644 index 8a0e5ae67..000000000 --- a/lib/std/outparams.nim +++ /dev/null @@ -1,38 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2022 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## `outParamsAt` macro for easy writing code that works with both 2.0 and 1.x. - -import macros - -macro outParamsAt*(positions: static openArray[int]; n: untyped): untyped = - ## Use this macro to annotate `out` parameters in a portable way. - runnableExamples: - proc p(x: var int) {.outParamsAt: [1].} = - discard "x is really an 'out int' if the Nim compiler supports 'out' parameters" - - result = n - when defined(nimHasOutParams): - var p = n.params - for po in positions: - p[po][^2].expectKind nnkVarTy - p[po][^2] = newTree(nnkOutTy, p[po][^2][0]) - -when isMainModule: - {.experimental: "strictDefs".} - - proc main(x: var int) {.outParamsAt: [1].} = - x = 3 - - proc us = - var x: int - main x - echo x - - us() diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim index e8e03af9e..a1402a7ff 100644 --- a/lib/std/packedsets.nim +++ b/lib/std/packedsets.nim @@ -7,21 +7,22 @@ # distribution, for details about the copyright. # -## The `packedsets` module implements an efficient `Ordinal` set implemented as a +## The ``packedsets`` module implements an efficient `Ordinal`set implemented as a ## `sparse bit set`:idx:. ## ## Supports any Ordinal type. ## -## See also -## ======== +## **Note**: Currently the assignment operator ``=`` for ``PackedSet[A]`` +## performs some rather meaningless shallow copy. Since Nim currently does +## not allow the assignment operator to be overloaded, use `assign proc +## <#assign,PackedSet[A],PackedSet[A]>`_ to get a deep copy. +## +## **See also:** ## * `sets module `_ for more general hash sets import std/private/since import hashes -when defined(nimPreviewSlimSystem): - import std/assertions - type BitScalar = uint @@ -36,18 +37,19 @@ const IntMask = 1 shl IntShift - 1 type - Trunk {.acyclic.} = ref object - next: Trunk # all nodes are connected with this pointer + PTrunk = ref Trunk + Trunk = object + next: PTrunk # all nodes are connected with this pointer key: int # start address at bit 0 bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector - TrunkSeq = seq[Trunk] + TrunkSeq = seq[PTrunk] + ## An efficient set of `Ordinal` types implemented as a sparse bit set. PackedSet*[A: Ordinal] = object - ## An efficient set of `Ordinal` types implemented as a sparse bit set. elems: int # only valid for small numbers counter, max: int - head: Trunk + head: PTrunk data: TrunkSeq a: array[0..33, int] # profiling shows that 34 elements are enough @@ -60,9 +62,9 @@ proc nextTry(h, maxHash: Hash, perturb: var Hash): Hash {.inline.} = const PERTURB_SHIFT = 5 var perturb2 = cast[uint](perturb) shr PERTURB_SHIFT perturb = cast[Hash](perturb2) - result = ((5 * h) + 1 + perturb) and maxHash + result = ((5*h) + 1 + perturb) and maxHash -proc packedSetGet[A](t: PackedSet[A], key: int): Trunk = +proc packedSetGet[A](t: PackedSet[A], key: int): PTrunk = var h = key and t.max var perturb = key while t.data[h] != nil: @@ -71,13 +73,13 @@ proc packedSetGet[A](t: PackedSet[A], key: int): Trunk = h = nextTry(h, t.max, perturb) result = nil -proc intSetRawInsert[A](t: PackedSet[A], data: var TrunkSeq, desc: Trunk) = +proc intSetRawInsert[A](t: PackedSet[A], data: var TrunkSeq, desc: PTrunk) = var h = desc.key and t.max var perturb = desc.key while data[h] != nil: - assert data[h] != desc + assert(data[h] != desc) h = nextTry(h, t.max, perturb) - assert data[h] == nil + assert(data[h] == nil) data[h] = desc proc intSetEnlarge[A](t: var PackedSet[A]) = @@ -89,7 +91,7 @@ proc intSetEnlarge[A](t: var PackedSet[A]) = if t.data[i] != nil: intSetRawInsert(t, n, t.data[i]) swap(t.data, n) -proc intSetPut[A](t: var PackedSet[A], key: int): Trunk = +proc intSetPut[A](t: var PackedSet[A], key: int): PTrunk = var h = key and t.max var perturb = key while t.data[h] != nil: @@ -101,7 +103,7 @@ proc intSetPut[A](t: var PackedSet[A], key: int): Trunk = h = key and t.max perturb = key while t.data[h] != nil: h = nextTry(h, t.max, perturb) - assert t.data[h] == nil + assert(t.data[h] == nil) new(result) result.next = t.head result.key = key @@ -109,8 +111,8 @@ proc intSetPut[A](t: var PackedSet[A], key: int): Trunk = t.data[h] = result proc bitincl[A](s: var PackedSet[A], key: int) {.inline.} = - var ret: Trunk - var t = intSetPut(s, key shr TrunkShift) + var ret: PTrunk + var t = intSetPut(s, `shr`(key, TrunkShift)) var u = key and TrunkMask t.bits[u shr IntShift] = t.bits[u shr IntShift] or (BitScalar(1) shl (u and IntMask)) @@ -119,8 +121,8 @@ proc exclImpl[A](s: var PackedSet[A], key: int) = if s.elems <= s.a.len: for i in 0..`_ + ## See also: + ## * `toPackedSet[A] proc <#toPackedSet,openArray[A]>`_ runnableExamples: - let a = initPackedSet[int]() + var a = initPackedSet[int]() assert len(a) == 0 type Id = distinct int @@ -177,23 +179,27 @@ proc initPackedSet*[A]: PackedSet[A] = counter: 0, max: 0, head: nil, - data: @[]) + data: when defined(nimNoNilSeqs): @[] else: nil) # a: array[0..33, int] # profiling shows that 34 elements are enough proc contains*[A](s: PackedSet[A], key: A): bool = ## Returns true if `key` is in `s`. ## - ## This allows the usage of the `in` operator. + ## This allows the usage of `in` operator. runnableExamples: type ABCD = enum A, B, C, D - let a = [1, 3, 5].toPackedSet + var a = initPackedSet[int]() + for x in [1, 3, 5]: + a.incl(x) assert a.contains(3) assert 3 in a - assert not a.contains(8) + assert(not a.contains(8)) assert 8 notin a - let letters = [A, C].toPackedSet + var letters = initPackedSet[ABCD]() + for x in [A, C]: + letters.incl(x) assert A in letters assert C in letters assert B notin letters @@ -202,7 +208,7 @@ proc contains*[A](s: PackedSet[A], key: A): bool = for i in 0..`_ for excluding an element - ## * `incl proc <#incl,PackedSet[A],PackedSet[A]>`_ for including a set + ## * `incl proc <#incl,PackedSet[A],PackedSet[A]>`_ for including other set ## * `containsOrIncl proc <#containsOrIncl,PackedSet[A],A>`_ runnableExamples: var a = initPackedSet[int]() @@ -230,10 +236,10 @@ proc incl*[A](s: var PackedSet[A], key: A) = if s.a[i] == ord(key): return if s.elems < s.a.len: s.a[s.elems] = ord(key) - inc(s.elems) + inc s.elems return newSeq(s.data, InitIntSetSize) - s.max = InitIntSetSize - 1 + s.max = InitIntSetSize-1 for i in 0..`_. ## - ## **See also:** - ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding a set + ## See also: + ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding other set ## * `incl proc <#incl,PackedSet[A],A>`_ for including an element ## * `containsOrIncl proc <#containsOrIncl,PackedSet[A],A>`_ runnableExamples: - var a = [1].toPackedSet - a.incl([5].toPackedSet) + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1) + b.incl(5) + a.incl(b) assert len(a) == 2 assert 5 in a for item in other.items: incl(s, item) proc toPackedSet*[A](x: openArray[A]): PackedSet[A] {.since: (1, 3).} = - ## Creates a new `PackedSet[A]` that contains the elements of `x`. + ## Creates a new PackedSet[A] that contains the elements of `x`. ## ## Duplicates are removed. ## - ## **See also:** - ## * `initPackedSet proc <#initPackedSet>`_ + ## See also: + ## * `initPackedSet[A] proc <#initPackedSet>`_ runnableExamples: - let a = [5, 6, 7, 8, 8].toPackedSet - assert len(a) == 4 - assert $a == "{5, 6, 7, 8}" + var + a = toPackedSet([5, 6, 7]) + b = toPackedSet(@[1, 8, 8, 8]) + assert len(a) == 3 + assert len(b) == 2 result = initPackedSet[A]() for item in x: @@ -277,11 +289,11 @@ proc containsOrIncl*[A](s: var PackedSet[A], key: A): bool = ## Includes `key` in the set `s` and tells if `key` was already in `s`. ## ## The difference with regards to the `incl proc <#incl,PackedSet[A],A>`_ is - ## that this proc returns true if `s` already contained `key`. The - ## proc will return false if `key` was added as a new value to `s` during + ## that this proc returns `true` if `s` already contained `key`. The + ## proc will return `false` if `key` was added as a new value to `s` during ## this call. ## - ## **See also:** + ## See also: ## * `incl proc <#incl,PackedSet[A],A>`_ for including an element ## * `missingOrExcl proc <#missingOrExcl,PackedSet[A],A>`_ runnableExamples: @@ -297,7 +309,7 @@ proc containsOrIncl*[A](s: var PackedSet[A], key: A): bool = incl(s, key) result = false else: - var t = packedSetGet(s, ord(key) shr TrunkShift) + var t = packedSetGet(s, `shr`(ord(key), TrunkShift)) if t != nil: var u = ord(key) and TrunkMask result = (t.bits[u shr IntShift] and BitScalar(1) shl (u and IntMask)) != 0 @@ -313,31 +325,36 @@ proc excl*[A](s: var PackedSet[A], key: A) = ## ## This doesn't do anything if `key` is not found in `s`. ## - ## **See also:** + ## See also: ## * `incl proc <#incl,PackedSet[A],A>`_ for including an element - ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding a set + ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding other set ## * `missingOrExcl proc <#missingOrExcl,PackedSet[A],A>`_ runnableExamples: - var a = [3].toPackedSet + var a = initPackedSet[int]() + a.incl(3) a.excl(3) a.excl(3) a.excl(99) assert len(a) == 0 - - exclImpl[A](s, ord(key)) + exclImpl[A](s, cast[int](key)) proc excl*[A](s: var PackedSet[A], other: PackedSet[A]) = ## Excludes all elements from `other` from `s`. ## ## This is the in-place version of `s - other <#-,PackedSet[A],PackedSet[A]>`_. ## - ## **See also:** - ## * `incl proc <#incl,PackedSet[A],PackedSet[A]>`_ for including a set + ## See also: + ## * `incl proc <#incl,PackedSet[A],PackedSet[A]>`_ for including other set ## * `excl proc <#excl,PackedSet[A],A>`_ for excluding an element ## * `missingOrExcl proc <#missingOrExcl,PackedSet[A],A>`_ runnableExamples: - var a = [1, 5].toPackedSet - a.excl([5].toPackedSet) + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1) + a.incl(5) + b.incl(5) + a.excl(b) assert len(a) == 1 assert 5 notin a @@ -346,10 +363,6 @@ proc excl*[A](s: var PackedSet[A], other: PackedSet[A]) = proc len*[A](s: PackedSet[A]): int {.inline.} = ## Returns the number of elements in `s`. - runnableExamples: - let a = [1, 3, 5].toPackedSet - assert len(a) == 3 - if s.elems < s.a.len: result = s.elems else: @@ -359,59 +372,67 @@ proc len*[A](s: PackedSet[A]): int {.inline.} = inc(result) proc missingOrExcl*[A](s: var PackedSet[A], key: A): bool = - ## Excludes `key` from the set `s` and tells if `key` was already missing from `s`. + ## Excludes `key` in the set `s` and tells if `key` was already missing from `s`. ## ## The difference with regards to the `excl proc <#excl,PackedSet[A],A>`_ is - ## that this proc returns true if `key` was missing from `s`. - ## The proc will return false if `key` was in `s` and it was removed + ## that this proc returns `true` if `key` was missing from `s`. + ## The proc will return `false` if `key` was in `s` and it was removed ## during this call. ## - ## **See also:** + ## See also: ## * `excl proc <#excl,PackedSet[A],A>`_ for excluding an element - ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding a set + ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding other set ## * `containsOrIncl proc <#containsOrIncl,PackedSet[A],A>`_ runnableExamples: - var a = [5].toPackedSet + var a = initPackedSet[int]() + a.incl(5) assert a.missingOrExcl(5) == false assert a.missingOrExcl(5) == true var count = s.len - exclImpl(s, ord(key)) + exclImpl(s, cast[int](key)) result = count == s.len proc clear*[A](result: var PackedSet[A]) = - ## Clears the `PackedSet[A]` back to an empty state. + ## Clears the PackedSet[A] back to an empty state. runnableExamples: - var a = [5, 7].toPackedSet + var a = initPackedSet[int]() + a.incl(5) + a.incl(7) clear(a) assert len(a) == 0 # setLen(result.data, InitIntSetSize) - # for i in 0..InitIntSetSize - 1: result.data[i] = nil - # result.max = InitIntSetSize - 1 - result.data = @[] + # for i in 0..InitIntSetSize-1: result.data[i] = nil + # result.max = InitIntSetSize-1 + when defined(nimNoNilSeqs): + result.data = @[] + else: + result.data = nil result.max = 0 result.counter = 0 result.head = nil result.elems = 0 -proc isNil*[A](x: PackedSet[A]): bool {.inline.} = - ## Returns true if `x` is empty, false otherwise. - runnableExamples: - var a = initPackedSet[int]() - assert a.isNil - a.incl(2) - assert not a.isNil - a.excl(2) - assert a.isNil +proc isNil*[A](x: PackedSet[A]): bool {.inline.} = x.head.isNil and x.elems == 0 - x.head.isNil and x.elems == 0 - -proc `=copy`*[A](dest: var PackedSet[A], src: PackedSet[A]) = +proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) = ## Copies `src` to `dest`. - ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_. + ## `dest` does not need to be initialized by `initPackedSet[A] proc <#initPackedSet>`_. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + b.incl(5) + b.incl(7) + a.assign(b) + assert len(a) == 2 + if src.elems <= src.a.len: - dest.data = @[] + when defined(nimNoNilSeqs): + dest.data = @[] + else: + dest.data = nil dest.max = 0 dest.counter = src.counter dest.head = nil @@ -428,8 +449,8 @@ proc `=copy`*[A](dest: var PackedSet[A], src: PackedSet[A]) = var h = it.key and dest.max var perturb = it.key while dest.data[h] != nil: h = nextTry(h, dest.max, perturb) - assert dest.data[h] == nil - var n: Trunk + assert(dest.data[h] == nil) + var n: PTrunk new(n) n.next = dest.head n.key = it.key @@ -438,30 +459,18 @@ proc `=copy`*[A](dest: var PackedSet[A], src: PackedSet[A]) = dest.data[h] = n it = it.next -proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) {.inline, deprecated.} = - ## Copies `src` to `dest`. - ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_. - runnableExamples: - var - a = initPackedSet[int]() - b = initPackedSet[int]() - b.incl(5) - b.incl(7) - a.assign(b) - assert len(a) == 2 - `=copy`(dest, src) - proc union*[A](s1, s2: PackedSet[A]): PackedSet[A] = ## Returns the union of the sets `s1` and `s2`. ## ## The same as `s1 + s2 <#+,PackedSet[A],PackedSet[A]>`_. runnableExamples: - let - a = [1, 2, 3].toPackedSet - b = [3, 4, 5].toPackedSet - c = union(a, b) - assert c.len == 5 - assert c == [1, 2, 3, 4, 5].toPackedSet + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2); a.incl(3) + b.incl(3); b.incl(4); b.incl(5) + assert union(a, b).len == 5 + ## {1, 2, 3, 4, 5} result.assign(s1) incl(result, s2) @@ -471,12 +480,13 @@ proc intersection*[A](s1, s2: PackedSet[A]): PackedSet[A] = ## ## The same as `s1 * s2 <#*,PackedSet[A],PackedSet[A]>`_. runnableExamples: - let - a = [1, 2, 3].toPackedSet - b = [3, 4, 5].toPackedSet - c = intersection(a, b) - assert c.len == 1 - assert c == [3].toPackedSet + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2); a.incl(3) + b.incl(3); b.incl(4); b.incl(5) + assert intersection(a, b).len == 1 + ## {3} result = initPackedSet[A]() for item in s1.items: @@ -488,12 +498,13 @@ proc difference*[A](s1, s2: PackedSet[A]): PackedSet[A] = ## ## The same as `s1 - s2 <#-,PackedSet[A],PackedSet[A]>`_. runnableExamples: - let - a = [1, 2, 3].toPackedSet - b = [3, 4, 5].toPackedSet - c = difference(a, b) - assert c.len == 2 - assert c == [1, 2].toPackedSet + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2); a.incl(3) + b.incl(3); b.incl(4); b.incl(5) + assert difference(a, b).len == 2 + ## {1, 2} result = initPackedSet[A]() for item in s1.items: @@ -503,17 +514,17 @@ proc difference*[A](s1, s2: PackedSet[A]): PackedSet[A] = proc symmetricDifference*[A](s1, s2: PackedSet[A]): PackedSet[A] = ## Returns the symmetric difference of the sets `s1` and `s2`. runnableExamples: - let - a = [1, 2, 3].toPackedSet - b = [3, 4, 5].toPackedSet - c = symmetricDifference(a, b) - assert c.len == 4 - assert c == [1, 2, 4, 5].toPackedSet + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2); a.incl(3) + b.incl(3); b.incl(4); b.incl(5) + assert symmetricDifference(a, b).len == 4 + ## {1, 2, 4, 5} result.assign(s1) for item in s2.items: - if containsOrIncl(result, item): - excl(result, item) + if containsOrIncl(result, item): excl(result, item) proc `+`*[A](s1, s2: PackedSet[A]): PackedSet[A] {.inline.} = ## Alias for `union(s1, s2) <#union,PackedSet[A],PackedSet[A]>`_. @@ -530,12 +541,14 @@ proc `-`*[A](s1, s2: PackedSet[A]): PackedSet[A] {.inline.} = proc disjoint*[A](s1, s2: PackedSet[A]): bool = ## Returns true if the sets `s1` and `s2` have no items in common. runnableExamples: - let - a = [1, 2].toPackedSet - b = [2, 3].toPackedSet - c = [3, 4].toPackedSet + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2) + b.incl(2); b.incl(3) assert disjoint(a, b) == false - assert disjoint(a, c) == true + b.excl(2) + assert disjoint(a, b) == true for item in s1.items: if contains(s2, item): @@ -544,24 +557,24 @@ proc disjoint*[A](s1, s2: PackedSet[A]): bool = proc card*[A](s: PackedSet[A]): int {.inline.} = ## Alias for `len() <#len,PackedSet[A]>`_. - ## - ## Card stands for the [cardinality](http://en.wikipedia.org/wiki/Cardinality) - ## of a set. result = s.len() proc `<=`*[A](s1, s2: PackedSet[A]): bool = - ## Returns true if `s1` is a subset of `s2`. + ## Returns true if `s1` is subset of `s2`. ## - ## A subset `s1` has all of its elements in `s2`, but `s2` doesn't necessarily + ## A subset `s1` has all of its elements in `s2`, and `s2` doesn't necessarily ## have more elements than `s1`. That is, `s1` can be equal to `s2`. runnableExamples: - let - a = [1].toPackedSet - b = [1, 2].toPackedSet - c = [1, 3].toPackedSet + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1) + b.incl(1); b.incl(2) assert a <= b - assert b <= b - assert not (c <= b) + a.incl(2) + assert a <= b + a.incl(3) + assert(not (a <= b)) for item in s1.items: if not s2.contains(item): @@ -569,33 +582,27 @@ proc `<=`*[A](s1, s2: PackedSet[A]): bool = return true proc `<`*[A](s1, s2: PackedSet[A]): bool = - ## Returns true if `s1` is a proper subset of `s2`. + ## Returns true if `s1` is proper subset of `s2`. ## ## A strict or proper subset `s1` has all of its elements in `s2`, but `s2` has ## more elements than `s1`. runnableExamples: - let - a = [1].toPackedSet - b = [1, 2].toPackedSet - c = [1, 3].toPackedSet + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1) + b.incl(1); b.incl(2) assert a < b - assert not (b < b) - assert not (c < b) - + a.incl(2) + assert(not (a < b)) return s1 <= s2 and not (s2 <= s1) proc `==`*[A](s1, s2: PackedSet[A]): bool = ## Returns true if both `s1` and `s2` have the same elements and set size. - runnableExamples: - assert [1, 2].toPackedSet == [2, 1].toPackedSet - assert [1, 2].toPackedSet == [2, 1, 2].toPackedSet - return s1 <= s2 and s2 <= s1 proc `$`*[A](s: PackedSet[A]): string = - ## Converts `s` to a string. - runnableExamples: - let a = [1, 2, 3].toPackedSet - assert $a == "{1, 2, 3}" - + ## The `$` operator for int sets. + ## + ## Converts the set `s` to a string, mostly for logging and printing purposes. dollarImpl() diff --git a/lib/std/paths.nim b/lib/std/paths.nim deleted file mode 100644 index c29096982..000000000 --- a/lib/std/paths.nim +++ /dev/null @@ -1,289 +0,0 @@ -## This module implements path handling. - -import std/private/osseps -export osseps - -import std/envvars -import std/private/osappdirs - -import pathnorm - -from std/private/ospaths2 import joinPath, splitPath, - ReadDirEffect, WriteDirEffect, - isAbsolute, relativePath, - normalizePathEnd, isRelativeTo, parentDir, - tailDir, isRootDir, parentDirs, `/../`, - extractFilename, lastPathPart, - changeFileExt, addFileExt, cmpPaths, splitFile, - unixToNativePath, absolutePath, normalizeExe, - normalizePath -export ReadDirEffect, WriteDirEffect - -type - Path* = distinct string - -func `==`*(x, y: Path): bool {.inline.} = - ## Compares two paths. - ## - ## On a case-sensitive filesystem this is done - ## case-sensitively otherwise case-insensitively. - result = cmpPaths(x.string, y.string) == 0 - -template endsWith(a: string, b: set[char]): bool = - a.len > 0 and a[^1] in b - -func add(x: var string, tail: string) = - var state = 0 - let trailingSep = tail.endsWith({DirSep, AltSep}) or tail.len == 0 and x.endsWith({DirSep, AltSep}) - normalizePathEnd(x, trailingSep=false) - addNormalizePath(tail, x, state, DirSep) - normalizePathEnd(x, trailingSep=trailingSep) - -func add*(x: var Path, y: Path) {.borrow.} - -func `/`*(head, tail: Path): Path {.inline.} = - ## Joins two directory names to one. - ## - ## returns normalized path concatenation of `head` and `tail`, preserving - ## whether or not `tail` has a trailing slash (or, if tail if empty, whether - ## head has one). - ## - ## See also: - ## * `splitPath proc`_ - ## * `uri.combine proc `_ - ## * `uri./ proc `_ - Path(joinPath(head.string, tail.string)) - -func splitPath*(path: Path): tuple[head, tail: Path] {.inline.} = - ## Splits a directory into `(head, tail)` tuple, so that - ## ``head / tail == path`` (except for edge cases like "/usr"). - ## - ## See also: - ## * `add proc`_ - ## * `/ proc`_ - ## * `/../ proc`_ - ## * `relativePath proc`_ - let res = splitPath(path.string) - result = (Path(res.head), Path(res.tail)) - -func splitFile*(path: Path): tuple[dir, name: Path, ext: string] {.inline.} = - ## Splits a filename into `(dir, name, extension)` tuple. - ## - ## `dir` does not end in DirSep unless it's `/`. - ## `extension` includes the leading dot. - ## - ## If `path` has no extension, `ext` is the empty string. - ## If `path` has no directory component, `dir` is the empty string. - ## If `path` has no filename component, `name` and `ext` are empty strings. - ## - ## See also: - ## * `extractFilename proc`_ - ## * `lastPathPart proc`_ - ## * `changeFileExt proc`_ - ## * `addFileExt proc`_ - let res = splitFile(path.string) - result = (Path(res.dir), Path(res.name), res.ext) - -func isAbsolute*(path: Path): bool {.inline, raises: [].} = - ## Checks whether a given `path` is absolute. - ## - ## On Windows, network paths are considered absolute too. - result = isAbsolute(path.string) - -proc relativePath*(path, base: Path, sep = DirSep): Path {.inline.} = - ## Converts `path` to a path relative to `base`. - ## - ## The `sep` (default: DirSep) is used for the path normalizations, - ## this can be useful to ensure the relative path only contains `'/'` - ## so that it can be used for URL constructions. - ## - ## On Windows, if a root of `path` and a root of `base` are different, - ## returns `path` as is because it is impossible to make a relative path. - ## That means an absolute path can be returned. - ## - ## See also: - ## * `splitPath proc`_ - ## * `parentDir proc`_ - ## * `tailDir proc`_ - result = Path(relativePath(path.string, base.string, sep)) - -proc isRelativeTo*(path: Path, base: Path): bool {.inline.} = - ## Returns true if `path` is relative to `base`. - result = isRelativeTo(path.string, base.string) - - -func parentDir*(path: Path): Path {.inline.} = - ## Returns the parent directory of `path`. - ## - ## This is similar to ``splitPath(path).head`` when ``path`` doesn't end - ## in a dir separator, but also takes care of path normalizations. - ## The remainder can be obtained with `lastPathPart(path) proc`_. - ## - ## See also: - ## * `relativePath proc`_ - ## * `splitPath proc`_ - ## * `tailDir proc`_ - ## * `parentDirs iterator`_ - result = Path(parentDir(path.string)) - -func tailDir*(path: Path): Path {.inline.} = - ## Returns the tail part of `path`. - ## - ## See also: - ## * `relativePath proc`_ - ## * `splitPath proc`_ - ## * `parentDir proc`_ - result = Path(tailDir(path.string)) - -func isRootDir*(path: Path): bool {.inline.} = - ## Checks whether a given `path` is a root directory. - result = isRootDir(path.string) - -iterator parentDirs*(path: Path, fromRoot=false, inclusive=true): Path = - ## Walks over all parent directories of a given `path`. - ## - ## If `fromRoot` is true (default: false), the traversal will start from - ## the file system root directory. - ## If `inclusive` is true (default), the original argument will be included - ## in the traversal. - ## - ## Relative paths won't be expanded by this iterator. Instead, it will traverse - ## only the directories appearing in the relative path. - ## - ## See also: - ## * `parentDir proc`_ - ## - for p in parentDirs(path.string, fromRoot, inclusive): - yield Path(p) - -func `/../`*(head, tail: Path): Path {.inline.} = - ## The same as ``parentDir(head) / tail``, unless there is no parent - ## directory. Then ``head / tail`` is performed instead. - ## - ## See also: - ## * `/ proc`_ - ## * `parentDir proc`_ - Path(`/../`(head.string, tail.string)) - -func extractFilename*(path: Path): Path {.inline.} = - ## Extracts the filename of a given `path`. - ## - ## This is the same as ``name & ext`` from `splitFile(path) proc`_. - ## - ## See also: - ## * `splitFile proc`_ - ## * `lastPathPart proc`_ - ## * `changeFileExt proc`_ - ## * `addFileExt proc`_ - result = Path(extractFilename(path.string)) - -func lastPathPart*(path: Path): Path {.inline.} = - ## Like `extractFilename proc`_, but ignores - ## trailing dir separator; aka: `baseName`:idx: in some other languages. - ## - ## See also: - ## * `splitFile proc`_ - ## * `extractFilename proc`_ - ## * `changeFileExt proc`_ - ## * `addFileExt proc`_ - result = Path(lastPathPart(path.string)) - -func changeFileExt*(filename: Path, ext: string): Path {.inline.} = - ## Changes the file extension to `ext`. - ## - ## If the `filename` has no extension, `ext` will be added. - ## If `ext` == "" then any extension is removed. - ## - ## `Ext` should be given without the leading `'.'`, because some - ## filesystems may use a different character. (Although I know - ## of none such beast.) - ## - ## See also: - ## * `splitFile proc`_ - ## * `extractFilename proc`_ - ## * `lastPathPart proc`_ - ## * `addFileExt proc`_ - result = Path(changeFileExt(filename.string, ext)) - -func addFileExt*(filename: Path, ext: string): Path {.inline.} = - ## Adds the file extension `ext` to `filename`, unless - ## `filename` already has an extension. - ## - ## `Ext` should be given without the leading `'.'`, because some - ## filesystems may use a different character. - ## (Although I know of none such beast.) - ## - ## See also: - ## * `splitFile proc`_ - ## * `extractFilename proc`_ - ## * `lastPathPart proc`_ - ## * `changeFileExt proc`_ - result = Path(addFileExt(filename.string, ext)) - -func unixToNativePath*(path: Path, drive=Path("")): Path {.inline.} = - ## Converts an UNIX-like path to a native one. - ## - ## On an UNIX system this does nothing. Else it converts - ## `'/'`, `'.'`, `'..'` to the appropriate things. - ## - ## On systems with a concept of "drives", `drive` is used to determine - ## which drive label to use during absolute path conversion. - ## `drive` defaults to the drive of the current working directory, and is - ## ignored on systems that do not have a concept of "drives". - result = Path(unixToNativePath(path.string, drive.string)) - -proc getCurrentDir*(): Path {.inline, tags: [].} = - ## Returns the `current working directory`:idx: i.e. where the built - ## binary is run. - ## - ## So the path returned by this proc is determined at run time. - ## - ## See also: - ## * `getHomeDir proc `_ - ## * `getConfigDir proc `_ - ## * `getTempDir proc `_ - ## * `setCurrentDir proc `_ - ## * `currentSourcePath template `_ - ## * `getProjectPath proc `_ - result = Path(ospaths2.getCurrentDir()) - -proc normalizeExe*(file: var Path) {.borrow.} - -proc normalizePath*(path: var Path) {.borrow.} - -proc normalizePathEnd*(path: var Path, trailingSep = false) {.borrow.} - -proc absolutePath*(path: Path, root = getCurrentDir()): Path = - ## Returns the absolute path of `path`, rooted at `root` (which must be absolute; - ## default: current directory). - ## If `path` is absolute, return it, ignoring `root`. - ## - ## See also: - ## * `normalizePath proc`_ - result = Path(absolutePath(path.string, root.string)) - -proc expandTildeImpl(path: string): string {. - tags: [ReadEnvEffect, ReadIOEffect].} = - if len(path) == 0 or path[0] != '~': - result = path - elif len(path) == 1: - result = getHomeDir() - elif (path[1] in {DirSep, AltSep}): - result = joinPath(getHomeDir(), path.substr(2)) - else: - # TODO: handle `~bob` and `~bob/` which means home of bob - result = path - -proc expandTilde*(path: Path): Path {.inline, - tags: [ReadEnvEffect, ReadIOEffect].} = - ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing - ## ``~`` with `getHomeDir() `_ (otherwise returns ``path`` unmodified). - ## - ## Windows: this is still supported despite the Windows platform not having this - ## convention; also, both ``~/`` and ``~\`` are handled. - runnableExamples: - import std/appdirs - assert expandTilde(Path("~") / Path("appname.cfg")) == getHomeDir() / Path("appname.cfg") - assert expandTilde(Path("~/foo/bar")) == getHomeDir() / Path("foo/bar") - assert expandTilde(Path("/foo/bar")) == Path("/foo/bar") - result = Path(expandTildeImpl(path.string)) diff --git a/lib/std/private/bitops_utils.nim b/lib/std/private/bitops_utils.nim deleted file mode 100644 index 0b9484416..000000000 --- a/lib/std/private/bitops_utils.nim +++ /dev/null @@ -1,22 +0,0 @@ -template forwardImpl*(impl, arg) {.dirty.} = - when sizeof(x) <= 4: - when x is SomeSignedInt: - impl(cast[uint32](x.int32)) - else: - impl(x.uint32) - else: - when x is SomeSignedInt: - impl(cast[uint64](x.int64)) - else: - impl(x.uint64) - -# this could also be implemented via: -# import std/typetraits -# template castToUnsigned*(x: SomeInteger): auto = cast[toUnsigned(typeof(x))](x) - -template castToUnsigned*(x: int8): uint8 = cast[uint8](x) -template castToUnsigned*(x: int16): uint16 = cast[uint16](x) -template castToUnsigned*(x: int32): uint32 = cast[uint32](x) -template castToUnsigned*(x: int64): uint64 = cast[uint64](x) -template castToUnsigned*(x: int): uint = cast[uint](x) -template castToUnsigned*[T: SomeUnsignedInt](x: T): T = x diff --git a/lib/std/private/decode_helpers.nim b/lib/std/private/decode_helpers.nim index f11e3060a..e28651690 100644 --- a/lib/std/private/decode_helpers.nim +++ b/lib/std/private/decode_helpers.nim @@ -21,13 +21,6 @@ proc handleHexChar*(c: char, x: var int): bool {.inline.} = else: result = false -proc handleHexChar*(c: char): int {.inline.} = - case c - of '0'..'9': result = (ord(c) - ord('0')) - of 'a'..'f': result = (ord(c) - ord('a') + 10) - of 'A'..'F': result = (ord(c) - ord('A') + 10) - else: discard - proc decodePercent*(s: openArray[char], i: var int): char = ## Converts `%xx` hexadecimal to the character with ordinal number `xx`. ## diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim deleted file mode 100644 index e051e5218..000000000 --- a/lib/std/private/digitsutils.nim +++ /dev/null @@ -1,115 +0,0 @@ -const - trailingZeros100: array[100, int8] = [2'i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0] - - digits100: array[200, char] = ['0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', - '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', - '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2', '2', '3', - '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', - '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', - '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', - '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', - '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', - '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', - '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', - '8', '7', '8', '8', '8', '9', '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', - '9', '6', '9', '7', '9', '8', '9', '9'] - -# Inspired by https://engineering.fb.com/2013/03/15/developer-tools/three-optimization-tips-for-c -# Generates: -# .. code-block:: nim -# var res = "" -# for i in 0 .. 99: -# if i < 10: -# res.add "0" & $i -# else: -# res.add $i -# doAssert res == digits100 - -proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = - buf[pos] = digits100[2 * digits] - buf[pos+1] = digits100[2 * digits + 1] - #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char))) - -proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} = - return trailingZeros100[digits.int8] - -when defined(js): - proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".} - -func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} = - let old = result.len - result.setLen old + n - template impl = - for i in 0..= nbatch: - let originNum = num - num = num div nbatch - let index = int16((originNum - num * nbatch) shl 1) - tmp[next] = digits100[index + 1] - tmp[next - 1] = digits100[index] - dec(next, 2) - - # process last 1-2 digits - if num < 10: - tmp[next] = chr(ord('0') + num.uint8) - else: - let index = num * 2 - tmp[next] = digits100[index + 1] - tmp[next - 1] = digits100[index] - dec next - addChars(result, tmp, next, tmp.len - next) - -when not defined(nimHasEnforceNoRaises): - {.pragma: enforceNoRaises.} - -func addInt*(result: var string, x: uint64) {.enforceNoRaises.} = - when nimvm: addIntImpl(result, x) - else: - when not defined(js): addIntImpl(result, x) - else: - addChars(result, numToString(x)) - -proc addInt*(result: var string; x: int64) {.enforceNoRaises.} = - ## Converts integer to its string representation and appends it to `result`. - runnableExamples: - var s = "foo" - s.addInt(45) - assert s == "foo45" - template impl = - var num: uint64 - if x < 0: - if x == low(int64): - num = cast[uint64](x) - else: - num = uint64(-x) - result.add '-' - else: - num = uint64(x) - addInt(result, num) - when nimvm: impl() - else: - when defined(js): - addChars(result, numToString(x)) - else: impl() - -proc addInt*(result: var string; x: int) {.inline, enforceNoRaises.} = - addInt(result, int64(x)) diff --git a/lib/std/private/dragonbox.nim b/lib/std/private/dragonbox.nim deleted file mode 100644 index 2ba22a751..000000000 --- a/lib/std/private/dragonbox.nim +++ /dev/null @@ -1,1325 +0,0 @@ -## Copyright 2020 Junekey Jeon -## Copyright 2020 Alexander Bolz -## -## Distributed under the Boost Software License, Version 1.0. -## (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) - -## char* output_end = Dtoa(buffer, value); -## -## Converts the given double-precision number into decimal form and stores the result in the given -## buffer. -## -## The buffer must be large enough, i.e. >= DtoaMinBufferLength. -## The output format is similar to printf("%g"). -## The output is _not_ null-terminted. -## -## The output is optimal, i.e. the output string -## 1. rounds back to the input number when read in (using round-to-nearest-even) -## 2. is as short as possible, -## 3. is as close to the input number as possible. -## -## Note: -## This function may temporarily write up to DtoaMinBufferLength characters into the buffer. - - -import std/private/digitsutils - -when defined(nimPreviewSlimSystem): - import std/assertions - -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: -## https://github.com/jk-jeon/dragonbox -## -## 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 - BitsType* = uint64 - -type - Double* = object - bits*: BitsType - -const ## = p (includes the hidden bit) - significandSize*: int32 = 53 - -const ## static constexpr int32_t MaxExponent = 1024 - 1 - (SignificandSize - 1); - ## static constexpr int32_t MinExponent = std::numeric_limits::min_exponent - 1 - (SignificandSize - 1); - exponentBias*: int32 = 1024 - 1 + (significandSize - 1) - -const - maxIeeeExponent*: BitsType = BitsType(2 * 1024 - 1) - -const ## = 2^(p-1) - hiddenBit*: BitsType = BitsType(1) shl (significandSize - 1) - -const ## = 2^(p-1) - 1 - significandMask*: BitsType = hiddenBit - 1 - -const - exponentMask*: BitsType = maxIeeeExponent shl (significandSize - 1) - -const - signMask*: BitsType = not (not BitsType(0) shr 1) - -proc constructDouble*(bits: BitsType): Double {.constructor.} = - result.bits = bits - -proc constructDouble*(value: ValueType): Double {.constructor.} = - result.bits = cast[typeof(result.bits)](value) - -proc physicalSignificand*(this: Double): BitsType {.noSideEffect.} = - return this.bits and significandMask - -proc physicalExponent*(this: Double): BitsType {.noSideEffect.} = - return (this.bits and exponentMask) shr (significandSize - 1) - -proc isFinite*(this: Double): bool {.noSideEffect.} = - return (this.bits and exponentMask) != exponentMask - -proc isInf*(this: Double): bool {.noSideEffect.} = - return (this.bits and exponentMask) == exponentMask and - (this.bits and significandMask) == 0 - -proc isNaN*(this: Double): bool {.noSideEffect.} = - return (this.bits and exponentMask) == exponentMask and - (this.bits and significandMask) != 0 - -proc isZero*(this: Double): bool {.noSideEffect.} = - return (this.bits and not signMask) == 0 - -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... -## Should easily be optimized into SAR (or equivalent) instruction. - -proc floorDivPow2*(x: int32; n: int32): int32 {.inline.} = - return x shr n - -proc floorLog2Pow10*(e: int32): int32 {.inline.} = - dragonbox_Assert(e >= -1233) - dragonbox_Assert(e <= 1233) - return floorDivPow2(e * 1741647, 19) - -proc floorLog10Pow2*(e: int32): int32 {.inline.} = - dragonbox_Assert(e >= -1500) - dragonbox_Assert(e <= 1500) - return floorDivPow2(e * 1262611, 22) - -proc floorLog10ThreeQuartersPow2*(e: int32): int32 {.inline.} = - dragonbox_Assert(e >= -1500) - dragonbox_Assert(e <= 1500) - return floorDivPow2(e * 1262611 - 524031, 22) - -# ================================================================================================== -# -# ================================================================================================== - -type - uint64x2* {.bycopy.} = object - hi*: uint64 - lo*: uint64 - - -proc computePow10*(k: int32): uint64x2 {.inline.} = - const - kMin: int32 = -292 - const - kMax: int32 = 326 - const - pow10: array[kMax - kMin + 1, uint64x2] = [ - uint64x2(hi: 0xFF77B1FCBEBCDC4F'u, lo: 0x25E8E89C13BB0F7B'u), - uint64x2(hi: 0x9FAACF3DF73609B1'u, lo: 0x77B191618C54E9AD'u), - uint64x2(hi: 0xC795830D75038C1D'u, lo: 0xD59DF5B9EF6A2418'u), - uint64x2(hi: 0xF97AE3D0D2446F25'u, lo: 0x4B0573286B44AD1E'u), - uint64x2(hi: 0x9BECCE62836AC577'u, lo: 0x4EE367F9430AEC33'u), - uint64x2(hi: 0xC2E801FB244576D5'u, lo: 0x229C41F793CDA740'u), - uint64x2(hi: 0xF3A20279ED56D48A'u, lo: 0x6B43527578C11110'u), - uint64x2(hi: 0x9845418C345644D6'u, lo: 0x830A13896B78AAAA'u), - uint64x2(hi: 0xBE5691EF416BD60C'u, lo: 0x23CC986BC656D554'u), - uint64x2(hi: 0xEDEC366B11C6CB8F'u, lo: 0x2CBFBE86B7EC8AA9'u), - uint64x2(hi: 0x94B3A202EB1C3F39'u, lo: 0x7BF7D71432F3D6AA'u), - uint64x2(hi: 0xB9E08A83A5E34F07'u, lo: 0xDAF5CCD93FB0CC54'u), - uint64x2(hi: 0xE858AD248F5C22C9'u, lo: 0xD1B3400F8F9CFF69'u), - uint64x2(hi: 0x91376C36D99995BE'u, lo: 0x23100809B9C21FA2'u), - uint64x2(hi: 0xB58547448FFFFB2D'u, lo: 0xABD40A0C2832A78B'u), - uint64x2(hi: 0xE2E69915B3FFF9F9'u, lo: 0x16C90C8F323F516D'u), - uint64x2(hi: 0x8DD01FAD907FFC3B'u, lo: 0xAE3DA7D97F6792E4'u), - uint64x2(hi: 0xB1442798F49FFB4A'u, lo: 0x99CD11CFDF41779D'u), - uint64x2(hi: 0xDD95317F31C7FA1D'u, lo: 0x40405643D711D584'u), - uint64x2(hi: 0x8A7D3EEF7F1CFC52'u, lo: 0x482835EA666B2573'u), - uint64x2(hi: 0xAD1C8EAB5EE43B66'u, lo: 0xDA3243650005EED0'u), - uint64x2(hi: 0xD863B256369D4A40'u, lo: 0x90BED43E40076A83'u), - uint64x2(hi: 0x873E4F75E2224E68'u, lo: 0x5A7744A6E804A292'u), - uint64x2(hi: 0xA90DE3535AAAE202'u, lo: 0x711515D0A205CB37'u), - uint64x2(hi: 0xD3515C2831559A83'u, lo: 0x0D5A5B44CA873E04'u), - uint64x2(hi: 0x8412D9991ED58091'u, lo: 0xE858790AFE9486C3'u), - uint64x2(hi: 0xA5178FFF668AE0B6'u, lo: 0x626E974DBE39A873'u), - uint64x2(hi: 0xCE5D73FF402D98E3'u, lo: 0xFB0A3D212DC81290'u), - uint64x2(hi: 0x80FA687F881C7F8E'u, lo: 0x7CE66634BC9D0B9A'u), - uint64x2(hi: 0xA139029F6A239F72'u, lo: 0x1C1FFFC1EBC44E81'u), - uint64x2(hi: 0xC987434744AC874E'u, lo: 0xA327FFB266B56221'u), - uint64x2(hi: 0xFBE9141915D7A922'u, lo: 0x4BF1FF9F0062BAA9'u), - uint64x2(hi: 0x9D71AC8FADA6C9B5'u, lo: 0x6F773FC3603DB4AA'u), - uint64x2(hi: 0xC4CE17B399107C22'u, lo: 0xCB550FB4384D21D4'u), - uint64x2(hi: 0xF6019DA07F549B2B'u, lo: 0x7E2A53A146606A49'u), - uint64x2(hi: 0x99C102844F94E0FB'u, lo: 0x2EDA7444CBFC426E'u), - uint64x2(hi: 0xC0314325637A1939'u, lo: 0xFA911155FEFB5309'u), - uint64x2(hi: 0xF03D93EEBC589F88'u, lo: 0x793555AB7EBA27CB'u), - uint64x2(hi: 0x96267C7535B763B5'u, lo: 0x4BC1558B2F3458DF'u), - uint64x2(hi: 0xBBB01B9283253CA2'u, lo: 0x9EB1AAEDFB016F17'u), - uint64x2(hi: 0xEA9C227723EE8BCB'u, lo: 0x465E15A979C1CADD'u), - uint64x2(hi: 0x92A1958A7675175F'u, lo: 0x0BFACD89EC191ECA'u), - uint64x2(hi: 0xB749FAED14125D36'u, lo: 0xCEF980EC671F667C'u), - uint64x2(hi: 0xE51C79A85916F484'u, lo: 0x82B7E12780E7401B'u), - uint64x2(hi: 0x8F31CC0937AE58D2'u, lo: 0xD1B2ECB8B0908811'u), - uint64x2(hi: 0xB2FE3F0B8599EF07'u, lo: 0x861FA7E6DCB4AA16'u), - uint64x2(hi: 0xDFBDCECE67006AC9'u, lo: 0x67A791E093E1D49B'u), - uint64x2(hi: 0x8BD6A141006042BD'u, lo: 0xE0C8BB2C5C6D24E1'u), - uint64x2(hi: 0xAECC49914078536D'u, lo: 0x58FAE9F773886E19'u), - uint64x2(hi: 0xDA7F5BF590966848'u, lo: 0xAF39A475506A899F'u), - uint64x2(hi: 0x888F99797A5E012D'u, lo: 0x6D8406C952429604'u), - uint64x2(hi: 0xAAB37FD7D8F58178'u, lo: 0xC8E5087BA6D33B84'u), - uint64x2(hi: 0xD5605FCDCF32E1D6'u, lo: 0xFB1E4A9A90880A65'u), - uint64x2(hi: 0x855C3BE0A17FCD26'u, lo: 0x5CF2EEA09A550680'u), - uint64x2(hi: 0xA6B34AD8C9DFC06F'u, lo: 0xF42FAA48C0EA481F'u), - uint64x2(hi: 0xD0601D8EFC57B08B'u, lo: 0xF13B94DAF124DA27'u), - uint64x2(hi: 0x823C12795DB6CE57'u, lo: 0x76C53D08D6B70859'u), - uint64x2(hi: 0xA2CB1717B52481ED'u, lo: 0x54768C4B0C64CA6F'u), - uint64x2(hi: 0xCB7DDCDDA26DA268'u, lo: 0xA9942F5DCF7DFD0A'u), - uint64x2(hi: 0xFE5D54150B090B02'u, lo: 0xD3F93B35435D7C4D'u), - uint64x2(hi: 0x9EFA548D26E5A6E1'u, lo: 0xC47BC5014A1A6DB0'u), - uint64x2(hi: 0xC6B8E9B0709F109A'u, lo: 0x359AB6419CA1091C'u), - uint64x2(hi: 0xF867241C8CC6D4C0'u, lo: 0xC30163D203C94B63'u), - uint64x2(hi: 0x9B407691D7FC44F8'u, lo: 0x79E0DE63425DCF1E'u), - uint64x2(hi: 0xC21094364DFB5636'u, lo: 0x985915FC12F542E5'u), - uint64x2(hi: 0xF294B943E17A2BC4'u, lo: 0x3E6F5B7B17B2939E'u), - uint64x2(hi: 0x979CF3CA6CEC5B5A'u, lo: 0xA705992CEECF9C43'u), - uint64x2(hi: 0xBD8430BD08277231'u, lo: 0x50C6FF782A838354'u), - uint64x2(hi: 0xECE53CEC4A314EBD'u, lo: 0xA4F8BF5635246429'u), - uint64x2(hi: 0x940F4613AE5ED136'u, lo: 0x871B7795E136BE9A'u), - uint64x2(hi: 0xB913179899F68584'u, lo: 0x28E2557B59846E40'u), - uint64x2(hi: 0xE757DD7EC07426E5'u, lo: 0x331AEADA2FE589D0'u), - uint64x2(hi: 0x9096EA6F3848984F'u, lo: 0x3FF0D2C85DEF7622'u), - uint64x2(hi: 0xB4BCA50B065ABE63'u, lo: 0x0FED077A756B53AA'u), - uint64x2(hi: 0xE1EBCE4DC7F16DFB'u, lo: 0xD3E8495912C62895'u), - uint64x2(hi: 0x8D3360F09CF6E4BD'u, lo: 0x64712DD7ABBBD95D'u), - uint64x2(hi: 0xB080392CC4349DEC'u, lo: 0xBD8D794D96AACFB4'u), - uint64x2(hi: 0xDCA04777F541C567'u, lo: 0xECF0D7A0FC5583A1'u), - uint64x2(hi: 0x89E42CAAF9491B60'u, lo: 0xF41686C49DB57245'u), - uint64x2(hi: 0xAC5D37D5B79B6239'u, lo: 0x311C2875C522CED6'u), - uint64x2(hi: 0xD77485CB25823AC7'u, lo: 0x7D633293366B828C'u), - uint64x2(hi: 0x86A8D39EF77164BC'u, lo: 0xAE5DFF9C02033198'u), - uint64x2(hi: 0xA8530886B54DBDEB'u, lo: 0xD9F57F830283FDFD'u), - uint64x2(hi: 0xD267CAA862A12D66'u, lo: 0xD072DF63C324FD7C'u), - uint64x2(hi: 0x8380DEA93DA4BC60'u, lo: 0x4247CB9E59F71E6E'u), - uint64x2(hi: 0xA46116538D0DEB78'u, lo: 0x52D9BE85F074E609'u), - uint64x2(hi: 0xCD795BE870516656'u, lo: 0x67902E276C921F8C'u), - uint64x2(hi: 0x806BD9714632DFF6'u, lo: 0x00BA1CD8A3DB53B7'u), - uint64x2(hi: 0xA086CFCD97BF97F3'u, lo: 0x80E8A40ECCD228A5'u), - uint64x2(hi: 0xC8A883C0FDAF7DF0'u, lo: 0x6122CD128006B2CE'u), - uint64x2(hi: 0xFAD2A4B13D1B5D6C'u, lo: 0x796B805720085F82'u), - uint64x2(hi: 0x9CC3A6EEC6311A63'u, lo: 0xCBE3303674053BB1'u), - uint64x2(hi: 0xC3F490AA77BD60FC'u, lo: 0xBEDBFC4411068A9D'u), - uint64x2(hi: 0xF4F1B4D515ACB93B'u, lo: 0xEE92FB5515482D45'u), - uint64x2(hi: 0x991711052D8BF3C5'u, lo: 0x751BDD152D4D1C4B'u), - uint64x2(hi: 0xBF5CD54678EEF0B6'u, lo: 0xD262D45A78A0635E'u), - uint64x2(hi: 0xEF340A98172AACE4'u, lo: 0x86FB897116C87C35'u), - uint64x2(hi: 0x9580869F0E7AAC0E'u, lo: 0xD45D35E6AE3D4DA1'u), - uint64x2(hi: 0xBAE0A846D2195712'u, lo: 0x8974836059CCA10A'u), - uint64x2(hi: 0xE998D258869FACD7'u, lo: 0x2BD1A438703FC94C'u), - uint64x2(hi: 0x91FF83775423CC06'u, lo: 0x7B6306A34627DDD0'u), - uint64x2(hi: 0xB67F6455292CBF08'u, lo: 0x1A3BC84C17B1D543'u), - uint64x2(hi: 0xE41F3D6A7377EECA'u, lo: 0x20CABA5F1D9E4A94'u), - uint64x2(hi: 0x8E938662882AF53E'u, lo: 0x547EB47B7282EE9D'u), - uint64x2(hi: 0xB23867FB2A35B28D'u, lo: 0xE99E619A4F23AA44'u), - uint64x2(hi: 0xDEC681F9F4C31F31'u, lo: 0x6405FA00E2EC94D5'u), - uint64x2(hi: 0x8B3C113C38F9F37E'u, lo: 0xDE83BC408DD3DD05'u), - uint64x2(hi: 0xAE0B158B4738705E'u, lo: 0x9624AB50B148D446'u), - uint64x2(hi: 0xD98DDAEE19068C76'u, lo: 0x3BADD624DD9B0958'u), - uint64x2(hi: 0x87F8A8D4CFA417C9'u, lo: 0xE54CA5D70A80E5D7'u), - uint64x2(hi: 0xA9F6D30A038D1DBC'u, lo: 0x5E9FCF4CCD211F4D'u), - uint64x2(hi: 0xD47487CC8470652B'u, lo: 0x7647C32000696720'u), - uint64x2(hi: 0x84C8D4DFD2C63F3B'u, lo: 0x29ECD9F40041E074'u), - uint64x2(hi: 0xA5FB0A17C777CF09'u, lo: 0xF468107100525891'u), - uint64x2(hi: 0xCF79CC9DB955C2CC'u, lo: 0x7182148D4066EEB5'u), - uint64x2(hi: 0x81AC1FE293D599BF'u, lo: 0xC6F14CD848405531'u), - uint64x2(hi: 0xA21727DB38CB002F'u, lo: 0xB8ADA00E5A506A7D'u), - uint64x2(hi: 0xCA9CF1D206FDC03B'u, lo: 0xA6D90811F0E4851D'u), - uint64x2(hi: 0xFD442E4688BD304A'u, lo: 0x908F4A166D1DA664'u), - uint64x2(hi: 0x9E4A9CEC15763E2E'u, lo: 0x9A598E4E043287FF'u), - uint64x2(hi: 0xC5DD44271AD3CDBA'u, lo: 0x40EFF1E1853F29FE'u), - uint64x2(hi: 0xF7549530E188C128'u, lo: 0xD12BEE59E68EF47D'u), - uint64x2(hi: 0x9A94DD3E8CF578B9'u, lo: 0x82BB74F8301958CF'u), - uint64x2(hi: 0xC13A148E3032D6E7'u, lo: 0xE36A52363C1FAF02'u), - uint64x2(hi: 0xF18899B1BC3F8CA1'u, lo: 0xDC44E6C3CB279AC2'u), - uint64x2(hi: 0x96F5600F15A7B7E5'u, lo: 0x29AB103A5EF8C0BA'u), - uint64x2(hi: 0xBCB2B812DB11A5DE'u, lo: 0x7415D448F6B6F0E8'u), - uint64x2(hi: 0xEBDF661791D60F56'u, lo: 0x111B495B3464AD22'u), - uint64x2(hi: 0x936B9FCEBB25C995'u, lo: 0xCAB10DD900BEEC35'u), - uint64x2(hi: 0xB84687C269EF3BFB'u, lo: 0x3D5D514F40EEA743'u), - uint64x2(hi: 0xE65829B3046B0AFA'u, lo: 0x0CB4A5A3112A5113'u), - uint64x2(hi: 0x8FF71A0FE2C2E6DC'u, lo: 0x47F0E785EABA72AC'u), - uint64x2(hi: 0xB3F4E093DB73A093'u, lo: 0x59ED216765690F57'u), - uint64x2(hi: 0xE0F218B8D25088B8'u, lo: 0x306869C13EC3532D'u), - uint64x2(hi: 0x8C974F7383725573'u, lo: 0x1E414218C73A13FC'u), - uint64x2(hi: 0xAFBD2350644EEACF'u, lo: 0xE5D1929EF90898FB'u), - uint64x2(hi: 0xDBAC6C247D62A583'u, lo: 0xDF45F746B74ABF3A'u), - uint64x2(hi: 0x894BC396CE5DA772'u, lo: 0x6B8BBA8C328EB784'u), - uint64x2(hi: 0xAB9EB47C81F5114F'u, lo: 0x066EA92F3F326565'u), - uint64x2(hi: 0xD686619BA27255A2'u, lo: 0xC80A537B0EFEFEBE'u), - uint64x2(hi: 0x8613FD0145877585'u, lo: 0xBD06742CE95F5F37'u), - uint64x2(hi: 0xA798FC4196E952E7'u, lo: 0x2C48113823B73705'u), - uint64x2(hi: 0xD17F3B51FCA3A7A0'u, lo: 0xF75A15862CA504C6'u), - uint64x2(hi: 0x82EF85133DE648C4'u, lo: 0x9A984D73DBE722FC'u), - uint64x2(hi: 0xA3AB66580D5FDAF5'u, lo: 0xC13E60D0D2E0EBBB'u), - uint64x2(hi: 0xCC963FEE10B7D1B3'u, lo: 0x318DF905079926A9'u), - uint64x2(hi: 0xFFBBCFE994E5C61F'u, lo: 0xFDF17746497F7053'u), - uint64x2(hi: 0x9FD561F1FD0F9BD3'u, lo: 0xFEB6EA8BEDEFA634'u), - uint64x2(hi: 0xC7CABA6E7C5382C8'u, lo: 0xFE64A52EE96B8FC1'u), - uint64x2(hi: 0xF9BD690A1B68637B'u, lo: 0x3DFDCE7AA3C673B1'u), - uint64x2(hi: 0x9C1661A651213E2D'u, lo: 0x06BEA10CA65C084F'u), - uint64x2(hi: 0xC31BFA0FE5698DB8'u, lo: 0x486E494FCFF30A63'u), - uint64x2(hi: 0xF3E2F893DEC3F126'u, lo: 0x5A89DBA3C3EFCCFB'u), - uint64x2(hi: 0x986DDB5C6B3A76B7'u, lo: 0xF89629465A75E01D'u), - uint64x2(hi: 0xBE89523386091465'u, lo: 0xF6BBB397F1135824'u), - uint64x2(hi: 0xEE2BA6C0678B597F'u, lo: 0x746AA07DED582E2D'u), - uint64x2(hi: 0x94DB483840B717EF'u, lo: 0xA8C2A44EB4571CDD'u), - uint64x2(hi: 0xBA121A4650E4DDEB'u, lo: 0x92F34D62616CE414'u), - uint64x2(hi: 0xE896A0D7E51E1566'u, lo: 0x77B020BAF9C81D18'u), - uint64x2(hi: 0x915E2486EF32CD60'u, lo: 0x0ACE1474DC1D122F'u), - uint64x2(hi: 0xB5B5ADA8AAFF80B8'u, lo: 0x0D819992132456BB'u), - uint64x2(hi: 0xE3231912D5BF60E6'u, lo: 0x10E1FFF697ED6C6A'u), - uint64x2(hi: 0x8DF5EFABC5979C8F'u, lo: 0xCA8D3FFA1EF463C2'u), - uint64x2(hi: 0xB1736B96B6FD83B3'u, lo: 0xBD308FF8A6B17CB3'u), - uint64x2(hi: 0xDDD0467C64BCE4A0'u, lo: 0xAC7CB3F6D05DDBDF'u), - uint64x2(hi: 0x8AA22C0DBEF60EE4'u, lo: 0x6BCDF07A423AA96C'u), - uint64x2(hi: 0xAD4AB7112EB3929D'u, lo: 0x86C16C98D2C953C7'u), - uint64x2(hi: 0xD89D64D57A607744'u, lo: 0xE871C7BF077BA8B8'u), - uint64x2(hi: 0x87625F056C7C4A8B'u, lo: 0x11471CD764AD4973'u), - uint64x2(hi: 0xA93AF6C6C79B5D2D'u, lo: 0xD598E40D3DD89BD0'u), - uint64x2(hi: 0xD389B47879823479'u, lo: 0x4AFF1D108D4EC2C4'u), - uint64x2(hi: 0x843610CB4BF160CB'u, lo: 0xCEDF722A585139BB'u), - uint64x2(hi: 0xA54394FE1EEDB8FE'u, lo: 0xC2974EB4EE658829'u), - uint64x2(hi: 0xCE947A3DA6A9273E'u, lo: 0x733D226229FEEA33'u), - uint64x2(hi: 0x811CCC668829B887'u, lo: 0x0806357D5A3F5260'u), - uint64x2(hi: 0xA163FF802A3426A8'u, lo: 0xCA07C2DCB0CF26F8'u), - uint64x2(hi: 0xC9BCFF6034C13052'u, lo: 0xFC89B393DD02F0B6'u), - uint64x2(hi: 0xFC2C3F3841F17C67'u, lo: 0xBBAC2078D443ACE3'u), - uint64x2(hi: 0x9D9BA7832936EDC0'u, lo: 0xD54B944B84AA4C0E'u), - uint64x2(hi: 0xC5029163F384A931'u, lo: 0x0A9E795E65D4DF12'u), - uint64x2(hi: 0xF64335BCF065D37D'u, lo: 0x4D4617B5FF4A16D6'u), - uint64x2(hi: 0x99EA0196163FA42E'u, lo: 0x504BCED1BF8E4E46'u), - uint64x2(hi: 0xC06481FB9BCF8D39'u, lo: 0xE45EC2862F71E1D7'u), - uint64x2(hi: 0xF07DA27A82C37088'u, lo: 0x5D767327BB4E5A4D'u), - uint64x2(hi: 0x964E858C91BA2655'u, lo: 0x3A6A07F8D510F870'u), - uint64x2(hi: 0xBBE226EFB628AFEA'u, lo: 0x890489F70A55368C'u), - uint64x2(hi: 0xEADAB0ABA3B2DBE5'u, lo: 0x2B45AC74CCEA842F'u), - uint64x2(hi: 0x92C8AE6B464FC96F'u, lo: 0x3B0B8BC90012929E'u), - uint64x2(hi: 0xB77ADA0617E3BBCB'u, lo: 0x09CE6EBB40173745'u), - uint64x2(hi: 0xE55990879DDCAABD'u, lo: 0xCC420A6A101D0516'u), - uint64x2(hi: 0x8F57FA54C2A9EAB6'u, lo: 0x9FA946824A12232E'u), - uint64x2(hi: 0xB32DF8E9F3546564'u, lo: 0x47939822DC96ABFA'u), - uint64x2(hi: 0xDFF9772470297EBD'u, lo: 0x59787E2B93BC56F8'u), - uint64x2(hi: 0x8BFBEA76C619EF36'u, lo: 0x57EB4EDB3C55B65B'u), - uint64x2(hi: 0xAEFAE51477A06B03'u, lo: 0xEDE622920B6B23F2'u), - uint64x2(hi: 0xDAB99E59958885C4'u, lo: 0xE95FAB368E45ECEE'u), - uint64x2(hi: 0x88B402F7FD75539B'u, lo: 0x11DBCB0218EBB415'u), - uint64x2(hi: 0xAAE103B5FCD2A881'u, lo: 0xD652BDC29F26A11A'u), - uint64x2(hi: 0xD59944A37C0752A2'u, lo: 0x4BE76D3346F04960'u), - uint64x2(hi: 0x857FCAE62D8493A5'u, lo: 0x6F70A4400C562DDC'u), - uint64x2(hi: 0xA6DFBD9FB8E5B88E'u, lo: 0xCB4CCD500F6BB953'u), - uint64x2(hi: 0xD097AD07A71F26B2'u, lo: 0x7E2000A41346A7A8'u), - uint64x2(hi: 0x825ECC24C873782F'u, lo: 0x8ED400668C0C28C9'u), - uint64x2(hi: 0xA2F67F2DFA90563B'u, lo: 0x728900802F0F32FB'u), - uint64x2(hi: 0xCBB41EF979346BCA'u, lo: 0x4F2B40A03AD2FFBA'u), - uint64x2(hi: 0xFEA126B7D78186BC'u, lo: 0xE2F610C84987BFA9'u), - uint64x2(hi: 0x9F24B832E6B0F436'u, lo: 0x0DD9CA7D2DF4D7CA'u), - uint64x2(hi: 0xC6EDE63FA05D3143'u, lo: 0x91503D1C79720DBC'u), - uint64x2(hi: 0xF8A95FCF88747D94'u, lo: 0x75A44C6397CE912B'u), - uint64x2(hi: 0x9B69DBE1B548CE7C'u, lo: 0xC986AFBE3EE11ABB'u), - uint64x2(hi: 0xC24452DA229B021B'u, lo: 0xFBE85BADCE996169'u), - uint64x2(hi: 0xF2D56790AB41C2A2'u, lo: 0xFAE27299423FB9C4'u), - uint64x2(hi: 0x97C560BA6B0919A5'u, lo: 0xDCCD879FC967D41B'u), - uint64x2(hi: 0xBDB6B8E905CB600F'u, lo: 0x5400E987BBC1C921'u), - uint64x2(hi: 0xED246723473E3813'u, lo: 0x290123E9AAB23B69'u), - uint64x2(hi: 0x9436C0760C86E30B'u, lo: 0xF9A0B6720AAF6522'u), - uint64x2(hi: 0xB94470938FA89BCE'u, lo: 0xF808E40E8D5B3E6A'u), - uint64x2(hi: 0xE7958CB87392C2C2'u, lo: 0xB60B1D1230B20E05'u), - uint64x2(hi: 0x90BD77F3483BB9B9'u, lo: 0xB1C6F22B5E6F48C3'u), - uint64x2(hi: 0xB4ECD5F01A4AA828'u, lo: 0x1E38AEB6360B1AF4'u), - uint64x2(hi: 0xE2280B6C20DD5232'u, lo: 0x25C6DA63C38DE1B1'u), - uint64x2(hi: 0x8D590723948A535F'u, lo: 0x579C487E5A38AD0F'u), - uint64x2(hi: 0xB0AF48EC79ACE837'u, lo: 0x2D835A9DF0C6D852'u), - uint64x2(hi: 0xDCDB1B2798182244'u, lo: 0xF8E431456CF88E66'u), - uint64x2(hi: 0x8A08F0F8BF0F156B'u, lo: 0x1B8E9ECB641B5900'u), - uint64x2(hi: 0xAC8B2D36EED2DAC5'u, lo: 0xE272467E3D222F40'u), - uint64x2(hi: 0xD7ADF884AA879177'u, lo: 0x5B0ED81DCC6ABB10'u), - uint64x2(hi: 0x86CCBB52EA94BAEA'u, lo: 0x98E947129FC2B4EA'u), - uint64x2(hi: 0xA87FEA27A539E9A5'u, lo: 0x3F2398D747B36225'u), - uint64x2(hi: 0xD29FE4B18E88640E'u, lo: 0x8EEC7F0D19A03AAE'u), - uint64x2(hi: 0x83A3EEEEF9153E89'u, lo: 0x1953CF68300424AD'u), - uint64x2(hi: 0xA48CEAAAB75A8E2B'u, lo: 0x5FA8C3423C052DD8'u), - uint64x2(hi: 0xCDB02555653131B6'u, lo: 0x3792F412CB06794E'u), - uint64x2(hi: 0x808E17555F3EBF11'u, lo: 0xE2BBD88BBEE40BD1'u), - uint64x2(hi: 0xA0B19D2AB70E6ED6'u, lo: 0x5B6ACEAEAE9D0EC5'u), - uint64x2(hi: 0xC8DE047564D20A8B'u, lo: 0xF245825A5A445276'u), - uint64x2(hi: 0xFB158592BE068D2E'u, lo: 0xEED6E2F0F0D56713'u), - uint64x2(hi: 0x9CED737BB6C4183D'u, lo: 0x55464DD69685606C'u), - uint64x2(hi: 0xC428D05AA4751E4C'u, lo: 0xAA97E14C3C26B887'u), - uint64x2(hi: 0xF53304714D9265DF'u, lo: 0xD53DD99F4B3066A9'u), - uint64x2(hi: 0x993FE2C6D07B7FAB'u, lo: 0xE546A8038EFE402A'u), - uint64x2(hi: 0xBF8FDB78849A5F96'u, lo: 0xDE98520472BDD034'u), - uint64x2(hi: 0xEF73D256A5C0F77C'u, lo: 0x963E66858F6D4441'u), - uint64x2(hi: 0x95A8637627989AAD'u, lo: 0xDDE7001379A44AA9'u), - uint64x2(hi: 0xBB127C53B17EC159'u, lo: 0x5560C018580D5D53'u), - uint64x2(hi: 0xE9D71B689DDE71AF'u, lo: 0xAAB8F01E6E10B4A7'u), - uint64x2(hi: 0x9226712162AB070D'u, lo: 0xCAB3961304CA70E9'u), - uint64x2(hi: 0xB6B00D69BB55C8D1'u, lo: 0x3D607B97C5FD0D23'u), - uint64x2(hi: 0xE45C10C42A2B3B05'u, lo: 0x8CB89A7DB77C506B'u), - uint64x2(hi: 0x8EB98A7A9A5B04E3'u, lo: 0x77F3608E92ADB243'u), - uint64x2(hi: 0xB267ED1940F1C61C'u, lo: 0x55F038B237591ED4'u), - uint64x2(hi: 0xDF01E85F912E37A3'u, lo: 0x6B6C46DEC52F6689'u), - uint64x2(hi: 0x8B61313BBABCE2C6'u, lo: 0x2323AC4B3B3DA016'u), - uint64x2(hi: 0xAE397D8AA96C1B77'u, lo: 0xABEC975E0A0D081B'u), - uint64x2(hi: 0xD9C7DCED53C72255'u, lo: 0x96E7BD358C904A22'u), - uint64x2(hi: 0x881CEA14545C7575'u, lo: 0x7E50D64177DA2E55'u), - uint64x2(hi: 0xAA242499697392D2'u, lo: 0xDDE50BD1D5D0B9EA'u), - uint64x2(hi: 0xD4AD2DBFC3D07787'u, lo: 0x955E4EC64B44E865'u), - uint64x2(hi: 0x84EC3C97DA624AB4'u, lo: 0xBD5AF13BEF0B113F'u), - uint64x2(hi: 0xA6274BBDD0FADD61'u, lo: 0xECB1AD8AEACDD58F'u), - uint64x2(hi: 0xCFB11EAD453994BA'u, lo: 0x67DE18EDA5814AF3'u), - uint64x2(hi: 0x81CEB32C4B43FCF4'u, lo: 0x80EACF948770CED8'u), - uint64x2(hi: 0xA2425FF75E14FC31'u, lo: 0xA1258379A94D028E'u), - uint64x2(hi: 0xCAD2F7F5359A3B3E'u, lo: 0x096EE45813A04331'u), - uint64x2(hi: 0xFD87B5F28300CA0D'u, lo: 0x8BCA9D6E188853FD'u), - uint64x2(hi: 0x9E74D1B791E07E48'u, lo: 0x775EA264CF55347E'u), - uint64x2(hi: 0xC612062576589DDA'u, lo: 0x95364AFE032A819E'u), - uint64x2(hi: 0xF79687AED3EEC551'u, lo: 0x3A83DDBD83F52205'u), - uint64x2(hi: 0x9ABE14CD44753B52'u, lo: 0xC4926A9672793543'u), - uint64x2(hi: 0xC16D9A0095928A27'u, lo: 0x75B7053C0F178294'u), - uint64x2(hi: 0xF1C90080BAF72CB1'u, lo: 0x5324C68B12DD6339'u), - uint64x2(hi: 0x971DA05074DA7BEE'u, lo: 0xD3F6FC16EBCA5E04'u), - uint64x2(hi: 0xBCE5086492111AEA'u, lo: 0x88F4BB1CA6BCF585'u), - uint64x2(hi: 0xEC1E4A7DB69561A5'u, lo: 0x2B31E9E3D06C32E6'u), - uint64x2(hi: 0x9392EE8E921D5D07'u, lo: 0x3AFF322E62439FD0'u), - uint64x2(hi: 0xB877AA3236A4B449'u, lo: 0x09BEFEB9FAD487C3'u), - uint64x2(hi: 0xE69594BEC44DE15B'u, lo: 0x4C2EBE687989A9B4'u), - uint64x2(hi: 0x901D7CF73AB0ACD9'u, lo: 0x0F9D37014BF60A11'u), - uint64x2(hi: 0xB424DC35095CD80F'u, lo: 0x538484C19EF38C95'u), - uint64x2(hi: 0xE12E13424BB40E13'u, lo: 0x2865A5F206B06FBA'u), - uint64x2(hi: 0x8CBCCC096F5088CB'u, lo: 0xF93F87B7442E45D4'u), - uint64x2(hi: 0xAFEBFF0BCB24AAFE'u, lo: 0xF78F69A51539D749'u), - uint64x2(hi: 0xDBE6FECEBDEDD5BE'u, lo: 0xB573440E5A884D1C'u), - uint64x2(hi: 0x89705F4136B4A597'u, lo: 0x31680A88F8953031'u), - uint64x2(hi: 0xABCC77118461CEFC'u, lo: 0xFDC20D2B36BA7C3E'u), - uint64x2(hi: 0xD6BF94D5E57A42BC'u, lo: 0x3D32907604691B4D'u), - uint64x2(hi: 0x8637BD05AF6C69B5'u, lo: 0xA63F9A49C2C1B110'u), - uint64x2(hi: 0xA7C5AC471B478423'u, lo: 0x0FCF80DC33721D54'u), - uint64x2(hi: 0xD1B71758E219652B'u, lo: 0xD3C36113404EA4A9'u), - uint64x2(hi: 0x83126E978D4FDF3B'u, lo: 0x645A1CAC083126EA'u), - uint64x2(hi: 0xA3D70A3D70A3D70A'u, lo: 0x3D70A3D70A3D70A4'u), - uint64x2(hi: 0xCCCCCCCCCCCCCCCC'u, lo: 0xCCCCCCCCCCCCCCCD'u), - uint64x2(hi: 0x8000000000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xA000000000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xC800000000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xFA00000000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0x9C40000000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xC350000000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xF424000000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0x9896800000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xBEBC200000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xEE6B280000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0x9502F90000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xBA43B74000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xE8D4A51000000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0x9184E72A00000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xB5E620F480000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xE35FA931A0000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0x8E1BC9BF04000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xB1A2BC2EC5000000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xDE0B6B3A76400000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0x8AC7230489E80000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xAD78EBC5AC620000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xD8D726B7177A8000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0x878678326EAC9000'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xA968163F0A57B400'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xD3C21BCECCEDA100'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0x84595161401484A0'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xA56FA5B99019A5C8'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0xCECB8F27F4200F3A'u, lo: 0x0000000000000000'u), - uint64x2(hi: 0x813F3978F8940984'u, lo: 0x4000000000000000'u), - uint64x2(hi: 0xA18F07D736B90BE5'u, lo: 0x5000000000000000'u), - uint64x2(hi: 0xC9F2C9CD04674EDE'u, lo: 0xA400000000000000'u), - uint64x2(hi: 0xFC6F7C4045812296'u, lo: 0x4D00000000000000'u), - uint64x2(hi: 0x9DC5ADA82B70B59D'u, lo: 0xF020000000000000'u), - uint64x2(hi: 0xC5371912364CE305'u, lo: 0x6C28000000000000'u), - uint64x2(hi: 0xF684DF56C3E01BC6'u, lo: 0xC732000000000000'u), - uint64x2(hi: 0x9A130B963A6C115C'u, lo: 0x3C7F400000000000'u), - uint64x2(hi: 0xC097CE7BC90715B3'u, lo: 0x4B9F100000000000'u), - uint64x2(hi: 0xF0BDC21ABB48DB20'u, lo: 0x1E86D40000000000'u), - uint64x2(hi: 0x96769950B50D88F4'u, lo: 0x1314448000000000'u), - uint64x2(hi: 0xBC143FA4E250EB31'u, lo: 0x17D955A000000000'u), - uint64x2(hi: 0xEB194F8E1AE525FD'u, lo: 0x5DCFAB0800000000'u), - uint64x2(hi: 0x92EFD1B8D0CF37BE'u, lo: 0x5AA1CAE500000000'u), - uint64x2(hi: 0xB7ABC627050305AD'u, lo: 0xF14A3D9E40000000'u), - uint64x2(hi: 0xE596B7B0C643C719'u, lo: 0x6D9CCD05D0000000'u), - uint64x2(hi: 0x8F7E32CE7BEA5C6F'u, lo: 0xE4820023A2000000'u), - uint64x2(hi: 0xB35DBF821AE4F38B'u, lo: 0xDDA2802C8A800000'u), - uint64x2(hi: 0xE0352F62A19E306E'u, lo: 0xD50B2037AD200000'u), - uint64x2(hi: 0x8C213D9DA502DE45'u, lo: 0x4526F422CC340000'u), - uint64x2(hi: 0xAF298D050E4395D6'u, lo: 0x9670B12B7F410000'u), - uint64x2(hi: 0xDAF3F04651D47B4C'u, lo: 0x3C0CDD765F114000'u), - uint64x2(hi: 0x88D8762BF324CD0F'u, lo: 0xA5880A69FB6AC800'u), - uint64x2(hi: 0xAB0E93B6EFEE0053'u, lo: 0x8EEA0D047A457A00'u), - uint64x2(hi: 0xD5D238A4ABE98068'u, lo: 0x72A4904598D6D880'u), - uint64x2(hi: 0x85A36366EB71F041'u, lo: 0x47A6DA2B7F864750'u), - uint64x2(hi: 0xA70C3C40A64E6C51'u, lo: 0x999090B65F67D924'u), - uint64x2(hi: 0xD0CF4B50CFE20765'u, lo: 0xFFF4B4E3F741CF6D'u), - uint64x2(hi: 0x82818F1281ED449F'u, lo: 0xBFF8F10E7A8921A4'u), - uint64x2(hi: 0xA321F2D7226895C7'u, lo: 0xAFF72D52192B6A0D'u), - uint64x2(hi: 0xCBEA6F8CEB02BB39'u, lo: 0x9BF4F8A69F764490'u), - uint64x2(hi: 0xFEE50B7025C36A08'u, lo: 0x02F236D04753D5B4'u), - uint64x2(hi: 0x9F4F2726179A2245'u, lo: 0x01D762422C946590'u), - uint64x2(hi: 0xC722F0EF9D80AAD6'u, lo: 0x424D3AD2B7B97EF5'u), - uint64x2(hi: 0xF8EBAD2B84E0D58B'u, lo: 0xD2E0898765A7DEB2'u), - uint64x2(hi: 0x9B934C3B330C8577'u, lo: 0x63CC55F49F88EB2F'u), - uint64x2(hi: 0xC2781F49FFCFA6D5'u, lo: 0x3CBF6B71C76B25FB'u), - uint64x2(hi: 0xF316271C7FC3908A'u, lo: 0x8BEF464E3945EF7A'u), - uint64x2(hi: 0x97EDD871CFDA3A56'u, lo: 0x97758BF0E3CBB5AC'u), - uint64x2(hi: 0xBDE94E8E43D0C8EC'u, lo: 0x3D52EEED1CBEA317'u), - uint64x2(hi: 0xED63A231D4C4FB27'u, lo: 0x4CA7AAA863EE4BDD'u), - uint64x2(hi: 0x945E455F24FB1CF8'u, lo: 0x8FE8CAA93E74EF6A'u), - uint64x2(hi: 0xB975D6B6EE39E436'u, lo: 0xB3E2FD538E122B44'u), - uint64x2(hi: 0xE7D34C64A9C85D44'u, lo: 0x60DBBCA87196B616'u), - uint64x2(hi: 0x90E40FBEEA1D3A4A'u, lo: 0xBC8955E946FE31CD'u), - uint64x2(hi: 0xB51D13AEA4A488DD'u, lo: 0x6BABAB6398BDBE41'u), - uint64x2(hi: 0xE264589A4DCDAB14'u, lo: 0xC696963C7EED2DD1'u), - uint64x2(hi: 0x8D7EB76070A08AEC'u, lo: 0xFC1E1DE5CF543CA2'u), - uint64x2(hi: 0xB0DE65388CC8ADA8'u, lo: 0x3B25A55F43294BCB'u), - uint64x2(hi: 0xDD15FE86AFFAD912'u, lo: 0x49EF0EB713F39EBE'u), - uint64x2(hi: 0x8A2DBF142DFCC7AB'u, lo: 0x6E3569326C784337'u), - uint64x2(hi: 0xACB92ED9397BF996'u, lo: 0x49C2C37F07965404'u), - uint64x2(hi: 0xD7E77A8F87DAF7FB'u, lo: 0xDC33745EC97BE906'u), - uint64x2(hi: 0x86F0AC99B4E8DAFD'u, lo: 0x69A028BB3DED71A3'u), - uint64x2(hi: 0xA8ACD7C0222311BC'u, lo: 0xC40832EA0D68CE0C'u), - uint64x2(hi: 0xD2D80DB02AABD62B'u, lo: 0xF50A3FA490C30190'u), - uint64x2(hi: 0x83C7088E1AAB65DB'u, lo: 0x792667C6DA79E0FA'u), - uint64x2(hi: 0xA4B8CAB1A1563F52'u, lo: 0x577001B891185938'u), - uint64x2(hi: 0xCDE6FD5E09ABCF26'u, lo: 0xED4C0226B55E6F86'u), - uint64x2(hi: 0x80B05E5AC60B6178'u, lo: 0x544F8158315B05B4'u), - uint64x2(hi: 0xA0DC75F1778E39D6'u, lo: 0x696361AE3DB1C721'u), - uint64x2(hi: 0xC913936DD571C84C'u, lo: 0x03BC3A19CD1E38E9'u), - uint64x2(hi: 0xFB5878494ACE3A5F'u, lo: 0x04AB48A04065C723'u), - uint64x2(hi: 0x9D174B2DCEC0E47B'u, lo: 0x62EB0D64283F9C76'u), - uint64x2(hi: 0xC45D1DF942711D9A'u, lo: 0x3BA5D0BD324F8394'u), - uint64x2(hi: 0xF5746577930D6500'u, lo: 0xCA8F44EC7EE36479'u), - uint64x2(hi: 0x9968BF6ABBE85F20'u, lo: 0x7E998B13CF4E1ECB'u), - uint64x2(hi: 0xBFC2EF456AE276E8'u, lo: 0x9E3FEDD8C321A67E'u), - uint64x2(hi: 0xEFB3AB16C59B14A2'u, lo: 0xC5CFE94EF3EA101E'u), - uint64x2(hi: 0x95D04AEE3B80ECE5'u, lo: 0xBBA1F1D158724A12'u), - uint64x2(hi: 0xBB445DA9CA61281F'u, lo: 0x2A8A6E45AE8EDC97'u), - uint64x2(hi: 0xEA1575143CF97226'u, lo: 0xF52D09D71A3293BD'u), - uint64x2(hi: 0x924D692CA61BE758'u, lo: 0x593C2626705F9C56'u), - uint64x2(hi: 0xB6E0C377CFA2E12E'u, lo: 0x6F8B2FB00C77836C'u), - uint64x2(hi: 0xE498F455C38B997A'u, lo: 0x0B6DFB9C0F956447'u), - uint64x2(hi: 0x8EDF98B59A373FEC'u, lo: 0x4724BD4189BD5EAC'u), - uint64x2(hi: 0xB2977EE300C50FE7'u, lo: 0x58EDEC91EC2CB657'u), - uint64x2(hi: 0xDF3D5E9BC0F653E1'u, lo: 0x2F2967B66737E3ED'u), - uint64x2(hi: 0x8B865B215899F46C'u, lo: 0xBD79E0D20082EE74'u), - uint64x2(hi: 0xAE67F1E9AEC07187'u, lo: 0xECD8590680A3AA11'u), - uint64x2(hi: 0xDA01EE641A708DE9'u, lo: 0xE80E6F4820CC9495'u), - uint64x2(hi: 0x884134FE908658B2'u, lo: 0x3109058D147FDCDD'u), - uint64x2(hi: 0xAA51823E34A7EEDE'u, lo: 0xBD4B46F0599FD415'u), - uint64x2(hi: 0xD4E5E2CDC1D1EA96'u, lo: 0x6C9E18AC7007C91A'u), - uint64x2(hi: 0x850FADC09923329E'u, lo: 0x03E2CF6BC604DDB0'u), - uint64x2(hi: 0xA6539930BF6BFF45'u, lo: 0x84DB8346B786151C'u), - uint64x2(hi: 0xCFE87F7CEF46FF16'u, lo: 0xE612641865679A63'u), - uint64x2(hi: 0x81F14FAE158C5F6E'u, lo: 0x4FCB7E8F3F60C07E'u), - uint64x2(hi: 0xA26DA3999AEF7749'u, lo: 0xE3BE5E330F38F09D'u), - uint64x2(hi: 0xCB090C8001AB551C'u, lo: 0x5CADF5BFD3072CC5'u), - uint64x2(hi: 0xFDCB4FA002162A63'u, lo: 0x73D9732FC7C8F7F6'u), - uint64x2(hi: 0x9E9F11C4014DDA7E'u, lo: 0x2867E7FDDCDD9AFA'u), - uint64x2(hi: 0xC646D63501A1511D'u, lo: 0xB281E1FD541501B8'u), - uint64x2(hi: 0xF7D88BC24209A565'u, lo: 0x1F225A7CA91A4226'u), - uint64x2(hi: 0x9AE757596946075F'u, lo: 0x3375788DE9B06958'u), - uint64x2(hi: 0xC1A12D2FC3978937'u, lo: 0x0052D6B1641C83AE'u), - uint64x2(hi: 0xF209787BB47D6B84'u, lo: 0xC0678C5DBD23A49A'u), - uint64x2(hi: 0x9745EB4D50CE6332'u, lo: 0xF840B7BA963646E0'u), - uint64x2(hi: 0xBD176620A501FBFF'u, lo: 0xB650E5A93BC3D898'u), - uint64x2(hi: 0xEC5D3FA8CE427AFF'u, lo: 0xA3E51F138AB4CEBE'u), - uint64x2(hi: 0x93BA47C980E98CDF'u, lo: 0xC66F336C36B10137'u), - uint64x2(hi: 0xB8A8D9BBE123F017'u, lo: 0xB80B0047445D4184'u), - uint64x2(hi: 0xE6D3102AD96CEC1D'u, lo: 0xA60DC059157491E5'u), - uint64x2(hi: 0x9043EA1AC7E41392'u, lo: 0x87C89837AD68DB2F'u), - uint64x2(hi: 0xB454E4A179DD1877'u, lo: 0x29BABE4598C311FB'u), - uint64x2(hi: 0xE16A1DC9D8545E94'u, lo: 0xF4296DD6FEF3D67A'u), - uint64x2(hi: 0x8CE2529E2734BB1D'u, lo: 0x1899E4A65F58660C'u), - uint64x2(hi: 0xB01AE745B101E9E4'u, lo: 0x5EC05DCFF72E7F8F'u), - uint64x2(hi: 0xDC21A1171D42645D'u, lo: 0x76707543F4FA1F73'u), - uint64x2(hi: 0x899504AE72497EBA'u, lo: 0x6A06494A791C53A8'u), - uint64x2(hi: 0xABFA45DA0EDBDE69'u, lo: 0x0487DB9D17636892'u), - uint64x2(hi: 0xD6F8D7509292D603'u, lo: 0x45A9D2845D3C42B6'u), - uint64x2(hi: 0x865B86925B9BC5C2'u, lo: 0x0B8A2392BA45A9B2'u), - uint64x2(hi: 0xA7F26836F282B732'u, lo: 0x8E6CAC7768D7141E'u), - uint64x2(hi: 0xD1EF0244AF2364FF'u, lo: 0x3207D795430CD926'u), - uint64x2(hi: 0x8335616AED761F1F'u, lo: 0x7F44E6BD49E807B8'u), - uint64x2(hi: 0xA402B9C5A8D3A6E7'u, lo: 0x5F16206C9C6209A6'u), - uint64x2(hi: 0xCD036837130890A1'u, lo: 0x36DBA887C37A8C0F'u), - uint64x2(hi: 0x802221226BE55A64'u, lo: 0xC2494954DA2C9789'u), - uint64x2(hi: 0xA02AA96B06DEB0FD'u, lo: 0xF2DB9BAA10B7BD6C'u), - uint64x2(hi: 0xC83553C5C8965D3D'u, lo: 0x6F92829494E5ACC7'u), - uint64x2(hi: 0xFA42A8B73ABBF48C'u, lo: 0xCB772339BA1F17F9'u), - uint64x2(hi: 0x9C69A97284B578D7'u, lo: 0xFF2A760414536EFB'u), - uint64x2(hi: 0xC38413CF25E2D70D'u, lo: 0xFEF5138519684ABA'u), - uint64x2(hi: 0xF46518C2EF5B8CD1'u, lo: 0x7EB258665FC25D69'u), - uint64x2(hi: 0x98BF2F79D5993802'u, lo: 0xEF2F773FFBD97A61'u), - uint64x2(hi: 0xBEEEFB584AFF8603'u, lo: 0xAAFB550FFACFD8FA'u), - uint64x2(hi: 0xEEAABA2E5DBF6784'u, lo: 0x95BA2A53F983CF38'u), - uint64x2(hi: 0x952AB45CFA97A0B2'u, lo: 0xDD945A747BF26183'u), - uint64x2(hi: 0xBA756174393D88DF'u, lo: 0x94F971119AEEF9E4'u), - uint64x2(hi: 0xE912B9D1478CEB17'u, lo: 0x7A37CD5601AAB85D'u), - uint64x2(hi: 0x91ABB422CCB812EE'u, lo: 0xAC62E055C10AB33A'u), - uint64x2(hi: 0xB616A12B7FE617AA'u, lo: 0x577B986B314D6009'u), - uint64x2(hi: 0xE39C49765FDF9D94'u, lo: 0xED5A7E85FDA0B80B'u), - uint64x2(hi: 0x8E41ADE9FBEBC27D'u, lo: 0x14588F13BE847307'u), - uint64x2(hi: 0xB1D219647AE6B31C'u, lo: 0x596EB2D8AE258FC8'u), - uint64x2(hi: 0xDE469FBD99A05FE3'u, lo: 0x6FCA5F8ED9AEF3BB'u), - uint64x2(hi: 0x8AEC23D680043BEE'u, lo: 0x25DE7BB9480D5854'u), - uint64x2(hi: 0xADA72CCC20054AE9'u, lo: 0xAF561AA79A10AE6A'u), - uint64x2(hi: 0xD910F7FF28069DA4'u, lo: 0x1B2BA1518094DA04'u), - uint64x2(hi: 0x87AA9AFF79042286'u, lo: 0x90FB44D2F05D0842'u), - uint64x2(hi: 0xA99541BF57452B28'u, lo: 0x353A1607AC744A53'u), - uint64x2(hi: 0xD3FA922F2D1675F2'u, lo: 0x42889B8997915CE8'u), - uint64x2(hi: 0x847C9B5D7C2E09B7'u, lo: 0x69956135FEBADA11'u), - uint64x2(hi: 0xA59BC234DB398C25'u, lo: 0x43FAB9837E699095'u), - uint64x2(hi: 0xCF02B2C21207EF2E'u, lo: 0x94F967E45E03F4BB'u), - uint64x2(hi: 0x8161AFB94B44F57D'u, lo: 0x1D1BE0EEBAC278F5'u), - uint64x2(hi: 0xA1BA1BA79E1632DC'u, lo: 0x6462D92A69731732'u), - uint64x2(hi: 0xCA28A291859BBF93'u, lo: 0x7D7B8F7503CFDCFE'u), - uint64x2(hi: 0xFCB2CB35E702AF78'u, lo: 0x5CDA735244C3D43E'u), - uint64x2(hi: 0x9DEFBF01B061ADAB'u, lo: 0x3A0888136AFA64A7'u), - uint64x2(hi: 0xC56BAEC21C7A1916'u, lo: 0x088AAA1845B8FDD0'u), - uint64x2(hi: 0xF6C69A72A3989F5B'u, lo: 0x8AAD549E57273D45'u), - uint64x2(hi: 0x9A3C2087A63F6399'u, lo: 0x36AC54E2F678864B'u), - uint64x2(hi: 0xC0CB28A98FCF3C7F'u, lo: 0x84576A1BB416A7DD'u), - uint64x2(hi: 0xF0FDF2D3F3C30B9F'u, lo: 0x656D44A2A11C51D5'u), - uint64x2(hi: 0x969EB7C47859E743'u, lo: 0x9F644AE5A4B1B325'u), - uint64x2(hi: 0xBC4665B596706114'u, lo: 0x873D5D9F0DDE1FEE'u), - uint64x2(hi: 0xEB57FF22FC0C7959'u, lo: 0xA90CB506D155A7EA'u), - uint64x2(hi: 0x9316FF75DD87CBD8'u, lo: 0x09A7F12442D588F2'u), - uint64x2(hi: 0xB7DCBF5354E9BECE'u, lo: 0x0C11ED6D538AEB2F'u), - uint64x2(hi: 0xE5D3EF282A242E81'u, lo: 0x8F1668C8A86DA5FA'u), - uint64x2(hi: 0x8FA475791A569D10'u, lo: 0xF96E017D694487BC'u), - uint64x2(hi: 0xB38D92D760EC4455'u, lo: 0x37C981DCC395A9AC'u), - uint64x2(hi: 0xE070F78D3927556A'u, lo: 0x85BBE253F47B1417'u), - uint64x2(hi: 0x8C469AB843B89562'u, lo: 0x93956D7478CCEC8E'u), - uint64x2(hi: 0xAF58416654A6BABB'u, lo: 0x387AC8D1970027B2'u), - uint64x2(hi: 0xDB2E51BFE9D0696A'u, lo: 0x06997B05FCC0319E'u), - uint64x2(hi: 0x88FCF317F22241E2'u, lo: 0x441FECE3BDF81F03'u), - uint64x2(hi: 0xAB3C2FDDEEAAD25A'u, lo: 0xD527E81CAD7626C3'u), - uint64x2(hi: 0xD60B3BD56A5586F1'u, lo: 0x8A71E223D8D3B074'u), - uint64x2(hi: 0x85C7056562757456'u, lo: 0xF6872D5667844E49'u), - uint64x2(hi: 0xA738C6BEBB12D16C'u, lo: 0xB428F8AC016561DB'u), - uint64x2(hi: 0xD106F86E69D785C7'u, lo: 0xE13336D701BEBA52'u), - uint64x2(hi: 0x82A45B450226B39C'u, lo: 0xECC0024661173473'u), - uint64x2(hi: 0xA34D721642B06084'u, lo: 0x27F002D7F95D0190'u), - uint64x2(hi: 0xCC20CE9BD35C78A5'u, lo: 0x31EC038DF7B441F4'u), - uint64x2(hi: 0xFF290242C83396CE'u, lo: 0x7E67047175A15271'u), - uint64x2(hi: 0x9F79A169BD203E41'u, lo: 0x0F0062C6E984D386'u), - uint64x2(hi: 0xC75809C42C684DD1'u, lo: 0x52C07B78A3E60868'u), - uint64x2(hi: 0xF92E0C3537826145'u, lo: 0xA7709A56CCDF8A82'u), - uint64x2(hi: 0x9BBCC7A142B17CCB'u, lo: 0x88A66076400BB691'u), - uint64x2(hi: 0xC2ABF989935DDBFE'u, lo: 0x6ACFF893D00EA435'u), - uint64x2(hi: 0xF356F7EBF83552FE'u, lo: 0x0583F6B8C4124D43'u), - uint64x2(hi: 0x98165AF37B2153DE'u, lo: 0xC3727A337A8B704A'u), - uint64x2(hi: 0xBE1BF1B059E9A8D6'u, lo: 0x744F18C0592E4C5C'u), - uint64x2(hi: 0xEDA2EE1C7064130C'u, lo: 0x1162DEF06F79DF73'u), - uint64x2(hi: 0x9485D4D1C63E8BE7'u, lo: 0x8ADDCB5645AC2BA8'u), - uint64x2(hi: 0xB9A74A0637CE2EE1'u, lo: 0x6D953E2BD7173692'u), - uint64x2(hi: 0xE8111C87C5C1BA99'u, lo: 0xC8FA8DB6CCDD0437'u), - uint64x2(hi: 0x910AB1D4DB9914A0'u, lo: 0x1D9C9892400A22A2'u), - uint64x2(hi: 0xB54D5E4A127F59C8'u, lo: 0x2503BEB6D00CAB4B'u), - uint64x2(hi: 0xE2A0B5DC971F303A'u, lo: 0x2E44AE64840FD61D'u), - uint64x2(hi: 0x8DA471A9DE737E24'u, lo: 0x5CEAECFED289E5D2'u), - uint64x2(hi: 0xB10D8E1456105DAD'u, lo: 0x7425A83E872C5F47'u), - uint64x2(hi: 0xDD50F1996B947518'u, lo: 0xD12F124E28F77719'u), - uint64x2(hi: 0x8A5296FFE33CC92F'u, lo: 0x82BD6B70D99AAA6F'u), - uint64x2(hi: 0xACE73CBFDC0BFB7B'u, lo: 0x636CC64D1001550B'u), - uint64x2(hi: 0xD8210BEFD30EFA5A'u, lo: 0x3C47F7E05401AA4E'u), - uint64x2(hi: 0x8714A775E3E95C78'u, lo: 0x65ACFAEC34810A71'u), - uint64x2(hi: 0xA8D9D1535CE3B396'u, lo: 0x7F1839A741A14D0D'u), - uint64x2(hi: 0xD31045A8341CA07C'u, lo: 0x1EDE48111209A050'u), - uint64x2(hi: 0x83EA2B892091E44D'u, lo: 0x934AED0AAB460432'u), - uint64x2(hi: 0xA4E4B66B68B65D60'u, lo: 0xF81DA84D5617853F'u), - uint64x2(hi: 0xCE1DE40642E3F4B9'u, lo: 0x36251260AB9D668E'u), - uint64x2(hi: 0x80D2AE83E9CE78F3'u, lo: 0xC1D72B7C6B426019'u), - uint64x2(hi: 0xA1075A24E4421730'u, lo: 0xB24CF65B8612F81F'u), - uint64x2(hi: 0xC94930AE1D529CFC'u, lo: 0xDEE033F26797B627'u), - uint64x2(hi: 0xFB9B7CD9A4A7443C'u, lo: 0x169840EF017DA3B1'u), - uint64x2(hi: 0x9D412E0806E88AA5'u, lo: 0x8E1F289560EE864E'u), - uint64x2(hi: 0xC491798A08A2AD4E'u, lo: 0xF1A6F2BAB92A27E2'u), - uint64x2(hi: 0xF5B5D7EC8ACB58A2'u, lo: 0xAE10AF696774B1DB'u), - uint64x2(hi: 0x9991A6F3D6BF1765'u, lo: 0xACCA6DA1E0A8EF29'u), - uint64x2(hi: 0xBFF610B0CC6EDD3F'u, lo: 0x17FD090A58D32AF3'u), - uint64x2(hi: 0xEFF394DCFF8A948E'u, lo: 0xDDFC4B4CEF07F5B0'u), - uint64x2(hi: 0x95F83D0A1FB69CD9'u, lo: 0x4ABDAF101564F98E'u), - uint64x2(hi: 0xBB764C4CA7A4440F'u, lo: 0x9D6D1AD41ABE37F1'u), - uint64x2(hi: 0xEA53DF5FD18D5513'u, lo: 0x84C86189216DC5ED'u), - uint64x2(hi: 0x92746B9BE2F8552C'u, lo: 0x32FD3CF5B4E49BB4'u), - uint64x2(hi: 0xB7118682DBB66A77'u, lo: 0x3FBC8C33221DC2A1'u), - uint64x2(hi: 0xE4D5E82392A40515'u, lo: 0x0FABAF3FEAA5334A'u), - uint64x2(hi: 0x8F05B1163BA6832D'u, lo: 0x29CB4D87F2A7400E'u), - uint64x2(hi: 0xB2C71D5BCA9023F8'u, lo: 0x743E20E9EF511012'u), - uint64x2(hi: 0xDF78E4B2BD342CF6'u, lo: 0x914DA9246B255416'u), - uint64x2(hi: 0x8BAB8EEFB6409C1A'u, lo: 0x1AD089B6C2F7548E'u), - uint64x2(hi: 0xAE9672ABA3D0C320'u, lo: 0xA184AC2473B529B1'u), - uint64x2(hi: 0xDA3C0F568CC4F3E8'u, lo: 0xC9E5D72D90A2741E'u), - uint64x2(hi: 0x8865899617FB1871'u, lo: 0x7E2FA67C7A658892'u), - uint64x2(hi: 0xAA7EEBFB9DF9DE8D'u, lo: 0xDDBB901B98FEEAB7'u), - uint64x2(hi: 0xD51EA6FA85785631'u, lo: 0x552A74227F3EA565'u), - uint64x2(hi: 0x8533285C936B35DE'u, lo: 0xD53A88958F87275F'u), - uint64x2(hi: 0xA67FF273B8460356'u, lo: 0x8A892ABAF368F137'u), - uint64x2(hi: 0xD01FEF10A657842C'u, lo: 0x2D2B7569B0432D85'u), - uint64x2(hi: 0x8213F56A67F6B29B'u, lo: 0x9C3B29620E29FC73'u), - uint64x2(hi: 0xA298F2C501F45F42'u, lo: 0x8349F3BA91B47B8F'u), - uint64x2(hi: 0xCB3F2F7642717713'u, lo: 0x241C70A936219A73'u), - uint64x2(hi: 0xFE0EFB53D30DD4D7'u, lo: 0xED238CD383AA0110'u), - uint64x2(hi: 0x9EC95D1463E8A506'u, lo: 0xF4363804324A40AA'u), - uint64x2(hi: 0xC67BB4597CE2CE48'u, lo: 0xB143C6053EDCD0D5'u), - uint64x2(hi: 0xF81AA16FDC1B81DA'u, lo: 0xDD94B7868E94050A'u), - uint64x2(hi: 0x9B10A4E5E9913128'u, lo: 0xCA7CF2B4191C8326'u), - uint64x2(hi: 0xC1D4CE1F63F57D72'u, lo: 0xFD1C2F611F63A3F0'u), - uint64x2(hi: 0xF24A01A73CF2DCCF'u, lo: 0xBC633B39673C8CEC'u), - uint64x2(hi: 0x976E41088617CA01'u, lo: 0xD5BE0503E085D813'u), - uint64x2(hi: 0xBD49D14AA79DBC82'u, lo: 0x4B2D8644D8A74E18'u), - uint64x2(hi: 0xEC9C459D51852BA2'u, lo: 0xDDF8E7D60ED1219E'u), - uint64x2(hi: 0x93E1AB8252F33B45'u, lo: 0xCABB90E5C942B503'u), - uint64x2(hi: 0xB8DA1662E7B00A17'u, lo: 0x3D6A751F3B936243'u), - uint64x2(hi: 0xE7109BFBA19C0C9D'u, lo: 0x0CC512670A783AD4'u), - uint64x2(hi: 0x906A617D450187E2'u, lo: 0x27FB2B80668B24C5'u), - uint64x2(hi: 0xB484F9DC9641E9DA'u, lo: 0xB1F9F660802DEDF6'u), - uint64x2(hi: 0xE1A63853BBD26451'u, lo: 0x5E7873F8A0396973'u), - uint64x2(hi: 0x8D07E33455637EB2'u, lo: 0xDB0B487B6423E1E8'u), - uint64x2(hi: 0xB049DC016ABC5E5F'u, lo: 0x91CE1A9A3D2CDA62'u), - uint64x2(hi: 0xDC5C5301C56B75F7'u, lo: 0x7641A140CC7810FB'u), - uint64x2(hi: 0x89B9B3E11B6329BA'u, lo: 0xA9E904C87FCB0A9D'u), - uint64x2(hi: 0xAC2820D9623BF429'u, lo: 0x546345FA9FBDCD44'u), - uint64x2(hi: 0xD732290FBACAF133'u, lo: 0xA97C177947AD4095'u), - uint64x2(hi: 0x867F59A9D4BED6C0'u, lo: 0x49ED8EABCCCC485D'u), - uint64x2(hi: 0xA81F301449EE8C70'u, lo: 0x5C68F256BFFF5A74'u), - uint64x2(hi: 0xD226FC195C6A2F8C'u, lo: 0x73832EEC6FFF3111'u), - uint64x2(hi: 0x83585D8FD9C25DB7'u, lo: 0xC831FD53C5FF7EAB'u), - uint64x2(hi: 0xA42E74F3D032F525'u, lo: 0xBA3E7CA8B77F5E55'u), - uint64x2(hi: 0xCD3A1230C43FB26F'u, lo: 0x28CE1BD2E55F35EB'u), - uint64x2(hi: 0x80444B5E7AA7CF85'u, lo: 0x7980D163CF5B81B3'u), - uint64x2(hi: 0xA0555E361951C366'u, lo: 0xD7E105BCC332621F'u), - uint64x2(hi: 0xC86AB5C39FA63440'u, lo: 0x8DD9472BF3FEFAA7'u), - uint64x2(hi: 0xFA856334878FC150'u, lo: 0xB14F98F6F0FEB951'u), - uint64x2(hi: 0x9C935E00D4B9D8D2'u, lo: 0x6ED1BF9A569F33D3'u), - uint64x2(hi: 0xC3B8358109E84F07'u, lo: 0x0A862F80EC4700C8'u), - uint64x2(hi: 0xF4A642E14C6262C8'u, lo: 0xCD27BB612758C0FA'u), - uint64x2(hi: 0x98E7E9CCCFBD7DBD'u, lo: 0x8038D51CB897789C'u), - uint64x2(hi: 0xBF21E44003ACDD2C'u, lo: 0xE0470A63E6BD56C3'u), - uint64x2(hi: 0xEEEA5D5004981478'u, lo: 0x1858CCFCE06CAC74'u), - uint64x2(hi: 0x95527A5202DF0CCB'u, lo: 0x0F37801E0C43EBC8'u), - uint64x2(hi: 0xBAA718E68396CFFD'u, lo: 0xD30560258F54E6BA'u), - uint64x2(hi: 0xE950DF20247C83FD'u, lo: 0x47C6B82EF32A2069'u), - uint64x2(hi: 0x91D28B7416CDD27E'u, lo: 0x4CDC331D57FA5441'u), - uint64x2(hi: 0xB6472E511C81471D'u, lo: 0xE0133FE4ADF8E952'u), - uint64x2(hi: 0xE3D8F9E563A198E5'u, lo: 0x58180FDDD97723A6'u), - uint64x2(hi: 0x8E679C2F5E44FF8F'u, lo: 0x570F09EAA7EA7648'u), - uint64x2(hi: 0xB201833B35D63F73'u, lo: 0x2CD2CC6551E513DA'u), - uint64x2(hi: 0xDE81E40A034BCF4F'u, lo: 0xF8077F7EA65E58D1'u), - uint64x2(hi: 0x8B112E86420F6191'u, lo: 0xFB04AFAF27FAF782'u), - uint64x2(hi: 0xADD57A27D29339F6'u, lo: 0x79C5DB9AF1F9B563'u), - uint64x2(hi: 0xD94AD8B1C7380874'u, lo: 0x18375281AE7822BC'u), - uint64x2(hi: 0x87CEC76F1C830548'u, lo: 0x8F2293910D0B15B5'u), - uint64x2(hi: 0xA9C2794AE3A3C69A'u, lo: 0xB2EB3875504DDB22'u), - uint64x2(hi: 0xD433179D9C8CB841'u, lo: 0x5FA60692A46151EB'u), - uint64x2(hi: 0x849FEEC281D7F328'u, lo: 0xDBC7C41BA6BCD333'u), - uint64x2(hi: 0xA5C7EA73224DEFF3'u, lo: 0x12B9B522906C0800'u), - uint64x2(hi: 0xCF39E50FEAE16BEF'u, lo: 0xD768226B34870A00'u), - uint64x2(hi: 0x81842F29F2CCE375'u, lo: 0xE6A1158300D46640'u), - uint64x2(hi: 0xA1E53AF46F801C53'u, lo: 0x60495AE3C1097FD0'u), - uint64x2(hi: 0xCA5E89B18B602368'u, lo: 0x385BB19CB14BDFC4'u), - uint64x2(hi: 0xFCF62C1DEE382C42'u, lo: 0x46729E03DD9ED7B5'u), - uint64x2(hi: 0x9E19DB92B4E31BA9'u, lo: 0x6C07A2C26A8346D1'u), - uint64x2(hi: 0xC5A05277621BE293'u, lo: 0xC7098B7305241885'u), - uint64x2(hi: 0xF70867153AA2DB38'u, lo: 0xB8CBEE4FC66D1EA7'u)] - dragonbox_Assert(k >= kMin) - dragonbox_Assert(k <= kMax) - return pow10[k - kMin] - -## Returns whether value is divisible by 2^e2 - -proc multipleOfPow2*(value: uint64; e2: int32): bool {.inline.} = - dragonbox_Assert(e2 >= 0) - return e2 < 64 and (value and ((uint64(1) shl e2) - 1)) == 0 - -## Returns whether value is divisible by 5^e5 - -proc multipleOfPow5*(value: uint64; e5: int32): bool {.inline.} = - type - MulCmp {.bycopy.} = object - mul: uint64 - cmp: uint64 - - const - mod5 = [MulCmp(mul: 0x0000000000000001'u, cmp: 0xFFFFFFFFFFFFFFFF'u), - MulCmp(mul: 0xCCCCCCCCCCCCCCCD'u, cmp: 0x3333333333333333'u), - MulCmp(mul: 0x8F5C28F5C28F5C29'u, cmp: 0x0A3D70A3D70A3D70'u), - MulCmp(mul: 0x1CAC083126E978D5'u, cmp: 0x020C49BA5E353F7C'u), - MulCmp(mul: 0xD288CE703AFB7E91'u, cmp: 0x0068DB8BAC710CB2'u), - MulCmp(mul: 0x5D4E8FB00BCBE61D'u, cmp: 0x0014F8B588E368F0'u), - MulCmp(mul: 0x790FB65668C26139'u, cmp: 0x000431BDE82D7B63'u), - MulCmp(mul: 0xE5032477AE8D46A5'u, cmp: 0x0000D6BF94D5E57A'u), - MulCmp(mul: 0xC767074B22E90E21'u, cmp: 0x00002AF31DC46118'u), - MulCmp(mul: 0x8E47CE423A2E9C6D'u, cmp: 0x0000089705F4136B'u), - MulCmp(mul: 0x4FA7F60D3ED61F49'u, cmp: 0x000001B7CDFD9D7B'u), - MulCmp(mul: 0x0FEE64690C913975'u, cmp: 0x00000057F5FF85E5'u), - MulCmp(mul: 0x3662E0E1CF503EB1'u, cmp: 0x000000119799812D'u), - MulCmp(mul: 0xA47A2CF9F6433FBD'u, cmp: 0x0000000384B84D09'u), - MulCmp(mul: 0x54186F653140A659'u, cmp: 0x00000000B424DC35'u), - MulCmp(mul: 0x7738164770402145'u, cmp: 0x0000000024075F3D'u), - MulCmp(mul: 0xE4A4D1417CD9A041'u, cmp: 0x000000000734ACA5'u), - MulCmp(mul: 0xC75429D9E5C5200D'u, cmp: 0x000000000170EF54'u), - MulCmp(mul: 0xC1773B91FAC10669'u, cmp: 0x000000000049C977'u), - MulCmp(mul: 0x26B172506559CE15'u, cmp: 0x00000000000EC1E4'u), - MulCmp(mul: 0xD489E3A9ADDEC2D1'u, cmp: 0x000000000002F394'u), - MulCmp(mul: 0x90E860BB892C8D5D'u, cmp: 0x000000000000971D'u), - MulCmp(mul: 0x502E79BF1B6F4F79'u, cmp: 0x0000000000001E39'u), - MulCmp(mul: 0xDCD618596BE30FE5'u, cmp: 0x000000000000060B'u), - MulCmp(mul: 0x2C2AD1AB7BFA3661'u, cmp: 0x0000000000000135'u)] - dragonbox_Assert(e5 >= 0) - dragonbox_Assert(e5 <= 24) - let m5: MulCmp = mod5[e5] - return value * m5.mul <= m5.cmp - -type - FloatingDecimal64* {.bycopy.} = object - significand*: uint64 - exponent*: int32 - - -proc toDecimal64AsymmetricInterval*(e2: int32): FloatingDecimal64 {.inline.} = - ## NB: - ## accept_lower_endpoint = true - ## accept_upper_endpoint = true - const - P: int32 = significandSize - ## Compute k and beta - let minusK: int32 = floorLog10ThreeQuartersPow2(e2) - let betaMinus1: int32 = e2 + floorLog2Pow10(-minusK) - ## Compute xi and zi - let pow10: uint64x2 = computePow10(-minusK) - let lowerEndpoint: uint64 = (pow10.hi - (pow10.hi shr (P + 1))) shr - (64 - P - betaMinus1) - let upperEndpoint: uint64 = (pow10.hi + (pow10.hi shr (P + 0))) shr - (64 - P - betaMinus1) - ## If we don't accept the left endpoint (but we do!) or - ## if the left endpoint is not an integer, increase it - let lowerEndpointIsInteger: bool = (2 <= e2 and e2 <= 3) - let xi: uint64 = lowerEndpoint + uint64(not lowerEndpointIsInteger) - let zi: uint64 = upperEndpoint - ## Try bigger divisor - var q: uint64 = zi div 10 - if q * 10 >= xi: - return FloatingDecimal64(significand: q, exponent: minusK + 1) - q = ((pow10.hi shr (64 - (P + 1) - betaMinus1)) + 1) div 2 - ## When tie occurs, choose one of them according to the rule - if e2 == -77: - dec(q, ord(q mod 2 != 0)) - ## Round to even. - else: - inc(q, ord(q < xi)) - return FloatingDecimal64(significand: q, exponent: minusK) - -proc computeDelta*(pow10: uint64x2; betaMinus1: int32): uint32 {.inline.} = - dragonbox_Assert(betaMinus1 >= 0) - dragonbox_Assert(betaMinus1 <= 63) - return cast[uint32](pow10.hi shr (64 - 1 - betaMinus1)) - -when defined(sizeof_Int128): - proc mul128*(x: uint64; y: uint64): uint64x2 {.inline.} = - ## 1 mulx - type - uint128T = uint128 - let p: uint128T = uint128T(x) * y - let hi: uint64 = cast[uint64](p shr 64) - let lo: uint64 = cast[uint64](p) - return (hi, lo) - -elif defined(vcc) and defined(cpu64): - proc umul128(x, y: uint64, z: ptr uint64): uint64 {.importc: "_umul128", header: "".} - proc mul128*(x: uint64; y: uint64): uint64x2 {.inline.} = - var hi: uint64 = 0 - var lo: uint64 = umul128(x, y, addr(hi)) - return uint64x2(hi: hi, lo: lo) - -else: - proc lo32*(x: uint64): uint32 {.inline.} = - return cast[uint32](x) - - proc hi32*(x: uint64): uint32 {.inline.} = - return cast[uint32](x shr 32) - - proc mul128*(a: uint64; b: uint64): uint64x2 {.inline.} = - let b00: uint64 = uint64(lo32(a)) * lo32(b) - let b01: uint64 = uint64(lo32(a)) * hi32(b) - let b10: uint64 = uint64(hi32(a)) * lo32(b) - let b11: uint64 = uint64(hi32(a)) * hi32(b) - let mid1: uint64 = b10 + hi32(b00) - let mid2: uint64 = b01 + lo32(mid1) - let hi: uint64 = b11 + hi32(mid1) + hi32(mid2) - let lo: uint64 = lo32(b00) or uint64(lo32(mid2)) shl 32 - return uint64x2(hi: hi, lo: lo) - -## Returns (x * y) / 2^128 - -proc mulShift*(x: uint64; y: uint64x2): uint64 {.inline.} = - ## 2 mulx - var p1: uint64x2 = mul128(x, y.hi) - var p0: uint64x2 = mul128(x, y.lo) - p1.lo += p0.hi - inc(p1.hi, ord(p1.lo < p0.hi)) - return p1.hi - -proc mulParity*(twoF: uint64; pow10: uint64x2; betaMinus1: int32): bool {.inline.} = - ## 1 mulx, 1 mul - dragonbox_Assert(betaMinus1 >= 1) - dragonbox_Assert(betaMinus1 <= 63) - let p01: uint64 = twoF * pow10.hi - let p10: uint64 = mul128(twoF, pow10.lo).hi - let mid: uint64 = p01 + p10 - return (mid and (uint64(1) shl (64 - betaMinus1))) != 0 - -proc isIntegralEndpoint*(twoF: uint64; e2: int32; minusK: int32): bool {.inline.} = - if e2 < -2: - return false - if e2 <= 9: - return true - if e2 <= 86: - return multipleOfPow5(twoF, minusK) - return false - -proc isIntegralMidpoint*(twoF: uint64; e2: int32; minusK: int32): bool {.inline.} = - if e2 < -4: - return multipleOfPow2(twoF, minusK - e2 + 1) - if e2 <= 9: - return true - if e2 <= 86: - return multipleOfPow5(twoF, minusK) - return false - -proc toDecimal64*(ieeeSignificand: uint64; ieeeExponent: uint64): FloatingDecimal64 {. - inline.} = - const - kappa: int32 = 2 - const - bigDivisor: uint32 = 1000 - ## 10^(kappa + 1) - const - smallDivisor: uint32 = 100 - ## 10^(kappa) - ## - ## Step 1: - ## integer promotion & Schubfach multiplier calculation. - ## - var m2: uint64 - var e2: int32 - if ieeeExponent != 0: - m2 = hiddenBit or ieeeSignificand - e2 = cast[int32](ieeeExponent) - exponentBias - if 0 <= -e2 and -e2 < significandSize and multipleOfPow2(m2, -e2): - ## Small integer. - return FloatingDecimal64(significand: m2 shr -e2, exponent: 0) - if ieeeSignificand == 0 and ieeeExponent > 1: - ## Shorter interval case; proceed like Schubfach. - return toDecimal64AsymmetricInterval(e2) - else: - ## Subnormal case; interval is always regular. - m2 = ieeeSignificand - e2 = 1 - exponentBias - let isEven: bool = (m2 mod 2 == 0) - let acceptLower: bool = isEven - let acceptUpper: bool = isEven - ## Compute k and beta. - let minusK: int32 = floorLog10Pow2(e2) - kappa - let betaMinus1: int32 = e2 + floorLog2Pow10(-minusK) - dragonbox_Assert(betaMinus1 >= 6) - dragonbox_Assert(betaMinus1 <= 9) - let pow10: uint64x2 = computePow10(-minusK) - ## Compute delta - ## 10^kappa <= delta < 10^(kappa + 1) - ## 100 <= delta < 1000 - let delta: uint32 = computeDelta(pow10, betaMinus1) - dragonbox_Assert(delta >= smallDivisor) - dragonbox_Assert(delta < bigDivisor) - let twoFl: uint64 = 2 * m2 - 1 - let twoFc: uint64 = 2 * m2 - let twoFr: uint64 = 2 * m2 + 1 - ## (54 bits) - ## Compute zi - ## (54 + 9 = 63 bits) - let zi: uint64 = mulShift(twoFr shl betaMinus1, pow10) - ## 2 mulx - ## - ## Step 2: - ## Try larger divisor. - ## - var q: uint64 = zi div bigDivisor - ## uint64_t q = Mul128(zi, 0x83126E978D4FDF3Cu).hi >> 9; // 1 mulx - var r: uint32 = cast[uint32](zi) - bigDivisor * cast[uint32](q) - ## r = zi % BigDivisor - ## 0 <= r < 1000 - if r < delta: ## likely ~50% ?! - ## (r > deltai) - ## Exclude the right endpoint if necessary - if r != 0 or acceptUpper or not isIntegralEndpoint(twoFr, e2, minusK): - return FloatingDecimal64(significand: q, exponent: minusK + kappa + 1) - dragonbox_Assert(q != 0) - dec(q) - r = bigDivisor - elif r == delta: ## unlikely - ## Compare fractional parts. - ## Check conditions in the order different from the paper - ## to take advantage of short-circuiting - if (acceptLower and isIntegralEndpoint(twoFl, e2, minusK)) or - mulParity(twoFl, pow10, betaMinus1): - return FloatingDecimal64(significand: q, exponent: minusK + kappa + 1) - else: - discard - ## - ## Step 3: - ## Find the significand with the smaller divisor - ## - q = q * 10 - ## 1 hmul - ## 0 <= r <= 1000 - let dist: uint32 = r - (delta div 2) + (smallDivisor div 2) - let distQ: uint32 = dist div 100 - ## 1 mul - ## const uint32_t dist_r = dist % 100; - q += distQ - ## if /*likely*/ (dist_r == 0) - if dist == distQ * 100: - ## const bool approx_y_parity = ((dist ^ (SmallDivisor / 2)) & 1) != 0; - let approxYParity: bool = (dist and 1) != 0 - ## Check z^(f) >= epsilon^(f) - ## We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, - ## where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) - ## Since there are only 2 possibilities, we only need to care about the - ## parity. Also, zi and r should have the same parity since the divisor - ## is an even number - if mulParity(twoFc, pow10, betaMinus1) != approxYParity: - dec(q) - elif q mod 2 != 0 and isIntegralMidpoint(twoFc, e2, minusK): - dec(q) - return FloatingDecimal64(significand: q, exponent: minusK + kappa) - -# ================================================================================================== -# ToChars -# ================================================================================================== - -when false: - template `+!`(x: cstring; offset: int): cstring = cast[cstring](cast[uint](x) + uint(offset)) - - template dec(x: cstring; offset=1) = x = cast[cstring](cast[uint](x) - uint(offset)) - template inc(x: cstring; offset=1) = x = cast[cstring](cast[uint](x) + uint(offset)) - - proc memset(x: cstring; ch: char; L: int) {.importc, nodecl.} - proc memmove(a, b: cstring; L: int) {.importc, nodecl.} - -proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: uint32): int32 {.inline.} = - dragonbox_Assert(digits >= 1) - dragonbox_Assert(digits <= 99999999'u32) - let q: uint32 = digits div 10000 - let r: uint32 = digits mod 10000 - let qH: uint32 = q div 100 - let qL: uint32 = q mod 100 - utoa2Digits(buf, pos, qH) - utoa2Digits(buf, pos + 2, qL) - if r == 0: - return trailingZeros2Digits(if qL == 0: qH else: qL) + (if qL == 0: 6 else: 4) - else: - let rH: uint32 = r div 100 - let rL: uint32 = r mod 100 - utoa2Digits(buf, pos + 4, rH) - utoa2Digits(buf, pos + 6, rL) - return trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0) - -proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64: uint64): int32 {.inline.} = - var pos = pos - var output64 = output64 - var tz: int32 = 0 - ## number of trailing zeros removed. - var nd: int32 = 0 - ## number of decimal digits processed. - ## At most 17 digits remaining - if output64 >= 100000000'u64: - let q: uint64 = output64 div 100000000'u64 - let r: uint32 = cast[uint32](output64 mod 100000000'u64) - output64 = q - dec(pos, 8) - if r != 0: - tz = utoa8DigitsSkipTrailingZeros(buf, pos, r) - dragonbox_Assert(tz >= 0) - dragonbox_Assert(tz <= 7) - else: - tz = 8 - nd = 8 - dragonbox_Assert(output64 <= high(uint32)) - var output = cast[uint32](output64) - if output >= 10000: - let q: uint32 = output div 10000 - let r: uint32 = output mod 10000 - output = q - dec(pos, 4) - if r != 0: - let rH: uint32 = r div 100 - let rL: uint32 = r mod 100 - utoa2Digits(buf, pos, rH) - utoa2Digits(buf, pos + 2, rL) - if tz == nd: - inc(tz, trailingZeros2Digits(if rL == 0: rH else: rL) + - (if rL == 0: 2 else: 0)) - else: - if tz == nd: - inc(tz, 4) - else: - for i in 0..3: buf[pos+i] = '0' - ## (actually not required...) - inc(nd, 4) - if output >= 100: - let q: uint32 = output div 100 - let r: uint32 = output mod 100 - output = q - dec(pos, 2) - utoa2Digits(buf, pos, r) - if tz == nd: - inc(tz, trailingZeros2Digits(r)) - inc(nd, 2) - if output >= 100: - let q2: uint32 = output div 100 - let r2: uint32 = output mod 100 - output = q2 - dec(pos, 2) - utoa2Digits(buf, pos, r2) - if tz == nd: - inc(tz, trailingZeros2Digits(r2)) - inc(nd, 2) - dragonbox_Assert(output >= 1) - dragonbox_Assert(output <= 99) - if output >= 10: - let q: uint32 = output - dec(pos, 2) - utoa2Digits(buf, pos, q) - if tz == nd: - inc(tz, trailingZeros2Digits(q)) - else: - let q: uint32 = output - dragonbox_Assert(q >= 1) - dragonbox_Assert(q <= 9) - dec(pos) - buf[pos] = chr(ord('0') + q) - return tz - -proc decimalLength*(v: uint64): int {.inline.} = - dragonbox_Assert(v >= 1) - dragonbox_Assert(v <= 99999999999999999'u64) - if cast[uint32](v shr 32) != 0: - if v >= 10000000000000000'u64: - return 17 - if v >= 1000000000000000'u64: - return 16 - if v >= 100000000000000'u64: - return 15 - if v >= 10000000000000'u64: - return 14 - if v >= 1000000000000'u64: - return 13 - if v >= 100000000000'u64: - return 12 - if v >= 10000000000'u64: - return 11 - return 10 - let v32: uint32 = cast[uint32](v) - if v32 >= 1000000000'u32: - return 10 - if v32 >= 100000000'u32: - return 9 - if v32 >= 10000000'u32: - return 8 - if v32 >= 1000000'u32: - return 7 - if v32 >= 100000'u32: - return 6 - if v32 >= 10000'u32: - return 5 - if v32 >= 1000'u32: - return 4 - if v32 >= 100'u32: - return 3 - if v32 >= 10'u32: - return 2 - return 1 - -proc formatDigits*[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint64; decimalExponent: int; - forceTrailingDotZero = false): int {.inline.} = - const - minFixedDecimalPoint = -6 - const - maxFixedDecimalPoint = 17 - var pos:int = pos.int - assert(minFixedDecimalPoint <= -1, "internal error") - assert(maxFixedDecimalPoint >= 17, "internal error") - dragonbox_Assert(digits >= 1) - dragonbox_Assert(digits <= 99999999999999999'u64) - dragonbox_Assert(decimalExponent >= -999) - dragonbox_Assert(decimalExponent <= 999) - var numDigits = decimalLength(digits) - let decimalPoint = numDigits + decimalExponent - let useFixed: bool = minFixedDecimalPoint <= decimalPoint and - decimalPoint <= maxFixedDecimalPoint - ## Prepare the buffer. - for i in 0..<32: buffer[pos+i] = '0' - assert(minFixedDecimalPoint >= -30, "internal error") - assert(maxFixedDecimalPoint <= 32, "internal error") - var decimalDigitsPosition: int - if useFixed: - if decimalPoint <= 0: - ## 0.[000]digits - decimalDigitsPosition = 2 - decimalPoint - else: - ## dig.its - ## digits[000] - decimalDigitsPosition = 0 - else: - ## dE+123 or d.igitsE+123 - decimalDigitsPosition = 1 - var digitsEnd = pos + int(decimalDigitsPosition + numDigits) - let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits) - dec(digitsEnd, tz) - dec(numDigits, tz) - ## decimal_exponent += tz; // => decimal_point unchanged. - if useFixed: - if decimalPoint <= 0: - ## 0.[000]digits - buffer[pos+1] = '.' - pos = digitsEnd - elif decimalPoint < numDigits: - ## dig.its - when true: #defined(vcc) and not defined(clang): - ## VC does not inline the memmove call below. (Even if compiled with /arch:AVX2.) - ## However, memcpy will be inlined. - var tmp: array[16, char] - for i in 0..<16: tmp[i] = buffer[i+pos+decimalPoint] - for i in 0..<16: buffer[i+pos+decimalPoint+1] = tmp[i] - else: - memmove(buffer +! (decimalPoint + 1), buffer +! decimalPoint, 16) - buffer[pos+decimalPoint] = '.' - pos = digitsEnd + 1 - else: - ## digits[000] - inc(pos, decimalPoint) - if forceTrailingDotZero: - buffer[pos] = '.' - buffer[pos+1] = '0' - inc(pos, 2) - else: - ## Copy the first digit one place to the left. - buffer[pos] = buffer[pos+1] - if numDigits == 1: - ## dE+123 - inc(pos) - else: - ## d.igitsE+123 - buffer[pos+1] = '.' - pos = digitsEnd - let scientificExponent: int = decimalPoint - 1 - ## SF_ASSERT(scientific_exponent != 0); - buffer[pos] = 'e' - buffer[pos+1] = if scientificExponent < 0: '-' else: '+' - inc(pos, 2) - let k: uint32 = cast[uint32](if scientificExponent < 0: -scientificExponent else: scientificExponent) - if k < 10: - buffer[pos] = chr(ord('0') + k) - inc(pos) - elif k < 100: - utoa2Digits(buffer, pos, k) - inc(pos, 2) - else: - let q: uint32 = k div 100 - let r: uint32 = k mod 100 - buffer[pos] = chr(ord('0') + q) - inc(pos) - utoa2Digits(buffer, pos, r) - inc(pos, 2) - return pos - -proc toChars*(buffer: var openArray[char]; v: float; forceTrailingDotZero = false): int {. - inline.} = - var pos = 0 - let significand: uint64 = physicalSignificand(constructDouble(v)) - let exponent: uint64 = physicalExponent(constructDouble(v)) - if exponent != maxIeeeExponent: - ## Finite - buffer[pos] = '-' - inc(pos, signBit(constructDouble(v))) - if exponent != 0 or significand != 0: - ## != 0 - let dec = toDecimal64(significand, exponent) - return formatDigits(buffer, pos, dec.significand, dec.exponent.int, - forceTrailingDotZero) - else: - buffer[pos] = '0' - buffer[pos+1] = '.' - buffer[pos+2] = '0' - buffer[pos+3] = ' ' - inc(pos, if forceTrailingDotZero: 3 else: 1) - return pos - if significand == 0: - buffer[pos] = '-' - inc(pos, signBit(constructDouble(v))) - buffer[pos] = 'i' - buffer[pos+1] = 'n' - buffer[pos+2] = 'f' - buffer[pos+3] = ' ' - return pos + 3 - else: - buffer[pos] = 'n' - buffer[pos+1] = 'a' - buffer[pos+2] = 'n' - buffer[pos+3] = ' ' - return pos + 3 - -when false: - proc toString*(value: float): string = - var buffer: array[dtoaMinBufferLength, char] - let last = toChars(addr buffer, value) - let L = cast[int](last) - cast[int](addr(buffer)) - result = newString(L) - copyMem(addr result[0], addr buffer[0], L) - diff --git a/lib/std/private/gitutils.nim b/lib/std/private/gitutils.nim deleted file mode 100644 index db323bee1..000000000 --- a/lib/std/private/gitutils.nim +++ /dev/null @@ -1,81 +0,0 @@ -##[ -internal API for now, API subject to change -]## - -# xxx move other git utilities here; candidate for stdlib. - -import std/[os, osproc, strutils, tempfiles] - -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - -const commitHead* = "HEAD" - -template retryCall*(maxRetry = 3, backoffDuration = 1.0, call: untyped): bool = - ## Retry `call` up to `maxRetry` times with exponential backoff and initial - ## duraton of `backoffDuration` seconds. - ## This is in particular useful for network commands that can fail. - runnableExamples: - doAssert not retryCall(maxRetry = 2, backoffDuration = 0.1, false) - var i = 0 - doAssert: retryCall(maxRetry = 3, backoffDuration = 0.1, (i.inc; i >= 3)) - doAssert retryCall(call = true) - var result = false - var t = backoffDuration - for i in 0..= 2 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':': - result[0] = '/' - result[1] = path[0] - if path.len > 2 and path[2] != '\\': - doAssert false, "paths like `C:foo` are currently unsupported, path: " & path + doAssert not path.isAbsolute # not implemented here; absolute files need more care for the drive when DirSep == '\\': - result = replace(result, '\\', '/') + result = replace(path, '\\', '/') + else: result = path when isMainModule: import sugar - for a in walkDirRecFilter(".", follow = a=>a.path.lastPathPart notin ["nimcache", ".git", "csources_v1", "csources", "bin"]): + for a in walkDirRecFilter(".", follow = a=>a.path.lastPathPart notin ["nimcache", ".git", ".csources", "bin"]): echo a diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim deleted file mode 100644 index 836b3512a..000000000 --- a/lib/std/private/jsutils.nim +++ /dev/null @@ -1,83 +0,0 @@ -when defined(js): - import std/jsbigints - - type - ArrayBuffer* = ref object of JsRoot - Float64Array* = ref object of JsRoot - Uint32Array* = ref object of JsRoot - Uint8Array* = ref object of JsRoot - BigUint64Array* = ref object of JsRoot - - - func newArrayBuffer*(n: int): ArrayBuffer {.importjs: "new ArrayBuffer(#)".} - func newFloat64Array*(buffer: ArrayBuffer): Float64Array {.importjs: "new Float64Array(#)".} - func newUint32Array*(buffer: ArrayBuffer): Uint32Array {.importjs: "new Uint32Array(#)".} - func newBigUint64Array*(buffer: ArrayBuffer): BigUint64Array {.importjs: "new BigUint64Array(#)".} - - func newUint8Array*(n: int): Uint8Array {.importjs: "new Uint8Array(#)".} - - func `[]`*(arr: Uint32Array, i: int): uint32 {.importjs: "#[#]".} - func `[]`*(arr: Uint8Array, i: int): uint8 {.importjs: "#[#]".} - func `[]`*(arr: BigUint64Array, i: int): JsBigInt {.importjs: "#[#]".} - func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".} - - proc jsTypeOf*[T](x: T): cstring {.importjs: "typeof(#)".} = - ## Returns the name of the JsObject's JavaScript type as a cstring. - # xxx replace jsffi.jsTypeOf with this definition and add tests - runnableExamples: - import std/[jsffi, jsbigints] - assert jsTypeOf(1.toJs) == "number" - assert jsTypeOf(false.toJs) == "boolean" - assert [1].toJs.jsTypeOf == "object" # note the difference with `getProtoName` - assert big"1".toJs.jsTypeOf == "bigint" - - proc jsConstructorName*[T](a: T): cstring = - runnableExamples: - import std/jsffi - let a = array[2, float64].default - assert jsConstructorName(a) == "Float64Array" - assert jsConstructorName(a.toJs) == "Float64Array" - asm """`result` = `a`.constructor.name""" - - proc hasJsBigInt*(): bool = - asm """`result` = typeof BigInt != 'undefined'""" - - proc hasBigUint64Array*(): bool = - asm """`result` = typeof BigUint64Array != 'undefined'""" - - proc getProtoName*[T](a: T): cstring {.importjs: "Object.prototype.toString.call(#)".} = - runnableExamples: - import std/[jsffi, jsbigints] - type A = ref object - assert 1.toJs.getProtoName == "[object Number]" - assert "a".toJs.getProtoName == "[object String]" - assert big"1".toJs.getProtoName == "[object BigInt]" - assert false.toJs.getProtoName == "[object Boolean]" - assert (a: 1).toJs.getProtoName == "[object Object]" - assert A.default.toJs.getProtoName == "[object Null]" - assert [1].toJs.getProtoName == "[object Int32Array]" # implementation defined - assert @[1].toJs.getProtoName == "[object Array]" # ditto - - const maxSafeInteger* = 9007199254740991 - ## The same as `Number.MAX_SAFE_INTEGER` or `2^53 - 1`. - ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER - runnableExamples: - let a {.importjs: "Number.MAX_SAFE_INTEGER".}: int64 - assert a == maxSafeInteger - - proc isInteger*[T](x: T): bool {.importjs: "Number.isInteger(#)".} = - runnableExamples: - import std/jsffi - assert 1.isInteger - assert not 1.5.isInteger - assert 1.toJs.isInteger - assert not 1.5.toJs.isInteger - - proc isSafeInteger*[T](x: T): bool {.importjs: "Number.isSafeInteger(#)".} = - runnableExamples: - import std/jsffi - assert not "123".toJs.isSafeInteger - assert 123.isSafeInteger - assert 123.toJs.isSafeInteger - assert 9007199254740991.toJs.isSafeInteger - assert not 9007199254740992.toJs.isSafeInteger diff --git a/lib/std/private/miscdollars.nim b/lib/std/private/miscdollars.nim index 06fda6fa1..a41cf1bc1 100644 --- a/lib/std/private/miscdollars.nim +++ b/lib/std/private/miscdollars.nim @@ -1,5 +1,3 @@ -from std/private/digitsutils import addInt - template toLocation*(result: var string, file: string | cstring, line: int, col: int) = ## avoids spurious allocations # Hopefully this can be re-used everywhere so that if a user needs to customize, @@ -7,33 +5,11 @@ template toLocation*(result: var string, file: string | cstring, line: int, col: result.add file if line > 0: result.add "(" - addInt(result, line) + # simplify this after moving moving `include strmantle` above import assertions` + when declared(addInt): result.addInt line + else: result.add $line if col > 0: result.add ", " - addInt(result, col) + when declared(addInt): result.addInt col + else: result.add $col result.add ")" - -proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} - -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 deleted file mode 100644 index 7c8661bb7..000000000 --- a/lib/std/private/ntpath.nim +++ /dev/null @@ -1,61 +0,0 @@ -# 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.. 0: - let buffer = newWideCString(size.int) - if getTempPath(size, buffer) > 0: - result = $buffer - elif defined(android): result = "/data/local/tmp" - else: - getTempDirImpl(result) - if result.len == 0: - result = tempDirDefault - normalizePathEnd(result, trailingSep=true) diff --git a/lib/std/private/oscommon.nim b/lib/std/private/oscommon.nim deleted file mode 100644 index b747e33f1..000000000 --- a/lib/std/private/oscommon.nim +++ /dev/null @@ -1,186 +0,0 @@ -include system/inclrtl - -import std/[oserrors] - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions, widestrs] - -## .. importdoc:: osdirs.nim, os.nim - -const weirdTarget* = defined(nimscript) or defined(js) - - -type - ReadDirEffect* = object of ReadIOEffect ## Effect that denotes a read - ## operation from the directory - ## structure. - WriteDirEffect* = object of WriteIOEffect ## Effect that denotes a write - ## operation to - ## the directory structure. - - -when weirdTarget: - discard -elif defined(windows): - import winlean, times -elif defined(posix): - import posix - proc c_rename(oldname, newname: cstring): cint {. - importc: "rename", header: "".} -else: - {.error: "OS module not ported to your operating system!".} - - -when weirdTarget: - {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".} -else: - {.pragma: noWeirdTarget.} - - -when defined(nimscript): - # for procs already defined in scriptconfig.nim - template noNimJs(body): untyped = discard -elif defined(js): - {.pragma: noNimJs, error: "this proc is not available on the js target".} -else: - {.pragma: noNimJs.} - - -when defined(windows) and not weirdTarget: - template wrapUnary*(varname, winApiProc, arg: untyped) = - var varname = winApiProc(newWideCString(arg)) - - template wrapBinary*(varname, winApiProc, arg, arg2: untyped) = - var varname = winApiProc(newWideCString(arg), arg2) - proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle = - result = findFirstFileW(newWideCString(a), b) - template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b) - - template getFilename*(f: untyped): untyped = - $cast[WideCString](addr(f.cFileName[0])) - - proc skipFindData*(f: WIN32_FIND_DATA): bool {.inline.} = - # Note - takes advantage of null delimiter in the cstring - const dot = ord('.') - result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or - f.cFileName[1].int == dot and f.cFileName[2].int == 0) - - -type - PathComponent* = enum ## Enumeration specifying a path component. - ## - ## See also: - ## * `walkDirRec iterator`_ - ## * `FileInfo object`_ - pcFile, ## path refers to a file - pcLinkToFile, ## path refers to a symbolic link to a file - pcDir, ## path refers to a directory - pcLinkToDir ## path refers to a symbolic link to a directory - - -when defined(posix) and not weirdTarget: - proc getSymlinkFileKind*(path: string): - tuple[pc: PathComponent, isSpecial: bool] = - # Helper function. - var s: Stat - assert(path != "") - result = (pcLinkToFile, false) - if stat(path, s) == 0'i32: - if S_ISDIR(s.st_mode): - result = (pcLinkToDir, false) - elif not S_ISREG(s.st_mode): - result = (pcLinkToFile, true) - -proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} = - ## Moves a file (or directory if `isDir` is true) from `source` to `dest`. - ## - ## Returns false in case of `EXDEV` error or `AccessDeniedError` on Windows (if `isDir` is true). - ## In case of other errors `OSError` is raised. - ## Returns true in case of success. - when defined(windows): - let s = newWideCString(source) - let d = newWideCString(dest) - result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 - else: - result = c_rename(source, dest) == 0'i32 - - if not result: - let err = osLastError() - let isAccessDeniedError = - when defined(windows): - const AccessDeniedError = OSErrorCode(5) - isDir and err == AccessDeniedError - else: - err == EXDEV.OSErrorCode - if not isAccessDeniedError: - raiseOSError(err, $(source, dest)) - -when not defined(windows): - const maxSymlinkLen* = 1024 - -proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", - tags: [ReadDirEffect], noNimJs.} = - ## Returns true if `filename` exists and is a regular file or symlink. - ## - ## Directories, device files, named pipes and sockets return false. - ## - ## See also: - ## * `dirExists proc`_ - ## * `symlinkExists proc`_ - when defined(windows): - wrapUnary(a, getFileAttributesW, filename) - if a != -1'i32: - result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32 - else: - var res: Stat - return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) - - -proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], - noNimJs.} = - ## Returns true if the directory `dir` exists. If `dir` is a file, false - ## is returned. Follows symlinks. - ## - ## See also: - ## * `fileExists proc`_ - ## * `symlinkExists proc`_ - when defined(windows): - wrapUnary(a, getFileAttributesW, dir) - if a != -1'i32: - result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 - else: - var res: Stat - result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) - - -proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", - tags: [ReadDirEffect], - noWeirdTarget.} = - ## Returns true if the symlink `link` exists. Will return true - ## regardless of whether the link points to a directory or file. - ## - ## See also: - ## * `fileExists proc`_ - ## * `dirExists proc`_ - when defined(windows): - wrapUnary(a, getFileAttributesW, link) - if a != -1'i32: - # xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK` - # may also be needed. - result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32 - else: - var res: Stat - result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode) - -when defined(windows) and not weirdTarget: - proc openHandle*(path: string, followSymlink=true, writeAccess=false): Handle = - var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL - if not followSymlink: - flags = flags or FILE_FLAG_OPEN_REPARSE_POINT - let access = if writeAccess: GENERIC_WRITE else: 0'i32 - - result = createFileW( - newWideCString(path), access, - FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, - nil, OPEN_EXISTING, flags, 0 - ) diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim deleted file mode 100644 index a4318367d..000000000 --- a/lib/std/private/osdirs.nim +++ /dev/null @@ -1,561 +0,0 @@ -## .. importdoc:: osfiles.nim, appdirs.nim, paths.nim - -include system/inclrtl -import std/oserrors - - -import ospaths2, osfiles -import oscommon -export dirExists, PathComponent - - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions, widestrs] - - -when weirdTarget: - discard -elif defined(windows): - import winlean, times -elif defined(posix): - import posix, times - -else: - {.error: "OS module not ported to your operating system!".} - - -when weirdTarget: - {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".} -else: - {.pragma: noWeirdTarget.} - - -when defined(nimscript): - # for procs already defined in scriptconfig.nim - template noNimJs(body): untyped = discard -elif defined(js): - {.pragma: noNimJs, error: "this proc is not available on the js target".} -else: - {.pragma: noNimJs.} - -# Templates for filtering directories and files -when defined(windows) and not weirdTarget: - template isDir(f: WIN32_FIND_DATA): bool = - (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 - template isFile(f: WIN32_FIND_DATA): bool = - not isDir(f) -else: - template isDir(f: string): bool {.dirty.} = - dirExists(f) - template isFile(f: string): bool {.dirty.} = - fileExists(f) - -template defaultWalkFilter(item): bool = - ## Walk filter used to return true on both - ## files and directories - true - -template walkCommon(pattern: string, filter) = - ## Common code for getting the files and directories with the - ## specified `pattern` - when defined(windows): - var - f: WIN32_FIND_DATA - res: int - res = findFirstFile(pattern, f) - if res != -1: - defer: findClose(res) - let dotPos = searchExtPos(pattern) - while true: - if not skipFindData(f) and filter(f): - # Windows bug/gotcha: 't*.nim' matches 'tfoo.nims' -.- so we check - # that the file extensions have the same length ... - let ff = getFilename(f) - let idx = ff.len - pattern.len + dotPos - if dotPos < 0 or idx >= ff.len or (idx >= 0 and ff[idx] == '.') or - (dotPos >= 0 and dotPos+1 < pattern.len and pattern[dotPos+1] == '*'): - yield splitFile(pattern).dir / extractFilename(ff) - if findNextFile(res, f) == 0'i32: - let errCode = getLastError() - if errCode == ERROR_NO_MORE_FILES: break - else: raiseOSError(errCode.OSErrorCode) - else: # here we use glob - var - f: Glob - res: int - f.gl_offs = 0 - f.gl_pathc = 0 - f.gl_pathv = nil - res = glob(pattern, 0, nil, addr(f)) - defer: globfree(addr(f)) - if res == 0: - for i in 0.. f.gl_pathc - 1: - assert(f.gl_pathv[i] != nil) - let path = $f.gl_pathv[i] - if filter(path): - yield path - -iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = - ## Iterate over all the files and directories that match the `pattern`. - ## - ## On POSIX this uses the `glob`:idx: call. - ## `pattern` is OS dependent, but at least the `"*.ext"` - ## notation is supported. - ## - ## See also: - ## * `walkFiles iterator`_ - ## * `walkDirs iterator`_ - ## * `walkDir iterator`_ - ## * `walkDirRec iterator`_ - runnableExamples: - import std/os - import std/sequtils - let paths = toSeq(walkPattern("lib/pure/*")) # works on Windows too - assert "lib/pure/concurrency".unixToNativePath in paths - assert "lib/pure/os.nim".unixToNativePath in paths - walkCommon(pattern, defaultWalkFilter) - -iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = - ## Iterate over all the files that match the `pattern`. - ## - ## On POSIX this uses the `glob`:idx: call. - ## `pattern` is OS dependent, but at least the `"*.ext"` - ## notation is supported. - ## - ## See also: - ## * `walkPattern iterator`_ - ## * `walkDirs iterator`_ - ## * `walkDir iterator`_ - ## * `walkDirRec iterator`_ - runnableExamples: - import std/os - import std/sequtils - assert "lib/pure/os.nim".unixToNativePath in toSeq(walkFiles("lib/pure/*.nim")) # works on Windows too - walkCommon(pattern, isFile) - -iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = - ## Iterate over all the directories that match the `pattern`. - ## - ## On POSIX this uses the `glob`:idx: call. - ## `pattern` is OS dependent, but at least the `"*.ext"` - ## notation is supported. - ## - ## See also: - ## * `walkPattern iterator`_ - ## * `walkFiles iterator`_ - ## * `walkDir iterator`_ - ## * `walkDirRec iterator`_ - runnableExamples: - import std/os - import std/sequtils - let paths = toSeq(walkDirs("lib/pure/*")) # works on Windows too - assert "lib/pure/concurrency".unixToNativePath in paths - walkCommon(pattern, isDir) - -proc staticWalkDir(dir: string; relative: bool): seq[ - tuple[kind: PathComponent, path: string]] = - discard - -iterator walkDir*(dir: string; relative = false, checkDir = false, - skipSpecial = false): - tuple[kind: PathComponent, path: string] {.tags: [ReadDirEffect].} = - ## Walks over the directory `dir` and yields for each directory or file in - ## `dir`. The component type and full path for each item are returned. - ## - ## Walking is not recursive. - ## * If `relative` is true (default: false) - ## the resulting path is shortened to be relative to ``dir``, - ## otherwise the full path is returned. - ## * If `checkDir` is true, `OSError` is raised when `dir` - ## doesn't exist. - ## * If `skipSpecial` is true, then (besides all directories) only *regular* - ## files (**without** special "file" objects like FIFOs, device files, - ## etc) will be yielded on Unix. - ## - ## **Example:** - ## - ## This directory structure: - ## - ## dirA / dirB / fileB1.txt - ## / dirC - ## / fileA1.txt - ## / fileA2.txt - ## - ## and this code: - runnableExamples("-r:off"): - import std/[strutils, sugar] - # note: order is not guaranteed - # this also works at compile time - assert collect(for k in walkDir("dirA"): k.path).join(" ") == - "dirA/dirB dirA/dirC dirA/fileA2.txt dirA/fileA1.txt" - ## See also: - ## * `walkPattern iterator`_ - ## * `walkFiles iterator`_ - ## * `walkDirs iterator`_ - ## * `walkDirRec iterator`_ - - when nimvm: - for k, v in items(staticWalkDir(dir, relative)): - yield (k, v) - else: - when weirdTarget: - for k, v in items(staticWalkDir(dir, relative)): - yield (k, v) - elif defined(windows): - var f: WIN32_FIND_DATA - var h = findFirstFile(dir / "*", f) - if h == -1: - if checkDir: - raiseOSError(osLastError(), dir) - else: - defer: findClose(h) - while true: - var k = pcFile - if not skipFindData(f): - if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: - k = pcDir - if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: - k = succ(k) - let xx = if relative: extractFilename(getFilename(f)) - else: dir / extractFilename(getFilename(f)) - yield (k, xx) - if findNextFile(h, f) == 0'i32: - let errCode = getLastError() - if errCode == ERROR_NO_MORE_FILES: break - else: raiseOSError(errCode.OSErrorCode) - else: - var d = opendir(dir) - if d == nil: - if checkDir: - raiseOSError(osLastError(), dir) - else: - defer: discard closedir(d) - while true: - var x = readdir(d) - if x == nil: break - var y = $cast[cstring](addr x.d_name) - if y != "." and y != "..": - var s: Stat - let path = dir / y - if not relative: - y = path - var k = pcFile - - template resolveSymlink() = - var isSpecial: bool - (k, isSpecial) = getSymlinkFileKind(path) - if skipSpecial and isSpecial: continue - - template kSetGeneric() = # pure Posix component `k` resolution - 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): - resolveSymlink() - elif skipSpecial and not S_ISREG(s.st_mode): continue - - when defined(linux) or defined(macosx) or - defined(bsd) or defined(genode) or defined(nintendoswitch): - case x.d_type - of DT_DIR: k = pcDir - of DT_LNK: - resolveSymlink() - of DT_UNKNOWN: - kSetGeneric() - else: # DT_REG or special "files" like FIFOs - if skipSpecial and x.d_type != DT_REG: continue - else: discard # leave it as pcFile - else: # assuming that field `d_type` is not present - kSetGeneric() - - yield (k, y) - -iterator walkDirRec*(dir: string, - yieldFilter = {pcFile}, followFilter = {pcDir}, - relative = false, checkDir = false, skipSpecial = false): - string {.tags: [ReadDirEffect].} = - ## Recursively walks over the directory `dir` and yields for each file - ## or directory in `dir`. - ## - ## Options `relative`, `checkdir`, `skipSpecial` are explained in - ## [walkDir iterator] description. - ## - ## .. warning:: Modifying the directory structure while the iterator - ## is traversing may result in undefined behavior! - ## - ## 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: - ## * `walkPattern iterator`_ - ## * `walkFiles iterator`_ - ## * `walkDirs iterator`_ - ## * `walkDir iterator`_ - - var stack = @[""] - var checkDir = checkDir - while stack.len > 0: - let d = stack.pop() - for k, p in walkDir(dir / d, relative = true, checkDir = checkDir, - skipSpecial = skipSpecial): - let rel = d / p - if k in {pcDir, pcLinkToDir} and k in followFilter: - stack.add rel - if k in yieldFilter: - yield if relative: rel else: dir / rel - checkDir = false - # We only check top-level dir, otherwise if a subdir is invalid (eg. wrong - # permissions), it'll abort iteration and there would be no way to - # continue iteration. - # Future work can provide a way to customize this and do error reporting. - - -proc rawRemoveDir(dir: string) {.noWeirdTarget.} = - when defined(windows): - wrapUnary(res, removeDirectoryW, dir) - let lastError = osLastError() - if res == 0'i32 and lastError.int32 != 3'i32 and - lastError.int32 != 18'i32 and lastError.int32 != 2'i32: - raiseOSError(lastError, dir) - else: - if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError(), dir) - -proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [ - WriteDirEffect, ReadDirEffect], benign, noWeirdTarget.} = - ## Removes the directory `dir` including all subdirectories and files - ## in `dir` (recursively). - ## - ## If this fails, `OSError` is raised. This does not fail if the directory never - ## existed in the first place, unless `checkDir` = true. - ## - ## See also: - ## * `tryRemoveFile proc`_ - ## * `removeFile proc`_ - ## * `existsOrCreateDir proc`_ - ## * `createDir proc`_ - ## * `copyDir proc`_ - ## * `copyDirWithPermissions proc`_ - ## * `moveDir proc`_ - for kind, path in walkDir(dir, checkDir = checkDir): - case kind - of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path) - of pcDir: removeDir(path, true) - # for subdirectories there is no benefit in `checkDir = false` - # (unless perhaps for edge case of concurrent processes also deleting - # the same files) - rawRemoveDir(dir) - -proc rawCreateDir(dir: string): bool {.noWeirdTarget.} = - # Try to create one directory (not the whole path). - # returns `true` for success, `false` if the path has previously existed - # - # This is a thin wrapper over mkDir (or alternatives on other systems), - # so in case of a pre-existing path we don't check that it is a directory. - when defined(solaris): - let res = mkdir(dir, 0o777) - if res == 0'i32: - result = true - elif errno in {EEXIST, ENOSYS}: - result = false - else: - raiseOSError(osLastError(), dir) - elif defined(haiku): - let res = mkdir(dir, 0o777) - if res == 0'i32: - result = true - elif errno == EEXIST or errno == EROFS: - result = false - else: - raiseOSError(osLastError(), dir) - elif defined(posix): - let res = mkdir(dir, 0o777) - if res == 0'i32: - result = true - elif errno == EEXIST: - result = false - else: - #echo res - raiseOSError(osLastError(), dir) - else: - wrapUnary(res, createDirectoryW, dir) - - if res != 0'i32: - result = true - elif getLastError() == 183'i32: - result = false - else: - raiseOSError(osLastError(), dir) - -proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", - tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} = - ## Checks if a `directory`:idx: `dir` exists, and creates it otherwise. - ## - ## Does not create parent directories (raises `OSError` if parent directories do not exist). - ## Returns `true` if the directory already exists, and `false` otherwise. - ## - ## See also: - ## * `removeDir proc`_ - ## * `createDir proc`_ - ## * `copyDir proc`_ - ## * `copyDirWithPermissions proc`_ - ## * `moveDir proc`_ - result = not rawCreateDir(dir) - if result: - # path already exists - need to check that it is indeed a directory - if not dirExists(dir): - raise newException(IOError, "Failed to create '" & dir & "'") - -proc createDir*(dir: string) {.rtl, extern: "nos$1", - tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} = - ## Creates the `directory`:idx: `dir`. - ## - ## The directory may contain several subdirectories that do not exist yet. - ## The full path is created. If this fails, `OSError` is raised. - ## - ## It does **not** fail if the directory already exists because for - ## most usages this does not indicate an error. - ## - ## See also: - ## * `removeDir proc`_ - ## * `existsOrCreateDir proc`_ - ## * `copyDir proc`_ - ## * `copyDirWithPermissions proc`_ - ## * `moveDir proc`_ - 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.} = - ## Copies a directory from `source` to `dest`. - ## - ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks - ## are skipped. - ## - ## If this fails, `OSError` is raised. - ## - ## On the Windows platform this proc will copy the attributes from - ## `source` into `dest`. - ## - ## On other platforms created files and directories will inherit the - ## default permissions of a newly created file/directory for the user. - ## Use `copyDirWithPermissions proc`_ - ## to preserve attributes recursively on these platforms. - ## - ## See also: - ## * `copyDirWithPermissions proc`_ - ## * `copyFile proc`_ - ## * `copyFileWithPermissions proc`_ - ## * `removeDir proc`_ - ## * `existsOrCreateDir proc`_ - ## * `createDir proc`_ - ## * `moveDir proc`_ - createDir(dest) - for kind, path in walkDir(source): - var noSource = splitPath(path).tail - if kind == pcDir: - copyDir(path, dest / noSource) - else: - copyFile(path, dest / noSource, {cfSymlinkAsIs}) - - -proc copyDirWithPermissions*(source, dest: string, - ignorePermissionErrors = true) - {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], - benign, noWeirdTarget.} = - ## Copies a directory from `source` to `dest` preserving file permissions. - ## - ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks - ## are skipped. - ## - ## If this fails, `OSError` is raised. This is a wrapper proc around - ## `copyDir`_ and `copyFileWithPermissions`_ procs - ## on non-Windows platforms. - ## - ## On Windows this proc is just a wrapper for `copyDir proc`_ since - ## that proc already copies attributes. - ## - ## On non-Windows systems permissions are copied after the file or directory - ## itself has been copied, which won't happen atomically and could lead to a - ## race condition. If `ignorePermissionErrors` is true (default), errors while - ## reading/setting file attributes will be ignored, otherwise will raise - ## `OSError`. - ## - ## See also: - ## * `copyDir proc`_ - ## * `copyFile proc`_ - ## * `copyFileWithPermissions proc`_ - ## * `removeDir proc`_ - ## * `moveDir proc`_ - ## * `existsOrCreateDir proc`_ - ## * `createDir proc`_ - createDir(dest) - when not defined(windows): - try: - setFilePermissions(dest, getFilePermissions(source), followSymlinks = - false) - except: - if not ignorePermissionErrors: - raise - for kind, path in walkDir(source): - var noSource = splitPath(path).tail - if kind == pcDir: - copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors) - else: - copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors, {cfSymlinkAsIs}) - -proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} = - ## Moves a directory from `source` to `dest`. - ## - ## Symlinks are not followed: if `source` contains symlinks, they themself are - ## moved, not their target. - ## - ## If this fails, `OSError` is raised. - ## - ## See also: - ## * `moveFile proc`_ - ## * `copyDir proc`_ - ## * `copyDirWithPermissions proc`_ - ## * `removeDir proc`_ - ## * `existsOrCreateDir proc`_ - ## * `createDir proc`_ - if not tryMoveFSObject(source, dest, isDir = true): - # Fallback to copy & del - copyDir(source, dest) - removeDir(source) - -proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} = - ## Sets the `current working directory`:idx:; `OSError` - ## is raised if `newDir` cannot been set. - ## - ## See also: - ## * `getHomeDir proc`_ - ## * `getConfigDir proc`_ - ## * `getTempDir proc`_ - ## * `getCurrentDir proc`_ - when defined(windows): - if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32: - raiseOSError(osLastError(), newDir) - else: - if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir) diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim deleted file mode 100644 index 7f822ffcc..000000000 --- a/lib/std/private/osfiles.nim +++ /dev/null @@ -1,403 +0,0 @@ -include system/inclrtl -import std/private/since -import std/oserrors - -import oscommon -export fileExists - -import ospaths2, ossymlinks - -## .. importdoc:: osdirs.nim, os.nim - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions, widestrs] - -when weirdTarget: - discard -elif defined(windows): - import winlean -elif defined(posix): - import posix, times - - proc toTime(ts: Timespec): times.Time {.inline.} = - result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) -else: - {.error: "OS module not ported to your operating system!".} - - -when weirdTarget: - {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".} -else: - {.pragma: noWeirdTarget.} - - -when defined(nimscript): - # for procs already defined in scriptconfig.nim - template noNimJs(body): untyped = discard -elif defined(js): - {.pragma: noNimJs, error: "this proc is not available on the js target".} -else: - {.pragma: noNimJs.} - - -type - FilePermission* = enum ## File access permission, modelled after UNIX. - ## - ## See also: - ## * `getFilePermissions`_ - ## * `setFilePermissions`_ - ## * `FileInfo object`_ - fpUserExec, ## execute access for the file owner - fpUserWrite, ## write access for the file owner - fpUserRead, ## read access for the file owner - fpGroupExec, ## execute access for the group - fpGroupWrite, ## write access for the group - fpGroupRead, ## read access for the group - fpOthersExec, ## execute access for others - fpOthersWrite, ## write access for others - fpOthersRead ## read access for others - -proc getFilePermissions*(filename: string): set[FilePermission] {. - rtl, extern: "nos$1", tags: [ReadDirEffect], noWeirdTarget.} = - ## Retrieves file permissions for `filename`. - ## - ## `OSError` is raised in case of an error. - ## On Windows, only the ``readonly`` flag is checked, every other - ## permission is available in any case. - ## - ## See also: - ## * `setFilePermissions proc`_ - ## * `FilePermission enum`_ - when defined(posix): - var a: Stat - if stat(filename, a) < 0'i32: raiseOSError(osLastError(), filename) - result = {} - if (a.st_mode and S_IRUSR.Mode) != 0.Mode: result.incl(fpUserRead) - if (a.st_mode and S_IWUSR.Mode) != 0.Mode: result.incl(fpUserWrite) - if (a.st_mode and S_IXUSR.Mode) != 0.Mode: result.incl(fpUserExec) - - if (a.st_mode and S_IRGRP.Mode) != 0.Mode: result.incl(fpGroupRead) - if (a.st_mode and S_IWGRP.Mode) != 0.Mode: result.incl(fpGroupWrite) - if (a.st_mode and S_IXGRP.Mode) != 0.Mode: result.incl(fpGroupExec) - - if (a.st_mode and S_IROTH.Mode) != 0.Mode: result.incl(fpOthersRead) - if (a.st_mode and S_IWOTH.Mode) != 0.Mode: result.incl(fpOthersWrite) - if (a.st_mode and S_IXOTH.Mode) != 0.Mode: result.incl(fpOthersExec) - else: - wrapUnary(res, getFileAttributesW, filename) - if res == -1'i32: raiseOSError(osLastError(), filename) - if (res and FILE_ATTRIBUTE_READONLY) != 0'i32: - result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, - fpOthersExec, fpOthersRead} - else: - result = {fpUserExec..fpOthersRead} - -proc setFilePermissions*(filename: string, permissions: set[FilePermission], - followSymlinks = true) - {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], - noWeirdTarget.} = - ## Sets the file permissions for `filename`. - ## - ## If `followSymlinks` set to true (default) and ``filename`` points to a - ## symlink, permissions are set to the file symlink points to. - ## `followSymlinks` set to false is a noop on Windows and some POSIX - ## systems (including Linux) on which `lchmod` is either unavailable or always - ## fails, given that symlinks permissions there are not observed. - ## - ## `OSError` is raised in case of an error. - ## On Windows, only the ``readonly`` flag is changed, depending on - ## ``fpUserWrite`` permission. - ## - ## See also: - ## * `getFilePermissions proc`_ - ## * `FilePermission enum`_ - when defined(posix): - var p = 0.Mode - if fpUserRead in permissions: p = p or S_IRUSR.Mode - if fpUserWrite in permissions: p = p or S_IWUSR.Mode - if fpUserExec in permissions: p = p or S_IXUSR.Mode - - if fpGroupRead in permissions: p = p or S_IRGRP.Mode - if fpGroupWrite in permissions: p = p or S_IWGRP.Mode - if fpGroupExec in permissions: p = p or S_IXGRP.Mode - - if fpOthersRead in permissions: p = p or S_IROTH.Mode - if fpOthersWrite in permissions: p = p or S_IWOTH.Mode - if fpOthersExec in permissions: p = p or S_IXOTH.Mode - - if not followSymlinks and filename.symlinkExists: - when declared(lchmod): - if lchmod(filename, cast[Mode](p)) != 0: - raiseOSError(osLastError(), $(filename, permissions)) - else: - if chmod(filename, cast[Mode](p)) != 0: - raiseOSError(osLastError(), $(filename, permissions)) - else: - wrapUnary(res, getFileAttributesW, filename) - if res == -1'i32: raiseOSError(osLastError(), filename) - if fpUserWrite in permissions: - res = res and not FILE_ATTRIBUTE_READONLY - else: - res = res or FILE_ATTRIBUTE_READONLY - wrapBinary(res2, setFileAttributesW, filename, res) - if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions)) - - -const hasCCopyfile = defined(osx) and not defined(nimLegacyCopyFile) - # xxx instead of `nimLegacyCopyFile`, support something like: `when osxVersion >= (10, 5)` - -when hasCCopyfile: - # `copyfile` API available since osx 10.5. - {.push nodecl, header: "".} - type - copyfile_state_t {.nodecl.} = pointer - copyfile_flags_t = cint - proc copyfile_state_alloc(): copyfile_state_t - proc copyfile_state_free(state: copyfile_state_t): cint - proc c_copyfile(src, dst: cstring, state: copyfile_state_t, flags: copyfile_flags_t): cint {.importc: "copyfile".} - # replace with `let` pending bootstrap >= 1.4.0 - var - COPYFILE_DATA {.nodecl.}: copyfile_flags_t - COPYFILE_XATTR {.nodecl.}: copyfile_flags_t - {.pop.} - -type - CopyFlag* = enum ## Copy options. - cfSymlinkAsIs, ## Copy symlinks as symlinks - cfSymlinkFollow, ## Copy the files symlinks point to - cfSymlinkIgnore ## Ignore symlinks - -const copyFlagSymlink = {cfSymlinkAsIs, cfSymlinkFollow, cfSymlinkIgnore} - -proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, - extern: "nos$1", tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], - noWeirdTarget.} = - ## Copies a file from `source` to `dest`, where `dest.parentDir` must exist. - ## - ## On non-Windows OSes, `options` specify the way file is copied; by default, - ## if `source` is a symlink, copies the file symlink points to. `options` is - ## ignored on Windows: symlinks are skipped. - ## - ## If this fails, `OSError` is raised. - ## - ## On the Windows platform this proc will - ## copy the source file's attributes into dest. - ## - ## On other platforms you need - ## to use `getFilePermissions`_ and - ## `setFilePermissions`_ - ## procs - ## to copy them by hand (or use the convenience `copyFileWithPermissions - ## proc`_), - ## otherwise `dest` will inherit the default permissions of a newly - ## created file for the user. - ## - ## If `dest` already exists, the file attributes - ## will be preserved and the content overwritten. - ## - ## On OSX, `copyfile` C api will be used (available since OSX 10.5) unless - ## `-d:nimLegacyCopyFile` is used. - ## - ## See also: - ## * `CopyFlag enum`_ - ## * `copyDir proc`_ - ## * `copyFileWithPermissions proc`_ - ## * `tryRemoveFile proc`_ - ## * `removeFile proc`_ - ## * `moveFile proc`_ - - doAssert card(copyFlagSymlink * options) == 1, "There should be exactly " & - "one cfSymlink* in options" - let isSymlink = source.symlinkExists - if isSymlink and (cfSymlinkIgnore in options or defined(windows)): - return - when defined(windows): - let s = newWideCString(source) - let d = newWideCString(dest) - if copyFileW(s, d, 0'i32) == 0'i32: - raiseOSError(osLastError(), $(source, dest)) - else: - if isSymlink and cfSymlinkAsIs in options: - createSymlink(expandSymlink(source), dest) - else: - when hasCCopyfile: - let state = copyfile_state_alloc() - # xxx `COPYFILE_STAT` could be used for one-shot - # `copyFileWithPermissions`. - let status = c_copyfile(source.cstring, dest.cstring, state, - COPYFILE_DATA) - if status != 0: - let err = osLastError() - discard copyfile_state_free(state) - raiseOSError(err, $(source, dest)) - let status2 = copyfile_state_free(state) - if status2 != 0: raiseOSError(osLastError(), $(source, dest)) - else: - # generic version of copyFile which works for any platform: - const bufSize = 8000 # better for memory manager - var d, s: File - if not open(s, source):raiseOSError(osLastError(), source) - if not open(d, dest, fmWrite): - close(s) - raiseOSError(osLastError(), dest) - var buf = alloc(bufSize) - while true: - var bytesread = readBuffer(s, buf, bufSize) - if bytesread > 0: - var byteswritten = writeBuffer(d, buf, bytesread) - if bytesread != byteswritten: - dealloc(buf) - close(s) - close(d) - raiseOSError(osLastError(), dest) - if bytesread != bufSize: break - dealloc(buf) - close(s) - flushFile(d) - close(d) - -proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}) - {.noWeirdTarget, since: (1,3,7).} = - ## Copies a file `source` into directory `dir`, which must exist. - ## - ## On non-Windows OSes, `options` specify the way file is copied; by default, - ## if `source` is a symlink, copies the file symlink points to. `options` is - ## ignored on Windows: symlinks are skipped. - ## - ## See also: - ## * `CopyFlag enum`_ - ## * `copyFile proc`_ - if dir.len == 0: # treating "" as "." is error prone - raise newException(ValueError, "dest is empty") - copyFile(source, dir / source.lastPathPart, options) - - -proc copyFileWithPermissions*(source, dest: string, - ignorePermissionErrors = true, - options = {cfSymlinkFollow}) {.noWeirdTarget.} = - ## Copies a file from `source` to `dest` preserving file permissions. - ## - ## On non-Windows OSes, `options` specify the way file is copied; by default, - ## if `source` is a symlink, copies the file symlink points to. `options` is - ## ignored on Windows: symlinks are skipped. - ## - ## This is a wrapper proc around `copyFile`_, - ## `getFilePermissions`_ and `setFilePermissions`_ - ## procs on non-Windows platforms. - ## - ## On Windows this proc is just a wrapper for `copyFile proc`_ since - ## that proc already copies attributes. - ## - ## On non-Windows systems permissions are copied after the file itself has - ## been copied, which won't happen atomically and could lead to a race - ## condition. If `ignorePermissionErrors` is true (default), errors while - ## reading/setting file attributes will be ignored, otherwise will raise - ## `OSError`. - ## - ## See also: - ## * `CopyFlag enum`_ - ## * `copyFile proc`_ - ## * `copyDir proc`_ - ## * `tryRemoveFile proc`_ - ## * `removeFile proc`_ - ## * `moveFile proc`_ - ## * `copyDirWithPermissions proc`_ - copyFile(source, dest, options) - when not defined(windows): - try: - setFilePermissions(dest, getFilePermissions(source), followSymlinks = - (cfSymlinkFollow in options)) - except: - if not ignorePermissionErrors: - raise - -when not declared(ENOENT) and not defined(windows): - when defined(nimscript): - when not defined(haiku): - const ENOENT = cint(2) # 2 on most systems including Solaris - else: - const ENOENT = cint(-2147459069) - else: - var ENOENT {.importc, header: "".}: cint - -when defined(windows) and not weirdTarget: - template deleteFile(file: untyped): untyped = deleteFileW(file) - template setFileAttributes(file, attrs: untyped): untyped = - setFileAttributesW(file, attrs) - -proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = - ## Removes the `file`. - ## - ## If this fails, returns `false`. This does not fail - ## if the file never existed in the first place. - ## - ## On Windows, ignores the read-only attribute. - ## - ## See also: - ## * `copyFile proc`_ - ## * `copyFileWithPermissions proc`_ - ## * `removeFile proc`_ - ## * `moveFile proc`_ - result = true - when defined(windows): - let f = newWideCString(file) - if deleteFile(f) == 0: - result = false - let err = getLastError() - if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND: - result = true - elif err == ERROR_ACCESS_DENIED and - setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and - deleteFile(f) != 0: - result = true - else: - if unlink(file) != 0'i32 and errno != ENOENT: - result = false - -proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = - ## Removes the `file`. - ## - ## If this fails, `OSError` is raised. This does not fail - ## if the file never existed in the first place. - ## - ## On Windows, ignores the read-only attribute. - ## - ## See also: - ## * `removeDir proc`_ - ## * `copyFile proc`_ - ## * `copyFileWithPermissions proc`_ - ## * `tryRemoveFile proc`_ - ## * `moveFile proc`_ - if not tryRemoveFile(file): - raiseOSError(osLastError(), file) - -proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", - tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], noWeirdTarget.} = - ## Moves a file from `source` to `dest`. - ## - ## Symlinks are not followed: if `source` is a symlink, it is itself moved, - ## not its target. - ## - ## If this fails, `OSError` is raised. - ## If `dest` already exists, it will be overwritten. - ## - ## Can be used to `rename files`:idx:. - ## - ## See also: - ## * `moveDir proc`_ - ## * `copyFile proc`_ - ## * `copyFileWithPermissions proc`_ - ## * `removeFile proc`_ - ## * `tryRemoveFile proc`_ - - if not tryMoveFSObject(source, dest, isDir = false): - when defined(windows): - doAssert false - else: - # Fallback to copy & del - copyFile(source, dest, {cfSymlinkAsIs}) - try: - removeFile(source) - except: - discard tryRemoveFile(dest) - raise diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim deleted file mode 100644 index 612003023..000000000 --- a/lib/std/private/ospaths2.nim +++ /dev/null @@ -1,1017 +0,0 @@ -include system/inclrtl -import std/private/since - -import strutils, pathnorm -import std/oserrors - -import oscommon -export ReadDirEffect, WriteDirEffect - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions, widestrs] - -## .. importdoc:: osappdirs.nim, osdirs.nim, osseps.nim, os.nim - -const weirdTarget = defined(nimscript) or defined(js) - -when weirdTarget: - discard -elif defined(windows): - import winlean -elif defined(posix): - import posix, system/ansi_c -else: - {.error: "OS module not ported to your operating system!".} - -when weirdTarget: - {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".} -else: - {.pragma: noWeirdTarget.} - -when defined(nimscript): - # for procs already defined in scriptconfig.nim - template noNimJs(body): untyped = discard -elif defined(js): - {.pragma: noNimJs, error: "this proc is not available on the js target".} -else: - {.pragma: noNimJs.} - - -proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.} - - -import std/private/osseps -export osseps - -proc absolutePathInternal(path: string): string {.gcsafe.} - -proc normalizePathEnd*(path: var string, trailingSep = false) = - ## Ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on - ## ``trailingSep``, and taking care of edge cases: it preservers whether - ## a path is absolute or relative, and makes sure trailing sep is `DirSep`, - ## not `AltSep`. Trailing `/.` are compressed, see examples. - if path.len == 0: return - var i = path.len - while i >= 1: - if path[i-1] in {DirSep, AltSep}: dec(i) - elif path[i-1] == '.' and i >= 2 and path[i-2] in {DirSep, AltSep}: dec(i) - else: break - if trailingSep: - # foo// => foo - path.setLen(i) - # foo => foo/ - path.add DirSep - elif i > 0: - # foo// => foo - path.setLen(i) - else: - # // => / (empty case was already taken care of) - path = $DirSep - -proc normalizePathEnd*(path: string, trailingSep = false): string = - ## outplace overload - runnableExamples: - when defined(posix): - assert normalizePathEnd("/lib//.//", trailingSep = true) == "/lib/" - assert normalizePathEnd("lib/./.", trailingSep = false) == "lib" - assert normalizePathEnd(".//./.", trailingSep = false) == "." - assert normalizePathEnd("", trailingSep = true) == "" # not / ! - assert normalizePathEnd("/", trailingSep = false) == "/" # not "" ! - result = path - result.normalizePathEnd(trailingSep) - -template endsWith(a: string, b: set[char]): bool = - a.len > 0 and a[^1] in b - -proc joinPathImpl(result: var string, state: var int, tail: string) = - let trailingSep = tail.endsWith({DirSep, AltSep}) or tail.len == 0 and result.endsWith({DirSep, AltSep}) - normalizePathEnd(result, trailingSep=false) - addNormalizePath(tail, result, state, DirSep) - normalizePathEnd(result, trailingSep=trailingSep) - -proc joinPath*(head, tail: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Joins two directory names to one. - ## - ## returns normalized path concatenation of `head` and `tail`, preserving - ## whether or not `tail` has a trailing slash (or, if tail if empty, whether - ## head has one). - ## - ## See also: - ## * `joinPath(parts: varargs[string]) proc`_ - ## * `/ proc`_ - ## * `splitPath proc`_ - ## * `uri.combine proc `_ - ## * `uri./ proc `_ - runnableExamples: - when defined(posix): - assert joinPath("usr", "lib") == "usr/lib" - assert joinPath("usr", "lib/") == "usr/lib/" - assert joinPath("usr", "") == "usr" - assert joinPath("usr/", "") == "usr/" - assert joinPath("", "") == "" - assert joinPath("", "lib") == "lib" - assert joinPath("", "/lib") == "/lib" - assert joinPath("usr/", "/lib") == "usr/lib" - assert joinPath("usr/lib", "../bin") == "usr/bin" - - result = newStringOfCap(head.len + tail.len) - var state = 0 - joinPathImpl(result, state, head) - joinPathImpl(result, state, tail) - when false: - if len(head) == 0: - result = tail - elif head[len(head)-1] in {DirSep, AltSep}: - if tail.len > 0 and tail[0] in {DirSep, AltSep}: - result = head & substr(tail, 1) - else: - result = head & tail - else: - if tail.len > 0 and tail[0] in {DirSep, AltSep}: - result = head & tail - else: - result = head & DirSep & tail - -proc joinPath*(parts: varargs[string]): string {.noSideEffect, - rtl, extern: "nos$1OpenArray".} = - ## The same as `joinPath(head, tail) proc`_, - ## but works with any number of directory parts. - ## - ## You need to pass at least one element or the proc - ## will assert in debug builds and crash on release builds. - ## - ## See also: - ## * `joinPath(head, tail) proc`_ - ## * `/ proc`_ - ## * `/../ proc`_ - ## * `splitPath proc`_ - runnableExamples: - when defined(posix): - assert joinPath("a") == "a" - assert joinPath("a", "b", "c") == "a/b/c" - assert joinPath("usr/lib", "../../var", "log") == "var/log" - - var estimatedLen = 0 - for p in parts: estimatedLen += p.len - result = newStringOfCap(estimatedLen) - var state = 0 - for i in 0..high(parts): - joinPathImpl(result, state, parts[i]) - -proc `/`*(head, tail: string): string {.noSideEffect, inline.} = - ## The same as `joinPath(head, tail) proc`_. - ## - ## See also: - ## * `/../ proc`_ - ## * `joinPath(head, tail) proc`_ - ## * `joinPath(parts: varargs[string]) proc`_ - ## * `splitPath proc`_ - ## * `uri.combine proc `_ - ## * `uri./ proc `_ - runnableExamples: - when defined(posix): - assert "usr" / "" == "usr" - assert "" / "lib" == "lib" - assert "" / "/lib" == "/lib" - assert "usr/" / "/lib/" == "usr/lib/" - assert "usr" / "lib" / "../bin" == "usr/bin" - - 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 - ## ``head / tail == path`` (except for edge cases like "/usr"). - ## - ## See also: - ## * `joinPath(head, tail) proc`_ - ## * `joinPath(parts: varargs[string]) proc`_ - ## * `/ proc`_ - ## * `/../ proc`_ - ## * `relativePath proc`_ - runnableExamples: - assert splitPath("usr/local/bin") == ("usr/local", "bin") - assert splitPath("usr/local/bin/") == ("usr/local/bin", "") - assert splitPath("/bin/") == ("/bin", "") - when (NimMajor, NimMinor) <= (1, 0): - assert splitPath("/bin") == ("", "bin") - else: - assert splitPath("/bin") == ("/", "bin") - 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, stop): - if path[i] in {DirSep, AltSep}: - sepPos = i - break - if sepPos >= 0: - result.head = substr(path, 0, - when (NimMajor, NimMinor) <= (1, 0): - sepPos-1 - else: - if likely(sepPos >= 1): sepPos-1 else: 0 - ) - result.tail = substr(path, sepPos+1) - else: - 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. - ## - ## On Windows, network paths are considered absolute too. - runnableExamples: - assert not "".isAbsolute - assert not ".".isAbsolute - when defined(posix): - assert "/".isAbsolute - assert not "a/".isAbsolute - assert "/a/".isAbsolute - - if len(path) == 0: return false - - when doslikeFileSystem: - var len = len(path) - result = (path[0] in {'/', '\\'}) or - (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':') - elif defined(macos): - # according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path - result = path[0] != ':' - elif defined(RISCOS): - result = path[0] == '$' - elif defined(posix) or defined(js): - # `or defined(js)` wouldn't be needed pending https://github.com/nim-lang/Nim/issues/13469 - # This works around the problem for posix, but Windows is still broken with nim js -d:nodejs - result = path[0] == '/' - else: - doAssert false # if ever hits here, adapt as needed - -when FileSystemCaseSensitive: - template `!=?`(a, b: char): bool = a != b -else: - template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b) - -when doslikeFileSystem: - proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: [].} = - ## An absolute path from the root of the current drive (e.g. "\foo") - path.len > 0 and - (path[0] == AltSep or - (path[0] == DirSep and - (path.len == 1 or path[1] notin {DirSep, AltSep, ':'}))) - - proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: [].} = - ## Return true if path1 and path2 have a same root. - ## - ## Detail of Windows path formats: - ## https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats - - assert(isAbsolute(path1)) - assert(isAbsolute(path2)) - - if isAbsFromCurrentDrive(path1) and isAbsFromCurrentDrive(path2): - result = true - elif cmpIgnoreCase(splitDrive(path1).drive, splitDrive(path2).drive) == 0: - result = true - else: - result = false - -proc relativePath*(path, base: string, sep = DirSep): string {. - rtl, extern: "nos$1".} = - ## Converts `path` to a path relative to `base`. - ## - ## The `sep` (default: DirSep_) is used for the path normalizations, - ## this can be useful to ensure the relative path only contains `'/'` - ## so that it can be used for URL constructions. - ## - ## On Windows, if a root of `path` and a root of `base` are different, - ## returns `path` as is because it is impossible to make a relative path. - ## That means an absolute path can be returned. - ## - ## See also: - ## * `splitPath proc`_ - ## * `parentDir proc`_ - ## * `tailDir proc`_ - 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" - 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" - assert relativePath("foo", "foo", '/') == "." - - if path.len == 0: return "" - var base = if base == ".": "" else: base - var path = path - path.normalizePathAux - base.normalizePathAux - let a1 = isAbsolute(path) - let a2 = isAbsolute(base) - if a1 and not a2: - base = absolutePathInternal(base) - elif a2 and not a1: - path = absolutePathInternal(path) - - when doslikeFileSystem: - if isAbsolute(path) and isAbsolute(base): - if not sameRoot(path, base): - return path - - var f = default PathIter - var b = default PathIter - var ff = (0, -1) - var bb = (0, -1) # (int, int) - result = newStringOfCap(path.len) - # skip the common prefix: - while f.hasNext(path) and b.hasNext(base): - ff = next(f, path) - bb = next(b, base) - let diff = ff[1] - ff[0] - if diff != bb[1] - bb[0]: break - var same = true - for i in 0..diff: - if path[i + ff[0]] !=? base[i + bb[0]]: - same = false - break - if not same: break - ff = (0, -1) - bb = (0, -1) - # for i in 0..diff: - # result.add base[i + bb[0]] - - # /foo/bar/xxx/ -- base - # /foo/bar/baz -- path path - # ../baz - # every directory that is in 'base', needs to add '..' - while true: - if bb[1] >= bb[0]: - if result.len > 0 and result[^1] != sep: - result.add sep - result.add ".." - if not b.hasNext(base): break - bb = b.next(base) - - # add the rest of 'path': - while true: - if ff[1] >= ff[0]: - if result.len > 0 and result[^1] != sep: - result.add sep - for i in 0..ff[1] - ff[0]: - result.add path[i + ff[0]] - if not f.hasNext(path): break - ff = f.next(path) - - when not defined(nimOldRelativePathBehavior): - if result.len == 0: result.add "." - -proc isRelativeTo*(path: string, base: string): bool {.since: (1, 1).} = - ## Returns true if `path` is relative to `base`. - runnableExamples: - doAssert isRelativeTo("./foo//bar", "foo") - doAssert isRelativeTo("foo/bar", ".") - doAssert isRelativeTo("/foo/bar.nim", "/foo/bar.nim") - doAssert not isRelativeTo("foo/bar.nims", "foo/bar.nim") - let path = path.normalizePath - let base = base.normalizePath - let ret = relativePath(path, base) - result = path.len > 0 and not ret.startsWith ".." - -proc parentDirPos(path: string): int = - var q = 1 - if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2 - for i in countdown(len(path)-q, 0): - if path[i] in {DirSep, AltSep}: return i - result = -1 - -proc parentDir*(path: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Returns the parent directory of `path`. - ## - ## This is similar to ``splitPath(path).head`` when ``path`` doesn't end - ## in a dir separator, but also takes care of path normalizations. - ## The remainder can be obtained with `lastPathPart(path) proc`_. - ## - ## See also: - ## * `relativePath proc`_ - ## * `splitPath proc`_ - ## * `tailDir proc`_ - ## * `parentDirs iterator`_ - runnableExamples: - assert parentDir("") == "" - when defined(posix): - assert parentDir("/usr/local/bin") == "/usr/local" - assert parentDir("foo/bar//") == "foo" - assert parentDir("//foo//bar//.") == "/foo" - assert parentDir("./foo") == "." - assert parentDir("/./foo//./") == "/" - 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) - normalizePathEnd(result) - elif result == ".." or result == "." or result.len == 0 or result[^1] in {DirSep, AltSep}: - # `.` => `..` and .. => `../..`(etc) would be a sensible alternative - # `/` => `/` (as done with splitFile) would be a sensible alternative - 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".} = - ## Returns the tail part of `path`. - ## - ## See also: - ## * `relativePath proc`_ - ## * `splitPath proc`_ - ## * `parentDir proc`_ - runnableExamples: - assert tailDir("/bin") == "bin" - assert tailDir("bin") == "" - assert tailDir("bin/") == "" - assert tailDir("/usr/local/bin") == "usr/local/bin" - assert tailDir("//usr//local//bin//") == "usr//local//bin//" - assert tailDir("./usr/local/bin") == "usr/local/bin" - 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 - return substr(path, i) - inc i - result = "" - -proc isRootDir*(path: string): bool {. - noSideEffect, rtl, extern: "nos$1".} = - ## Checks whether a given `path` is a root directory. - runnableExamples: - assert isRootDir("") - assert isRootDir(".") - assert isRootDir("/") - assert isRootDir("a") - 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 = - ## Walks over all parent directories of a given `path`. - ## - ## If `fromRoot` is true (default: false), the traversal will start from - ## the file system root directory. - ## If `inclusive` is true (default), the original argument will be included - ## in the traversal. - ## - ## Relative paths won't be expanded by this iterator. Instead, it will traverse - ## only the directories appearing in the relative path. - ## - ## See also: - ## * `parentDir proc`_ - ## - runnableExamples: - let g = "a/b/c" - - for p in g.parentDirs: - echo p - # a/b/c - # a/b - # a - - for p in g.parentDirs(fromRoot=true): - echo p - # a/ - # a/b/ - # a/b/c - - for p in g.parentDirs(inclusive=false): - echo p - # a/b - # a - - if not fromRoot: - var current = path - if inclusive: yield path - while true: - if current.isRootDir: break - current = current.parentDir - yield current - else: - 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}): - yield path.substr(0, i) - - if inclusive: yield path - -proc `/../`*(head, tail: string): string {.noSideEffect.} = - ## The same as ``parentDir(head) / tail``, unless there is no parent - ## directory. Then ``head / tail`` is performed instead. - ## - ## See also: - ## * `/ proc`_ - ## * `parentDir proc`_ - runnableExamples: - when defined(posix): - 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 - else: result = ExtSep & ext - -proc searchExtPos*(path: string): int = - ## Returns index of the `'.'` char in `path` if it signifies the beginning - ## of the file extension. Returns -1 otherwise. - ## - ## See also: - ## * `splitFile proc`_ - ## * `extractFilename proc`_ - ## * `lastPathPart proc`_ - ## * `changeFileExt proc`_ - ## * `addFileExt proc`_ - runnableExamples: - assert searchExtPos("a/b/c") == -1 - assert searchExtPos("c.nim") == 1 - assert searchExtPos("a/b/c.nim") == 5 - assert searchExtPos("a.b.c.nim") == 5 - - # BUGFIX: do not search until 0! .DS_Store is no file extension! - result = -1 - for i in countdown(len(path)-1, 1): - if path[i] == ExtSep: - result = i - break - elif path[i] in {DirSep, AltSep}: - break # do not skip over path - -proc splitFile*(path: string): tuple[dir, name, ext: string] {. - noSideEffect, rtl, extern: "nos$1".} = - ## Splits a filename into `(dir, name, extension)` tuple. - ## - ## `dir` does not end in DirSep_ unless it's `/`. - ## `extension` includes the leading dot. - ## - ## If `path` has no extension, `ext` is the empty string. - ## If `path` has no directory component, `dir` is the empty string. - ## If `path` has no filename component, `name` and `ext` are empty strings. - ## - ## See also: - ## * `searchExtPos proc`_ - ## * `extractFilename proc`_ - ## * `lastPathPart proc`_ - ## * `changeFileExt proc`_ - ## * `addFileExt proc`_ - runnableExamples: - var (dir, name, ext) = splitFile("usr/local/nimc.html") - assert dir == "usr/local" - assert name == "nimc" - assert ext == ".html" - (dir, name, ext) = splitFile("/usr/local/os") - assert dir == "/usr/local" - assert name == "os" - assert ext == "" - (dir, name, ext) = splitFile("/usr/local/") - assert dir == "/usr/local" - assert name == "" - assert ext == "" - (dir, name, ext) = splitFile("/tmp.txt") - assert dir == "/" - assert name == "tmp" - assert ext == ".txt" - - var namePos = 0 - var dotPos = 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) - namePos = i + 1 - if dotPos > i: - result.name = substr(path, namePos, dotPos - 1) - result.ext = substr(path, dotPos) - else: - result.name = substr(path, namePos) - break - elif path[i] == ExtSep and i > 0 and i < len(path) - 1 and - path[i - 1] notin {DirSep, AltSep} and - path[i + 1] != ExtSep and dotPos == 0: - dotPos = i - -proc extractFilename*(path: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Extracts the filename of a given `path`. - ## - ## This is the same as ``name & ext`` from `splitFile(path) proc`_. - ## - ## See also: - ## * `searchExtPos proc`_ - ## * `splitFile proc`_ - ## * `lastPathPart proc`_ - ## * `changeFileExt proc`_ - ## * `addFileExt proc`_ - runnableExamples: - assert extractFilename("foo/bar/") == "" - assert extractFilename("foo/bar") == "bar" - assert extractFilename("foo/bar.baz") == "bar.baz" - - if path.len == 0 or path[path.len-1] in {DirSep, AltSep}: - result = "" - else: - result = splitPath(path).tail - -proc lastPathPart*(path: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Like `extractFilename proc`_, but ignores - ## trailing dir separator; aka: `baseName`:idx: in some other languages. - ## - ## See also: - ## * `searchExtPos proc`_ - ## * `splitFile proc`_ - ## * `extractFilename proc`_ - ## * `changeFileExt proc`_ - ## * `addFileExt proc`_ - runnableExamples: - assert lastPathPart("foo/bar/") == "bar" - assert lastPathPart("foo/bar") == "bar" - - let path = path.normalizePathEnd(trailingSep = false) - result = extractFilename(path) - -proc changeFileExt*(filename, ext: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Changes the file extension to `ext`. - ## - ## If the `filename` has no extension, `ext` will be added. - ## If `ext` == "" then any extension is removed. - ## - ## `Ext` should be given without the leading `'.'`, because some - ## filesystems may use a different character. (Although I know - ## of none such beast.) - ## - ## See also: - ## * `searchExtPos proc`_ - ## * `splitFile proc`_ - ## * `extractFilename proc`_ - ## * `lastPathPart proc`_ - ## * `addFileExt proc`_ - runnableExamples: - assert changeFileExt("foo.bar", "baz") == "foo.baz" - assert changeFileExt("foo.bar", "") == "foo" - assert changeFileExt("foo", "baz") == "foo.baz" - - var extPos = searchExtPos(filename) - if extPos < 0: result = filename & normExt(ext) - else: result = substr(filename, 0, extPos-1) & normExt(ext) - -proc addFileExt*(filename, ext: string): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Adds the file extension `ext` to `filename`, unless - ## `filename` already has an extension. - ## - ## `Ext` should be given without the leading `'.'`, because some - ## filesystems may use a different character. - ## (Although I know of none such beast.) - ## - ## See also: - ## * `searchExtPos proc`_ - ## * `splitFile proc`_ - ## * `extractFilename proc`_ - ## * `lastPathPart proc`_ - ## * `changeFileExt proc`_ - runnableExamples: - assert addFileExt("foo.bar", "baz") == "foo.bar" - assert addFileExt("foo.bar", "") == "foo.bar" - assert addFileExt("foo", "baz") == "foo.baz" - - var extPos = searchExtPos(filename) - if extPos < 0: result = filename & normExt(ext) - else: result = filename - -proc cmpPaths*(pathA, pathB: string): int {. - noSideEffect, rtl, extern: "nos$1".} = - ## Compares two paths. - ## - ## On a case-sensitive filesystem this is done - ## case-sensitively otherwise case-insensitively. Returns: - ## - ## | 0 if pathA == pathB - ## | < 0 if pathA < pathB - ## | > 0 if pathA > pathB - runnableExamples: - when defined(macosx): - assert cmpPaths("foo", "Foo") == 0 - elif defined(posix): - assert cmpPaths("foo", "Foo") > 0 - - let a = normalizePath(pathA) - let b = normalizePath(pathB) - if FileSystemCaseSensitive: - result = cmp(a, b) - else: - when defined(nimscript): - result = cmpic(a, b) - elif defined(nimdoc): discard - else: - result = cmpIgnoreCase(a, b) - -proc unixToNativePath*(path: string, drive=""): string {. - noSideEffect, rtl, extern: "nos$1".} = - ## Converts an UNIX-like path to a native one. - ## - ## On an UNIX system this does nothing. Else it converts - ## `'/'`, `'.'`, `'..'` to the appropriate things. - ## - ## On systems with a concept of "drives", `drive` is used to determine - ## which drive label to use during absolute path conversion. - ## `drive` defaults to the drive of the current working directory, and is - ## ignored on systems that do not have a concept of "drives". - when defined(unix): - result = path - else: - if path.len == 0: return "" - - var start: int - if path[0] == '/': - # an absolute path - when doslikeFileSystem: - if drive != "": - result = drive & ":" & DirSep - else: - result = $DirSep - elif defined(macos): - result = "" # must not start with ':' - else: - result = $DirSep - start = 1 - elif path[0] == '.' and (path.len == 1 or path[1] == '/'): - # current directory - result = $CurDir - start = when doslikeFileSystem: 1 else: 2 - else: - result = "" - start = 0 - - var i = start - while i < len(path): # ../../../ --> :::: - if i+2 < path.len and path[i] == '.' and path[i+1] == '.' and path[i+2] == '/': - # parent directory - when defined(macos): - if result[high(result)] == ':': - add result, ':' - else: - add result, ParDir - else: - add result, ParDir & DirSep - inc(i, 3) - elif path[i] == '/': - add result, DirSep - inc(i) - else: - add result, path[i] - inc(i) - - -when not defined(nimscript): - proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = - ## Returns the `current working directory`:idx: i.e. where the built - ## binary is run. - ## - ## So the path returned by this proc is determined at run time. - ## - ## See also: - ## * `getHomeDir proc`_ - ## * `getConfigDir proc`_ - ## * `getTempDir proc`_ - ## * `setCurrentDir proc`_ - ## * `currentSourcePath template `_ - ## * `getProjectPath proc `_ - when defined(nodejs): - var ret: cstring - {.emit: "`ret` = process.cwd();".} - return $ret - elif defined(js): - doAssert false, "use -d:nodejs to have `getCurrentDir` defined" - elif defined(windows): - var bufsize = MAX_PATH.int32 - var res = newWideCString("", bufsize) - while true: - var L = getCurrentDirectoryW(bufsize, res) - if L == 0'i32: - raiseOSError(osLastError()) - elif L > bufsize: - res = newWideCString("", L) - bufsize = L - else: - result = res$L - break - else: - var bufsize = 1024 # should be enough - result = newString(bufsize) - while true: - if getcwd(result.cstring, bufsize) != nil: - setLen(result, c_strlen(result.cstring)) - break - else: - let err = osLastError() - if err.int32 == ERANGE: - bufsize = bufsize shl 1 - doAssert(bufsize >= 0) - result = newString(bufsize) - else: - raiseOSError(osLastError()) - -proc absolutePath*(path: string, root = getCurrentDir()): string = - ## Returns the absolute path of `path`, rooted at `root` (which must be absolute; - ## default: current directory). - ## If `path` is absolute, return it, ignoring `root`. - ## - ## See also: - ## * `normalizedPath proc`_ - ## * `normalizePath proc`_ - runnableExamples: - assert absolutePath("a") == getCurrentDir() / "a" - - if isAbsolute(path): path - else: - if not root.isAbsolute: - raise newException(ValueError, "The specified root is not absolute: " & root) - joinPath(root, path) - -proc absolutePathInternal(path: string): string = - absolutePath(path, getCurrentDir()) - - -proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} = - ## Normalize a path. - ## - ## Consecutive directory separators are collapsed, including an initial double slash. - ## - ## On relative paths, double dot (`..`) sequences are collapsed if possible. - ## On absolute paths they are always collapsed. - ## - ## .. warning:: URL-encoded and Unicode attempts at directory traversal are not detected. - ## Triple dot is not handled. - ## - ## See also: - ## * `absolutePath proc`_ - ## * `normalizedPath proc`_ for outplace version - ## * `normalizeExe proc`_ - runnableExamples: - when defined(posix): - var a = "a///b//..//c///d" - a.normalizePath() - assert a == "a/c/d" - - path = pathnorm.normalizePath(path) - when false: - let isAbs = isAbsolute(path) - var stack: seq[string] = @[] - for p in split(path, {DirSep}): - case p - of "", ".": - continue - of "..": - if stack.len == 0: - if isAbs: - discard # collapse all double dots on absoluta paths - else: - stack.add(p) - elif stack[^1] == "..": - stack.add(p) - else: - discard stack.pop() - else: - stack.add(p) - - if isAbs: - path = DirSep & join(stack, $DirSep) - elif stack.len > 0: - path = join(stack, $DirSep) - else: - path = "." - -proc normalizePathAux(path: var string) = normalizePath(path) - -proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [].} = - ## Returns a normalized path for the current OS. - ## - ## See also: - ## * `absolutePath proc`_ - ## * `normalizePath proc`_ for the in-place version - runnableExamples: - when defined(posix): - assert normalizedPath("a///b//..//c///d") == "a/c/d" - result = pathnorm.normalizePath(path) - -proc normalizeExe*(file: var string) {.since: (1, 3, 5).} = - ## on posix, prepends `./` if `file` doesn't contain `/` and is not `"", ".", ".."`. - runnableExamples: - import std/sugar - when defined(posix): - doAssert "foo".dup(normalizeExe) == "./foo" - doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" - doAssert "".dup(normalizeExe) == "" - when defined(posix): - if file.len > 0 and DirSep notin file and file != "." and file != "..": - file = "./" & file - -proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", - tags: [ReadDirEffect], noWeirdTarget.} = - ## Returns true if both pathname arguments refer to the same physical - ## file or directory. - ## - ## Raises `OSError` if any of the files does not - ## exist or information about it can not be obtained. - ## - ## This proc will return true if given two alternative hard-linked or - ## sym-linked paths to the same file or directory. - ## - ## See also: - ## * `sameFileContent proc`_ - when defined(windows): - var success = true - var f1 = openHandle(path1) - var f2 = openHandle(path2) - - var lastErr: OSErrorCode - if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE: - var fi1, fi2: BY_HANDLE_FILE_INFORMATION - - if getFileInformationByHandle(f1, addr(fi1)) != 0 and - getFileInformationByHandle(f2, addr(fi2)) != 0: - result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and - fi1.nFileIndexHigh == fi2.nFileIndexHigh and - fi1.nFileIndexLow == fi2.nFileIndexLow - else: - lastErr = osLastError() - success = false - else: - lastErr = osLastError() - success = false - - discard closeHandle(f1) - discard closeHandle(f2) - - if not success: raiseOSError(lastErr, $(path1, path2)) - else: - var a, b: Stat - if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32: - raiseOSError(osLastError(), $(path1, path2)) - else: - result = a.st_dev == b.st_dev and a.st_ino == b.st_ino diff --git a/lib/std/private/ossymlinks.nim b/lib/std/private/ossymlinks.nim deleted file mode 100644 index 18737b8b5..000000000 --- a/lib/std/private/ossymlinks.nim +++ /dev/null @@ -1,76 +0,0 @@ -include system/inclrtl -import std/oserrors - -import oscommon -export symlinkExists - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions, widestrs] - -when weirdTarget: - discard -elif defined(windows): - import winlean, times -elif defined(posix): - import posix -else: - {.error: "OS module not ported to your operating system!".} - - -when weirdTarget: - {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".} -else: - {.pragma: noWeirdTarget.} - - -when defined(nimscript): - # for procs already defined in scriptconfig.nim - template noNimJs(body): untyped = discard -elif defined(js): - {.pragma: noNimJs, error: "this proc is not available on the js target".} -else: - {.pragma: noNimJs.} - -## .. importdoc:: os.nim - -proc createSymlink*(src, dest: string) {.noWeirdTarget.} = - ## Create a symbolic link at `dest` which points to the item specified - ## 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 developer mode enabled. - ## - ## See also: - ## * `createHardlink proc`_ - ## * `expandSymlink proc`_ - - when defined(windows): - const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 2 - # allows anyone with developer mode on to create a link - let flag = dirExists(src).int32 or SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE - var wSrc = newWideCString(src) - var wDst = newWideCString(dest) - if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0: - raiseOSError(osLastError(), $(src, dest)) - else: - if symlink(src, dest) != 0: - raiseOSError(osLastError(), $(src, dest)) - -proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = - ## Returns a string representing the path to which the symbolic link points. - ## - ## On Windows this is a noop, `symlinkPath` is simply returned. - ## - ## See also: - ## * `createSymlink proc`_ - when defined(windows) or defined(nintendoswitch): - result = symlinkPath - else: - result = newString(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.cstring, len) - setLen(result, len) diff --git a/lib/std/private/schubfach.nim b/lib/std/private/schubfach.nim deleted file mode 100644 index dad8363ba..000000000 --- a/lib/std/private/schubfach.nim +++ /dev/null @@ -1,436 +0,0 @@ -## Copyright 2020 Alexander Bolz -## -## Distributed under the Boost Software License, Version 1.0. -## (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) - -# -------------------------------------------------------------------------------------------------- -## This file contains an implementation of the Schubfach algorithm as described in -## -## \[1] Raffaello Giulietti, "The Schubfach way to render doubles", -## https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN -# -------------------------------------------------------------------------------------------------- - -import std/private/digitsutils - -when defined(nimPreviewSlimSystem): - import std/assertions - - -template sf_Assert(x: untyped): untyped = - assert(x) - -# ================================================================================================== -# -# ================================================================================================== - -type - ValueType = float32 - BitsType = uint32 - Single {.bycopy.} = object - bits: BitsType - -const - significandSize: int32 = 24 - MaxExponent = 128 - exponentBias: int32 = MaxExponent - 1 + (significandSize - 1) - maxIeeeExponent: BitsType = BitsType(2 * MaxExponent - 1) - hiddenBit: BitsType = BitsType(1) shl (significandSize - 1) - significandMask: BitsType = hiddenBit - 1 - exponentMask: BitsType = maxIeeeExponent shl (significandSize - 1) - signMask: BitsType = not (not BitsType(0) shr 1) - -proc constructSingle(bits: BitsType): Single {.constructor.} = - result.bits = bits - -proc constructSingle(value: ValueType): Single {.constructor.} = - result.bits = cast[typeof(result.bits)](value) - -proc physicalSignificand(this: Single): BitsType {.noSideEffect.} = - return this.bits and significandMask - -proc physicalExponent(this: Single): BitsType {.noSideEffect.} = - return (this.bits and exponentMask) shr (significandSize - 1) - -proc isFinite(this: Single): bool {.noSideEffect.} = - return (this.bits and exponentMask) != exponentMask - -proc isInf(this: Single): bool {.noSideEffect.} = - return (this.bits and exponentMask) == exponentMask and - (this.bits and significandMask) == 0 - -proc isNaN(this: Single): bool {.noSideEffect.} = - return (this.bits and exponentMask) == exponentMask and - (this.bits and significandMask) != 0 - -proc isZero(this: Single): bool {.noSideEffect.} = - return (this.bits and not signMask) == 0 - -proc signBit(this: Single): int {.noSideEffect.} = - return int((this.bits and signMask) != 0) - -# ================================================================================================== -## Returns floor(x / 2^n). -## -## Technically, right-shift of negative integers is implementation defined... -## Should easily be optimized into SAR (or equivalent) instruction. - -proc floorDivPow2(x: int32; n: int32): int32 {.inline.} = - return x shr n - -## Returns floor(log_10(2^e)) -## ```c -## static inline int32_t FloorLog10Pow2(int32_t e) -## { -## SF_ASSERT(e >= -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.} = - sf_Assert(e >= -1233) - sf_Assert(e <= 1233) - return floorDivPow2(e * 1741647, 19) - -const - kMin: int32 = -31 - kMax: int32 = 45 - g: array[kMax - kMin + 1, uint64] = [0x81CEB32C4B43FCF5'u64, 0xA2425FF75E14FC32'u64, - 0xCAD2F7F5359A3B3F'u64, 0xFD87B5F28300CA0E'u64, 0x9E74D1B791E07E49'u64, - 0xC612062576589DDB'u64, 0xF79687AED3EEC552'u64, 0x9ABE14CD44753B53'u64, - 0xC16D9A0095928A28'u64, 0xF1C90080BAF72CB2'u64, 0x971DA05074DA7BEF'u64, - 0xBCE5086492111AEB'u64, 0xEC1E4A7DB69561A6'u64, 0x9392EE8E921D5D08'u64, - 0xB877AA3236A4B44A'u64, 0xE69594BEC44DE15C'u64, 0x901D7CF73AB0ACDA'u64, - 0xB424DC35095CD810'u64, 0xE12E13424BB40E14'u64, 0x8CBCCC096F5088CC'u64, - 0xAFEBFF0BCB24AAFF'u64, 0xDBE6FECEBDEDD5BF'u64, 0x89705F4136B4A598'u64, - 0xABCC77118461CEFD'u64, 0xD6BF94D5E57A42BD'u64, 0x8637BD05AF6C69B6'u64, - 0xA7C5AC471B478424'u64, 0xD1B71758E219652C'u64, 0x83126E978D4FDF3C'u64, - 0xA3D70A3D70A3D70B'u64, 0xCCCCCCCCCCCCCCCD'u64, 0x8000000000000000'u64, - 0xA000000000000000'u64, 0xC800000000000000'u64, 0xFA00000000000000'u64, - 0x9C40000000000000'u64, 0xC350000000000000'u64, 0xF424000000000000'u64, - 0x9896800000000000'u64, 0xBEBC200000000000'u64, 0xEE6B280000000000'u64, - 0x9502F90000000000'u64, 0xBA43B74000000000'u64, 0xE8D4A51000000000'u64, - 0x9184E72A00000000'u64, 0xB5E620F480000000'u64, 0xE35FA931A0000000'u64, - 0x8E1BC9BF04000000'u64, 0xB1A2BC2EC5000000'u64, 0xDE0B6B3A76400000'u64, - 0x8AC7230489E80000'u64, 0xAD78EBC5AC620000'u64, 0xD8D726B7177A8000'u64, - 0x878678326EAC9000'u64, 0xA968163F0A57B400'u64, 0xD3C21BCECCEDA100'u64, - 0x84595161401484A0'u64, 0xA56FA5B99019A5C8'u64, 0xCECB8F27F4200F3A'u64, - 0x813F3978F8940985'u64, 0xA18F07D736B90BE6'u64, 0xC9F2C9CD04674EDF'u64, - 0xFC6F7C4045812297'u64, 0x9DC5ADA82B70B59E'u64, 0xC5371912364CE306'u64, - 0xF684DF56C3E01BC7'u64, 0x9A130B963A6C115D'u64, 0xC097CE7BC90715B4'u64, - 0xF0BDC21ABB48DB21'u64, 0x96769950B50D88F5'u64, 0xBC143FA4E250EB32'u64, - 0xEB194F8E1AE525FE'u64, 0x92EFD1B8D0CF37BF'u64, 0xB7ABC627050305AE'u64, - 0xE596B7B0C643C71A'u64, 0x8F7E32CE7BEA5C70'u64, 0xB35DBF821AE4F38C'u64] - -proc computePow10Single(k: int32): uint64 {.inline.} = - ## There are unique beta and r such that 10^k = beta 2^r and - ## 2^63 <= beta < 2^64, namely r = floor(log_2 10^k) - 63 and - ## beta = 2^-r 10^k. - ## Let g = ceil(beta), so (g-1) 2^r < 10^k <= g 2^r, with the latter - ## value being a pretty good overestimate for 10^k. - ## NB: Since for all the required exponents k, we have g < 2^64, - ## all constants can be stored in 128-bit integers. - sf_Assert(k >= kMin) - sf_Assert(k <= kMax) - return g[k - kMin] - -proc lo32(x: uint64): uint32 {.inline.} = - return cast[uint32](x) - -proc hi32(x: uint64): uint32 {.inline.} = - return cast[uint32](x shr 32) - -when defined(sizeof_Int128): - proc roundToOdd(g: uint64; cp: uint32): uint32 {.inline.} = - let p: uint128 = uint128(g) * cp - let y1: uint32 = lo32(cast[uint64](p shr 64)) - let y0: uint32 = hi32(cast[uint64](p)) - return y1 or uint32(y0 > 1) - -elif defined(vcc) and defined(cpu64): - proc umul128(x, y: uint64, z: ptr uint64): uint64 {.importc: "_umul128", header: "".} - proc roundToOdd(g: uint64; cpHi: uint32): uint32 {.inline.} = - var p1: uint64 = 0 - var p0: uint64 = umul128(g, cpHi, addr(p1)) - let y1: uint32 = lo32(p1) - let y0: uint32 = hi32(p0) - return y1 or uint32(y0 > 1) - -else: - proc roundToOdd(g: uint64; cp: uint32): uint32 {.inline.} = - let b01: uint64 = uint64(lo32(g)) * cp - let b11: uint64 = uint64(hi32(g)) * cp - let hi: uint64 = b11 + hi32(b01) - let y1: uint32 = hi32(hi) - let y0: uint32 = lo32(hi) - return y1 or uint32(y0 > 1) - -## Returns whether value is divisible by 2^e2 - -proc multipleOfPow2(value: uint32; e2: int32): bool {.inline.} = - sf_Assert(e2 >= 0) - sf_Assert(e2 <= 31) - return (value and ((uint32(1) shl e2) - 1)) == 0 - -type - FloatingDecimal32 {.bycopy.} = object - digits: uint32 ## num_digits <= 9 - exponent: int32 - -proc toDecimal32(ieeeSignificand: uint32; ieeeExponent: uint32): FloatingDecimal32 {. - inline.} = - var c: uint32 - var q: int32 - if ieeeExponent != 0: - c = hiddenBit or ieeeSignificand - q = cast[int32](ieeeExponent) - exponentBias - if 0 <= -q and -q < significandSize and multipleOfPow2(c, -q): - return FloatingDecimal32(digits: c shr -q, exponent: 0'i32) - else: - c = ieeeSignificand - q = 1 - exponentBias - let isEven: bool = (c mod 2 == 0) - let lowerBoundaryIsCloser: bool = (ieeeSignificand == 0 and ieeeExponent > 1) - ## const int32_t qb = q - 2; - let cbl: uint32 = 4 * c - 2 + uint32(lowerBoundaryIsCloser) - let cb: uint32 = 4 * c - let cbr: uint32 = 4 * c + 2 - ## (q * 1262611 ) >> 22 == floor(log_10( 2^q)) - ## (q * 1262611 - 524031) >> 22 == floor(log_10(3/4 2^q)) - sf_Assert(q >= -1500) - sf_Assert(q <= 1500) - let k: int32 = floorDivPow2(q * 1262611 - (if lowerBoundaryIsCloser: 524031 else: 0), 22) - let h: int32 = q + floorLog2Pow10(-k) + 1 - sf_Assert(h >= 1) - sf_Assert(h <= 4) - let pow10: uint64 = computePow10Single(-k) - let vbl: uint32 = roundToOdd(pow10, cbl shl h) - let vb: uint32 = roundToOdd(pow10, cb shl h) - let vbr: uint32 = roundToOdd(pow10, cbr shl h) - let lower: uint32 = vbl + uint32(not isEven) - let upper: uint32 = vbr - uint32(not isEven) - ## See Figure 4 in [1]. - ## And the modifications in Figure 6. - let s: uint32 = vb div 4 - ## NB: 4 * s == vb & ~3 == vb & -4 - if s >= 10: - let sp: uint32 = s div 10 - ## = vb / 40 - let upInside: bool = lower <= 40 * sp - let wpInside: bool = 40 * sp + 40 <= upper - ## if (up_inside || wp_inside) // NB: At most one of u' and w' is in R_v. - if upInside != wpInside: - return FloatingDecimal32(digits: sp + uint32(wpInside), exponent: k + 1) - let uInside: bool = lower <= 4 * s - let wInside: bool = 4 * s + 4 <= upper - if uInside != wInside: - return FloatingDecimal32(digits: s + uint32(wInside), exponent: k) - let mid: uint32 = 4 * s + 2 - ## = 2(s + t) - let roundUp: bool = vb > mid or (vb == mid and (s and 1) != 0) - return FloatingDecimal32(digits: s + uint32(roundUp), exponent: k) - -## ================================================================================================== -## ToChars -## ================================================================================================== - -proc printDecimalDigitsBackwards[T: Ordinal](buf: var openArray[char]; pos: T; output: uint32): int32 {.inline.} = - var output = output - var pos = pos - var tz: int32 = 0 - ## number of trailing zeros removed. - var nd: int32 = 0 - ## number of decimal digits processed. - ## At most 9 digits remaining - if output >= 10000: - let q: uint32 = output div 10000 - let r: uint32 = output mod 10000 - output = q - dec(pos, 4) - if r != 0: - let rH: uint32 = r div 100 - let rL: uint32 = r mod 100 - utoa2Digits(buf, pos, rH) - utoa2Digits(buf, pos + 2, rL) - tz = trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0) - else: - tz = 4 - nd = 4 - if output >= 100: - let q: uint32 = output div 100 - let r: uint32 = output mod 100 - output = q - dec(pos, 2) - utoa2Digits(buf, pos, r) - if tz == nd: - inc(tz, trailingZeros2Digits(r)) - inc(nd, 2) - if output >= 100: - let q2: uint32 = output div 100 - let r2: uint32 = output mod 100 - output = q2 - dec(pos, 2) - utoa2Digits(buf, pos, r2) - if tz == nd: - inc(tz, trailingZeros2Digits(r2)) - inc(nd, 2) - sf_Assert(output >= 1) - sf_Assert(output <= 99) - if output >= 10: - let q: uint32 = output - dec(pos, 2) - utoa2Digits(buf, pos, q) - if tz == nd: - inc(tz, trailingZeros2Digits(q)) - else: - let q: uint32 = output - sf_Assert(q >= 1) - sf_Assert(q <= 9) - dec(pos) - buf[pos] = chr(uint32('0') + q) - return tz - -proc decimalLength(v: uint32): int {.inline.} = - sf_Assert(v >= 1) - sf_Assert(v <= 999999999'u) - if v >= 100000000'u: - return 9 - if v >= 10000000'u: - return 8 - if v >= 1000000'u: - return 7 - if v >= 100000'u: - return 6 - if v >= 10000'u: - return 5 - if v >= 1000'u: - return 4 - if v >= 100'u: - return 3 - if v >= 10'u: - return 2 - return 1 - -proc formatDigits[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint32; decimalExponent: int; - forceTrailingDotZero: bool = false): int {.inline.} = - const - minFixedDecimalPoint: int32 = -4 - maxFixedDecimalPoint: int32 = 9 - var pos = pos - assert(minFixedDecimalPoint <= -1, "internal error") - assert(maxFixedDecimalPoint >= 1, "internal error") - sf_Assert(digits >= 1) - sf_Assert(digits <= 999999999'u) - sf_Assert(decimalExponent >= -99) - sf_Assert(decimalExponent <= 99) - var numDigits = decimalLength(digits) - let decimalPoint = numDigits + decimalExponent - let useFixed: bool = minFixedDecimalPoint <= decimalPoint and - decimalPoint <= maxFixedDecimalPoint - ## Prepare the buffer. - ## Avoid calling memset/memcpy with variable arguments below... - for i in 0..<32: buffer[pos+i] = '0' - assert(minFixedDecimalPoint >= -30, "internal error") - assert(maxFixedDecimalPoint <= 32, "internal error") - var decimalDigitsPosition: int - if useFixed: - if decimalPoint <= 0: - ## 0.[000]digits - decimalDigitsPosition = 2 - decimalPoint - else: - ## dig.its - ## digits[000] - decimalDigitsPosition = 0 - else: - ## dE+123 or d.igitsE+123 - decimalDigitsPosition = 1 - var digitsEnd = pos + decimalDigitsPosition + numDigits - let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits) - dec(digitsEnd, tz) - dec(numDigits, tz) - ## decimal_exponent += tz; // => decimal_point unchanged. - if useFixed: - if decimalPoint <= 0: - ## 0.[000]digits - buffer[pos+1] = '.' - pos = digitsEnd - elif decimalPoint < numDigits: - ## dig.its - for i in countdown(7, 0): - buffer[i + decimalPoint + 1] = buffer[i + decimalPoint] - buffer[pos+decimalPoint] = '.' - pos = digitsEnd + 1 - else: - ## digits[000] - inc(pos, decimalPoint) - if forceTrailingDotZero: - buffer[pos] = '.' - buffer[pos+1] = '0' - inc(pos, 2) - else: - buffer[pos] = buffer[pos+1] - if numDigits == 1: - ## dE+123 - inc(pos) - else: - ## d.igitsE+123 - buffer[pos+1] = '.' - pos = digitsEnd - let scientificExponent = decimalPoint - 1 - ## SF_ASSERT(scientific_exponent != 0); - buffer[pos] = 'e' - buffer[pos+1] = if scientificExponent < 0: '-' else: '+' - inc(pos, 2) - let k: uint32 = cast[uint32](if scientificExponent < 0: -scientificExponent else: scientificExponent) - if k < 10: - buffer[pos] = chr(uint32('0') + k) - inc pos - else: - utoa2Digits(buffer, pos, k) - inc(pos, 2) - return pos - -proc float32ToChars*(buffer: var openArray[char]; v: float32; forceTrailingDotZero = false): int {. - inline.} = - let significand: uint32 = physicalSignificand(constructSingle(v)) - let exponent: uint32 = physicalExponent(constructSingle(v)) - var pos = 0 - if exponent != maxIeeeExponent: - ## Finite - buffer[pos] = '-' - inc(pos, signBit(constructSingle(v))) - if exponent != 0 or significand != 0: - ## != 0 - let dec: auto = toDecimal32(significand, exponent) - return formatDigits(buffer, pos, dec.digits, dec.exponent.int, forceTrailingDotZero) - else: - buffer[pos] = '0' - buffer[pos+1] = '.' - buffer[pos+2] = '0' - buffer[pos+3] = ' ' - inc(pos, if forceTrailingDotZero: 3 else: 1) - return pos - if significand == 0: - buffer[pos] = '-' - inc(pos, signBit(constructSingle(v))) - buffer[pos] = 'i' - buffer[pos+1] = 'n' - buffer[pos+2] = 'f' - buffer[pos+3] = ' ' - return pos + 3 - else: - buffer[pos] = 'n' - buffer[pos+1] = 'a' - buffer[pos+2] = 'n' - buffer[pos+3] = ' ' - return pos + 3 diff --git a/lib/std/private/since.nim b/lib/std/private/since.nim index 5b22b6391..e4aecb61a 100644 --- a/lib/std/private/since.nim +++ b/lib/std/private/since.nim @@ -1,17 +1,3 @@ -##[ -`since` is used to emulate older versions of nim stdlib with `--useVersion`, -see `tuse_version.nim`. - -If a symbol `foo` is added in version `(1,3,5)`, use `{.since: (1.3.5).}`, not -`{.since: (1.4).}`, so that it works in devel in between releases. - -The emulation cannot be 100% faithful and to avoid adding too much complexity, -`since` is not needed in those cases: -* if a new module is added -* if an overload is added -* if an extra parameter to an existing routine is added -]## - template since*(version: (int, int), body: untyped) {.dirty.} = ## Evaluates `body` if the ``(NimMajor, NimMinor)`` is greater than ## or equal to `version`. Usage: @@ -30,4 +16,4 @@ template since*(version: (int, int, int), body: untyped) {.dirty.} = ## proc fun*() {.since: (1, 3, 1).} ## since (1, 3, 1): fun() when (NimMajor, NimMinor, NimPatch) >= version: - body + body \ No newline at end of file diff --git a/lib/std/private/strimpl.nim b/lib/std/private/strimpl.nim index f8c9236a5..3fa0dc1d3 100644 --- a/lib/std/private/strimpl.nim +++ b/lib/std/private/strimpl.nim @@ -70,44 +70,3 @@ template endsWithImpl*[T: string | cstring](s, suffix: T) = if s[i+j] != suffix[i]: return false inc(i) if i >= suffixLen: return true - - -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[int](found) -% cast[int](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(cast[cstring](s[start].unsafeAddr), sub) - if not found.isNil: - result = cast[int](found) -% cast[int](s) - else: - result = -1 diff --git a/lib/std/private/syslocks.nim b/lib/std/private/syslocks.nim deleted file mode 100644 index ca8897dc2..000000000 --- a/lib/std/private/syslocks.nim +++ /dev/null @@ -1,234 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# Low level system locks and condition vars. - -{.push stackTrace: off.} - -when defined(windows): - type - Handle = int - - SysLock* {.importc: "CRITICAL_SECTION", - header: "", final, pure.} = object # CRITICAL_SECTION in WinApi - DebugInfo: pointer - LockCount: int32 - RecursionCount: int32 - OwningThread: int - LockSemaphore: int - SpinCount: int - - SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "".} = object - thePtr {.importc: "Ptr".} : Handle - - proc initSysLock*(L: var SysLock) {.importc: "InitializeCriticalSection", - header: "".} - ## Initializes the lock `L`. - - proc tryAcquireSysAux(L: var SysLock): int32 {.importc: "TryEnterCriticalSection", - header: "".} - ## Tries to acquire the lock `L`. - - proc tryAcquireSys*(L: var SysLock): bool {.inline.} = - result = tryAcquireSysAux(L) != 0'i32 - - proc acquireSys*(L: var SysLock) {.importc: "EnterCriticalSection", - header: "".} - ## Acquires the lock `L`. - - proc releaseSys*(L: var SysLock) {.importc: "LeaveCriticalSection", - header: "".} - ## Releases the lock `L`. - - proc deinitSys*(L: var SysLock) {.importc: "DeleteCriticalSection", - header: "".} - - proc initializeConditionVariable( - conditionVariable: var SysCond - ) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "InitializeConditionVariable".} - - proc sleepConditionVariableCS( - conditionVariable: var SysCond, - PCRITICAL_SECTION: var SysLock, - dwMilliseconds: int - ): int32 {.stdcall, noSideEffect, dynlib: "kernel32", importc: "SleepConditionVariableCS".} - - - proc signalSysCond*(hEvent: var SysCond) {.stdcall, noSideEffect, - dynlib: "kernel32", importc: "WakeConditionVariable".} - - proc broadcastSysCond*(hEvent: var SysCond) {.stdcall, noSideEffect, - dynlib: "kernel32", importc: "WakeAllConditionVariable".} - - proc initSysCond*(cond: var SysCond) {.inline.} = - initializeConditionVariable(cond) - proc deinitSysCond*(cond: var SysCond) {.inline.} = - discard - proc waitSysCond*(cond: var SysCond, lock: var SysLock) = - discard sleepConditionVariableCS(cond, lock, -1'i32) - -elif defined(genode): - const - Header = "genode_cpp/syslocks.h" - type - SysLock* {.importcpp: "Nim::SysLock", pure, final, - header: Header.} = object - SysCond* {.importcpp: "Nim::SysCond", pure, final, - header: Header.} = object - - proc initSysLock*(L: var SysLock) = discard - proc deinitSys*(L: var SysLock) = discard - proc acquireSys*(L: var SysLock) {.noSideEffect, importcpp.} - proc tryAcquireSys*(L: var SysLock): bool {.noSideEffect, importcpp.} - proc releaseSys*(L: var SysLock) {.noSideEffect, importcpp.} - - proc initSysCond*(L: var SysCond) = discard - proc deinitSysCond*(L: var SysCond) = discard - proc waitSysCond*(cond: var SysCond, lock: var SysLock) {. - noSideEffect, importcpp.} - proc signalSysCond*(cond: var SysCond) {. - noSideEffect, importcpp.} - proc broadcastSysCond*(cond: var SysCond) {. - noSideEffect, importcpp.} - -else: - type - SysLockObj {.importc: "pthread_mutex_t", pure, final, - header: """#include - #include """.} = object - when defined(linux) and defined(amd64): - abi: array[40 div sizeof(clong), clong] - - SysLockAttr* {.importc: "pthread_mutexattr_t", pure, final - header: """#include - #include """.} = object - when defined(linux) and defined(amd64): - abi: array[4 div sizeof(cint), cint] # actually a cint - - SysCondObj {.importc: "pthread_cond_t", pure, final, - header: """#include - #include """.} = object - when defined(linux) and defined(amd64): - abi: array[48 div sizeof(clonglong), clonglong] - - SysCondAttr {.importc: "pthread_condattr_t", pure, final - header: """#include - #include """.} = object - when defined(linux) and defined(amd64): - abi: array[4 div sizeof(cint), cint] # actually a cint - - SysLockType = distinct cint - - proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {. - importc: "pthread_mutex_init", header: "", noSideEffect.} - proc deinitSysAux(L: var SysLockObj) {.noSideEffect, - importc: "pthread_mutex_destroy", header: "".} - - proc acquireSysAux(L: var SysLockObj) {.noSideEffect, - importc: "pthread_mutex_lock", header: "".} - proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect, - importc: "pthread_mutex_trylock", header: "".} - - proc releaseSysAux(L: var SysLockObj) {.noSideEffect, - importc: "pthread_mutex_unlock", header: "".} - - when defined(ios): - # iOS will behave badly if sync primitives are moved in memory. In order - # to prevent this once and for all, we're doing an extra malloc when - # initializing the primitive. - type - SysLock* = ptr SysLockObj - SysCond* = ptr SysCondObj - - when not declared(c_malloc): - proc c_malloc(size: csize_t): pointer {. - importc: "malloc", header: "".} - proc c_free(p: pointer) {. - importc: "free", header: "".} - - proc initSysLock*(L: var SysLock, attr: ptr SysLockAttr = nil) = - L = cast[SysLock](c_malloc(csize_t(sizeof(SysLockObj)))) - initSysLockAux(L[], attr) - - proc deinitSys*(L: var SysLock) = - deinitSysAux(L[]) - c_free(L) - - template acquireSys*(L: var SysLock) = - acquireSysAux(L[]) - template tryAcquireSys*(L: var SysLock): bool = - tryAcquireSysAux(L[]) == 0'i32 - template releaseSys*(L: var SysLock) = - releaseSysAux(L[]) - else: - type - SysLock* = SysLockObj - SysCond* = SysCondObj - - template initSysLock*(L: var SysLock, attr: ptr SysLockAttr = nil) = - initSysLockAux(L, attr) - template deinitSys*(L: var SysLock) = - deinitSysAux(L) - template acquireSys*(L: var SysLock) = - acquireSysAux(L) - template tryAcquireSys*(L: var SysLock): bool = - tryAcquireSysAux(L) == 0'i32 - template releaseSys*(L: var SysLock) = - releaseSysAux(L) - - # rlocks - var SysLockType_Reentrant* {.importc: "PTHREAD_MUTEX_RECURSIVE", - header: "".}: SysLockType - proc initSysLockAttr*(a: var SysLockAttr) {. - importc: "pthread_mutexattr_init", header: "", noSideEffect.} - proc setSysLockType*(a: var SysLockAttr, t: SysLockType) {. - importc: "pthread_mutexattr_settype", header: "", noSideEffect.} - - # locks - proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {. - importc: "pthread_cond_init", header: "", noSideEffect.} - proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect, - importc: "pthread_cond_destroy", header: "".} - - proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj): cint {. - importc: "pthread_cond_wait", header: "", noSideEffect.} - proc signalSysCondAux(cond: var SysCondObj) {. - importc: "pthread_cond_signal", header: "", noSideEffect.} - proc broadcastSysCondAux(cond: var SysCondObj) {. - importc: "pthread_cond_broadcast", header: "", noSideEffect.} - - when defined(ios): - proc initSysCond*(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = - cond = cast[SysCond](c_malloc(csize_t(sizeof(SysCondObj)))) - initSysCondAux(cond[], cond_attr) - - proc deinitSysCond*(cond: var SysCond) = - deinitSysCondAux(cond[]) - c_free(cond) - - template waitSysCond*(cond: var SysCond, lock: var SysLock) = - discard waitSysCondAux(cond[], lock[]) - template signalSysCond*(cond: var SysCond) = - signalSysCondAux(cond[]) - template broadcastSysCond*(cond: var SysCond) = - broadcastSysCondAux(cond[]) - else: - template initSysCond*(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = - initSysCondAux(cond, cond_attr) - template deinitSysCond*(cond: var SysCond) = - deinitSysCondAux(cond) - - template waitSysCond*(cond: var SysCond, lock: var SysLock) = - discard waitSysCondAux(cond, lock) - template signalSysCond*(cond: var SysCond) = - signalSysCondAux(cond) - template broadcastSysCond*(cond: var SysCond) = - broadcastSysCondAux(cond) - -{.pop.} diff --git a/lib/std/private/threadtypes.nim b/lib/std/private/threadtypes.nim deleted file mode 100644 index a1cdf21dc..000000000 --- a/lib/std/private/threadtypes.nim +++ /dev/null @@ -1,176 +0,0 @@ -include system/inclrtl - -const hasSharedHeap* = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own - -when defined(windows): - type - Handle* = int - SysThread* = Handle - WinThreadProc* = proc (x: pointer): int32 {.stdcall.} - - proc createThread*(lpThreadAttributes: pointer, dwStackSize: int32, - lpStartAddress: WinThreadProc, - lpParameter: pointer, - dwCreationFlags: int32, - lpThreadId: var int32): SysThread {. - stdcall, dynlib: "kernel32", importc: "CreateThread".} - - proc winSuspendThread*(hThread: SysThread): int32 {. - stdcall, dynlib: "kernel32", importc: "SuspendThread".} - - proc winResumeThread*(hThread: SysThread): int32 {. - stdcall, dynlib: "kernel32", importc: "ResumeThread".} - - proc waitForSingleObject*(hHandle: SysThread, dwMilliseconds: int32): int32 {. - stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".} - - proc waitForMultipleObjects*(nCount: int32, - lpHandles: ptr SysThread, - bWaitAll: int32, - dwMilliseconds: int32): int32 {. - stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".} - - proc terminateThread*(hThread: SysThread, dwExitCode: int32): int32 {. - stdcall, dynlib: "kernel32", importc: "TerminateThread".} - - proc setThreadAffinityMask*(hThread: SysThread, dwThreadAffinityMask: uint) {. - importc: "SetThreadAffinityMask", stdcall, header: "".} - -elif defined(genode): - const - GenodeHeader* = "genode_cpp/threads.h" - type - SysThread* {.importcpp: "Nim::SysThread", - header: GenodeHeader, final, pure.} = object - GenodeThreadProc* = proc (x: pointer) {.noconv.} - - proc initThread*(s: var SysThread, - env: GenodeEnv, - stackSize: culonglong, - entry: GenodeThreadProc, - arg: pointer, - affinity: cuint) {. - importcpp: "#.initThread(@)".} - - -else: - when not (defined(macosx) or defined(haiku)): - {.passl: "-pthread".} - - when not defined(haiku): - {.passc: "-pthread".} - - const - schedh = "#define _GNU_SOURCE\n#include " - pthreadh* = "#define _GNU_SOURCE\n#include " - - when not declared(Time): - when defined(linux): - type Time = clong - else: - type Time = int - - when (defined(linux) or defined(nintendoswitch)) and defined(amd64): - type - SysThread* {.importc: "pthread_t", - header: "" .} = distinct culong - Pthread_attr* {.importc: "pthread_attr_t", - header: "".} = object - abi: array[56 div sizeof(clong), clong] - elif defined(openbsd) and defined(amd64): - type - SysThread* {.importc: "pthread_t", header: "".} = object - Pthread_attr* {.importc: "pthread_attr_t", - header: "".} = object - else: - type - SysThread* {.importc: "pthread_t", header: "".} = int - Pthread_attr* {.importc: "pthread_attr_t", - header: "".} = object - type - Timespec* {.importc: "struct timespec", header: "".} = object - tv_sec*: Time - tv_nsec*: clong - - proc pthread_attr_init*(a1: var Pthread_attr): cint {. - importc, header: pthreadh.} - proc pthread_attr_setstack*(a1: ptr Pthread_attr, a2: pointer, a3: int): cint {. - importc, header: pthreadh.} - proc pthread_attr_setstacksize*(a1: var Pthread_attr, a2: int): cint {. - importc, header: pthreadh.} - proc pthread_attr_destroy*(a1: var Pthread_attr): cint {. - importc, header: pthreadh.} - - proc pthread_create*(a1: var SysThread, a2: var Pthread_attr, - a3: proc (x: pointer): pointer {.noconv.}, - a4: pointer): cint {.importc: "pthread_create", - header: pthreadh.} - proc pthread_join*(a1: SysThread, a2: ptr pointer): cint {. - importc, header: pthreadh.} - - proc pthread_cancel*(a1: SysThread): cint {. - importc: "pthread_cancel", header: pthreadh.} - - type CpuSet* {.importc: "cpu_set_t", header: schedh.} = object - when defined(linux) and defined(amd64): - abi: array[1024 div (8 * sizeof(culong)), culong] - - proc cpusetZero*(s: var CpuSet) {.importc: "CPU_ZERO", header: schedh.} - proc cpusetIncl*(cpu: cint; s: var CpuSet) {. - importc: "CPU_SET", header: schedh.} - - when defined(android): - # libc of android doesn't implement pthread_setaffinity_np, - # it exposes pthread_gettid_np though, so we can use that in combination - # with sched_setaffinity to set the thread affinity. - type Pid* {.importc: "pid_t", header: "".} = int32 # From posix_other.nim - - proc setAffinityTID*(tid: Pid; setsize: csize_t; s: var CpuSet) {. - importc: "sched_setaffinity", header: schedh.} - - proc pthread_gettid_np*(thread: SysThread): Pid {. - importc: "pthread_gettid_np", header: pthreadh.} - - proc setAffinity*(thread: SysThread; setsize: csize_t; s: var CpuSet) = - setAffinityTID(pthread_gettid_np(thread), setsize, s) - else: - proc setAffinity*(thread: SysThread; setsize: csize_t; s: var CpuSet) {. - importc: "pthread_setaffinity_np", header: pthreadh.} - - -const - emulatedThreadVars* = compileOption("tlsEmulation") -# we preallocate a fixed size for thread local storage, so that no heap -# allocations are needed. Currently less than 16K are used on a 64bit machine. -# We use `float` for proper alignment: -const nimTlsSize {.intdefine.} = 16000 -type - ThreadLocalStorage* = array[0..(nimTlsSize div sizeof(float)), float] - PGcThread* = ptr GcThread - GcThread* {.pure, inheritable.} = object - when emulatedThreadVars: - tls*: ThreadLocalStorage - else: - nil - when hasSharedHeap: - next*, prev*: PGcThread - stackBottom*, stackTop*: pointer - stackSize*: int - else: - nil - -const hasAllocStack* = defined(zephyr) # maybe freertos too? - -type - Thread*[TArg] = object - core*: PGcThread - sys*: SysThread - when TArg is void: - dataFn*: proc () {.nimcall, gcsafe.} - else: - dataFn*: proc (m: TArg) {.nimcall, gcsafe.} - data*: TArg - when hasAllocStack: - rawStack*: pointer - -proc `=copy`*[TArg](x: var Thread[TArg], y: Thread[TArg]) {.error.} diff --git a/lib/std/private/underscored_calls.nim b/lib/std/private/underscored_calls.nim index f0bcbcc74..6d0a99ab5 100644 --- a/lib/std/private/underscored_calls.nim +++ b/lib/std/private/underscored_calls.nim @@ -39,7 +39,7 @@ proc underscoredCall(n, arg0: NimNode): NimNode = result.add arg0 proc underscoredCalls*(result, calls, arg0: NimNode) = - expectKind calls, {nnkArgList, nnkStmtList, nnkStmtListExpr} + expectKind calls, {nnkArglist, nnkStmtList, nnkStmtListExpr} for call in calls: if call.kind in {nnkStmtList, nnkStmtListExpr}: diff --git a/lib/std/private/win_getsysteminfo.nim b/lib/std/private/win_getsysteminfo.nim deleted file mode 100644 index b98478231..000000000 --- a/lib/std/private/win_getsysteminfo.nim +++ /dev/null @@ -1,15 +0,0 @@ -type - SystemInfo* = object - u1: uint32 - dwPageSize: uint32 - lpMinimumApplicationAddress: pointer - lpMaximumApplicationAddress: pointer - dwActiveProcessorMask: ptr uint32 - dwNumberOfProcessors*: uint32 - dwProcessorType: uint32 - dwAllocationGranularity*: uint32 - wProcessorLevel: uint16 - wProcessorRevision: uint16 - -proc getSystemInfo*(lpSystemInfo: ptr SystemInfo) {.stdcall, - dynlib: "kernel32", importc: "GetSystemInfo".} diff --git a/lib/std/private/win_setenv.nim b/lib/std/private/win_setenv.nim deleted file mode 100644 index 66e199dfe..000000000 --- a/lib/std/private/win_setenv.nim +++ /dev/null @@ -1,106 +0,0 @@ -#[ -Copyright (c) Facebook, Inc. and its affiliates. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -Adapted `setenv` from https://github.com/facebook/folly/blob/master/folly/portability/Stdlib.cpp -translated from C to nim. -]# - -#[ -Introduced in https://github.com/facebook/folly/commit/5d8ca09a3f96afefb44e35808f03651a096ab9c7 - -TODO: -check errno_t vs cint -]# - -when not defined(windows): discard -else: - when defined(nimPreviewSlimSystem): - import std/widestrs - - type wchar_t {.importc: "wchar_t".} = int16 - - proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {. - stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.} - # same as winlean.setEnvironmentVariableA - - proc c_getenv(varname: cstring): cstring {.importc: "getenv", header: "".} - proc c_wputenv(envstring: ptr wchar_t): cint {.importc: "_wputenv", header: "".} - proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "".} - - var errno {.importc, header: "".}: cint - var genviron {.importc: "_environ".}: ptr ptr char - # xxx `ptr UncheckedArray[WideCString]` did not work - - proc wcstombs(wcstr: ptr char, mbstr: ptr wchar_t, 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) - let wideName: WideCString = newWideCString(name) - if overwrite == 0 and c_wgetenv(cast[ptr wchar_t](wideName)) != nil: - return 0 - - if value != "": - let envstring: WideCString = newWideCString(name & "=" & value) - let e = c_wputenv(cast[ptr wchar_t](envstring)) - if e != 0: - errno = EINVAL - return -1 - return 0 - #[ - We are trying to set the value to an empty string, but `_putenv` deletes - entries if the value is an empty string, and just calling - SetEnvironmentVariableA doesn't update `_environ`, - so we have to do these terrible things. - ]# - let envstring: WideCString = newWideCString(name & "= ") - if c_wputenv(cast[ptr wchar_t](envstring)) != 0: - errno = EINVAL - return -1 - # Here lies the documentation we blatently ignore to make this work. - var s = cast[WideCString](c_wgetenv(cast[ptr wchar_t](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 = cast[WideCString](c_wgetenv(cast[ptr wchar_t](wideName))) - s[1] = Utf16Char('=') - #[ - 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 MBCS environment - even though it's never actually used in most programs. - ]# - 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, cast[ptr wchar_t](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, cast[ptr wchar_t](wideName), csize_t(requiredSize + 1)) != high(csize_t): - var ptrToEnv = c_getenv(cast[cstring](buf2)) - ptrToEnv[0] = '\0' - ptrToEnv = c_getenv(cast[cstring](buf2)) - ptrToEnv[1] = '=' - - # And now, we have to update the outer environment to have a proper empty value. - if setEnvironmentVariableW(wideName, value.newWideCString) == 0: - errno = EINVAL - return -1 - return 0 diff --git a/lib/std/setutils.nim b/lib/std/setutils.nim index 4664d6dcc..c6385180e 100644 --- a/lib/std/setutils.nim +++ b/lib/std/setutils.nim @@ -1,20 +1,16 @@ # # -# Nim's Runtime Library +# The Nim Compiler # (c) Copyright 2020 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This module adds functionality for the built-in `set` type. -## -## See also -## ======== -## * `std/packedsets `_ -## * `std/sets `_ +## This module adds functionality to the built-in `set` type. +## See also std/packedsets, std/sets -import typetraits, macros +import typetraits #[ type SetElement* = char|byte|bool|int16|uint16|enum|uint8|int8 @@ -22,56 +18,15 @@ import typetraits, macros ]# template toSet*(iter: untyped): untyped = - ## Returns a built-in set from the elements of the iterable `iter`. - runnableExamples: + ## Return a built-in set from the elements of iterable `iter` + runnableExamples: assert "helloWorld".toSet == {'W', 'd', 'e', 'h', 'l', 'o', 'r'} assert toSet([10u16, 20, 30]) == {10u16, 20, 30} assert [30u8, 100, 10].toSet == {10u8, 30, 100} assert toSet(@[1321i16, 321, 90]) == {90i16, 321, 1321} assert toSet([false]) == {false} assert toSet(0u8..10) == {0u8..10} - var result: set[elementType(iter)] for x in iter: incl(result, x) result - -macro enumElementsAsSet(enm: typed): untyped = result = newNimNode(nnkCurly).add(enm.getType[1][1..^1]) - -# func fullSet*(T: typedesc): set[T] {.inline.} = # xxx would give: Error: ordinal type expected -func fullSet*[T](U: typedesc[T]): set[T] {.inline.} = - ## Returns a set containing all elements in `U`. - runnableExamples: - assert bool.fullSet == {true, false} - type A = range[1..3] - assert A.fullSet == {1.A, 2, 3} - assert int8.fullSet.len == 256 - when T is Ordinal: - {T.low..T.high} - else: # Hole filled enum - enumElementsAsSet(T) - -func complement*[T](s: set[T]): set[T] {.inline.} = - ## Returns the set complement of `a`. - runnableExamples: - type Colors = enum - red, green = 3, blue - assert complement({red, blue}) == {green} - assert complement({red, green, blue}).card == 0 - assert complement({range[0..10](0), 1, 2, 3}) == {range[0..10](4), 5, 6, 7, 8, 9, 10} - assert complement({'0'..'9'}) == {0.char..255.char} - {'0'..'9'} - fullSet(T) - s - -func `[]=`*[T](t: var set[T], key: T, val: bool) {.inline.} = - ## Syntax sugar for `if val: t.incl key else: t.excl key` - runnableExamples: - type A = enum - a0, a1, a2, a3 - var s = {a0, a3} - s[a0] = false - s[a1] = false - assert s == {a3} - s[a2] = true - s[a3] = true - assert s == {a2, a3} - if val: t.incl key else: t.excl key diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index 50175024c..3c1674bcb 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -1,41 +1,46 @@ # # -# Nim's Runtime Library +# The Nim Compiler # (c) Copyright 2015 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## [SHA-1 (Secure Hash Algorithm 1)](https://en.wikipedia.org/wiki/SHA-1) -## is a cryptographic hash function which takes an input and produces -## a 160-bit (20-byte) hash value known as a message digest. + +## **Note:** Import ``std/sha1`` to use this module ## -## See also -## ======== -## * `base64 module`_ for a Base64 encoder and decoder +## SHA-1 (Secure Hash Algorithm 1) is a cryptographic hash function which +## takes an input and produces a 160-bit (20-byte) hash value known as a +## message digest. +## +## .. code-block:: +## import std/sha1 +## +## let accessName = secureHash("John Doe") +## assert $accessName == "AE6E4D1209F17B460503904FAD297B31E9CF6362" +## +## .. code-block:: +## import std/sha1 +## +## let +## a = secureHashFile("myFile.nim") +## b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C") +## +## if a == b: +## echo "Files match" +## +## **See also:** +## * `base64 module`_ implements a base64 encoder and decoder ## * `hashes module`_ for efficient computations of hash values for diverse Nim types -## * `md5 module`_ for the MD5 checksum algorithm - -runnableExamples: - let accessName = secureHash("John Doe") - assert $accessName == "AE6E4D1209F17B460503904FAD297B31E9CF6362" - -runnableExamples("-r:off"): - let - a = secureHashFile("myFile.nim") - b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C") - assert a == b, "files don't match" +## * `md5 module`_ implements the MD5 checksum algorithm import strutils from endians import bigEndian32, bigEndian64 -when defined(nimPreviewSlimSystem): - import std/syncio - const Sha1DigestSize = 20 type - Sha1Digest* = array[0 .. Sha1DigestSize - 1, uint8] + Sha1Digest* = array[0 .. Sha1DigestSize-1, uint8] SecureHash* = distinct Sha1Digest type @@ -44,14 +49,10 @@ type state: array[5, uint32] buf: array[64, byte] -# This implementation of the SHA-1 algorithm was ported from the Chromium OS one +# This implementation of the SHA1 algorithm was ported from the Chromium OS one # with minor modifications that should not affect its functionality. proc newSha1State*(): Sha1State = - ## Creates a `Sha1State`. - ## - ## If you use the `secureHash proc <#secureHash,openArray[char]>`_, - ## there's no need to call this function explicitly. result.count = 0 result.state[0] = 0x67452301'u32 result.state[1] = 0xEFCDAB89'u32 @@ -145,10 +146,6 @@ proc transform(ctx: var Sha1State) = ctx.state[4] += e proc update*(ctx: var Sha1State, data: openArray[char]) = - ## Updates the `Sha1State` with `data`. - ## - ## If you use the `secureHash proc <#secureHash,openArray[char]>`_, - ## there's no need to call this function explicitly. var i = ctx.count mod 64 var j = 0 var len = data.len @@ -180,10 +177,6 @@ proc update*(ctx: var Sha1State, data: openArray[char]) = ctx.count += data.len proc finalize*(ctx: var Sha1State): Sha1Digest = - ## Finalizes the `Sha1State` and returns a `Sha1Digest`. - ## - ## If you use the `secureHash proc <#secureHash,openArray[char]>`_, - ## there's no need to call this function explicitly. var cnt = uint64(ctx.count * 8) # a 1 bit update(ctx, "\x80") @@ -202,32 +195,31 @@ proc finalize*(ctx: var Sha1State): Sha1Digest = # Public API proc secureHash*(str: openArray[char]): SecureHash = - ## Generates a `SecureHash` from `str`. + ## Generates a ``SecureHash`` from a ``str``. ## ## **See also:** - ## * `secureHashFile proc <#secureHashFile,string>`_ for generating a `SecureHash` from a file - ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string `hash` to `SecureHash` + ## * `secureHashFile proc <#secureHashFile,string>`_ for generating a ``SecureHash`` from a file + ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string ``hash`` to ``SecureHash`` runnableExamples: let hash = secureHash("Hello World") assert hash == parseSecureHash("0A4D55A8D778E5022FAB701977C5D840BBC486D0") - var state = newSha1State() state.update(str) SecureHash(state.finalize()) proc secureHashFile*(filename: string): SecureHash = - ## Generates a `SecureHash` from a file. + ## Generates a ``SecureHash`` from a file. ## ## **See also:** - ## * `secureHash proc <#secureHash,openArray[char]>`_ for generating a `SecureHash` from a string - ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string `hash` to `SecureHash` + ## * `secureHash proc <#secureHash,openArray[char]>`_ for generating a ``SecureHash`` from a string + ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string ``hash`` to ``SecureHash`` const BufferLength = 8192 let f = open(filename) var state = newSha1State() var buffer = newString(BufferLength) while true: - let length = readChars(f, buffer) + let length = readChars(f, buffer, 0, BufferLength) if length == 0: break buffer.setLen(length) @@ -239,35 +231,33 @@ proc secureHashFile*(filename: string): SecureHash = SecureHash(state.finalize()) proc `$`*(self: SecureHash): string = - ## Returns the string representation of a `SecureHash`. + ## Returns the string representation of a ``SecureHash``. ## ## **See also:** - ## * `secureHash proc <#secureHash,openArray[char]>`_ for generating a `SecureHash` from a string + ## * `secureHash proc <#secureHash,openArray[char]>`_ for generating a ``SecureHash`` from a string runnableExamples: let hash = secureHash("Hello World") assert $hash == "0A4D55A8D778E5022FAB701977C5D840BBC486D0" - result = "" for v in Sha1Digest(self): result.add(toHex(int(v), 2)) proc parseSecureHash*(hash: string): SecureHash = - ## Converts a string `hash` to a `SecureHash`. + ## Converts a string ``hash`` to ``SecureHash``. ## ## **See also:** - ## * `secureHash proc <#secureHash,openArray[char]>`_ for generating a `SecureHash` from a string - ## * `secureHashFile proc <#secureHashFile,string>`_ for generating a `SecureHash` from a file + ## * `secureHash proc <#secureHash,openArray[char]>`_ for generating a ``SecureHash`` from a string + ## * `secureHashFile proc <#secureHashFile,string>`_ for generating a ``SecureHash`` from a file runnableExamples: let hashStr = "0A4D55A8D778E5022FAB701977C5D840BBC486D0" secureHash = secureHash("Hello World") assert secureHash == parseSecureHash(hashStr) - for i in 0 ..< Sha1DigestSize: Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1])) proc `==`*(a, b: SecureHash): bool = - ## Checks if two `SecureHash` values are identical. + ## Checks if two ``SecureHash`` values are identical. runnableExamples: let a = secureHash("Hello World") @@ -275,10 +265,5 @@ proc `==`*(a, b: SecureHash): bool = c = parseSecureHash("0A4D55A8D778E5022FAB701977C5D840BBC486D0") assert a != b assert a == c - # Not a constant-time comparison, but that's acceptable in this context Sha1Digest(a) == Sha1Digest(b) - -proc isValidSha1Hash*(s: string): bool = - ## Checks if a string is a valid sha1 hash sum. - s.len == 40 and allCharsInSet(s, HexDigits) diff --git a/lib/std/socketstreams.nim b/lib/std/socketstreams.nim deleted file mode 100644 index b53d6a5d3..000000000 --- a/lib/std/socketstreams.nim +++ /dev/null @@ -1,181 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2021 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module provides an implementation of the streams interface for sockets. -## It contains two separate implementations, a -## `ReadSocketStream <#ReadSocketStream>`_ and a -## `WriteSocketStream <#WriteSocketStream>`_. -## -## The `ReadSocketStream` only supports reading, peeking, and seeking. -## It reads into a buffer, so even by -## seeking backwards it will only read the same position a single time from the -## underlying socket. To clear the buffer and free the data read into it you -## can call `resetStream`, this will also reset the position back to 0 but -## won't do anything to the underlying socket. -## -## The `WriteSocketStream` allows both reading and writing, but it performs the -## reads on the internal buffer. So by writing to the buffer you can then read -## back what was written but without receiving anything from the socket. You -## can also set the position and overwrite parts of the buffer, and to send -## anything over the socket you need to call `flush` at which point you can't -## write anything to the buffer before the point of the flush (but it can still -## be read). Again to empty the underlying buffer you need to call -## `resetStream`. -## -## Examples -## ======== -## -## .. code-block:: Nim -## import std/socketstreams -## -## var -## socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) -## stream = newReadSocketStream(socket) -## socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST") -## echo stream.readLine() # Will call `recv` -## stream.setPosition(0) -## echo stream.readLine() # Will return the read line from the buffer -## stream.resetStream() # Buffer is now empty, position is 0 -## echo stream.readLine() # Will call `recv` again -## stream.close() # Closes the socket -## -## .. code-block:: Nim -## -## import std/socketstreams -## -## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) -## socket.connect("127.0.0.1", Port(12345)) -## var sendStream = newWriteSocketStream(socket) -## sendStream.write "NOM" -## sendStream.setPosition(1) -## echo sendStream.peekStr(2) # OM -## sendStream.write "I" -## sendStream.setPosition(0) -## echo sendStream.readStr(3) # NIM -## echo sendStream.getPosition() # 3 -## sendStream.flush() # This actually performs the writing to the socket -## sendStream.setPosition(1) -## sendStream.write "I" # Throws an error as we can't write into an already sent buffer - -import net, streams - -type - ReadSocketStream* = ref ReadSocketStreamObj - ReadSocketStreamObj* = object of StreamObj - data: Socket - pos: int - buf: seq[byte] - WriteSocketStream* = ref WriteSocketStreamObj - WriteSocketStreamObj* = object of ReadSocketStreamObj - lastFlush: int - -proc rsAtEnd(s: Stream): bool = - return false - -proc rsSetPosition(s: Stream, pos: int) = - var s = ReadSocketStream(s) - s.pos = pos - -proc rsGetPosition(s: Stream): int = - var s = ReadSocketStream(s) - return s.pos - -proc rsPeekData(s: Stream, buffer: pointer, bufLen: int): int = - let s = ReadSocketStream(s) - if bufLen > 0: - let oldLen = s.buf.len - s.buf.setLen(max(s.pos + bufLen, s.buf.len)) - if s.pos + bufLen > oldLen: - result = s.data.recv(s.buf[oldLen].addr, s.buf.len - oldLen) - if result > 0: - result += oldLen - s.pos - else: - result = bufLen - copyMem(buffer, s.buf[s.pos].addr, result) - -proc rsReadData(s: Stream, buffer: pointer, bufLen: int): int = - result = s.rsPeekData(buffer, bufLen) - var s = ReadSocketStream(s) - s.pos += bufLen - -proc rsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int = - var s = ReadSocketStream(s) - result = slice.b + 1 - slice.a - if result > 0: - result = s.rsReadData(buffer[slice.a].addr, result) - inc(s.pos, result) - else: - result = 0 - -proc wsWriteData(s: Stream, buffer: pointer, bufLen: int) = - var s = WriteSocketStream(s) - if s.pos < s.lastFlush: - raise newException(IOError, "Unable to write into buffer that has already been sent") - if s.buf.len < s.pos + bufLen: - s.buf.setLen(s.pos + bufLen) - copyMem(s.buf[s.pos].addr, buffer, bufLen) - s.pos += bufLen - -proc wsPeekData(s: Stream, buffer: pointer, bufLen: int): int = - var s = WriteSocketStream(s) - result = bufLen - if result > 0: - if s.pos > s.buf.len or s.pos == s.buf.len or s.pos + bufLen > s.buf.len: - raise newException(IOError, "Unable to read past end of write buffer") - else: - copyMem(buffer, s.buf[s.pos].addr, bufLen) - -proc wsReadData(s: Stream, buffer: pointer, bufLen: int): int = - result = s.wsPeekData(buffer, bufLen) - var s = ReadSocketStream(s) - s.pos += bufLen - -proc wsAtEnd(s: Stream): bool = - var s = WriteSocketStream(s) - return s.pos == s.buf.len - -proc wsFlush(s: Stream) = - var s = WriteSocketStream(s) - discard s.data.send(s.buf[s.lastFlush].addr, s.buf.len - s.lastFlush) - s.lastFlush = s.buf.len - -proc rsClose(s: Stream) = - {.cast(raises: [IOError, OSError]), cast(tags: []).}: # todo fixme maybe do something? - var s = ReadSocketStream(s) - s.data.close() - -proc newReadSocketStream*(s: Socket): owned ReadSocketStream = - result = ReadSocketStream(data: s, pos: 0, - closeImpl: rsClose, - atEndImpl: rsAtEnd, - setPositionImpl: rsSetPosition, - getPositionImpl: rsGetPosition, - readDataImpl: rsReadData, - peekDataImpl: rsPeekData, - readDataStrImpl: rsReadDataStr) - -proc resetStream*(s: ReadSocketStream) = - s.buf = @[] - s.pos = 0 - -proc newWriteSocketStream*(s: Socket): owned WriteSocketStream = - result = WriteSocketStream(data: s, pos: 0, - closeImpl: rsClose, - atEndImpl: wsAtEnd, - setPositionImpl: rsSetPosition, - getPositionImpl: rsGetPosition, - writeDataImpl: wsWriteData, - readDataImpl: wsReadData, - peekDataImpl: wsPeekData, - flushImpl: wsFlush) - -proc resetStream*(s: WriteSocketStream) = - s.buf = @[] - s.pos = 0 - s.lastFlush = 0 diff --git a/lib/std/strbasics.nim b/lib/std/strbasics.nim deleted file mode 100644 index be1dd7a58..000000000 --- a/lib/std/strbasics.nim +++ /dev/null @@ -1,119 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2021 Nim Contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module provides some high performance string operations. -## -## 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]) = - ## Concatenates `x` and `y` in place. `y` must not overlap with `x` to - ## allow future `memcpy` optimizations. - # Use `{.noalias.}` ? - let n = x.len - x.setLen n + y.len - # pending https://github.com/nim-lang/Nim/issues/14655#issuecomment-643671397 - # use x.setLen(n + y.len, isInit = false) - var i = 0 - while i < y.len: - x[n + i] = y[i] - i.inc - # xxx use `nimCopyMem(x[n].addr, y[0].addr, y.len)` after some refactoring - -func stripSlice(s: openArray[char], leading = true, trailing = true, chars: set[char] = whitespaces): Slice[int] = - ## Returns the slice range of `s` which is stripped `chars`. - runnableExamples: - assert stripSlice(" abc ") == 1 .. 3 - var - first = 0 - last = high(s) - if leading: - while first <= last and s[first] in chars: inc(first) - if trailing: - while last >= first and s[last] in chars: dec(last) - result = first .. last - -func setSlice*(s: var string, slice: Slice[int]) = - ## Inplace version of `substr`. - runnableExamples: - import std/sugar - - var a = "Hello, Nim!" - doAssert a.dup(setSlice(7 .. 9)) == "Nim" - doAssert a.dup(setSlice(0 .. 0)) == "H" - doAssert a.dup(setSlice(0 .. 1)) == "He" - doAssert a.dup(setSlice(0 .. 10)) == a - doAssert a.dup(setSlice(1 .. 0)).len == 0 - doAssert a.dup(setSlice(20 .. -1)).len == 0 - - - doAssertRaises(AssertionDefect): - discard a.dup(setSlice(-1 .. 1)) - - doAssertRaises(AssertionDefect): - discard a.dup(setSlice(1 .. 11)) - - - let first = slice.a - let last = slice.b - - assert first >= 0 - assert last <= s.high - - if first > last: - s.setLen(0) - return - template impl = - for index in first .. last: - s[index - first] = s[index] - if first > 0: - when nimvm: impl() - else: - # not JS and not Nimscript - when not declared(moveMem): - impl() - else: - when defined(nimSeqsV2): - prepareMutation(s) - moveMem(addr s[0], addr s[first], last - first + 1) - s.setLen(last - first + 1) - -func strip*(a: var string, leading = true, trailing = true, chars: set[char] = whitespaces) {.inline.} = - ## Inplace version of `strip`. Strips leading or - ## trailing `chars` (default: whitespace characters). - ## - ## If `leading` is true (default), leading `chars` are stripped. - ## If `trailing` is true (default), trailing `chars` are stripped. - ## If both are false, the string is unchanged. - runnableExamples: - var a = " vhellov " - strip(a) - assert a == "vhellov" - - a = " vhellov " - a.strip(leading = false) - assert a == " vhellov" - - a = " vhellov " - a.strip(trailing = false) - assert a == "vhellov " - - var c = "blaXbla" - c.strip(chars = {'b', 'a'}) - assert c == "laXbl" - c = "blaXbla" - c.strip(chars = {'b', 'a', 'l'}) - assert c == "X" - - setSlice(a, stripSlice(a, leading, trailing, chars)) diff --git a/lib/deprecated/pure/sums.nim b/lib/std/sums.nim similarity index 61% rename from lib/deprecated/pure/sums.nim rename to lib/std/sums.nim index a6ce1b85d..cc964b91a 100644 --- a/lib/deprecated/pure/sums.nim +++ b/lib/std/sums.nim @@ -6,39 +6,12 @@ # See the file "copying.txt", included in this # distribution, for details about the copyright. -## Accurate summation functions. +## Fast sumation functions. -{.deprecated: "use the nimble package `sums` instead.".} - -runnableExamples: - import std/math - - template `~=`(x, y: float): bool = abs(x - y) < 1e-4 - - let - n = 1_000_000 - first = 1e10 - small = 0.1 - var data = @[first] - for _ in 1 .. n: - data.add(small) - - let result = first + small * n.float - - doAssert abs(sum(data) - result) > 0.3 - doAssert sumKbn(data) ~= result - doAssert sumPairs(data) ~= result - -## See also -## ======== -## * `math module `_ for a standard `sum proc `_ func sumKbn*[T](x: openArray[T]): T = ## Kahan-Babuška-Neumaier summation: O(1) error growth, at the expense - ## of a considerable increase in computational cost. - ## - ## See: - ## * https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements + ## of a considerable increase in computational expense. if len(x) == 0: return var sum = x[0] var c = T(0) @@ -62,13 +35,13 @@ func sumPairwise[T](x: openArray[T], i0, n: int): T = result = sumPairwise(x, i0, n2) + sumPairwise(x, i0 + n2, n - n2) func sumPairs*[T](x: openArray[T]): T = - ## Pairwise (cascade) summation of `x[i0:i0+n-1]`, with O(log n) error growth + ## Pairwise (cascade) summation of ``x[i0:i0+n-1]``, with O(log n) error growth ## (vs O(n) for a simple loop) with negligible performance cost if ## the base case is large enough. ## ## See, e.g.: - ## * https://en.wikipedia.org/wiki/Pairwise_summation - ## * Higham, Nicholas J. (1993), "The accuracy of floating point + ## * http://en.wikipedia.org/wiki/Pairwise_summation + ## Higham, Nicholas J. (1993), "The accuracy of floating point ## summation", SIAM Journal on Scientific Computing 14 (4): 783–799. ## ## In fact, the root-mean-square error growth, assuming random roundoff @@ -76,5 +49,14 @@ func sumPairs*[T](x: openArray[T]): T = ## in practice. See: ## * Manfred Tasche and Hansmartin Zeuner, Handbook of ## Analytic-Computational Methods in Applied Mathematics (2000). + ## let n = len(x) if n == 0: T(0) else: sumPairwise(x, 0, n) + + +runnableExamples: + static: + block: + const data = [1, 2, 3, 4, 5, 6, 7, 8, 9] + doAssert sumKbn(data) == 45 + doAssert sumPairs(data) == 45 diff --git a/lib/std/symlinks.nim b/lib/std/symlinks.nim deleted file mode 100644 index 54ab7b677..000000000 --- a/lib/std/symlinks.nim +++ /dev/null @@ -1,33 +0,0 @@ -## This module implements symlink (symbolic link) handling. - -## .. importdoc:: os.nim - -from paths import Path, ReadDirEffect - -from std/private/ossymlinks import symlinkExists, createSymlink, expandSymlink - -proc symlinkExists*(link: Path): bool {.inline, tags: [ReadDirEffect].} = - ## Returns true if the symlink `link` exists. Will return true - ## regardless of whether the link points to a directory or file. - result = symlinkExists(link.string) - -proc createSymlink*(src, dest: Path) {.inline.} = - ## Create a symbolic link at `dest` which points to the item specified - ## 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 developer mode enabled. - ## - ## See also: - ## * `createHardlink proc`_ - ## * `expandSymlink proc`_ - createSymlink(src.string, dest.string) - -proc expandSymlink*(symlinkPath: Path): Path {.inline.} = - ## Returns a string representing the path to which the symbolic link points. - ## - ## On Windows this is a noop, `symlinkPath` is simply returned. - ## - ## See also: - ## * `createSymlink proc`_ - result = Path(expandSymlink(symlinkPath.string)) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim deleted file mode 100644 index eeaa23d72..000000000 --- a/lib/std/sysrand.nim +++ /dev/null @@ -1,325 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2021 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## .. warning:: This module was added in Nim 1.6. If you are using it for cryptographic purposes, -## keep in mind that so far this has not been audited by any security professionals, -## therefore may not be secure. -## -## `std/sysrand` generates random numbers from a secure source provided by the operating system. -## It is a cryptographically secure pseudorandom number generator -## and should be unpredictable enough for cryptographic applications, -## though its exact quality depends on the OS implementation. -## -## | Targets | Implementation | -## | :--- | ----: | -## | Windows | `BCryptGenRandom`_ | -## | Linux | `getrandom`_ | -## | MacOSX | `SecRandomCopyBytes`_ | -## | iOS | `SecRandomCopyBytes`_ | -## | OpenBSD | `getentropy openbsd`_ | -## | FreeBSD | `getrandom freebsd`_ | -## | JS (Web Browser) | `getRandomValues`_ | -## | Node.js | `randomFillSync`_ | -## | Other Unix platforms | `/dev/urandom`_ | -## -## .. _BCryptGenRandom: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom -## .. _getrandom: https://man7.org/linux/man-pages/man2/getrandom.2.html -## .. _getentropy: https://www.unix.com/man-page/mojave/2/getentropy -## .. _SecRandomCopyBytes: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc -## .. _getentropy openbsd: https://man.openbsd.org/getentropy.2 -## .. _getrandom freebsd: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable -## .. _getRandomValues: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues -## .. _randomFillSync: https://nodejs.org/api/crypto.html#crypto_crypto_randomfillsync_buffer_offset_size -## .. _/dev/urandom: https://en.wikipedia.org/wiki//dev/random -## -## On a Linux target, a call to the `getrandom` syscall can be avoided (e.g. -## for targets running kernel version < 3.17) by passing a compile flag of -## `-d:nimNoGetRandom`. If this flag is passed, sysrand will use `/dev/urandom` -## as with any other POSIX compliant OS. -## - -runnableExamples: - doAssert urandom(0).len == 0 - doAssert urandom(113).len == 113 - doAssert urandom(1234) != urandom(1234) # unlikely to fail in practice - -## -## See also -## ======== -## * `random module `_ -## - - -when not defined(js): - import std/oserrors - -when defined(posix): - import posix - -when defined(nimPreviewSlimSystem): - import std/assertions - -const - batchImplOS = defined(freebsd) or defined(openbsd) or defined(zephyr) - batchSize {.used.} = 256 - -when batchImplOS: - template batchImpl(result: var int, dest: var openArray[byte], getRandomImpl) = - let size = dest.len - if size == 0: - return - - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - - for i in 0 ..< chunks: - let readBytes = getRandomImpl(addr dest[result], batchSize) - if readBytes < 0: - return readBytes - inc(result, batchSize) - - result = getRandomImpl(addr dest[result], left) - -when defined(js): - import std/private/jsutils - - when defined(nodejs): - {.emit: "const _nim_nodejs_crypto = require('crypto');".} - - proc randomFillSync(p: Uint8Array) {.importjs: "_nim_nodejs_crypto.randomFillSync(#)".} - - template urandomImpl(result: var int, dest: var openArray[byte]) = - let size = dest.len - if size == 0: - return - - var src = newUint8Array(size) - randomFillSync(src) - for i in 0 ..< size: - dest[i] = src[i] - - else: - proc getRandomValues(p: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} - # The requested length of `p` must not be more than 65536. - - proc assign(dest: var openArray[byte], src: Uint8Array, base: int, size: int) = - getRandomValues(src) - for j in 0 ..< size: - dest[base + j] = src[j] - - template urandomImpl(result: var int, dest: var openArray[byte]) = - let size = dest.len - if size == 0: - return - - if size <= batchSize: - var src = newUint8Array(size) - assign(dest, src, 0, size) - return - - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - - var srcArray = newUint8Array(batchSize) - for i in 0 ..< chunks: - assign(dest, srcArray, result, batchSize) - inc(result, batchSize) - - var leftArray = newUint8Array(left) - assign(dest, leftArray, result, left) - -elif defined(windows): - type - PVOID = pointer - BCRYPT_ALG_HANDLE = PVOID - PUCHAR = ptr uint8 - NTSTATUS = clong - ULONG = culong - - const - STATUS_SUCCESS = 0x00000000 - BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002 - - proc bCryptGenRandom( - hAlgorithm: BCRYPT_ALG_HANDLE, - pbBuffer: PUCHAR, - cbBuffer: ULONG, - dwFlags: ULONG - ): NTSTATUS {.stdcall, importc: "BCryptGenRandom", dynlib: "Bcrypt.dll".} - - - proc randomBytes(pbBuffer: pointer, cbBuffer: Natural): int {.inline.} = - bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer), - BCRYPT_USE_SYSTEM_PREFERRED_RNG) - - template urandomImpl(result: var int, dest: var openArray[byte]) = - let size = dest.len - if size == 0: - return - - result = randomBytes(addr dest[0], size) - -elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten): - # TODO using let, pending bootstrap >= 1.4.0 - var SYS_getrandom {.importc: "SYS_getrandom", header: "".}: clong - const syscallHeader = """#include -#include """ - - 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 - # request is made to read a large number of bytes (more than 256), - # getrandom() will block until those bytes have been generated and - # transferred from kernel memory to buf. - - template urandomImpl(result: var int, dest: var openArray[byte]) = - let size = dest.len - if size == 0: - return - - while result < size: - let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0).int - if readBytes == 0: - doAssert false - elif readBytes > 0: - inc(result, readBytes) - else: - if osLastError().int in {EINTR, EAGAIN}: - discard - else: - result = -1 - break - -elif defined(openbsd): - proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} - # Fills a buffer with high-quality entropy, - # which can be used as input for process-context pseudorandom generators like `arc4random`. - # The maximum buffer size permitted is 256 bytes. - - proc getRandomImpl(p: pointer, size: int): int {.inline.} = - result = getentropy(p, cint(size)).int - -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 - result = sys_csrand_get(p, csize_t(size)).int - -elif defined(freebsd): - type cssize_t {.importc: "ssize_t", header: "".} = int - - proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} - # Upon successful completion, the number of bytes which were actually read - # is returned. For requests larger than 256 bytes, this can be fewer bytes - # than were requested. Otherwise, -1 is returned and the global variable - # errno is set to indicate the error. - - proc getRandomImpl(p: pointer, size: int): int {.inline.} = - result = getrandom(p, csize_t(size), 0) - -elif defined(ios) or defined(macosx): - {.passl: "-framework Security".} - - const errSecSuccess = 0 ## No error. - - type - SecRandom {.importc: "struct __SecRandom".} = object - - SecRandomRef = ptr SecRandom - ## An abstract Core Foundation-type object containing information about a random number generator. - - proc secRandomCopyBytes( - rnd: SecRandomRef, count: csize_t, bytes: pointer - ): cint {.importc: "SecRandomCopyBytes", header: "".} - ## https://developer.apple.com/documentation/security/1399291-secrandomcopybytes - - template urandomImpl(result: var int, dest: var openArray[byte]) = - let size = dest.len - if size == 0: - return - - result = secRandomCopyBytes(nil, csize_t(size), addr dest[0]) - -else: - template urandomImpl(result: var int, dest: var openArray[byte]) = - let size = dest.len - if size == 0: - return - - # see: https://www.2uo.de/myths-about-urandom/ which justifies using urandom instead of random - let fd = posix.open("/dev/urandom", O_RDONLY) - - if fd < 0: - result = -1 - else: - try: - var stat: Stat - if fstat(fd, stat) != -1 and S_ISCHR(stat.st_mode): - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - - for i in 0 ..< chunks: - let readBytes = posix.read(fd, addr dest[result], batchSize) - if readBytes < 0: - return readBytes - inc(result, batchSize) - - result = posix.read(fd, addr dest[result], left) - else: - result = -1 - finally: - discard posix.close(fd) - -proc urandomInternalImpl(dest: var openArray[byte]): int {.inline.} = - when batchImplOS: - batchImpl(result, dest, getRandomImpl) - else: - urandomImpl(result, dest) - -proc urandom*(dest: var openArray[byte]): bool = - ## Fills `dest` with random bytes suitable for cryptographic use. - ## If the call succeeds, returns `true`. - ## - ## If `dest` is empty, `urandom` immediately returns success, - ## without calling the underlying operating system API. - ## - ## .. warning:: The code hasn't been audited by cryptography experts and - ## is provided as-is without guarantees. Use at your own risks. For production - ## systems we advise you to request an external audit. - result = true - when defined(js): discard urandomInternalImpl(dest) - else: - let ret = urandomInternalImpl(dest) - when defined(windows): - if ret != STATUS_SUCCESS: - result = false - else: - if ret < 0: - result = false - -proc urandom*(size: Natural): seq[byte] {.inline.} = - ## Returns random bytes suitable for cryptographic use. - ## - ## .. warning:: The code hasn't been audited by cryptography experts and - ## is provided as-is without guarantees. Use at your own risks. For production - ## systems we advise you to request an external audit. - result = newSeq[byte](size) - when defined(js): discard urandomInternalImpl(result) - else: - if not urandom(result): - raiseOSError(osLastError()) diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim deleted file mode 100644 index 055ddf144..000000000 --- a/lib/std/tasks.nim +++ /dev/null @@ -1,277 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2021 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module provides basic primitives for creating parallel programs. -## A `Task` should be only owned by a single Thread, it cannot be shared by threads. - -import std/[macros, isolation, typetraits] -import system/ansi_c - -when defined(nimPreviewSlimSystem): - import std/assertions - -export isolation - - -when compileOption("threads"): - from std/effecttraits import isGcSafe - - -# -# proc hello(a: int, b: string) = -# echo $a & b -# -# let literal = "Nim" -# let t = toTask(hello(521, literal)) -# -# -# is roughly converted to -# -# type -# ScratchObj_369098780 = object -# a: int -# b: string -# -# let scratch_369098762 = cast[ptr ScratchObj_369098780](c_calloc(csize_t 1, -# csize_t sizeof(ScratchObj_369098780))) -# if scratch_369098762.isNil: -# raise newException(OutOfMemDefect, "Could not allocate memory") -# block: -# var isolate_369098776 = isolate(521) -# scratch_369098762.a = extract(isolate_369098776) -# var isolate_369098778 = isolate(literal) -# scratch_369098762.b = extract(isolate_369098778) -# proc hello_369098781(args`gensym3: pointer) {.nimcall.} = -# let objTemp_369098775 = cast[ptr ScratchObj_369098780](args`gensym3) -# let :tmp_369098777 = objTemp_369098775.a -# let :tmp_369098779 = objTemp_369098775.b -# hello(a = :tmp_369098777, b = :tmp_369098779) -# -# proc destroyScratch_369098782(args`gensym3: pointer) {.nimcall.} = -# let obj_369098783 = cast[ptr ScratchObj_369098780](args`gensym3) -# =destroy(obj_369098783[]) -# let t = Task(callback: hello_369098781, args: scratch_369098762, destroy: destroyScratch_369098782) -# - - -type - Task* = object ## `Task` contains the callback and its arguments. - callback: proc (args: pointer) {.nimcall, gcsafe.} - args: pointer - destroy: proc (args: pointer) {.nimcall, gcsafe.} - - -proc `=copy`*(x: var Task, y: Task) {.error.} - -proc `=destroy`*(t: var Task) {.inline, gcsafe.} = - ## Frees the resources allocated for a `Task`. - if t.args != nil: - if t.destroy != nil: - t.destroy(t.args) - c_free(t.args) - -proc invoke*(task: Task) {.inline, gcsafe.} = - ## Invokes the `task`. - assert task.callback != nil - task.callback(task.args) - -template checkIsolate(scratchAssignList: seq[NimNode], procParam, scratchDotExpr: NimNode) = - # block: - # var isoTempA = isolate(521) - # scratch.a = extract(isolateA) - # var isoTempB = isolate(literal) - # scratch.b = extract(isolateB) - let isolatedTemp = genSym(nskTemp, "isoTemp") - scratchAssignList.add newVarStmt(isolatedTemp, newCall(newIdentNode("isolate"), procParam)) - scratchAssignList.add newAssignment(scratchDotExpr, - newCall(newIdentNode("extract"), isolatedTemp)) - -template addAllNode(assignParam: NimNode, procParam: NimNode) = - let scratchDotExpr = newDotExpr(scratchIdent, formalParams[i][0]) - - checkIsolate(scratchAssignList, procParam, scratchDotExpr) - - let tempNode = genSym(kind = nskTemp, ident = formalParams[i][0].strVal) - callNode.add nnkExprEqExpr.newTree(formalParams[i][0], tempNode) - tempAssignList.add newLetStmt(tempNode, newDotExpr(objTemp, formalParams[i][0])) - scratchRecList.add newIdentDefs(newIdentNode(formalParams[i][0].strVal), assignParam) - -macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkCallStrLit}): Task = - ## Converts the call and its arguments to `Task`. - runnableExamples("--gc:orc"): - proc hello(a: int) = echo a - - let b = toTask hello(13) - assert b is Task - - if getTypeInst(e).typeKind != ntyVoid: - error("'toTask' cannot accept a call with a return value", e) - - when compileOption("threads"): - if not isGcSafe(e[0]): - error("'toTask' takes a GC safe call expression", e) - - if hasClosure(e[0]): - error("closure call is not allowed", e) - - if e.len > 1: - let scratchIdent = genSym(kind = nskTemp, ident = "scratch") - let impl = e[0].getTypeInst - - when defined(nimTasksDebug): - echo impl.treeRepr - echo e.treeRepr - let formalParams = impl[0] - - var - scratchRecList = newNimNode(nnkRecList) - scratchAssignList: seq[NimNode] - tempAssignList: seq[NimNode] - callNode: seq[NimNode] - - let - objTemp = genSym(nskTemp, ident = "objTemp") - - for i in 1 ..< formalParams.len: - var param = formalParams[i][1] - - if param.kind == nnkBracketExpr and param[0].eqIdent("sink"): - param = param[0] - - if param.typeKind in {ntyExpr, ntyStmt}: - error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter", e) - - case param.kind - of nnkVarTy: - error("'toTask'ed function cannot have a 'var' parameter", e) - of nnkBracketExpr: - if param[0].typeKind == ntyTypeDesc: - callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) - elif param[0].typeKind in {ntyVarargs, ntyOpenArray}: - if param[1].typeKind in {ntyExpr, ntyStmt}: - error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter", e) - let - seqType = nnkBracketExpr.newTree(newIdentNode("seq"), param[1]) - seqCallNode = newCall("@", e[i]) - addAllNode(seqType, seqCallNode) - else: - addAllNode(param, e[i]) - of nnkBracket, nnkObjConstr: - # passing by static parameters - # so we pass them directly instead of passing by scratchObj - callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) - of nnkSym, nnkPtrTy: - addAllNode(param, e[i]) - of nnkCharLit..nnkNilLit: - callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) - else: - error("'toTask'ed function cannot have a parameter of " & $param.kind & " kind", e) - - let scratchObjType = genSym(kind = nskType, ident = "ScratchObj") - let scratchObj = nnkTypeSection.newTree( - nnkTypeDef.newTree( - scratchObjType, - newEmptyNode(), - nnkObjectTy.newTree( - newEmptyNode(), - newEmptyNode(), - scratchRecList - ) - ) - ) - - - let scratchObjPtrType = quote do: - cast[ptr `scratchObjType`](c_calloc(csize_t 1, csize_t sizeof(`scratchObjType`))) - - let scratchLetSection = newLetStmt( - scratchIdent, - scratchObjPtrType - ) - - let scratchCheck = quote do: - if `scratchIdent`.isNil: - raise newException(OutOfMemDefect, "Could not allocate memory") - - var stmtList = newStmtList() - stmtList.add(scratchObj) - stmtList.add(scratchLetSection) - stmtList.add(scratchCheck) - stmtList.add(nnkBlockStmt.newTree(newEmptyNode(), newStmtList(scratchAssignList))) - - var functionStmtList = newStmtList() - let funcCall = newCall(e[0], callNode) - functionStmtList.add tempAssignList - functionStmtList.add funcCall - - let funcName = genSym(nskProc, e[0].strVal) - let destroyName = genSym(nskProc, "destroyScratch") - let objTemp2 = genSym(ident = "obj") - let tempNode = quote("@") do: - `=destroy`(@objTemp2[]) - - result = quote do: - `stmtList` - - proc `funcName`(args: pointer) {.gcsafe, nimcall.} = - let `objTemp` = cast[ptr `scratchObjType`](args) - `functionStmtList` - - proc `destroyName`(args: pointer) {.gcsafe, nimcall.} = - let `objTemp2` = cast[ptr `scratchObjType`](args) - `tempNode` - - Task(callback: `funcName`, args: `scratchIdent`, destroy: `destroyName`) - else: - let funcCall = newCall(e[0]) - let funcName = genSym(nskProc, e[0].strVal) - - result = quote do: - proc `funcName`(args: pointer) {.gcsafe, nimcall.} = - `funcCall` - - Task(callback: `funcName`, args: nil) - - when defined(nimTasksDebug): - echo result.repr - -runnableExamples("--gc:orc"): - block: - var num = 0 - proc hello(a: int) = inc num, a - - let b = toTask hello(13) - b.invoke() - assert num == 13 - # A task can be invoked multiple times - b.invoke() - assert num == 26 - - block: - type - Runnable = ref object - data: int - - var data: int - proc hello(a: Runnable) {.nimcall.} = - a.data += 2 - data = a.data - - - when false: - # the parameters of call must be isolated. - let x = Runnable(data: 12) - let b = toTask hello(x) # error ----> expression cannot be isolated: x - b.invoke() - - let b = toTask(hello(Runnable(data: 12))) - b.invoke() - assert data == 14 - b.invoke() - assert data == 16 diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim deleted file mode 100644 index 1160aaaad..000000000 --- a/lib/std/tempfiles.nim +++ /dev/null @@ -1,192 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2021 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module creates temporary files and directories. -## -## Experimental API, subject to change. - -#[ -See also: -* `GetTempFileName` (on windows), refs https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettempfilenamea -* `mkstemp` (posix), refs https://man7.org/linux/man-pages/man3/mkstemp.3.html -]# - -import os, random - -when defined(nimPreviewSlimSystem): - import std/syncio - -const - maxRetry = 10000 - letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - nimTempPathLength {.intdefine.} = 8 - - -when defined(windows): - import winlean - when defined(nimPreviewSlimSystem): - import std/widestrs - - var O_RDWR {.importc: "_O_RDWR", header: "".}: cint - - proc c_fdopen( - filehandle: cint, - mode: cstring - ): File {.importc: "_fdopen",header: "".} - - proc open_osfhandle(osh: Handle, mode: cint): cint {. - importc: "_open_osfhandle", header: "".} - - proc close_osfandle(fd: cint): cint {. - importc: "_close", header: "".} -else: - import posix - - proc c_fdopen( - filehandle: cint, - mode: cstring - ): File {.importc: "fdopen",header: "".} - - -proc safeOpen(filename: string): File = - ## Open files exclusively; returns `nil` if the file already exists. - # xxx this should be clarified; it doesn't in particular prevent other processes - # from opening the file, at least currently. - when defined(windows): - let dwShareMode = FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE - let dwCreation = CREATE_NEW - let dwFlags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL - let handle = createFileW(newWideCString(filename), GENERIC_READ or GENERIC_WRITE, dwShareMode, - nil, dwCreation, dwFlags, Handle(0)) - - if handle == INVALID_HANDLE_VALUE: - if getLastError() == ERROR_FILE_EXISTS: - return nil - else: - raiseOSError(osLastError(), filename) - - let fileHandle = open_osfhandle(handle, O_RDWR) - if fileHandle == -1: - discard closeHandle(handle) - raiseOSError(osLastError(), filename) - - result = c_fdopen(fileHandle, "w+") - if result == nil: - discard close_osfandle(fileHandle) - raiseOSError(osLastError(), filename) - else: - # xxx we need a `proc toMode(a: FilePermission): Mode`, possibly by - # exposing fusion/filepermissions.fromFilePermissions to stdlib; then we need - # to expose a `perm` param so users can customize this (e.g. the temp file may - # need execute permissions), and figure out how to make the API cross platform. - let mode = Mode(S_IRUSR or S_IWUSR) - let flags = posix.O_RDWR or posix.O_CREAT or posix.O_EXCL - let fileHandle = posix.open(filename, flags, mode) - if fileHandle == -1: - if errno == EEXIST: - # xxx `getLastError()` should be defined on posix too and resolve to `errno`? - return nil - else: - raiseOSError(osLastError(), filename) - - result = c_fdopen(fileHandle, "w+") - if result == nil: - discard posix.close(fileHandle) # TODO handles failure when closing file - raiseOSError(osLastError(), filename) - - -type - NimTempPathState = object - state: Rand - isInit: bool - -var nimTempPathState {.threadvar.}: NimTempPathState - -template randomPathName(length: Natural): string = - var res = newString(length) - if not nimTempPathState.isInit: - nimTempPathState.isInit = true - nimTempPathState.state = initRand() - - for i in 0 ..< length: - res[i] = nimTempPathState.state.sample(letters) - res - -proc getTempDirImpl(dir: string): string {.inline.} = - result = dir - if result.len == 0: - result = getTempDir() - -proc genTempPath*(prefix, suffix: string, dir = ""): string = - ## Generates a path name in `dir`. - ## - ## The path begins with `prefix` and ends with `suffix`. - ## - ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir `_). - let dir = getTempDirImpl(dir) - result = dir / (prefix & randomPathName(nimTempPathLength) & suffix) - -proc createTempFile*(prefix, suffix: string, dir = ""): tuple[cfile: File, path: string] = - ## Creates a new temporary file in the directory `dir`. - ## - ## This generates a path name using `genTempPath(prefix, suffix, dir)` and - ## returns a file handle to an open file and the path of that file, possibly after - ## retrying to ensure it doesn't already exist. - ## - ## If failing to create a temporary file, `OSError` will be raised. - ## - ## .. note:: It is the caller's responsibility to close `result.cfile` and - ## remove `result.file` when no longer needed. - ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir `_). - runnableExamples: - import std/os - doAssertRaises(OSError): discard createTempFile("", "", "nonexistent") - let (cfile, path) = createTempFile("tmpprefix_", "_end.tmp") - # path looks like: getTempDir() / "tmpprefix_FDCIRZA0_end.tmp" - cfile.write "foo" - cfile.setFilePos 0 - assert readAll(cfile) == "foo" - close cfile - assert readFile(path) == "foo" - removeFile(path) - # xxx why does above work without `cfile.flushFile` ? - let dir = getTempDirImpl(dir) - for i in 0 ..< maxRetry: - result.path = genTempPath(prefix, suffix, dir) - result.cfile = safeOpen(result.path) - if result.cfile != nil: - return - - raise newException(OSError, "Failed to create a temporary file under directory " & dir) - -proc createTempDir*(prefix, suffix: string, dir = ""): string = - ## Creates a new temporary directory in the directory `dir`. - ## - ## This generates a dir name using `genTempPath(prefix, suffix, dir)`, creates - ## the directory and returns it, possibly after retrying to ensure it doesn't - ## already exist. - ## - ## If failing to create a temporary directory, `OSError` will be raised. - ## - ## .. note:: It is the caller's responsibility to remove the directory when no longer needed. - ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir `_). - runnableExamples: - import std/os - doAssertRaises(OSError): discard createTempDir("", "", "nonexistent") - let dir = createTempDir("tmpprefix_", "_end") - # dir looks like: getTempDir() / "tmpprefix_YEl9VuVj_end" - assert dirExists(dir) - removeDir(dir) - let dir = getTempDirImpl(dir) - for i in 0 ..< maxRetry: - result = genTempPath(prefix, suffix, dir) - if not existsOrCreateDir(result): - return - - raise newException(OSError, "Failed to create a temporary directory under directory " & dir) diff --git a/lib/std/time_t.nim b/lib/std/time_t.nim index 7fb6e6d46..5fd2752c7 100644 --- a/lib/std/time_t.nim +++ b/lib/std/time_t.nim @@ -11,7 +11,7 @@ when defined(nimdoc): type Impl = distinct int64 Time* = Impl ## \ - ## Wrapper for `time_t`. On posix, this is an alias to `posix.Time`. + ## Wrapper for ``time_t``. On posix, this is an alias to ``posix.Time``. elif defined(windows): when defined(i386) and defined(gcc): type Time* {.importc: "time_t", header: "".} = distinct int32 diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim deleted file mode 100644 index 8359ca353..000000000 --- a/lib/std/typedthreads.nim +++ /dev/null @@ -1,263 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Thread support for Nim. -## -## Examples -## ======== -## -## .. code-block:: Nim -## -## import std/locks -## -## var -## thr: array[0..4, Thread[tuple[a,b: int]]] -## L: Lock -## -## proc threadFunc(interval: tuple[a,b: int]) {.thread.} = -## for i in interval.a..interval.b: -## acquire(L) # lock stdout -## echo i -## release(L) -## -## initLock(L) -## -## for i in 0..high(thr): -## createThread(thr[i], threadFunc, (i*10, i*10+5)) -## joinThreads(thr) -## -## deinitLock(L) - -import std/private/[threadtypes] -export Thread - -import system/ansi_c - -when defined(nimPreviewSlimSystem): - import std/assertions - -when defined(genode): - import genode/env - -when hostOS == "any": - {.error: "Threads not implemented for os:any. Please compile with --threads:off.".} - -when hasAllocStack or defined(zephyr) or defined(freertos) or defined(nuttx) or - defined(cpu16) or defined(cpu8): - const - nimThreadStackSize {.intdefine.} = 8192 - nimThreadStackGuard {.intdefine.} = 128 - - StackGuardSize = nimThreadStackGuard - ThreadStackSize = nimThreadStackSize - nimThreadStackGuard -else: - const - StackGuardSize = 4096 - ThreadStackMask = - when defined(genode): - 1024*64*sizeof(int)-1 - else: - 1024*256*sizeof(int)-1 - - ThreadStackSize = ThreadStackMask+1 - StackGuardSize - - -when defined(gcDestructors): - proc allocThreadStorage(size: int): pointer = - result = c_malloc(csize_t size) - zeroMem(result, size) -else: - template allocThreadStorage(size: untyped): untyped = allocShared0(size) - -#const globalsSlot = ThreadVarSlot(0) -#sysAssert checkSlot.int == globalsSlot.int - -# Zephyr doesn't include this properly without some help -when defined(zephyr): - {.emit: """/*INCLUDESECTION*/ - #include - """.} - - -# We jump through some hops here to ensure that Nim thread procs can have -# the Nim calling convention. This is needed because thread procs are -# ``stdcall`` on Windows and ``noconv`` on UNIX. Alternative would be to just -# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway. - - - -{.push stack_trace:off.} -when defined(windows): - proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} = - nimThreadProcWrapperBody(closure) - # implicitly return 0 -elif defined(genode): - proc threadProcWrapper[TArg](closure: pointer) {.noconv.} = - nimThreadProcWrapperBody(closure) -else: - proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} = - nimThreadProcWrapperBody(closure) -{.pop.} - -proc running*[TArg](t: Thread[TArg]): bool {.inline.} = - ## Returns true if `t` is running. - result = t.dataFn != nil - -proc handle*[TArg](t: Thread[TArg]): SysThread {.inline.} = - ## Returns the thread handle of `t`. - result = t.sys - -when hostOS == "windows": - const MAXIMUM_WAIT_OBJECTS = 64 - - proc joinThread*[TArg](t: Thread[TArg]) {.inline.} = - ## Waits for the thread `t` to finish. - discard waitForSingleObject(t.sys, -1'i32) - - proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = - ## Waits for every thread in `t` to finish. - var a: array[MAXIMUM_WAIT_OBJECTS, SysThread] - var k = 0 - while k < len(t): - var count = min(len(t) - k, MAXIMUM_WAIT_OBJECTS) - for i in 0..(count - 1): a[i] = t[i + k].sys - discard waitForMultipleObjects(int32(count), - cast[ptr SysThread](addr(a)), 1, -1) - inc(k, MAXIMUM_WAIT_OBJECTS) - -elif defined(genode): - proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.} - ## Waits for the thread `t` to finish. - - proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = - ## Waits for every thread in `t` to finish. - for i in 0..t.high: joinThread(t[i]) - -else: - proc joinThread*[TArg](t: Thread[TArg]) {.inline.} = - ## Waits for the thread `t` to finish. - discard pthread_join(t.sys, nil) - - proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = - ## Waits for every thread in `t` to finish. - for i in 0..t.high: joinThread(t[i]) - -when false: - # XXX a thread should really release its heap here somehow: - proc destroyThread*[TArg](t: var Thread[TArg]) = - ## Forces the thread `t` to terminate. This is potentially dangerous if - ## you don't have full control over `t` and its acquired resources. - when hostOS == "windows": - discard TerminateThread(t.sys, 1'i32) - else: - discard pthread_cancel(t.sys) - when declared(registerThread): unregisterThread(addr(t)) - t.dataFn = nil - ## if thread `t` already exited, `t.core` will be `null`. - if not isNil(t.core): - deallocThreadStorage(t.core) - t.core = nil - -when hostOS == "windows": - proc createThread*[TArg](t: var Thread[TArg], - tp: proc (arg: TArg) {.thread, nimcall.}, - param: TArg) = - ## Creates a new thread `t` and starts its execution. - ## - ## Entry point is the proc `tp`. - ## `param` is passed to `tp`. `TArg` can be `void` if you - ## don't need to pass any data to the thread. - t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) - - when TArg isnot void: t.data = param - t.dataFn = tp - when hasSharedHeap: t.core.stackSize = ThreadStackSize - var dummyThreadId: int32 - t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg], - addr(t), 0'i32, dummyThreadId) - if t.sys <= 0: - raise newException(ResourceExhaustedError, "cannot create thread") - - proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = - ## Pins a thread to a `CPU`:idx:. - ## - ## In other words sets a thread's `affinity`:idx:. - ## If you don't know what this means, you shouldn't use this proc. - setThreadAffinityMask(t.sys, uint(1 shl cpu)) - -elif defined(genode): - var affinityOffset: cuint = 1 - ## CPU affinity offset for next thread, safe to roll-over. - - proc createThread*[TArg](t: var Thread[TArg], - tp: proc (arg: TArg) {.thread, nimcall.}, - param: TArg) = - t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) - - when TArg isnot void: t.data = param - t.dataFn = tp - when hasSharedHeap: t.stackSize = ThreadStackSize - t.sys.initThread( - runtimeEnv, - ThreadStackSize.culonglong, - threadProcWrapper[TArg], addr(t), affinityOffset) - inc affinityOffset - - proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = - {.hint: "cannot change Genode thread CPU affinity after initialization".} - discard - -else: - proc createThread*[TArg](t: var Thread[TArg], - tp: proc (arg: TArg) {.thread, nimcall.}, - param: TArg) = - ## Creates a new thread `t` and starts its execution. - ## - ## Entry point is the proc `tp`. `param` is passed to `tp`. - ## `TArg` can be `void` if you - ## don't need to pass any data to the thread. - t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) - - when TArg isnot void: t.data = param - t.dataFn = tp - when hasSharedHeap: t.core.stackSize = ThreadStackSize - var a {.noinit.}: Pthread_attr - doAssert pthread_attr_init(a) == 0 - when hasAllocStack: - var - rawstk = allocThreadStorage(ThreadStackSize + StackGuardSize) - stk = cast[pointer](cast[uint](rawstk) + StackGuardSize) - let setstacksizeResult = pthread_attr_setstack(addr a, stk, ThreadStackSize) - t.rawStack = rawstk - else: - let setstacksizeResult = pthread_attr_setstacksize(a, ThreadStackSize) - - when not defined(ios): - # This fails on iOS - doAssert(setstacksizeResult == 0) - if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: - raise newException(ResourceExhaustedError, "cannot create thread") - doAssert pthread_attr_destroy(a) == 0 - - proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = - ## Pins a thread to a `CPU`:idx:. - ## - ## In other words sets a thread's `affinity`:idx:. - ## If you don't know what this means, you shouldn't use this proc. - when not defined(macosx): - var s {.noinit.}: CpuSet - cpusetZero(s) - cpusetIncl(cpu.cint, s) - setAffinity(t.sys, csize_t(sizeof(s)), s) - -proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) = - createThread[void](t, tp) - -when not defined(gcOrc): - include system/threadids diff --git a/lib/std/vmutils.nim b/lib/std/vmutils.nim deleted file mode 100644 index e16912a3c..000000000 --- a/lib/std/vmutils.nim +++ /dev/null @@ -1,11 +0,0 @@ -##[ -Experimental API, subject to change. -]## - -proc vmTrace*(on: bool) {.compileTime.} = - runnableExamples: - static: vmTrace(true) - proc fn = - var a = 1 - vmTrace(false) - static: fn() diff --git a/lib/std/widestrs.nim b/lib/std/widestrs.nim deleted file mode 100644 index 8973579e1..000000000 --- a/lib/std/widestrs.nim +++ /dev/null @@ -1,229 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Nim support for C/C++'s `wide strings`:idx:. - -#when not declared(ThisIsSystem): -# {.error: "You must not import this module explicitly".} - -type - Utf16Char* = distinct int16 - -when not (defined(cpu16) or defined(cpu8)): - when defined(nimv2): - - type - WideCString* = ptr UncheckedArray[Utf16Char] - - WideCStringObj* = object - bytes: int - data: WideCString - - proc `=destroy`(a: var WideCStringObj) = - if a.data != nil: - when compileOption("threads"): - deallocShared(a.data) - else: - dealloc(a.data) - - proc `=copy`(a: var WideCStringObj; b: WideCStringObj) {.error.} - - proc `=sink`(a: var WideCStringObj; b: WideCStringObj) = - a.bytes = b.bytes - a.data = b.data - - proc createWide(a: var WideCStringObj; bytes: int) = - a.bytes = bytes - when compileOption("threads"): - a.data = cast[typeof(a.data)](allocShared0(bytes)) - else: - a.data = cast[typeof(a.data)](alloc0(bytes)) - - template `[]`*(a: WideCStringObj; idx: int): Utf16Char = a.data[idx] - template `[]=`*(a: WideCStringObj; idx: int; val: Utf16Char) = a.data[idx] = val - - template nullWide(): untyped = WideCStringObj(bytes: 0, data: nil) - - converter toWideCString*(x: WideCStringObj): WideCString {.inline.} = - result = x.data - - else: - template nullWide(): untyped = nil - - type - WideCString* = ref UncheckedArray[Utf16Char] - WideCStringObj* = WideCString - - template createWide(a; L) = - unsafeNew(a, L) - - proc ord(arg: Utf16Char): int = int(cast[uint16](arg)) - - proc len*(w: WideCString): int = - ## returns the length of a widestring. This traverses the whole string to - ## find the binary zero end marker! - result = 0 - while int16(w[result]) != 0'i16: inc result - - const - UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16) - UNI_MAX_BMP = 0x0000FFFF - UNI_MAX_UTF16 = 0x0010FFFF - # UNI_MAX_UTF32 = 0x7FFFFFFF - # UNI_MAX_LEGAL_UTF32 = 0x0010FFFF - - halfShift = 10 - halfBase = 0x0010000 - halfMask = 0x3FF - - UNI_SUR_HIGH_START = 0xD800 - UNI_SUR_HIGH_END = 0xDBFF - UNI_SUR_LOW_START = 0xDC00 - UNI_SUR_LOW_END = 0xDFFF - UNI_REPL = 0xFFFD - - template ones(n: untyped): untyped = ((1 shl n)-1) - - template fastRuneAt(s: cstring, i, L: int, result: untyped, doInc = true) = - ## Returns the unicode character `s[i]` in `result`. If `doInc == true` - ## `i` is incremented by the number of bytes that have been processed. - bind ones - - if ord(s[i]) <= 127: - result = ord(s[i]) - when doInc: inc(i) - elif ord(s[i]) shr 5 == 0b110: - #assert(ord(s[i+1]) shr 6 == 0b10) - if i <= L - 2: - result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6)) - when doInc: inc(i, 2) - else: - result = UNI_REPL - when doInc: inc(i) - elif ord(s[i]) shr 4 == 0b1110: - if i <= L - 3: - #assert(ord(s[i+1]) shr 6 == 0b10) - #assert(ord(s[i+2]) shr 6 == 0b10) - result = (ord(s[i]) and ones(4)) shl 12 or - (ord(s[i+1]) and ones(6)) shl 6 or - (ord(s[i+2]) and ones(6)) - when doInc: inc(i, 3) - else: - result = UNI_REPL - when doInc: inc(i) - elif ord(s[i]) shr 3 == 0b11110: - if i <= L - 4: - #assert(ord(s[i+1]) shr 6 == 0b10) - #assert(ord(s[i+2]) shr 6 == 0b10) - #assert(ord(s[i+3]) shr 6 == 0b10) - result = (ord(s[i]) and ones(3)) shl 18 or - (ord(s[i+1]) and ones(6)) shl 12 or - (ord(s[i+2]) and ones(6)) shl 6 or - (ord(s[i+3]) and ones(6)) - when doInc: inc(i, 4) - else: - result = UNI_REPL - when doInc: inc(i) - else: - result = 0xFFFD - when doInc: inc(i) - - iterator runes(s: cstring, L: int): int = - var - i = 0 - result: int - while i < L: - fastRuneAt(s, i, L, result, true) - yield result - - proc newWideCString*(size: int): WideCStringObj = - createWide(result, size * 2 + 2) - - proc newWideCString*(source: cstring, L: int): WideCStringObj = - createWide(result, L * 2 + 2) - var d = 0 - for ch in runes(source, L): - - if ch <= UNI_MAX_BMP: - if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_LOW_END: - result[d] = UNI_REPLACEMENT_CHAR - else: - result[d] = cast[Utf16Char](uint16(ch)) - elif ch > UNI_MAX_UTF16: - result[d] = UNI_REPLACEMENT_CHAR - else: - let ch = ch - halfBase - result[d] = cast[Utf16Char](uint16((ch shr halfShift) + UNI_SUR_HIGH_START)) - inc d - result[d] = cast[Utf16Char](uint16((ch and halfMask) + UNI_SUR_LOW_START)) - inc d - result[d] = Utf16Char(0) - - proc newWideCString*(s: cstring): WideCStringObj = - if s.isNil: return nullWide - - result = newWideCString(s, s.len) - - proc newWideCString*(s: string): WideCStringObj = - result = newWideCString(cstring s, s.len) - - proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string = - result = newStringOfCap(estimate + estimate shr 2) - - var i = 0 - while w[i].int16 != 0'i16: - var ch = ord(w[i]) - inc i - if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END: - # If the 16 bits following the high surrogate are in the source buffer... - let ch2 = ord(w[i]) - - # If it's a low surrogate, convert to UTF32: - if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END: - ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase - inc i - else: - #invalid UTF-16 - ch = replacement - elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END: - #invalid UTF-16 - ch = replacement - - if ch < 0x80: - result.add chr(ch) - elif ch < 0x800: - result.add chr((ch shr 6) or 0xc0) - result.add chr((ch and 0x3f) or 0x80) - elif ch < 0x10000: - result.add chr((ch shr 12) or 0xe0) - result.add chr(((ch shr 6) and 0x3f) or 0x80) - result.add chr((ch and 0x3f) or 0x80) - elif ch <= 0x10FFFF: - result.add chr((ch shr 18) or 0xf0) - result.add chr(((ch shr 12) and 0x3f) or 0x80) - result.add chr(((ch shr 6) and 0x3f) or 0x80) - result.add chr((ch and 0x3f) or 0x80) - else: - # replacement char(in case user give very large number): - result.add chr(0xFFFD shr 12 or 0b1110_0000) - result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00) - result.add chr(0xFFFD and ones(6) or 0b10_0000_00) - - proc `$`*(s: WideCString): string = - result = s $ 80 - - when defined(nimv2): - proc `$`*(s: WideCStringObj, estimate: int, replacement: int = 0xFFFD): string = - `$`(s.data, estimate, replacement) - - proc `$`*(s: WideCStringObj): string = - $(s.data) - - proc len*(w: WideCStringObj): int {.inline.} = - len(w.data) diff --git a/lib/std/with.nim b/lib/std/with.nim index c7338b4e4..e6784478c 100644 --- a/lib/std/with.nim +++ b/lib/std/with.nim @@ -7,21 +7,20 @@ # distribution, for details about the copyright. # -## This module implements the `with` macro for easy +## This module implements the ``with`` macro for easy ## function chaining. See https://github.com/nim-lang/RFCs/issues/193 ## and https://github.com/nim-lang/RFCs/issues/192 for details leading to this ## particular design. ## -## **Since:** version 1.2. +## **Since** version 1.2. import macros, private / underscored_calls macro with*(arg: typed; calls: varargs[untyped]): untyped = - ## This macro provides `chaining`:idx: of function calls. + ## This macro provides the `chaining`:idx: of function calls. ## It does so by patching every call in `calls` to ## use `arg` as the first argument. - ## - ## .. caution:: This evaluates `arg` multiple times! + ## **This evaluates `arg` multiple times!** runnableExamples: var x = "yay" with x: diff --git a/lib/std/wrapnils.nim b/lib/std/wrapnils.nim index 235638134..71205c887 100644 --- a/lib/std/wrapnils.nim +++ b/lib/std/wrapnils.nim @@ -1,19 +1,9 @@ -## This module allows evaluating expressions safely against the following conditions: -## * nil dereferences -## * field accesses with incorrect discriminant in case objects +## This module allows chains of field-access and indexing where the LHS can be nil. +## This simplifies code by reducing need for if-else branches around intermediate values +## that maybe be nil. ## -## `default(T)` is returned in those cases when evaluating an expression of type `T`. -## This simplifies code by reducing need for if-else branches. -## -## Note: experimental module, unstable API. - -#[ -TODO: -consider handling indexing operations, eg: -doAssert ?.default(seq[int])[3] == default(int) -]# - -import macros +## Note: experimental module and relies on {.experimental: "dotOperators".} +## Unstable API. runnableExamples: type Foo = ref object @@ -29,165 +19,91 @@ runnableExamples: assert ?.f2.x1 == "a" # same as f2.x1 (no nil LHS in this chain) assert ?.Foo(x1: "a").x1 == "a" # can use constructor inside - # when you know a sub-expression doesn't involve a `nil` (e.g. `f2.x2.x2`), - # you can scope it as follows: - assert ?.(f2.x2.x2).x3[] == 0 + # when you know a sub-expression is not nil, you can scope it as follows: + assert ?.(f2.x2.x2).x3[] == 0 # because `f` is nil - assert (?.f2.x2.x2).x3 == nil # this terminates ?. early +type Wrapnil[T] = object + valueImpl: T + validImpl: bool -runnableExamples: - # ?. also allows case object - type B = object - b0: int - case cond: bool - of false: discard - of true: - b1: float +proc wrapnil[T](a: T): Wrapnil[T] = + ## See top-level example. + Wrapnil[T](valueImpl: a, validImpl: true) - var b = B(cond: false, b0: 3) - doAssertRaises(FieldDefect): discard b.b1 # wrong discriminant - doAssert ?.b.b1 == 0.0 # safe - b = B(cond: true, b1: 4.5) - doAssert ?.b.b1 == 4.5 +template unwrap(a: Wrapnil): untyped = + ## See top-level example. + a.valueImpl - # lvalue semantics are preserved: - if (let p = ?.b.b1.addr; p != nil): p[] = 4.7 - doAssert b.b1 == 4.7 +{.push experimental: "dotOperators".} -proc finalize(n: NimNode, lhs: NimNode, level: int): NimNode = - if level == 0: - result = quote: `lhs` = `n` - else: - result = quote: (let `lhs` = `n`) - -proc process(n: NimNode, lhs: NimNode, label: NimNode, level: int): NimNode = - var n = n.copyNimTree - var it = n - let addr2 = bindSym"addr" - var old: tuple[n: NimNode, index: int] - while true: - if it.len == 0: - result = finalize(n, lhs, level) - break - elif it.kind == nnkCheckedFieldExpr: - let dot = it[0] - let obj = dot[0] - let objRef = quote do: `addr2`(`obj`) - # avoids a copy and preserves lvalue semantics, see tests - let check = it[1] - let okSet = check[1] - let kind1 = check[2] - let tmp = genSym(nskLet, "tmpCase") - let body = process(objRef, tmp, label, level + 1) - let tmp3 = nnkDerefExpr.newTree(tmp) - it[0][0] = tmp3 - let dot2 = nnkDotExpr.newTree(@[tmp, dot[1]]) - if old.n != nil: old.n[old.index] = dot2 - else: n = dot2 - let assgn = finalize(n, lhs, level) - result = quote do: - `body` - if `tmp3`.`kind1` notin `okSet`: break `label` - `assgn` - break - elif it.kind in {nnkHiddenDeref, nnkDerefExpr}: - let tmp = genSym(nskLet, "tmp") - let body = process(it[0], tmp, label, level + 1) - it[0] = tmp - let assgn = finalize(n, lhs, level) - result = quote do: - `body` - if `tmp` == nil: break `label` - `assgn` - break - elif it.kind == nnkCall: # consider extending to `nnkCallKinds` - # `copyNimTree` needed to avoid `typ = nil` issues - old = (it, 1) - it = it[1].copyNimTree - else: - old = (it, 0) - it = it[0] - -macro `?.`*(a: typed): auto = - ## Transforms `a` into an expression that can be safely evaluated even in - ## presence of intermediate nil pointers/references, in which case a default - ## value is produced. - let lhs = genSym(nskVar, "lhs") - let label = genSym(nskLabel, "label") - let body = process(a, lhs, label, 0) - result = quote do: - var `lhs`: type(`a`) - block `label`: - `body` - `lhs` - -# the code below is not needed for `?.` -from options import Option, isSome, get, option, unsafeGet, UnpackDefect - -macro `??.`*(a: typed): Option = - ## Same as `?.` but returns an `Option`. - runnableExamples: - import std/options - type Foo = ref object - x1: ref int - x2: int - # `?.` can't distinguish between a valid vs invalid default value, but `??.` can: - var f1 = Foo(x1: int.new, x2: 2) - doAssert (??.f1.x1[]).get == 0 # not enough to tell when the chain was valid. - doAssert (??.f1.x1[]).isSome # a nil didn't occur in the chain - doAssert (??.f1.x2).get == 2 - - var f2: Foo - doAssert not (??.f2.x1[]).isSome # f2 was nil - - doAssertRaises(UnpackDefect): discard (??.f2.x1[]).get - doAssert ?.f2.x1[] == 0 # in contrast, this returns default(int) - - let lhs = genSym(nskVar, "lhs") - let lhs2 = genSym(nskVar, "lhs") - let label = genSym(nskLabel, "label") - let body = process(a, lhs2, label, 0) - result = quote do: - var `lhs`: Option[type(`a`)] - block `label`: - var `lhs2`: type(`a`) - `body` - `lhs` = option(`lhs2`) - `lhs` - -template fakeDot*(a: Option, b): untyped = +template `.`*(a: Wrapnil, b): untyped = ## See top-level example. let a1 = a # to avoid double evaluations - type T = Option[typeof(unsafeGet(a1).b)] - if isSome(a1): - let a2 = unsafeGet(a1) - when typeof(a2) is ref|ptr: + let a2 = a1.valueImpl + type T = Wrapnil[type(a2.b)] + if a1.validImpl: + when type(a2) is ref|ptr: if a2 == nil: default(T) else: - option(a2.b) + wrapnil(a2.b) else: - option(a2.b) + wrapnil(a2.b) else: # nil is "sticky"; this is needed, see tests default(T) -# xxx this should but doesn't work: func `[]`*[T, I](a: Option[T], i: I): Option {.inline.} = +{.pop.} -func `[]`*[T, I](a: Option[T], i: I): auto {.inline.} = +proc isValid(a: Wrapnil): bool = + ## Returns true if `a` didn't contain intermediate `nil` values (note that + ## `a.valueImpl` itself can be nil even in that case) + a.validImpl + +template `[]`*[I](a: Wrapnil, i: I): untyped = ## See top-level example. - if isSome(a): + let a1 = a # to avoid double evaluations + if a1.validImpl: # correctly will raise IndexDefect if a is valid but wraps an empty container - result = option(a.unsafeGet[i]) + wrapnil(a1.valueImpl[i]) + else: + default(Wrapnil[type(a1.valueImpl[i])]) -func `[]`*[U](a: Option[U]): auto {.inline.} = +template `[]`*(a: Wrapnil): untyped = ## See top-level example. - if isSome(a): - let a2 = a.unsafeGet - if a2 != nil: - result = option(a2[]) + let a1 = a # to avoid double evaluations + let a2 = a1.valueImpl + type T = Wrapnil[type(a2[])] + if a1.validImpl: + if a2 == nil: + default(T) + else: + wrapnil(a2[]) + else: + default(T) -when false: - # xxx: expose a way to do this directly in std/options, e.g.: `getAsIs` - proc safeGet[T](a: Option[T]): T {.inline.} = - get(a, default(T)) +import std/macros + +proc replace(n: NimNode): NimNode = + if n.kind == nnkPar: + doAssert n.len == 1 + newCall(bindSym"wrapnil", n[0]) + elif n.kind in {nnkCall, nnkObjConstr}: + newCall(bindSym"wrapnil", n) + elif n.len == 0: + newCall(bindSym"wrapnil", n) + else: + n[0] = replace(n[0]) + n + +macro `?.`*(a: untyped): untyped = + ## Transforms `a` into an expression that can be safely evaluated even in + ## presence of intermediate nil pointers/references, in which case a default + ## value is produced. + #[ + Using a template like this wouldn't work: + template `?.`*(a: untyped): untyped = wrapnil(a)[] + ]# + result = replace(a) + result = quote do: + `result`.valueImpl diff --git a/lib/system.nim b/lib/system.nim index af7e0e4c8..e220ba7a3 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -14,7 +14,7 @@ ## ## Each module implicitly imports the System module; it must not be listed ## explicitly. Because of this there cannot be a user-defined module named -## `system`. +## ``system``. ## ## System module ## ============= @@ -22,55 +22,57 @@ ## .. 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 + +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* = distinct auto ## Meta type for any supported type. + 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). + include "system/basic_types" -func zeroDefault*[T](_: typedesc[T]): T {.magic: "ZeroDefault".} = - ## returns the default value of the type `T`. -include "system/compilation" +proc compileOption*(option: string): bool {. + magic: "CompileOption", noSideEffect.} + ## Can be used to determine an `on|off` compile-time option. Example: + ## + ## .. code-block:: Nim + ## when compileOption("floatchecks"): + ## echo "compiled with floating point NaN and Inf checks" + +proc compileOption*(option, arg: string): bool {. + magic: "CompileOptionArg", noSideEffect.} + ## Can be used to determine an enum compile-time option. Example: + ## + ## .. code-block:: Nim + ## when compileOption("opt", "size") and compileOption("gc", "boehm"): + ## echo "compiled with optimization for size and uses Boehm's GC" {.push warning[GcMem]: off, warning[Uninit]: off.} -# {.push hints: off.} - -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. +{.push hints: off.} proc `or`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} ## Constructs an `or` meta class. @@ -81,61 +83,176 @@ proc `and`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} proc `not`*(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} ## Constructs an `not` meta class. -when defined(nimHasIterable): - type - iterable*[T] {.magic: IterableType.} ## Represents an expression that yields `T` type - Ordinal*[T] {.magic: Ordinal.} ## Generic ordinal type. Includes integer, - ## bool, character, and enumeration types - ## as well as their subtypes. See also - ## `SomeOrdinal`. + SomeFloat* = float|float32|float64 + ## Type class matching all floating point number types. + SomeNumber* = SomeInteger|SomeFloat + ## Type class matching all number types. -proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = +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: + ## + ## .. code-block:: Nim + ## when not defined(release): + ## # Do here programmer friendly expensive sanity checks. + ## # Put here the normal code + +when defined(nimHashOrdinalFixed): + type + Ordinal*[T] {.magic: Ordinal.} ## Generic ordinal type. Includes integer, + ## bool, character, and enumeration types + ## as well as their subtypes. See also + ## `SomeOrdinal`. +else: + # bootstrap <= 0.20.0 + type + OrdinalImpl[T] {.magic: Ordinal.} + Ordinal* = OrdinalImpl | uint | uint64 + +when defined(nimHasRunnableExamples): + 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. + ## + ## Usage: + ## + ## .. code-block:: Nim + ## proc double*(x: int): int = + ## ## This proc doubles a number. + ## runnableExamples: + ## ## at module scope + ## assert double(5) == 10 + ## block: ## at block scope + ## defer: echo "done" + ## result = 2 * x + ## runnableExamples "-d:foo -b:cpp": + ## import std/compilesettings + ## doAssert querySetting(backend) == "cpp" + ## runnableExamples "-r:off": ## this one is only compiled + ## import std/browsers + ## openDefaultBrowser "https://forum.nim-lang.org/" +else: + template runnableExamples*(doccmd = "", body: untyped) = + discard + +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: var T): ptr T {.magic: "Addr", noSideEffect.} = ## Builtin `addr` operator for taking the address of a memory location. - ## - ## .. note:: This works for `let` variables or parameters - ## for better interop with C. When you use it to write a wrapper - ## for a C library and take the address of `let` variables or parameters, - ## you should always check that the original library - ## does never write to data behind the pointer that is returned from - ## this procedure. - ## ## Cannot be overloaded. ## - ## ``` - ## var - ## buf: seq[char] = @['a','b','c'] - ## p = buf[1].addr - ## echo p.repr # ref 0x7faa35c40059 --> 'b' - ## echo p[] # b - ## ``` + ## See also: + ## * `unsafeAddr <#unsafeAddr,T>`_ + ## + ## .. code-block:: Nim + ## 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.} = - ## .. warning:: `unsafeAddr` is a deprecated alias for `addr`, - ## use `addr` instead. + ## Builtin `addr` operator for taking the address of a memory + ## location. This works even for ``let`` variables or parameters + ## for better interop with C and so it is considered even more + ## unsafe than the ordinary `addr <#addr,T>`_. + ## + ## **Note**: When you use it to write a wrapper for a C library, you should + ## always check that the original library does never write to data behind the + ## pointer that is returned from this procedure. + ## + ## Cannot be overloaded. discard +when defined(nimNewTypedesc): + 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``. +else: + proc `type`*(x: untyped): typedesc {.magic: "TypeOf", noSideEffect, compileTime.} = + ## Builtin `type` operator for accessing the type of an expression. + ## Cannot be overloaded. + discard + +when defined(nimHasTypeof): + 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. + discard + const ThisIsSystem = true proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.} ## Leaked implementation detail. Do not use. -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! +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 wasMoved*[T](obj: var T) {.magic: "WasMoved", noSideEffect.} = ## Resets an object `obj` to its initial (binary zero) value to signify @@ -159,9 +276,14 @@ type seq*[T]{.magic: "Seq".} ## Generic type to construct sequences. set*[T]{.magic: "Set".} ## Generic type to construct bit sets. -type - UncheckedArray*[T]{.magic: "UncheckedArray".} - ## Array with no bounds checking. +when defined(nimUncheckedArrayTyp): + type + UncheckedArray*[T]{.magic: "UncheckedArray".} + ## Array with no bounds checking. +else: + type + UncheckedArray*[T]{.unchecked.} = array[0,T] + ## Array with no bounds checking. type sink*[T]{.magic: "BuiltinType".} type lent*[T]{.magic: "BuiltinType".} @@ -175,57 +297,52 @@ 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]>`_ ## - ## ``` - ## high(2) # => 9223372036854775807 - ## ``` + ## .. code-block:: Nim + ## 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 - ## ``` + ## ``high(int)`` is Nim's way of writing `INT_MAX`:idx: or `MAX_INT`:idx:. ## ## 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`. @@ -236,13 +353,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)`.".} @@ -252,57 +369,52 @@ 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]>`_ ## - ## ``` - ## low(2) # => -9223372036854775808 - ## ``` + ## .. code-block:: Nim + ## 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 - ## ``` + ## ``low(int)`` is Nim's way of writing `INT_MIN`:idx: or `MIN_INT`:idx:. ## ## 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`. @@ -312,96 +424,98 @@ 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 -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. +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 {. - noSideEffect, magic: "ArrGet".} -proc `[]=`*[I: Ordinal;T,S](a: T; i: I; - x: sink S) {.noSideEffect, magic: "ArrPut".} -proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} -proc `=copy`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} +when defined(nimArrIdx): + # :array|openArray|string|seq|cstring|tuple + proc `[]`*[I: Ordinal;T](a: T; i: I): T {. + noSideEffect, magic: "ArrGet".} + proc `[]=`*[I: Ordinal;T,S](a: T; i: I; + x: sink S) {.noSideEffect, magic: "ArrPut".} + proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} -proc arrGet[I: Ordinal;T](a: T; i: I): T {. - noSideEffect, magic: "ArrGet".} -proc arrPut[I: Ordinal;T,S](a: T; i: I; - x: S) {.noSideEffect, magic: "ArrPut".} + proc arrGet[I: Ordinal;T](a: T; i: I): T {. + noSideEffect, magic: "ArrGet".} + proc arrPut[I: Ordinal;T,S](a: T; i: I; + x: S) {.noSideEffect, magic: "ArrPut".} -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, nodestroy, magic: "Asgn".} = - ## Generic `sink`:idx: implementation that can be overridden. - 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".} = - ## Generic `trace`:idx: implementation that can be overridden. + 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".} = + ## Generic `sink`:idx: implementation that can be overridden. + shallowCopy(x, y) type HSlice*[T, U] = object ## "Heterogeneous" slice type. a*: T ## The lower bound (inclusive). b*: U ## The upper bound (inclusive). - Slice*[T] = HSlice[T, T] ## An alias for `HSlice[T, T]`. + Slice*[T] = HSlice[T, T] ## An alias for ``HSlice[T, T]``. proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, magic: "DotDot".} = - ## Binary `slice`:idx: operator that constructs an interval `[a, b]`, both `a` + ## Binary `slice`:idx: operator that constructs an interval ``[a, b]``, both `a` ## and `b` are inclusive. ## ## 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]`. - ## ``` +proc `..`*[T](b: sink T): HSlice[int, T] {.noSideEffect, inline, magic: "DotDot".} = + ## 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 not defined(niminheritable): + {.pragma: inheritable.} +when not defined(nimunion): + {.pragma: unchecked.} +when not defined(nimHasHotCodeReloading): + {.pragma: nonReloadable.} when defined(hotCodeReloading): {.pragma: hcrInline, inline.} 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" const - appType* {.magic: "AppType".}: string = "" + appType* {.magic: "AppType"}: string = "" ## A string that describes the application type. Possible values: ## `"console"`, `"gui"`, `"lib"`. include "system/inclrtl" -const NoFakeVars = defined(nimscript) ## `true` if the backend doesn't support \ +const NoFakeVars* = defined(nimscript) ## `true` if the backend doesn't support \ ## "fake variables" like `var EBADF {.importc.}: cint`. const notJSnotNims = not defined(js) and not defined(nimscript) @@ -427,7 +541,7 @@ when notJSnotNims: include "system/hti" type - byte* = uint8 ## This is an alias for `uint8`, that is an unsigned + byte* = uint8 ## This is an alias for ``uint8``, that is an unsigned ## integer, 8 bits wide. Natural* = range[0..high(int)] @@ -438,7 +552,6 @@ 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. ## @@ -446,64 +559,8 @@ type ## However, objects that have no ancestor are also allowed. RootRef* = ref RootObj ## Reference to `RootObj`. -const NimStackTraceMsgs = compileOption("stacktraceMsgs") -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. - -when defined(nimIcIntegrityChecks): - include "system/exceptions" -else: - import system/exceptions - export exceptions +include "system/exceptions" when defined(js) or defined(nimdoc): type @@ -511,10 +568,10 @@ when defined(js) or defined(nimdoc): ## Root type of the JavaScript object hierarchy proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.} - ## Creates a new object of type `T` and returns a safe (traced) - ## reference to it in `a`. + ## Creates a new object of type ``T`` and returns a safe (traced) + ## reference to it in ``a``. ## - ## This is **unsafe** as it allocates an object of the passed `size`. + ## This is **unsafe** as it allocates an object of the passed ``size``. ## This should only be used for optimization purposes when you know ## what you're doing! ## @@ -522,51 +579,54 @@ proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.} ## * `new <#new,ref.T,proc(ref.T)>`_ proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} - ## Returns the size of `x` in bytes. + ## Returns the size of ``x`` in bytes. ## ## Since this is a low-level proc, ## its usage is discouraged - using `new <#new,ref.T,proc(ref.T)>`_ for - ## the most cases suffices that one never needs to know `x`'s size. + ## the most cases suffices that one never needs to know ``x``'s size. ## - ## As a special semantic rule, `x` may also be a type identifier - ## (`sizeof(int)` is valid). + ## As a special semantic rule, ``x`` may also be a type identifier + ## (``sizeof(int)`` is valid). ## ## Limitations: If used for types that are imported from C or C++, - ## sizeof should fallback to the `sizeof` in the C compiler. The + ## 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. - ## ``` - ## sizeof('A') # => 1 - ## sizeof(2) # => 8 - ## ``` + ## + ## .. code-block:: Nim + ## sizeof('A') # => 1 + ## sizeof(2) # => 8 -proc alignof*[T](x: T): int {.magic: "AlignOf", noSideEffect.} -proc alignof*(x: typedesc): int {.magic: "AlignOf", noSideEffect.} +when defined(nimHasalignOf): + proc alignof*[T](x: T): int {.magic: "AlignOf", noSideEffect.} + proc alignof*(x: typedesc): int {.magic: "AlignOf", noSideEffect.} -proc offsetOfDotExpr(typeAccess: typed): int {.magic: "OffsetOf", noSideEffect, compileTime.} + proc offsetOfDotExpr(typeAccess: typed): int {.magic: "OffsetOf", noSideEffect, compileTime.} -template offsetOf*[T](t: typedesc[T]; member: untyped): int = - var tmp {.noinit.}: ptr T - offsetOfDotExpr(tmp[].member) + template offsetOf*[T](t: typedesc[T]; member: untyped): int = + var tmp {.noinit.}: ptr T + offsetOfDotExpr(tmp[].member) -template offsetOf*[T](value: T; member: untyped): int = - offsetOfDotExpr(value.member) + template offsetOf*[T](value: T; member: untyped): int = + offsetOfDotExpr(value.member) -#proc offsetOf*(memberaccess: typed): int {.magic: "OffsetOf", noSideEffect.} + #proc offsetOf*(memberaccess: typed): int {.magic: "OffsetOf", noSideEffect.} -proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} +when defined(nimtypedescfixed): + proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.} - ## Creates a new sequence of type `seq[T]` with length `len`. + ## Creates a new sequence of type ``seq[T]`` with length ``len``. ## - ## This is equivalent to `s = @[]; setlen(s, len)`, but more + ## This is equivalent to ``s = @[]; setlen(s, len)``, but more ## efficient since no reallocation is needed. ## ## 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 @@ -574,53 +634,51 @@ 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`. + ## Creates a new sequence of type ``seq[T]`` with length ``len``. ## ## 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`. Example: - ## ``` + ## Creates a new sequence of type ``seq[T]`` with length zero and capacity + ## ``cap``. + ## + ## .. code-block:: Nim ## var x = newSeqOfCap[int](5) ## assert len(x) == 0 ## x.add(10) ## assert len(x) == 1 - ## ``` discard when not defined(js): proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] = - ## Creates a new sequence of type `seq[T]` with length `len`. + ## Creates a new sequence of type ``seq[T]`` with length ``len``. ## ## 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. - ## Example: - ## ``` + ## + ## .. code-block:: Nim ## 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 @@ -628,80 +686,88 @@ when not defined(js): var s = cast[PGenericSeq](result) s.len = len -func len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.magic: "LengthOpenArray".} = +proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {. + magic: "LengthOpenArray", noSideEffect.} ## Returns the length of an openArray. - runnableExamples: - proc bar[T](a: openArray[T]): int = len(a) - assert bar([1,2]) == 2 - assert [1,2].len == 2 + ## + ## .. code-block:: Nim + ## var s = [1, 1, 1, 1, 1] + ## echo len(s) # => 5 -func len*(x: string): int {.magic: "LengthStr".} = +proc len*(x: string): int {.magic: "LengthStr", noSideEffect.} ## Returns the length of a string. - runnableExamples: - assert "abc".len == 3 - assert "".len == 0 - assert string.default.len == 0 + ## + ## .. code-block:: Nim + ## var str = "Hello world!" + ## echo len(str) # => 12 -proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.} = - ## Returns the length of a compatible string. This is an O(n) operation except - ## in js at runtime. +proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.} + ## Returns the length of a compatible string. This is sometimes + ## an O(n) operation. ## ## **Note:** On the JS backend this currently counts UTF-16 code points ## instead of bytes at runtime (not at compile time). For now, if you ## need the byte length of the UTF-8 encoding, convert to string with ## `$` first then call `len`. - runnableExamples: - doAssert len(cstring"abc") == 3 - doAssert len(cstring r"ab\0c") == 5 # \0 is escaped - doAssert len(cstring"ab\0c") == 5 # ditto - var a: cstring = "ab\0c" - when defined(js): doAssert a.len == 4 # len ignores \0 for js - else: doAssert a.len == 2 # \0 is a null terminator - static: - var a2: cstring = "ab\0c" - doAssert a2.len == 2 # \0 is a null terminator, even in js vm + ## + ## .. code-block:: Nim + ## var str: cstring = "Hello world!" + ## len(str) # => 12 -func len*(x: (type array)|array): int {.magic: "LengthArray".} = +proc len*(x: (type array)|array): int {.magic: "LengthArray", noSideEffect.} ## Returns the length of an array or an array type. - ## This is roughly the same as `high(T)-low(T)+1`. - runnableExamples: - var a = [1, 1, 1] - assert a.len == 3 - assert array[0, float].len == 0 - static: assert array[-2..2, float].len == 5 + ## This is roughly the same as ``high(T)-low(T)+1``. + ## + ## .. code-block:: Nim + ## var arr = [1, 1, 1, 1, 1] + ## echo len(arr) # => 5 + ## echo len(array[3..8, int]) # => 6 -func len*[T](x: seq[T]): int {.magic: "LengthSeq".} = - ## Returns the length of `x`. - runnableExamples: - assert @[0, 1].len == 2 - assert seq[int].default.len == 0 - assert newSeq[int](3).len == 3 - let s = newSeqOfCap[int](3) - assert s.len == 0 - # xxx this gives cgen error: assert newSeqOfCap[int](3).len == 0 +proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.} + ## Returns the length of a sequence. + ## + ## .. code-block:: Nim + ## var s = @[1, 1, 1, 1, 1] + ## echo len(s) # => 5 -func ord*[T: Ordinal|enum](x: T): int {.magic: "Ord".} = - ## Returns the internal `int` value of `x`, including for enum with holes - ## and distinct ordinal types. - runnableExamples: - assert ord('A') == 65 - type Foo = enum - f0 = 0, f1 = 3 - assert f1.ord == 3 - type Bar = distinct int - assert 3.Bar.ord == 3 -func chr*(u: range[0..255]): char {.magic: "Chr".} = - ## Converts `u` to a `char`, same as `char(u)`. - runnableExamples: - doAssert chr(65) == 'A' - doAssert chr(255) == '\255' - doAssert chr(255) == char(255) - doAssert not compiles chr(256) - doAssert not compiles char(256) - var x = 256 - doAssertRaises(RangeDefect): discard chr(x) - doAssertRaises(RangeDefect): discard char(x) +proc ord*[T: Ordinal|enum](x: T): int {.magic: "Ord", noSideEffect.} + ## Returns the internal `int` value of an ordinal value ``x``. + ## + ## .. code-block:: Nim + ## echo ord('A') # => 65 + ## echo ord('a') # => 97 + +proc chr*(u: range[0..255]): char {.magic: "Chr", noSideEffect.} + ## Converts an `int` in the range `0..255` to a character. + ## + ## .. code-block:: Nim + ## echo chr(65) # => A + ## echo chr(97) # => a + + +# 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" @@ -709,36 +775,33 @@ 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`. - ## ``` + ## `value >= s.a and value <= s.b` + ## + ## .. code-block:: Nim ## 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 -when not defined(nimHasCallsitePragma): - {.pragma: callsite.} - -template `in`*(x, y: untyped): untyped {.dirty, callsite.} = contains(y, x) +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, callsite.} = not contains(y, x) +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 ## @@ -750,13 +813,12 @@ 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 {.callsite.} = not (x is y) - ## Negated version of `is <#is,T,S>`_. Equivalent to `not(x is y)`. - ## ``` +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`. @@ -765,15 +827,15 @@ else: when defined(nimOwnedEnabled) and not defined(nimscript): proc new*[T](a: var owned(ref T)) {.magic: "New", noSideEffect.} - ## Creates a new object of type `T` and returns a safe (traced) - ## reference to it in `a`. + ## Creates a new object of type ``T`` and returns a safe (traced) + ## reference to it in ``a``. proc new*(t: typedesc): auto = - ## Creates a new object of type `T` and returns a safe (traced) + ## Creates a new object of type ``T`` and returns a safe (traced) ## reference to it as result value. ## - ## When `T` is a ref type then the resulting type will be `T`, - ## otherwise it will be `ref T`. + ## When ``T`` is a ref type then the resulting type will be ``T``, + ## otherwise it will be ``ref T``. when (t is ref): var r: owned t else: @@ -782,22 +844,24 @@ when defined(nimOwnedEnabled) and not defined(nimscript): return r proc unown*[T](x: T): T {.magic: "Unown", noSideEffect.} - ## Use the expression `x` ignoring its ownership attribute. + ## Use the expression ``x`` ignoring its ownership attribute. + # This is only required to make 0.20 compile with the 0.19 line. + template ``*(t: untyped): untyped = owned(t) else: template unown*(x: typed): untyped = x proc new*[T](a: var ref T) {.magic: "New", noSideEffect.} - ## Creates a new object of type `T` and returns a safe (traced) - ## reference to it in `a`. + ## Creates a new object of type ``T`` and returns a safe (traced) + ## reference to it in ``a``. proc new*(t: typedesc): auto = - ## Creates a new object of type `T` and returns a safe (traced) + ## Creates a new object of type ``T`` and returns a safe (traced) ## reference to it as result value. ## - ## When `T` is a ref type then the resulting type will be `T`, - ## otherwise it will be `ref T`. + ## When ``T`` is a ref type then the resulting type will be ``T``, + ## otherwise it will be ``ref T``. when (t is ref): var r: t else: @@ -805,6 +869,8 @@ else: new(r) return r + # This is only required to make 0.20 compile with the 0.19 line. + template ``*(t: untyped): untyped = t template disarm*(x: typed) = ## Useful for `disarming` dangling pointers explicitly for `--newruntime`. @@ -813,31 +879,14 @@ template disarm*(x: typed) = ## experimental API! x = nil -proc `of`*[T, S](x: T, y: typedesc[S]): bool {.magic: "Of", noSideEffect.} = - ## Checks if `x` is an instance of `y`. - runnableExamples: - type - Base = ref object of RootObj - Sub1 = ref object of Base - Sub2 = ref object of Base - Unrelated = ref object - - var base: Base = Sub1() # downcast - doAssert base of Base # generates `CondTrue` (statically true) - doAssert base of Sub1 - doAssert base isnot Sub1 - doAssert not (base of Sub2) - - base = Sub2() # re-assign - doAssert base of Sub2 - doAssert Sub2(base) != nil # upcast - doAssertRaises(ObjectConversionDefect): discard Sub1(base) - - var sub1 = Sub1() - doAssert sub1 of Base - doAssert sub1.Base of Sub1 - - doAssert not compiles(base of Unrelated) +proc `of`*[T, S](x: typedesc[T], y: typedesc[S]): bool {.magic: "Of", noSideEffect.} +proc `of`*[T, S](x: T, y: typedesc[S]): bool {.magic: "Of", noSideEffect.} +proc `of`*[T, S](x: T, y: S): bool {.magic: "Of", noSideEffect.} + ## Checks if `x` has a type of `y`. + ## + ## .. code-block:: Nim + ## assert(FloatingPointDefect of Exception) + ## assert(DivByZeroDefect of Exception) proc cmp*[T](x, y: T): int = ## Generic compare proc. @@ -849,10 +898,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. - ## ``` - ## import std/algorithm - ## echo sorted(@[4, 2, 6, 5, 8, 7], cmp[int]) - ## ``` + ## + ## .. code-block:: Nim + ## import algorithm + ## echo sorted(@[4, 2, 6, 5, 8, 7], cmp[int]) if x == y: return 0 if x < y: return -1 return 1 @@ -863,141 +912,145 @@ proc cmp*(x, y: string): int {.noSideEffect.} ## **Note**: The precise result values depend on the used C runtime library and ## can differ between operating systems! -proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {.magic: "ArrToSeq", noSideEffect.} - ## Turns an array into a sequence. - ## - ## This most often useful for constructing - ## sequences with the array constructor: `@[1, 2, 3]` has the type - ## `seq[int]`, while `[1, 2, 3]` has the type `array[0..2, int]`. - ## - ## ``` - ## let - ## a = [1, 3, 5] - ## b = "foo" - ## - ## echo @a # => @[1, 3, 5] - ## echo @b # => @['f', 'o', 'o'] - ## ``` +when defined(nimHasDefault): + proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {. + magic: "ArrToSeq", noSideEffect.} + ## Turns an array into a sequence. + ## + ## This most often useful for constructing + ## 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`. - runnableExamples: - assert (int, float).default == (0, 0.0) - # note: `var a = default(T)` is usually the same as `var a: T` and (currently) generates - # a value whose binary representation is all 0, regardless of whether this - # would violate type constraints such as `range`, `not nil`, etc. This - # property is required to implement certain algorithms efficiently which - # may require intermediate invalid states. - type Foo = object - a: range[2..6] - var a1: range[2..6] # currently, this compiles - # var a2: Foo # currently, this errors: Error: The Foo type doesn't have a default value. - # var a3 = Foo() # ditto - var a3 = Foo.default # this works, but generates a `UnsafeDefault` warning. - # note: the doc comment also explains why `default` can't be implemented - # via: `template default*[T](t: typedesc[T]): T = (var v: T; v)` + proc default*(T: typedesc): T {.magic: "Default", noSideEffect.} + ## returns the default value of the type ``T``. + proc reset*[T](obj: var T) {.noSideEffect.} = + ## Resets an object `obj` to its default value. + obj = default(typeof(obj)) -proc reset*[T](obj: var T) {.noSideEffect.} = - ## Resets an object `obj` to its default value. - obj = default(typeof(obj)) +else: + proc `@`* [IDX, T](a: array[IDX, T]): seq[T] {. + magic: "ArrToSeq", noSideEffect.} + when defined(nimV2): + proc reset*[T](obj: var T) {.magic: "Destroy", noSideEffect.} + else: + proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.} proc setLen*[T](s: var seq[T], newlen: Natural) {. magic: "SetLengthSeq", noSideEffect.} - ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type. + ## Sets the length of seq `s` to `newlen`. ``T`` may be any sequence type. ## ## If the current length is greater than the new length, - ## `s` will be truncated. - ## ``` + ## ``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.} ## Sets the length of string `s` to `newlen`. ## ## If the current length is greater than the new length, - ## `s` will be truncated. - ## ``` - ## var myS = "Nim is great!!" - ## myS.setLen(3) # myS <- "Nim" - ## echo myS, " is fantastic!!" - ## ``` + ## ``s`` will be truncated. + ## + ## .. code-block:: Nim + ## var myS = "Nim is great!!" + ## myS.setLen(3) # myS <- "Nim" + ## echo myS, " is fantastic!!" proc newString*(len: Natural): string {. magic: "NewString", importc: "mnewString", noSideEffect.} - ## Returns a new string of length `len` but with uninitialized + ## Returns a new string of length ``len`` but with uninitialized ## content. One needs to fill the string character after character - ## with the index operator `s[i]`. + ## with the index operator ``s[i]``. ## ## This procedure exists only for optimization purposes; - ## the same effect can be achieved with the `&` operator or with `add`. + ## the same effect can be achieved with the ``&`` operator or with ``add``. proc newStringOfCap*(cap: Natural): string {. magic: "NewStringOfCap", importc: "rawNewString", noSideEffect.} - ## Returns a new string of length `0` but with capacity `cap`. + ## Returns a new string of length ``0`` but with capacity `cap`. ## ## This procedure exists only for optimization purposes; the same effect can - ## be achieved with the `&` operator or with `add`. + ## be achieved with the ``&`` operator or with ``add``. proc `&`*(x: string, y: char): string {. - magic: "ConStrStr", noSideEffect.} + magic: "ConStrStr", noSideEffect, merge.} ## Concatenates `x` with `y`. - ## ``` + ## + ## .. code-block:: Nim ## assert("ab" & 'c' == "abc") - ## ``` proc `&`*(x, y: char): string {. - magic: "ConStrStr", noSideEffect.} + magic: "ConStrStr", noSideEffect, merge.} ## Concatenates characters `x` and `y` into a string. - ## ``` + ## + ## .. code-block:: Nim ## assert('a' & 'b' == "ab") - ## ``` proc `&`*(x, y: string): string {. - magic: "ConStrStr", noSideEffect.} + magic: "ConStrStr", noSideEffect, merge.} ## Concatenates strings `x` and `y`. - ## ``` + ## + ## .. code-block:: Nim ## assert("ab" & "cd" == "abcd") - ## ``` proc `&`*(x: char, y: string): string {. - magic: "ConStrStr", noSideEffect.} + magic: "ConStrStr", noSideEffect, merge.} ## 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.} = +proc add*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} ## Concatenates `x` and `y` in place. ## - ## See also `strbasics.add`. - runnableExamples: - var tmp = "" - tmp.add("ab") - tmp.add("cd") - assert tmp == "abcd" + ## .. code-block:: Nim + ## var tmp = "" + ## tmp.add("ab") + ## tmp.add("cd") + ## assert(tmp == "abcd") + type Endianness* = enum ## Type describing the endianness of a processor. littleEndian, bigEndian const - cpuEndian* {.magic: "CpuEndian".}: Endianness = littleEndian + 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 ## magic. @@ -1015,7 +1068,7 @@ const ## Possible values: ## `"i386"`, `"alpha"`, `"powerpc"`, `"powerpc64"`, `"powerpc64el"`, ## `"sparc"`, `"amd64"`, `"mips"`, `"mipsel"`, `"arm"`, `"arm64"`, - ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`, '"loongarch64"'. + ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`. seqShallowFlag = low(int) strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \ @@ -1025,10 +1078,12 @@ const const hasThreadSupport = compileOption("threads") and not defined(nimscript) hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own + taintMode = compileOption("taintmode") + nimEnableCovariance* = defined(nimEnableCovariance) # or true when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): # tcc doesn't support TLS - {.error: "`--tlsEmulation:on` must be used when using threads with tcc backend".} + {.error: "``--tlsEmulation:on`` must be used when using threads with tcc backend".} when defined(boehmgc): when defined(windows): @@ -1039,16 +1094,29 @@ when defined(boehmgc): elif defined(macosx): const boehmLib = "libgc.dylib" elif defined(openbsd): - const boehmLib = "libgc.so.(4|5).0" + const boehmLib = "libgc.so.4.0" elif defined(freebsd): const boehmLib = "libgc-threaded.so.1" else: const boehmLib = "libgc.so.1" {.pragma: boehmGC, noconv, dynlib: boehmLib.} -when not defined(nimPreviewSlimSystem): - type TaintedString* {.deprecated: "Deprecated since 1.5".} = string +when taintMode: + type TaintedString* = distinct string ## A distinct string type that + ## is `tainted`:idx:, see `taint mode + ## `_ + ## for details. It is an alias for + ## ``string`` if the taint mode is not + ## turned on. + proc len*(s: TaintedString): int {.borrow.} +else: + type TaintedString* = string ## A distinct string type that + ## is `tainted`:idx:, see `taint mode + ## `_ + ## for details. It is an alias for + ## ``string`` if the taint mode is not + ## turned on. when defined(profiler) and not defined(nimscript): proc nimProfile() {.compilerproc, noinline.} @@ -1071,8 +1139,6 @@ when not defined(js) and hostOS != "standalone": ## deprecated, prefer `quit` or `exitprocs.getProgramResult`, `exitprocs.setProgramResult`. import std/private/since -import system/ctypes -export ctypes proc align(address, alignment: int): int = if alignment == 0: # Actually, this is illegal. This branch exists to actively @@ -1081,9 +1147,49 @@ proc align(address, alignment: int): int = else: result = (address + (alignment - 1)) and not (alignment - 1) -include system/rawquits -when defined(genode): - export GenodeEnv +when defined(nimdoc): + proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.} + ## Stops the program immediately with an exit code. + ## + ## Before stopping the program the "exit procedures" are called in the + ## opposite order they were added with `addExitProc `_. + ## ``quit`` never returns and ignores any exception that may have been raised + ## by the quit procedures. It does *not* call the garbage collector to free + ## all the memory, unless a quit procedure calls `GC_fullCollect + ## <#GC_fullCollect>`_. + ## + ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim + ## program finishes without incident for platforms where this is the + ## expected behavior. A raised unhandled exception is + ## equivalent to calling ``quit(QuitFailure)``. + ## + ## Note that this is a *runtime* call and using ``quit`` inside a macro won't + ## have any compile time effect. If you need to stop the compiler inside a + ## macro, use the `error `_ or `fatal + ## `_ pragmas. + +elif defined(genode): + include genode/env + + var systemEnv {.exportc: runtimeEnvSym.}: GenodeEnvPtr + + type GenodeEnv* = GenodeEnvPtr + ## Opaque type representing Genode environment. + + proc quit*(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn, + importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "".} + + proc quit*(errorcode: int = QuitSuccess) = + systemEnv.quit(errorcode) + +elif defined(js) and defined(nodejs) and not defined(nimscript): + proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", + importc: "process.exit", noreturn.} + +else: + proc quit*(errorcode: int = QuitSuccess) {. + magic: "Exit", importc: "exit", header: "", noreturn.} + template sysAssert(cond: bool, msg: string) = when defined(useSysAssert): @@ -1091,7 +1197,7 @@ template sysAssert(cond: bool, msg: string) = cstderr.rawWrite "[SYSASSERT] " cstderr.rawWrite msg cstderr.rawWrite "\n" - rawQuit 1 + quit 1 const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript) @@ -1117,13 +1223,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) @@ -1142,13 +1248,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) @@ -1156,33 +1262,57 @@ else: when defined(nimSeqsV2): - template movingCopy(a, b: typed) = + template movingCopy(a, b) = a = move(b) else: - template movingCopy(a, b: typed) = + template movingCopy(a, b) = shallowCopy(a, b) proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} = - ## Deletes the item at index `i` by putting `x[high(x)]` into position `i`. + ## Deletes the item at index `i` by putting ``x[high(x)]`` into position `i`. ## ## This is an `O(1)` operation. ## ## See also: ## * `delete <#delete,seq[T],Natural>`_ for preserving the order - runnableExamples: - var a = @[10, 11, 12, 13, 14] - a.del(2) - assert a == @[10, 11, 14, 13] + ## + ## .. code-block:: Nim + ## var i = @[1, 2, 3, 4, 5] + ## i.del(2) # => @[1, 2, 5, 4] let xl = x.len - 1 movingCopy(x[i], x[xl]) setLen(x, xl) +proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = + ## Deletes the item at index `i` by moving all ``x[i+1..]`` items by one position. + ## + ## This is an `O(n)` operation. + ## + ## See also: + ## * `del <#delete,seq[T],Natural>`_ for O(1) operation + ## + ## .. code-block:: Nim + ## var i = @[1, 2, 3, 4, 5] + ## i.delete(2) # => @[1, 2, 4, 5] + template defaultImpl = + let xl = x.len + for j in i.int..xl-2: movingCopy(x[j], x[j+1]) + setLen(x, xl-1) + + when nimvm: + defaultImpl() + else: + when defined(js): + {.emit: "`x`.splice(`i`, 1);".} + else: + defaultImpl() + proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} = ## Inserts `item` into `x` at position `i`. - ## ``` - ## var i = @[1, 3, 5] - ## i.insert(99, 0) # i <- @[99, 1, 3, 5] - ## ``` + ## + ## .. code-block:: Nim + ## var i = @[1, 3, 5] + ## i.insert(99, 0) # i <- @[99, 1, 3, 5] {.noSideEffect.}: template defaultImpl = let xl = x.len @@ -1210,17 +1340,164 @@ when not defined(nimV2): ## ## It works even for complex data graphs with cycles. This is a great ## debugging tool. - ## ``` - ## 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] - ## ``` + ## + ## .. 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] -when not defined(nimPreviewSlimSystem): +type + ByteAddress* = int + ## is the signed integer type that should be used for converting + ## pointers to integer addresses for readability. + + 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 - 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. + 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.} = char + ## This is the same as the type ``unsigned char`` in *C*. + 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 toFloat*(i: int): float {.noSideEffect, inline.} = + ## Converts an integer `i` into a ``float``. + ## + ## 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.} = + ## Same as `toFloat <#toFloat,int>`_ but for ``BiggestInt`` to ``BiggestFloat``. + BiggestFloat(i) + +proc toInt*(f: float): int {.noSideEffect.} = + ## Converts a floating point number `f` into an ``int``. + ## + ## Conversion rounds `f` half away from 0, see + ## `Round half away from zero + ## `_. + ## + ## 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. + ## + ## 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`. + ## + ## 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 + ## + ## swap(a, b) + ## + ## 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) = + # Optimize swapping of array elements if they are refs. Default swap + # implementation will cause unsureAsgnRef to be emitted which causes + # unnecessary slow down in this case. + swap(cast[ptr pointer](addr arr[a])[], cast[ptr pointer](addr arr[b])[]) const Inf* = 0x7FF0000000000000'f64 @@ -1231,65 +1508,21 @@ const ## 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 + ## and expect a reasonable result - use the `classify` procedure ## in the `math module `_ for checking for NaN. -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)`. - ## - ## If the conversion fails, `ValueError` is raised. - ## However, on most platforms the conversion cannot fail. - ## - ## ``` - ## let - ## a = 2 - ## b = 3.7 - ## - ## echo a.toFloat + b # => 5.7 - ## ``` - float(i) +include "system/memalloc" -proc toBiggestFloat*(i: BiggestInt): BiggestFloat {.noSideEffect, inline.} = - ## Same as `toFloat <#toFloat,int>`_ but for `BiggestInt` to `BiggestFloat`. - BiggestFloat(i) -proc toInt*(f: float): int {.noSideEffect.} = - ## Converts a floating point number `f` into an `int`. - ## - ## Conversion rounds `f` half away from 0, see - ## `Round half away from zero - ## `_, - ## as opposed to a type conversion which rounds towards zero. - ## - ## Note that some floating point numbers (e.g. infinity or even 1e19) - ## cannot be accurately converted. - ## ``` - ## 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 `|`*(a, b: typedesc): typedesc = discard -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) +include "system/iterators_1" -proc `/`*(x, y: int): float {.inline, noSideEffect.} = - ## Division of integers that results in a float. - ## ``` - ## echo 7 / 5 # => 1.4 - ## ``` - ## - ## 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: @@ -1309,115 +1542,72 @@ proc abs*[T: float64 | float32](x: T): T {.noSideEffect, inline.} = 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 +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 -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`. - ## - ## This is often more efficient than `tmp = a; a = b; b = tmp`. - ## Particularly useful for sorting algorithms. - ## - ## ``` - ## var - ## a = 5 - ## b = 9 - ## - ## swap(a, b) - ## - ## 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) = - # Optimize swapping of array elements if they are refs. Default swap - # implementation will cause unsureAsgnRef to be emitted which causes - # unnecessary slow down in this case. - swap(cast[ptr pointer](addr arr[a])[], cast[ptr pointer](addr arr[b])[]) - -when not defined(nimscript): - {.push stackTrace: off, profiler: off.} - - when not defined(nimPreviewSlimSystem): - import std/sysatomics - export sysatomics - else: - import std/sysatomics - - {.pop.} - -include "system/memalloc" - - -proc `|`*(a, b: typedesc): typedesc = discard - -include "system/iterators_1" +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 defined(nimNoNilSeqs2): + when not compileOption("nilseqs"): + {.pragma: nilError, error.} + else: + {.pragma: nilError.} +else: + {.pragma: nilError.} + +proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil", nilError.} + ## Requires `--nilseqs:on` since 0.19. + ## + ## Seqs are no longer nil by default, but set and empty. + ## Check for zero length instead. + ## + ## See also: + ## * `isNil(string) <#isNil,string>`_ + proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".} +proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil", nilError.} + ## Requires `--nilseqs:on`. + ## + ## See also: + ## * `isNil(seq[T]) <#isNil,seq[T]>`_ proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".} proc isNil*(x: pointer): bool {.noSideEffect, magic: "IsNil".} proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".} proc isNil*[T: proc](x: T): bool {.noSideEffect, magic: "IsNil".} ## Fast check whether `x` is nil. This is sometimes more efficient than - ## `== nil`. + ## ``== nil``. -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] +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): @@ -1426,12 +1616,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]) @@ -1442,12 +1632,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]) @@ -1457,9 +1647,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: @@ -1471,12 +1661,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] @@ -1487,12 +1677,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] @@ -1502,15 +1692,19 @@ 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 @@ -1519,12 +1713,12 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[ ## While similar to the `caller info`:idx: of other languages, it is determined ## at compile time. ## - ## This proc is mostly useful for meta programming (eg. `assert` template) + ## This proc is mostly useful for meta programming (eg. ``assert`` template) ## to retrieve information about the current filename and line number. ## Example: ## - ## ``` - ## import std/strutils + ## .. code-block:: nim + ## import strutils ## ## template testException(exception, code: untyped): typed = ## try: @@ -1545,18 +1739,27 @@ 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 - import system/memory + import "system/ansi_c" + import "system/memory" {.push stackTrace: off.} when not defined(js) and hasThreadSupport and hostOS != "standalone": - import std/private/syslocks + const insideRLocksModule = false + include "system/syslocks" include "system/threadlocalstorage" when not defined(js) and defined(nimV2): @@ -1565,14 +1768,11 @@ when not defined(js) and defined(nimV2): TNimTypeV2 {.compilerproc.} = object destructor: pointer size: int - align: int16 - depth: int16 - display: ptr UncheckedArray[uint32] # classToken - when defined(nimTypeNames): - name: cstring + align: int + name: cstring traceImpl: pointer + disposeImpl: pointer typeInfoV1: pointer # for backwards compat, usually nil - flags: int PNimTypeV2 = ptr TNimTypeV2 when notJSnotNims and defined(nimSeqsV2): @@ -1583,26 +1783,34 @@ when notJSnotNims and defined(nimSeqsV2): when not defined(nimscript): proc writeStackTrace*() {.tags: [], gcsafe, raises: [].} - ## Writes the current stack trace to `stderr`. This is only works + ## Writes the current stack trace to ``stderr``. This is only works ## for debug builds. Since it's usually used for debugging, this ## is proclaimed to have no IO effect! when not declared(sysFatal): include "system/fatal" +when not defined(nimscript): + {.push stackTrace: off, profiler: off.} + + proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, + discardable, benign.} + ## Atomic increment of `memLoc`. Returns the value after the operation. + + proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, + discardable, benign.} + ## Atomic decrement of `memLoc`. Returns the value after the operation. + + include "system/atomics" + + {.pop.} + when defined(nimV2): include system/arc -template newException*(exceptn: typedesc, message: string; - parentException: ref Exception = nil): untyped = - ## Creates an exception object of type `exceptn` and sets its `msg` field - ## to `message`. Returns the new exception object. - (ref exceptn)(msg: message, parent: parentException) - -when not defined(nimPreviewSlimSystem): - import std/assertions - export assertions +import system/assertions +export assertions import system/iterators export iterators @@ -1619,23 +1827,21 @@ proc find*[T, S](a: T, item: S): int {.inline.}= proc contains*[T](a: openArray[T], item: T): bool {.inline.}= ## Returns true if `item` is in `a` or false if not found. This is a shortcut - ## for `find(a, item) >= 0`. + ## for ``find(a, item) >= 0``. ## ## 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.} = - ## Returns the last item of `s` and decreases `s.len` by one. This treats + ## Returns the last item of `s` and decreases ``s.len`` by one. This treats ## `s` as a stack and implements the common *pop* operation. - ## - ## Raises `IndexDefect` if `s` is empty. runnableExamples: var a = @[1, 3, 5, 7] let b = pop(a) @@ -1651,14 +1857,14 @@ proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} = setLen(s, L) proc `==`*[T: tuple|object](x, y: T): bool = - ## Generic `==` operator for tuples that is lifted from the components. + ## Generic ``==`` operator for tuples that is lifted from the components. ## of `x` and `y`. for a, b in fields(x, y): if a != b: return false return true proc `<=`*[T: tuple](x, y: T): bool = - ## Generic lexicographic `<=` operator for tuples that is lifted from the + ## Generic lexicographic ``<=`` operator for tuples that is lifted from the ## components of `x` and `y`. This implementation uses `cmp`. for a, b in fields(x, y): var c = cmp(a, b) @@ -1667,7 +1873,7 @@ proc `<=`*[T: tuple](x, y: T): bool = return true proc `<`*[T: tuple](x, y: T): bool = - ## Generic lexicographic `<` operator for tuples that is lifted from the + ## Generic lexicographic ``<`` operator for tuples that is lifted from the ## components of `x` and `y`. This implementation uses `cmp`. for a, b in fields(x, y): var c = cmp(a, b) @@ -1681,7 +1887,25 @@ include "system/gc_interface" # we have to compute this here before turning it off in except.nim anyway ... const NimStackTrace = compileOption("stacktrace") -import system/coro_detection +template coroutinesSupportedPlatform(): bool = + when defined(sparc) or defined(ELATE) or compileOption("gc", "v2") or + defined(boehmgc) or defined(gogc) or defined(nogc) or defined(gcRegions) or + defined(gcMarkAndSweep): + false + else: + true + +when defined(nimCoroutines): + # Explicit opt-in. + when not coroutinesSupportedPlatform(): + {.error: "Coroutines are not supported on this architecture and/or garbage collector.".} + const nimCoroutines* = true +elif defined(noNimCoroutines): + # Explicit opt-out. + const nimCoroutines* = false +else: + # Autodetect coroutine support. + const nimCoroutines* = false {.push checks: off.} # obviously we cannot generate checking operations here :-) @@ -1695,9 +1919,10 @@ when notJSnotNims: ## With this hook you can influence exception handling on a global level. ## If not nil, every 'raise' statement ends up calling this hook. ## - ## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this. + ## **Warning**: Ordinary application code should never set this hook! + ## You better know what you do when setting this. ## - ## If `globalRaiseHook` returns false, the exception is caught and does + ## If ``globalRaiseHook`` returns false, the exception is caught and does ## not propagate further through the call stack. localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.} @@ -1705,9 +1930,10 @@ when notJSnotNims: ## thread local level. ## If not nil, every 'raise' statement ends up calling this hook. ## - ## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this. + ## **Warning**: Ordinary application code should never set this hook! + ## You better know what you do when setting this. ## - ## If `localRaiseHook` returns false, the exception + ## If ``localRaiseHook`` returns false, the exception ## is caught and does not propagate further through the call stack. outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].} @@ -1717,7 +1943,8 @@ 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" @@ -1726,7 +1953,6 @@ when notJSnotNims: ## raise gOutOfMem ## ## system.outOfMemHook = handleOOM - ## ``` ## ## If the handler does not raise an exception, ordinary control flow ## continues and the program is terminated. @@ -1750,14 +1976,8 @@ type when NimStackTraceMsgs: frameMsgLen*: int ## end position in frameMsgBuf for this frame. -when defined(js) or defined(nimdoc): +when defined(js): proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = - ## Appends `y` to `x` in place. - runnableExamples: - var tmp = "" - tmp.add(cstring("ab")) - tmp.add(cstring("cd")) - doAssert tmp == "abcd" asm """ if (`x` === null) { `x` = []; } var off = `x`.length; @@ -1766,15 +1986,7 @@ when defined(js) or defined(nimdoc): `x`[off+i] = `y`.charCodeAt(i); } """ - proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".} = - ## Appends `y` to `x` in place. - ## Only implemented for JS backend. - runnableExamples: - when defined(js): - var tmp: cstring = "" - tmp.add(cstring("ab")) - tmp.add(cstring("cd")) - doAssert tmp == cstring("abcd") + proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".} elif hasAlloc: {.push stackTrace: off, profiler: off.} @@ -1786,32 +1998,45 @@ elif hasAlloc: inc(i) {.pop.} -proc echo*(x: varargs[typed, `$`]) {.magic: "Echo", benign, sideEffect.} - ## Writes and flushes the parameters to the standard output. - ## - ## Special built-in that takes a variable number of arguments. Each argument - ## is converted to a string via `$`, so it works for user-defined - ## types that have an overloaded `$` operator. - ## It is roughly equivalent to `writeLine(stdout, x); flushFile(stdout)`, but - ## available for the JavaScript target too. - ## - ## Unlike other IO operations this is guaranteed to be thread-safe as - ## `echo` is very often used for debugging convenience. If you want to use - ## `echo` inside a `proc without side effects - ## `_ you can use `debugEcho - ## <#debugEcho,varargs[typed,]>`_ instead. +when defined(nimvarargstyped): + proc echo*(x: varargs[typed, `$`]) {.magic: "Echo", tags: [WriteIOEffect], + benign, sideEffect.} + ## Writes and flushes the parameters to the standard output. + ## + ## Special built-in that takes a variable number of arguments. Each argument + ## is converted to a string via ``$``, so it works for user-defined + ## types that have an overloaded ``$`` operator. + ## It is roughly equivalent to ``writeLine(stdout, x); flushFile(stdout)``, but + ## available for the JavaScript target too. + ## + ## Unlike other IO operations this is guaranteed to be thread-safe as + ## ``echo`` is very often used for debugging convenience. If you want to use + ## ``echo`` inside a `proc without side effects + ## `_ you can use `debugEcho + ## <#debugEcho,varargs[typed,]>`_ instead. -proc debugEcho*(x: varargs[typed, `$`]) {.magic: "Echo", noSideEffect, - tags: [], raises: [].} - ## Same as `echo <#echo,varargs[typed,]>`_, but as a special semantic rule, - ## `debugEcho` pretends to be free of side effects, so that it can be used - ## for debugging routines marked as `noSideEffect - ## `_. + proc debugEcho*(x: varargs[typed, `$`]) {.magic: "Echo", noSideEffect, + tags: [], raises: [].} + ## Same as `echo <#echo,varargs[typed,]>`_, but as a special semantic rule, + ## ``debugEcho`` pretends to be free of side effects, so that it can be used + ## for debugging routines marked as `noSideEffect + ## `_. +else: + proc echo*(x: varargs[untyped, `$`]) {.magic: "Echo", tags: [WriteIOEffect], + benign, sideEffect.} + proc debugEcho*(x: varargs[untyped, `$`]) {.magic: "Echo", noSideEffect, + tags: [], raises: [].} + +template newException*(exceptn: typedesc, message: string; + parentException: ref Exception = nil): untyped = + ## Creates an exception object of type ``exceptn`` and sets its ``msg`` field + ## to `message`. Returns the new exception object. + (ref exceptn)(msg: message, parent: parentException) when hostOS == "standalone" and defined(nogc): proc nimToCStringConv(s: NimString): cstring {.compilerproc, inline.} = if s == nil or s.len == 0: result = cstring"" - else: result = cast[cstring](addr s.data) + else: result = cstring(addr s.data) proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.} ## Get type information for `x`. @@ -1819,6 +2044,22 @@ 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): @@ -1831,13 +2072,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. @@ -1855,13 +2096,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. @@ -1873,54 +2114,39 @@ 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 = 5 + ## 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 -when defined(nimAuditDelete): - {.pragma: auditDelete, deprecated: "review this call for out of bounds behavior".} -else: - {.pragma: auditDelete.} - -proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect, auditDelete.} = - ## Deletes the item at index `i` by moving all `x[i+1..^1]` items by one position. - ## - ## This is an `O(n)` operation. - ## - ## .. note:: With `-d:nimStrictDelete`, an index error is produced when the index passed - ## to it was out of bounds. `-d:nimStrictDelete` will become the default - ## in upcoming versions. - ## - ## See also: - ## * `del <#del,seq[T],Natural>`_ for O(1) operation - ## - runnableExamples: - var s = @[1, 2, 3, 4, 5] - s.delete(2) - doAssert s == @[1, 2, 4, 5] - - when defined(nimStrictDelete): - if i > high(x): - # xxx this should call `raiseIndexError2(i, high(x))` after some refactoring - raise (ref IndexDefect)(msg: "index out of bounds: '" & $i & "' < '" & $x.len & "' failed") - - template defaultImpl = - let xl = x.len - for j in i.int..xl-2: movingCopy(x[j], x[j+1]) - setLen(x, xl-1) - - when nimvm: - defaultImpl() - else: - when defined(js): - {.emit: "`x`.splice(`i`, 1);".} - else: - defaultImpl() - - 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.} @@ -1973,7 +2199,7 @@ when notJSnotNims: proc equalMem(a, b: pointer, size: Natural): bool = nimCmpMem(a, b, size) == 0 proc cmpMem(a, b: pointer, size: Natural): int = - nimCmpMem(a, b, size).int + nimCmpMem(a, b, size) when not defined(js): proc cmp(x, y: string): int = @@ -1990,14 +2216,14 @@ when not defined(js): when declared(newSeq): proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] = - ## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be - ## of length `len`. + ## Converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be + ## of length ``len``. newSeq(result, len) for i in 0..len-1: result[i] = $a[i] proc cstringArrayToSeq*(a: cstringArray): seq[string] = - ## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be - ## terminated by `nil`. + ## Converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be + ## terminated by ``nil``. var L = 0 while a[L] != nil: inc(L) result = cstringArrayToSeq(a, L) @@ -2035,12 +2261,7 @@ when not defined(js): when declared(initAllocator): initAllocator() when hasThreadSupport: - when hostOS != "standalone": - include system/threadimpl - when not defined(nimPreviewSlimSystem): - import std/typedthreads - export typedthreads - + when hostOS != "standalone": include "system/threads" elif not defined(nogc) and not defined(nimscript): when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom() when declared(initGC): initGC() @@ -2049,16 +2270,6 @@ when notJSnotNims: proc setControlCHook*(hook: proc () {.noconv.}) ## Allows you to override the behaviour of your application when CTRL+C ## is pressed. Only one such hook is supported. - ## Example: - ## - ## ``` - ## proc ctrlc() {.noconv.} = - ## echo "Ctrl+C fired!" - ## # do clean up stuff - ## quit() - ## - ## setControlCHook(ctrlc) - ## ``` when not defined(noSignalHandler) and not defined(useNimRtl): proc unsetControlCHook*() @@ -2070,7 +2281,7 @@ when notJSnotNims: proc getStackTrace*(e: ref Exception): string {.gcsafe.} ## Gets the stack trace associated with `e`, which is the stack that - ## lead to the `raise` statement. This only works for debug builds. + ## lead to the ``raise`` statement. This only works for debug builds. {.push stackTrace: off, profiler: off.} when defined(memtracker): @@ -2084,7 +2295,10 @@ when notJSnotNims: # we cannot compile this with stack tracing on # as it would recurse endlessly! - include "system/integerops" + when defined(nimNewIntegerOps): + include "system/integerops" + else: + include "system/arithm" {.pop.} @@ -2099,7 +2313,6 @@ when notJSnotNims: when hostOS != "standalone" and hostOS != "any": include "system/dyncalls" - import system/countbits_impl include "system/sets" when defined(gogc): @@ -2126,7 +2339,7 @@ when notJSnotNims: if discr < cast[uint](n.len): result = n.sons[discr] if result == nil: result = n.sons[n.len] - # n.sons[n.len] contains the `else` part (but may be nil) + # n.sons[n.len] contains the ``else`` part (but may be nil) else: result = n.sons[n.len] @@ -2146,8 +2359,7 @@ when notJSnotNims and hasAlloc: include "system/repr" when notJSnotNims and hasThreadSupport and hostOS != "standalone": - when not defined(nimPreviewSlimSystem): - include "system/channels_builtin" + include "system/channels" when notJSnotNims and hostOS != "standalone": @@ -2168,7 +2380,7 @@ when notJSnotNims and hostOS != "standalone": proc setCurrentException*(exc: ref Exception) {.inline, benign.} = ## Sets the current exception. ## - ## .. warning:: Only use this if you know what you are doing. + ## **Warning**: Only use this if you know what you are doing. currException = exc elif defined(nimscript): proc getCurrentException*(): ref Exception {.compilerRtl.} = discard @@ -2181,22 +2393,17 @@ when notJSnotNims: proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} = ## Retrieves the raw proc pointer of the closure `x`. This is - ## useful for interfacing closures with C/C++, hash compuations, etc. + ## useful for interfacing closures with C. when T is "closure": - #[ - The conversion from function pointer to `void*` is a tricky topic, but this - should work at least for c++ >= c++11, e.g. for `dlsym` support. - refs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869, - https://stackoverflow.com/questions/14125474/casts-between-pointer-to-function-and-pointer-to-object-in-c-and-c - ]# {.emit: """ - `result` = (void*)`x`.ClP_0; + `result` = `x`.ClP_0; """.} else: {.error: "Only closure function and iterator are allowed!".} proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} = - ## Retrieves the raw environment pointer of the closure `x`. See also `rawProc`. + ## Retrieves the raw environment pointer of the closure `x`. This is + ## useful for interfacing closures with C. when T is "closure": {.emit: """ `result` = `x`.ClE_0; @@ -2204,7 +2411,7 @@ when notJSnotNims: else: {.error: "Only closure function and iterator are allowed!".} - proc finished*[T: proc](x: T): bool {.noSideEffect, inline, magic: "Finished".} = + proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} = ## It can be used to determine if a first class iterator has finished. when T is "iterator": {.emit: """ @@ -2213,73 +2420,19 @@ when notJSnotNims: else: {.error: "Only closure iterator is allowed!".} -from std/private/digitsutils import addInt -export addInt - when defined(js): include "system/jssys" include "system/reprjs" +when defined(js) or defined(nimscript): + proc addInt*(result: var string; x: int64) = + result.add $x -when defined(nimNoQuit): - proc quit*(errorcode: int = QuitSuccess) = discard "ignoring quit" - -elif defined(nimdoc): - proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.} - ## Stops the program immediately with an exit code. - ## - ## Before stopping the program the "exit procedures" are called in the - ## opposite order they were added with `addExitProc `_. - ## - ## The proc `quit(QuitSuccess)` is called implicitly when your nim - ## program finishes without incident for platforms where this is the - ## expected behavior. A raised unhandled exception is - ## equivalent to calling `quit(QuitFailure)`. - ## - ## Note that this is a *runtime* call and using `quit` inside a macro won't - ## have any compile time effect. If you need to stop the compiler inside a - ## macro, use the `error `_ or `fatal - ## `_ pragmas. - ## - ## .. warning:: `errorcode` gets saturated when it exceeds the valid range - ## on the specific platform. On Posix, the valid range is `low(int8)..high(int8)`. - ## On Windows, the valid range is `low(int32)..high(int32)`. For instance, - ## `quit(int(0x100000000))` is equal to `quit(127)` on Linux. - ## - ## .. danger:: In almost all cases, in particular in library code, prefer - ## alternatives, e.g. `doAssert false` or raise a `Defect`. - ## `quit` bypasses regular control flow in particular `defer`, - ## `try`, `catch`, `finally` and `destructors`, and exceptions that may have been - ## raised by an `addExitProc` proc, as well as cleanup code in other threads. - ## It does *not* call the garbage collector to free all the memory, - ## unless an `addExitProc` proc calls `GC_fullCollect <#GC_fullCollect>`_. - -elif defined(genode): - proc quit*(errorcode: int = QuitSuccess) {.inline, noreturn.} = - rawQuit(errorcode) - -elif defined(js) and defined(nodejs) and not defined(nimscript): - proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", - importc: "process.exit", noreturn.} - -else: - proc quit*(errorcode: int = QuitSuccess) {.inline, noreturn.} = - when defined(posix): # posix uses low 8 bits - type ExitCodeRange = int8 - else: # win32 uses low 32 bits - type ExitCodeRange = cint - when sizeof(errorcode) > sizeof(ExitCodeRange): - if errorcode < low(ExitCodeRange): - rawQuit(low(ExitCodeRange).cint) - elif errorcode > high(ExitCodeRange): - rawQuit(high(ExitCodeRange).cint) - else: - rawQuit(errorcode.cint) - else: - rawQuit(errorcode.cint) + proc addFloat*(result: var string; x: float) = + result.add $x proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} = - ## A shorthand for `echo(errormsg); quit(errorcode)`. + ## A shorthand for ``echo(errormsg); quit(errorcode)``. when defined(nimscript) or defined(js) or (hostOS == "standalone"): echo errormsg else: @@ -2291,57 +2444,316 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} = quit(errorcode) {.pop.} # checks: off -# {.pop.} # hints: off +{.pop.} # hints: off -include "system/indices" +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](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](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 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. ## - ## If not specialized further, an alias for `add`. + ## 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) = ## Helper for performing user-defined range checks. - ## Such checks will be performed only when the `rangechecks` + ## Such checks will be performed only when the ``rangechecks`` ## compile-time option is enabled. if not cond: sysFatal(RangeDefect, "range check failed") else: template rangeCheck*(cond) = discard -when not defined(gcArc) and not defined(gcOrc): - proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = - ## Marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not - ## perform deep copies of `s`. - ## - ## This is only useful for optimization purposes. - if s.len == 0: return - when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2): - var s = cast[PGenericSeq](s) - {.noSideEffect.}: - s.reserved = s.reserved or seqShallowFlag +when not defined(nimhygiene): + {.pragma: inject.} - proc shallow*(s: var string) {.noSideEffect, inline.} = - ## Marks a string `s` as `shallow`:idx:. Subsequent assignments will not - ## perform deep copies of `s`. - ## - ## This is only useful for optimization purposes. - when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2): - var s = cast[PGenericSeq](s) - if s == nil: - s = cast[PGenericSeq](newString(0)) - # string literals cannot become 'shallow': - if (s.reserved and strlitFlag) == 0: - {.noSideEffect.}: - s.reserved = s.reserved or seqShallowFlag +proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = + ## Marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not + ## perform deep copies of `s`. + ## + ## This is only useful for optimization purposes. + if s.len == 0: return + when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2): + var s = cast[PGenericSeq](s) + s.reserved = s.reserved or seqShallowFlag + +proc shallow*(s: var string) {.noSideEffect, inline.} = + ## Marks a string `s` as `shallow`:idx:. Subsequent assignments will not + ## perform deep copies of `s`. + ## + ## This is only useful for optimization purposes. + when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2): + var s = cast[PGenericSeq](s) + if s == nil: + s = cast[PGenericSeq](newString(0)) + # string literals cannot become 'shallow': + if (s.reserved and strlitFlag) == 0: + s.reserved = s.reserved or seqShallowFlag type NimNodeObj = object @@ -2349,35 +2761,36 @@ type NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj ## Represents a Nim AST node. Macros operate on this type. -type - ForLoopStmt* {.compilerproc.} = object ## \ - ## A special type that marks a macro as a `for-loop macro`:idx:. - ## See `"For Loop Macro" `_. +when defined(nimV2): + import system/repr_v2 + export repr_v2 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 defined(nimV2): - import system/repr_v2 - export repr_v2 +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 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: - when defined(gcArc) or defined(gcOrc): - x[j+item.len] = move x[j] - else: - shallowCopy(x[j+item.len], x[j]) + shallowCopy(x[j+item.len], x[j]) dec(j) j = 0 while j < item.len: @@ -2390,33 +2803,32 @@ when declared(initDebugger): proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} = ## Adds a char to string `s` and applies the following escaping: ## - ## * replaces any ``\`` by `\\` - ## * replaces any `'` by `\'` - ## * replaces any `"` by `\"` - ## * replaces any `\a` by `\\a` - ## * replaces any `\b` by `\\b` - ## * replaces any `\t` by `\\t` - ## * replaces any `\n` by `\\n` - ## * replaces any `\v` by `\\v` - ## * replaces any `\f` by `\\f` - ## * replaces any `\r` by `\\r` - ## * replaces any `\e` by `\\e` - ## * replaces any other character not in the set `{\21..\126}` - ## by `\xHH` where `HH` is its hexadecimal value + ## * replaces any ``\`` by ``\\`` + ## * replaces any ``'`` by ``\'`` + ## * replaces any ``"`` by ``\"`` + ## * replaces any ``\a`` by ``\\a`` + ## * replaces any ``\b`` by ``\\b`` + ## * replaces any ``\t`` by ``\\t`` + ## * replaces any ``\n`` by ``\\n`` + ## * replaces any ``\v`` by ``\\v`` + ## * replaces any ``\f`` by ``\\f`` + ## * replaces any ``\c`` by ``\\c`` + ## * replaces any ``\e`` by ``\\e`` + ## * replaces any other character not in the set ``{'\21..'\126'} + ## by ``\xHH`` where ``HH`` is its hexadecimal value. ## ## The procedure has been designed so that its output is usable for many ## different common syntaxes. ## - ## .. warning:: This is **not correct** for producing ANSI C code! - ## + ## **Note**: This is **not correct** for producing Ansi C code! case c of '\a': s.add "\\a" # \x07 of '\b': s.add "\\b" # \x08 of '\t': s.add "\\t" # \x09 - of '\n': s.add "\\n" # \x0A + of '\L': s.add "\\n" # \x0A of '\v': s.add "\\v" # \x0B of '\f': s.add "\\f" # \x0C - of '\r': (when defined(nimLegacyAddEscapedCharx0D): s.add "\\c" else: s.add "\\r") # \x0D + of '\c': s.add "\\c" # \x0D of '\e': s.add "\\e" # \x1B of '\\': s.add("\\\\") of '\'': s.add("\\'") @@ -2435,9 +2847,9 @@ proc addQuoted*[T](s: var string, x: T) = ## ## See `addEscapedChar <#addEscapedChar,string,char>`_ ## for the escaping scheme. When `x` is a string, characters in the - ## range `{\128..\255}` are never escaped so that multibyte UTF-8 + ## range ``{\128..\255}`` are never escaped so that multibyte UTF-8 ## characters are untouched (note that this behavior is different from - ## `addEscapedChar`). + ## ``addEscapedChar``). ## ## The Nim standard library uses this function on the elements of ## collections when producing a string representation of a collection. @@ -2445,7 +2857,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(", ") @@ -2453,7 +2865,6 @@ 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: @@ -2469,7 +2880,7 @@ proc addQuoted*[T](s: var string, x: T) = s.addEscapedChar(x) s.add("'") # prevent temporary string allocation - elif T is SomeInteger: + elif T is SomeSignedInt: s.addInt(x) elif T is SomeFloat: s.addFloat(x) @@ -2484,10 +2895,10 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## ## This is quite fast as it does not rely ## on any debug or runtime information. Note that in contrast to what - ## the official signature says, the return type is *not* `RootObj` but a + ## 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" @@ -2502,7 +2913,6 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## # -> name a with value something ## # -> name b with value 4 ## # -> B is 1 - ## ``` discard when hasAlloc and notJSnotNims: @@ -2511,10 +2921,10 @@ when hasAlloc and notJSnotNims: ## Performs a deep copy of `y` and copies it into `x`. ## ## This is also used by the code generator - ## for the implementation of `spawn`. + ## for the implementation of ``spawn``. ## - ## For `--gc:arc` or `--gc:orc` deepcopy support has to be enabled - ## via `--deepcopy:on`. + ## For ``--gc:arc`` or ``--gc:orc`` deepcopy support has to be enabled + ## via ``--deepcopy:on``. discard proc deepCopy*[T](y: T): T = @@ -2526,10 +2936,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 @@ -2542,6 +2952,20 @@ proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect, elif x.isNil or y.isNil: result = false else: result = strcmp(x, y) == 0 +when defined(nimNoNilSeqs2) and not compileOption("nilseqs"): + when defined(nimHasUserErrors): + # bug #9149; ensure that 'type(nil)' does not match *too* well by using 'type(nil) | type(nil)'. + # Eventually (in 0.20?) we will be able to remove this hack completely. + proc `==`*(x: string; y: type(nil) | type(nil)): bool {. + error: "'nil' is now invalid for 'string'; compile with --nilseqs:on for a migration period".} = + discard + proc `==`*(x: type(nil) | type(nil); y: string): bool {. + error: "'nil' is now invalid for 'string'; compile with --nilseqs:on for a migration period".} = + discard + else: + proc `==`*(x: string; y: type(nil) | type(nil)): bool {.error.} = discard + proc `==`*(x: type(nil) | type(nil); y: string): bool {.error.} = discard + template closureScope*(body: untyped): untyped = ## Useful when creating a closure in a loop to capture local loop variables by ## their current iteration values. @@ -2551,7 +2975,7 @@ template closureScope*(body: untyped): untyped = ## ## Example: ## - ## ``` + ## .. code-block:: Nim ## var myClosure : proc() ## # without closureScope: ## for i in 0 .. 5: @@ -2566,19 +2990,20 @@ 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). - ## ``` - ## proc draw(t: Triangle) = - ## once: - ## graphicsInit() - ## line(t.p1, t.p2) - ## line(t.p2, t.p3) - ## line(t.p3, t.p1) - ## ``` + ## + ## .. code-block:: Nim + ## + ## 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 @@ -2586,26 +3011,14 @@ template once*(body: untyped): untyped = {.pop.} # warning[GcMem]: off, warning[Uninit]: off -proc substr*(s: openArray[char]): string = - ## Copies a slice of `s` into a new string and returns this new - ## string. - runnableExamples: - let a = "abcdefgh" - assert a.substr(2, 5) == "cdef" - assert a.substr(2) == "cdefgh" - assert a.substr(5, 99) == "fgh" - result = newString(s.len) - for i, ch in s: - result[i] = ch - -proc substr*(s: string, first, last: int): string = # A bug with `magic: Slice` requires this to exist this way +proc substr*(s: string, first, last: int): string = ## Copies a slice of `s` into a new string and returns this new ## string. ## ## The bounds `first` and `last` denote the indices of - ## the first and last characters that shall be copied. If `last` - ## is omitted, it is treated as `high(s)`. If `last >= s.len`, `s.len` - ## is used instead: This means `substr` can also be used to `cut`:idx: + ## the first and last characters that shall be copied. If ``last`` + ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len`` + ## is used instead: This means ``substr`` can also be used to `cut`:idx: ## or `limit`:idx: a string's length. runnableExamples: let a = "abcdefgh" @@ -2628,10 +3041,11 @@ when defined(nimconfig): when not defined(js): proc toOpenArray*[T](x: ptr UncheckedArray[T]; first, last: int): openArray[T] {. magic: "Slice".} - proc toOpenArray*(x: cstring; first, last: int): openArray[char] {. - magic: "Slice".} - proc toOpenArrayByte*(x: cstring; first, last: int): openArray[byte] {. - magic: "Slice".} + when defined(nimToOpenArrayCString): + proc toOpenArray*(x: cstring; first, last: int): openArray[char] {. + magic: "Slice".} + proc toOpenArrayByte*(x: cstring; first, last: int): openArray[byte] {. + magic: "Slice".} proc toOpenArray*[T](x: seq[T]; first, last: int): openArray[T] {. magic: "Slice".} @@ -2649,19 +3063,24 @@ 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. ## ## This hook is called after all globals are initialized. ## When this hook is set the component will not automatically exit, - ## call `quit` explicitly to do so. This is the only available method + ## call ``quit`` explicitly to do so. This is the only available method ## of accessing the initial Genode environment. proc nim_component_construct(env: GenodeEnv) {.exportc.} = - ## Procedure called during `Component::construct` by the loader. + ## Procedure called during ``Component::construct`` by the loader. if componentConstructHook.isNil: - env.rawQuit(programResult) + env.quit(programResult) # No native Genode application initialization, # exit as would POSIX. else: @@ -2670,104 +3089,11 @@ when defined(genode): # and return to thread entrypoint. -when not defined(nimPreviewSlimSystem): - import std/widestrs - export widestrs +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 = compileOption("threads") and - not defined(windows) and - not defined(android) and - not defined(nintendoswitch) and - not defined(freertos) and - not defined(zephyr) and - not defined(nuttx) 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): - import std/syncio - export syncio +import system/io +export io when not defined(createNimHcr) and not defined(nimscript): include nimhcr - -when notJSnotNims and not defined(nimSeqsV2): - proc prepareMutation*(s: var string) {.inline.} = - ## String literals (e.g. "abc", etc) in the ARC/ORC mode are "copy on write", - ## therefore you should call `prepareMutation` before modifying the strings - ## via `addr`. - runnableExamples("--gc:arc"): - var x = "abc" - var y = "defgh" - prepareMutation(y) # without this, you may get a `SIGBUS` or `SIGSEGV` - moveMem(addr y[0], addr x[0], x.len) - assert y == "abcgh" - discard - -proc nimArrayWith[T](y: T, size: static int): array[size, T] {.compilerRtl, raises: [].} = - ## Creates a new array filled with `y`. - for i in 0..size-1: - result[i] = y diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index cee70f677..76c744f59 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -11,8 +11,6 @@ {.push profiler:off.} include osalloc -import std/private/syslocks -import std/sysatomics template track(op, address, size) = when defined(memTracker): @@ -24,7 +22,7 @@ template track(op, address, size) = const nimMinHeapPages {.intdefine.} = 128 # 0.5 MB SmallChunkSize = PageSize - MaxFli = when sizeof(int) > 2: 30 else: 14 + MaxFli = 30 MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32' # everywhere! MaxSli = 1 shl MaxLog2Sli @@ -32,7 +30,7 @@ const RealFli = MaxFli - FliOffset # size of chunks in last matrix bin - MaxBigChunkSize = int(1'i32 shl MaxFli - 1'i32 shl (MaxFli-MaxLog2Sli-1)) + MaxBigChunkSize = 1 shl MaxFli - 1 shl (MaxFli-MaxLog2Sli-1) HugeChunkSize = MaxBigChunkSize + 1 type @@ -46,6 +44,44 @@ type IntSet = object data: TrunkBuckets +type + FreeCell {.final, pure.} = object + next: ptr FreeCell # next free cell in chunk (overlaid with refcount) + when not defined(gcDestructors): + zeroField: int # 0 means cell is not used (overlaid with typ field) + # 1 means cell is manually managed pointer + # otherwise a PNimType is stored in there + else: + alignment: int + + PChunk = ptr BaseChunk + PBigChunk = ptr BigChunk + PSmallChunk = ptr SmallChunk + BaseChunk {.pure, inheritable.} = object + prevSize: int # size of previous chunk; for coalescing + # 0th bit == 1 if 'used + size: int # if < PageSize it is a small chunk + + SmallChunk = object of BaseChunk + next, prev: PSmallChunk # chunks of the same size + freeList: ptr FreeCell + free: int # how many bytes remain + acc: int # accumulator for small object allocation + when defined(nimAlignPragma): + data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory + else: + data: UncheckedArray[byte] + + BigChunk = object of BaseChunk # not necessarily > PageSize! + next, prev: PBigChunk # chunks of the same (or bigger) size + when defined(nimAlignPragma): + data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory + else: + data: UncheckedArray[byte] + +template smallChunkOverhead(): untyped = sizeof(SmallChunk) +template bigChunkOverhead(): untyped = sizeof(BigChunk) + # ------------- chunk table --------------------------------------------------- # We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk # endings of big chunks. This is needed by the merging operation. The only @@ -66,90 +102,29 @@ type key, upperBound: int level: int -const - RegionHasLock = false # hasThreadSupport and defined(gcDestructors) - -type - FreeCell {.final, pure.} = object - next: ptr FreeCell # next free cell in chunk (overlaid with refcount) - when not defined(gcDestructors): - zeroField: int # 0 means cell is not used (overlaid with typ field) - # 1 means cell is manually managed pointer - # otherwise a PNimType is stored in there - else: - alignment: int - - PChunk = ptr BaseChunk - PBigChunk = ptr BigChunk - PSmallChunk = ptr SmallChunk - BaseChunk {.pure, inheritable.} = object - prevSize: int # size of previous chunk; for coalescing - # 0th bit == 1 if 'used - size: int # if < PageSize it is a small chunk - owner: ptr MemRegion - - SmallChunk = object of BaseChunk - next, prev: PSmallChunk # chunks of the same size - freeList: ptr FreeCell - free: int # how many bytes remain - acc: int # accumulator for small object allocation - when defined(gcDestructors): - sharedFreeList: ptr FreeCell # make no attempt at avoiding false sharing for now for this object field - data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory - - BigChunk = object of BaseChunk # not necessarily > PageSize! - next, prev: PBigChunk # chunks of the same (or bigger) size - data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory - HeapLinks = object len: int chunks: array[30, (PBigChunk, int)] next: ptr HeapLinks MemRegion = object - when not defined(gcDestructors): - minLargeObj, maxLargeObj: int - freeSmallChunks: array[0..max(1,SmallChunkSize div MemAlign-1), PSmallChunk] + minLargeObj, maxLargeObj: int + freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk] flBitmap: uint32 slBitmap: array[RealFli, uint32] matrix: array[RealFli, array[MaxSli, PBigChunk]] llmem: PLLChunk currMem, maxMem, freeMem, occ: int # memory sizes (allocated from OS) lastSize: int # needed for the case that OS gives us pages linearly - when RegionHasLock: - lock: SysLock - when defined(gcDestructors): - sharedFreeListBigChunks: PBigChunk # make no attempt at avoiding false sharing for now for this object field - chunkStarts: IntSet - when not defined(gcDestructors): - root, deleted, last, freeAvlNodes: PAvlNode - lockActive, locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages. + root, deleted, last, freeAvlNodes: PAvlNode + locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages. nextChunkSize: int - when not defined(gcDestructors): - bottomData: AvlNode + bottomData: AvlNode heapLinks: HeapLinks when defined(nimTypeNames): allocCounter, deallocCounter: int -template smallChunkOverhead(): untyped = sizeof(SmallChunk) -template bigChunkOverhead(): untyped = sizeof(BigChunk) - -when hasThreadSupport: - template loada(x: untyped): untyped = atomicLoadN(unsafeAddr x, ATOMIC_RELAXED) - template storea(x, y: untyped) = atomicStoreN(unsafeAddr x, y, ATOMIC_RELAXED) - - when false: - # not yet required - template atomicStatDec(x, diff: untyped) = discard atomicSubFetch(unsafeAddr x, diff, ATOMIC_RELAXED) - template atomicStatInc(x, diff: untyped) = discard atomicAddFetch(unsafeAddr x, diff, ATOMIC_RELAXED) -else: - template loada(x: untyped): untyped = x - template storea(x, y: untyped) = x = y - -template atomicStatDec(x, diff: untyped) = dec x, diff -template atomicStatInc(x, diff: untyped) = inc x, diff - const fsLookupTable: array[byte, int8] = [ -1'i8, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, @@ -190,7 +165,7 @@ proc mappingSearch(r, fl, sl: var int) {.inline.} = let t = roundup((1 shl (msbit(uint32 r) - MaxLog2Sli)), PageSize) - 1 r = r + t r = r and not t - r = min(r, MaxBigChunkSize).int + r = min(r, MaxBigChunkSize) fl = msbit(uint32 r) sl = (r shr (fl - MaxLog2Sli)) - MaxSli dec fl, FliOffset @@ -256,11 +231,11 @@ proc addChunkToMatrix(a: var MemRegion; b: PBigChunk) = setBit(fl, a.flBitmap) proc incCurrMem(a: var MemRegion, bytes: int) {.inline.} = - atomicStatInc(a.currMem, bytes) + inc(a.currMem, bytes) proc decCurrMem(a: var MemRegion, bytes: int) {.inline.} = a.maxMem = max(a.maxMem, a.currMem) - atomicStatDec(a.currMem, bytes) + dec(a.currMem, bytes) proc getMaxMem(a: var MemRegion): int = # Since we update maxPagesCount only when freeing pages, @@ -284,41 +259,40 @@ proc llAlloc(a: var MemRegion, size: int): pointer = a.llmem.size = PageSize - sizeof(LLChunk) a.llmem.acc = sizeof(LLChunk) a.llmem.next = old - result = cast[pointer](cast[int](a.llmem) + a.llmem.acc) + result = cast[pointer](cast[ByteAddress](a.llmem) + a.llmem.acc) dec(a.llmem.size, size) inc(a.llmem.acc, size) zeroMem(result, size) -when not defined(gcDestructors): - proc getBottom(a: var MemRegion): PAvlNode = - result = addr(a.bottomData) - if result.link[0] == nil: - result.link[0] = result - result.link[1] = result +proc getBottom(a: var MemRegion): PAvlNode = + result = addr(a.bottomData) + if result.link[0] == nil: + result.link[0] = result + result.link[1] = result - proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode = - if a.freeAvlNodes != nil: - result = a.freeAvlNodes - a.freeAvlNodes = a.freeAvlNodes.link[0] - else: - result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode))) - when defined(nimAvlcorruption): - cprintf("tracking location: %p\n", result) - result.key = key - result.upperBound = upperBound - let bottom = getBottom(a) - result.link[0] = bottom - result.link[1] = bottom - result.level = 1 - #when defined(nimAvlcorruption): - # track("allocAvlNode", result, sizeof(AvlNode)) - sysAssert(bottom == addr(a.bottomData), "bottom data") - sysAssert(bottom.link[0] == bottom, "bottom link[0]") - sysAssert(bottom.link[1] == bottom, "bottom link[1]") +proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode = + if a.freeAvlNodes != nil: + result = a.freeAvlNodes + a.freeAvlNodes = a.freeAvlNodes.link[0] + else: + result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode))) + when defined(nimAvlcorruption): + cprintf("tracking location: %p\n", result) + result.key = key + result.upperBound = upperBound + let bottom = getBottom(a) + result.link[0] = bottom + result.link[1] = bottom + result.level = 1 + #when defined(nimAvlcorruption): + # track("allocAvlNode", result, sizeof(AvlNode)) + sysAssert(bottom == addr(a.bottomData), "bottom data") + sysAssert(bottom.link[0] == bottom, "bottom link[0]") + sysAssert(bottom.link[1] == bottom, "bottom link[1]") - proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} = - n.link[0] = a.freeAvlNodes - a.freeAvlNodes = n +proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} = + n.link[0] = a.freeAvlNodes + a.freeAvlNodes = n proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) = var it = addr(a.heapLinks) @@ -334,8 +308,7 @@ proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) = it.chunks[L] = (p, size) inc it.len -when not defined(gcDestructors): - include "system/avltree" +include "system/avltree" proc llDeallocAll(a: var MemRegion) = var it = a.llmem @@ -400,7 +373,7 @@ iterator elements(t: IntSet): int {.inline.} = r = r.next proc isSmallChunk(c: PChunk): bool {.inline.} = - result = c.size <= SmallChunkSize-smallChunkOverhead() + return c.size <= SmallChunkSize-smallChunkOverhead() proc chunkUnused(c: PChunk): bool {.inline.} = result = (c.prevSize and 1) == 0 @@ -416,7 +389,7 @@ iterator allObjects(m: var MemRegion): pointer {.inline.} = var c = cast[PSmallChunk](c) let size = c.size - var a = cast[int](addr(c.data)) + var a = cast[ByteAddress](addr(c.data)) let limit = a + c.acc while a <% limit: yield cast[pointer](a) @@ -435,13 +408,13 @@ when not defined(gcDestructors): # ------------- chunk management ---------------------------------------------- proc pageIndex(c: PChunk): int {.inline.} = - result = cast[int](c) shr PageShift + result = cast[ByteAddress](c) shr PageShift proc pageIndex(p: pointer): int {.inline.} = - result = cast[int](p) shr PageShift + result = cast[ByteAddress](p) shr PageShift proc pageAddr(p: pointer): PChunk {.inline.} = - result = cast[PChunk](cast[int](p) and not PageMask) + result = cast[PChunk](cast[ByteAddress](p) and not PageMask) #sysAssert(Contains(allocator.chunkStarts, pageIndex(result))) when false: @@ -466,7 +439,7 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = a.nextChunkSize = PageSize*4 else: a.nextChunkSize = min(roundup(usedMem shr 2, PageSize), a.nextChunkSize * 2) - a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize).int + a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize) var size = size if size > a.nextChunkSize: @@ -489,13 +462,13 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = when defined(memtracker): trackLocation(addr result.size, sizeof(int)) - sysAssert((cast[int](result) and PageMask) == 0, "requestOsChunks 1") + sysAssert((cast[ByteAddress](result) and PageMask) == 0, "requestOsChunks 1") #zeroMem(result, size) result.next = nil result.prev = nil result.size = size # update next.prevSize: - var nxt = cast[int](result) +% size + var nxt = cast[ByteAddress](result) +% size sysAssert((nxt and PageMask) == 0, "requestOsChunks 2") var next = cast[PChunk](nxt) if pageIndex(next) in a.chunkStarts: @@ -503,7 +476,7 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = next.prevSize = size or (next.prevSize and 1) # set result.prevSize: var lastSize = if a.lastSize != 0: a.lastSize else: PageSize - var prv = cast[int](result) -% lastSize + var prv = cast[ByteAddress](result) -% lastSize sysAssert((nxt and PageMask) == 0, "requestOsChunks 3") var prev = cast[PChunk](prv) if pageIndex(prev) in a.chunkStarts and prev.size == lastSize: @@ -549,22 +522,21 @@ proc listRemove[T](head: var T, c: T) {.inline.} = proc updatePrevSize(a: var MemRegion, c: PBigChunk, prevSize: int) {.inline.} = - var ri = cast[PChunk](cast[int](c) +% c.size) - sysAssert((cast[int](ri) and PageMask) == 0, "updatePrevSize") + var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) + sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize") if isAccessible(a, ri): ri.prevSize = prevSize or (ri.prevSize and 1) proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk = - result = cast[PBigChunk](cast[int](c) +% size) + result = cast[PBigChunk](cast[ByteAddress](c) +% size) result.size = c.size - size track("result.size", addr result.size, sizeof(int)) - when not defined(nimOptimizedSplitChunk): - # still active because of weird codegen issue on some of our CIs: - result.next = nil - result.prev = nil + # XXX check if these two nil assignments are dead code given + # addChunkToMatrix's implementation: + result.next = nil + result.prev = nil # size and not used: result.prevSize = size - result.owner = addr a sysAssert((size and 1) == 0, "splitChunk 2") sysAssert((size and PageMask) == 0, "splitChunk: size is not a multiple of the PageSize") @@ -584,8 +556,8 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) = when coalescLeft: let prevSize = c.prevSize if prevSize != 0: - var le = cast[PChunk](cast[int](c) -% prevSize) - sysAssert((cast[int](le) and PageMask) == 0, "freeBigChunk 4") + var le = cast[PChunk](cast[ByteAddress](c) -% prevSize) + sysAssert((cast[ByteAddress](le) and PageMask) == 0, "freeBigChunk 4") if isAccessible(a, le) and chunkUnused(le): sysAssert(not isSmallChunk(le), "freeBigChunk 5") if not isSmallChunk(le) and le.size < MaxBigChunkSize: @@ -595,14 +567,11 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) = c = cast[PBigChunk](le) if c.size > MaxBigChunkSize: let rest = splitChunk2(a, c, MaxBigChunkSize) - when defined(nimOptimizedSplitChunk): - rest.next = nil - rest.prev = nil addChunkToMatrix(a, c) c = rest when coalescRight: - var ri = cast[PChunk](cast[int](c) +% c.size) - sysAssert((cast[int](ri) and PageMask) == 0, "freeBigChunk 2") + var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) + sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "freeBigChunk 2") if isAccessible(a, ri) and chunkUnused(ri): sysAssert(not isSmallChunk(ri), "freeBigChunk 3") if not isSmallChunk(ri) and c.size < MaxBigChunkSize: @@ -622,13 +591,6 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk = mappingSearch(size, fl, sl) sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk") result = findSuitableBlock(a, fl, sl) - - when RegionHasLock: - if not a.lockActive: - a.lockActive = true - initSysLock(a.lock) - acquireSys a.lock - if result == nil: if size < nimMinHeapPages * PageSize: result = requestOsChunks(a, nimMinHeapPages * PageSize) @@ -638,7 +600,6 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk = # if we over allocated split the chunk: if result.size > size: splitChunk(a, result, size) - result.owner = addr a else: removeChunkFromMatrix2(a, result, fl, sl) if result.size >= size + PageSize: @@ -646,33 +607,22 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk = # set 'used' to to true: result.prevSize = 1 track("setUsedToFalse", addr result.size, sizeof(int)) - sysAssert result.owner == addr a, "getBigChunk: No owner set!" incl(a, a.chunkStarts, pageIndex(result)) dec(a.freeMem, size) - when RegionHasLock: - releaseSys a.lock proc getHugeChunk(a: var MemRegion; size: int): PBigChunk = result = cast[PBigChunk](osAllocPages(size)) - when RegionHasLock: - if not a.lockActive: - a.lockActive = true - initSysLock(a.lock) - acquireSys a.lock incCurrMem(a, size) # XXX add this to the heap links. But also remove it from it later. when false: a.addHeapLink(result, size) - sysAssert((cast[int](result) and PageMask) == 0, "getHugeChunk") + sysAssert((cast[ByteAddress](result) and PageMask) == 0, "getHugeChunk") result.next = nil result.prev = nil result.size = size # set 'used' to to true: result.prevSize = 1 - result.owner = addr a incl(a, a.chunkStarts, pageIndex(result)) - when RegionHasLock: - releaseSys a.lock proc freeHugeChunk(a: var MemRegion; c: PBigChunk) = let size = c.size @@ -736,87 +686,76 @@ else: template trackSize(x) = discard template untrackSize(x) = discard -proc deallocBigChunk(a: var MemRegion, c: PBigChunk) = - when RegionHasLock: - acquireSys a.lock - dec a.occ, c.size - untrackSize(c.size) - sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)" - when not defined(gcDestructors): - a.deleted = getBottom(a) - del(a, a.root, cast[int](addr(c.data))) - if c.size >= HugeChunkSize: freeHugeChunk(a, c) - else: freeBigChunk(a, c) - when RegionHasLock: - releaseSys a.lock - -when defined(gcDestructors): - template atomicPrepend(head, elem: untyped) = - # see also https://en.cppreference.com/w/cpp/atomic/atomic_compare_exchange - when hasThreadSupport: - while true: - elem.next.storea head.loada - if atomicCompareExchangeN(addr head, addr elem.next, elem, weak = true, ATOMIC_RELEASE, ATOMIC_RELAXED): - break +when false: + # not yet used by the GCs + proc rawTryAlloc(a: var MemRegion; requestedSize: int): pointer = + sysAssert(allocInv(a), "rawAlloc: begin") + sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken") + sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small") + var size = roundup(requestedSize, MemAlign) + inc a.occ, size + trackSize(size) + sysAssert(size >= requestedSize, "insufficient allocated size!") + #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size) + if size <= SmallChunkSize-smallChunkOverhead(): + # allocate a small block: for small chunks, we use only its next pointer + var s = size div MemAlign + var c = a.freeSmallChunks[s] + if c == nil: + result = nil + else: + sysAssert c.size == size, "rawAlloc 6" + if c.freeList == nil: + sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize, + "rawAlloc 7") + result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc) + inc(c.acc, size) + else: + result = c.freeList + sysAssert(c.freeList.zeroField == 0, "rawAlloc 8") + c.freeList = c.freeList.next + dec(c.free, size) + sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9") + if c.free < size: + listRemove(a.freeSmallChunks[s], c) + sysAssert(allocInv(a), "rawAlloc: end listRemove test") + sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %% + size == 0, "rawAlloc 21") + sysAssert(allocInv(a), "rawAlloc: end small size") else: - elem.next.storea head.loada - head.storea elem - - proc addToSharedFreeListBigChunks(a: var MemRegion; c: PBigChunk) {.inline.} = - sysAssert c.next == nil, "c.next pointer must be nil" - atomicPrepend a.sharedFreeListBigChunks, c - - proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell) {.inline.} = - atomicPrepend c.sharedFreeList, f - - proc compensateCounters(a: var MemRegion; c: PSmallChunk; size: int) = - # rawDealloc did NOT do the usual: - # `inc(c.free, size); dec(a.occ, size)` because it wasn't the owner of these - # memory locations. We have to compensate here for these for the entire list. - # Well, not for the entire list, but for `max` elements of the list because - # we split the list in order to achieve bounded response times. - var it = c.freeList - var x = 0 - var maxIters = 20 # make it time-bounded - while it != nil: - if maxIters == 0: - let rest = it.next.loada - if rest != nil: - it.next.storea nil - addToSharedFreeList(c, rest) - break - inc x, size - it = it.next.loada - dec maxIters - inc(c.free, x) - dec(a.occ, x) - - proc freeDeferredObjects(a: var MemRegion; root: PBigChunk) = - var it = root - var maxIters = 20 # make it time-bounded - while true: - if maxIters == 0: - let rest = it.next.loada - it.next.storea nil - addToSharedFreeListBigChunks(a, rest) - break - it = it.next.loada - dec maxIters - if it == nil: break + inc size, bigChunkOverhead() + var fl, sl: int + mappingSearch(size, fl, sl) + sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk") + let c = findSuitableBlock(a, fl, sl) + if c != nil: + removeChunkFromMatrix2(a, c, fl, sl) + if c.size >= size + PageSize: + splitChunk(a, c, size) + # set 'used' to to true: + c.prevSize = 1 + incl(a, a.chunkStarts, pageIndex(c)) + dec(a.freeMem, size) + result = addr(c.data) + sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13") + sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary") + if a.root == nil: a.root = getBottom(a) + add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size) + else: + result = nil proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = when defined(nimTypeNames): inc(a.allocCounter) sysAssert(allocInv(a), "rawAlloc: begin") sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken") + sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small") var size = roundup(requestedSize, MemAlign) - sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small") - sysAssert(size >= requestedSize, "insufficient allocated size!") #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size) if size <= SmallChunkSize-smallChunkOverhead(): # allocate a small block: for small chunks, we use only its next pointer - let s = size div MemAlign + var s = size div MemAlign var c = a.freeSmallChunks[s] if c == nil: c = getSmallChunk(a) @@ -824,33 +763,22 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = sysAssert c.size == PageSize, "rawAlloc 3" c.size = size c.acc = size - when defined(gcDestructors): - c.sharedFreeList = nil c.free = SmallChunkSize - smallChunkOverhead() - size - sysAssert c.owner == addr(a), "rawAlloc: No owner set!" c.next = nil c.prev = nil listAdd(a.freeSmallChunks[s], c) result = addr(c.data) - sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 4") + sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 4") else: sysAssert(allocInv(a), "rawAlloc: begin c != nil") sysAssert c.next != c, "rawAlloc 5" #if c.size != size: # c_fprintf(stdout, "csize: %lld; size %lld\n", c.size, size) sysAssert c.size == size, "rawAlloc 6" - when defined(gcDestructors): - if c.freeList == nil: - when hasThreadSupport: - c.freeList = atomicExchangeN(addr c.sharedFreeList, nil, ATOMIC_RELAXED) - else: - c.freeList = c.sharedFreeList - c.sharedFreeList = nil - compensateCounters(a, c, size) if c.freeList == nil: sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize, "rawAlloc 7") - result = cast[pointer](cast[int](addr(c.data)) +% c.acc) + result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc) inc(c.acc, size) else: result = c.freeList @@ -858,28 +786,19 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = sysAssert(c.freeList.zeroField == 0, "rawAlloc 8") c.freeList = c.freeList.next dec(c.free, size) - sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 9") + sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9") sysAssert(allocInv(a), "rawAlloc: end c != nil") sysAssert(allocInv(a), "rawAlloc: before c.free < size") if c.free < size: sysAssert(allocInv(a), "rawAlloc: before listRemove test") listRemove(a.freeSmallChunks[s], c) sysAssert(allocInv(a), "rawAlloc: end listRemove test") - sysAssert(((cast[int](result) and PageMask) - smallChunkOverhead()) %% + sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %% size == 0, "rawAlloc 21") sysAssert(allocInv(a), "rawAlloc: end small size") inc a.occ, size trackSize(c.size) else: - when defined(gcDestructors): - when hasThreadSupport: - let deferredFrees = atomicExchangeN(addr a.sharedFreeListBigChunks, nil, ATOMIC_RELAXED) - else: - let deferredFrees = a.sharedFreeListBigChunks - a.sharedFreeListBigChunks = nil - if deferredFrees != nil: - freeDeferredObjects(a, deferredFrees) - size = requestedSize + bigChunkOverhead() # roundup(requestedSize+bigChunkOverhead(), PageSize) # allocate a large block var c = if size >= HugeChunkSize: getHugeChunk(a, size) @@ -887,11 +806,10 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = sysAssert c.prev == nil, "rawAlloc 10" sysAssert c.next == nil, "rawAlloc 11" result = addr(c.data) - sysAssert((cast[int](c) and (MemAlign-1)) == 0, "rawAlloc 13") - sysAssert((cast[int](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary") - when not defined(gcDestructors): - if a.root == nil: a.root = getBottom(a) - add(a, a.root, cast[int](result), cast[int](result)+%size) + sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13") + sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary") + if a.root == nil: a.root = getBottom(a) + add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size) inc a.occ, c.size trackSize(c.size) sysAssert(isAccessible(a, result), "rawAlloc 14") @@ -908,57 +826,51 @@ proc rawDealloc(a: var MemRegion, p: pointer) = #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer") sysAssert(allocInv(a), "rawDealloc: begin") var c = pageAddr(p) - sysAssert(c != nil, "rawDealloc: begin") if isSmallChunk(c): # `p` is within a small chunk: var c = cast[PSmallChunk](c) var s = c.size - # ^ We might access thread foreign storage here. - # The other thread cannot possibly free this block as it's still alive. + dec a.occ, s + untrackSize(s) + sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)" + sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %% + s == 0, "rawDealloc 3") var f = cast[ptr FreeCell](p) - if c.owner == addr(a): - # We own the block, there is no foreign thread involved. - dec a.occ, s - untrackSize(s) - sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)" - sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %% - s == 0, "rawDealloc 3") - when not defined(gcDestructors): - #echo("setting to nil: ", $cast[int](addr(f.zeroField))) - sysAssert(f.zeroField != 0, "rawDealloc 1") - f.zeroField = 0 - f.next = c.freeList - c.freeList = f - when overwriteFree: - # set to 0xff to check for usage after free bugs: - nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32, - s -% sizeof(FreeCell)) - # check if it is not in the freeSmallChunks[s] list: - if c.free < s: - # add it to the freeSmallChunks[s] array: - listAdd(a.freeSmallChunks[s div MemAlign], c) - inc(c.free, s) - else: - inc(c.free, s) - if c.free == SmallChunkSize-smallChunkOverhead(): - listRemove(a.freeSmallChunks[s div MemAlign], c) - c.size = SmallChunkSize - freeBigChunk(a, cast[PBigChunk](c)) + when not defined(gcDestructors): + #echo("setting to nil: ", $cast[ByteAddress](addr(f.zeroField))) + sysAssert(f.zeroField != 0, "rawDealloc 1") + f.zeroField = 0 + f.next = c.freeList + c.freeList = f + when overwriteFree: + # set to 0xff to check for usage after free bugs: + nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32, + s -% sizeof(FreeCell)) + # check if it is not in the freeSmallChunks[s] list: + if c.free < s: + # add it to the freeSmallChunks[s] array: + listAdd(a.freeSmallChunks[s div MemAlign], c) + inc(c.free, s) else: - when defined(gcDestructors): - addToSharedFreeList(c, f) - sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %% + inc(c.free, s) + if c.free == SmallChunkSize-smallChunkOverhead(): + listRemove(a.freeSmallChunks[s div MemAlign], c) + c.size = SmallChunkSize + freeBigChunk(a, cast[PBigChunk](c)) + sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %% s == 0, "rawDealloc 2") else: # set to 0xff to check for usage after free bugs: when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead()) - when defined(gcDestructors): - if c.owner == addr(a): - deallocBigChunk(a, cast[PBigChunk](c)) - else: - addToSharedFreeListBigChunks(c.owner[], cast[PBigChunk](c)) - else: - deallocBigChunk(a, cast[PBigChunk](c)) + # free big chunk + var c = cast[PBigChunk](c) + dec a.occ, c.size + untrackSize(c.size) + sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)" + a.deleted = getBottom(a) + del(a, a.root, cast[int](addr(c.data))) + if c.size >= HugeChunkSize: freeHugeChunk(a, c) + else: freeBigChunk(a, c) sysAssert(allocInv(a), "rawDealloc: end") when logAlloc: cprintf("dealloc(pointer_%p)\n", p) @@ -969,7 +881,7 @@ when not defined(gcDestructors): if not chunkUnused(c): if isSmallChunk(c): var c = cast[PSmallChunk](c) - var offset = (cast[int](p) and (PageSize-1)) -% + var offset = (cast[ByteAddress](p) and (PageSize-1)) -% smallChunkOverhead() result = (c.acc >% offset) and (offset %% c.size == 0) and (cast[ptr FreeCell](p).zeroField >% 1) @@ -987,12 +899,12 @@ when not defined(gcDestructors): if not chunkUnused(c): if isSmallChunk(c): var c = cast[PSmallChunk](c) - var offset = (cast[int](p) and (PageSize-1)) -% + var offset = (cast[ByteAddress](p) and (PageSize-1)) -% smallChunkOverhead() if c.acc >% offset: - sysAssert(cast[int](addr(c.data)) +% offset == - cast[int](p), "offset is not what you think it is") - var d = cast[ptr FreeCell](cast[int](addr(c.data)) +% + sysAssert(cast[ByteAddress](addr(c.data)) +% offset == + cast[ByteAddress](p), "offset is not what you think it is") + var d = cast[ptr FreeCell](cast[ByteAddress](addr(c.data)) +% offset -% (offset %% c.size)) if d.zeroField >% 1: result = d @@ -1019,7 +931,7 @@ when not defined(gcDestructors): proc ptrSize(p: pointer): int = when not defined(gcDestructors): - var x = cast[pointer](cast[int](p) -% sizeof(FreeCell)) + var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell)) var c = pageAddr(p) sysAssert(not chunkUnused(c), "ptrSize") result = c.size -% sizeof(FreeCell) @@ -1037,7 +949,7 @@ proc alloc(allocator: var MemRegion, size: Natural): pointer {.gcsafe.} = result = rawAlloc(allocator, size+sizeof(FreeCell)) cast[ptr FreeCell](result).zeroField = 1 # mark it as used sysAssert(not isAllocatedPtr(allocator, result), "alloc") - result = cast[pointer](cast[int](result) +% sizeof(FreeCell)) + result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell)) track("alloc", result, size) else: result = rawAlloc(allocator, size) @@ -1049,7 +961,7 @@ proc alloc0(allocator: var MemRegion, size: Natural): pointer = proc dealloc(allocator: var MemRegion, p: pointer) = when not defined(gcDestructors): sysAssert(p != nil, "dealloc: p is nil") - var x = cast[pointer](cast[int](p) -% sizeof(FreeCell)) + var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell)) sysAssert(x != nil, "dealloc: x is nil") sysAssert(isAccessible(allocator, x), "is not accessible") sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc: object header corrupted") @@ -1110,7 +1022,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = result = interiorAllocatedPtr(allocator, p) proc isAllocatedPtr*(p: pointer): bool = - let p = cast[pointer](cast[int](p)-%ByteAddress(sizeof(Cell))) + let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(Cell))) result = isAllocatedPtr(allocator, p) proc deallocOsPages = deallocOsPages(allocator) @@ -1140,34 +1052,25 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = inc(result, it.size) it = it.next - when hasThreadSupport and not defined(gcDestructors): - proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "".} - - var sharedHeap: MemRegion - var heapLock: SysLock - initSysLock(heapLock) - addSysExitProc(proc() {.noconv.} = deinitSys(heapLock)) - proc getFreeMem(): int = - #sysAssert(result == countFreeMem()) result = allocator.freeMem + #sysAssert(result == countFreeMem()) - proc getTotalMem(): int = - result = allocator.currMem - - proc getOccupiedMem(): int = - result = allocator.occ #getTotalMem() - getFreeMem() - - proc getMaxMem*(): int = - result = getMaxMem(allocator) + proc getTotalMem(): int = return allocator.currMem + proc getOccupiedMem(): int = return allocator.occ #getTotalMem() - getFreeMem() + proc getMaxMem*(): int = return getMaxMem(allocator) when defined(nimTypeNames): proc getMemCounters*(): (int, int) = getMemCounters(allocator) # -------------------- shared heap region ---------------------------------- + when hasThreadSupport: + var sharedHeap: MemRegion + var heapLock: SysLock + initSysLock(heapLock) proc allocSharedImpl(size: Natural): pointer = - when hasThreadSupport and not defined(gcDestructors): + when hasThreadSupport: acquireSys(heapLock) result = alloc(sharedHeap, size) releaseSys(heapLock) @@ -1179,7 +1082,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = zeroMem(result, size) proc deallocSharedImpl(p: pointer) = - when hasThreadSupport and not defined(gcDestructors): + when hasThreadSupport: acquireSys(heapLock) dealloc(sharedHeap, p) releaseSys(heapLock) @@ -1187,7 +1090,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = deallocImpl(p) proc reallocSharedImpl(p: pointer, newSize: Natural): pointer = - when hasThreadSupport and not defined(gcDestructors): + when hasThreadSupport: acquireSys(heapLock) result = realloc(sharedHeap, p, newSize) releaseSys(heapLock) @@ -1195,7 +1098,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = result = reallocImpl(p, newSize) proc reallocShared0Impl(p: pointer, oldSize, newSize: Natural): pointer = - when hasThreadSupport and not defined(gcDestructors): + when hasThreadSupport: acquireSys(heapLock) result = realloc0(sharedHeap, p, oldSize, newSize) releaseSys(heapLock) @@ -1203,31 +1106,20 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = result = realloc0Impl(p, oldSize, newSize) when hasThreadSupport: - when defined(gcDestructors): - proc getFreeSharedMem(): int = - allocator.freeMem + template sharedMemStatsShared(v: int) = + acquireSys(heapLock) + result = v + releaseSys(heapLock) - proc getTotalSharedMem(): int = - allocator.currMem + proc getFreeSharedMem(): int = + sharedMemStatsShared(sharedHeap.freeMem) - proc getOccupiedSharedMem(): int = - allocator.occ + proc getTotalSharedMem(): int = + sharedMemStatsShared(sharedHeap.currMem) - else: - template sharedMemStatsShared(v: int) = - acquireSys(heapLock) - result = v - releaseSys(heapLock) - - proc getFreeSharedMem(): int = - sharedMemStatsShared(sharedHeap.freeMem) - - proc getTotalSharedMem(): int = - sharedMemStatsShared(sharedHeap.currMem) - - proc getOccupiedSharedMem(): int = - sharedMemStatsShared(sharedHeap.occ) - #sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem) + proc getOccupiedSharedMem(): int = + sharedMemStatsShared(sharedHeap.occ) + #sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem) {.pop.} {.pop.} diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index a8a4bd767..4839407c1 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -12,6 +12,8 @@ # All symbols are prefixed with 'c_' to avoid ambiguities {.push hints:off, stack_trace: off, profiler: off.} +when not defined(nimHasHotCodeReloading): + {.pragma: nonReloadable.} proc c_memchr*(s: pointer, c: cint, n: csize_t): pointer {. importc: "memchr", header: "".} @@ -31,10 +33,7 @@ proc c_abort*() {. importc: "abort", header: "", noSideEffect, noreturn.} -when defined(nimBuiltinSetjmp): - type - C_JmpBuf* = array[5, pointer] -elif defined(linux) and defined(amd64): +when defined(linux) and defined(amd64): type C_JmpBuf* {.importc: "jmp_buf", header: "", bycopy.} = object abi: array[200 div sizeof(clong), clong] @@ -43,7 +42,6 @@ else: C_JmpBuf* {.importc: "jmp_buf", header: "".} = object -type CSighandlerT = proc (a: cint) {.noconv.} when defined(windows): const SIGABRT* = cint(22) @@ -52,7 +50,6 @@ when defined(windows): SIGINT* = cint(2) SIGSEGV* = cint(11) SIGTERM = cint(15) - SIG_DFL* = cast[CSighandlerT](0) elif defined(macosx) or defined(linux) or defined(freebsd) or defined(openbsd) or defined(netbsd) or defined(solaris) or defined(dragonfly) or defined(nintendoswitch) or defined(genode) or @@ -65,7 +62,6 @@ elif defined(macosx) or defined(linux) or defined(freebsd) or SIGSEGV* = cint(11) SIGTERM* = cint(15) SIGPIPE* = cint(13) - SIG_DFL* = cast[CSighandlerT](0) elif defined(haiku): const SIGABRT* = cint(6) @@ -75,9 +71,8 @@ elif defined(haiku): SIGSEGV* = cint(11) SIGTERM* = cint(15) SIGPIPE* = cint(7) - SIG_DFL* = cast[CSighandlerT](0) else: - when defined(nimscript): + when NoFakeVars: {.error: "SIGABRT not ported to your platform".} else: var @@ -86,7 +81,6 @@ else: SIGABRT* {.importc: "SIGABRT", nodecl.}: cint SIGFPE* {.importc: "SIGFPE", nodecl.}: cint SIGILL* {.importc: "SIGILL", nodecl.}: cint - SIG_DFL* {.importc: "SIG_DFL", nodecl.}: CSighandlerT when defined(macosx) or defined(linux): var SIGPIPE* {.importc: "SIGPIPE", nodecl.}: cint @@ -95,67 +89,27 @@ when defined(macosx): elif defined(haiku): const SIGBUS* = cint(30) -# "nimRawSetjmp" is defined by default for certain platforms, so we need the -# "nimStdSetjmp" escape hatch with it. -when defined(nimSigSetjmp): +when defined(nimSigSetjmp) and not defined(nimStdSetjmp): proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "siglongjmp".} - proc c_setjmp*(jmpb: C_JmpBuf): cint = + template c_setjmp*(jmpb: C_JmpBuf): cint = proc c_sigsetjmp(jmpb: C_JmpBuf, savemask: cint): cint {. header: "", importc: "sigsetjmp".} c_sigsetjmp(jmpb, 0) -elif defined(nimBuiltinSetjmp): - proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) = - # Apple's Clang++ has trouble converting array names to pointers, so we need - # to be very explicit here. - proc c_builtin_longjmp(jmpb: ptr pointer, retval: cint) {. - importc: "__builtin_longjmp", nodecl.} - # The second parameter needs to be 1 and sometimes the C/C++ compiler checks it. - c_builtin_longjmp(unsafeAddr jmpb[0], 1) - proc c_setjmp*(jmpb: C_JmpBuf): cint = - 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".} - 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".} - proc c_setjmp*(jmpb: C_JmpBuf): cint {. - header: "", importc: "_setjmp".} + proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. + header: "", importc: "_longjmp".} + proc c_setjmp*(jmpb: C_JmpBuf): cint {. + header: "", importc: "_setjmp".} else: proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "longjmp".} proc c_setjmp*(jmpb: C_JmpBuf): cint {. header: "", importc: "setjmp".} -proc c_signal*(sign: cint, handler: CSighandlerT): CSighandlerT {. +type CSighandlerT = proc (a: cint) {.noconv.} +proc c_signal*(sign: cint, handler: proc (a: cint) {.noconv.}): CSighandlerT {. importc: "signal", header: "", discardable.} -proc c_raise*(sign: cint): cint {.importc: "raise", header: "".} type CFile {.importc: "FILE", header: "", @@ -180,41 +134,24 @@ 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.} # we use it only in a way that cannot lead to security issues -when defined(zephyr) and not defined(zephyrUseLibcMalloc): - proc c_malloc*(size: csize_t): pointer {. - importc: "k_malloc", header: "".} - proc c_calloc*(nmemb, size: csize_t): pointer {. - importc: "k_calloc", header: "".} - proc c_free*(p: pointer) {. - importc: "k_free", header: "".} - proc c_realloc*(p: pointer, newsize: csize_t): pointer = - # Zephyr's kernel malloc doesn't support realloc - result = c_malloc(newSize) - # match the ansi c behavior - if not result.isNil(): - copyMem(result, p, newSize) - c_free(p) -else: - proc c_malloc*(size: csize_t): pointer {. - importc: "malloc", header: "".} - proc c_calloc*(nmemb, size: csize_t): pointer {. - importc: "calloc", header: "".} - proc c_free*(p: pointer) {. - importc: "free", header: "".} - proc c_realloc*(p: pointer, newsize: csize_t): pointer {. - importc: "realloc", header: "".} +proc c_malloc*(size: csize_t): pointer {. + importc: "malloc", header: "".} +proc c_calloc*(nmemb, size: csize_t): pointer {. + importc: "calloc", header: "".} +proc c_free*(p: pointer) {. + importc: "free", header: "".} +proc c_realloc*(p: pointer, newsize: csize_t): pointer {. + importc: "realloc", header: "".} -proc c_fwrite*(buf: pointer, size, n: csize_t, f: CFilePtr): csize_t {. +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 d8527e1e4..9ee367543 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -12,6 +12,26 @@ In this new runtime we simplify the object layouts a bit: The runtime type information is only accessed for the objects that have it and it's always at offset 0 then. The ``ref`` object header is independent from the runtime type and only contains a reference count. + +Object subtyping is checked via the generated 'name'. This should have +comparable overhead to the old pointer chasing approach but has the benefit +that it works across DLL boundaries. + +The generated name is a concatenation of the object names in the hierarchy +so that a subtype check becomes a substring check. For example:: + + type + ObjectA = object of RootObj + ObjectB = object of ObjectA + +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 +hash of ``package & "." & module & "." & name`` to save space. + ]# when defined(gcOrc): @@ -134,7 +154,7 @@ proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} = when defined(nimOwnedEnabled): if head(p).rc >= rcIncrement: cstderr.rawWrite "[FATAL] dangling references exist\n" - rawQuit 1 + quit 1 when defined(nimArcDebug): # we do NOT really free the memory here in order to reliably detect use-after-frees if freedCells.data == nil: init(freedCells) @@ -143,7 +163,7 @@ proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} = let hdrSize = align(sizeof(RefHeader), alignment) alignedDealloc(p -! hdrSize, alignment) -template `=dispose`*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf) +template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf) #proc dispose*(x: pointer) = nimRawDispose(x) proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} = @@ -187,8 +207,10 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} = proc GC_unref*[T](x: ref T) = ## New runtime only supports this operation for 'ref T'. - var y {.cursor.} = x - `=destroy`(y) + if nimDecRefIsLast(cast[pointer](x)): + # XXX this does NOT work for virtual destructors! + `=destroy`(x[]) + nimRawDispose(cast[pointer](x), T.alignOf) proc GC_ref*[T](x: ref T) = ## New runtime only supports this operation for 'ref T'. @@ -196,16 +218,22 @@ proc GC_ref*[T](x: ref T) = when not defined(gcOrc): template GC_fullCollect* = - ## Forces a full garbage collection pass. With `--gc:arc` a nop. + ## Forces a full garbage collection pass. With ``--gc:arc`` a nop. discard template setupForeignThreadGc* = - ## With `--gc:arc` a nop. + ## With ``--gc:arc`` a nop. discard template tearDownForeignThreadGc* = - ## With `--gc:arc` a nop. + ## With ``--gc:arc`` a nop. discard -proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inline.} = - result = targetDepth <= source.depth and source.display[targetDepth] == token +proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} = + proc strstr(s, sub: cstring): cstring {.header: "", importc.} + + result = strstr(obj.name, subclass) != nil + +proc chckObj(obj: PNimTypeV2, subclass: cstring) {.compilerRtl.} = + # checks if obj is of type subclass: + if not isObj(obj, subclass): sysFatal(ObjectConversionDefect, "invalid object conversion") diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim new file mode 100644 index 000000000..64caddce8 --- /dev/null +++ b/lib/system/arithm.nim @@ -0,0 +1,425 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + + +# simple integer arithmetic with overflow checking + +proc raiseOverflow {.compilerproc, noinline.} = + # a single proc to reduce code size to a minimum + sysFatal(OverflowDefect, "over- or underflow") + +proc raiseDivByZero {.compilerproc, noinline.} = + sysFatal(DivByZeroDefect, "division by zero") + +when defined(builtinOverflow): + # Builtin compiler functions for improved performance + when sizeof(clong) == 8: + proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. + importc: "__builtin_saddl_overflow", nodecl, nosideeffect.} + + proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. + importc: "__builtin_ssubl_overflow", nodecl, nosideeffect.} + + proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. + importc: "__builtin_smull_overflow", nodecl, nosideeffect.} + + elif sizeof(clonglong) == 8: + proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. + importc: "__builtin_saddll_overflow", nodecl, nosideeffect.} + + proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. + importc: "__builtin_ssubll_overflow", nodecl, nosideeffect.} + + proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. + importc: "__builtin_smulll_overflow", nodecl, nosideeffect.} + + when sizeof(int) == 8: + proc addIntOverflow(a, b: int, c: var int): bool {.inline.} = + addInt64Overflow(a, b, c) + + proc subIntOverflow(a, b: int, c: var int): bool {.inline.} = + subInt64Overflow(a, b, c) + + proc mulIntOverflow(a, b: int, c: var int): bool {.inline.} = + mulInt64Overflow(a, b, c) + + elif sizeof(int) == 4 and sizeof(cint) == 4: + proc addIntOverflow(a, b: int, c: var int): bool {. + importc: "__builtin_sadd_overflow", nodecl, nosideeffect.} + + proc subIntOverflow(a, b: int, c: var int): bool {. + importc: "__builtin_ssub_overflow", nodecl, nosideeffect.} + + proc mulIntOverflow(a, b: int, c: var int): bool {. + importc: "__builtin_smul_overflow", nodecl, nosideeffect.} + + proc addInt64(a, b: int64): int64 {.compilerproc, inline.} = + if addInt64Overflow(a, b, result): + raiseOverflow() + + proc subInt64(a, b: int64): int64 {.compilerproc, inline.} = + if subInt64Overflow(a, b, result): + raiseOverflow() + + proc mulInt64(a, b: int64): int64 {.compilerproc, inline.} = + if mulInt64Overflow(a, b, result): + raiseOverflow() +else: + proc addInt64(a, b: int64): int64 {.compilerproc, inline.} = + result = a +% b + if (result xor a) >= int64(0) or (result xor b) >= int64(0): + return result + raiseOverflow() + + proc subInt64(a, b: int64): int64 {.compilerproc, inline.} = + result = a -% b + if (result xor a) >= int64(0) or (result xor not b) >= int64(0): + return result + raiseOverflow() + + # + # This code has been inspired by Python's source code. + # The native int product x*y is either exactly right or *way* off, being + # just the last n bits of the true product, where n is the number of bits + # in an int (the delivered product is the true product plus i*2**n for + # some integer i). + # + # The native float64 product x*y is subject to three + # rounding errors: on a sizeof(int)==8 box, each cast to double can lose + # info, and even on a sizeof(int)==4 box, the multiplication can lose info. + # But, unlike the native int product, it's not in *range* trouble: even + # if sizeof(int)==32 (256-bit ints), the product easily fits in the + # dynamic range of a float64. So the leading 50 (or so) bits of the float64 + # product are correct. + # + # We check these two ways against each other, and declare victory if they're + # approximately the same. Else, because the native int product is the only + # one that can lose catastrophic amounts of information, it's the native int + # product that must have overflowed. + # + proc mulInt64(a, b: int64): int64 {.compilerproc.} = + var + resAsFloat, floatProd: float64 + result = a *% b + floatProd = toBiggestFloat(a) # conversion + floatProd = floatProd * toBiggestFloat(b) + resAsFloat = toBiggestFloat(result) + + # Fast path for normal case: small multiplicands, and no info + # is lost in either method. + if resAsFloat == floatProd: return result + + # Somebody somewhere lost info. Close enough, or way off? Note + # that a != 0 and b != 0 (else resAsFloat == floatProd == 0). + # The difference either is or isn't significant compared to the + # true value (of which floatProd is a good approximation). + + # abs(diff)/abs(prod) <= 1/32 iff + # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" + if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): + return result + raiseOverflow() + +proc negInt64(a: int64): int64 {.compilerproc, inline.} = + if a != low(int64): return -a + raiseOverflow() + +proc absInt64(a: int64): int64 {.compilerproc, inline.} = + if a != low(int64): + if a >= 0: return a + else: return -a + raiseOverflow() + +proc divInt64(a, b: int64): int64 {.compilerproc, inline.} = + if b == int64(0): + raiseDivByZero() + if a == low(int64) and b == int64(-1): + raiseOverflow() + return a div b + +proc modInt64(a, b: int64): int64 {.compilerproc, inline.} = + if b == int64(0): + raiseDivByZero() + return a mod b + +proc absInt(a: int): int {.compilerproc, inline.} = + if a != low(int): + if a >= 0: return a + else: return -a + raiseOverflow() + +const + asmVersion = defined(I386) and (defined(vcc) or defined(wcc) or + defined(dmc) or defined(gcc) or defined(llvm_gcc)) + # my Version of Borland C++Builder does not have + # tasm32, which is needed for assembler blocks + # this is why Borland is not included in the 'when' + +when asmVersion and not defined(gcc) and not defined(llvm_gcc): + # assembler optimized versions for compilers that + # have an intel syntax assembler: + proc addInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = + # a in eax, and b in edx + asm """ + mov eax, ecx + add eax, edx + jno theEnd + call `raiseOverflow` + theEnd: + ret + """ + + proc subInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = + asm """ + mov eax, ecx + sub eax, edx + jno theEnd + call `raiseOverflow` + theEnd: + ret + """ + + proc negInt(a: int): int {.compilerproc, asmNoStackFrame.} = + asm """ + mov eax, ecx + neg eax + jno theEnd + call `raiseOverflow` + theEnd: + ret + """ + + proc divInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = + asm """ + test edx, edx + jne L_NOT_ZERO + call `raiseDivByZero` + L_NOT_ZERO: + cmp ecx, 0x80000000 + jne L_DO_DIV + cmp edx, -1 + jne L_DO_DIV + call `raiseOverflow` + L_DO_DIV: + mov eax, ecx + mov ecx, edx + cdq + idiv ecx + ret + """ + + proc modInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = + asm """ + test edx, edx + jne L_NOT_ZERO + call `raiseDivByZero` + L_NOT_ZERO: + cmp ecx, 0x80000000 + jne L_DO_DIV + cmp edx, -1 + jne L_DO_DIV + call `raiseOverflow` + L_DO_DIV: + mov eax, ecx + mov ecx, edx + cdq + idiv ecx + mov eax, edx + ret + """ + + proc mulInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = + asm """ + mov eax, ecx + mov ecx, edx + xor edx, edx + imul ecx + jno theEnd + call `raiseOverflow` + theEnd: + ret + """ + +elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)): + proc addInt(a, b: int): int {.compilerproc, inline.} = + # don't use a pure proc here! + asm """ + "addl %%ecx, %%eax\n" + "jno 1\n" + "call _raiseOverflow\n" + "1: \n" + :"=a"(`result`) + :"a"(`a`), "c"(`b`) + """ + #".intel_syntax noprefix" + #/* Intel syntax here */ + #".att_syntax" + + proc subInt(a, b: int): int {.compilerproc, inline.} = + asm """ "subl %%ecx,%%eax\n" + "jno 1\n" + "call _raiseOverflow\n" + "1: \n" + :"=a"(`result`) + :"a"(`a`), "c"(`b`) + """ + + proc mulInt(a, b: int): int {.compilerproc, inline.} = + asm """ "xorl %%edx, %%edx\n" + "imull %%ecx\n" + "jno 1\n" + "call _raiseOverflow\n" + "1: \n" + :"=a"(`result`) + :"a"(`a`), "c"(`b`) + :"%edx" + """ + + proc negInt(a: int): int {.compilerproc, inline.} = + asm """ "negl %%eax\n" + "jno 1\n" + "call _raiseOverflow\n" + "1: \n" + :"=a"(`result`) + :"a"(`a`) + """ + + proc divInt(a, b: int): int {.compilerproc, inline.} = + asm """ "xorl %%edx, %%edx\n" + "idivl %%ecx\n" + "jno 1\n" + "call _raiseOverflow\n" + "1: \n" + :"=a"(`result`) + :"a"(`a`), "c"(`b`) + :"%edx" + """ + + proc modInt(a, b: int): int {.compilerproc, inline.} = + asm """ "xorl %%edx, %%edx\n" + "idivl %%ecx\n" + "jno 1\n" + "call _raiseOverflow\n" + "1: \n" + "movl %%edx, %%eax" + :"=a"(`result`) + :"a"(`a`), "c"(`b`) + :"%edx" + """ + +when not declared(addInt) and defined(builtinOverflow): + proc addInt(a, b: int): int {.compilerproc, inline.} = + if addIntOverflow(a, b, result): + raiseOverflow() + +when not declared(subInt) and defined(builtinOverflow): + proc subInt(a, b: int): int {.compilerproc, inline.} = + if subIntOverflow(a, b, result): + raiseOverflow() + +when not declared(mulInt) and defined(builtinOverflow): + proc mulInt(a, b: int): int {.compilerproc, inline.} = + if mulIntOverflow(a, b, result): + raiseOverflow() + +# Platform independent versions of the above (slower!) +when not declared(addInt): + proc addInt(a, b: int): int {.compilerproc, inline.} = + result = a +% b + if (result xor a) >= 0 or (result xor b) >= 0: + return result + raiseOverflow() + +when not declared(subInt): + proc subInt(a, b: int): int {.compilerproc, inline.} = + result = a -% b + if (result xor a) >= 0 or (result xor not b) >= 0: + return result + raiseOverflow() + +when not declared(negInt): + proc negInt(a: int): int {.compilerproc, inline.} = + if a != low(int): return -a + raiseOverflow() + +when not declared(divInt): + proc divInt(a, b: int): int {.compilerproc, inline.} = + if b == 0: + raiseDivByZero() + if a == low(int) and b == -1: + raiseOverflow() + return a div b + +when not declared(modInt): + proc modInt(a, b: int): int {.compilerproc, inline.} = + if b == 0: + raiseDivByZero() + return a mod b + +when not declared(mulInt): + # + # This code has been inspired by Python's source code. + # The native int product x*y is either exactly right or *way* off, being + # just the last n bits of the true product, where n is the number of bits + # in an int (the delivered product is the true product plus i*2**n for + # some integer i). + # + # The native float64 product x*y is subject to three + # rounding errors: on a sizeof(int)==8 box, each cast to double can lose + # info, and even on a sizeof(int)==4 box, the multiplication can lose info. + # But, unlike the native int product, it's not in *range* trouble: even + # if sizeof(int)==32 (256-bit ints), the product easily fits in the + # dynamic range of a float64. So the leading 50 (or so) bits of the float64 + # product are correct. + # + # We check these two ways against each other, and declare victory if + # they're approximately the same. Else, because the native int product is + # the only one that can lose catastrophic amounts of information, it's the + # native int product that must have overflowed. + # + proc mulInt(a, b: int): int {.compilerproc.} = + var + resAsFloat, floatProd: float + + result = a *% b + floatProd = toFloat(a) * toFloat(b) + resAsFloat = toFloat(result) + + # Fast path for normal case: small multiplicands, and no info + # is lost in either method. + if resAsFloat == floatProd: return result + + # Somebody somewhere lost info. Close enough, or way off? Note + # that a != 0 and b != 0 (else resAsFloat == floatProd == 0). + # The difference either is or isn't significant compared to the + # true value (of which floatProd is a good approximation). + + # abs(diff)/abs(prod) <= 1/32 iff + # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" + if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): + return result + raiseOverflow() + +# We avoid setting the FPU control word here for compatibility with libraries +# written in other languages. + +proc raiseFloatInvalidOp {.compilerproc, noinline.} = + sysFatal(FloatInvalidOpDefect, "FPU operation caused a NaN result") + +proc nanCheck(x: float64) {.compilerproc, inline.} = + if x != x: raiseFloatInvalidOp() + +proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} = + if x > 0.0: + sysFatal(FloatOverflowDefect, "FPU operation caused an overflow") + else: + sysFatal(FloatUnderflowDefect, "FPU operations caused an underflow") + +proc infCheck(x: float64) {.compilerproc, inline.} = + if x != 0.0 and x*0.5 == x: raiseFloatOverflow(x) diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim index 5c21e56ef..4242cd1e3 100644 --- a/lib/system/arithmetics.nim +++ b/lib/system/arithmetics.nim @@ -1,4 +1,4 @@ -proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} = +proc succ*[T: Ordinal](x: T, y = 1): T {.magic: "Succ", noSideEffect.} = ## Returns the `y`-th successor (default: 1) of the value `x`. ## ## If such a value does not exist, `OverflowDefect` is raised @@ -7,7 +7,7 @@ proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} = assert succ(5) == 6 assert succ(5, 3) == 8 -proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} = +proc pred*[T: Ordinal](x: T, y = 1): T {.magic: "Pred", noSideEffect.} = ## Returns the `y`-th predecessor (default: 1) of the value `x`. ## ## If such a value does not exist, `OverflowDefect` is raised @@ -16,7 +16,7 @@ proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} = assert pred(5) == 4 assert pred(5, 3) == 2 -proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} = +proc inc*[T: Ordinal](x: var T, y = 1) {.magic: "Inc", noSideEffect.} = ## Increments the ordinal `x` by `y`. ## ## If such a value does not exist, `OverflowDefect` is raised or a compile @@ -28,7 +28,7 @@ proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} = inc(i, 3) assert i == 6 -proc dec*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Dec", noSideEffect.} = +proc dec*[T: Ordinal](x: var T, y = 1) {.magic: "Dec", noSideEffect.} = ## Decrements the ordinal `x` by `y`. ## ## If such a value does not exist, `OverflowDefect` is raised or a compile @@ -45,6 +45,109 @@ proc dec*[T, V: Ordinal](x: var T, y: V = 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. @@ -124,7 +227,7 @@ proc `mod`*(x, y: int16): int16 {.magic: "ModI", noSideEffect.} proc `mod`*(x, y: int32): int32 {.magic: "ModI", noSideEffect.} proc `mod`*(x, y: int64): int64 {.magic: "ModI", noSideEffect.} -when defined(nimOldShiftRight): +when defined(nimOldShiftRight) or not defined(nimAshr): const shrDepMessage = "`shr` will become sign preserving." proc `shr`*(x: int, y: SomeInteger): int {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.} proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.} @@ -168,23 +271,27 @@ proc `shl`*(x: int16, y: SomeInteger): int16 {.magic: "ShlI", noSideEffect.} proc `shl`*(x: int32, y: SomeInteger): int32 {.magic: "ShlI", noSideEffect.} proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.} -proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} = - ## Shifts right by pushing copies of the leftmost bit in from the left, - ## and let the rightmost bits fall off. - ## - ## Note that `ashr` is not an operator so use the normal function - ## call syntax for it. - ## - ## See also: - ## * `shr func<#shr,int,SomeInteger>`_ - runnableExamples: - assert ashr(0b0001_0000'i8, 2) == 0b0000_0100'i8 - assert ashr(0b1000_0000'i8, 8) == 0b1111_1111'i8 - assert ashr(0b1000_0000'i8, 1) == 0b1100_0000'i8 -proc ashr*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.} -proc ashr*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.} -proc ashr*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.} -proc ashr*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.} +when defined(nimAshr): + proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} = + ## Shifts right by pushing copies of the leftmost bit in from the left, + ## and let the rightmost bits fall off. + ## + ## Note that `ashr` is not an operator so use the normal function + ## call syntax for it. + ## + ## See also: + ## * `shr func<#shr,int,SomeInteger>`_ + runnableExamples: + assert ashr(0b0001_0000'i8, 2) == 0b0000_0100'i8 + assert ashr(0b1000_0000'i8, 8) == 0b1111_1111'i8 + assert ashr(0b1000_0000'i8, 1) == 0b1100_0000'i8 + proc ashr*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.} + proc ashr*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.} + proc ashr*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.} + proc ashr*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.} +else: + # used for bootstrapping the compiler + proc ashr*[T](x: T, y: SomeInteger): T = discard proc `and`*(x, y: int): int {.magic: "BitandI", noSideEffect.} = ## Computes the `bitwise and` of numbers `x` and `y`. @@ -282,7 +389,7 @@ proc `*`*(x, y: uint64): uint64 {.magic: "MulU", noSideEffect.} proc `div`*(x, y: uint): uint {.magic: "DivU", noSideEffect.} ## Computes the integer division for unsigned integers. - ## This is roughly the same as `trunc(x/y)`. + ## This is roughly the same as ``trunc(x/y)``. proc `div`*(x, y: uint8): uint8 {.magic: "DivU", noSideEffect.} proc `div`*(x, y: uint16): uint16 {.magic: "DivU", noSideEffect.} proc `div`*(x, y: uint32): uint32 {.magic: "DivU", noSideEffect.} @@ -290,65 +397,12 @@ proc `div`*(x, y: uint64): uint64 {.magic: "DivU", noSideEffect.} proc `mod`*(x, y: uint): uint {.magic: "ModU", noSideEffect.} ## Computes the integer modulo operation (remainder) for unsigned integers. - ## This is the same as `x - (x div y) * y`. + ## This is the same as ``x - (x div y) * y``. proc `mod`*(x, y: uint8): uint8 {.magic: "ModU", noSideEffect.} 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. ## @@ -404,58 +458,15 @@ 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)) -when not defined(nimPreviewSlimSystem): - 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 `+=`*[T: SomeInteger](x: var T, y: T) {. + magic: "Inc", noSideEffect.} + ## Increments an integer. - 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 `-=`*[T: SomeInteger](x: var T, y: T) {. + magic: "Dec", noSideEffect.} + ## Decrements an integer. - 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) +proc `*=`*[T: SomeInteger](x: var T, y: T) {. + inline, noSideEffect.} = + ## Binary `*=` operator for integers. + x = x * y diff --git a/lib/system/assertions.nim b/lib/system/assertions.nim new file mode 100644 index 000000000..4c25d2a56 --- /dev/null +++ b/lib/system/assertions.nim @@ -0,0 +1,111 @@ +when not declared(sysFatal): + include "system/fatal" + +import std/private/miscdollars +# --------------------------------------------------------------------------- +# helpers + +type InstantiationInfo = tuple[filename: string, line: int, column: int] + +proc `$`(x: int): string {.magic: "IntToStr", noSideEffect.} +proc `$`(info: InstantiationInfo): string = + # The +1 is needed here + # instead of overriding `$` (and changing its meaning), consider explicit name. + result = "" + result.toLocation(info.filename, info.line, info.column+1) + +# --------------------------------------------------------------------------- + +when not defined(nimHasSinkInference): + {.pragma: nosinks.} + +proc raiseAssert*(msg: string) {.noinline, noreturn, nosinks.} = + sysFatal(AssertionDefect, msg) + +proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = + # trick the compiler to not list ``AssertionDefect`` when called + # by ``assert``. + type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect, + tags: [].} + cast[Hide](raiseAssert)(msg) + +template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) = + when enabled: + const + loc = instantiationInfo(fullPaths = compileOption("excessiveStackTrace")) + ploc = $loc + bind instantiationInfo + mixin failedAssertImpl + {.line: loc.}: + if not cond: + failedAssertImpl(ploc & " `" & expr & "` " & msg) + +template assert*(cond: untyped, msg = "") = + ## Raises ``AssertionDefect`` with `msg` if `cond` is false. Note + ## that ``AssertionDefect`` is hidden from the effect system, so it doesn't + ## produce ``{.raises: [AssertionDefect].}``. This exception is only supposed + ## to be caught by unit testing frameworks. + ## + ## The compiler may not generate any code at all for ``assert`` if it is + ## advised to do so through the ``-d:danger`` or ``--assertions:off`` + ## `command line switches `_. + ## + ## .. code-block:: nim + ## static: assert 1 == 9, "This assertion generates code when not built with -d:danger or --assertions:off" + const expr = astToStr(cond) + assertImpl(cond, msg, expr, compileOption("assertions")) + +template doAssert*(cond: untyped, msg = "") = + ## Similar to ``assert`` but is always turned on regardless of ``--assertions``. + ## + ## .. code-block:: nim + ## static: doAssert 1 == 9, "This assertion generates code when built with/without -d:danger or --assertions:off" + const expr = astToStr(cond) + assertImpl(cond, msg, expr, true) + +template onFailedAssert*(msg, code: untyped): untyped {.dirty.} = + ## Sets an assertion failure handler that will intercept any assert + ## statements following `onFailedAssert` in the current module scope. + ## + ## .. code-block:: nim + ## # module-wide policy to change the failed assert + ## # exception type in order to include a lineinfo + ## onFailedAssert(msg): + ## var e = new(TMyError) + ## e.msg = msg + ## e.lineinfo = instantiationInfo(-2) + ## raise e + ## + template failedAssertImpl(msgIMPL: string): untyped {.dirty.} = + let msg = msgIMPL + code + +template doAssertRaises*(exception: typedesc, code: untyped) = + ## Raises ``AssertionDefect`` if specified ``code`` does not raise `exception`. + ## Example: + ## + ## .. code-block:: nim + ## doAssertRaises(ValueError): + ## raise newException(ValueError, "Hello World") + var wrong = false + const begin = "expected raising '" & astToStr(exception) & "', instead" + const msgEnd = " by: " & astToStr(code) + template raisedForeign = raiseAssert(begin & " raised foreign exception" & msgEnd) + when Exception is exception: + try: + if true: + code + wrong = true + except Exception as e: discard + except: raisedForeign() + else: + try: + if true: + code + wrong = true + except exception: + discard + except Exception as e: raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd) + except: raisedForeign() + if wrong: + raiseAssert(begin & " nothing was raised" & msgEnd) diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 42587929c..a809fa423 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -15,8 +15,8 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.benign. proc genericAssignAux(dest, src: pointer, n: ptr TNimNode, shallow: bool) {.benign.} = var - d = cast[int](dest) - s = cast[int](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) case n.kind of nkSlot: genericAssignAux(cast[pointer](d +% n.offset), @@ -56,8 +56,8 @@ template deepSeqAssignImpl(operation, additionalArg) {.dirty.} = proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = var - d = cast[int](dest) - s = cast[int](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) sysAssert(mt != nil, "genericAssignAux 2") case mt.kind of tyString: @@ -89,17 +89,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = var ss = nimNewSeqOfCap(mt, seq.len) cast[PGenericSeq](ss).len = seq.len unsureAsgnRef(x, ss) - var dst = cast[int](cast[PPointer](dest)[]) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) copyMem(cast[pointer](dst +% align(GenericSeqSize, mt.base.align)), - cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align)), + cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align)), seq.len *% mt.base.size) else: unsureAsgnRef(x, newSeq(mt, seq.len)) - var dst = cast[int](cast[PPointer](dest)[]) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) for i in 0..seq.len-1: genericAssignAux( cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), - cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), + cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), mt.base, shallow) of tyObject: var it = mt.base @@ -122,11 +122,7 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = # var tbObj = TB(p) # tbObj of TC # needs to be false! #c_fprintf(stdout, "%s %s\n", pint[].name, mt.name) - let srcType = cast[ptr PNimType](src)[] - if srcType != nil: - # `!= nil` needed because of cases where object is not initialized properly (see bug #16706) - # note that you can have `srcType == nil` yet `src != nil` - chckObjAsgn(srcType, mt) + chckObjAsgn(cast[ptr PNimType](src)[], mt) pint[] = mt # cast[ptr PNimType](src)[] of tyTuple: genericAssignAux(dest, src, mt.node, shallow) @@ -167,7 +163,7 @@ when false: of tyPointer: k = "range" of tyOpenArray: k = "openarray" of tyString: k = "string" - of tyCstring: k = "cstring" + of tyCString: k = "cstring" of tyInt: k = "int" of tyInt32: k = "int32" else: k = "other" @@ -181,15 +177,15 @@ proc genericSeqAssign(dest, src: pointer, mt: PNimType) {.compilerproc.} = proc genericAssignOpenArray(dest, src: pointer, len: int, mt: PNimType) {.compilerproc.} = var - d = cast[int](dest) - s = cast[int](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) for i in 0..len-1: genericAssign(cast[pointer](d +% i *% mt.base.size), cast[pointer](s +% i *% mt.base.size), mt.base) proc objectInit(dest: pointer, typ: PNimType) {.compilerproc, benign.} proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} = - var d = cast[int](dest) + var d = cast[ByteAddress](dest) case n.kind of nkNone: sysAssert(false, "objectInitAux") of nkSlot: objectInit(cast[pointer](d +% n.offset), n.typ) @@ -203,7 +199,7 @@ proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} = proc objectInit(dest: pointer, typ: PNimType) = # the generic init proc that takes care of initialization of complex # objects on the stack or heap - var d = cast[int](dest) + var d = cast[ByteAddress](dest) case typ.kind of tyObject: # iterate over any structural type @@ -226,7 +222,7 @@ proc objectInit(dest: pointer, typ: PNimType) = proc genericReset(dest: pointer, mt: PNimType) {.compilerproc, benign.} proc genericResetAux(dest: pointer, n: ptr TNimNode) = - var d = cast[int](dest) + var d = cast[ByteAddress](dest) case n.kind of nkNone: sysAssert(false, "genericResetAux") of nkSlot: genericReset(cast[pointer](d +% n.offset), n.typ) @@ -238,7 +234,7 @@ proc genericResetAux(dest: pointer, n: ptr TNimNode) = zeroMem(cast[pointer](d +% n.offset), n.typ.size) proc genericReset(dest: pointer, mt: PNimType) = - var d = cast[int](dest) + var d = cast[ByteAddress](dest) sysAssert(mt != nil, "genericReset 2") case mt.kind of tyRef: diff --git a/lib/std/sysatomics.nim b/lib/system/atomics.nim similarity index 81% rename from lib/std/sysatomics.nim rename to lib/system/atomics.nim index 36a4e5537..f69cb753d 100644 --- a/lib/std/sysatomics.nim +++ b/lib/system/atomics.nim @@ -7,22 +7,17 @@ # distribution, for details about the copyright. # -when defined(nimPreviewSlimSystem): - {.deprecated: "use `std/atomics` instead".} - # Atomic operations for Nim. {.push stackTrace:off, profiler:off.} -const - hasThreadSupport = compileOption("threads") and not defined(nimscript) -const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang) or defined(nintendoswitch) +const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang) const someVcc = defined(vcc) or defined(clang_cl) type AtomType* = SomeNumber|pointer|ptr|char|bool ## Type Class representing valid types for use with atomic procs -when someGcc: +when someGcc and hasThreadSupport: type AtomMemModel* = distinct cint var ATOMIC_RELAXED* {.importc: "__ATOMIC_RELAXED", nodecl.}: AtomMemModel @@ -78,7 +73,7 @@ when someGcc: proc atomicCompareExchangeN*[T: AtomType](p, expected: ptr T, desired: T, weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {. - importc: "__atomic_compare_exchange_n", nodecl.} + importc: "__atomic_compare_exchange_n ", nodecl.} ## This proc implements an atomic compare and exchange operation. This compares the ## contents at p with the contents at expected and if equal, writes desired at p. ## If they are not equal, the current contents at p is written into expected. @@ -105,13 +100,13 @@ when someGcc: proc atomicSubFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. importc: "__atomic_sub_fetch", nodecl.} proc atomicOrFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. - importc: "__atomic_or_fetch", nodecl.} + importc: "__atomic_or_fetch ", nodecl.} proc atomicAndFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. importc: "__atomic_and_fetch", nodecl.} proc atomicXorFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. importc: "__atomic_xor_fetch", nodecl.} proc atomicNandFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. - importc: "__atomic_nand_fetch", nodecl.} + importc: "__atomic_nand_fetch ", nodecl.} ## Perform the operation return the old value, all memory models are valid proc atomicFetchAdd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. @@ -169,48 +164,23 @@ when someGcc: ## ignore this parameter. template fence*() = atomicThreadFence(ATOMIC_SEQ_CST) -elif someVcc: +elif someVcc and hasThreadSupport: type AtomMemModel* = distinct cint const - ATOMIC_RELAXED* = 0.AtomMemModel - ATOMIC_CONSUME* = 1.AtomMemModel - ATOMIC_ACQUIRE* = 2.AtomMemModel - ATOMIC_RELEASE* = 3.AtomMemModel - ATOMIC_ACQ_REL* = 4.AtomMemModel - ATOMIC_SEQ_CST* = 5.AtomMemModel + ATOMIC_RELAXED = 0.AtomMemModel + ATOMIC_CONSUME = 1.AtomMemModel + ATOMIC_ACQUIRE = 2.AtomMemModel + ATOMIC_RELEASE = 3.AtomMemModel + ATOMIC_ACQ_REL = 4.AtomMemModel + ATOMIC_SEQ_CST = 5.AtomMemModel proc `==`(x1, x2: AtomMemModel): bool {.borrow.} - proc readBarrier() {.importc: "_ReadBarrier", header: "".} - proc writeBarrier() {.importc: "_WriteBarrier", header: "".} + proc readBarrier() {.importc: "_ReadBarrier", header: "".} + proc writeBarrier() {.importc: "_WriteBarrier", header: "".} proc fence*() {.importc: "_ReadWriteBarrier", header: "".} - when defined(cpp): - proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64 - {.importcpp: "_InterlockedCompareExchange64(static_cast(#), #, #)", header: "".} - proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32 - {.importcpp: "_InterlockedCompareExchange(static_cast(#), #, #)", header: "".} - proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte - {.importcpp: "_InterlockedCompareExchange8(static_cast(#), #, #)", header: "".} - proc interlockedExchange8(location: pointer; desired: int8): int8 {.importcpp: "_InterlockedExchange8(static_cast(#), #)", header: "".} - proc interlockedExchange16(location: pointer; desired: int16): int16 {.importcpp: "_InterlockedExchange16(static_cast(#), #)", header: "".} - proc interlockedExchange32(location: pointer; desired: int32): int32 {.importcpp: "_InterlockedExchange(static_cast(#), #)", header: "".} - proc interlockedExchange64(location: pointer; desired: int64): int64 {.importcpp: "_InterlockedExchange64(static_cast(#), #)", header: "".} - else: - proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64 - {.importc: "_InterlockedCompareExchange64", header: "".} - proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32 - {.importc: "_InterlockedCompareExchange", header: "".} - proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte - {.importc: "_InterlockedCompareExchange8", header: "".} - - proc interlockedExchange8(location: pointer; desired: int8): int8 {.importc: "_InterlockedExchange8", header: "".} - proc interlockedExchange16(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange16", header: "".} - proc interlockedExchange32(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange", header: "".} - proc interlockedExchange64(location: pointer; desired: int64): int64 {.importc: "_InterlockedExchange64", header: "".} - - template barrier(mem: AtomMemModel) = when mem == ATOMIC_RELAXED: discard elif mem == ATOMIC_CONSUME: readBarrier() @@ -219,28 +189,10 @@ elif someVcc: elif mem == ATOMIC_ACQ_REL: fence() elif mem == ATOMIC_SEQ_CST: fence() - proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: static[AtomMemModel]) = - barrier(mem) - p[] = val - proc atomicLoadN*[T: AtomType](p: ptr T, mem: static[AtomMemModel]): T = result = p[] barrier(mem) - proc atomicCompareExchangeN*[T: ptr](p, expected: ptr T, desired: T, - weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool = - when sizeof(T) == 8: - interlockedCompareExchange64(p, cast[int64](desired), cast[int64](expected)) == - cast[int64](expected) - elif sizeof(T) == 4: - interlockedCompareExchange32(p, cast[int32](desired), cast[int32](expected)) == - cast[int32](expected) - - proc atomicExchangeN*[T: ptr](p: ptr T, val: T, mem: AtomMemModel): T = - when sizeof(T) == 8: - cast[T](interlockedExchange64(p, cast[int64](val))) - elif sizeof(T) == 4: - cast[T](interlockedExchange32(p, cast[int32](val))) when defined(cpp): when sizeof(int) == 8: proc addAndFetch*(p: ptr int, val: int): int {. @@ -263,10 +215,9 @@ else: inc(p[], val) result = p[] - -proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} = +proc atomicInc*(memLoc: var int, x: int = 1): int = when someGcc and hasThreadSupport: - result = atomicAddFetch(memLoc.addr, x, ATOMIC_SEQ_CST) + result = atomicAddFetch(memLoc.addr, x, ATOMIC_RELAXED) elif someVcc and hasThreadSupport: result = addAndFetch(memLoc.addr, x) inc(result, x) @@ -274,12 +225,12 @@ proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: inc(memLoc, x) result = memLoc -proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} = +proc atomicDec*(memLoc: var int, x: int = 1): int = when someGcc and hasThreadSupport: when declared(atomicSubFetch): - result = atomicSubFetch(memLoc.addr, x, ATOMIC_SEQ_CST) + result = atomicSubFetch(memLoc.addr, x, ATOMIC_RELAXED) else: - result = atomicAddFetch(memLoc.addr, -x, ATOMIC_SEQ_CST) + result = atomicAddFetch(memLoc.addr, -x, ATOMIC_RELAXED) elif someVcc and hasThreadSupport: result = addAndFetch(memLoc.addr, -x) dec(result, x) @@ -288,6 +239,21 @@ proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: result = memLoc when someVcc: + when defined(cpp): + proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64 + {.importcpp: "_InterlockedCompareExchange64(static_cast(#), #, #)", header: "".} + proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32 + {.importcpp: "_InterlockedCompareExchange(static_cast(#), #, #)", header: "".} + proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte + {.importcpp: "_InterlockedCompareExchange8(static_cast(#), #, #)", header: "".} + else: + proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64 + {.importc: "_InterlockedCompareExchange64", header: "".} + proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32 + {.importc: "_InterlockedCompareExchange", header: "".} + proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte + {.importc: "_InterlockedCompareExchange8", header: "".} + proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = when sizeof(T) == 8: interlockedCompareExchange64(p, cast[int64](newValue), cast[int64](oldValue)) == diff --git a/lib/system/basic_types.nim b/lib/system/basic_types.nim index bf81b9b6a..9db81d1c4 100644 --- a/lib/system/basic_types.nim +++ b/lib/system/basic_types.nim @@ -1,53 +1,23 @@ 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. - -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). + 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 # we need to start a new type section here, so that ``0`` can have a type bool* {.magic: "Bool".} = enum ## Built-in boolean type. false = 0, true = 1 const - on* = true ## Alias for `true`. - off* = false ## Alias for `false`. + on* = true ## Alias for ``true``. + off* = false ## Alias for ``false``. type SomeSignedInt* = int|int8|int16|int32|int64 @@ -59,35 +29,34 @@ 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.} proc `not`*(x: bool): bool {.magic: "Not", noSideEffect.} - ## Boolean not; returns true if `x == false`. + ## Boolean not; returns true if ``x == false``. proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.} - ## Boolean `and`; returns true if `x == y == true` (if both arguments + ## Boolean ``and``; returns true if ``x == y == true`` (if both arguments ## are true). ## - ## Evaluation is lazy: if `x` is false, `y` will not even be evaluated. + ## Evaluation is lazy: if ``x`` is false, ``y`` will not even be evaluated. proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.} - ## Boolean `or`; returns true if `not (not x and not y)` (if any of + ## Boolean ``or``; returns true if ``not (not x and not y)`` (if any of ## the arguments is true). ## - ## Evaluation is lazy: if `x` is true, `y` will not even be evaluated. + ## Evaluation is lazy: if ``x`` is true, ``y`` will not even be evaluated. proc `xor`*(x, y: bool): bool {.magic: "Xor", noSideEffect.} - ## Boolean `exclusive or`; returns true if `x != y` (if either argument + ## Boolean `exclusive or`; returns true if ``x != y`` (if either argument ## is true while the other is false). {.pop.} diff --git a/lib/system/bitmasks.nim b/lib/system/bitmasks.nim index 0663247c2..d7c55a4d9 100644 --- a/lib/system/bitmasks.nim +++ b/lib/system/bitmasks.nim @@ -10,18 +10,13 @@ # Page size of the system; in most cases 4096 bytes. For exotic OS or # CPU this needs to be changed: const - PageShift = when defined(nimPage256) or defined(cpu16): 3 - elif defined(nimPage512): 9 - elif defined(nimPage1k): 10 - else: 12 # \ # my tests showed no improvements for using larger page sizes. - + PageShift = when defined(cpu16): 8 else: 12 # \ + # my tests showed no improvements for using larger page sizes. PageSize = 1 shl PageShift PageMask = PageSize-1 - MemAlign = # also minimal allocatable memory block - when defined(nimMemAlignTiny): 4 - elif defined(useMalloc): + when defined(useMalloc): when defined(amd64): 16 else: 8 else: 16 diff --git a/lib/system/cellseqs_v2.nim b/lib/system/cellseqs_v2.nim index 27be48d78..59cf67dcd 100644 --- a/lib/system/cellseqs_v2.nim +++ b/lib/system/cellseqs_v2.nim @@ -10,20 +10,20 @@ # Cell seqs for cyclebreaker and cyclicrefs_v2. type - CellTuple[T] = (T, PNimTypeV2) - CellArray[T] = ptr UncheckedArray[CellTuple[T]] - CellSeq[T] = object + CellTuple = (PT, PNimTypeV2) + CellArray = ptr UncheckedArray[CellTuple] + CellSeq = object len, cap: int - d: CellArray[T] + d: CellArray -proc add[T](s: var CellSeq[T], c: T; t: PNimTypeV2) {.inline.} = +proc add(s: var CellSeq, c: PT; t: PNimTypeV2) {.inline.} = if s.len >= s.cap: s.cap = s.cap * 3 div 2 when compileOption("threads"): - var d = cast[CellArray[T]](allocShared(uint(s.cap * sizeof(CellTuple[T])))) + var d = cast[CellArray](allocShared(uint(s.cap * sizeof(CellTuple)))) else: - var d = cast[CellArray[T]](alloc(s.cap * sizeof(CellTuple[T]))) - copyMem(d, s.d, s.len * sizeof(CellTuple[T])) + var d = cast[CellArray](alloc(s.cap * sizeof(CellTuple))) + copyMem(d, s.d, s.len * sizeof(CellTuple)) when compileOption("threads"): deallocShared(s.d) else: @@ -33,15 +33,15 @@ proc add[T](s: var CellSeq[T], c: T; t: PNimTypeV2) {.inline.} = s.d[s.len] = (c, t) inc(s.len) -proc init[T](s: var CellSeq[T], cap: int = 1024) = +proc init(s: var CellSeq, cap: int = 1024) = s.len = 0 s.cap = cap when compileOption("threads"): - s.d = cast[CellArray[T]](allocShared(uint(s.cap * sizeof(CellTuple[T])))) + s.d = cast[CellArray](allocShared(uint(s.cap * sizeof(CellTuple)))) else: - s.d = cast[CellArray[T]](alloc(s.cap * sizeof(CellTuple[T]))) + s.d = cast[CellArray](alloc(s.cap * sizeof(CellTuple))) -proc deinit[T](s: var CellSeq[T]) = +proc deinit(s: var CellSeq) = if s.d != nil: when compileOption("threads"): deallocShared(s.d) @@ -51,6 +51,6 @@ proc deinit[T](s: var CellSeq[T]) = s.len = 0 s.cap = 0 -proc pop[T](s: var CellSeq[T]): (T, PNimTypeV2) = +proc pop(s: var CellSeq): (PT, PNimTypeV2) = result = s.d[s.len-1] dec s.len diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim index a0f1fabf9..ea00176b5 100644 --- a/lib/system/cellsets.nim +++ b/lib/system/cellsets.nim @@ -7,47 +7,13 @@ # distribution, for details about the copyright. # - -#[ - -Efficient set of pointers for the GC (and repr) ------------------------------------------------ - -The GC depends on an extremely efficient datastructure for storing a -set of pointers - this is called a `CellSet` in the source code. -Inserting, deleting and searching are done in constant time. However, -modifying a `CellSet` during traversal leads to undefined behaviour. - -All operations on a CellSet have to perform efficiently. Because a Cellset can -become huge a hash table alone is not suitable for this. - -We use a mixture of bitset and hash table for this. The hash table maps *pages* -to a page descriptor. The page descriptor contains a bit for any possible cell -address within this page. So including a cell is done as follows: - -- Find the page descriptor for the page the cell belongs to. -- Set the appropriate bit in the page descriptor indicating that the - cell points to the start of a memory block. - -Removing a cell is analogous - the bit has to be set to zero. -Single page descriptors are never deleted from the hash table. This is not -needed as the data structures needs to be rebuilt periodically anyway. - -Complete traversal is done in this way:: - - for each page descriptor d: - for each bit in d: - if bit == 1: - traverse the pointer belonging to this bit - -]# +# Efficient set of pointers for the GC (and repr) when defined(gcOrc) or defined(gcArc): type PCell = Cell - when not declaredInScope(PageShift): - include bitmasks + include bitmasks else: type diff --git a/lib/system/channels_builtin.nim b/lib/system/channels.nim similarity index 92% rename from lib/system/channels_builtin.nim rename to lib/system/channels.nim index e04f2a0db..e4b172c16 100644 --- a/lib/system/channels_builtin.nim +++ b/lib/system/channels.nim @@ -10,10 +10,10 @@ ## Channel support for threads. ## ## **Note**: This is part of the system module. Do not import it directly. -## To activate thread support compile with the `--threads:on` command line switch. +## To activate thread support compile with the ``--threads:on`` command line switch. ## -## **Note:** Channels are designed for the `Thread` type. They are unstable when -## used with `spawn` +## **Note:** Channels are designed for the ``Thread`` type. They are unstable when +## used with ``spawn`` ## ## **Note:** The current implementation of message passing does ## not work with cyclic data structures. @@ -26,11 +26,11 @@ ## The following is a simple example of two different ways to use channels: ## blocking and non-blocking. ## -## .. code-block:: Nim +## .. code-block :: Nim ## # Be sure to compile with --threads:on. ## # The channels and threads modules are part of system and should not be ## # imported. -## import std/os +## import os ## ## # Channels can either be: ## # - declared at the module level, or @@ -91,18 +91,17 @@ ## Sample output ## ------------- ## The program should output something similar to this, but keep in mind that -## exact results may vary in the real world: -## -## Hello World! -## Pretend I'm doing useful work... -## Pretend I'm doing useful work... -## Pretend I'm doing useful work... -## Pretend I'm doing useful work... -## Pretend I'm doing useful work... -## Another message +## exact results may vary in the real world:: +## Hello World! +## Pretend I'm doing useful work... +## Pretend I'm doing useful work... +## Pretend I'm doing useful work... +## Pretend I'm doing useful work... +## Pretend I'm doing useful work... +## Another message ## ## Passing Channels Safely -## ----------------------- +## ---------------------- ## Note that when passing objects to procedures on another thread by pointer ## (for example through a thread's argument), objects created using the default ## allocator will use thread-local, GC-managed memory. Thus it is generally @@ -110,10 +109,10 @@ ## in which case they will use a process-wide (thread-safe) shared heap. ## ## However, it is possible to manually allocate shared memory for channels -## using e.g. `system.allocShared0` and pass these pointers through thread +## using e.g. ``system.allocShared0`` and pass these pointers through thread ## arguments: ## -## .. code-block:: Nim +## .. code-block :: Nim ## proc worker(channel: ptr Channel[string]) = ## let greeting = channel[].recv() ## echo greeting @@ -139,8 +138,6 @@ when not declared(ThisIsSystem): {.error: "You must not import this module explicitly".} -import std/private/syslocks - type pbytes = ptr UncheckedArray[byte] RawChannel {.pure, final.} = object ## msg queue for a thread @@ -154,7 +151,7 @@ type region: MemRegion PRawChannel = ptr RawChannel LoadStoreMode = enum mStore, mLoad - Channel*[TMsg] {.gcsafe.} = RawChannel ## a channel for thread communication + Channel* {.gcsafe.}[TMsg] = RawChannel ## a channel for thread communication const ChannelDeadMask = -2 @@ -185,8 +182,8 @@ when not usesDestructors: proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel, mode: LoadStoreMode) {.benign.} = var - d = cast[int](dest) - s = cast[int](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) case n.kind of nkSlot: storeAux(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), n.typ, t, mode) @@ -205,8 +202,8 @@ when not usesDestructors: cast[pointer](cast[int](p) +% x) var - d = cast[int](dest) - s = cast[int](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) sysAssert(mt != nil, "mt == nil") case mt.kind of tyString: @@ -245,14 +242,14 @@ when not usesDestructors: x[] = alloc0(t.region, align(GenericSeqSize, mt.base.align) +% seq.len *% mt.base.size) else: unsureAsgnRef(x, newSeq(mt, seq.len)) - var dst = cast[int](cast[PPointer](dest)[]) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) var dstseq = cast[PGenericSeq](dst) dstseq.len = seq.len dstseq.reserved = seq.len for i in 0..seq.len-1: storeAux( - cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), - cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% + cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i*% mt.base.size), + cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), mt.base, t, mode) if mode != mStore: dealloc(t.region, s2) @@ -268,8 +265,8 @@ when not usesDestructors: storeAux(dest, src, mt.node, t, mode) of tyArray, tyArrayConstr: for i in 0..(mt.size div mt.base.size)-1: - storeAux(cast[pointer](d +% i *% mt.base.size), - cast[pointer](s +% i *% mt.base.size), mt.base, t, mode) + storeAux(cast[pointer](d +% i*% mt.base.size), + cast[pointer](s +% i*% mt.base.size), mt.base, t, mode) of tyRef: var s = cast[PPointer](src)[] var x = cast[PPointer](dest) @@ -363,8 +360,8 @@ proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool rawSend(q, msg, typ) q.elemType = typ - signalSysCond(q.cond) releaseSys(q.lock) + signalSysCond(q.cond) result = true proc send*[TMsg](c: var Channel[TMsg], msg: sink TMsg) {.inline.} = @@ -413,8 +410,8 @@ proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool, ## Tries to receive a message from the channel `c`, but this can fail ## for all sort of reasons, including contention. ## - ## If it fails, it returns `(false, default(msg))` otherwise it - ## returns `(true, msg)`. + ## If it fails, it returns ``(false, default(msg))`` otherwise it + ## returns ``(true, msg)``. var q = cast[PRawChannel](addr(c)) if q.mask != ChannelDeadMask: if tryAcquireSys(q.lock): diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index dd26d140d..e0a8bd78e 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -9,8 +9,6 @@ # Implementation of some runtime checks. include system/indexerrors -when defined(nimPreviewSlimSystem): - import std/formatfloat proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} = when hostOS == "standalone": @@ -18,9 +16,6 @@ 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)) @@ -31,29 +26,13 @@ proc raiseIndexError() {.compilerproc, noinline.} = sysFatal(IndexDefect, "index out of bounds") proc raiseFieldError(f: string) {.compilerproc, noinline.} = - ## remove after bootstrap > 1.5.1 sysFatal(FieldDefect, f) -when defined(nimV2): - proc raiseFieldError2(f: string, discVal: int) {.compilerproc, noinline.} = - ## raised when field is inaccessible given runtime value of discriminant - sysFatal(FieldDefect, f & $discVal & "'") -else: - proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noinline.} = - ## raised when field is inaccessible given runtime value of discriminant - sysFatal(FieldDefect, formatFieldDefect(f, discVal)) - proc raiseRangeErrorI(i, a, b: BiggestInt) {.compilerproc, noinline.} = - when defined(standalone): - sysFatal(RangeDefect, "value out of range") - else: - sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b) + sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b) proc raiseRangeErrorF(i, a, b: float) {.compilerproc, noinline.} = - when defined(standalone): - sysFatal(RangeDefect, "value out of range") - else: - sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b) + sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b) proc raiseRangeErrorU(i, a, b: uint64) {.compilerproc, noinline.} = # todo: better error reporting diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index 36d4d06a8..c57cfa965 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -9,7 +9,7 @@ proc `==`*[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.} = place1, place2 = 3 var e1 = field1 - e2 = place2.ord.Enum1 + e2 = Enum1(place2) assert e1 == e2 assert not compiles(e1 == place2) # raises error proc `==`*(x, y: pointer): bool {.magic: "EqRef", noSideEffect.} = @@ -125,19 +125,16 @@ proc `<`*[T](x, y: ref T): bool {.magic: "LtPtr", noSideEffect.} proc `<`*[T](x, y: ptr T): bool {.magic: "LtPtr", noSideEffect.} proc `<`*(x, y: pointer): bool {.magic: "LtPtr", noSideEffect.} -when not defined(nimHasCallsitePragma): - {.pragma: callsite.} - -template `!=`*(x, y: untyped): untyped {.callsite.} = - ## Unequals operator. This is a shorthand for `not (x == y)`. +template `!=`*(x, y: untyped): untyped = + ## Unequals operator. This is a shorthand for ``not (x == y)``. not (x == y) -template `>=`*(x, y: untyped): untyped {.callsite.} = - ## "is greater or equals" operator. This is the same as `y <= x`. +template `>=`*(x, y: untyped): untyped = + ## "is greater or equals" operator. This is the same as ``y <= x``. y <= x -template `>`*(x, y: untyped): untyped {.callsite.} = - ## "is greater" operator. This is the same as `y < x`. +template `>`*(x, y: untyped): untyped = + ## "is greater" operator. This is the same as ``y < x``. y < x @@ -163,14 +160,14 @@ proc `<`*(x, y: int32): bool {.magic: "LtI", noSideEffect.} proc `<`*(x, y: int64): bool {.magic: "LtI", noSideEffect.} proc `<=`*(x, y: uint): bool {.magic: "LeU", noSideEffect.} - ## Returns true if `x <= y`. + ## Returns true if ``x <= y``. proc `<=`*(x, y: uint8): bool {.magic: "LeU", noSideEffect.} proc `<=`*(x, y: uint16): bool {.magic: "LeU", noSideEffect.} proc `<=`*(x, y: uint32): bool {.magic: "LeU", noSideEffect.} proc `<=`*(x, y: uint64): bool {.magic: "LeU", noSideEffect.} proc `<`*(x, y: uint): bool {.magic: "LtU", noSideEffect.} - ## Returns true if `x < y`. + ## Returns true if ``x < y``. proc `<`*(x, y: uint8): bool {.magic: "LtU", noSideEffect.} proc `<`*(x, y: uint16): bool {.magic: "LtU", noSideEffect.} proc `<`*(x, y: uint32): bool {.magic: "LtU", noSideEffect.} @@ -178,7 +175,7 @@ proc `<`*(x, y: uint64): bool {.magic: "LtU", noSideEffect.} proc `<=%`*(x, y: int): bool {.inline.} = ## Treats `x` and `y` as unsigned and compares them. - ## Returns true if `unsigned(x) <= unsigned(y)`. + ## Returns true if ``unsigned(x) <= unsigned(y)``. cast[uint](x) <= cast[uint](y) proc `<=%`*(x, y: int8): bool {.inline.} = cast[uint8](x) <= cast[uint8](y) proc `<=%`*(x, y: int16): bool {.inline.} = cast[uint16](x) <= cast[uint16](y) @@ -187,7 +184,7 @@ proc `<=%`*(x, y: int64): bool {.inline.} = cast[uint64](x) <= cast[uint64](y) proc `<%`*(x, y: int): bool {.inline.} = ## Treats `x` and `y` as unsigned and compares them. - ## Returns true if `unsigned(x) < unsigned(y)`. + ## Returns true if ``unsigned(x) < unsigned(y)``. cast[uint](x) < cast[uint](y) proc `<%`*(x, y: int8): bool {.inline.} = cast[uint8](x) < cast[uint8](y) proc `<%`*(x, y: int16): bool {.inline.} = cast[uint16](x) < cast[uint16](y) @@ -196,11 +193,11 @@ proc `<%`*(x, y: int64): bool {.inline.} = cast[uint64](x) < cast[uint64](y) template `>=%`*(x, y: untyped): untyped = y <=% x ## Treats `x` and `y` as unsigned and compares them. - ## Returns true if `unsigned(x) >= unsigned(y)`. + ## Returns true if ``unsigned(x) >= unsigned(y)``. template `>%`*(x, y: untyped): untyped = y <% x ## Treats `x` and `y` as unsigned and compares them. - ## Returns true if `unsigned(x) > unsigned(y)`. + ## Returns true if ``unsigned(x) > unsigned(y)``. proc `==`*(x, y: uint): bool {.magic: "EqI", noSideEffect.} ## Compares two unsigned integers for equality. @@ -209,14 +206,6 @@ 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.} @@ -231,13 +220,6 @@ 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 @@ -250,23 +232,16 @@ 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 = - ## The minimum value of `x`. `T` needs to have a `<` operator. + ## The minimum value of `x`. ``T`` needs to have a ``<`` operator. result = x[0] for i in 1..high(x): if x[i] < result: result = x[i] proc max*[T](x: openArray[T]): T = - ## The maximum value of `x`. `T` needs to have a `<` operator. + ## The maximum value of `x`. ``T`` needs to have a ``<`` operator. result = x[0] for i in 1..high(x): if result < x[i]: result = x[i] @@ -275,13 +250,10 @@ proc max*[T](x: openArray[T]): T = proc clamp*[T](x, a, b: T): T = - ## Limits the value `x` within the interval \[a, b]. - ## This proc is equivalent to but faster than `max(a, min(b, x))`. + ## Limits the value `x` within the interval [a, b]. + ## This proc is equivalent to but fatser than `max(a, min(b, x))`. ## - ## .. warning:: `a <= b` is assumed and will not be checked (currently). - ## - ## **See also:** - ## `math.clamp` for a version that takes a `Slice[T]` instead. + ## **Note:** `a <= b` is assumed and will not be checked. runnableExamples: assert (1.4).clamp(0.0, 1.0) == 1.0 assert (0.5).clamp(0.0, 1.0) == 0.5 @@ -310,8 +282,12 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} = ## Generic equals operator for sequences: relies on a equals operator for ## the element type `T`. when nimvm: - if x.len == 0 and y.len == 0: - return true + when not defined(nimNoNil): + if x.isNil and y.isNil: + return true + else: + if x.len == 0 and y.len == 0: + return true else: when not defined(js): proc seqToPtr[T](x: seq[T]): pointer {.inline, noSideEffect.} = @@ -327,6 +303,10 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} = asm """`sameObject` = `x` === `y`""" if sameObject: return true + when not defined(nimNoNil): + if x.isNil or y.isNil: + return false + if x.len != y.len: return false diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim deleted file mode 100644 index c04990277..000000000 --- a/lib/system/compilation.nim +++ /dev/null @@ -1,208 +0,0 @@ -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 = 9 - ## 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. - -{.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 `_ - -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>`_ - -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. - -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/coro_detection.nim b/lib/system/coro_detection.nim deleted file mode 100644 index f6c1b5c15..000000000 --- a/lib/system/coro_detection.nim +++ /dev/null @@ -1,20 +0,0 @@ -## Coroutine detection logic - -template coroutinesSupportedPlatform(): bool = - when defined(sparc) or defined(ELATE) or defined(boehmgc) or defined(gogc) or - defined(nogc) or defined(gcRegions) or defined(gcMarkAndSweep): - false - else: - true - -when defined(nimCoroutines): - # Explicit opt-in. - when not coroutinesSupportedPlatform(): - {.error: "Coroutines are not supported on this architecture and/or garbage collector.".} - const nimCoroutines* = true -elif defined(noNimCoroutines): - # Explicit opt-out. - const nimCoroutines* = false -else: - # Autodetect coroutine support. - const nimCoroutines* = false diff --git a/lib/system/countbits_impl.nim b/lib/system/countbits_impl.nim deleted file mode 100644 index 34969cb32..000000000 --- a/lib/system/countbits_impl.nim +++ /dev/null @@ -1,93 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Contains the used algorithms for counting bits. - -from std/private/bitops_utils import forwardImpl, castToUnsigned - -const useBuiltins* = not defined(noIntrinsicsBitOpts) -const noUndefined* = defined(noUndefinedBitOpts) -const useGCC_builtins* = (defined(gcc) or defined(llvm_gcc) or - defined(clang)) and useBuiltins -const useICC_builtins* = defined(icc) and useBuiltins -const useVCC_builtins* = defined(vcc) and useBuiltins -const arch64* = sizeof(int) == 8 - -template countBitsImpl(n: uint32): int = - # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel - var v = uint32(n) - v = v - ((v shr 1'u32) and 0x55555555'u32) - v = (v and 0x33333333'u32) + ((v shr 2'u32) and 0x33333333'u32) - (((v + (v shr 4'u32) and 0xF0F0F0F'u32) * 0x1010101'u32) shr 24'u32).int - -template countBitsImpl(n: uint64): int = - # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel - var v = uint64(n) - v = v - ((v shr 1'u64) and 0x5555555555555555'u64) - v = (v and 0x3333333333333333'u64) + ((v shr 2'u64) and 0x3333333333333333'u64) - v = (v + (v shr 4'u64) and 0x0F0F0F0F0F0F0F0F'u64) - ((v * 0x0101010101010101'u64) shr 56'u64).int - - -when useGCC_builtins: - # Returns the number of set 1-bits in value. - proc builtin_popcount(x: cuint): cint {.importc: "__builtin_popcount", cdecl.} - proc builtin_popcountll(x: culonglong): cint {. - importc: "__builtin_popcountll", cdecl.} - -elif useVCC_builtins: - # Counts the number of one bits (population count) in a 16-, 32-, or 64-byte unsigned integer. - func builtin_popcnt16(a2: uint16): uint16 {. - importc: "__popcnt16", header: "".} - func builtin_popcnt32(a2: uint32): uint32 {. - importc: "__popcnt", header: "".} - func builtin_popcnt64(a2: uint64): uint64 {. - importc: "__popcnt64", header: "".} - -elif useICC_builtins: - # Intel compiler intrinsics: http://fulla.fnal.gov/intel/compiler_c/main_cls/intref_cls/common/intref_allia_misc.htm - # see also: https://software.intel.com/en-us/node/523362 - # Count the number of bits set to 1 in an integer a, and return that count in dst. - func builtin_popcnt32(a: cint): cint {. - importc: "_popcnt", header: "".} - func builtin_popcnt64(a: uint64): cint {. - importc: "_popcnt64", header: "".} - - -func countSetBitsImpl*(x: SomeInteger): int {.inline.} = - ## Counts the set bits in an integer (also called `Hamming weight`:idx:). - # TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT. - # like GCC and MSVC - let x = x.castToUnsigned - when nimvm: - result = forwardImpl(countBitsImpl, x) - else: - when useGCC_builtins: - when sizeof(x) <= 4: result = builtin_popcount(x.cuint).int - else: result = builtin_popcountll(x.culonglong).int - elif useVCC_builtins: - when sizeof(x) <= 2: result = builtin_popcnt16(x.uint16).int - elif sizeof(x) <= 4: result = builtin_popcnt32(x.uint32).int - elif arch64: result = builtin_popcnt64(x.uint64).int - else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).uint32).int + - builtin_popcnt32((x.uint64 shr 32'u64).uint32).int - elif useICC_builtins: - when sizeof(x) <= 4: result = builtin_popcnt32(x.cint).int - elif arch64: result = builtin_popcnt64(x.uint64).int - else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).cint).int + - builtin_popcnt32((x.uint64 shr 32'u64).cint).int - else: - when sizeof(x) <= 4: result = countBitsImpl(x.uint32) - else: result = countBitsImpl(x.uint64) - -proc countBits32*(n: uint32): int {.compilerproc, inline.} = - result = countSetBitsImpl(n) - -proc countBits64*(n: uint64): int {.compilerproc, inline.} = - result = countSetBitsImpl(n) diff --git a/lib/system/ctypes.nim b/lib/system/ctypes.nim deleted file mode 100644 index f6a341477..000000000 --- a/lib/system/ctypes.nim +++ /dev/null @@ -1,90 +0,0 @@ -## 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* {.deprecated: "use `uint`".} = 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/cyclebreaker.nim b/lib/system/cyclebreaker.nim index 45b0a5a65..a3159f95c 100644 --- a/lib/system/cyclebreaker.nim +++ b/lib/system/cyclebreaker.nim @@ -53,6 +53,7 @@ depth-first traversal suffices. ]# +type PT = ptr pointer include cellseqs_v2 const @@ -77,7 +78,7 @@ proc nimMarkCyclic(p: pointer) {.compilerRtl, inl.} = discard type GcEnv = object - traceStack: CellSeq[ptr pointer] + traceStack: CellSeq proc trace(p: pointer; desc: PNimTypeV2; j: var GcEnv) {.inline.} = when false: @@ -138,8 +139,7 @@ proc breakCycles(s: Cell; desc: PNimTypeV2) = else: # anyhow as a link that the produced destructor does not have to follow: u[] = nil - when traceCollector: - cprintf("[Bug] %p %s RC %ld\n", t, desc.name, t.rc shr rcShift) + cprintf("[Bug] %p %s RC %ld\n", t, desc.name, t.rc shr rcShift) deinit j.traceStack proc thinout*[T](x: ref T) {.inline.} = @@ -149,7 +149,7 @@ proc thinout*[T](x: ref T) {.inline.} = ## and thus would keep the graph from being freed are `nil`'ed. ## This is a form of cycle collection that works well with Nim's ARC ## and its associated cost model. - proc getDynamicTypeInfo[T](x: T): PNimTypeV2 {.magic: "GetTypeInfoV2", noSideEffect.} + proc getDynamicTypeInfo[T](x: T): PNimTypeV2 {.magic: "GetTypeInfoV2", noSideEffect, locks: 0.} breakCycles(head(cast[pointer](x)), getDynamicTypeInfo(x[])) diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 72d35f518..1f30b8427 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -61,8 +61,8 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode; tab: var PtrTable) {.benign.} = var - d = cast[int](dest) - s = cast[int](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) case n.kind of nkSlot: genericDeepCopyAux(cast[pointer](d +% n.offset), @@ -85,8 +85,8 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode; proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = var - d = cast[int](dest) - s = cast[int](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) sysAssert(mt != nil, "genericDeepCopyAux 2") case mt.kind of tyString: @@ -113,11 +113,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = return sysAssert(dest != nil, "genericDeepCopyAux 3") unsureAsgnRef(x, newSeq(mt, seq.len)) - var dst = cast[int](cast[PPointer](dest)[]) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) for i in 0..seq.len-1: genericDeepCopyAux( cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), - cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), + cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), mt.base, tab) of tyObject: # we need to copy m_type field for tyObject, as it could be empty for @@ -199,8 +199,8 @@ proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerproc.} = proc genericDeepCopyOpenArray(dest, src: pointer, len: int, mt: PNimType) {.compilerproc.} = var - d = cast[int](dest) - s = cast[int](src) + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) for i in 0..len-1: genericDeepCopy(cast[pointer](d +% i *% mt.base.size), cast[pointer](s +% i *% mt.base.size), mt.base) diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index 4ff3d0ae6..e87c237ad 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -1,38 +1,52 @@ -## `$` is Nim's general way of spelling `toString`:idx:. -runnableExamples: - assert $0.1 == "0.1" - assert $(-2*3) == "-6" +import std/private/since -import std/private/[digitsutils, miscdollars] +proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.} + ## The stringify operator for an integer argument. Returns `x` + ## converted to a decimal string. ``$`` is Nim's general way of + ## spelling `toString`:idx:. -when not defined(nimPreviewSlimSystem): - import std/formatfloat - export addFloat +when defined(js): + since (1, 3): + proc `$`*(x: uint): string = + ## Caveat: currently implemented as $(cast[int](x)), tied to current + ## semantics of js' Number type. + # for c, see strmantle.`$` + $(cast[int](x)) - func `$`*(x: float | float32): string = - ## Outplace version of `addFloat`. - result.addFloat(x) + proc `$`*(x: uint64): string = + ## Compatibility note: + ## the results may change in future releases if/when js target implements + ## 64bit ints. + # pending https://github.com/nim-lang/RFCs/issues/187 + $(cast[int](x)) +else: + proc `$`*(x: uint64): string {.noSideEffect, raises: [].} = + ## The stringify operator for an unsigned integer argument. Returns `x` + ## converted to a decimal string. + if x == 0: + result = "0" + else: + result = newString(60) + var i = 0 + var n = x + while n != 0: + let nn = n div 10'u64 + result[i] = char(n - 10'u64 * nn + ord('0')) + inc i + n = nn + result.setLen i -proc `$`*(x: int): string {.raises: [].} = - ## Outplace version of `addInt`. - result.addInt(x) + let half = i div 2 + # Reverse + for t in 0 .. half-1: swap(result[t], result[i-t-1]) -proc `$`*(x: int64): string {.raises: [].} = - ## Outplace version of `addInt`. - result.addInt(x) - -proc `$`*(x: uint64): string {.raises: [].} = - ## Outplace version of `addInt`. - addInt(result, x) - -# same as old `ctfeWhitelist` behavior, whether or not this is a good idea. -template gen(T) = - # xxx simplify this by supporting this in compiler: int{lit} | uint64{lit} | int64{lit} - func `$`*(x: T{lit}): string {.compileTime.} = result.addInt(x) -gen(int) -gen(uint64) -gen(int64) +proc `$`*(x: int64): string {.magic: "Int64ToStr", noSideEffect.} + ## The stringify operator for an integer argument. Returns `x` + ## converted to a decimal string. +proc `$`*(x: float): string {.magic: "FloatToStr", noSideEffect.} + ## The stringify operator for a float argument. Returns `x` + ## converted to a decimal string. proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.} ## The stringify operator for a boolean argument. Returns `x` @@ -52,19 +66,19 @@ proc `$`*(x: cstring): string {.magic: "CStrToStr", noSideEffect.} proc `$`*(x: string): string {.magic: "StrToStr", noSideEffect.} ## The stringify operator for a string argument. Returns `x` ## as it is. This operator is useful for generic code, so - ## that `$expr` also works if `expr` is already a string. + ## that ``$expr`` also works if ``expr`` is already a string. proc `$`*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.} ## The stringify operator for an enumeration argument. This works for ## any enumeration type thanks to compiler magic. ## - ## If a `$` operator for a concrete enumeration is provided, this is + ## If a ``$`` operator for a concrete enumeration is provided, this is ## used instead. (In other words: *Overwriting* is possible.) proc `$`*(t: typedesc): string {.magic: "TypeTrait".} ## Returns the name of the given type. ## - ## For more procedures dealing with `typedesc`, see + ## For more procedures dealing with ``typedesc``, see ## `typetraits module `_. ## ## .. code-block:: Nim @@ -72,19 +86,53 @@ proc `$`*(t: typedesc): string {.magic: "TypeTrait".} ## doAssert $(typeof("Foo")) == "string" ## static: doAssert $(typeof(@['A', 'B'])) == "seq[char]" -proc `$`*[T: tuple](x: T): string = - ## Generic `$` operator for tuples that is lifted from the components +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 = + ## Generic ``$`` operator for tuples that is lifted from the components ## of `x`. Example: ## ## .. code-block:: Nim ## $(23, 45) == "(23, 45)" ## $(a: 23, b: 45) == "(a: 23, b: 45)" ## $() == "()" - tupleObjectDollar(result, x) + result = "(" + const isNamed = T is object or isNamedTuple(T) + var count = 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(")") -when not defined(nimPreviewSlimSystem): - import std/objectdollar - export objectdollar proc collectionToString[T](x: T, prefix, separator, suffix: string): string = result = prefix @@ -106,7 +154,7 @@ proc collectionToString[T](x: T, prefix, separator, suffix: string): string = result.add(suffix) proc `$`*[T](x: set[T]): string = - ## Generic `$` operator for sets that is lifted from the components + ## Generic ``$`` operator for sets that is lifted from the components ## of `x`. Example: ## ## .. code-block:: Nim @@ -114,7 +162,7 @@ proc `$`*[T](x: set[T]): string = collectionToString(x, "{", ", ", "}") proc `$`*[T](x: seq[T]): string = - ## Generic `$` operator for seqs that is lifted from the components + ## Generic ``$`` operator for seqs that is lifted from the components ## of `x`. Example: ## ## .. code-block:: Nim @@ -122,7 +170,7 @@ proc `$`*[T](x: seq[T]): string = collectionToString(x, "@[", ", ", "]") proc `$`*[T, U](x: HSlice[T, U]): string = - ## Generic `$` operator for slices that is lifted from the components + ## Generic ``$`` operator for slices that is lifted from the components ## of `x`. Example: ## ## .. code-block:: Nim @@ -134,11 +182,11 @@ proc `$`*[T, U](x: HSlice[T, U]): string = when not defined(nimNoArrayToString): proc `$`*[T, IDX](x: array[IDX, T]): string = - ## Generic `$` operator for arrays that is lifted from the components. + ## Generic ``$`` operator for arrays that is lifted from the components. collectionToString(x, "[", ", ", "]") proc `$`*[T](x: openArray[T]): string = - ## Generic `$` operator for openarrays that is lifted from the components + ## Generic ``$`` operator for openarrays that is lifted from the components ## of `x`. Example: ## ## .. code-block:: Nim diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index 2162b234f..1b0a3e64c 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -47,14 +47,14 @@ proc nimLoadLibraryError(path: string) = copyMem(msg[msgIdx].addr, badExe.cstring, badExe.len) discard MessageBoxA(nil, msg[0].addr, nil, 0) cstderr.rawWrite("\n") - rawQuit(1) + quit(1) proc procAddrError(name: cstring) {.compilerproc, nonReloadable, hcrInline.} = # carefully written to avoid memory allocation: cstderr.rawWrite("could not import: ") cstderr.rawWrite(name) cstderr.rawWrite("\n") - rawQuit(1) + quit(1) # this code was inspired from Lua's source code: # Lua - An Extensible Extension Language @@ -161,38 +161,41 @@ elif defined(windows) or defined(dos): dec(m) k = k div 10 if k == 0: break - result = getProcAddress(cast[THINSTANCE](lib), cast[cstring](addr decorated)) + when defined(nimNoArrayToCstringConversion): + result = getProcAddress(cast[THINSTANCE](lib), addr decorated) + else: + result = getProcAddress(cast[THINSTANCE](lib), decorated) if result != nil: return procAddrError(name) elif defined(genode): - proc nimUnloadLibrary(lib: LibHandle) = - raiseAssert("nimUnloadLibrary not implemented") + proc nimUnloadLibrary(lib: LibHandle) {. + error: "nimUnloadLibrary not implemented".} - proc nimLoadLibrary(path: string): LibHandle = - raiseAssert("nimLoadLibrary not implemented") + proc nimLoadLibrary(path: string): LibHandle {. + error: "nimLoadLibrary not implemented".} - proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = - raiseAssert("nimGetProcAddr not implemented") + proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {. + error: "nimGetProcAddr not implemented".} -elif defined(nintendoswitch) or defined(freertos) or defined(zephyr) or defined(nuttx): +elif defined(nintendoswitch) or defined(freertos): proc nimUnloadLibrary(lib: LibHandle) = cstderr.rawWrite("nimUnLoadLibrary not implemented") cstderr.rawWrite("\n") - rawQuit(1) + quit(1) proc nimLoadLibrary(path: string): LibHandle = cstderr.rawWrite("nimLoadLibrary not implemented") cstderr.rawWrite("\n") - rawQuit(1) + quit(1) proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = cstderr.rawWrite("nimGetProAddr not implemented") cstderr.rawWrite(name) cstderr.rawWrite("\n") - rawQuit(1) + quit(1) else: {.error: "no implementation for dyncalls".} diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim index e0b053c7b..258558c3f 100644 --- a/lib/system/embedded.nim +++ b/lib/system/embedded.nim @@ -34,10 +34,6 @@ proc quitOrDebug() {.noreturn, importc: "abort", header: "", nodecl.} proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} = sysFatal(ReraiseDefect, "exception handling is not available") -proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring, - line: int) {.compilerRtl.} = - sysFatal(ReraiseDefect, "exception handling is not available") - proc reraiseException() {.compilerRtl.} = sysFatal(ReraiseDefect, "no exception to reraise") diff --git a/lib/system/exceptions.nim b/lib/system/exceptions.nim index 63588f858..fc8bd89f7 100644 --- a/lib/system/exceptions.nim +++ b/lib/system/exceptions.nim @@ -1,13 +1,61 @@ -## Exception and effect types used in Nim code. +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. 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. -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 objects holding the cstrings in "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. IOError* = object of CatchableError ## \ ## Raised if an IO error occurred. EOFError* = object of IOError ## \ @@ -62,13 +110,13 @@ type ## Raised if an object gets assigned to its parent's object. ObjectConversionDefect* = object of Defect ## \ ## Raised if an object is converted to an incompatible object type. - ## You can use `of` operator to check if conversion will succeed. + ## You can use ``of`` operator to check if conversion will succeed. FloatingPointDefect* = object of Defect ## \ ## Base class for floating point exceptions. FloatInvalidOpDefect* = object of FloatingPointDefect ## \ ## Raised by invalid operations according to IEEE. ## - ## Raised by `0.0/0.0`, for example. + ## Raised by ``0.0/0.0``, for example. FloatDivByZeroDefect* = object of FloatingPointDefect ## \ ## Raised by division by zero. ## @@ -86,37 +134,35 @@ type ## Raised for inexact results. ## ## The operation produced a result that cannot be represented with infinite - ## precision -- for example: `2.0 / 3.0, log(1.1)` + ## precision -- for example: ``2.0 / 3.0, log(1.1)`` ## ## **Note**: Nim currently does not detect these! DeadThreadDefect* = object of Defect ## \ ## Raised if it is attempted to send a message to a dead thread. NilAccessDefect* = object of Defect ## \ - ## Raised on dereferences of `nil` pointers. + ## Raised on dereferences of ``nil`` pointers. ## ## This is only raised if the `segfaults module `_ was imported! -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 + 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/excpt.nim b/lib/system/excpt.nim index 86cfff9cd..aec25e3f3 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -146,7 +146,7 @@ proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} = # some platforms have native support for stack traces: const - nativeStackTraceSupported = (defined(macosx) or defined(linux)) and + nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and not NimStackTrace hasSomeStackTrace = NimStackTrace or defined(nimStackTraceOverride) or (defined(nativeStackTrace) and nativeStackTraceSupported) @@ -242,7 +242,7 @@ template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) = for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') add(s, f.procname) when NimStackTraceMsgs: - when typeof(f) is StackTraceEntry: + when type(f) is StackTraceEntry: add(s, f.frameMsg) else: var first = if f.prev == nil: 0 else: f.prev.frameMsgLen @@ -351,10 +351,10 @@ var onUnhandledException*: (proc (errorMsg: string) {. nimcall, gcsafe.}) ## Set this error \ ## handler to override the existing behaviour on an unhandled exception. ## - ## The default is to write a stacktrace to `stderr` and then call `quit(1)`. + ## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``. ## Unstable API. -proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = +proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = when hasSomeStackTrace: var buf = newStringOfCap(2000) if e.trace.len == 0: @@ -362,8 +362,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = else: var trace = $e.trace add(buf, trace) - {.gcsafe.}: - `=destroy`(trace) + `=destroy`(trace) add(buf, "Error: unhandled exception: ") add(buf, e.msg) add(buf, " [") @@ -374,13 +373,12 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = onUnhandledException(buf) else: showErrorMessage2(buf) - {.gcsafe.}: - `=destroy`(buf) + `=destroy`(buf) else: # ugly, but avoids heap allocations :-) template xadd(buf, s, slen) = if L + slen < high(buf): - copyMem(addr(buf[L]), (when s is cstring: s else: cstring(s)), slen) + copyMem(addr(buf[L]), cstring(s), slen) inc L, slen template add(buf, s) = xadd(buf, s, s.len) @@ -389,23 +387,29 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = if e.trace.len != 0: var trace = $e.trace add(buf, trace) - {.gcsafe.}: - `=destroy`(trace) + `=destroy`(trace) add(buf, "Error: unhandled exception: ") add(buf, e.msg) add(buf, " [") xadd(buf, e.name, e.name.len) add(buf, "]\n") - if onUnhandledException != nil: - onUnhandledException($cast[cstring](buf.addr)) + when defined(nimNoArrayToCstringConversion): + template tbuf(): untyped = addr buf else: - showErrorMessage(cast[cstring](buf.addr), L) + template tbuf(): untyped = buf -proc reportUnhandledError(e: ref Exception) {.nodestroy, gcsafe.} = + if onUnhandledException != nil: + onUnhandledException($tbuf()) + else: + showErrorMessage(tbuf(), L) + +proc reportUnhandledError(e: ref Exception) {.nodestroy.} = if unhandledExceptionHook != nil: unhandledExceptionHook(e) when hostOS != "any": reportUnhandledErrorAux(e) + else: + discard () proc nimLeaveFinally() {.compilerRtl.} = when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions: @@ -415,7 +419,7 @@ proc nimLeaveFinally() {.compilerRtl.} = c_longjmp(excHandler.context, 1) else: reportUnhandledError(currException) - rawQuit(1) + quit(1) when gotoBasedExceptions: var nimInErrorMode {.threadvar.}: bool @@ -424,19 +428,19 @@ when gotoBasedExceptions: result = addr(nimInErrorMode) proc nimTestErrorFlag() {.compilerRtl.} = - ## This proc must be called before `currException` is destroyed. + ## This proc must be called before ``currException`` is destroyed. ## It also must be called at the end of every thread to ensure no ## error is swallowed. if nimInErrorMode and currException != nil: reportUnhandledError(currException) currException = nil - rawQuit(1) + quit(1) proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = when defined(nimPanics): if e of Defect: reportUnhandledError(e) - rawQuit(1) + quit(1) if localRaiseHook != nil: if not localRaiseHook(e): return @@ -447,9 +451,11 @@ proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = {.emit: "throw;".} else: pushCurrentException(e) - {.emit: "throw `e`;".} + {.emit: "throw e;".} elif defined(nimQuirky) or gotoBasedExceptions: - pushCurrentException(e) + # XXX This check should likely also be done in the setjmp case below. + if e != currException: + pushCurrentException(e) when gotoBasedExceptions: inc nimInErrorMode else: @@ -458,7 +464,7 @@ proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = c_longjmp(excHandler.context, 1) else: reportUnhandledError(e) - rawQuit(1) + quit(1) proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring, line: int) {.compilerRtl, nodestroy.} = @@ -501,7 +507,7 @@ proc threadTrouble() = if currException != nil: reportUnhandledError(currException) except: discard - rawQuit 1 + quit 1 proc writeStackTrace() = when hasSomeStackTrace: @@ -524,10 +530,13 @@ proc getStackTrace(e: ref Exception): string = else: result = "" -proc getStackTraceEntries*(e: ref Exception): lent seq[StackTraceEntry] = - ## Returns the attached stack trace to the exception `e` as - ## a `seq`. This is not yet available for the JS backend. - e.trace +proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] = + ## Returns the attached stack trace to the exception ``e`` as + ## a ``seq``. This is not yet available for the JS backend. + when not defined(nimSeqsV2): + shallowCopy(result, e.trace) + else: + result = move(e.trace) proc getStackTraceEntries*(): seq[StackTraceEntry] = ## Returns the stack trace entries for the current stack trace. @@ -544,7 +553,7 @@ proc callDepthLimitReached() {.noinline.} = "-d:nimCallDepthLimit= but really try to avoid deep " & "recursions instead.\n" showErrorMessage2(msg) - rawQuit(1) + quit(1) proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} = if framePtr == nil: @@ -565,7 +574,7 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and type StdException {.importcpp: "std::exception", header: "".} = object - proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())", nodecl.} + proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())".} proc setTerminate(handler: proc() {.noconv.}) {.importc: "std::set_terminate", header: "".} @@ -597,12 +606,9 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and else: writeToStdErr msg & "\n" - rawQuit 1 + quit 1 when not defined(noSignalHandler) and not defined(useNimRtl): - type Sighandler = proc (a: cint) {.noconv, benign.} - # xxx factor with ansi_c.CSighandlerT, posix.Sighandler - proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv.} = template processSignal(s, action: untyped) {.dirty.} = if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") @@ -641,23 +647,9 @@ when not defined(noSignalHandler) and not defined(useNimRtl): # unless there's a good reason to use cstring in signal handler to avoid # using gc? showErrorMessage(msg, msg.len) - - when defined(posix): - # reset the signal handler to OS default - c_signal(sign, SIG_DFL) - - # re-raise the signal, which will arrive once this handler exit. - # this lets the OS perform actions like core dumping and will - # also return the correct exit code to the shell. - discard c_raise(sign) - else: - rawQuit(1) - - var SIG_IGN {.importc: "SIG_IGN", header: "".}: Sighandler + quit(1) # always quit when SIGABRT proc registerSignalHandler() = - # xxx `signal` is deprecated and has many caveats, we should use `sigaction` instead, e.g. - # https://stackoverflow.com/questions/231912/what-is-the-difference-between-sigaction-and-signal c_signal(SIGINT, signalHandler) c_signal(SIGSEGV, signalHandler) c_signal(SIGABRT, signalHandler) @@ -666,17 +658,14 @@ when not defined(noSignalHandler) and not defined(useNimRtl): when declared(SIGBUS): c_signal(SIGBUS, signalHandler) when declared(SIGPIPE): - when defined(nimLegacySigpipeHandler): - c_signal(SIGPIPE, signalHandler) - else: - c_signal(SIGPIPE, SIG_IGN) + c_signal(SIGPIPE, signalHandler) registerSignalHandler() # call it in initialization section proc setControlCHook(hook: proc () {.noconv.}) = # ugly cast, but should work on all architectures: - when declared(Sighandler): - c_signal(SIGINT, cast[Sighandler](hook)) + type SignalHandler = proc (sign: cint) {.noconv, benign.} + c_signal(SIGINT, cast[SignalHandler](hook)) when not defined(noSignalHandler) and not defined(useNimRtl): proc unsetControlCHook() = diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim index 6073ee779..64ec9cda3 100644 --- a/lib/system/fatal.nim +++ b/lib/system/fatal.nim @@ -9,48 +9,50 @@ {.push profiler: off.} -const gotoBasedExceptions = compileOption("exceptions", "goto") +when defined(nimHasExceptionsQuery): + const gotoBasedExceptions = compileOption("exceptions", "goto") +else: + const gotoBasedExceptions = false when hostOS == "standalone": include "$projectpath/panicoverride" - func sysFatal(exceptn: typedesc, message: string) {.inline.} = + proc sysFatal(exceptn: typedesc, message: string) {.inline.} = panic(message) - func sysFatal(exceptn: typedesc, message, arg: string) {.inline.} = + proc sysFatal(exceptn: typedesc, message, arg: string) {.inline.} = rawoutput(message) panic(arg) elif (defined(nimQuirky) or defined(nimPanics)) and not defined(nimscript): import ansi_c - func name(t: typedesc): string {.magic: "TypeTrait".} + proc name(t: typedesc): string {.magic: "TypeTrait".} - func sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} = + proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} = when nimvm: # TODO when doAssertRaises works in CT, add a test for it raise (ref exceptn)(msg: message & arg) else: - {.noSideEffect.}: - writeStackTrace() - var buf = newStringOfCap(200) - add(buf, "Error: unhandled exception: ") - add(buf, message) - add(buf, arg) - add(buf, " [") - add(buf, name exceptn) - add(buf, "]\n") - cstderr.rawWrite buf - rawQuit 1 + writeStackTrace() + var buf = newStringOfCap(200) + add(buf, "Error: unhandled exception: ") + add(buf, message) + add(buf, arg) + add(buf, " [") + add(buf, name exceptn) + add(buf, "]\n") + cstderr.rawWrite buf + quit 1 - func sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} = + proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} = sysFatal(exceptn, message, "") else: - func sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} = + proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} = raise (ref exceptn)(msg: message) - func sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} = + proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} = raise (ref exceptn)(msg: message & arg) {.pop.} diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim index 70dd857d5..cb46c8c36 100644 --- a/lib/system/formatfloat.nim +++ b/lib/system/formatfloat.nim @@ -1,6 +1,59 @@ -when not defined(nimPreviewSlimSystem): - import std/formatfloat - export formatfloat - {.deprecated: "use `std/formatfloat`".} -else: - {.error: "use `std/formatfloat`".} +# +# +# Nim's Runtime Library +# (c) Copyright 2019 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +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 writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = + ## This is the implementation to format floats in the Nim + ## programming language. The specific format for floating point + ## numbers is not specified in the Nim programming language and + ## might change slightly in the future, but at least wherever you + ## format a float, it should be consistent. + ## + ## returns the amount of bytes written to `buf` not counting the + ## terminating '\0' character. + ## + ## * `buf` - A buffer to write into. The buffer does not need to be + ## initialized and it will be overridden. + ## + 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 diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 7db2b6567..1f7164266 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -12,53 +12,6 @@ # Refcounting + Mark&Sweep. Complex algorithms avoided. # Been there, done that, didn't work. -#[ - -A *cell* is anything that is traced by the GC -(sequences, refs, strings, closures). - -The basic algorithm is *Deferrent Reference Counting* with cycle detection. -References on the stack are not counted for better performance and easier C -code generation. - -Each cell has a header consisting of a RC and a pointer to its type -descriptor. However the program does not know about these, so they are placed at -negative offsets. In the GC code the type `PCell` denotes a pointer -decremented by the right offset, so that the header can be accessed easily. It -is extremely important that `pointer` is not confused with a `PCell`. - -In Nim the compiler cannot always know if a reference -is stored on the stack or not. This is caused by var parameters. -Consider this example: - -.. code-block:: Nim - proc setRef(r: var ref TNode) = - new(r) - - proc usage = - var - r: ref TNode - setRef(r) # here we should not update the reference counts, because - # r is on the stack - setRef(r.left) # here we should update the refcounts! - -We have to decide at runtime whether the reference is on the stack or not. -The generated code looks roughly like this: - -.. code-block:: C - void setref(TNode** ref) { - unsureAsgnRef(ref, newObj(TNode_TI, sizeof(TNode))) - } - void usage(void) { - setRef(&r) - setRef(&r->left) - } - -Note that for systems with a continuous stack (which most systems have) -the check whether the ref is on the stack is very cheap (only two -comparisons). -]# - {.push profiler:off.} const @@ -94,7 +47,7 @@ type waZctDecRef, waPush #, waDebug - Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].} + Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} # A ref type can have a finalizer that is called before the object's # storage is freed. @@ -161,7 +114,7 @@ template gcAssert(cond: bool, msg: string) = writeStackTrace() #var x: ptr int #echo x[] - rawQuit 1 + quit 1 proc addZCT(s: var CellSeq, c: PCell) {.noinline.} = if (c.refcount and ZctFlag) == 0: @@ -170,11 +123,11 @@ proc addZCT(s: var CellSeq, c: PCell) {.noinline.} = proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata - result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell))) + result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell))) proc usrToCell(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) - result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell))) + result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell))) proc extGetCellType(c: pointer): PNimType {.compilerproc.} = # used for code generation concerning debugging @@ -219,11 +172,11 @@ template gcTrace(cell, state: untyped) = when traceGC: traceCell(cell, state) # forward declarations: -proc collectCT(gch: var GcHeap) {.benign, raises: [].} -proc isOnStack(p: pointer): bool {.noinline, benign, raises: [].} -proc forAllChildren(cell: PCell, op: WalkOp) {.benign, raises: [].} -proc doOperation(p: pointer, op: WalkOp) {.benign, raises: [].} -proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign, raises: [].} +proc collectCT(gch: var GcHeap) {.benign.} +proc isOnStack(p: pointer): bool {.noinline, benign.} +proc forAllChildren(cell: PCell, op: WalkOp) {.benign.} +proc doOperation(p: pointer, op: WalkOp) {.benign.} +proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} # we need the prototype here for debugging purposes proc incRef(c: PCell) {.inline.} = @@ -336,7 +289,7 @@ proc cellsetReset(s: var CellSet) = {.push stacktrace:off.} proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = - var d = cast[int](dest) + var d = cast[ByteAddress](dest) case n.kind of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkList: @@ -356,7 +309,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = of nkNone: sysAssert(false, "forAllSlotsAux") proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = - var d = cast[int](dest) + var d = cast[ByteAddress](dest) if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind @@ -382,7 +335,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) = of tyRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: - var d = cast[int](cellToUsr(cell)) + var d = cast[ByteAddress](cellToUsr(cell)) var s = cast[PGenericSeq](d) if s != nil: for i in 0..s.len-1: @@ -457,7 +410,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) #gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small" - gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2") + gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ setFrameInfo(res) @@ -482,7 +435,7 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = result = rawNewObj(typ, size, gch) when defined(memProfiler): nimProfile(size) -proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} = +proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = result = rawNewObj(typ, size, gch) zeroMem(result, size) when defined(memProfiler): nimProfile(size) @@ -497,7 +450,7 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = when defined(memProfiler): nimProfile(size) {.pop.} -proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} = +proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = # generates a new object and sets its reference counter to 1 incTypeSize typ, size sysAssert(allocInv(gch.region), "newObjRC1 begin") @@ -507,7 +460,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} = var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc") - sysAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2") + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ setFrameInfo(res) @@ -549,9 +502,9 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len * elemSize copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[int](res) +% oldsize +% sizeof(Cell)), + zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)), newsize-oldsize) - sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3") + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") # This can be wrong for intermediate temps that are nevertheless on the # heap because of lambda lifting: #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4") @@ -561,8 +514,35 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = gcTrace(res, csAllocated) track("growObj old", ol, 0) track("growObj new", res, newsize) - # since we steal the old seq's contents, we set the old length to 0. - cast[PGenericSeq](old).len = 0 + 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)) when useCellIds: inc gch.idGenerator res.id = gch.idGenerator * 1000_000 + gch.gcThreadId @@ -609,7 +589,7 @@ proc markS(gch: var GcHeap, c: PCell) = if not containsOrIncl(gch.marked, d): forAllChildren(d, waMarkPrecise) -proc markGlobals(gch: var GcHeap) {.raises: [].} = +proc markGlobals(gch: var GcHeap) = if gch.gcThreadId == 0: for i in 0 .. globalMarkersLen-1: globalMarkers[i]() for i in 0 .. threadLocalMarkersLen-1: threadLocalMarkers[i]() @@ -626,7 +606,7 @@ when logGC: if cycleCheckA[i] == c: return true if cycleCheckALen == len(cycleCheckA): gcAssert(false, "cycle detection overflow") - rawQuit 1 + quit 1 cycleCheckA[cycleCheckALen] = c inc cycleCheckALen @@ -664,9 +644,9 @@ proc doOperation(p: pointer, op: WalkOp) = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = doOperation(d, WalkOp(op)) -proc collectZCT(gch: var GcHeap): bool {.benign, raises: [].} +proc collectZCT(gch: var GcHeap): bool {.benign.} -proc collectCycles(gch: var GcHeap) {.raises: [].} = +proc collectCycles(gch: var GcHeap) = when hasThreadSupport: for c in gch.toDispose: nimGCunref(c) @@ -683,16 +663,16 @@ proc collectCycles(gch: var GcHeap) {.raises: [].} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: sysAssert(allocInv(gch.region), "gcMark begin") - var c = cast[int](p) + var cell = usrToCell(p) + var c = cast[ByteAddress](cell) if c >% PageSize: # fast check: does it look like a cell? - var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p)) + var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) if objStart != nil: # mark the cell: incRef(objStart) add(gch.decStack, objStart) when false: - let cell = usrToCell(p) if isAllocatedPtr(gch.region, cell): sysAssert false, "allocated pointer but not interior?" # mark the cell: @@ -773,7 +753,7 @@ proc unmarkStackAndRegisters(gch: var GcHeap) = decRef(d[i]) gch.decStack.len = 0 -proc collectCTBody(gch: var GcHeap) {.raises: [].} = +proc collectCTBody(gch: var GcHeap) = when withRealTime: let t0 = getticks() sysAssert(allocInv(gch.region), "collectCT: begin") @@ -848,10 +828,10 @@ when withRealTime: stack.bottomSaved = stack.bottom when stackIncreases: stack.bottom = cast[pointer]( - cast[int](stack.pos) - sizeof(pointer) * 6 - stackSize) + cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize) else: stack.bottom = cast[pointer]( - cast[int](stack.pos) + sizeof(pointer) * 6 + stackSize) + cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize) GC_step(gch, us, strongAdvice) diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index ed046b5fd..09388b6e8 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -7,9 +7,6 @@ # distribution, for details about the copyright. # -# xxx deadcode, consider removing unless something could be reused. - - # Garbage Collector # # The basic algorithm is an incremental mark @@ -129,15 +126,15 @@ template gcAssert(cond: bool, msg: string) = echo "[GCASSERT] ", msg GC_disable() writeStackTrace() - rawQuit 1 + quit 1 proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata - result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell))) + result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell))) proc usrToCell(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) - result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell))) + result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell))) proc extGetCellType(c: pointer): PNimType {.compilerproc.} = # used for code generation concerning debugging @@ -252,7 +249,7 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc.} = dest[] = src proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = - var d = cast[int](dest) + var d = cast[ByteAddress](dest) case n.kind of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkList: @@ -264,7 +261,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = of nkNone: sysAssert(false, "forAllSlotsAux") proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = - var d = cast[int](dest) + var d = cast[ByteAddress](dest) if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind @@ -290,7 +287,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) = of tyRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: - var d = cast[int](cellToUsr(cell)) + var d = cast[ByteAddress](cellToUsr(cell)) var s = cast[PGenericSeq](d) if s != nil: for i in 0..s.len-1: @@ -330,7 +327,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) - gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2") + gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ when leakDetector and not hasThreadSupport: @@ -388,9 +385,9 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[int](res)+% oldsize +% sizeof(Cell)), + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), newsize-oldsize) - sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3") + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") when false: # this is wrong since seqs can be shared via 'shallow': when reallyDealloc: rawDealloc(gch.region, ol) @@ -593,7 +590,7 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: sysAssert(allocInv(gch.region), "gcMark begin") var cell = usrToCell(p) - var c = cast[int](cell) + var c = cast[ByteAddress](cell) if c >% PageSize: # fast check: does it look like a cell? var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) @@ -697,10 +694,10 @@ when withRealTime: stack.bottomSaved = stack.bottom when stackIncreases: stack.bottom = cast[pointer]( - cast[int](stack.pos) - sizeof(pointer) * 6 - stackSize) + cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize) else: stack.bottom = cast[pointer]( - cast[int](stack.pos) + sizeof(pointer) * 6 + stackSize) + cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize) GC_step(gch, us, strongAdvice) diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 54c51e783..658c5d025 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -166,16 +166,16 @@ when defined(nimdoc): ## this thread will only be initialized once per thread, no matter how often ## it is called. ## - ## This function is available only when `--threads:on` and `--tlsEmulation:off` + ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off`` ## switches are used discard proc tearDownForeignThreadGc*() {.gcsafe.} = - ## Call this to tear down the GC, previously initialized by `setupForeignThreadGc`. + ## Call this to tear down the GC, previously initialized by ``setupForeignThreadGc``. ## If GC has not been previously initialized, or has already been torn down, the ## call does nothing. ## - ## This function is available only when `--threads:on` and `--tlsEmulation:off` + ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off`` ## switches are used discard elif declared(threadType): @@ -222,9 +222,9 @@ proc stackSize(stack: ptr GcStack): int {.noinline.} = if pos != nil: when stackIncreases: - result = cast[int](pos) -% cast[int](stack.bottom) + result = cast[ByteAddress](pos) -% cast[ByteAddress](stack.bottom) else: - result = cast[int](stack.bottom) -% cast[int](pos) + result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](pos) else: result = 0 @@ -295,8 +295,8 @@ when not defined(useNimRtl): # the first init must be the one that defines the stack bottom: gch.stack.bottom = theStackBottom elif theStackBottom != gch.stack.bottom: - var a = cast[int](theStackBottom) # and not PageMask - PageSize*2 - var b = cast[int](gch.stack.bottom) + var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2 + var b = cast[ByteAddress](gch.stack.bottom) #c_fprintf(stdout, "old: %p new: %p;\n",gch.stack.bottom,theStackBottom) when stackIncreases: gch.stack.bottom = cast[pointer](min(a, b)) @@ -312,11 +312,11 @@ when not defined(useNimRtl): proc isOnStack(p: pointer): bool = var stackTop {.volatile, noinit.}: pointer stackTop = addr(stackTop) - var a = cast[int](gch.getActiveStack().bottom) - var b = cast[int](stackTop) + var a = cast[ByteAddress](gch.getActiveStack().bottom) + var b = cast[ByteAddress](stackTop) when not stackIncreases: swap(a, b) - var x = cast[int](p) + var x = cast[ByteAddress](p) result = a <=% x and x <=% b when defined(sparc): # For SPARC architecture. @@ -337,7 +337,7 @@ when defined(sparc): # For SPARC architecture. # Addresses decrease as the stack grows. while sp <= max: gcMark(gch, sp[]) - sp = cast[PPointer](cast[int](sp) +% sizeof(pointer)) + sp = cast[PPointer](cast[ByteAddress](sp) +% sizeof(pointer)) elif defined(ELATE): {.error: "stack marking code is to be written for this architecture".} @@ -354,8 +354,8 @@ elif stackIncreases: template forEachStackSlotAux(gch, gcMark: untyped) {.dirty.} = for stack in gch.stack.items(): - var max = cast[int](gch.stack.bottom) - var sp = cast[int](addr(registers)) -% sizeof(pointer) + var max = cast[ByteAddress](gch.stack.bottom) + var sp = cast[ByteAddress](addr(registers)) -% sizeof(pointer) while sp >=% max: gcMark(gch, cast[PPointer](sp)[]) sp = sp -% sizeof(pointer) @@ -383,8 +383,8 @@ else: gch.getActiveStack().setPosition(addr(registers)) if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. for stack in gch.stack.items(): - var max = cast[int](stack.bottom) - var sp = cast[int](addr(registers)) + var max = cast[ByteAddress](stack.bottom) + var sp = cast[ByteAddress](addr(registers)) when defined(amd64): if stack.isActiveStack(): # words within the jmp_buf structure may not be properly aligned. @@ -415,7 +415,7 @@ else: # end of non-portable code # ---------------------------------------------------------------------------- -proc prepareDealloc(cell: PCell) {.raises: [].} = +proc prepareDealloc(cell: PCell) = when declared(useMarkForDebug): when useMarkForDebug: gcAssert(cell notin gch.marked, "Cell still alive!") @@ -432,10 +432,10 @@ proc prepareDealloc(cell: PCell) {.raises: [].} = decTypeSize(cell, t) proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) = - ## Frees the thread local heap. Runs every finalizer if `runFinalizers` - ## is true. If `allowGcAfterwards` is true, a minimal amount of allocation + ## Frees the thread local heap. Runs every finalizer if ``runFinalizers`` + ## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation ## happens to ensure the GC can continue to work after the call - ## to `deallocHeap`. + ## to ``deallocHeap``. template deallocCell(x) = if isCell(x): # cast to PCell is correct here: @@ -458,7 +458,7 @@ proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) = initGC() type - GlobalMarkerProc = proc () {.nimcall, benign, raises: [].} + GlobalMarkerProc = proc () {.nimcall, benign.} var globalMarkersLen {.exportc.}: int globalMarkers {.exportc.}: array[0..3499, GlobalMarkerProc] @@ -472,7 +472,7 @@ proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = inc globalMarkersLen else: cstderr.rawWrite("[GC] cannot register global variable; too many global variables") - rawQuit 1 + quit 1 proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = if threadLocalMarkersLen <= high(threadLocalMarkers): @@ -480,4 +480,4 @@ proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} inc threadLocalMarkersLen else: cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables") - rawQuit 1 + quit 1 diff --git a/lib/system/gc_hooks.nim b/lib/system/gc_hooks.nim index ace62eea0..70f02e657 100644 --- a/lib/system/gc_hooks.nim +++ b/lib/system/gc_hooks.nim @@ -24,7 +24,7 @@ proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = inc globalMarkersLen else: cstderr.rawWrite("[GC] cannot register global variable; too many global variables") - rawQuit 1 + quit 1 proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} = if threadLocalMarkersLen <= high(threadLocalMarkers): @@ -32,7 +32,7 @@ proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} inc threadLocalMarkersLen else: cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables") - rawQuit 1 + quit 1 proc traverseGlobals*() = for i in 0..globalMarkersLen-1: diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 72d22f08b..852f5d7aa 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -36,7 +36,7 @@ type # local waMarkPrecise # fast precise marking - Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].} + Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} # A ref type can have a finalizer that is called before the object's # storage is freed. @@ -90,15 +90,15 @@ template gcAssert(cond: bool, msg: string) = if not cond: cstderr.rawWrite "[GCASSERT] " cstderr.rawWrite msg - rawQuit 1 + quit 1 proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata - result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell))) + result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell))) proc usrToCell(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) - result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell))) + result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell))) proc extGetCellType(c: pointer): PNimType {.compilerproc.} = # used for code generation concerning debugging @@ -115,10 +115,10 @@ when BitsPerPage mod (sizeof(int)*8) != 0: {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} # forward declarations: -proc collectCT(gch: var GcHeap; size: int) {.benign, raises: [].} -proc forAllChildren(cell: PCell, op: WalkOp) {.benign, raises: [].} -proc doOperation(p: pointer, op: WalkOp) {.benign, raises: [].} -proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign, raises: [].} +proc collectCT(gch: var GcHeap; size: int) {.benign.} +proc forAllChildren(cell: PCell, op: WalkOp) {.benign.} +proc doOperation(p: pointer, op: WalkOp) {.benign.} +proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} # we need the prototype here for debugging purposes when defined(nimGcRefLeak): @@ -217,7 +217,7 @@ proc initGC() = gcAssert(gch.gcThreadId >= 0, "invalid computed thread ID") proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = - var d = cast[int](dest) + var d = cast[ByteAddress](dest) case n.kind of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkList: @@ -229,7 +229,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = of nkNone: sysAssert(false, "forAllSlotsAux") proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = - var d = cast[int](dest) + var d = cast[ByteAddress](dest) if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind @@ -255,7 +255,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) = forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: when not defined(nimSeqsV2): - var d = cast[int](cellToUsr(cell)) + var d = cast[ByteAddress](cellToUsr(cell)) var s = cast[PGenericSeq](d) if s != nil: for i in 0..s.len-1: @@ -268,7 +268,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") collectCT(gch, size + sizeof(Cell)) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) - gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2") + gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ when leakDetector and not hasThreadSupport: @@ -336,9 +336,9 @@ when not defined(nimSeqsV2): var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[int](res)+% oldsize +% sizeof(Cell)), + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), newsize-oldsize) - sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3") + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") when withBitvectors: incl(gch.allocated, res) when useCellIds: inc gch.idGenerator @@ -428,6 +428,15 @@ proc sweep(gch: var GcHeap) = if c.refcount == rcBlack: c.refcount = rcWhite else: freeCyclicCell(gch, c) +when false: + proc newGcInvariant*() = + for x in allObjects(gch.region): + if isCell(x): + var c = cast[PCell](x) + if c.typ == nil: + writeStackTrace() + quit 1 + proc markGlobals(gch: var GcHeap) = if gch.gcThreadId == 0: when defined(nimTracing): @@ -446,10 +455,11 @@ proc markGlobals(gch: var GcHeap) = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: - var c = cast[int](p) + var cell = usrToCell(p) + var c = cast[ByteAddress](cell) if c >% PageSize: # fast check: does it look like a cell? - var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p)) + var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) if objStart != nil: mark(gch, objStart) diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim index d96de7eac..6ced04c99 100644 --- a/lib/system/gc_regions.nim +++ b/lib/system/gc_regions.nim @@ -7,7 +7,6 @@ # # "Stack GC" for embedded devices or ultra performance requirements. -import std/private/syslocks when defined(memProfiler): proc nimProfile(requestedSize: int) {.benign.} diff --git a/lib/system/hti.nim b/lib/system/hti.nim index 9acaae88b..3dbcd7615 100644 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -40,7 +40,7 @@ type tyPointer, tyOpenArray, tyString, - tyCstring, + tyCString, tyForward, tyInt, tyInt8, diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index 3bf0b9893..d80980c72 100644 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -30,13 +30,12 @@ when defined(createNimRtl): {.pragma: inl.} {.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.} elif defined(useNimRtl): - #[ - `{.rtl.}` should only be used for non-generic procs. - ]# - const nimrtl* = - when defined(windows): "nimrtl.dll" - elif defined(macosx): "libnimrtl.dylib" - else: "libnimrtl.so" + when defined(windows): + const nimrtl* = "nimrtl.dll" + elif defined(macosx): + const nimrtl* = "libnimrtl.dylib" + else: + const nimrtl* = "libnimrtl.so" {.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl, gcsafe.} {.pragma: inl.} {.pragma: compilerRtl, compilerproc, importc: "nimrtl_$1", dynlib: nimrtl.} @@ -45,6 +44,10 @@ else: {.pragma: inl, inline.} {.pragma: compilerRtl, compilerproc.} -{.pragma: benign, gcsafe.} +when defined(nimlocks): + {.pragma: benign, gcsafe, locks: 0.} +else: + {.pragma: benign, gcsafe.} -{.push sinkInference: on.} +when defined(nimHasSinkInference): + {.push sinkInference: on.} diff --git a/lib/system/indexerrors.nim b/lib/system/indexerrors.nim index 6a8cb8a0a..1b91789bd 100644 --- a/lib/system/indexerrors.nim +++ b/lib/system/indexerrors.nim @@ -1,5 +1,4 @@ # imported by other modules, unlike helpers.nim which is included -# xxx this is now included instead of imported, we should import instead template formatErrorIndexBound*[T](i, a, b: T): string = when defined(standalone): @@ -10,6 +9,3 @@ template formatErrorIndexBound*[T](i, a, b: T): string = template formatErrorIndexBound*[T](i, n: T): string = formatErrorIndexBound(i, 0, n) - -template formatFieldDefect*(f, discVal): string = - f & discVal & "'" diff --git a/lib/system/indices.nim b/lib/system/indices.nim deleted file mode 100644 index fd6770e23..000000000 --- a/lib/system/indices.nim +++ /dev/null @@ -1,158 +0,0 @@ -when not defined(nimHasSystemRaisesDefect): - {.pragma: systemRaisesDefect.} - -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, systemRaisesDefect.} = - system.`[]`(s, s.len - int(i)) - -proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline, systemRaisesDefect.} = - a[Idx(a.len - int(i) + int low(a))] -proc `[]`*(s: string; i: BackwardsIndex): char {.inline, systemRaisesDefect.} = s[s.len - int(i)] - -proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline, systemRaisesDefect.} = - system.`[]`(s, s.len - int(i)) -proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline, systemRaisesDefect.} = - a[Idx(a.len - int(i) + int low(a))] -proc `[]`*(s: var string; i: BackwardsIndex): var char {.inline, systemRaisesDefect.} = s[s.len - int(i)] - -proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline, systemRaisesDefect.} = - system.`[]=`(s, s.len - int(i), x) -proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline, systemRaisesDefect.} = - a[Idx(a.len - int(i) + int low(a))] = x -proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline, systemRaisesDefect.} = - 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: typed): 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, systemRaisesDefect.} = - ## 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) {.systemRaisesDefect.} = - ## 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..", tags: [ReadIOEffect].} proc c_fgetc(stream: File): cint {. - importc: "fgetc", header: "", tags: [].} + importc: "fgetc", header: "", tags: [ReadIOEffect].} proc c_ungetc(c: cint, f: File): cint {. importc: "ungetc", header: "", tags: [].} proc c_putc(c: cint, stream: File): cint {. @@ -105,7 +93,7 @@ proc c_feof(f: File): cint {. importc: "feof", header: "".} when not declared(c_fwrite): - proc c_fwrite(buf: pointer, size, n: csize_t, f: File): csize_t {. + proc c_fwrite(buf: pointer, size, n: csize_t, f: File): cint {. importc: "fwrite", header: "".} # C routine that is used here: @@ -120,22 +108,8 @@ when defined(windows): else: proc c_fseek(f: File, offset: int64, whence: cint): cint {. importc: "_fseeki64", header: "", tags: [].} - when defined(tcc): - proc c_fsetpos(f: File, pos: var int64): int32 {. - importc: "fsetpos", header: "", tags: [].} - proc c_fgetpos(f: File, pos: var int64): int32 {. - importc: "fgetpos", header: "", tags: [].} - proc c_telli64(f: cint): int64 {. - importc: "_telli64", header: "", tags: [].} - proc c_ftell(f: File): int64 = - # Taken from https://pt.osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.4-trunk/mingwrt/mingwex/stdio/ftelli64.c - result = -1'i64 - var pos: int64 - if c_fgetpos(f, pos) == 0 and c_fsetpos(f, pos) == 0: - result = c_telli64(c_fileno(f)) - else: - proc c_ftell(f: File): int64 {. - importc: "_ftelli64", header: "", tags: [].} + proc c_ftell(f: File): int64 {. + importc: "_ftelli64", header: "", tags: [].} else: proc c_fseek(f: File, offset: int64, whence: cint): cint {. importc: "fseeko", header: "", tags: [].} @@ -151,6 +125,13 @@ 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) @@ -162,13 +143,13 @@ proc raiseEOF() {.noinline, noreturn.} = proc strerror(errnum: cint): cstring {.importc, header: "".} -when not defined(nimscript): +when not defined(NimScript): var errno {.importc, header: "".}: cint ## error variable EINTR {.importc: "EINTR", header: "".}: cint proc checkErr(f: File) = - when not defined(nimscript): + when not defined(NimScript): if c_ferror(f) != 0: let msg = "errno: " & $errno & " `" & $strerror(errno) & "`" c_clearerr(f) @@ -177,35 +158,30 @@ proc checkErr(f: File) = # shouldn't happen quit(1) -{.push stackTrace: off, profiler: off.} +{.push stackTrace:off, profiler:off.} proc readBuffer*(f: File, buffer: pointer, len: Natural): int {. tags: [ReadIOEffect], benign.} = - ## Reads `len` bytes into the buffer pointed to by `buffer`. Returns + ## reads `len` bytes into the buffer pointed to by `buffer`. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. result = cast[int](c_fread(buffer, 1, cast[csize_t](len), f)) if result != len: checkErr(f) -proc readBytes*(f: File, a: var openArray[int8|uint8], start, - len: Natural): int {. +proc readBytes*(f: File, a: var openArray[int8|uint8], start, len: Natural): int {. tags: [ReadIOEffect], benign.} = - ## Reads `len` bytes into the buffer `a` starting at `a[start]`. Returns + ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. result = readBuffer(f, addr(a[start]), len) -proc readChars*(f: File, a: var openArray[char]): int {.tags: [ReadIOEffect], benign.} = - ## Reads up to `a.len` bytes into the buffer `a`. Returns - ## the actual number of bytes that have been read which may be less than - ## `a.len` (if not as many bytes are remaining), but not greater. - result = readBuffer(f, addr(a[0]), a.len) - proc readChars*(f: File, a: var openArray[char], start, len: Natural): int {. - tags: [ReadIOEffect], benign, deprecated: - "use other `readChars` overload, possibly via: readChars(toOpenArray(buf, start, len-1))".} = - ## Reads `len` bytes into the buffer `a` starting at `a[start]`. Returns + tags: [ReadIOEffect], benign.} = + ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. + ## + ## **Warning:** The buffer `a` must be pre-allocated. This can be done + ## using, for example, ``newString``. if (start + len) > len(a): raiseEIO("buffer overflow: (start+len) > length of openarray buffer") result = readBuffer(f, addr(a[start]), len) @@ -217,7 +193,7 @@ proc write*(f: File, c: cstring) {.tags: [WriteIOEffect], benign.} = proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {. tags: [WriteIOEffect], benign.} = - ## Writes the bytes of buffer pointed to by the parameter `buffer` to the + ## writes the bytes of buffer pointed to by the parameter `buffer` to the ## file `f`. Returns the number of actual written bytes, which may be less ## than `len` in case of an error. result = cast[int](c_fwrite(buffer, 1, cast[csize_t](len), f)) @@ -225,7 +201,7 @@ proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {. proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {. tags: [WriteIOEffect], benign.} = - ## Writes the bytes of `a[start..start+len-1]` to the file `f`. Returns + ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns ## the number of actual written bytes, which may be less than `len` in case ## of an error. var x = cast[ptr UncheckedArray[int8]](a) @@ -233,7 +209,7 @@ proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {. proc writeChars*(f: File, a: openArray[char], start, len: Natural): int {. tags: [WriteIOEffect], benign.} = - ## Writes the bytes of `a[start..start+len-1]` to the file `f`. Returns + ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns ## the number of actual written bytes, which may be less than `len` in case ## of an error. var x = cast[ptr UncheckedArray[int8]](a) @@ -247,6 +223,9 @@ when defined(windows): # 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) + if i < 0: + if doRaise: raiseEIO("cannot write string to file") + return while i < s.len: if s[i] == '\0': let w = c_fputc('\0', f) @@ -269,7 +248,7 @@ proc write*(f: File, s: string) {.tags: [WriteIOEffect], benign.} = raiseEIO("cannot write string to file") {.pop.} -when defined(nimscript): +when NoFakeVars: when defined(windows): const IOFBF = cint(0) @@ -342,7 +321,7 @@ proc flushFile*(f: File) {.tags: [WriteIOEffect].} = discard c_fflush(f) proc getFileHandle*(f: File): FileHandle = - ## Returns the file handle of the file `f`. This is only useful for + ## returns the file handle of the file ``f``. This is only useful for ## platform specific programming. ## Note that on Windows this doesn't return the Windows-specific handle, ## but the C library's notion of a handle, whatever that means. @@ -350,7 +329,7 @@ proc getFileHandle*(f: File): FileHandle = c_fileno(f) proc getOsFileHandle*(f: File): FileHandle = - ## Returns the OS file handle of the file `f`. This is only useful for + ## returns the OS file handle of the file ``f``. This is only useful for ## platform specific programming. when defined(windows): result = FileHandle getOsfhandle(cint getFileHandle(f)) @@ -360,14 +339,14 @@ proc getOsFileHandle*(f: File): FileHandle = when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(windows): proc setInheritable*(f: FileHandle, inheritable: bool): bool = ## control whether a file handle can be inherited by child processes. Returns - ## `true` on success. This requires the OS file handle, which can be + ## ``true`` on success. This requires the OS file handle, which can be ## retrieved via `getOsFileHandle <#getOsFileHandle,File>`_. ## ## This procedure is not guaranteed to be available for all platforms. Test for ## availability with `declared() `. when SupportIoctlInheritCtl: result = c_ioctl(f, if inheritable: FIONCLEX else: FIOCLEX) != -1 - elif defined(freertos) or defined(zephyr): + elif defined(freertos): result = true elif defined(posix): var flags = c_fcntl(f, F_GETFD) @@ -379,18 +358,18 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w result = setHandleInformation(cast[IoHandle](f), HANDLE_FLAG_INHERIT, inheritable.WinDWORD) != 0 -proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], +proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], benign.} = - ## Reads a line of text from the file `f` into `line`. May throw an IO + ## reads a line of text from the file `f` into `line`. May throw an IO ## exception. - ## A line of text may be delimited by `LF` or `CRLF`. The newline - ## character(s) are not part of the returned string. Returns `false` - ## if the end of the file has been reached, `true` otherwise. If - ## `false` is returned `line` contains no new data. + ## A line of text may be delimited by ``LF`` or ``CRLF``. The newline + ## character(s) are not part of the returned string. Returns ``false`` + ## if the end of the file has been reached, ``true`` otherwise. If + ## ``false`` is returned `line` contains no new data. proc c_memchr(s: pointer, c: cint, n: csize_t): pointer {. importc: "memchr", header: "".} - when defined(windows): + when defined(windows) and not defined(useWinAnsi): proc readConsole(hConsoleInput: FileHandle, lpBuffer: pointer, nNumberOfCharsToRead: int32, lpNumberOfCharsRead: ptr int32, @@ -438,38 +417,38 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], numberOfCharsRead -= 2 # handle Ctrl+Z as EOF for i in 0.. 0 and line[last-1] == '\c': - line.setLen(last-1) + var last = cast[ByteAddress](m) - cast[ByteAddress](addr line.string[0]) + if last > 0 and line.string[last-1] == '\c': + line.string.setLen(last-1) return last > 1 or fgetsSuccess - elif last > 0 and line[last-1] == '\0': - # We have to distinguish among three possible cases: + # We have to distinguish between two possible cases: # \0\l\0 => line ending in a null character. # \0\l\l => last line without newline, null was put there by fgets. - # \0\l => last line without newline, null was put there by fgets. - if last >= pos + sp - 1 or line[last+1] != '\0': # bug #21273 + elif last > 0 and line.string[last-1] == '\0': + if last < pos + sp - 1 and line.string[last+1] != '\0': dec last - line.setLen(last) + line.string.setLen(last) return last > 0 or fgetsSuccess else: # fgets will have inserted a null byte at the end of the string. @@ -499,13 +477,13 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], # No \l found: Increase buffer and read more inc pos, sp sp = 128 # read in 128 bytes at a time - line.setLen(pos+sp) + line.string.setLen(pos+sp) -proc readLine*(f: File): string {.tags: [ReadIOEffect], benign.} = - ## Reads a line of text from the file `f`. May throw an IO exception. - ## A line of text may be delimited by `LF` or `CRLF`. The newline +proc readLine*(f: File): TaintedString {.tags: [ReadIOEffect], benign.} = + ## reads a line of text from the file `f`. May throw an IO exception. + ## A line of text may be delimited by ``LF`` or ``CRLF``. The newline ## character(s) are not part of the returned string. - result = newStringOfCap(80) + result = TaintedString(newStringOfCap(80)) if not readLine(f, result): raiseEOF() proc write*(f: File, i: int) {.tags: [WriteIOEffect], benign.} = @@ -574,7 +552,7 @@ proc readAllFile(file: File, len: int64): string = result = newString(len) let bytes = readBuffer(file, addr(result[0]), len) if endOfFile(file): - if bytes.int64 < len: + if bytes < len: result.setLen(bytes) else: # We read all the bytes but did not reach the EOF @@ -585,7 +563,7 @@ proc readAllFile(file: File): string = var len = rawFileSize(file) result = readAllFile(file, len) -proc readAll*(file: File): string {.tags: [ReadIOEffect], benign.} = +proc readAll*(file: File): TaintedString {.tags: [ReadIOEffect], benign.} = ## Reads all data from the stream `file`. ## ## Raises an IO exception in case of an error. It is an error if the @@ -598,13 +576,13 @@ proc readAll*(file: File): string {.tags: [ReadIOEffect], benign.} = else: let len = rawFileSize(file) if len > 0: - result = readAllFile(file, len) + result = readAllFile(file, len).TaintedString else: - result = readAllBuffer(file) + result = readAllBuffer(file).TaintedString proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, tags: [WriteIOEffect], benign.} = - ## Writes the values `x` to `f` and then writes "\\n". + ## writes the values `x` to `f` and then writes "\\n". ## May throw an IO exception. for i in items(x): write(f, i) @@ -612,7 +590,7 @@ proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, # interface to the C procs: -when defined(windows): +when defined(windows) and not defined(useWinAnsi): when defined(cpp): proc wfopen(filename, mode: WideCString): pointer {. importcpp: "_wfopen((const wchar_t*)#, (const wchar_t*)#)", nodecl.} @@ -651,8 +629,8 @@ const "" else: "" - FormatOpen: array[FileMode, cstring] = [ - cstring("rb" & NoInheritFlag), "wb" & NoInheritFlag, "w+b" & NoInheritFlag, + FormatOpen: array[FileMode, string] = [ + "rb" & NoInheritFlag, "wb" & NoInheritFlag, "w+b" & NoInheritFlag, "r+b" & NoInheritFlag, "ab" & NoInheritFlag ] #"rt", "wt", "w+t", "r+t", "at" @@ -668,7 +646,7 @@ when defined(posix) and not defined(nimscript): Stat {.importc: "struct stat", header: "", final, pure.} = object ## struct stat filler_1: array[24, char] - st_mode: Mode ## Mode of file + st_mode: Mode ## Mode of file filler_2: array[144 - 24 - 4, char] proc modeIsDir(m: Mode): bool = @@ -681,7 +659,7 @@ when defined(posix) and not defined(nimscript): Stat {.importc: "struct stat", header: "", final, pure.} = object ## struct stat - st_mode: Mode ## Mode of file + st_mode: Mode ## Mode of file proc modeIsDir(m: Mode): bool {.importc: "S_ISDIR", header: "".} ## Test for a directory. @@ -698,8 +676,8 @@ proc open*(f: var File, filename: string, ## Default mode is readonly. Returns true if the file could be opened. ## This throws no exception if the file could not be opened. ## - ## The file handle associated with the resulting `File` is not inheritable. - var p = fopen(filename.cstring, FormatOpen[mode]) + ## The file handle associated with the resulting ``File`` is not inheritable. + var p = fopen(filename, FormatOpen[mode]) if p != nil: var f2 = cast[File](p) when defined(posix) and not defined(nimscript): @@ -718,21 +696,21 @@ proc open*(f: var File, filename: string, result = true f = cast[File](p) - if bufSize > 0 and bufSize.uint <= high(uint): + if bufSize > 0 and bufSize <= high(cint).int: discard c_setvbuf(f, nil, IOFBF, cast[csize_t](bufSize)) elif bufSize == 0: discard c_setvbuf(f, nil, IONBF, 0) proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {. tags: [], benign.} = - ## Reopens the file `f` with given `filename` and `mode`. This + ## reopens the file `f` with given `filename` and `mode`. This ## is often used to redirect the `stdin`, `stdout` or `stderr` ## file variables. ## ## Default mode is readonly. Returns true if the file could be reopened. ## ## The file handle associated with `f` won't be inheritable. - if freopen(filename.cstring, FormatOpen[mode], f) != nil: + if freopen(filename, FormatOpen[mode], f) != nil: when not defined(nimInheritHandles) and declared(setInheritable) and NoInheritFlag.len == 0: if not setInheritable(getOsFileHandle(f), false): @@ -742,14 +720,13 @@ proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {. proc open*(f: var File, filehandle: FileHandle, mode: FileMode = fmRead): bool {.tags: [], raises: [], benign.} = - ## Creates a `File` from a `filehandle` with given `mode`. + ## Creates a ``File`` from a `filehandle` with given `mode`. ## ## Default mode is readonly. Returns true if the file could be opened. ## ## The passed file handle will no longer be inheritable. when not defined(nimInheritHandles) and declared(setInheritable): - let oshandle = when defined(windows): FileHandle getOsfhandle( - filehandle) else: filehandle + let oshandle = when defined(windows): FileHandle getOsfhandle(filehandle) else: filehandle if not setInheritable(oshandle, false): return false f = c_fdopen(filehandle, FormatOpen[mode]) @@ -759,27 +736,27 @@ proc open*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): File = ## Opens a file named `filename` with given `mode`. ## - ## Default mode is readonly. Raises an `IOError` if the file + ## Default mode is readonly. Raises an ``IOError`` if the file ## could not be opened. ## - ## The file handle associated with the resulting `File` is not inheritable. + ## The file handle associated with the resulting ``File`` is not inheritable. if not open(result, filename, mode, bufSize): sysFatal(IOError, "cannot open: " & filename) proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.} = - ## Sets the position of the file pointer that is used for read/write + ## sets the position of the file pointer that is used for read/write ## operations. The file's first byte has the index zero. if c_fseek(f, pos, cint(relativeTo)) != 0: raiseEIO("cannot set file position") proc getFilePos*(f: File): int64 {.benign.} = - ## Retrieves the current position of the file pointer that is used to + ## retrieves the current position of the file pointer that is used to ## read from the file `f`. The file's first byte has the index zero. result = c_ftell(f) if result < 0: raiseEIO("cannot retrieve file position") proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.} = - ## Retrieves the file size (in bytes) of `f`. + ## retrieves the file size (in bytes) of `f`. let oldPos = getFilePos(f) discard c_fseek(f, 0, 2) # seek the end of the file result = getFilePos(f) @@ -794,6 +771,50 @@ proc setStdIoUnbuffered*() {.tags: [], benign.} = when declared(stdin): discard c_setvbuf(stdin, nil, IONBF, 0) +when declared(stdout): + when defined(windows) and compileOption("threads"): + const insideRLocksModule = false + include "system/syslocks" + + var echoLock: SysLock + initSysLock echoLock + + const stdOutLock = not defined(windows) and not defined(android) and + not defined(nintendoswitch) and not defined(freertos) and + hostOS != "any" + + const echoDoRaise = not defined(nimLegacyEchoNoRaise) and not defined(guiapp) # see PR #16366 + + template checkErrMaybe(succeeded: bool): untyped = + if not succeeded: + when echoDoRaise: + checkErr(stdout) + + 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) + defer: funlockfile(stdout) + when defined(windows) and compileOption("threads"): + acquireSys echoLock + defer: releaseSys echoLock + for s in args: + when defined(windows): + writeWindows(stdout, s, doRaise = echoDoRaise) + else: + checkErrMaybe(c_fwrite(s.cstring, cast[csize_t](s.len), 1, stdout) == s.len) + const linefeed = "\n" + checkErrMaybe(c_fwrite(linefeed.cstring, linefeed.len, 1, stdout) == linefeed.len) + checkErrMaybe(c_fflush(stdout) == 0) + when defined(windows) and not defined(nimscript) and not defined(js): # work-around C's sucking abstraction: @@ -811,35 +832,16 @@ when defined(windows) and not defined(nimscript) and not defined(js): when defined(windows) and appType == "console" and not defined(nimDontSetUtf8CodePage) and not defined(nimscript): - import std/exitprocs - proc setConsoleOutputCP(codepage: cuint): int32 {.stdcall, dynlib: "kernel32", importc: "SetConsoleOutputCP".} proc setConsoleCP(wCodePageID: cuint): int32 {.stdcall, dynlib: "kernel32", importc: "SetConsoleCP".} - proc getConsoleOutputCP(): cuint {.stdcall, dynlib: "kernel32", - importc: "GetConsoleOutputCP".} - proc getConsoleCP(): cuint {.stdcall, dynlib: "kernel32", - importc: "GetConsoleCP".} - const Utf8codepage = 65001'u32 + const Utf8codepage = 65001 + discard setConsoleOutputCP(Utf8codepage) + discard setConsoleCP(Utf8codepage) - let - consoleOutputCP = getConsoleOutputCP() - consoleCP = getConsoleCP() - - proc restoreConsoleOutputCP() = discard setConsoleOutputCP(consoleOutputCP) - proc restoreConsoleCP() = discard setConsoleCP(consoleCP) - - if consoleOutputCP != Utf8codepage: - discard setConsoleOutputCP(Utf8codepage) - addExitProc(restoreConsoleOutputCP) - - if consoleCP != Utf8codepage: - discard setConsoleCP(Utf8codepage) - addExitProc(restoreConsoleCP) - -proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.} = +proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} = ## Opens a file named `filename` for reading, calls `readAll ## <#readAll,File>`_ and closes the file afterwards. Returns the string. ## Raises an IO exception in case of an error. If you need to call @@ -880,15 +882,15 @@ proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} = else: raise newException(IOError, "cannot open: " & filename) -proc readLines*(filename: string, n: Natural): seq[string] = - ## Reads `n` lines from the file named `filename`. Raises an IO exception +proc readLines*(filename: string, n: Natural): seq[TaintedString] = + ## read `n` lines from the file named `filename`. Raises an IO exception ## in case of an error. Raises EOF if file does not contain at least `n` lines. - ## Available at compile time. A line of text may be delimited by `LF` or `CRLF`. + ## Available at compile time. A line of text may be delimited by ``LF`` or ``CRLF``. ## The newline character(s) are not part of the returned strings. var f: File = nil if open(f, filename): try: - result = newSeq[string](n) + result = newSeq[TaintedString](n) for i in 0 .. n - 1: if not readLine(f, result[i]): raiseEOF() @@ -897,46 +899,42 @@ proc readLines*(filename: string, n: Natural): seq[string] = else: sysFatal(IOError, "cannot open: " & filename) -template readLines*(filename: string): seq[ - string] {.deprecated: "use readLines with two arguments".} = +template readLines*(filename: string): seq[TaintedString] {.deprecated: "use readLines with two arguments".} = readLines(filename, 1) -iterator lines*(filename: string): string {.tags: [ReadIOEffect].} = +iterator lines*(filename: string): TaintedString {.tags: [ReadIOEffect].} = ## Iterates over any line in the file named `filename`. ## ## If the file does not exist `IOError` is raised. The trailing newline ## character(s) are removed from the iterated lines. Example: ## - runnableExamples: - import std/strutils - - proc transformLetters(filename: string) = - var buffer = "" - for line in filename.lines: - buffer.add(line.replace("a", "0") & '\n') - writeFile(filename, buffer) - var f = open(filename, bufSize = 8000) + ## .. code-block:: nim + ## import strutils + ## + ## proc transformLetters(filename: string) = + ## var buffer = "" + ## for line in filename.lines: + ## buffer.add(line.replace("a", "0") & '\x0A') + ## writeFile(filename, buffer) + var f = open(filename, bufSize=8000) try: - var res = newStringOfCap(80) + var res = TaintedString(newStringOfCap(80)) while f.readLine(res): yield res finally: close(f) -iterator lines*(f: File): string {.tags: [ReadIOEffect].} = - ## Iterates over any line in the file `f`. +iterator lines*(f: File): TaintedString {.tags: [ReadIOEffect].} = + ## Iterate over any line in the file `f`. ## ## The trailing newline character(s) are removed from the iterated lines. + ## Example: ## - runnableExamples: - proc countZeros(filename: File): tuple[lines, zeros: int] = - for line in filename.lines: - for letter in line: - if letter == '0': - result.zeros += 1 - result.lines += 1 - var res = newStringOfCap(80) + ## .. code-block:: nim + ## proc countZeros(filename: File): tuple[lines, zeros: int] = + ## for line in filename.lines: + ## for letter in line: + ## if letter == '0': + ## result.zeros += 1 + ## result.lines += 1 + var res = TaintedString(newStringOfCap(80)) while f.readLine(res): yield res - -template `&=`*(f: File, x: typed) = - ## An alias for `write`. - write(f, x) diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index 4bd12680f..c8c0edbd4 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -1,9 +1,4 @@ -## Default iterators for some Nim types. - -when defined(nimPreviewSlimSystem): - import std/assertions - -when not defined(nimNoLentIterators): +when defined(nimHasLentIterators) and not defined(nimWorkaround14447): template lent2(T): untyped = lent T else: template lent2(T): untyped = T @@ -34,8 +29,8 @@ iterator mitems*[T](a: var openArray[T]): var T {.inline.} = iterator items*[IX, T](a: array[IX, T]): T {.inline.} = ## Iterates over each item of `a`. - when a.len > 0: - var i = low(IX) + var i = low(IX) + if i <= high(IX): while true: yield a[i] if i >= high(IX): break @@ -43,8 +38,8 @@ iterator items*[IX, T](a: array[IX, T]): T {.inline.} = iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} = ## Iterates over each item of `a` so that you can modify the yielded value. - when a.len > 0: - var i = low(IX) + var i = low(IX) + if i <= high(IX): while true: yield a[i] if i >= high(IX): break @@ -61,116 +56,79 @@ iterator items*[T](a: set[T]): T {.inline.} = iterator items*(a: cstring): char {.inline.} = ## Iterates over each item of `a`. - runnableExamples: - from std/sequtils import toSeq - assert toSeq("abc\0def".cstring) == @['a', 'b', 'c'] - assert toSeq("abc".cstring) == @['a', 'b', 'c'] - #[ - assert toSeq(nil.cstring) == @[] # xxx fails with SIGSEGV - this fails with SIGSEGV; unclear whether we want to instead yield nothing - or pay a small price to check for `nil`, a benchmark is needed. Note that - other procs support `nil`. - ]# - template impl() = + when defined(js): var i = 0 - let n = len(a) - while i < n: + var L = len(a) + while i < L: yield a[i] inc(i) - when defined(js): impl() else: - when nimvm: - # xxx `cstring` should behave like c backend instead. - impl() - else: - var i = 0 - while a[i] != '\0': - yield a[i] - inc(i) + var i = 0 + while a[i] != '\0': + yield a[i] + inc(i) iterator mitems*(a: var cstring): var char {.inline.} = ## Iterates over each item of `a` so that you can modify the yielded value. - # xxx this should give CT error in js RT. - runnableExamples: - from std/sugar import collect - var a = "abc\0def" - prepareMutation(a) - var b = a.cstring - let s = collect: - for bi in mitems(b): - if bi == 'b': bi = 'B' - bi - assert s == @['a', 'B', 'c'] - assert b == "aBc" - assert a == "aBc\0def" - - template impl() = + when defined(js): var i = 0 - let n = len(a) - while i < n: + var L = len(a) + while i < L: yield a[i] inc(i) - when defined(js): impl() else: - when nimvm: impl() - else: - var i = 0 - while a[i] != '\0': - yield a[i] - inc(i) + var i = 0 + while a[i] != '\0': + yield a[i] + inc(i) -iterator items*[T: enum and Ordinal](E: typedesc[T]): T = - ## Iterates over the values of `E`. - ## See also `enumutils.items` for enums with holes. - runnableExamples: - type Goo = enum g0 = 2, g1, g2 - from std/sequtils import toSeq - assert Goo.toSeq == [g0, g1, g2] +iterator items*[T: enum](E: typedesc[T]): T = + ## Iterates over the values of the enum ``E``. for v in low(E) .. high(E): yield v -iterator items*[T: Ordinal](s: Slice[T]): T = +iterator items*[T](s: HSlice[T, T]): T = ## Iterates over the slice `s`, yielding each value between `s.a` and `s.b` ## (inclusively). for x in s.a .. s.b: yield x iterator pairs*[T](a: openArray[T]): tuple[key: int, val: T] {.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. var i = 0 while i < len(a): yield (i, a[i]) inc(i) iterator mpairs*[T](a: var openArray[T]): tuple[key: int, val: var T]{.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. - ## `a[index]` can be modified. + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. var i = 0 while i < len(a): yield (i, a[i]) inc(i) iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. - when a.len > 0: - var i = low(IX) + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + var i = low(IX) + if i <= high(IX): while true: yield (i, a[i]) if i >= high(IX): break inc(i) iterator mpairs*[IX, T](a: var array[IX, T]): tuple[key: IX, val: var T] {.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. - ## `a[index]` can be modified. - when a.len > 0: - var i = low(IX) + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. + var i = low(IX) + if i <= high(IX): while true: yield (i, a[i]) if i >= high(IX): break inc(i) iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. var i = 0 let L = len(a) while i < L: @@ -179,8 +137,8 @@ iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} = assert(len(a) == L, "the length of the seq changed while iterating over it") iterator mpairs*[T](a: var seq[T]): tuple[key: int, val: var T] {.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. - ## `a[index]` can be modified. + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. var i = 0 let L = len(a) while i < L: @@ -189,7 +147,7 @@ iterator mpairs*[T](a: var seq[T]): tuple[key: int, val: var T] {.inline.} = assert(len(a) == L, "the length of the seq changed while iterating over it") iterator pairs*(a: string): tuple[key: int, val: char] {.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. var i = 0 let L = len(a) while i < L: @@ -198,8 +156,8 @@ iterator pairs*(a: string): tuple[key: int, val: char] {.inline.} = assert(len(a) == L, "the length of the string changed while iterating over it") iterator mpairs*(a: var string): tuple[key: int, val: var char] {.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. - ## `a[index]` can be modified. + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. var i = 0 let L = len(a) while i < L: @@ -208,7 +166,7 @@ iterator mpairs*(a: var string): tuple[key: int, val: var char] {.inline.} = assert(len(a) == L, "the length of the string changed while iterating over it") iterator pairs*(a: cstring): tuple[key: int, val: char] {.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. when defined(js): var i = 0 var L = len(a) @@ -222,8 +180,8 @@ iterator pairs*(a: cstring): tuple[key: int, val: char] {.inline.} = inc(i) iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} = - ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. - ## `a[index]` can be modified. + ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. + ## ``a[index]`` can be modified. when defined(js): var i = 0 var L = len(a) @@ -277,24 +235,24 @@ iterator fields*[T: tuple|object](x: T): RootObj {. magic: "Fields", noSideEffect.} = ## Iterates over every field of `x`. ## - ## .. warning:: This really transforms the 'for' and unrolls the loop. - ## The current implementation also has a bug - ## that affects symbol binding in the loop body. + ## **Warning**: This really transforms the 'for' and unrolls the loop. + ## The current implementation also has a bug + ## that affects symbol binding in the loop body. runnableExamples: var t = (1, "foo") - for v in fields(t): v = default(typeof(v)) + for v in fields(t): v = default(type(v)) doAssert t == (0, "") iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[key: string, val: RootObj] {. magic: "Fields", noSideEffect.} = ## Iterates over every field of `x` and `y`. ## - ## .. warning:: This really transforms the 'for' and unrolls the loop. - ## The current implementation also has a bug that affects symbol binding - ## in the loop body. + ## **Warning**: This really transforms the 'for' and unrolls the loop. + ## The current implementation also has a bug that affects symbol binding + ## in the loop body. runnableExamples: var t1 = (1, "foo") - var t2 = default(typeof(t1)) + var t2 = default(type(t1)) for v1, v2 in fields(t1, t2): v2 = v1 doAssert t1 == t2 @@ -303,16 +261,16 @@ iterator fieldPairs*[T: tuple|object](x: T): tuple[key: string, val: RootObj] {. ## Iterates over every field of `x` returning their name and value. ## ## When you iterate over objects with different field types you have to use - ## the compile time `when` instead of a runtime `if` to select the code + ## the compile time ``when`` instead of a runtime ``if`` to select the code ## you want to run for each type. To perform the comparison use the `is ## operator `_. - ## Another way to do the same without `when` is to leave the task of + ## Another way to do the same without ``when`` is to leave the task of ## picking the appropriate code to a secondary proc which you overload for ## each field type and pass the `value` to. ## - ## .. warning:: This really transforms the 'for' and unrolls the loop. The - ## current implementation also has a bug that affects symbol binding in the - ## loop body. + ## **Warning**: This really transforms the 'for' and unrolls the loop. The + ## current implementation also has a bug that affects symbol binding in the + ## loop body. runnableExamples: type Custom = object @@ -331,9 +289,9 @@ iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[ magic: "FieldPairs", noSideEffect.} = ## Iterates over every field of `x` and `y`. ## - ## .. warning:: This really transforms the 'for' and unrolls the loop. - ## The current implementation also has a bug that affects symbol binding - ## in the loop body. + ## **Warning**: This really transforms the 'for' and unrolls the loop. + ## The current implementation also has a bug that affects symbol binding + ## in the loop body. runnableExamples: type Foo = object x1: int diff --git a/lib/system/iterators_1.nim b/lib/system/iterators_1.nim index be61fd62c..2bc58c2b7 100644 --- a/lib/system/iterators_1.nim +++ b/lib/system/iterators_1.nim @@ -12,7 +12,7 @@ iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} = ## **Note**: This fails to count to `low(int)` if T = int for ## efficiency reasons. runnableExamples: - import std/sugar + import sugar let x = collect(newSeq): for i in countdown(7, 3): i @@ -49,7 +49,7 @@ iterator countup*[T](a, b: T, step: Positive = 1): T {.inline.} = ## **Note**: This fails to count to `high(int)` if T = int for ## efficiency reasons. runnableExamples: - import std/sugar + import sugar let x = collect(newSeq): for i in countup(3, 7): i @@ -78,7 +78,7 @@ iterator `..`*[T](a, b: T): T {.inline.} = ## See also: ## * [..<](#..<.i,T,T) runnableExamples: - import std/sugar + import sugar let x = collect(newSeq): for i in 3 .. 7: diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 4f64403fe..64c766482 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -69,9 +69,6 @@ proc getCurrentExceptionMsg*(): string = return $msg return "" -proc setCurrentException*(exc: ref Exception) = - lastJSError = cast[PJSError](exc) - proc auxWriteStackTrace(f: PCallFrame): string = type TempFrame = tuple[procname: cstring, line: int, filename: cstring] @@ -130,8 +127,7 @@ proc unhandledException(e: ref Exception) {. when NimStackTrace: add(buf, rawWriteStackTrace()) let cbuf = cstring(buf) - when NimStackTrace: - framePtr = nil + framePtr = nil {.emit: """ if (typeof(Error) !== "undefined") { throw new Error(`cbuf`); @@ -172,8 +168,8 @@ proc raiseRangeError() {.compilerproc, noreturn.} = proc raiseIndexError(i, a, b: int) {.compilerproc, noreturn.} = raise newException(IndexDefect, formatErrorIndexBound(int(i), int(a), int(b))) -proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noreturn.} = - raise newException(FieldDefect, formatFieldDefect(f, discVal)) +proc raiseFieldError(f: string) {.compilerproc, noreturn.} = + raise newException(FieldDefect, f) proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = asm """ @@ -497,12 +493,51 @@ proc negInt(a: int): int {.compilerproc.} = proc negInt64(a: int64): int64 {.compilerproc.} = result = a*(-1) +proc nimFloatToString(a: float): cstring {.compilerproc.} = + ## 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 absInt(a: int): int {.compilerproc.} = result = if a < 0: a*(-1) else: a proc absInt64(a: int64): int64 {.compilerproc.} = result = if a < 0: a*(-1) else: a +when not defined(nimNoZeroExtendMagic): + proc ze*(a: int): int {.compilerproc.} = + result = a + + proc ze64*(a: int64): int64 {.compilerproc.} = + result = a + + proc toU8*(a: int): int8 {.asmNoStackFrame, compilerproc.} = + asm """ + return `a`; + """ + + proc toU16*(a: int): int16 {.asmNoStackFrame, compilerproc.} = + asm """ + return `a`; + """ + + proc toU32*(a: int64): int32 {.asmNoStackFrame, compilerproc.} = + asm """ + return `a`; + """ + proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b @@ -566,41 +601,18 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = else: asm "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;" nimCopyAux(result, src, ti.node) - of tyArrayConstr, tyArray: - # In order to prevent a type change (TypedArray -> Array) and to have better copying performance, - # arrays constructors are considered separately - asm """ - if(ArrayBuffer.isView(`src`)) { - if(`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { - `dest` = new `src`.constructor(`src`); - } else { - `dest`.set(`src`, 0); - } - `result` = `dest`; - } else { - if (`src` === null) { - `result` = null; - } - else { - if (`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { - `dest` = new Array(`src`.length); - } - `result` = `dest`; - for (var i = 0; i < `src`.length; ++i) { - `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base); - } - } - } - """ - of tySequence, tyOpenArray: + of tySequence, tyArrayConstr, tyOpenArray, tyArray: asm """ if (`src` === null) { `result` = null; } else { - if (`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { + if (`dest` === null || `dest` === undefined) { `dest` = new Array(`src`.length); } + else { + `dest`.length = `src`.length; + } `result` = `dest`; for (var i = 0; i < `src`.length; ++i) { `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base); @@ -633,7 +645,7 @@ proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} = asm "`result` = {m_type: `ti`};" else: asm "`result` = {};" - of tySequence, tyOpenArray, tyString: + of tySequence, tyOpenArray: asm """ `result` = []; """ @@ -688,7 +700,6 @@ proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} = {.pop.} proc tenToThePowerOf(b: int): BiggestFloat = - # xxx deadcode var b = b var a = 10.0 result = 1.0 @@ -702,75 +713,80 @@ proc tenToThePowerOf(b: int): BiggestFloat = const IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} - -proc parseFloatNative(a: openarray[char]): float = - var str = "" - for x in a: - str.add x - - let cstr = cstring str - - asm """ - `result` = Number(`cstr`); - """ - -proc nimParseBiggestFloat(s: openarray[char], number: var BiggestFloat): int {.compilerproc.} = - var sign: bool - var i = 0 +# XXX use JS's native way here +proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start = 0): int {. + compilerproc.} = + var + esign = 1.0 + sign = 1.0 + i = start + exponent: int + flags: int + number = 0.0 if s[i] == '+': inc(i) elif s[i] == '-': - sign = true + sign = -1.0 inc(i) if s[i] == 'N' or s[i] == 'n': if s[i+1] == 'A' or s[i+1] == 'a': if s[i+2] == 'N' or s[i+2] == 'n': if s[i+3] notin IdentChars: number = NaN - return i+3 + return i+3 - start return 0 if s[i] == 'I' or s[i] == 'i': if s[i+1] == 'N' or s[i+1] == 'n': if s[i+2] == 'F' or s[i+2] == 'f': if s[i+3] notin IdentChars: - number = if sign: -Inf else: Inf - return i+3 + number = Inf*sign + return i+3 - start return 0 - - var buf: string - # we could also use an `array[char, N]` buffer to avoid reallocs, or - # use a 2-pass algorithm that first computes the length. - if sign: buf.add '-' - template addInc = - buf.add s[i] + while s[i] in {'0'..'9'}: + # Read integer part + flags = flags or 1 + number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) inc(i) - template eatUnderscores = while s[i] == '_': inc(i) - while s[i] in {'0'..'9'}: # Read integer part - buf.add s[i] + # Decimal? + if s[i] == '.': + var hd = 1.0 inc(i) - eatUnderscores() - if s[i] == '.': # Decimal? - addInc() - while s[i] in {'0'..'9'}: # Read fractional part - addInc() - eatUnderscores() - # Again, read integer and fractional part - if buf.len == ord(sign): return 0 - if s[i] in {'e', 'E'}: # Exponent? - addInc() - if s[i] == '+': inc(i) - elif s[i] == '-': addInc() - if s[i] notin {'0'..'9'}: return 0 while s[i] in {'0'..'9'}: - addInc() - eatUnderscores() - number = parseFloatNative(buf) - result = i + # Read fractional part + flags = flags or 2 + number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) + hd = hd * 10.0 + inc(i) + while s[i] == '_': inc(i) + number = number / hd # this complicated way preserves precision + # Again, read integer and fractional part + if flags == 0: return 0 + # Exponent? + if s[i] in {'e', 'E'}: + inc(i) + if s[i] == '+': + inc(i) + elif s[i] == '-': + esign = -1.0 + inc(i) + if s[i] notin {'0'..'9'}: + return 0 + while s[i] in {'0'..'9'}: + exponent = exponent * 10 + ord(s[i]) - ord('0') + inc(i) + while s[i] == '_': inc(i) + # Calculate Exponent + let hd = tenToThePowerOf(exponent) + if esign > 0.0: number = number * hd + else: number = number / hd + # evaluate sign + number = number * sign + result = i - start + # Workaround for IE, IE up to version 11 lacks 'Math.trunc'. We produce # 'Math.trunc' for Nim's ``div`` and ``mod`` operators: -when defined(nimJsMathTruncPolyfill): - {.emit: """ +const jsMathTrunc = """ if (!Math.trunc) { Math.trunc = function(v) { v = +v; @@ -778,4 +794,5 @@ if (!Math.trunc) { return (v - v % 1) || (v < 0 ? -0 : v === 0 ? v : 0); }; } -""".} +""" +when not defined(nodejs): {.emit: jsMathTrunc .} diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim index a94d0995c..ffb5190ca 100644 --- a/lib/system/memalloc.nim +++ b/lib/system/memalloc.nim @@ -1,13 +1,13 @@ when notJSnotNims: proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect, - tags: [], raises: [].} + tags: [], locks: 0, raises: [].} ## Overwrites the contents of the memory at `p` with the value 0. ## ## Exactly `size` bytes will be overwritten. Like any procedure ## dealing with raw memory this is **unsafe**. proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign, - tags: [], raises: [].} + tags: [], locks: 0, raises: [].} ## Copies the contents from the memory at `source` to the memory ## at `dest`. ## Exactly `size` bytes will be copied. The memory @@ -15,7 +15,7 @@ when notJSnotNims: ## memory this is **unsafe**. proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign, - tags: [], raises: [].} + tags: [], locks: 0, raises: [].} ## Copies the contents from the memory at `source` to the memory ## at `dest`. ## @@ -25,7 +25,7 @@ when notJSnotNims: ## dealing with raw memory this is still **unsafe**, though. proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect, - tags: [], raises: [].} + tags: [], locks: 0, raises: [].} ## Compares the memory blocks `a` and `b`. `size` bytes will ## be compared. ## @@ -34,7 +34,7 @@ when notJSnotNims: ## **unsafe**. proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect, - tags: [], raises: [].} + tags: [], locks: 0, raises: [].} ## Compares the memory blocks `a` and `b`. `size` bytes will ## be compared. ## @@ -80,7 +80,7 @@ when hasAlloc and not defined(js): when defined(nimAllocStats): var stats: AllocStats - template incStat(what: untyped) = atomicInc stats.what + template incStat(what: untyped) = inc stats.what proc getAllocStats*(): AllocStats = stats else: @@ -188,8 +188,8 @@ when hasAlloc and not defined(js): cast[ptr T](realloc(p, T.sizeof * newSize)) proc dealloc*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} = - ## Frees the memory allocated with `alloc`, `alloc0`, - ## `realloc`, `create` or `createU`. + ## Frees the memory allocated with `alloc`, `alloc0` or + ## `realloc`. ## ## **This procedure is dangerous!** ## If one forgets to free the memory a leak occurs; if one tries to @@ -213,7 +213,7 @@ when hasAlloc and not defined(js): ## to it is undefined behaviour! ## ## See also: - ## * `allocShared0 <#allocShared0.t,Natural>`_. + ## `allocShared0 <#allocShared0.t,Natural>`_. incStat(allocCount) allocSharedImpl(size) diff --git a/lib/system/memory.nim b/lib/system/memory.nim index ebda60d8d..50faa3d86 100644 --- a/lib/system/memory.nim +++ b/lib/system/memory.nim @@ -2,6 +2,9 @@ const useLibC = not defined(nimNoLibc) +when not defined(nimHasHotCodeReloading): + {.pragma: nonReloadable.} + when useLibC: import ansi_c diff --git a/lib/system/memtracker.nim b/lib/system/memtracker.nim index 289f4e024..115d4a973 100644 --- a/lib/system/memtracker.nim +++ b/lib/system/memtracker.nim @@ -35,7 +35,7 @@ type count*: int disabled: bool data*: array[400, LogEntry] - TrackLogger* = proc (log: TrackLog) {.nimcall, tags: [], gcsafe.} + TrackLogger* = proc (log: TrackLog) {.nimcall, tags: [], locks: 0, gcsafe.} var gLog*: TrackLog @@ -70,9 +70,9 @@ proc addEntry(entry: LogEntry) = if interesting: gLog.disabled = true cprintf("interesting %s:%ld %s\n", entry.file, entry.line, entry.op) - let x = cast[proc() {.nimcall, tags: [], gcsafe, raises: [].}](writeStackTrace) + let x = cast[proc() {.nimcall, tags: [], gcsafe, locks: 0, raises: [].}](writeStackTrace) x() - rawQuit 1 + quit 1 #if gLog.count > high(gLog.data): # gLogger(gLog) # gLog.count = 0 @@ -85,7 +85,7 @@ proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.co size: size, file: file, line: line, thread: myThreadId()) proc memTrackerOp*(op: cstring; address: pointer; size: int) {.tags: [], - gcsafe.} = + locks: 0, gcsafe.} = addEntry LogEntry(op: op, address: address, size: size, file: "", line: 0, thread: myThreadId()) @@ -100,7 +100,6 @@ proc logPendingOps() {.noconv.} = gLogger(gLog) gLog.count = 0 -import std/exitprocs -addExitProc logPendingOps +addQuitProc logPendingOps {.pop.} diff --git a/lib/system/mm/malloc.nim b/lib/system/mm/malloc.nim index d41dce705..2ba5c0fec 100644 --- a/lib/system/mm/malloc.nim +++ b/lib/system/mm/malloc.nim @@ -2,22 +2,13 @@ {.push stackTrace: off.} proc allocImpl(size: Natural): pointer = - result = c_malloc(size.csize_t) - when defined(zephyr): - if result == nil: - raiseOutOfMem() + c_malloc(size.csize_t) proc alloc0Impl(size: Natural): pointer = - result = c_calloc(size.csize_t, 1) - when defined(zephyr): - if result == nil: - raiseOutOfMem() + c_calloc(size.csize_t, 1) proc reallocImpl(p: pointer, newSize: Natural): pointer = - result = c_realloc(p, newSize.csize_t) - when defined(zephyr): - if result == nil: - raiseOutOfMem() + c_realloc(p, newSize.csize_t) proc realloc0Impl(p: pointer, oldsize, newSize: Natural): pointer = result = realloc(p, newSize.csize_t) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 26f2f0bbf..8589b678d 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -46,7 +46,7 @@ else: proc raiseOutOfMem() {.noinline.} = if outOfMemHook != nil: outOfMemHook() cstderr.rawWrite("out of memory\n") - rawQuit(1) + quit(1) when defined(boehmgc): include system / mm / boehm @@ -57,16 +57,6 @@ elif defined(gogc): elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc): include system / mm / malloc - when defined(nogc): - proc GC_getStatistics(): string = "" - proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = - result = alloc0(size) - - proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = - result = newObj(typ, align(GenericSeqSize, typ.align) + len * typ.base.size) - cast[PGenericSeq](result).len = len - cast[PGenericSeq](result).reserved = len - elif defined(nogc): include system / mm / none @@ -78,13 +68,14 @@ else: include "system/cellsets" when not leakDetector and not useCellIds and not defined(nimV2): sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell") - when defined(gcRegions): + when compileOption("gc", "v2"): + include "system/gc2" + elif defined(gcRegions): # XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here include "system/gc_regions" elif defined(nimV2) or usesDestructors: - when not defined(useNimRtl): - var allocator {.rtlThreadVar.}: MemRegion - instantiateForRegion(allocator) + var allocator {.rtlThreadVar.}: MemRegion + instantiateForRegion(allocator) when defined(gcHooks): include "system/gc_hooks" elif defined(gcMarkAndSweep): diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 0b49ea2e7..498faca3f 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -14,11 +14,11 @@ const buildOS* {.magic: "BuildOS".}: string = "" - ## The OS this build is running on. Can be different from `system.hostOS` + ## The OS this build is running on. Can be different from ``system.hostOS`` ## for cross compilations. buildCPU* {.magic: "BuildCPU".}: string = "" - ## The CPU this build is running on. Can be different from `system.hostCPU` + ## The CPU this build is running on. Can be different from ``system.hostCPU`` ## for cross compilations. template builtin = discard @@ -57,7 +57,7 @@ proc warningImpl(arg, orig: string) = discard proc hintImpl(arg, orig: string) = discard proc paramStr*(i: int): string = - ## Retrieves the `i`'th command line parameter. + ## Retrieves the ``i``'th command line parameter. builtin proc paramCount*(): int = @@ -66,7 +66,7 @@ proc paramCount*(): int = proc switch*(key: string, val="") = ## Sets a Nim compiler command line switch, for - ## example `switch("checks", "on")`. + ## example ``switch("checks", "on")``. builtin proc warning*(name: string; val: bool) = @@ -82,9 +82,9 @@ proc hint*(name: string; val: bool) = proc patchFile*(package, filename, replacement: string) = ## Overrides the location of a given file belonging to the ## passed package. - ## If the `replacement` is not an absolute path, the path + ## If the ``replacement`` is not an absolute path, the path ## is interpreted to be local to the Nimscript file that contains - ## the call to `patchFile`, Nim's `--path` is not used at all + ## the call to ``patchFile``, Nim's ``--path`` is not used at all ## to resolve the filename! ## ## Example: @@ -112,19 +112,19 @@ proc cmpic*(a, b: string): int = cmpIgnoreCase(a, b) proc getEnv*(key: string; default = ""): string {.tags: [ReadIOEffect].} = - ## Retrieves the environment variable of name `key`. + ## Retrieves the environment variable of name ``key``. builtin proc existsEnv*(key: string): bool {.tags: [ReadIOEffect].} = - ## Checks for the existence of an environment variable named `key`. + ## Checks for the existence of an environment variable named ``key``. builtin proc putEnv*(key, val: string) {.tags: [WriteIOEffect].} = - ## Sets the value of the environment variable named `key` to `val`. + ## Sets the value of the environment variable named ``key`` to ``val``. builtin proc delEnv*(key: string) {.tags: [WriteIOEffect].} = - ## Deletes the environment variable named `key`. + ## Deletes the environment variable named ``key``. builtin proc fileExists*(filename: string): bool {.tags: [ReadIOEffect].} = @@ -136,8 +136,17 @@ proc dirExists*(dir: string): bool {. ## Checks if the directory `dir` exists. builtin -proc selfExe*(): string {.deprecated: "Deprecated since v1.7; Use getCurrentCompilerExe".} = +template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = + # xxx: warning won't be shown for nimsscript because of current logic handling + # `foreignPackageNotes` + fileExists(args) + +template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} = + dirExists(args) + +proc selfExe*(): string = ## Returns the currently running nim or nimble executable. + # TODO: consider making this as deprecated alias of `getCurrentCompilerExe` builtin proc toExe*(filename: string): string = @@ -150,29 +159,16 @@ proc toDll*(filename: string): string = proc strip(s: string): string = var i = 0 - while s[i] in {' ', '\c', '\n'}: inc i + while s[i] in {' ', '\c', '\L'}: inc i result = s.substr(i) - if result[0] == '"' and result[^1] == '"': - result = result[1..^2] template `--`*(key, val: untyped) = - ## A shortcut for `switch <#switch,string,string>`_ - ## Example: - ## - ## .. code-block:: nim - ## - ## --path:somePath # same as switch("path", "somePath") - ## --path:"someOtherPath" # same as switch("path", "someOtherPath") - switch(strip(astToStr(key)), strip(astToStr(val))) + ## A shortcut for ``switch(astToStr(key), astToStr(val))``. + switch(astToStr(key), strip astToStr(val)) template `--`*(key: untyped) = - ## A shortcut for `switch <#switch,string,string>`_ - ## Example: - ## - ## .. code-block:: nim - ## - ## --listCmd # same as switch("listCmd") - switch(strip(astToStr(key))) + ## A shortcut for ``switch(astToStr(key)``. + switch(astToStr(key), "") type ScriptMode* {.pure.} = enum ## Controls the behaviour of the script. @@ -252,11 +248,11 @@ proc cpDir*(`from`, to: string) {.raises: [OSError].} = checkOsError() proc exec*(command: string) {. - raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} = + raises: [OSError], tags: [ExecIOEffect].} = ## Executes an external process. If the external process terminates with ## a non-zero exit code, an OSError exception is raised. ## - ## **Note:** If you need a version of `exec` that returns the exit code + ## **Note:** If you need a version of ``exec`` that returns the exit code ## and text output of the command, you can use `system.gorgeEx ## `_. log "exec: " & command: @@ -265,7 +261,7 @@ proc exec*(command: string) {. checkOsError() proc exec*(command: string, input: string, cache = "") {. - raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} = + raises: [OSError], tags: [ExecIOEffect].} = ## Executes an external process. If the external process terminates with ## a non-zero exit code, an OSError exception is raised. log "exec: " & command: @@ -275,9 +271,9 @@ proc exec*(command: string, input: string, cache = "") {. echo output proc selfExec*(command: string) {. - raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} = + raises: [OSError], tags: [ExecIOEffect].} = ## Executes an external command with the current nim/nimble executable. - ## `Command` must not contain the "nim " part. + ## ``Command`` must not contain the "nim " part. let c = selfExe() & " " & command log "exec: " & c: if rawExec(c) != 0: @@ -314,9 +310,9 @@ proc projectPath*(): string = builtin proc thisDir*(): string = - ## Retrieves the directory of the current `nims` script file. Its path is - ## obtained via `currentSourcePath` (although, currently, - ## `currentSourcePath` resolves symlinks, unlike `thisDir`). + ## Retrieves the directory of the current ``nims`` script file. Its path is + ## obtained via ``currentSourcePath`` (although, currently, + ## ``currentSourcePath`` resolves symlinks, unlike ``thisDir``). builtin proc cd*(dir: string) {.raises: [OSError].} = @@ -342,17 +338,17 @@ template withDir*(dir: string; body: untyped): untyped = ## Usage example: ## ## .. code-block:: nim - ## # inside /some/path/ ## withDir "foo": - ## # move to /some/path/foo/ - ## # back in /some/path/ - let curDir = getCurrentDir() + ## # inside foo + ## #back to last dir + var curDir = getCurrentDir() try: cd(dir) body finally: cd(curDir) + proc writeTask(name, desc: string) = if desc.len > 0: var spaces = " " @@ -360,25 +356,25 @@ proc writeTask(name, desc: string) = echo name, spaces, desc proc cppDefine*(define: string) = - ## tell Nim that `define` is a C preprocessor `#define` and so always + ## tell Nim that ``define`` is a C preprocessor ``#define`` and so always ## needs to be mangled. builtin -proc stdinReadLine(): string {. +proc stdinReadLine(): TaintedString {. tags: [ReadIOEffect], raises: [IOError].} = builtin -proc stdinReadAll(): string {. +proc stdinReadAll(): TaintedString {. tags: [ReadIOEffect], raises: [IOError].} = builtin -proc readLineFromStdin*(): string {.raises: [IOError].} = +proc readLineFromStdin*(): TaintedString {.raises: [IOError].} = ## Reads a line of data from stdin - blocks until \n or EOF which happens when stdin is closed log "readLineFromStdin": result = stdinReadLine() checkError(EOFError) -proc readAllFromStdin*(): string {.raises: [IOError].} = +proc readAllFromStdin*(): TaintedString {.raises: [IOError].} = ## Reads all data from stdin - blocks until EOF which happens when stdin is closed log "readAllFromStdin": result = stdinReadAll() @@ -395,8 +391,8 @@ when not defined(nimble): ## task build, "default build is via the C backend": ## setCommand "c" ## - ## For a task named `foo`, this template generates a `proc` named - ## `fooTask`. This is useful if you need to call one task in + ## For a task named ``foo``, this template generates a ``proc`` named + ## ``fooTask``. This is useful if you need to call one task in ## another in your Nimscript. ## ## Example: diff --git a/lib/system/orc.nim b/lib/system/orc.nim index a56a0c057..803f3d410 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -8,12 +8,13 @@ # # Cycle collector based on -# https://www.cs.purdue.edu/homes/hosking/690M/Bacon01Concurrent.pdf +# https://researcher.watson.ibm.com/researcher/files/us-bacon/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 # +type PT = Cell include cellseqs_v2 const @@ -67,10 +68,10 @@ const type GcEnv = object - traceStack: CellSeq[ptr pointer] + traceStack: CellSeq when useJumpStack: - jumpStack: CellSeq[ptr pointer] # Lins' jump stack in order to speed up traversals - toFree: CellSeq[Cell] + jumpStack: CellSeq # Lins' jump stack in order to speed up traversals + toFree: CellSeq freed, touched, edges, rcSum: int keepThreshold: bool @@ -79,12 +80,10 @@ proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} = var p = s +! sizeof(RefHeader) cast[TraceProc](desc.traceImpl)(p, addr(j)) -include threadids - when logOrc: proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) = - cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld; thread: %ld\n", - msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color, getThreadId()) + cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld\n", + msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color) proc free(s: Cell; desc: PNimTypeV2) {.inline.} = when traceCollector: @@ -93,13 +92,13 @@ proc free(s: Cell; desc: PNimTypeV2) {.inline.} = when logOrc: writeCell("free", s, desc) - if desc.destructor != nil: - cast[DestructorProc](desc.destructor)(p) + if desc.disposeImpl != nil: + cast[DisposeProc](desc.disposeImpl)(p) when false: cstderr.rawWrite desc.name cstderr.rawWrite " " - if desc.destructor == nil: + if desc.disposeImpl == nil: cstderr.rawWrite "lacks dispose" if desc.traceImpl != nil: cstderr.rawWrite ", but has trace\n" @@ -110,32 +109,26 @@ proc free(s: Cell; desc: PNimTypeV2) {.inline.} = nimRawDispose(p, desc.align) +proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inline.} = + let p = cast[ptr pointer](q) + if p[] != nil: + var j = cast[ptr GcEnv](env) + j.traceStack.add(head p[], desc) + +proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl, inline.} = + let p = cast[ptr pointer](q) + if p[] != nil: + var j = cast[ptr GcEnv](env) + j.traceStack.add(head p[], cast[ptr PNimTypeV2](p[])[]) + template orcAssert(cond, msg) = when logOrc: if not cond: cfprintf(cstderr, "[Bug!] %s\n", msg) - rawQuit 1 - -when logOrc: - proc strstr(s, sub: cstring): cstring {.header: "", importc.} - -proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inl.} = - let p = cast[ptr pointer](q) - if p[] != nil: - - orcAssert strstr(desc.name, "TType") == nil, "following a TType but it's acyclic!" - - var j = cast[ptr GcEnv](env) - j.traceStack.add(p, desc) - -proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl, inl.} = - let p = cast[ptr pointer](q) - if p[] != nil: - var j = cast[ptr GcEnv](env) - j.traceStack.add(p, cast[ptr PNimTypeV2](p[])[]) + quit 1 var - roots {.threadvar.}: CellSeq[Cell] + roots {.threadvar.}: CellSeq proc unregisterCycle(s: Cell) = # swap with the last element. O(1) @@ -143,7 +136,7 @@ proc unregisterCycle(s: Cell) = when false: if idx >= roots.len or idx < 0: cprintf("[Bug!] %ld\n", idx) - rawQuit 1 + quit 1 roots.d[idx] = roots.d[roots.len-1] roots.d[idx][0].rootIdx = idx+1 dec roots.len @@ -163,8 +156,7 @@ proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) = trace(s, desc, j) when logOrc: writeCell("root still alive", s, desc) while j.traceStack.len > until: - let (entry, desc) = j.traceStack.pop() - let t = head entry[] + let (t, desc) = j.traceStack.pop() inc t.rc, rcIncrement if t.color != colBlack: t.setColor colBlack @@ -189,8 +181,7 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = orcAssert(j.traceStack.len == 0, "markGray: trace stack not empty") trace(s, desc, j) while j.traceStack.len > 0: - let (entry, desc) = j.traceStack.pop() - let t = head entry[] + let (t, desc) = j.traceStack.pop() dec t.rc, rcIncrement inc j.edges when useJumpStack: @@ -198,7 +189,7 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = t.rc = t.rc or jumpStackFlag when traceCollector: cprintf("[Now in jumpstack] %p %ld color %ld in jumpstack %ld\n", t, t.rc shr rcShift, t.color, t.rc and jumpStackFlag) - j.jumpStack.add(entry, desc) + j.jumpStack.add(t, desc) if t.color != colGray: t.setColor colGray inc j.touched @@ -228,8 +219,7 @@ proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) = # that are still alive; we also need to mark what they # refer to as alive: while j.jumpStack.len > 0: - let (entry, desc) = j.jumpStack.pop - let t = head entry[] + let (t, desc) = j.jumpStack.pop # not in jump stack anymore! t.rc = t.rc and not jumpStackFlag if t.color == colGray and (t.rc shr rcShift) >= 0: @@ -243,8 +233,7 @@ proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) = s.setColor(colWhite) trace(s, desc, j) while j.traceStack.len > 0: - let (entry, desc) = j.traceStack.pop() - let t = head entry[] + let (t, desc) = j.traceStack.pop() if t.color == colGray: if (t.rc shr rcShift) >= 0: scanBlack(t, desc, j) @@ -254,8 +243,7 @@ proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) = # that are still alive; we also need to mark what they # refer to as alive: while j.jumpStack.len > 0: - let (entry, desc) = j.jumpStack.pop - let t = head entry[] + let (t, desc) = j.jumpStack.pop # not in jump stack anymore! t.rc = t.rc and not jumpStackFlag if t.color == colGray and (t.rc shr rcShift) >= 0: @@ -291,9 +279,7 @@ proc collectColor(s: Cell; desc: PNimTypeV2; col: int; j: var GcEnv) = j.toFree.add(s, desc) trace(s, desc, j) while j.traceStack.len > 0: - let (entry, desc) = j.traceStack.pop() - let t = head entry[] - entry[] = nil # ensure that the destructor does touch moribund objects! + let (t, desc) = j.traceStack.pop() if t.color == col and t.rootIdx == 0: j.toFree.add(t, desc) t.setColor(colBlack) @@ -416,22 +402,19 @@ proc registerCycle(s: Cell; desc: PNimTypeV2) = when logOrc: writeCell("[added root]", s, desc) - orcAssert strstr(desc.name, "TType") == nil, "added a TType as a root!" - proc GC_runOrc* = ## Forces a cycle collection pass. collectCycles() - orcAssert roots.len == 0, "roots not empty!" proc GC_enableOrc*() = - ## Enables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc` - ## specific API. Check with `when defined(gcOrc)` for its existence. + ## Enables the cycle collector subsystem of ``--gc:orc``. This is a ``--gc:orc`` + ## specific API. Check with ``when defined(gcOrc)`` for its existence. when not defined(nimStressOrc): rootsThreshold = defaultThreshold proc GC_disableOrc*() = - ## Disables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc` - ## specific API. Check with `when defined(gcOrc)` for its existence. + ## Disables the cycle collector subsystem of ``--gc:orc``. This is a ``--gc:orc`` + ## specific API. Check with ``when defined(gcOrc)`` for its existence. when not defined(nimStressOrc): rootsThreshold = high(int) @@ -441,27 +424,22 @@ proc GC_partialCollect*(limit: int) = partialCollect(limit) proc GC_fullCollect* = - ## Forces a full garbage collection pass. With `--gc:orc` triggers the cycle - ## collector. This is an alias for `GC_runOrc`. + ## Forces a full garbage collection pass. With ``--gc:orc`` triggers the cycle + ## collector. This is an alias for ``GC_runOrc``. collectCycles() proc GC_enableMarkAndSweep*() = - ## For `--gc:orc` an alias for `GC_enableOrc`. + ## For ``--gc:orc`` an alias for ``GC_enableOrc``. GC_enableOrc() proc GC_disableMarkAndSweep*() = - ## For `--gc:orc` an alias for `GC_disableOrc`. + ## For ``--gc:orc`` an alias for ``GC_disableOrc``. GC_disableOrc() -const - acyclicFlag = 1 # see also cggtypes.nim, proc genTypeInfoV2Impl - when optimizedOrc: - template markedAsCyclic(s: Cell; desc: PNimTypeV2): bool = - (desc.flags and acyclicFlag) == 0 and (s.rc and maybeCycle) != 0 + template markedAsCyclic(s: Cell): bool = (s.rc and maybeCycle) != 0 else: - template markedAsCyclic(s: Cell; desc: PNimTypeV2): bool = - (desc.flags and acyclicFlag) == 0 + template markedAsCyclic(s: Cell): bool = true proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} = if isDestroyAction: @@ -470,7 +448,7 @@ proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline. else: # do not call 'rememberCycle' again unless this cell # got an 'incRef' event: - if s.rootIdx == 0 and markedAsCyclic(s, desc): + if s.rootIdx == 0 and markedAsCyclic(s): s.setColor colBlack registerCycle(s, desc) @@ -485,19 +463,6 @@ proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} = #if cell.color == colPurple: rememberCycle(result, cell, cast[ptr PNimTypeV2](p)[]) -proc nimDecRefIsLastDyn(p: pointer): bool {.compilerRtl, inl.} = - if p != nil: - var cell = head(p) - if (cell.rc and not rcMask) == 0: - result = true - #cprintf("[DESTROY] %p\n", p) - else: - dec cell.rc, rcIncrement - #if cell.color == colPurple: - if result: - if cell.rootIdx > 0: - unregisterCycle(cell) - proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimTypeV2): bool {.compilerRtl, inl.} = if p != nil: var cell = head(p) diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 925e98ed3..32d3b166d 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -28,30 +28,7 @@ const doNotUnmap = not (defined(amd64) or defined(i386)) or defined(windows) or defined(nimAllocNoUnmap) -when defined(nimAllocPagesViaMalloc): - when not defined(gcArc) and not defined(gcOrc): - {.error: "-d:nimAllocPagesViaMalloc is only supported with --gc:arc or --gc:orc".} - - proc osTryAllocPages(size: int): pointer {.inline.} = - let base = c_malloc(csize_t size + PageSize - 1 + sizeof(uint32)) - if base == nil: raiseOutOfMem() - # memory layout: padding + offset (4 bytes) + user_data - # in order to deallocate: read offset at user_data - 4 bytes, - # then deallocate user_data - offset - let offset = PageSize - (cast[int](base) and (PageSize - 1)) - cast[ptr uint32](base +! (offset - sizeof(uint32)))[] = uint32(offset) - result = base +! offset - - proc osAllocPages(size: int): pointer {.inline.} = - result = osTryAllocPages(size) - if result == nil: raiseOutOfMem() - - proc osDeallocPages(p: pointer, size: int) {.inline.} = - # read offset at p - 4 bytes, then deallocate (p - offset) pointer - let offset = cast[ptr uint32](p -! sizeof(uint32))[] - c_free(p -! offset) - -elif defined(emscripten) and not defined(StandaloneHeapSize): +when defined(emscripten) and not defined(StandaloneHeapSize): const PROT_READ = 1 # page can be read PROT_WRITE = 2 # page can be written @@ -80,12 +57,12 @@ elif defined(emscripten) and not defined(StandaloneHeapSize): let pos = cast[int](result) # Convert pointer to PageSize correct one. - var new_pos = cast[int](pos) +% (PageSize - (pos %% PageSize)) + var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize)) if (new_pos-pos) < sizeof(EmscriptenMMapBlock): new_pos = new_pos +% PageSize result = cast[pointer](new_pos) - var mmapDescrPos = cast[int](result) -% sizeof(EmscriptenMMapBlock) + var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock) var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) mmapDescr.realSize = realSize @@ -96,24 +73,135 @@ elif defined(emscripten) and not defined(StandaloneHeapSize): proc osTryAllocPages(size: int): pointer = osAllocPages(size) proc osDeallocPages(p: pointer, size: int) {.inline.} = - var mmapDescrPos = cast[int](p) -% sizeof(EmscriptenMMapBlock) + var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock) var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) munmap(mmapDescr.realPointer, mmapDescr.realSize) elif defined(genode) and not defined(StandaloneHeapSize): include genode/alloc # osAllocPages, osTryAllocPages, osDeallocPages +elif defined(nintendoswitch) and not defined(StandaloneHeapSize): + + import nintendoswitch/switch_memory + + type + PSwitchBlock = ptr NSwitchBlock + ## This will hold the heap pointer data in a separate + ## block of memory that is PageSize bytes above + ## the requested memory. It's the only good way + ## to pass around data with heap allocations + NSwitchBlock {.pure, inheritable.} = object + realSize: int + heap: pointer # pointer to main heap alloc + heapMirror: pointer # pointer to virtmem mapped heap + + proc alignSize(size: int): int {.inline.} = + ## Align a size integer to be in multiples of PageSize + ## The nintendo switch will not allocate memory that is not + ## aligned to 0x1000 bytes and will just crash. + (size + (PageSize - 1)) and not (PageSize - 1) + + proc deallocate(heapMirror: pointer, heap: pointer, size: int) = + # Unmap the allocated memory + discard svcUnmapMemory(heapMirror, heap, size.uint64) + # These should be called (theoretically), but referencing them crashes the switch. + # The above call seems to free all heap memory, so these are not needed. + # virtmemFreeMap(nswitchBlock.heapMirror, nswitchBlock.realSize.csize) + # free(nswitchBlock.heap) + + proc freeMem(p: pointer) = + # Retrieve the switch block data from the pointer we set before + # The data is located just sizeof(NSwitchBlock) bytes below + # the top of the pointer to the heap + let + nswitchDescrPos = cast[ByteAddress](p) -% sizeof(NSwitchBlock) + nswitchBlock = cast[PSwitchBlock](nswitchDescrPos) + + deallocate( + nswitchBlock.heapMirror, nswitchBlock.heap, nswitchBlock.realSize + ) + + proc storeHeapData(address, heapMirror, heap: pointer, size: int) {.inline.} = + ## Store data in the heap for deallocation purposes later + + # the position of our heap pointer data. Since we allocated PageSize extra + # bytes, we should have a buffer on top of the requested size of at least + # PageSize bytes, which is much larger than sizeof(NSwitchBlock). So we + # decrement the address by sizeof(NSwitchBlock) and use that address + # to store our pointer data + let nswitchBlockPos = cast[ByteAddress](address) -% sizeof(NSwitchBlock) + + # We need to store this in a pointer obj (PSwitchBlock) so that the data sticks + # at the address we've chosen. If NSwitchBlock is used here, the data will + # be all 0 when we try to retrieve it later. + var nswitchBlock = cast[PSwitchBlock](nswitchBlockPos) + nswitchBlock.realSize = size + nswitchBlock.heap = heap + nswitchBlock.heapMirror = heapMirror + + proc getOriginalHeapPosition(address: pointer, difference: int): pointer {.inline.} = + ## This function sets the heap back to the originally requested + ## size + let + pos = cast[int](address) + newPos = cast[ByteAddress](pos) +% difference + + return cast[pointer](newPos) + + template allocPages(size: int, outOfMemoryStmt: untyped): untyped = + # This is to ensure we get a block of memory the requested + # size, as well as space to store our structure + let realSize = alignSize(size + sizeof(NSwitchBlock)) + + let heap = memalign(PageSize, realSize) + + if heap.isNil: + outOfMemoryStmt + + let heapMirror = virtmemReserveMap(realSize.csize) + result = heapMirror + + let rc = svcMapMemory(heapMirror, heap, realSize.uint64) + # Any return code not equal 0 means an error in libnx + if rc.uint32 != 0: + deallocate(heapMirror, heap, realSize) + outOfMemoryStmt + + # set result to be the original size requirement + result = getOriginalHeapPosition(result, realSize - size) + + storeHeapData(result, heapMirror, heap, realSize) + + proc osAllocPages(size: int): pointer {.inline.} = + allocPages(size): + raiseOutOfMem() + + proc osTryAllocPages(size: int): pointer = + allocPages(size): + return nil + + proc osDeallocPages(p: pointer, size: int) = + # Note that in order for the Switch not to crash, a call to + # deallocHeap(runFinalizers = true, allowGcAfterwards = false) + # must be run before gfxExit(). The Switch requires all memory + # to be deallocated before the graphics application has exited. + # + # gfxExit() can be found in in the github + # repo https://github.com/switchbrew/libnx + when reallyOsDealloc: + freeMem(p) + elif defined(posix) and not defined(StandaloneHeapSize): const PROT_READ = 1 # page can be read PROT_WRITE = 2 # page can be written when defined(netbsd) or defined(openbsd): - # OpenBSD security for setjmp/longjmp coroutines - var MAP_STACK {.importc: "MAP_STACK", header: "".}: cint + # OpenBSD security for setjmp/longjmp coroutines + var MAP_STACK {.importc: "MAP_STACK", header: "".}: cint else: const MAP_STACK = 0 # avoid sideeffects - + when defined(macosx) or defined(freebsd): const MAP_ANONYMOUS = 0x1000 const MAP_PRIVATE = 0x02 # Changes are private @@ -189,7 +277,7 @@ elif defined(windows) and not defined(StandaloneHeapSize): when reallyOsDealloc: if virtualFree(p, 0, MEM_RELEASE) == 0: cprintf "virtualFree failing!" - rawQuit 1 + quit 1 #VirtualFree(p, size, MEM_DECOMMIT) elif hostOS == "standalone" or defined(StandaloneHeapSize): diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index 0619f3fca..59a1333f1 100644 --- a/lib/system/platforms.nim +++ b/lib/system/platforms.nim @@ -10,8 +10,6 @@ ## Platform detection for NimScript. This module is included by the system module! ## Do not import it directly! -# CPU architectures have alias names mapped in tools/niminst/makefile.nimf - type CpuPlatform* {.pure.} = enum ## the CPU this program will run on. none, ## unknown CPU @@ -37,15 +35,13 @@ type msp430, ## TI MSP430 microcontroller riscv32, ## RISC-V 32-bit processor riscv64, ## RISC-V 64-bit processor - wasm32, ## WASM, 32-bit - e2k, ## MCST Elbrus 2000 - loongarch64, ## LoongArch 64-bit processor - s390x ## IBM Z + wasm32 ## WASM, 32-bit OsPlatform* {.pure.} = enum ## the OS this program will run on. none, dos, windows, os2, linux, morphos, skyos, solaris, irix, netbsd, freebsd, openbsd, aix, palmos, qnx, amiga, - atari, netware, macos, macosx, haiku, android, js, standalone, nintendoswitch + atari, netware, macos, macosx, haiku, android, js, nimVM, + standalone, nintendoswitch const targetOS* = when defined(windows): OsPlatform.windows @@ -70,6 +66,7 @@ const elif defined(haiku): OsPlatform.haiku elif defined(android): OsPlatform.android elif defined(js): OsPlatform.js + elif defined(nimVM): OsPlatform.nimVM elif defined(standalone): OsPlatform.standalone elif defined(nintendoswitch): OsPlatform.nintendoswitch else: OsPlatform.none @@ -98,8 +95,5 @@ const elif defined(riscv32): CpuPlatform.riscv32 elif defined(riscv64): CpuPlatform.riscv64 elif defined(wasm32): CpuPlatform.wasm32 - elif defined(e2k): CpuPlatform.e2k - elif defined(loongarch64): CpuPlatform.loongarch64 - elif defined(s390x): CpuPlatform.s390x else: CpuPlatform.none ## the CPU this program will run on. diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim index e7eb6ac82..0649f1176 100644 --- a/lib/system/profiler.nim +++ b/lib/system/profiler.nim @@ -60,13 +60,13 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) = b = b.prev var - profilingRequestedHook*: proc (): bool {.nimcall, gcsafe.} + profilingRequestedHook*: proc (): bool {.nimcall, locks: 0, gcsafe.} ## set this variable to provide a procedure that implements a profiler in ## user space. See the `nimprof` module for a reference implementation. when defined(memProfiler): type - MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, gcsafe.} + MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, locks: 0, gcsafe.} var profilerHook*: MemProfilerHook diff --git a/lib/system/rawquits.nim b/lib/system/rawquits.nim deleted file mode 100644 index f0ead10c6..000000000 --- a/lib/system/rawquits.nim +++ /dev/null @@ -1,27 +0,0 @@ -import system/ctypes - -when defined(nimNoQuit): - proc rawQuit(errorcode: int = QuitSuccess) = discard "ignoring quit" - -elif defined(genode): - import genode/env - - var systemEnv {.exportc: runtimeEnvSym.}: GenodeEnvPtr - - type GenodeEnv = GenodeEnvPtr - ## Opaque type representing Genode environment. - - proc rawQuit(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn, - importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "".} - - proc rawQuit(errorcode: int = QuitSuccess) {.inline, noreturn.} = - systemEnv.rawQuit(errorcode) - - -elif defined(js) and defined(nodejs) and not defined(nimscript): - proc rawQuit(errorcode: int = QuitSuccess) {.magic: "Exit", - importc: "process.exit", noreturn.} - -else: - proc rawQuit(errorcode: cint) {. - magic: "Exit", importc: "exit", header: "", noreturn.} \ No newline at end of file diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 6b6f7e340..ae51a4aab 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -16,9 +16,14 @@ proc reprInt(x: int64): string {.compilerproc.} = return $x proc reprFloat(x: float): string {.compilerproc.} = return $x proc reprPointer(x: pointer): string {.compilerproc.} = - result = newString(60) - let n = c_sprintf(cast[cstring](addr result[0]), "%p", x) - setLen(result, n) + when defined(nimNoArrayToCstringConversion): + result = newString(60) + let n = c_sprintf(addr result[0], "%p", x) + setLen(result, n) + else: + var buf: array[0..59, char] + discard c_sprintf(buf, "%p", x) + return $buf proc reprStrAux(result: var string, s: cstring; len: int) = if cast[pointer](s) == nil: @@ -72,8 +77,6 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = result = $e & " (invalid data!)" -include system/repr_impl - type PByteArray = ptr UncheckedArray[byte] # array[0xffff, byte] @@ -155,7 +158,7 @@ when not defined(useNimRtl): var bs = typ.base.size for i in 0..typ.size div bs - 1: if i > 0: add result, ", " - reprAux(result, cast[pointer](cast[int](p) + i*bs), typ.base, cl) + reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), typ.base, cl) add result, "]" when defined(nimSeqsV2): @@ -183,7 +186,7 @@ when not defined(useNimRtl): var bs = typ.base.size for i in 0..cast[PGenericSeq](p).len-1: if i > 0: add result, ", " - reprAux(result, cast[pointer](cast[int](payloadPtr(p)) + align(payloadOffset, typ.align) + i*bs), + reprAux(result, cast[pointer](cast[ByteAddress](payloadPtr(p)) + align(payloadOffset, typ.align) + i*bs), typ.base, cl) add result, "]" @@ -194,14 +197,14 @@ when not defined(useNimRtl): of nkSlot: add result, $n.name add result, " = " - reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl) + reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl) of nkList: for i in 0..n.len-1: if i > 0: add result, ",\n" reprRecordAux(result, p, n.sons[i], cl) of nkCase: var m = selectBranch(p, n) - reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl) + reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl) if m != nil: reprRecordAux(result, p, m, cl) proc reprRecord(result: var string, p: pointer, typ: PNimType, @@ -283,7 +286,7 @@ when not defined(useNimRtl): of tyString: let sp = cast[ptr string](p) reprStrAux(result, sp[].cstring, sp[].len) - of tyCstring: + of tyCString: let cs = cast[ptr cstring](p)[] if cs.isNil: add result, "nil" else: reprStrAux(result, cs, cs.len) @@ -307,7 +310,7 @@ when not defined(useNimRtl): var bs = elemtyp.size for i in 0..length - 1: if i > 0: add result, ", " - reprAux(result, cast[pointer](cast[int](p) + i*bs), elemtyp, cl) + reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), elemtyp, cl) add result, "]" deinitReprClosure(cl) diff --git a/lib/system/repr_impl.nim b/lib/system/repr_impl.nim deleted file mode 100644 index b9ec1890f..000000000 --- a/lib/system/repr_impl.nim +++ /dev/null @@ -1,15 +0,0 @@ -#[ -other APIs common to system/repr and system/reprjs could be refactored here, eg: -* reprChar -* reprBool -* reprStr - -Another possibility in future work would be to have a single include file instead -of system/repr and system/reprjs, and use `when defined(js)` inside it. -]# - -proc reprDiscriminant*(e: int, typ: PNimType): string {.compilerRtl.} = - case typ.kind - of tyEnum: reprEnum(e, typ) - of tyBool: $(e != 0) - else: $e diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index f6c720e2c..d456f4454 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -1,37 +1,33 @@ -include system/inclrtl - -when defined(nimPreviewSlimSystem): - import std/formatfloat - proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} ## imported from typetraits -proc distinctBase(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} +proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} ## imported from typetraits proc repr*(x: NimNode): string {.magic: "Repr", noSideEffect.} -proc repr*(x: int): string = - ## Same as $x - $x +proc repr*(x: int): string {.magic: "IntToStr", noSideEffect.} + ## repr for an integer argument. Returns `x` + ## converted to a decimal string. -proc repr*(x: int64): string = - ## Same as $x - $x +proc repr*(x: int64): string {.magic: "Int64ToStr", noSideEffect.} + ## repr for an integer argument. Returns `x` + ## converted to a decimal string. proc repr*(x: uint64): string {.noSideEffect.} = - ## Same as $x - $x + ## repr for an unsigned integer argument. Returns `x` + ## converted to a decimal string. + $x #Calls `$` from system/strmantle.nim -proc repr*(x: float): string = - ## Same as $x - $x +proc repr*(x: float): string {.magic: "FloatToStr", noSideEffect.} + ## repr for a float argument. Returns `x` + ## converted to a decimal string. proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.} ## repr for a boolean argument. Returns `x` ## converted to the string "false" or "true". -proc repr*(x: char): string {.noSideEffect, raises: [].} = +proc repr*(x: char): string {.noSideEffect.} = ## repr for a character argument. Returns `x` ## converted to an escaped string. ## @@ -47,7 +43,7 @@ proc repr*(x: char): string {.noSideEffect, raises: [].} = result.add x result.add '\'' -proc repr*(x: string | cstring): string {.noSideEffect, raises: [].} = +proc repr*(x: string | cstring): string {.noSideEffect.} = ## repr for a string argument. Returns `x` ## converted to a quoted and escaped string. result.add '\"' @@ -63,18 +59,13 @@ proc repr*(x: string | cstring): string {.noSideEffect, raises: [].} = result.add x[i] result.add '\"' -proc repr*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect, raises: [].} +proc repr*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.} ## repr for an enumeration argument. This works for ## any enumeration type thanks to compiler magic. ## ## If a `repr` operator for a concrete enumeration is provided, this is ## used instead. (In other words: *Overwriting* is possible.) -proc reprDiscriminant*(e: int): string {.compilerproc.} = - # repr and reprjs can use `PNimType` to symbolize `e`; making this work here - # would require a way to pass the set of enum stringified values to cgen. - $e - proc repr*(p: pointer): string = ## repr of pointer as its hexadecimal value if p == nil: @@ -93,14 +84,14 @@ proc repr*(p: pointer): string = proc repr*(p: proc): string = ## repr of a proc as its address - repr(cast[ptr pointer](unsafeAddr p)[]) + repr(cast[pointer](p)) template repr*(x: distinct): string = repr(distinctBase(typeof(x))(x)) template repr*(t: typedesc): string = $t -proc reprObject[T: tuple|object](res: var string, x: T) {.noSideEffect, raises: [].} = +proc reprObject[T: tuple|object](res: var string, x: T) = res.add '(' var firstElement = true const isNamed = T is object or isNamedTuple(T) @@ -121,7 +112,7 @@ proc reprObject[T: tuple|object](res: var string, x: T) {.noSideEffect, raises: res.add(')') -proc repr*[T: tuple|object](x: T): string {.noSideEffect, raises: [].} = +proc repr*[T: tuple|object](x: T): string = ## Generic `repr` operator for tuples that is lifted from the components ## of `x`. Example: ## @@ -133,7 +124,7 @@ proc repr*[T: tuple|object](x: T): string {.noSideEffect, raises: [].} = result = $typeof(x) reprObject(result, x) -proc repr*[T](x: ref T | ptr T): string {.noSideEffect, raises: [].} = +proc repr*[T](x: ref T | ptr T): string = if isNil(x): return "nil" when T is object: result = $typeof(x) @@ -142,7 +133,7 @@ proc repr*[T](x: ref T | ptr T): string {.noSideEffect, raises: [].} = result = when typeof(x) is ref: "ref " else: "ptr " result.add repr(x[]) -proc collectionToRepr[T](x: T, prefix, separator, suffix: string): string {.noSideEffect, raises: [].} = +proc collectionToRepr[T](x: T, prefix, separator, suffix: string): string = result = prefix var firstElement = true for value in items(x): @@ -190,6 +181,3 @@ proc repr*[T](x: openArray[T]): string = ## .. code-block:: Nim ## $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]" collectionToRepr(x, "[", ", ", "]") - -proc repr*[T](x: UncheckedArray[T]): string = - "[...]" diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 0818f9cc9..36972024a 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -8,9 +8,6 @@ # # 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 @@ -33,8 +30,6 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = else: result = $e & " (invalid data!)" -include system/repr_impl - proc reprChar(x: char): string {.compilerRtl.} = result = "\'" case x @@ -118,6 +113,7 @@ 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 @@ -204,7 +200,7 @@ proc reprAux(result: var string, p: pointer, typ: PNimType, var fp: int {. emit: "`fp` = `p`;\n" .} add(result, reprStr(cast[string](p))) - of tyCstring: + of tyCString: var fp: cstring {. emit: "`fp` = `p`;\n" .} if fp.isNil: diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index d7ace446d..b7f24ecd5 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -73,18 +73,16 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin q.cap = newCap result = q -proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} = +proc shrink*[T](x: var seq[T]; newLen: Natural) = when nimvm: - {.cast(tags: []).}: - setLen(x, newLen) + setLen(x, newLen) else: #sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'" when not supportsCopyMem(T): for i in countdown(x.len - 1, newLen): reset x[i] # XXX This is wrong for const seqs that were moved into 'x'! - {.noSideEffect.}: - cast[ptr NimSeqV2[T]](addr x).len = newLen + cast[ptr NimSeqV2[T]](addr x).len = newLen proc grow*[T](x: var seq[T]; newLen: Natural; value: T) = let oldLen = x.len @@ -104,17 +102,16 @@ proc add*[T](x: var seq[T]; value: sink T) {.magic: "AppendSeqElem", noSideEffec ## containers should also call their adding proc `add` for consistency. ## Generic code becomes much easier to write if the Nim naming scheme is ## respected. - {.cast(noSideEffect).}: - let oldLen = x.len - var xu = cast[ptr NimSeqV2[T]](addr x) - if xu.p == nil or xu.p.cap < oldLen+1: - xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, 1, sizeof(T), alignof(T))) - xu.len = oldLen+1 - # .nodestroy means `xu.p.data[oldLen] = value` is compiled into a - # copyMem(). This is fine as know by construction that - # in `xu.p.data[oldLen]` there is nothing to destroy. - # We also save the `wasMoved + destroy` pair for the sink parameter. - xu.p.data[oldLen] = value + let oldLen = x.len + var xu = cast[ptr NimSeqV2[T]](addr x) + if xu.p == nil or xu.p.cap < oldLen+1: + xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, 1, sizeof(T), alignof(T))) + xu.len = oldLen+1 + # .nodestroy means `xu.p.data[oldLen] = value` is compiled into a + # copyMem(). This is fine as know by construction that + # in `xu.p.data[oldLen]` there is nothing to destroy. + # We also save the `wasMoved + destroy` pair for the sink parameter. + xu.p.data[oldLen] = value proc setLen[T](s: var seq[T], newlen: Natural) = {.noSideEffect.}: @@ -127,25 +124,3 @@ proc setLen[T](s: var seq[T], newlen: Natural) = if xu.p == nil or xu.p.cap < newlen: xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T))) xu.len = newlen - for i in oldLen..= nbatch: + let originNum = num + num = num div nbatch + let index = (originNum - num * nbatch) shl 1 + result[next] = digitsTable[index + 1] + result[next - 1] = digitsTable[index] + dec(next, 2) + + # process last 1-2 digits + if num < 10: + result[next] = chr(ord('0') + num) + else: + let index = num * 2 + result[next] = digitsTable[index + 1] + result[next - 1] = digitsTable[index] + proc cmpStrings(a, b: string): int {.inline, compilerproc.} = let alen = a.len let blen = b.len let minlen = min(alen, blen) if minlen > 0: - result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], cast[csize_t](minlen)).int + result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], cast[csize_t](minlen)) if result == 0: result = alen - blen else: @@ -33,7 +96,7 @@ proc eqStrings(a, b: string): bool {.inline, compilerproc.} = proc hashString(s: string): int {.compilerproc.} = # the compiler needs exactly the same hash function! # this used to be used for efficient generation of string case statements - var h = 0'u + var h : uint = 0 for i in 0..len(s)-1: h = h + uint(s[i]) h = h + h shl 10 @@ -43,28 +106,63 @@ 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 addInt*(result: var string; x: int64) = + ## Converts integer to its string representation and appends it to `result`. + ## + ## .. code-block:: Nim + ## var + ## a = "123" + ## b = 45 + ## a.addInt(b) # a <- "12345" + let base = result.len + var length: int + var num: uint64 -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) + if x < 0: + if x == low(int64): + num = uint64(x) + else: + num = uint64(-x) + length = base + digits10(num) + 1 + setLen(result, length) + result[base] = '-' + else: + num = uint64(x) + length = base + digits10(num) + setLen(result, length) + numToString(result, num, length) + +proc nimIntToStr(x: int): string {.compilerRtl.} = + result = newStringOfCap(sizeof(x)*4) + result.addInt x + +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 + copyMem(result[oldLen].addr, buf, buflen) + +import formatfloat + +proc addFloat*(result: var string; x: float) = + ## Converts float to its string representation and appends it to `result`. + ## + ## .. code-block:: Nim + ## var + ## a = "123" + ## b = 45.67 + ## a.addFloat(b) # a <- "12345.67" + when nimvm: + result.add $x + else: + var buffer {.noinit.}: array[65, char] + let n = writeFloatToBuffer(buffer, x) + result.addCstringN(cstring(buffer[0].addr), n) + +proc nimFloatToStr(f: float): string {.compilerproc.} = + result = newStringOfCap(8) + result.addFloat f proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. importc: "strtod", header: "", noSideEffect.} @@ -75,11 +173,11 @@ const 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22] +when defined(nimHasInvariant): + {.push staticBoundChecks: off.} -{.push staticBoundChecks: off.} - -proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, - ): int {.compilerproc.} = +proc nimParseBiggestFloat(s: string, number: var BiggestFloat, + start = 0): int {.compilerproc.} = # This routine attempt to parse float that can parsed quickly. # i.e. whose integer part can fit inside a 53bits integer. # their real exponent must also be <= 22. If the float doesn't follow @@ -88,7 +186,7 @@ proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, # This avoid the problems of decimal character portability. # see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ var - i = 0 + i = start sign = 1.0 kdigits, fdigits = 0 exponent = 0 @@ -111,7 +209,7 @@ proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, if s[i+2] == 'N' or s[i+2] == 'n': if i+3 >= s.len or s[i+3] notin IdentChars: number = NaN - return i+3 + return i+3 - start return 0 # Inf? @@ -120,7 +218,7 @@ proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, if s[i+2] == 'F' or s[i+2] == 'f': if i+3 >= s.len or s[i+3] notin IdentChars: number = Inf*sign - return i+3 + return i+3 - start return 0 if i < s.len and s[i] in {'0'..'9'}: @@ -154,8 +252,8 @@ proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, # if has no digits: return error if kdigits + fdigits <= 0 and - (i == 0 or # no char consumed (empty string). - (i == 1 and hasSign)): # or only '+' or '- + (i == start or # no char consumed (empty string). + (i == start + 1 and hasSign)): # or only '+' or '- return 0 if i+1 < s.len and s[i] in {'e', 'E'}: @@ -182,7 +280,7 @@ proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, number = 0.0*sign else: number = Inf*sign - return i + return i - start # if integer is representable in 53 bits: fast path # max fast path integer is 1<<53 - 1 or 8999999999999999 (16 digits) @@ -194,30 +292,29 @@ proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, number = sign * integer.float / powtens[absExponent] else: number = sign * integer.float * powtens[absExponent] - return i + return i - start # if exponent is greater try to fit extra exponent above 22 by multiplying # integer part is there is space left. let slop = 15 - kdigits - fdigits if absExponent <= 22 + slop and not expNegative: number = sign * integer.float * powtens[slop] * powtens[absExponent-slop] - return i + return i - start # if failed: slow path with strtod. var t: array[500, char] # flaviu says: 325 is the longest reasonable literal var ti = 0 let maxlen = t.high - "e+000".len # reserve enough space for exponent - let endPos = i - result = endPos - i = 0 + result = i - start + i = start # re-parse without error checking, any error should be handled by the code above. - if i < endPos and s[i] == '.': i.inc - while i < endPos and s[i] in {'0'..'9','+','-'}: + if i < s.len and s[i] == '.': i.inc + while i < s.len and s[i] in {'0'..'9','+','-'}: if ti < maxlen: t[ti] = s[i]; inc(ti) inc(i) - while i < endPos and s[i] in {'.', '_'}: # skip underscore and decimal point + while i < s.len and s[i] in {'.', '_'}: # skip underscore and decimal point inc(i) # insert exponent @@ -232,9 +329,18 @@ proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, t[ti-2] = ('0'.ord + absExponent mod 10).char absExponent = absExponent div 10 t[ti-3] = ('0'.ord + absExponent mod 10).char - number = c_strtod(cast[cstring](addr t), nil) -{.pop.} # staticBoundChecks + when defined(nimNoArrayToCstringConversion): + number = c_strtod(addr t, nil) + else: + number = c_strtod(t, nil) + +when defined(nimHasInvariant): + {.pop.} # staticBoundChecks + +proc nimInt64ToStr(x: int64): string {.compilerRtl.} = + result = newStringOfCap(sizeof(x)*4) + result.addInt x proc nimBoolToStr(x: bool): string {.compilerRtl.} = return if x: "true" else: "false" diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 429724dab..fe117997b 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -36,7 +36,7 @@ template frees(s) = proc resize(old: int): int {.inline.} = if old <= 0: result = 4 - elif old <= high(int16): result = old * 2 + elif old < 65536: result = old * 2 else: result = old * 3 div 2 # for large arrays * 3/2 is better proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = @@ -89,7 +89,7 @@ proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} = proc nimToCStringConv(s: NimStringV2): cstring {.compilerproc, nonReloadable, inline.} = if s.len == 0: result = cstring"" - else: result = cast[cstring](unsafeAddr s.p.data) + else: result = cstring(unsafeAddr s.p.data) proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} = if src.len > 0: @@ -168,26 +168,3 @@ proc nimPrepareStrMutationImpl(s: var NimStringV2) = proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl, inline.} = if s.p != nil and (s.p.cap and strlitFlag) == strlitFlag: nimPrepareStrMutationImpl(s) - -proc prepareMutation*(s: var string) {.inline.} = - # string literals are "copy on write", so you need to call - # `prepareMutation` before modifying the strings via `addr`. - {.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/syslocks.nim b/lib/system/syslocks.nim new file mode 100644 index 000000000..07fb9cb8a --- /dev/null +++ b/lib/system/syslocks.nim @@ -0,0 +1,226 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# Low level system locks and condition vars. + +{.push stackTrace: off.} + +when defined(Windows): + type + Handle = int + + SysLock {.importc: "CRITICAL_SECTION", + header: "", final, pure.} = object # CRITICAL_SECTION in WinApi + DebugInfo: pointer + LockCount: int32 + RecursionCount: int32 + OwningThread: int + LockSemaphore: int + SpinCount: int + + SysCond = Handle + + proc initSysLock(L: var SysLock) {.importc: "InitializeCriticalSection", + header: "".} + ## Initializes the lock `L`. + + proc tryAcquireSysAux(L: var SysLock): int32 {.importc: "TryEnterCriticalSection", + header: "".} + ## Tries to acquire the lock `L`. + + proc tryAcquireSys(L: var SysLock): bool {.inline.} = + result = tryAcquireSysAux(L) != 0'i32 + + proc acquireSys(L: var SysLock) {.importc: "EnterCriticalSection", + header: "".} + ## Acquires the lock `L`. + + proc releaseSys(L: var SysLock) {.importc: "LeaveCriticalSection", + header: "".} + ## Releases the lock `L`. + + proc deinitSys(L: var SysLock) {.importc: "DeleteCriticalSection", + header: "".} + + proc createEvent(lpEventAttributes: pointer, + bManualReset, bInitialState: int32, + lpName: cstring): SysCond {.stdcall, noSideEffect, + dynlib: "kernel32", importc: "CreateEventA".} + + proc closeHandle(hObject: Handle) {.stdcall, noSideEffect, + dynlib: "kernel32", importc: "CloseHandle".} + proc waitForSingleObject(hHandle: Handle, dwMilliseconds: int32): int32 {. + stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", noSideEffect.} + + proc signalSysCond(hEvent: SysCond) {.stdcall, noSideEffect, + dynlib: "kernel32", importc: "SetEvent".} + + proc initSysCond(cond: var SysCond) {.inline.} = + cond = createEvent(nil, 0'i32, 0'i32, nil) + proc deinitSysCond(cond: var SysCond) {.inline.} = + closeHandle(cond) + proc waitSysCond(cond: var SysCond, lock: var SysLock) = + releaseSys(lock) + discard waitForSingleObject(cond, -1'i32) + acquireSys(lock) + + proc waitSysCondWindows(cond: var SysCond) = + discard waitForSingleObject(cond, -1'i32) + +elif defined(genode): + const + Header = "genode_cpp/syslocks.h" + type + SysLock {.importcpp: "Nim::SysLock", pure, final, + header: Header.} = object + SysCond {.importcpp: "Nim::SysCond", pure, final, + header: Header.} = object + + proc initSysLock(L: var SysLock) = discard + proc deinitSys(L: var SysLock) = discard + proc acquireSys(L: var SysLock) {.noSideEffect, importcpp.} + proc tryAcquireSys(L: var SysLock): bool {.noSideEffect, importcpp.} + proc releaseSys(L: var SysLock) {.noSideEffect, importcpp.} + + proc initSysCond(L: var SysCond) = discard + proc deinitSysCond(L: var SysCond) = discard + proc waitSysCond(cond: var SysCond, lock: var SysLock) {. + noSideEffect, importcpp.} + proc signalSysCond(cond: var SysCond) {. + noSideEffect, importcpp.} + +else: + type + SysLockObj {.importc: "pthread_mutex_t", pure, final, + header: """#include + #include """.} = object + when defined(linux) and defined(amd64): + abi: array[40 div sizeof(clong), clong] + + SysLockAttr {.importc: "pthread_mutexattr_t", pure, final + header: """#include + #include """.} = object + when defined(linux) and defined(amd64): + abi: array[4 div sizeof(cint), cint] # actually a cint + + SysCondObj {.importc: "pthread_cond_t", pure, final, + header: """#include + #include """.} = object + when defined(linux) and defined(amd64): + abi: array[48 div sizeof(clonglong), clonglong] + + SysCondAttr {.importc: "pthread_condattr_t", pure, final + header: """#include + #include """.} = object + when defined(linux) and defined(amd64): + abi: array[4 div sizeof(cint), cint] # actually a cint + + SysLockType = distinct cint + + proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {. + importc: "pthread_mutex_init", header: "", noSideEffect.} + proc deinitSysAux(L: var SysLockObj) {.noSideEffect, + importc: "pthread_mutex_destroy", header: "".} + + proc acquireSysAux(L: var SysLockObj) {.noSideEffect, + importc: "pthread_mutex_lock", header: "".} + proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect, + importc: "pthread_mutex_trylock", header: "".} + + proc releaseSysAux(L: var SysLockObj) {.noSideEffect, + importc: "pthread_mutex_unlock", header: "".} + + when defined(ios): + # iOS will behave badly if sync primitives are moved in memory. In order + # to prevent this once and for all, we're doing an extra malloc when + # initializing the primitive. + type + SysLock = ptr SysLockObj + SysCond = ptr SysCondObj + + when not declared(c_malloc): + proc c_malloc(size: csize_t): pointer {. + importc: "malloc", header: "".} + proc c_free(p: pointer) {. + importc: "free", header: "".} + + proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) = + L = cast[SysLock](c_malloc(csize_t(sizeof(SysLockObj)))) + initSysLockAux(L[], attr) + + proc deinitSys(L: var SysLock) = + deinitSysAux(L[]) + c_free(L) + + template acquireSys(L: var SysLock) = + acquireSysAux(L[]) + template tryAcquireSys(L: var SysLock): bool = + tryAcquireSysAux(L[]) == 0'i32 + template releaseSys(L: var SysLock) = + releaseSysAux(L[]) + else: + type + SysLock = SysLockObj + SysCond = SysCondObj + + template initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) = + initSysLockAux(L, attr) + template deinitSys(L: var SysLock) = + deinitSysAux(L) + template acquireSys(L: var SysLock) = + acquireSysAux(L) + template tryAcquireSys(L: var SysLock): bool = + tryAcquireSysAux(L) == 0'i32 + template releaseSys(L: var SysLock) = + releaseSysAux(L) + + when insideRLocksModule: + let SysLockType_Reentrant{.importc: "PTHREAD_MUTEX_RECURSIVE", + header: "".}: SysLockType + proc initSysLockAttr(a: var SysLockAttr) {. + importc: "pthread_mutexattr_init", header: "", noSideEffect.} + proc setSysLockType(a: var SysLockAttr, t: SysLockType) {. + importc: "pthread_mutexattr_settype", header: "", noSideEffect.} + + else: + proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {. + importc: "pthread_cond_init", header: "", noSideEffect.} + proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect, + importc: "pthread_cond_destroy", header: "".} + + proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj) {. + importc: "pthread_cond_wait", header: "", noSideEffect.} + proc signalSysCondAux(cond: var SysCondObj) {. + importc: "pthread_cond_signal", header: "", noSideEffect.} + + when defined(ios): + proc initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = + cond = cast[SysCond](c_malloc(csize_t(sizeof(SysCondObj)))) + initSysCondAux(cond[], cond_attr) + + proc deinitSysCond(cond: var SysCond) = + deinitSysCondAux(cond[]) + c_free(cond) + + template waitSysCond(cond: var SysCond, lock: var SysLock) = + waitSysCondAux(cond[], lock[]) + template signalSysCond(cond: var SysCond) = + signalSysCondAux(cond[]) + else: + template initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = + initSysCondAux(cond, cond_attr) + template deinitSysCond(cond: var SysCond) = + deinitSysCondAux(cond) + + template waitSysCond(cond: var SysCond, lock: var SysLock) = + waitSysCondAux(cond, lock) + template signalSysCond(cond: var SysCond) = + signalSysCondAux(cond) + +{.pop.} diff --git a/lib/system/sysspawn.nim b/lib/system/sysspawn.nim new file mode 100644 index 000000000..d1f5803f4 --- /dev/null +++ b/lib/system/sysspawn.nim @@ -0,0 +1,194 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Implements Nim's 'spawn'. + +when not declared(NimString): + {.error: "You must not import this module explicitly".} + +{.push stackTrace:off.} + +# We declare our own condition variables here to get rid of the dummy lock +# on Windows: + +type + CondVar = object + c: SysCond + when defined(posix): + stupidLock: SysLock + counter: int + +proc createCondVar(): CondVar = + initSysCond(result.c) + when defined(posix): + initSysLock(result.stupidLock) + #acquireSys(result.stupidLock) + +proc destroyCondVar(c: var CondVar) {.inline.} = + deinitSysCond(c.c) + +proc await(cv: var CondVar) = + when defined(posix): + acquireSys(cv.stupidLock) + while cv.counter <= 0: + waitSysCond(cv.c, cv.stupidLock) + dec cv.counter + releaseSys(cv.stupidLock) + else: + waitSysCondWindows(cv.c) + +proc signal(cv: var CondVar) = + when defined(posix): + acquireSys(cv.stupidLock) + inc cv.counter + releaseSys(cv.stupidLock) + signalSysCond(cv.c) + +type + FastCondVar = object + event, slowPath: bool + slow: CondVar + +proc createFastCondVar(): FastCondVar = + initSysCond(result.slow.c) + when defined(posix): + initSysLock(result.slow.stupidLock) + #acquireSys(result.slow.stupidLock) + result.event = false + result.slowPath = false + +proc await(cv: var FastCondVar) = + #for i in 0 .. 50: + # if cas(addr cv.event, true, false): + # # this is a HIT: Triggers > 95% in my tests. + # return + # cpuRelax() + #cv.slowPath = true + # XXX For some reason this crashes some test programs + await(cv.slow) + cv.event = false + +proc signal(cv: var FastCondVar) = + cv.event = true + #if cas(addr cv.slowPath, true, false): + signal(cv.slow) + +type + Barrier* {.compilerproc.} = object + counter: int + cv: CondVar + +proc barrierEnter*(b: ptr Barrier) {.compilerproc.} = + atomicInc b.counter + +proc barrierLeave*(b: ptr Barrier) {.compilerproc.} = + atomicDec b.counter + if b.counter <= 0: signal(b.cv) + +proc openBarrier*(b: ptr Barrier) {.compilerproc.} = + b.counter = 0 + b.cv = createCondVar() + +proc closeBarrier*(b: ptr Barrier) {.compilerproc.} = + await(b.cv) + destroyCondVar(b.cv) + +{.pop.} + +# ---------------------------------------------------------------------------- + +type + WorkerProc = proc (thread, args: pointer) {.nimcall, gcsafe.} + Worker = object + taskArrived: CondVar + taskStarted: FastCondVar #\ + # task data: + f: WorkerProc + data: pointer + ready: bool # put it here for correct alignment! + +proc nimArgsPassingDone(p: pointer) {.compilerproc.} = + let w = cast[ptr Worker](p) + signal(w.taskStarted) + +var gSomeReady = createFastCondVar() + +proc slave(w: ptr Worker) {.thread.} = + while true: + w.ready = true # If we instead signal "workerReady" we need the scheduler + # to notice this. The scheduler could then optimize the + # layout of the worker threads (e.g. keep the list sorted) + # so that no search for a "ready" thread is necessary. + # This might be implemented later, but is more tricky than + # it looks because 'spawn' itself can run concurrently. + signal(gSomeReady) + await(w.taskArrived) + assert(not w.ready) + # shield against spurious wakeups: + if w.data != nil: + w.f(w, w.data) + w.data = nil + +const NumThreads = 4 + +var + workers: array[NumThreads, Thread[ptr Worker]] + workersData: array[NumThreads, Worker] + +proc setup() = + for i in 0 ..< NumThreads: + workersData[i].taskArrived = createCondVar() + workersData[i].taskStarted = createFastCondVar() + createThread(workers[i], slave, addr(workersData[i])) + +proc preferSpawn*(): bool = + ## Use this proc to determine quickly if a 'spawn' or a direct call is + ## preferable. If it returns 'true' a 'spawn' may make sense. In general + ## it is not necessary to call this directly; use 'spawnX' instead. + result = gSomeReady.event + +proc spawn*(call: typed) {.magic: "Spawn".} + ## always spawns a new task, so that the 'call' is never executed on + ## the calling thread. 'call' has to be proc call 'p(...)' where 'p' + ## is gcsafe and has 'void' as the return type. + +template spawnX*(call: typed) = + ## spawns a new task if a CPU core is ready, otherwise executes the + ## call in the calling thread. Usually it is advised to + ## use 'spawn' in order to not block the producer for an unknown + ## amount of time. 'call' has to be proc call 'p(...)' where 'p' + ## is gcsafe and has 'void' as the return type. + if preferSpawn(): spawn call + else: call + +proc nimSpawn(fn: WorkerProc; data: pointer) {.compilerproc.} = + # implementation of 'spawn' that is used by the code generator. + while true: + for i in 0.. high(workers): + let w = addr(workersData[i]) + if cas(addr w.ready, true, false): + w.data = data + w.f = fn + signal(w.taskArrived) + await(w.taskStarted) + return + await(gSomeReady) + +proc sync*() = + ## a simple barrier to wait for all spawn'ed tasks. If you need more elaborate + ## waiting, you have to use an explicit barrier. + while true: + var allReady = true + for i in 0 .. high(workers): + if not allReady: break + allReady = allReady and workersData[i].ready + if allReady: break + await(gSomeReady) + +setup() diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index b8dc7101d..06e605a7b 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -17,10 +17,10 @@ proc dataPointer(a: PGenericSeq, elemAlign: int): pointer = - cast[pointer](cast[int](a) +% align(GenericSeqSize, elemAlign)) + cast[pointer](cast[ByteAddress](a) +% align(GenericSeqSize, elemAlign)) proc dataPointer(a: PGenericSeq, elemAlign, elemSize, index: int): pointer = - cast[pointer](cast[int](a) +% align(GenericSeqSize, elemAlign) +% (index*%elemSize)) + cast[pointer](cast[ByteAddress](a) +% align(GenericSeqSize, elemAlign) +% (index*%elemSize)) proc resize(old: int): int {.inline.} = if old <= 0: result = 4 @@ -91,7 +91,7 @@ proc copyStr(s: NimString, start: int): NimString {.compilerproc.} = proc nimToCStringConv(s: NimString): cstring {.compilerproc, nonReloadable, inline.} = if s == nil or s.len == 0: result = cstring"" - else: result = cast[cstring](addr s.data) + else: result = cstring(addr s.data) proc toNimStr(str: cstring, len: int): NimString {.compilerproc.} = result = rawNewStringNoInit(len) @@ -160,9 +160,13 @@ proc addChar(s: NimString, c: char): NimString = result = s if result.len >= result.space: let r = resize(result.space) - result = rawNewStringNoInit(r) - result.len = s.len - copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) + 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.reserved = r result.data[result.len] = c result.data[result.len+1] = '\0' @@ -206,9 +210,12 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = result = dest else: # slow path: let sp = max(resize(dest.space), dest.len + addlen) - result = rawNewStringNoInit(sp) - result.len = dest.len - copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1) + 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.reserved = sp #result = rawNewString(sp) #copyMem(result, dest, dest.len + sizeof(TGenericSeq)) @@ -232,11 +239,14 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = result = s else: let sp = max(resize(s.space), newLen) - 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 + 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.len = n result.data[n] = '\0' @@ -272,11 +282,15 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} = result = s if result.len >= result.space: let r = resize(result.space) - 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 + 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 proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericSeq {. compilerRtl, inl.} = @@ -290,10 +304,20 @@ 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): - if ntfNoRefs notin extGetCellType(result).base.flags: + when false: # compileOption("gc", "v2"): for i in newLen..result.len-1: + let len0 = gch.tempStack.len forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), - extGetCellType(result).base, waZctDecRef) + 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) # 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). @@ -310,33 +334,36 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. if s == nil: result = cast[PGenericSeq](newSeq(typ, newLen)) else: - 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) + 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) + # 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 else: - result = s - zeroMem(dataPointer(result, elemAlign, elemSize, result.len), (newLen-%result.len) *% elemSize) - result.len = newLen + result = setLengthSeq(s, typ.base.size, newLen) diff --git a/lib/system/threadids.nim b/lib/system/threadids.nim deleted file mode 100644 index 3a6eadcbb..000000000 --- a/lib/system/threadids.nim +++ /dev/null @@ -1,103 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2020 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# we need to cache current threadId to not perform syscall all the time -var threadId {.threadvar.}: int - -when defined(windows): - proc getCurrentThreadId(): int32 {. - stdcall, dynlib: "kernel32", importc: "GetCurrentThreadId".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(getCurrentThreadId()) - result = threadId - -elif defined(linux): - proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "".} - when defined(amd64): - const NR_gettid = clong(186) - else: - var NR_gettid {.importc: "__NR_gettid", header: "".}: clong - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(syscall(NR_gettid)) - result = threadId - -elif defined(dragonfly): - proc lwp_gettid(): int32 {.importc, header: "unistd.h".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(lwp_gettid()) - result = threadId - -elif defined(openbsd): - proc getthrid(): int32 {.importc: "getthrid", header: "".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(getthrid()) - result = threadId - -elif defined(netbsd): - proc lwp_self(): int32 {.importc: "_lwp_self", header: "".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(lwp_self()) - result = threadId - -elif defined(freebsd): - proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "".} - var SYS_thr_self {.importc:"SYS_thr_self", header:"".}: cint - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - var tid = 0.cint - if threadId == 0: - discard syscall(SYS_thr_self, addr tid) - threadId = tid - result = threadId - -elif defined(macosx): - proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "".} - var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"".}: cint - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(syscall(SYS_thread_selfid)) - result = threadId - -elif defined(solaris): - type thread_t {.importc: "thread_t", header: "".} = distinct int - proc thr_self(): thread_t {.importc, header: "".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(thr_self()) - result = threadId - -elif defined(haiku): - type thr_id {.importc: "thread_id", header: "".} = distinct int32 - proc find_thread(name: cstring): thr_id {.importc, header: "".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(find_thread(nil)) - result = threadId diff --git a/lib/system/threadimpl.nim b/lib/system/threadimpl.nim deleted file mode 100644 index 94db23336..000000000 --- a/lib/system/threadimpl.nim +++ /dev/null @@ -1,116 +0,0 @@ -var - nimThreadDestructionHandlers* {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}] -when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions): - proc deallocOsPages() {.rtl, raises: [].} -proc threadTrouble() {.raises: [], gcsafe.} -# create for the main thread. Note: do not insert this data into the list -# of all threads; it's not to be stopped etc. -when not defined(useNimRtl): - #when not defined(createNimRtl): initStackBottom() - when declared(initGC): - initGC() - when not emulatedThreadVars: - type ThreadType {.pure.} = enum - None = 0, - NimThread = 1, - ForeignThread = 2 - var - threadType {.rtlThreadVar.}: ThreadType - - threadType = ThreadType.NimThread - -when defined(gcDestructors): - proc allocThreadStorage(size: int): pointer = - result = c_malloc(csize_t size) - zeroMem(result, size) - - proc deallocThreadStorage(p: pointer) = c_free(p) -else: - template allocThreadStorage(size: untyped): untyped = allocShared0(size) - template deallocThreadStorage(p: pointer) = deallocShared(p) - -template afterThreadRuns() = - for i in countdown(nimThreadDestructionHandlers.len-1, 0): - nimThreadDestructionHandlers[i]() - -proc onThreadDestruction*(handler: proc () {.closure, gcsafe, raises: [].}) = - ## Registers a *thread local* handler that is called at the thread's - ## destruction. - ## - ## A thread is destructed when the `.thread` proc returns - ## normally or when it raises an exception. Note that unhandled exceptions - ## in a thread nevertheless cause the whole process to die. - nimThreadDestructionHandlers.add handler - -when defined(boehmgc): - type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.} - proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer) - {.importc: "GC_call_with_stack_base", boehmGC.} - proc boehmGC_register_my_thread(sb: pointer) - {.importc: "GC_register_my_thread", boehmGC.} - proc boehmGC_unregister_my_thread() - {.importc: "GC_unregister_my_thread", boehmGC.} - - proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv, raises: [].} = - boehmGC_register_my_thread(sb) - try: - let thrd = cast[ptr Thread[TArg]](thrd) - when TArg is void: - thrd.dataFn() - else: - thrd.dataFn(thrd.data) - except: - threadTrouble() - finally: - afterThreadRuns() - boehmGC_unregister_my_thread() -else: - proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = - try: - when TArg is void: - thrd.dataFn() - else: - when defined(nimV2): - thrd.dataFn(thrd.data) - else: - var x: TArg - deepCopy(x, thrd.data) - thrd.dataFn(x) - except: - threadTrouble() - finally: - afterThreadRuns() - when hasAllocStack: - deallocThreadStorage(thrd.rawStack) - -proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = - when defined(boehmgc): - boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd) - elif not defined(nogc) and not defined(gogc) and not defined(gcRegions) and not usesDestructors: - var p {.volatile.}: pointer - # init the GC for refc/markandsweep - nimGC_setStackBottom(addr(p)) - when declared(initGC): - initGC() - when declared(threadType): - threadType = ThreadType.NimThread - threadProcWrapDispatch[TArg](thrd) - when declared(deallocOsPages): deallocOsPages() - else: - threadProcWrapDispatch(thrd) - -template nimThreadProcWrapperBody*(closure: untyped): untyped = - var thrd = cast[ptr Thread[TArg]](closure) - var core = thrd.core - when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core) - threadProcWrapStackFrame(thrd) - # Since an unhandled exception terminates the whole process (!), there is - # no need for a ``try finally`` here, nor would it be correct: The current - # exception is tried to be re-raised by the code-gen after the ``finally``! - # However this is doomed to fail, because we already unmapped every heap - # page! - - # mark as not running anymore: - thrd.core = nil - thrd.dataFn = nil - deallocThreadStorage(cast[pointer](core)) diff --git a/lib/system/threadlocalstorage.nim b/lib/system/threadlocalstorage.nim index e6ad9dca5..117af4c25 100644 --- a/lib/system/threadlocalstorage.nim +++ b/lib/system/threadlocalstorage.nim @@ -1,33 +1,83 @@ -import std/private/threadtypes when defined(windows): type - ThreadVarSlot = distinct int32 + SysThread* = Handle + WinThreadProc = proc (x: pointer): int32 {.stdcall.} - proc threadVarAlloc(): ThreadVarSlot {. - importc: "TlsAlloc", stdcall, header: "".} - proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {. - importc: "TlsSetValue", stdcall, header: "".} - proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {. - importc: "TlsGetValue", stdcall, header: "".} + proc createThread(lpThreadAttributes: pointer, dwStackSize: int32, + lpStartAddress: WinThreadProc, + lpParameter: pointer, + dwCreationFlags: int32, + lpThreadId: var int32): SysThread {. + stdcall, dynlib: "kernel32", importc: "CreateThread".} - proc getLastError(): uint32 {. - importc: "GetLastError", stdcall, header: "".} - proc setLastError(x: uint32) {. - importc: "SetLastError", stdcall, header: "".} + proc winSuspendThread(hThread: SysThread): int32 {. + stdcall, dynlib: "kernel32", importc: "SuspendThread".} - proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer = - let realLastError = getLastError() - result = tlsGetValue(dwTlsIndex) - setLastError(realLastError) + proc winResumeThread(hThread: SysThread): int32 {. + stdcall, dynlib: "kernel32", importc: "ResumeThread".} -elif defined(genode): - const - GenodeHeader = "genode_cpp/threads.h" + proc waitForMultipleObjects(nCount: int32, + lpHandles: ptr SysThread, + bWaitAll: int32, + dwMilliseconds: int32): int32 {. + stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".} + + proc terminateThread(hThread: SysThread, dwExitCode: int32): int32 {. + stdcall, dynlib: "kernel32", importc: "TerminateThread".} + + proc getCurrentThreadId(): int32 {. + stdcall, dynlib: "kernel32", importc: "GetCurrentThreadId".} type + ThreadVarSlot = distinct int32 + + when true: + proc threadVarAlloc(): ThreadVarSlot {. + importc: "TlsAlloc", stdcall, header: "".} + proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {. + importc: "TlsSetValue", stdcall, header: "".} + proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {. + importc: "TlsGetValue", stdcall, header: "".} + + proc getLastError(): uint32 {. + importc: "GetLastError", stdcall, header: "".} + proc setLastError(x: uint32) {. + importc: "SetLastError", stdcall, header: "".} + + proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer = + let realLastError = getLastError() + result = tlsGetValue(dwTlsIndex) + setLastError(realLastError) + else: + proc threadVarAlloc(): ThreadVarSlot {. + importc: "TlsAlloc", stdcall, dynlib: "kernel32".} + proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {. + importc: "TlsSetValue", stdcall, dynlib: "kernel32".} + proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer {. + importc: "TlsGetValue", stdcall, dynlib: "kernel32".} + + proc setThreadAffinityMask(hThread: SysThread, dwThreadAffinityMask: uint) {. + importc: "SetThreadAffinityMask", stdcall, header: "".} + +elif defined(genode): + import genode/env + const + GenodeHeader = "genode_cpp/threads.h" + type + SysThread* {.importcpp: "Nim::SysThread", + header: GenodeHeader, final, pure.} = object + GenodeThreadProc = proc (x: pointer) {.noconv.} ThreadVarSlot = int + proc initThread(s: var SysThread, + env: GenodeEnv, + stackSize: culonglong, + entry: GenodeThreadProc, + arg: pointer, + affinity: cuint) {. + importcpp: "#.initThread(@)".} + proc threadVarAlloc(): ThreadVarSlot = 0 proc offMainThread(): bool {. @@ -63,18 +113,60 @@ else: when not defined(haiku): {.passc: "-pthread".} + const + schedh = "#define _GNU_SOURCE\n#include " + pthreadh = "#define _GNU_SOURCE\n#include " + + when not declared(Time): + when defined(linux): + type Time = clong + else: + type Time = int + when (defined(linux) or defined(nintendoswitch)) and defined(amd64): type + SysThread* {.importc: "pthread_t", + header: "" .} = distinct culong + Pthread_attr {.importc: "pthread_attr_t", + header: "".} = object + abi: array[56 div sizeof(clong), clong] ThreadVarSlot {.importc: "pthread_key_t", header: "".} = distinct cuint elif defined(openbsd) and defined(amd64): type + SysThread* {.importc: "pthread_t", header: "".} = object + Pthread_attr {.importc: "pthread_attr_t", + header: "".} = object ThreadVarSlot {.importc: "pthread_key_t", header: "".} = cint else: type + SysThread* {.importc: "pthread_t", header: "".} = object + Pthread_attr {.importc: "pthread_attr_t", + header: "".} = object ThreadVarSlot {.importc: "pthread_key_t", header: "".} = object + type + Timespec {.importc: "struct timespec", header: "".} = object + tv_sec: Time + tv_nsec: clong + + proc pthread_attr_init(a1: var Pthread_attr): cint {. + importc, header: pthreadh.} + proc pthread_attr_setstacksize(a1: var Pthread_attr, a2: int): cint {. + importc, header: pthreadh.} + proc pthread_attr_destroy(a1: var Pthread_attr): cint {. + importc, header: pthreadh.} + + proc pthread_create(a1: var SysThread, a2: var Pthread_attr, + a3: proc (x: pointer): pointer {.noconv.}, + a4: pointer): cint {.importc: "pthread_create", + header: pthreadh.} + proc pthread_join(a1: SysThread, a2: ptr pointer): cint {. + importc, header: pthreadh.} + + proc pthread_cancel(a1: SysThread): cint {. + importc: "pthread_cancel", header: pthreadh.} proc pthread_getspecific(a1: ThreadVarSlot): pointer {. importc: "pthread_getspecific", header: pthreadh.} @@ -94,13 +186,44 @@ else: proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} = result = pthread_getspecific(s) + type CpuSet {.importc: "cpu_set_t", header: schedh.} = object + when defined(linux) and defined(amd64): + abi: array[1024 div (8 * sizeof(culong)), culong] + + proc cpusetZero(s: var CpuSet) {.importc: "CPU_ZERO", header: schedh.} + proc cpusetIncl(cpu: cint; s: var CpuSet) {. + importc: "CPU_SET", header: schedh.} + + proc setAffinity(thread: SysThread; setsize: csize_t; s: var CpuSet) {. + importc: "pthread_setaffinity_np", header: pthreadh.} + + +const + emulatedThreadVars = compileOption("tlsEmulation") when emulatedThreadVars: # the compiler generates this proc for us, so that we can get the size of # the thread local var block; we use this only for sanity checking though proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".} - +# we preallocate a fixed size for thread local storage, so that no heap +# allocations are needed. Currently less than 16K are used on a 64bit machine. +# We use ``float`` for proper alignment: +const nimTlsSize {.intdefine.} = 16000 +type + ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float] + PGcThread = ptr GcThread + GcThread {.pure, inheritable.} = object + when emulatedThreadVars: + tls: ThreadLocalStorage + else: + nil + when hasSharedHeap: + next, prev: PGcThread + stackBottom, stackTop: pointer + stackSize: int + else: + nil when emulatedThreadVars: var globalsSlot: ThreadVarSlot @@ -122,4 +245,4 @@ when not defined(useNimRtl): if nimThreadVarsSize() > sizeof(ThreadLocalStorage): c_fprintf(cstderr, """too large thread local storage size requested, use -d:\"nimTlsSize=X\" to setup even more or stop using unittest.nim""") - rawQuit 1 + quit 1 diff --git a/lib/system/threads.nim b/lib/system/threads.nim new file mode 100644 index 000000000..cd1cf5e29 --- /dev/null +++ b/lib/system/threads.nim @@ -0,0 +1,434 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Thread support for Nim. +## +## **Note**: This is part of the system module. Do not import it directly. +## To activate thread support you need to compile +## with the ``--threads:on`` command line switch. +## +## Nim's memory model for threads is quite different from other common +## programming languages (C, Pascal): Each thread has its own +## (garbage collected) heap and sharing of memory is restricted. This helps +## to prevent race conditions and improves efficiency. See `the manual for +## details of this memory model `_. +## +## Examples +## ======== +## +## .. code-block:: Nim +## +## import locks +## +## var +## thr: array[0..4, Thread[tuple[a,b: int]]] +## L: Lock +## +## proc threadFunc(interval: tuple[a,b: int]) {.thread.} = +## for i in interval.a..interval.b: +## acquire(L) # lock stdout +## echo i +## release(L) +## +## initLock(L) +## +## for i in 0..high(thr): +## createThread(thr[i], threadFunc, (i*10, i*10+5)) +## joinThreads(thr) +## +## deinitLock(L) + +when not declared(ThisIsSystem): + {.error: "You must not import this module explicitly".} + +const + StackGuardSize = 4096 + ThreadStackMask = + when defined(genode): + 1024*64*sizeof(int)-1 + else: + 1024*256*sizeof(int)-1 + ThreadStackSize = ThreadStackMask+1 - StackGuardSize + +#const globalsSlot = ThreadVarSlot(0) +#sysAssert checkSlot.int == globalsSlot.int + +# create for the main thread. Note: do not insert this data into the list +# of all threads; it's not to be stopped etc. +when not defined(useNimRtl): + #when not defined(createNimRtl): initStackBottom() + when declared(initGC): + initGC() + when not emulatedThreadVars: + type ThreadType {.pure.} = enum + None = 0, + NimThread = 1, + ForeignThread = 2 + var + threadType {.rtlThreadVar.}: ThreadType + + threadType = ThreadType.NimThread + +# We jump through some hops here to ensure that Nim thread procs can have +# the Nim calling convention. This is needed because thread procs are +# ``stdcall`` on Windows and ``noconv`` on UNIX. Alternative would be to just +# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway. + +type + Thread*[TArg] = object + core: PGcThread + sys: SysThread + when TArg is void: + dataFn: proc () {.nimcall, gcsafe.} + else: + dataFn: proc (m: TArg) {.nimcall, gcsafe.} + data: TArg + +var + threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}] + +proc onThreadDestruction*(handler: proc () {.closure, gcsafe, raises: [].}) = + ## Registers a *thread local* handler that is called at the thread's + ## destruction. + ## + ## A thread is destructed when the ``.thread`` proc returns + ## normally or when it raises an exception. Note that unhandled exceptions + ## in a thread nevertheless cause the whole process to die. + threadDestructionHandlers.add handler + +template afterThreadRuns() = + for i in countdown(threadDestructionHandlers.len-1, 0): + threadDestructionHandlers[i]() + +when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions): + proc deallocOsPages() {.rtl, raises: [].} + +proc threadTrouble() {.raises: [], gcsafe.} + ## defined in system/excpt.nim + +when defined(boehmgc): + type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.} + proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer) + {.importc: "GC_call_with_stack_base", boehmGC.} + proc boehmGC_register_my_thread(sb: pointer) + {.importc: "GC_register_my_thread", boehmGC.} + proc boehmGC_unregister_my_thread() + {.importc: "GC_unregister_my_thread", boehmGC.} + + proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv, raises: [].} = + boehmGC_register_my_thread(sb) + try: + let thrd = cast[ptr Thread[TArg]](thrd) + when TArg is void: + thrd.dataFn() + else: + thrd.dataFn(thrd.data) + except: + threadTrouble() + finally: + afterThreadRuns() + boehmGC_unregister_my_thread() +else: + proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = + try: + when TArg is void: + thrd.dataFn() + else: + when defined(nimV2): + thrd.dataFn(thrd.data) + else: + var x: TArg + deepCopy(x, thrd.data) + thrd.dataFn(x) + except: + threadTrouble() + finally: + afterThreadRuns() + +proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = + when defined(boehmgc): + boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd) + elif not defined(nogc) and not defined(gogc) and not defined(gcRegions) and not usesDestructors: + var p {.volatile.}: pointer + # init the GC for refc/markandsweep + nimGC_setStackBottom(addr(p)) + initGC() + when declared(threadType): + threadType = ThreadType.NimThread + threadProcWrapDispatch[TArg](thrd) + when declared(deallocOsPages): deallocOsPages() + else: + threadProcWrapDispatch(thrd) + +template threadProcWrapperBody(closure: untyped): untyped = + var thrd = cast[ptr Thread[TArg]](closure) + var core = thrd.core + when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core) + threadProcWrapStackFrame(thrd) + # Since an unhandled exception terminates the whole process (!), there is + # no need for a ``try finally`` here, nor would it be correct: The current + # exception is tried to be re-raised by the code-gen after the ``finally``! + # However this is doomed to fail, because we already unmapped every heap + # page! + + # mark as not running anymore: + thrd.core = nil + thrd.dataFn = nil + deallocShared(cast[pointer](core)) + +{.push stack_trace:off.} +when defined(windows): + proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} = + threadProcWrapperBody(closure) + # implicitly return 0 +elif defined(genode): + proc threadProcWrapper[TArg](closure: pointer) {.noconv.} = + threadProcWrapperBody(closure) +else: + proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} = + threadProcWrapperBody(closure) +{.pop.} + +proc running*[TArg](t: Thread[TArg]): bool {.inline.} = + ## Returns true if `t` is running. + result = t.dataFn != nil + +proc handle*[TArg](t: Thread[TArg]): SysThread {.inline.} = + ## Returns the thread handle of `t`. + result = t.sys + +when hostOS == "windows": + const MAXIMUM_WAIT_OBJECTS = 64 + + proc joinThread*[TArg](t: Thread[TArg]) {.inline.} = + ## Waits for the thread `t` to finish. + discard waitForSingleObject(t.sys, -1'i32) + + proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = + ## Waits for every thread in `t` to finish. + var a: array[MAXIMUM_WAIT_OBJECTS, SysThread] + var k = 0 + while k < len(t): + var count = min(len(t) - k, MAXIMUM_WAIT_OBJECTS) + for i in 0..(count - 1): a[i] = t[i + k].sys + discard waitForMultipleObjects(int32(count), + cast[ptr SysThread](addr(a)), 1, -1) + inc(k, MAXIMUM_WAIT_OBJECTS) + +elif defined(genode): + proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.} + ## Waits for the thread `t` to finish. + + proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = + ## Waits for every thread in `t` to finish. + for i in 0..t.high: joinThread(t[i]) + +else: + proc joinThread*[TArg](t: Thread[TArg]) {.inline.} = + ## Waits for the thread `t` to finish. + discard pthread_join(t.sys, nil) + + proc joinThreads*[TArg](t: varargs[Thread[TArg]]) = + ## Waits for every thread in `t` to finish. + for i in 0..t.high: joinThread(t[i]) + +when false: + # XXX a thread should really release its heap here somehow: + proc destroyThread*[TArg](t: var Thread[TArg]) = + ## Forces the thread `t` to terminate. This is potentially dangerous if + ## you don't have full control over `t` and its acquired resources. + when hostOS == "windows": + discard TerminateThread(t.sys, 1'i32) + else: + discard pthread_cancel(t.sys) + when declared(registerThread): unregisterThread(addr(t)) + t.dataFn = nil + ## if thread `t` already exited, `t.core` will be `null`. + if not isNil(t.core): + deallocShared(t.core) + t.core = nil + +when hostOS == "windows": + proc createThread*[TArg](t: var Thread[TArg], + tp: proc (arg: TArg) {.thread, nimcall.}, + param: TArg) = + ## Creates a new thread `t` and starts its execution. + ## + ## Entry point is the proc `tp`. + ## `param` is passed to `tp`. `TArg` can be ``void`` if you + ## don't need to pass any data to the thread. + t.core = cast[PGcThread](allocShared0(sizeof(GcThread))) + + when TArg isnot void: t.data = param + t.dataFn = tp + when hasSharedHeap: t.core.stackSize = ThreadStackSize + var dummyThreadId: int32 + t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg], + addr(t), 0'i32, dummyThreadId) + if t.sys <= 0: + raise newException(ResourceExhaustedError, "cannot create thread") + + proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = + ## Pins a thread to a `CPU`:idx:. + ## + ## In other words sets a thread's `affinity`:idx:. + ## If you don't know what this means, you shouldn't use this proc. + setThreadAffinityMask(t.sys, uint(1 shl cpu)) + +elif defined(genode): + var affinityOffset: cuint = 1 + ## CPU affinity offset for next thread, safe to roll-over. + + proc createThread*[TArg](t: var Thread[TArg], + tp: proc (arg: TArg) {.thread, nimcall.}, + param: TArg) = + t.core = cast[PGcThread](allocShared0(sizeof(GcThread))) + + when TArg isnot void: t.data = param + t.dataFn = tp + when hasSharedHeap: t.stackSize = ThreadStackSize + t.sys.initThread( + runtimeEnv, + ThreadStackSize.culonglong, + threadProcWrapper[TArg], addr(t), affinityOffset) + inc affinityOffset + + proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = + {.hint: "cannot change Genode thread CPU affinity after initialization".} + discard + +else: + proc createThread*[TArg](t: var Thread[TArg], + tp: proc (arg: TArg) {.thread, nimcall.}, + param: TArg) = + ## Creates a new thread `t` and starts its execution. + ## + ## Entry point is the proc `tp`. `param` is passed to `tp`. + ## `TArg` can be ``void`` if you + ## don't need to pass any data to the thread. + t.core = cast[PGcThread](allocShared0(sizeof(GcThread))) + + when TArg isnot void: t.data = param + t.dataFn = tp + when hasSharedHeap: t.core.stackSize = ThreadStackSize + var a {.noinit.}: Pthread_attr + doAssert pthread_attr_init(a) == 0 + let setstacksizeResult = pthread_attr_setstacksize(a, ThreadStackSize) + when not defined(ios): + # This fails on iOS + doAssert(setstacksizeResult == 0) + if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: + raise newException(ResourceExhaustedError, "cannot create thread") + doAssert pthread_attr_destroy(a) == 0 + + proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = + ## Pins a thread to a `CPU`:idx:. + ## + ## In other words sets a thread's `affinity`:idx:. + ## If you don't know what this means, you shouldn't use this proc. + when not defined(macosx): + var s {.noinit.}: CpuSet + cpusetZero(s) + cpusetIncl(cpu.cint, s) + setAffinity(t.sys, csize_t(sizeof(s)), s) + +proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) = + createThread[void](t, tp) + +# we need to cache current threadId to not perform syscall all the time +var threadId {.threadvar.}: int + +when defined(windows): + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(getCurrentThreadId()) + result = threadId + +elif defined(linux): + proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "".} + when defined(amd64): + const NR_gettid = clong(186) + else: + var NR_gettid {.importc: "__NR_gettid", header: "".}: clong + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(syscall(NR_gettid)) + result = threadId + +elif defined(dragonfly): + proc lwp_gettid(): int32 {.importc, header: "unistd.h".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(lwp_gettid()) + result = threadId + +elif defined(openbsd): + proc getthrid(): int32 {.importc: "getthrid", header: "".} + + proc getThreadId*(): int = + ## get the ID of the currently running thread. + if threadId == 0: + threadId = int(getthrid()) + result = threadId + +elif defined(netbsd): + proc lwp_self(): int32 {.importc: "_lwp_self", header: "".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(lwp_self()) + result = threadId + +elif defined(freebsd): + proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "".} + var SYS_thr_self {.importc:"SYS_thr_self", header:""}: cint + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + var tid = 0.cint + if threadId == 0: + discard syscall(SYS_thr_self, addr tid) + threadId = tid + result = threadId + +elif defined(macosx): + proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "".} + var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"".}: cint + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(syscall(SYS_thread_selfid)) + result = threadId + +elif defined(solaris): + type thread_t {.importc: "thread_t", header: "".} = distinct int + proc thr_self(): thread_t {.importc, header: "".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(thr_self()) + result = threadId + +elif defined(haiku): + type thr_id {.importc: "thread_id", header: "".} = distinct int32 + proc find_thread(name: cstring): thr_id {.importc, header: "".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(find_thread(nil)) + result = threadId diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim new file mode 100644 index 000000000..955fab46a --- /dev/null +++ b/lib/system/widestrs.nim @@ -0,0 +1,223 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# Nim support for C/C++'s `wide strings`:idx:. This is part of the system +# module! Do not import it directly! + +#when not declared(ThisIsSystem): +# {.error: "You must not import this module explicitly".} + +type + Utf16Char* = distinct int16 + +when defined(nimv2): + + type + WideCString* = ptr UncheckedArray[Utf16Char] + + WideCStringObj* = object + bytes: int + data: WideCString + + proc `=destroy`(a: var WideCStringObj) = + if a.data != nil: + when compileOption("threads"): + deallocShared(a.data) + else: + dealloc(a.data) + + proc `=`(a: var WideCStringObj; b: WideCStringObj) {.error.} + + proc `=sink`(a: var WideCStringObj; b: WideCStringObj) = + a.bytes = b.bytes + a.data = b.data + + proc createWide(a: var WideCStringObj; bytes: int) = + a.bytes = bytes + when compileOption("threads"): + a.data = cast[typeof(a.data)](allocShared0(bytes)) + else: + a.data = cast[typeof(a.data)](alloc0(bytes)) + + template `[]`*(a: WideCStringObj; idx: int): Utf16Char = a.data[idx] + template `[]=`*(a: WideCStringObj; idx: int; val: Utf16Char) = a.data[idx] = val + + template nullWide(): untyped = WideCStringObj(bytes: 0, data: nil) + + converter toWideCString*(x: WideCStringObj): WideCString {.inline.} = + result = x.data + +else: + template nullWide(): untyped = nil + + type + WideCString* = ref UncheckedArray[Utf16Char] + WideCStringObj* = WideCString + + template createWide(a; L) = + unsafeNew(a, L) + +proc ord(arg: Utf16Char): int = int(cast[uint16](arg)) + +proc len*(w: WideCString): int = + ## returns the length of a widestring. This traverses the whole string to + ## find the binary zero end marker! + result = 0 + while int16(w[result]) != 0'i16: inc result + +const + UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16) + UNI_MAX_BMP = 0x0000FFFF + UNI_MAX_UTF16 = 0x0010FFFF + UNI_MAX_UTF32 = 0x7FFFFFFF + UNI_MAX_LEGAL_UTF32 = 0x0010FFFF + + halfShift = 10 + halfBase = 0x0010000 + halfMask = 0x3FF + + UNI_SUR_HIGH_START = 0xD800 + UNI_SUR_HIGH_END = 0xDBFF + UNI_SUR_LOW_START = 0xDC00 + UNI_SUR_LOW_END = 0xDFFF + UNI_REPL = 0xFFFD + +template ones(n: untyped): untyped = ((1 shl n)-1) + +template fastRuneAt(s: cstring, i, L: int, result: untyped, doInc = true) = + ## Returns the unicode character ``s[i]`` in `result`. If ``doInc == true`` + ## `i` is incremented by the number of bytes that have been processed. + bind ones + + if ord(s[i]) <= 127: + result = ord(s[i]) + when doInc: inc(i) + elif ord(s[i]) shr 5 == 0b110: + #assert(ord(s[i+1]) shr 6 == 0b10) + if i <= L - 2: + result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6)) + when doInc: inc(i, 2) + else: + result = UNI_REPL + when doInc: inc(i) + elif ord(s[i]) shr 4 == 0b1110: + if i <= L - 3: + #assert(ord(s[i+1]) shr 6 == 0b10) + #assert(ord(s[i+2]) shr 6 == 0b10) + result = (ord(s[i]) and ones(4)) shl 12 or + (ord(s[i+1]) and ones(6)) shl 6 or + (ord(s[i+2]) and ones(6)) + when doInc: inc(i, 3) + else: + result = UNI_REPL + when doInc: inc(i) + elif ord(s[i]) shr 3 == 0b11110: + if i <= L - 4: + #assert(ord(s[i+1]) shr 6 == 0b10) + #assert(ord(s[i+2]) shr 6 == 0b10) + #assert(ord(s[i+3]) shr 6 == 0b10) + result = (ord(s[i]) and ones(3)) shl 18 or + (ord(s[i+1]) and ones(6)) shl 12 or + (ord(s[i+2]) and ones(6)) shl 6 or + (ord(s[i+3]) and ones(6)) + when doInc: inc(i, 4) + else: + result = UNI_REPL + when doInc: inc(i) + else: + result = 0xFFFD + when doInc: inc(i) + +iterator runes(s: cstring, L: int): int = + var + i = 0 + result: int + while i < L: + fastRuneAt(s, i, L, result, true) + yield result + +proc newWideCString*(source: cstring, L: int): WideCStringObj = + createWide(result, L * 2 + 2) + var d = 0 + for ch in runes(source, L): + + if ch <= UNI_MAX_BMP: + if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_LOW_END: + result[d] = UNI_REPLACEMENT_CHAR + else: + result[d] = cast[Utf16Char](uint16(ch)) + elif ch > UNI_MAX_UTF16: + result[d] = UNI_REPLACEMENT_CHAR + else: + let ch = ch - halfBase + result[d] = cast[Utf16Char](uint16((ch shr halfShift) + UNI_SUR_HIGH_START)) + inc d + result[d] = cast[Utf16Char](uint16((ch and halfMask) + UNI_SUR_LOW_START)) + inc d + result[d] = Utf16Char(0) + +proc newWideCString*(s: cstring): WideCStringObj = + if s.isNil: return nullWide + + result = newWideCString(s, s.len) + +proc newWideCString*(s: string): WideCStringObj = + result = newWideCString(s, s.len) + +proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string = + result = newStringOfCap(estimate + estimate shr 2) + + var i = 0 + while w[i].int16 != 0'i16: + var ch = ord(w[i]) + inc i + if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END: + # If the 16 bits following the high surrogate are in the source buffer... + let ch2 = ord(w[i]) + + # If it's a low surrogate, convert to UTF32: + if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END: + ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase + inc i + else: + #invalid UTF-16 + ch = replacement + elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END: + #invalid UTF-16 + ch = replacement + + if ch < 0x80: + result.add chr(ch) + elif ch < 0x800: + result.add chr((ch shr 6) or 0xc0) + result.add chr((ch and 0x3f) or 0x80) + elif ch < 0x10000: + result.add chr((ch shr 12) or 0xe0) + result.add chr(((ch shr 6) and 0x3f) or 0x80) + result.add chr((ch and 0x3f) or 0x80) + elif ch <= 0x10FFFF: + result.add chr((ch shr 18) or 0xf0) + result.add chr(((ch shr 12) and 0x3f) or 0x80) + result.add chr(((ch shr 6) and 0x3f) or 0x80) + result.add chr((ch and 0x3f) or 0x80) + else: + # replacement char(in case user give very large number): + result.add chr(0xFFFD shr 12 or 0b1110_0000) + result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00) + result.add chr(0xFFFD and ones(6) or 0b10_0000_00) + +proc `$`*(s: WideCString): string = + result = s $ 80 + +when defined(nimv2): + proc `$`*(s: WideCStringObj, estimate: int, replacement: int = 0xFFFD): string = + `$`(s.data, estimate, replacement) + + proc `$`*(s: WideCStringObj): string = + $(s.data) diff --git a/lib/system_overview.rst b/lib/system_overview.rst index cc0643bf1..2e437e596 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -1,23 +1,19 @@ -.. default-role:: code -.. include:: ../doc/rstcommon.rst - The System module imports several separate modules, and their documentation is in separate files: * `iterators `_ -* `exceptions `_ * `assertions `_ * `dollars `_ -* `ctypes `_ +* `io `_ +* `widestrs `_ Here is a short overview of the most commonly used functions from the `system` module. Function names in the tables below are clickable and will take you to the full documentation of the function. -There are many more functions available than the ones listed in this overview. -Use the table of contents on the left-hand side and/or `Ctrl+F` to navigate -through this module. +The amount of available functions is much larger. Use the table of contents +on the left-hand side and/or `Ctrl+F` to navigate through this module. Strings and characters @@ -39,7 +35,7 @@ Proc Usage * `strutils module `_ for common string functions * `strformat module `_ for string interpolation and formatting * `unicode module `_ for Unicode UTF-8 handling -* `strscans `_ for `scanf` and `scanp` macros, which offer +* `strscans `_ for ``scanf`` and ``scanp`` macros, which offer easier substring extraction than regular expressions * `strtabs module `_ for efficient hash tables (dictionaries, in some programming languages) mapping from strings to strings @@ -49,27 +45,27 @@ Proc Usage Seqs ---- -============================================================= ========================================== -Proc Usage -============================================================= ========================================== -`newSeq<#newSeq>`_ Create a new sequence of a given length -`newSeqOfCap<#newSeqOfCap,Natural>`_ Create a new sequence with zero length - and a given capacity -`setLen<#setLen,seq[T],Natural>`_ Set the length of a sequence -`len<#len,seq[T]>`_ Return the length of a sequence -`@<#@,openArray[T]>`_ Turn an array into a sequence -`add<#add,seq[T],sinkT>`_ Add an item to the sequence -`insert<#insert,seq[T],sinkT>`_ Insert an item at a specific position -`delete<#delete,seq[T],Natural>`_ Delete an item while preserving the - order of elements (`O(n)` operation) -`del<#del,seq[T],Natural>`_ `O(1)` removal, doesn't preserve the order -`pop<#pop,seq[T]>`_ Remove and return last item of a sequence -`x & y<#&,seq[T],seq[T]>`_ Concatenate two sequences -`x[a .. b]<#[],openArray[T],HSlice[U: Ordinal,V: Ordinal]>`_ Slice of a sequence (both ends included) -`x[a .. ^b]<#[],openArray[T],HSlice[U: Ordinal,V: Ordinal]>`_ Slice of a sequence but `b` is a - reversed index (both ends included) -`x[a ..< b]<#[],openArray[T],HSlice[U: Ordinal,V: Ordinal]>`_ Slice of a sequence (excluded upper bound) -============================================================= ========================================== +============================================== ========================================== +Proc Usage +============================================== ========================================== +`newSeq<#newSeq>`_ Create a new sequence of a given length +`newSeqOfCap<#newSeqOfCap,Natural>`_ Create a new sequence with zero length + and a given capacity +`setLen<#setLen,seq[T],Natural>`_ Set the length of a sequence +`len<#len,seq[T]>`_ Return the length of a sequence +`@<#@,openArray[T]>`_ Turn an array into a sequence +`add<#add,seq[T],sinkT>`_ Add an item to the sequence +`insert<#insert,seq[T],sinkT>`_ Insert an item at a specific position +`delete<#delete,seq[T],Natural>`_ Delete an item while preserving the + order of elements (`O(n)` operation) +`del<#del,seq[T],Natural>`_ `O(1)` removal, doesn't preserve the order +`pop<#pop,seq[T]>`_ Remove and return last item of a sequence +`x & y<#&,seq[T],seq[T]>`_ Concatenate two sequences +`x[a .. b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence (both ends included) +`x[a .. ^b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence but `b` is a + reversed index (both ends included) +`x[a ..\< b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence (excluded upper bound) +============================================== ========================================== **See also:** * `sequtils module `_ for operations on container @@ -99,7 +95,6 @@ Proc Usage =============================== ====================================== **See also:** -* `setutils module `_ for bit set convenience functions * `sets module `_ for hash sets * `intsets module `_ for efficient int sets diff --git a/lib/windows/registry.nim b/lib/windows/registry.nim index 207172f8c..c17f2f455 100644 --- a/lib/windows/registry.nim +++ b/lib/windows/registry.nim @@ -9,10 +9,7 @@ ## This module is experimental and its interface may change. -import std/oserrors - -when defined(nimPreviewSlimSystem): - import std/widestrs +import winlean, os type HKEY* = uint @@ -49,26 +46,24 @@ template call(f) = proc getUnicodeValue*(path, key: string; handle: HKEY): string = let hh = newWideCString path let kk = newWideCString key - var bufSize: int32 + var bufsize: int32 # try a couple of different flag settings: var flags: int32 = RRF_RT_ANY - let err = regGetValue(handle, hh, kk, flags, nil, nil, addr bufSize) + let err = regGetValue(handle, hh, kk, flags, nil, nil, addr bufsize) if err != 0: var newHandle: HKEY call regOpenKeyEx(handle, hh, 0, KEY_READ or KEY_WOW64_64KEY, newHandle) - call regGetValue(newHandle, nil, kk, flags, nil, nil, addr bufSize) - if bufSize > 0: - var res = newWideCString(bufSize) - call regGetValue(newHandle, nil, kk, flags, nil, addr res[0], - addr bufSize) - result = res $ bufSize + call regGetValue(newHandle, nil, kk, flags, nil, nil, addr bufsize) + var res = newWideCString("", bufsize) + call regGetValue(newHandle, nil, kk, flags, nil, cast[pointer](res), + addr bufsize) + result = res $ bufsize call regCloseKey(newHandle) else: - if bufSize > 0: - var res = newWideCString(bufSize) - call regGetValue(handle, hh, kk, flags, nil, addr res[0], - addr bufSize) - result = res $ bufSize + var res = newWideCString("", bufsize) + call regGetValue(handle, hh, kk, flags, nil, cast[pointer](res), + addr bufsize) + result = res $ bufsize proc regSetValue(key: HKEY, lpSubKey, lpValueName: WideCString, dwType: int32; lpData: WideCString; cbData: int32): int32 {. diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index f8c6ce021..1334a85d5 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -17,13 +17,14 @@ when defined(nimHasStyleChecks): {.passc: "-DWIN32_LEAN_AND_MEAN".} -when defined(nimPreviewSlimSystem): - from std/syncio import FileHandle - import std/widestrs +const + useWinUnicode* = not defined(useWinAnsi) -type WinChar* = Utf16Char +when useWinUnicode: + type WinChar* = Utf16Char +else: + type WinChar* = char -# See https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types type Handle* = int LONG* = int32 @@ -32,7 +33,6 @@ type WINBOOL* = int32 ## `WINBOOL` uses opposite convention as posix, !=0 meaning success. # xxx this should be distinct int32, distinct would make code less error prone - PBOOL* = ptr WINBOOL DWORD* = int32 PDWORD* = ptr DWORD LPINT* = ptr int32 @@ -40,14 +40,13 @@ type PULONG_PTR* = ptr uint HDC* = Handle HGLRC* = Handle - BYTE* = uint8 - SECURITY_ATTRIBUTES* = object + SECURITY_ATTRIBUTES* {.final, pure.} = object nLength*: int32 lpSecurityDescriptor*: pointer bInheritHandle*: WINBOOL - STARTUPINFO* = object + STARTUPINFO* {.final, pure.} = object cb*: int32 lpReserved*: cstring lpDesktop*: cstring @@ -67,17 +66,17 @@ type hStdOutput*: Handle hStdError*: Handle - PROCESS_INFORMATION* = object + PROCESS_INFORMATION* {.final, pure.} = object hProcess*: Handle hThread*: Handle dwProcessId*: int32 dwThreadId*: int32 - FILETIME* = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT + FILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT dwLowDateTime*: DWORD dwHighDateTime*: DWORD - BY_HANDLE_FILE_INFORMATION* = object + BY_HANDLE_FILE_INFORMATION* {.final, pure.} = object dwFileAttributes*: DWORD ftCreationTime*: FILETIME ftLastAccessTime*: FILETIME @@ -89,7 +88,7 @@ type nFileIndexHigh*: DWORD nFileIndexLow*: DWORD - OSVERSIONINFO* = object + OSVERSIONINFO* {.final, pure.} = object dwOSVersionInfoSize*: DWORD dwMajorVersion*: DWORD dwMinorVersion*: DWORD @@ -137,10 +136,6 @@ const HANDLE_FLAG_INHERIT* = 0x00000001'i32 -proc isSuccess*(a: WINBOOL): bool {.inline.} = - ## Returns true if `a != 0`. Windows uses a different convention than POSIX, - ## where `a == 0` is commonly used on success. - a != 0 proc getVersionExW*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {. stdcall, dynlib: "kernel32", importc: "GetVersionExW", sideEffect.} proc getVersionExA*(lpVersionInfo: ptr OSVERSIONINFO): WINBOOL {. @@ -178,14 +173,26 @@ proc peekNamedPipe*(hNamedPipe: Handle, lpBuffer: pointer=nil, lpBytesLeftThisMessage: ptr int32 = nil): bool {. stdcall, dynlib: "kernel32", importc: "PeekNamedPipe".} -proc createProcessW*(lpApplicationName, lpCommandLine: WideCString, - lpProcessAttributes: ptr SECURITY_ATTRIBUTES, - lpThreadAttributes: ptr SECURITY_ATTRIBUTES, - bInheritHandles: WINBOOL, dwCreationFlags: int32, - lpEnvironment, lpCurrentDirectory: WideCString, - lpStartupInfo: var STARTUPINFO, - lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{. - stdcall, dynlib: "kernel32", importc: "CreateProcessW", sideEffect.} +when useWinUnicode: + proc createProcessW*(lpApplicationName, lpCommandLine: WideCString, + lpProcessAttributes: ptr SECURITY_ATTRIBUTES, + lpThreadAttributes: ptr SECURITY_ATTRIBUTES, + bInheritHandles: WINBOOL, dwCreationFlags: int32, + lpEnvironment, lpCurrentDirectory: WideCString, + lpStartupInfo: var STARTUPINFO, + lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{. + stdcall, dynlib: "kernel32", importc: "CreateProcessW", sideEffect.} + +else: + proc createProcessA*(lpApplicationName, lpCommandLine: cstring, + lpProcessAttributes: ptr SECURITY_ATTRIBUTES, + lpThreadAttributes: ptr SECURITY_ATTRIBUTES, + bInheritHandles: WINBOOL, dwCreationFlags: int32, + lpEnvironment: pointer, lpCurrentDirectory: cstring, + lpStartupInfo: var STARTUPINFO, + lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{. + stdcall, dynlib: "kernel32", importc: "CreateProcessA", sideEffect.} + proc suspendThread*(hThread: Handle): int32 {.stdcall, dynlib: "kernel32", importc: "SuspendThread", sideEffect.} @@ -214,37 +221,67 @@ proc getLastError*(): int32 {.importc: "GetLastError", proc setLastError*(error: int32) {.importc: "SetLastError", stdcall, dynlib: "kernel32", sideEffect.} -proc formatMessageW*(dwFlags: int32, lpSource: pointer, +when useWinUnicode: + proc formatMessageW*(dwFlags: int32, lpSource: pointer, + dwMessageId, dwLanguageId: int32, + lpBuffer: pointer, nSize: int32, + arguments: pointer): int32 {. + importc: "FormatMessageW", stdcall, dynlib: "kernel32".} +else: + proc formatMessageA*(dwFlags: int32, lpSource: pointer, dwMessageId, dwLanguageId: int32, lpBuffer: pointer, nSize: int32, arguments: pointer): int32 {. - importc: "FormatMessageW", stdcall, dynlib: "kernel32".} + importc: "FormatMessageA", stdcall, dynlib: "kernel32".} proc localFree*(p: pointer) {. importc: "LocalFree", stdcall, dynlib: "kernel32".} -proc getCurrentDirectoryW*(nBufferLength: int32, - lpBuffer: WideCString): int32 {. - importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} -proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {. - importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} -proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {. - importc: "CreateDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} -proc removeDirectoryW*(lpPathName: WideCString): int32 {. - importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} -proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {. - stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.} +when useWinUnicode: + proc getCurrentDirectoryW*(nBufferLength: int32, + lpBuffer: WideCString): int32 {. + importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} + proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {. + importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} + proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {. + importc: "CreateDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} + proc removeDirectoryW*(lpPathName: WideCString): int32 {. + importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} + proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {. + stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.} -proc getModuleFileNameW*(handle: Handle, buf: WideCString, - size: int32): int32 {.importc: "GetModuleFileNameW", - dynlib: "kernel32", stdcall.} + proc getModuleFileNameW*(handle: Handle, buf: WideCString, + size: int32): int32 {.importc: "GetModuleFileNameW", + dynlib: "kernel32", stdcall.} +else: + proc getCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {. + importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall, sideEffect.} + proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. + importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall, sideEffect.} + proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. + importc: "CreateDirectoryA", dynlib: "kernel32", stdcall, sideEffect.} + proc removeDirectoryA*(lpPathName: cstring): int32 {. + importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall, sideEffect.} + proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. + stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA", sideEffect.} -proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString, - flags: DWORD): int32 {. - importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall, sideEffect.} -proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString, - security: pointer=nil): int32 {. - importc:"CreateHardLinkW", dynlib: "kernel32", stdcall, sideEffect.} + proc getModuleFileNameA*(handle: Handle, buf: cstring, size: int32): int32 {. + importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} + +when useWinUnicode: + proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString, + flags: DWORD): int32 {. + importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall, sideEffect.} + proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString, + security: pointer=nil): int32 {. + importc:"CreateHardLinkW", dynlib: "kernel32", stdcall, sideEffect.} +else: + proc createSymbolicLinkA*(lpSymlinkFileName, lpTargetFileName: cstring, + flags: DWORD): int32 {. + importc:"CreateSymbolicLinkA", dynlib: "kernel32", stdcall, sideEffect.} + proc createHardLinkA*(lpFileName, lpExistingFileName: cstring, + security: pointer=nil): int32 {. + importc:"CreateHardLinkA", dynlib: "kernel32", stdcall, sideEffect.} const FILE_ATTRIBUTE_READONLY* = 0x00000001'i32 @@ -295,51 +332,90 @@ type cFileName*: array[0..(MAX_PATH) - 1, WinChar] cAlternateFileName*: array[0..13, WinChar] -proc findFirstFileW*(lpFileName: WideCString, - lpFindFileData: var WIN32_FIND_DATA): Handle {. - stdcall, dynlib: "kernel32", importc: "FindFirstFileW", sideEffect.} -proc findNextFileW*(hFindFile: Handle, - lpFindFileData: var WIN32_FIND_DATA): int32 {. - stdcall, dynlib: "kernel32", importc: "FindNextFileW", sideEffect.} +when useWinUnicode: + proc findFirstFileW*(lpFileName: WideCString, + lpFindFileData: var WIN32_FIND_DATA): Handle {. + stdcall, dynlib: "kernel32", importc: "FindFirstFileW", sideEffect.} + proc findNextFileW*(hFindFile: Handle, + lpFindFileData: var WIN32_FIND_DATA): int32 {. + stdcall, dynlib: "kernel32", importc: "FindNextFileW", sideEffect.} +else: + proc findFirstFileA*(lpFileName: cstring, + lpFindFileData: var WIN32_FIND_DATA): Handle {. + stdcall, dynlib: "kernel32", importc: "FindFirstFileA", sideEffect.} + proc findNextFileA*(hFindFile: Handle, + lpFindFileData: var WIN32_FIND_DATA): int32 {. + stdcall, dynlib: "kernel32", importc: "FindNextFileA", sideEffect.} proc findClose*(hFindFile: Handle) {.stdcall, dynlib: "kernel32", importc: "FindClose".} -proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32, - lpBuffer: WideCString, - lpFilePart: var WideCString): int32 {. - stdcall, dynlib: "kernel32", - importc: "GetFullPathNameW", sideEffect.} -proc getFileAttributesW*(lpFileName: WideCString): int32 {. +when useWinUnicode: + proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32, + lpBuffer: WideCString, + lpFilePart: var WideCString): int32 {. stdcall, dynlib: "kernel32", - importc: "GetFileAttributesW", sideEffect.} -proc setFileAttributesW*(lpFileName: WideCString, - dwFileAttributes: int32): WINBOOL {. - stdcall, dynlib: "kernel32", importc: "SetFileAttributesW", sideEffect.} + importc: "GetFullPathNameW", sideEffect.} + proc getFileAttributesW*(lpFileName: WideCString): int32 {. + stdcall, dynlib: "kernel32", + importc: "GetFileAttributesW", sideEffect.} + proc setFileAttributesW*(lpFileName: WideCString, + dwFileAttributes: int32): WINBOOL {. + stdcall, dynlib: "kernel32", importc: "SetFileAttributesW", sideEffect.} -proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString, - bFailIfExists: WINBOOL): WINBOOL {. - importc: "CopyFileW", stdcall, dynlib: "kernel32", sideEffect.} + proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString, + bFailIfExists: WINBOOL): WINBOOL {. + importc: "CopyFileW", stdcall, dynlib: "kernel32", sideEffect.} -proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {. - importc: "MoveFileW", stdcall, dynlib: "kernel32", sideEffect.} -proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString, - flags: DWORD): WINBOOL {. - importc: "MoveFileExW", stdcall, dynlib: "kernel32", sideEffect.} + proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {. + importc: "MoveFileW", stdcall, dynlib: "kernel32", sideEffect.} + proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString, + flags: DWORD): WINBOOL {. + importc: "MoveFileExW", stdcall, dynlib: "kernel32", sideEffect.} -proc getEnvironmentStringsW*(): WideCString {. - stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW", sideEffect.} -proc freeEnvironmentStringsW*(para1: WideCString): int32 {. - stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW", sideEffect.} + proc getEnvironmentStringsW*(): WideCString {. + stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW", sideEffect.} + proc freeEnvironmentStringsW*(para1: WideCString): int32 {. + stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW", sideEffect.} -proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW", - stdcall, dynlib: "kernel32", sideEffect.} + proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW", + stdcall, dynlib: "kernel32", sideEffect.} + +else: + proc getFullPathNameA*(lpFileName: cstring, nBufferLength: int32, + lpBuffer: cstring, lpFilePart: var cstring): int32 {. + stdcall, dynlib: "kernel32", + importc: "GetFullPathNameA", sideEffect.} + proc getFileAttributesA*(lpFileName: cstring): int32 {. + stdcall, dynlib: "kernel32", + importc: "GetFileAttributesA", sideEffect.} + proc setFileAttributesA*(lpFileName: cstring, + dwFileAttributes: int32): WINBOOL {. + stdcall, dynlib: "kernel32", importc: "SetFileAttributesA", sideEffect.} + + proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, + bFailIfExists: cint): cint {. + importc: "CopyFileA", stdcall, dynlib: "kernel32", sideEffect.} + + proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {. + importc: "MoveFileA", stdcall, dynlib: "kernel32", sideEffect.} + proc moveFileExA*(lpExistingFileName, lpNewFileName: cstring, + flags: DWORD): WINBOOL {. + importc: "MoveFileExA", stdcall, dynlib: "kernel32", sideEffect.} + + proc getEnvironmentStringsA*(): cstring {. + stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA", sideEffect.} + proc freeEnvironmentStringsA*(para1: cstring): int32 {. + stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsA", sideEffect.} + + proc getCommandLineA*(): cstring {. + importc: "GetCommandLineA", stdcall, dynlib: "kernel32", sideEffect.} proc rdFileTime*(f: FILETIME): int64 = - result = int64(cast[uint32](f.dwLowDateTime)) or (int64(cast[uint32](f.dwHighDateTime)) shl 32) + result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32) proc rdFileSize*(f: WIN32_FIND_DATA): int64 = - result = int64(cast[uint32](f.nFileSizeLow)) or (int64(cast[uint32](f.nFileSizeHigh)) shl 32) + result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32) proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {. importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall, sideEffect.} @@ -347,10 +423,17 @@ proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {. proc sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32", importc: "Sleep", sideEffect.} -proc shellExecuteW*(hwnd: Handle, lpOperation, lpFile, - lpParameters, lpDirectory: WideCString, - nShowCmd: int32): Handle{. - stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW", sideEffect.} +when useWinUnicode: + proc shellExecuteW*(hwnd: Handle, lpOperation, lpFile, + lpParameters, lpDirectory: WideCString, + nShowCmd: int32): Handle{. + stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW", sideEffect.} + +else: + proc shellExecuteA*(hwnd: Handle, lpOperation, lpFile, + lpParameters, lpDirectory: cstring, + nShowCmd: int32): Handle{. + stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA", sideEffect.} proc getFileInformationByHandle*(hFile: Handle, lpFileInformation: ptr BY_HANDLE_FILE_INFORMATION): WINBOOL{. @@ -388,7 +471,7 @@ type PSockAddr = ptr SockAddr - InAddr* {.importc: "IN_ADDR", header: "winsock2.h", union.} = object + InAddr* {.importc: "IN_ADDR", header: "winsock2.h".} = object s_addr*: uint32 # IP address Sockaddr_in* {.importc: "SOCKADDR_IN", @@ -412,9 +495,9 @@ type Sockaddr_storage* {.importc: "SOCKADDR_STORAGE", header: "winsock2.h".} = object ss_family*: uint16 - ss_pad1 {.importc: "__ss_pad1".}: array[6, byte] - ss_align {.importc: "__ss_align".}: int64 - ss_pad2 {.importc: "__ss_pad2".}: array[112, byte] + ss_pad1: array[6, byte] + ss_align: int64 + ss_pad2: array[112, byte] Servent* = object s_name*: cstring @@ -577,7 +660,7 @@ proc getaddrinfo*(nodename, servname: cstring, hints: ptr AddrInfo, res: var ptr AddrInfo): cint {. stdcall, importc: "getaddrinfo", dynlib: ws2dll.} -proc freeAddrInfo*(ai: ptr AddrInfo) {. +proc freeaddrinfo*(ai: ptr AddrInfo) {. stdcall, importc: "freeaddrinfo", dynlib: ws2dll.} proc inet_ntoa*(i: InAddr): cstring {. @@ -628,13 +711,12 @@ const # Error Constants const - ERROR_FILE_NOT_FOUND* = 2 ## https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- + ERROR_FILE_NOT_FOUND* = 2 ERROR_PATH_NOT_FOUND* = 3 ERROR_ACCESS_DENIED* = 5 ERROR_NO_MORE_FILES* = 18 ERROR_LOCK_VIOLATION* = 33 ERROR_HANDLE_EOF* = 38 - ERROR_FILE_EXISTS* = 80 ERROR_BAD_ARGUMENTS* = 165 proc duplicateHandle*(hSourceProcessHandle: Handle, hSourceHandle: Handle, @@ -700,6 +782,13 @@ proc createFileMappingW*(hFile: Handle, lpName: pointer): Handle {. stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".} +when not useWinUnicode: + proc createFileMappingA*(hFile: Handle, + lpFileMappingAttributes: pointer, + flProtect, dwMaximumSizeHigh: DWORD, + dwMaximumSizeLow: DWORD, lpName: cstring): Handle {. + stdcall, dynlib: "kernel32", importc: "CreateFileMappingA".} + proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall, dynlib: "kernel32", importc: "UnmapViewOfFile".} @@ -719,7 +808,7 @@ type POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD, para3: POVERLAPPED){.stdcall.} - GUID* = object + GUID* {.final, pure.} = object D1*: int32 D2*: int16 D3*: int16 @@ -831,9 +920,6 @@ proc getProcessTimes*(hProcess: Handle; lpCreationTime, lpExitTime, lpKernelTime, lpUserTime: var FILETIME): WINBOOL {.stdcall, dynlib: "kernel32", importc: "GetProcessTimes".} -proc getSystemTimePreciseAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {. - importc: "GetSystemTimePreciseAsFileTime", dynlib: "kernel32", stdcall, sideEffect.} - type inet_ntop_proc = proc(family: cint, paddr: pointer, pStringBuffer: cstring, stringBufSize: int32): cstring {.gcsafe, stdcall, tags: [].} @@ -875,7 +961,7 @@ proc inet_ntop*(family: cint, paddr: pointer, pStringBuffer: cstring, stringBufSize: int32): cstring {.stdcall.} = var ver: OSVERSIONINFO ver.dwOSVersionInfoSize = sizeof(ver).DWORD - let res = getVersionExW(ver.addr) + let res = when useWinUnicode: getVersionExW(ver.addr) else: getVersionExA(ver.addr) if res == 0: result = nil elif ver.dwMajorVersion >= 6: @@ -937,7 +1023,7 @@ const PROCESS_QUERY_LIMITED_INFORMATION* = 0x00001000'i32 PROCESS_SET_LIMITED_INFORMATION* = 0x00002000'i32 type - WAITORTIMERCALLBACK* = proc(para1: pointer, para2: int32) {.stdcall.} + WAITORTIMERCALLBACK* = proc(para1: pointer, para2: int32): void {.stdcall.} proc postQueuedCompletionStatus*(CompletionPort: Handle, dwNumberOfBytesTransferred: DWORD, @@ -959,10 +1045,16 @@ proc openProcess*(dwDesiredAccess: DWORD, bInheritHandle: WINBOOL, dwProcessId: DWORD): Handle {.stdcall, dynlib: "kernel32", importc: "OpenProcess".} -proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES, - bManualReset: DWORD, bInitialState: DWORD, - lpName: ptr Utf16Char): Handle - {.stdcall, dynlib: "kernel32", importc: "CreateEventW".} +when defined(useWinAnsi): + proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES, + bManualReset: DWORD, bInitialState: DWORD, + lpName: cstring): Handle + {.stdcall, dynlib: "kernel32", importc: "CreateEventA".} +else: + proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES, + bManualReset: DWORD, bInitialState: DWORD, + lpName: ptr Utf16Char): Handle + {.stdcall, dynlib: "kernel32", importc: "CreateEventW".} proc setEvent*(hEvent: Handle): cint {.stdcall, dynlib: "kernel32", importc: "SetEvent".} @@ -994,7 +1086,7 @@ proc wsaResetEvent*(hEvent: Handle): bool {.stdcall, importc: "WSAResetEvent", dynlib: "ws2_32.dll".} type - KEY_EVENT_RECORD* = object + KEY_EVENT_RECORD* {.final, pure.} = object eventType*: int16 bKeyDown*: WINBOOL wRepeatCount*: int16 @@ -1003,12 +1095,17 @@ type uChar*: int16 dwControlKeyState*: DWORD -proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, - lpNumberOfEventsRead: ptr cint): cint - {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".} +when defined(useWinAnsi): + proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, + lpNumberOfEventsRead: ptr cint): cint + {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputA".} +else: + proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, + lpNumberOfEventsRead: ptr cint): cint + {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".} type - LPFIBER_START_ROUTINE* = proc (param: pointer) {.stdcall.} + LPFIBER_START_ROUTINE* = proc (param: pointer): void {.stdcall.} const FIBER_FLAG_FLOAT_SWITCH* = 0x01 @@ -1017,12 +1114,12 @@ proc CreateFiber*(stackSize: int, fn: LPFIBER_START_ROUTINE, param: pointer): po proc CreateFiberEx*(stkCommit: int, stkReserve: int, flags: int32, fn: LPFIBER_START_ROUTINE, param: pointer): pointer {.stdcall, discardable, dynlib: "kernel32", importc.} proc ConvertThreadToFiber*(param: pointer): pointer {.stdcall, discardable, dynlib: "kernel32", importc.} proc ConvertThreadToFiberEx*(param: pointer, flags: int32): pointer {.stdcall, discardable, dynlib: "kernel32", importc.} -proc DeleteFiber*(fiber: pointer) {.stdcall, discardable, dynlib: "kernel32", importc.} -proc SwitchToFiber*(fiber: pointer) {.stdcall, discardable, dynlib: "kernel32", importc.} +proc DeleteFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} +proc SwitchToFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "windows.h".} proc toFILETIME*(t: int64): FILETIME = - ## Convert the Windows file time timestamp `t` to `FILETIME`. + ## Convert the Windows file time timestamp ``t`` to ``FILETIME``. result = FILETIME(dwLowDateTime: cast[DWORD](t), dwHighDateTime: DWORD(t shr 32)) type @@ -1032,42 +1129,5 @@ proc setFileTime*(hFile: Handle, lpCreationTime: LPFILETIME, lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL {.stdcall, dynlib: "kernel32", importc: "SetFileTime".} -type - # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_identifier_authority - SID_IDENTIFIER_AUTHORITY* {.importc, header: "".} = object - value* {.importc: "Value".}: array[6, BYTE] - # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid - SID* {.importc, header: "".} = object - Revision: BYTE - SubAuthorityCount: BYTE - IdentifierAuthority: SID_IDENTIFIER_AUTHORITY - SubAuthority: ptr ptr DWORD - PSID* = ptr SID - -const - # https://docs.microsoft.com/en-us/windows/win32/secauthz/sid-components - # https://github.com/mirror/mingw-w64/blob/84c950bdab7c999ace49fe8383856be77f88c4a8/mingw-w64-headers/include/winnt.h#L2994 - SECURITY_NT_AUTHORITY* = [BYTE(0), BYTE(0), BYTE(0), BYTE(0), BYTE(0), BYTE(5)] - SECURITY_BUILTIN_DOMAIN_RID* = 32 - DOMAIN_ALIAS_RID_ADMINS* = 544 - -proc allocateAndInitializeSid*(pIdentifierAuthority: ptr SID_IDENTIFIER_AUTHORITY, - nSubAuthorityCount: BYTE, - nSubAuthority0: DWORD, - nSubAuthority1: DWORD, - nSubAuthority2: DWORD, - nSubAuthority3: DWORD, - nSubAuthority4: DWORD, - nSubAuthority5: DWORD, - nSubAuthority6: DWORD, - nSubAuthority7: DWORD, - pSid: ptr PSID): WINBOOL - {.stdcall, dynlib: "Advapi32", importc: "AllocateAndInitializeSid".} -proc checkTokenMembership*(tokenHandle: Handle, sidToCheck: PSID, - isMember: PBOOL): WINBOOL - {.stdcall, dynlib: "Advapi32", importc: "CheckTokenMembership".} -proc freeSid*(pSid: PSID): PSID - {.stdcall, dynlib: "Advapi32", importc: "FreeSid".} - when defined(nimHasStyleChecks): {.pop.} # {.push styleChecks: off.} diff --git a/lib/wrappers/linenoise/linenoise.c b/lib/wrappers/linenoise/linenoise.c index be792b96b..ae185fece 100644 --- a/lib/wrappers/linenoise/linenoise.c +++ b/lib/wrappers/linenoise/linenoise.c @@ -765,7 +765,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) { * when ctrl+d is typed. * * The function returns the length of the current buffer. */ -static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt, linenoiseData* data) +static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) { struct linenoiseState l; @@ -827,7 +827,6 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, return (int)l.len; case CTRL_C: /* ctrl-c */ errno = EAGAIN; - data->status = linenoiseStatus_ctrl_C; return -1; case BACKSPACE: /* backspace */ case 8: /* ctrl-h */ @@ -840,7 +839,6 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, } else { history_len--; free(history[history_len]); - data->status = linenoiseStatus_ctrl_D; return -1; } break; @@ -981,7 +979,7 @@ void linenoisePrintKeyCodes(void) { /* This function calls the line editing function linenoiseEdit() using * the STDIN file descriptor set in raw mode. */ -static int linenoiseRaw(char *buf, size_t buflen, const char *prompt, linenoiseData* data) { +static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { int count; if (buflen == 0) { @@ -990,7 +988,7 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt, linenoiseD } if (enableRawMode(STDIN_FILENO) == -1) return -1; - count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt, data); + count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); disableRawMode(STDIN_FILENO); printf("\n"); return count; @@ -1037,7 +1035,7 @@ static char *linenoiseNoTTY(void) { * for a blacklist of stupid terminals, and later either calls the line * editing function or uses dummy fgets() so that you will be able to type * something even in the most desperate of the conditions. */ -char *linenoiseExtra(const char *prompt, linenoiseData* data) { +char *linenoise(const char *prompt) { char buf[LINENOISE_MAX_LINE]; int count; @@ -1058,18 +1056,12 @@ char *linenoiseExtra(const char *prompt, linenoiseData* data) { } return strdup(buf); } else { - count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt, data); + count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); if (count == -1) return NULL; return strdup(buf); } } -char *linenoise(const char *prompt) { - linenoiseData data; - data.status = linenoiseStatus_ctrl_unknown; - return linenoiseExtra(prompt, &data); -} - /* This is just a wrapper the user may want to call in order to make sure * the linenoise returned buffer is freed with the same allocator it was * created with. Useful when the main program is using an alternative diff --git a/lib/wrappers/linenoise/linenoise.h b/lib/wrappers/linenoise/linenoise.h index aa86ccb78..ed20232c5 100644 --- a/lib/wrappers/linenoise/linenoise.h +++ b/lib/wrappers/linenoise/linenoise.h @@ -48,16 +48,6 @@ typedef struct linenoiseCompletions { char **cvec; } linenoiseCompletions; -typedef enum linenoiseStatus { - linenoiseStatus_ctrl_unknown, - linenoiseStatus_ctrl_C, - linenoiseStatus_ctrl_D -} linenoiseStatus; - -typedef struct linenoiseData { - linenoiseStatus status; -} linenoiseData; - typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); typedef void(linenoiseFreeHintsCallback)(void *); @@ -67,7 +57,6 @@ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); void linenoiseAddCompletion(linenoiseCompletions *, const char *); char *linenoise(const char *prompt); -char *linenoiseExtra(const char *prompt, linenoiseData* data); void linenoiseFree(void *ptr); int linenoiseHistoryAdd(const char *line); int linenoiseHistorySetMaxLen(int len); diff --git a/lib/wrappers/linenoise/linenoise.nim b/lib/wrappers/linenoise/linenoise.nim index 186b3b252..a6260eb12 100644 --- a/lib/wrappers/linenoise/linenoise.nim +++ b/lib/wrappers/linenoise/linenoise.nim @@ -9,14 +9,14 @@ type Completions* = object - len*: csize_t + len*: csize cvec*: cstringArray CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.} {.compile: "linenoise.c".} -proc setCompletionCallback*(a2: CompletionCallback) {. +proc setCompletionCallback*(a2: ptr CompletionCallback) {. importc: "linenoiseSetCompletionCallback".} proc addCompletion*(a2: ptr Completions; a3: cstring) {. importc: "linenoiseAddCompletion".} @@ -32,41 +32,3 @@ proc printKeyCodes*() {.importc: "linenoisePrintKeyCodes".} proc free*(s: cstring) {.importc: "free", header: "".} -when defined(nimExperimentalLinenoiseExtra) and not defined(windows): - # C interface - type LinenoiseStatus = enum - linenoiseStatus_ctrl_unknown - linenoiseStatus_ctrl_C - linenoiseStatus_ctrl_D - - type LinenoiseData* = object - status: LinenoiseStatus - - proc linenoiseExtra(prompt: cstring, data: ptr LinenoiseData): cstring {.importc.} - - # stable nim interface - type Status* = enum - lnCtrlUnkown - lnCtrlC - lnCtrlD - - type ReadLineResult* = object - line*: string - status*: Status - - proc readLineStatus*(prompt: string, result: var ReadLineResult) = - ## line editing API that allows returning the line entered and an indicator - ## of which control key was entered, allowing user to distinguish between - ## for example ctrl-C vs ctrl-D. - runnableExamples("-d:nimExperimentalLinenoiseExtra -r:off"): - var ret: ReadLineResult - while true: - readLineStatus("name: ", ret) # ctrl-D will exit, ctrl-C will go to next prompt - if ret.line.len > 0: echo ret.line - if ret.status == lnCtrlD: break - echo "exiting" - var data: LinenoiseData - let buf = linenoiseExtra(prompt, data.addr) - result.line = $buf - free(buf) - result.status = data.status.ord.Status diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim new file mode 100644 index 000000000..209f63c21 --- /dev/null +++ b/lib/wrappers/mysql.nim @@ -0,0 +1,1115 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2010 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +{.push, callconv: cdecl.} +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +when defined(Unix): + when defined(macosx): + const + lib = "(libmysqlclient|libmariadbclient)(|.20|.19|.18|.17|.16|.15).dylib" + else: + const + lib = "(libmysqlclient|libmariadbclient).so(|.20|.19|.18|.17|.16|.15)" +when defined(Windows): + const + lib = "(libmysql.dll|libmariadb.dll)" +type + my_bool* = bool + Pmy_bool* = ptr my_bool + PVIO* = pointer + Pgptr* = ptr gptr + gptr* = cstring + Pmy_socket* = ptr my_socket + my_socket* = cint + PPByte* = pointer + cuint* = cint + +# ------------ Start of declaration in "mysql_com.h" --------------------- +# +# ** Common definition between mysql server & client +# +# Field/table name length + +const + NAME_LEN* = 64 + HOSTNAME_LENGTH* = 60 + USERNAME_LENGTH* = 16 + SERVER_VERSION_LENGTH* = 60 + SQLSTATE_LENGTH* = 5 + LOCAL_HOST* = "localhost" + LOCAL_HOST_NAMEDPIPE* = '.' + +const + NAMEDPIPE* = "MySQL" + SERVICENAME* = "MySQL" + +type + Enum_server_command* = enum + COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, COM_CREATE_DB, + COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, COM_PROCESS_INFO, + COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, COM_TIME, + COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP, + COM_CONNECT_OUT, COM_REGISTER_SLAVE, COM_STMT_PREPARE, COM_STMT_EXECUTE, + COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE, COM_STMT_RESET, COM_SET_OPTION, + COM_STMT_FETCH, COM_END +{.deprecated: [Tenum_server_command: Enum_server_command].} + +const + SCRAMBLE_LENGTH* = 20 # Length of random string sent by server on handshake; + # this is also length of obfuscated password, + # received from client + SCRAMBLE_LENGTH_323* = 8 # length of password stored in the db: + # new passwords are preceded with '*' + SCRAMBLED_PASSWORD_CHAR_LENGTH* = SCRAMBLE_LENGTH * 2 + 1 + SCRAMBLED_PASSWORD_CHAR_LENGTH_323* = SCRAMBLE_LENGTH_323 * 2 + NOT_NULL_FLAG* = 1 # Field can't be NULL + PRI_KEY_FLAG* = 2 # Field is part of a primary key + UNIQUE_KEY_FLAG* = 4 # Field is part of a unique key + MULTIPLE_KEY_FLAG* = 8 # Field is part of a key + BLOB_FLAG* = 16 # Field is a blob + UNSIGNED_FLAG* = 32 # Field is unsigned + ZEROFILL_FLAG* = 64 # Field is zerofill + BINARY_FLAG* = 128 # Field is binary + # The following are only sent to new clients + ENUM_FLAG* = 256 # field is an enum + AUTO_INCREMENT_FLAG* = 512 # field is a autoincrement field + TIMESTAMP_FLAG* = 1024 # Field is a timestamp + SET_FLAG* = 2048 # field is a set + NO_DEFAULT_VALUE_FLAG* = 4096 # Field doesn't have default value + NUM_FLAG* = 32768 # Field is num (for clients) + PART_KEY_FLAG* = 16384 # Intern; Part of some key + GROUP_FLAG* = 32768 # Intern: Group field + UNIQUE_FLAG* = 65536 # Intern: Used by sql_yacc + BINCMP_FLAG* = 131072 # Intern: Used by sql_yacc + REFRESH_GRANT* = 1 # Refresh grant tables + REFRESH_LOG* = 2 # Start on new log file + REFRESH_TABLES* = 4 # close all tables + REFRESH_HOSTS* = 8 # Flush host cache + REFRESH_STATUS* = 16 # Flush status variables + REFRESH_THREADS* = 32 # Flush thread cache + REFRESH_SLAVE* = 64 # Reset master info and restart slave thread + REFRESH_MASTER* = 128 # Remove all bin logs in the index and truncate the index + # The following can't be set with mysql_refresh() + REFRESH_READ_LOCK* = 16384 # Lock tables for read + REFRESH_FAST* = 32768 # Intern flag + REFRESH_QUERY_CACHE* = 65536 # RESET (remove all queries) from query cache + REFRESH_QUERY_CACHE_FREE* = 0x00020000 # pack query cache + REFRESH_DES_KEY_FILE* = 0x00040000 + REFRESH_USER_RESOURCES* = 0x00080000 + CLIENT_LONG_PASSWORD* = 1 # new more secure passwords + CLIENT_FOUND_ROWS* = 2 # Found instead of affected rows + CLIENT_LONG_FLAG* = 4 # Get all column flags + CLIENT_CONNECT_WITH_DB* = 8 # One can specify db on connect + CLIENT_NO_SCHEMA* = 16 # Don't allow database.table.column + CLIENT_COMPRESS* = 32 # Can use compression protocol + CLIENT_ODBC* = 64 # Odbc client + CLIENT_LOCAL_FILES* = 128 # Can use LOAD DATA LOCAL + CLIENT_IGNORE_SPACE* = 256 # Ignore spaces before '(' + CLIENT_PROTOCOL_41* = 512 # New 4.1 protocol + CLIENT_INTERACTIVE* = 1024 # This is an interactive client + CLIENT_SSL* = 2048 # Switch to SSL after handshake + CLIENT_IGNORE_SIGPIPE* = 4096 # IGNORE sigpipes + CLIENT_TRANSACTIONS* = 8192 # Client knows about transactions + CLIENT_RESERVED* = 16384 # Old flag for 4.1 protocol + CLIENT_SECURE_CONNECTION* = 32768 # New 4.1 authentication + CLIENT_MULTI_STATEMENTS* = 65536 # Enable/disable multi-stmt support + CLIENT_MULTI_RESULTS* = 131072 # Enable/disable multi-results + CLIENT_REMEMBER_OPTIONS*: int = 1 shl 31 + SERVER_STATUS_IN_TRANS* = 1 # Transaction has started + SERVER_STATUS_AUTOCOMMIT* = 2 # Server in auto_commit mode + SERVER_STATUS_MORE_RESULTS* = 4 # More results on server + SERVER_MORE_RESULTS_EXISTS* = 8 # Multi query - next query exists + SERVER_QUERY_NO_GOOD_INDEX_USED* = 16 + SERVER_QUERY_NO_INDEX_USED* = 32 # The server was able to fulfill the clients request and opened a + # read-only non-scrollable cursor for a query. This flag comes + # in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. + SERVER_STATUS_CURSOR_EXISTS* = 64 # This flag is sent when a read-only cursor is exhausted, in reply to + # COM_STMT_FETCH command. + SERVER_STATUS_LAST_ROW_SENT* = 128 + SERVER_STATUS_DB_DROPPED* = 256 # A database was dropped + SERVER_STATUS_NO_BACKSLASH_ESCAPES* = 512 + ERRMSG_SIZE* = 200 + NET_READ_TIMEOUT* = 30 # Timeout on read + NET_WRITE_TIMEOUT* = 60 # Timeout on write + NET_WAIT_TIMEOUT* = 8 * 60 * 60 # Wait for new query + ONLY_KILL_QUERY* = 1 + +const + MAX_TINYINT_WIDTH* = 3 # Max width for a TINY w.o. sign + MAX_SMALLINT_WIDTH* = 5 # Max width for a SHORT w.o. sign + MAX_MEDIUMINT_WIDTH* = 8 # Max width for a INT24 w.o. sign + MAX_INT_WIDTH* = 10 # Max width for a LONG w.o. sign + MAX_BIGINT_WIDTH* = 20 # Max width for a LONGLONG + MAX_CHAR_WIDTH* = 255 # Max length for a CHAR column + MAX_BLOB_WIDTH* = 8192 # Default width for blob + +type + Pst_net* = ptr St_net + St_net*{.final.} = object + vio*: PVIO + buff*: cstring + buff_end*: cstring + write_pos*: cstring + read_pos*: cstring + fd*: my_socket # For Perl DBI/dbd + max_packet*: int + max_packet_size*: int + pkt_nr*: cuint + compress_pkt_nr*: cuint + write_timeout*: cuint + read_timeout*: cuint + retry_count*: cuint + fcntl*: cint + compress*: my_bool # The following variable is set if we are doing several queries in one + # command ( as in LOAD TABLE ... FROM MASTER ), + # and do not want to confuse the client with OK at the wrong time + remain_in_buf*: int + len*: int + buf_length*: int + where_b*: int + return_status*: ptr cint + reading_or_writing*: char + save_char*: cchar + no_send_ok*: my_bool # For SPs and other things that do multiple stmts + no_send_eof*: my_bool # For SPs' first version read-only cursors + no_send_error*: my_bool # Set if OK packet is already sent, and + # we do not need to send error messages + # Pointer to query object in query cache, do not equal NULL (0) for + # queries in cache that have not stored its results yet + # $endif + last_error*: array[0..(ERRMSG_SIZE) - 1, char] + sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char] + last_errno*: cuint + error*: char + query_cache_query*: gptr + report_error*: my_bool # We should report error (we have unreported error) + return_errno*: my_bool + + NET* = St_net + PNET* = ptr NET +{.deprecated: [Tst_net: St_net, TNET: NET].} + +const + packet_error* = - 1 + +type + Enum_field_types* = enum # For backward compatibility + TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE, + TYPE_NULL, TYPE_TIMESTAMP, TYPE_LONGLONG, TYPE_INT24, TYPE_DATE, TYPE_TIME, + TYPE_DATETIME, TYPE_YEAR, TYPE_NEWDATE, TYPE_VARCHAR, TYPE_BIT, + TYPE_NEWDECIMAL = 246, TYPE_ENUM = 247, TYPE_SET = 248, + TYPE_TINY_BLOB = 249, TYPE_MEDIUM_BLOB = 250, TYPE_LONG_BLOB = 251, + TYPE_BLOB = 252, TYPE_VAR_STRING = 253, TYPE_STRING = 254, + TYPE_GEOMETRY = 255 +{.deprecated: [Tenum_field_types: Enum_field_types].} + +const + CLIENT_MULTI_QUERIES* = CLIENT_MULTI_STATEMENTS + FIELD_TYPE_DECIMAL* = TYPE_DECIMAL + FIELD_TYPE_NEWDECIMAL* = TYPE_NEWDECIMAL + FIELD_TYPE_TINY* = TYPE_TINY + FIELD_TYPE_SHORT* = TYPE_SHORT + FIELD_TYPE_LONG* = TYPE_LONG + FIELD_TYPE_FLOAT* = TYPE_FLOAT + FIELD_TYPE_DOUBLE* = TYPE_DOUBLE + FIELD_TYPE_NULL* = TYPE_NULL + FIELD_TYPE_TIMESTAMP* = TYPE_TIMESTAMP + FIELD_TYPE_LONGLONG* = TYPE_LONGLONG + FIELD_TYPE_INT24* = TYPE_INT24 + FIELD_TYPE_DATE* = TYPE_DATE + FIELD_TYPE_TIME* = TYPE_TIME + FIELD_TYPE_DATETIME* = TYPE_DATETIME + FIELD_TYPE_YEAR* = TYPE_YEAR + FIELD_TYPE_NEWDATE* = TYPE_NEWDATE + FIELD_TYPE_ENUM* = TYPE_ENUM + FIELD_TYPE_SET* = TYPE_SET + FIELD_TYPE_TINY_BLOB* = TYPE_TINY_BLOB + FIELD_TYPE_MEDIUM_BLOB* = TYPE_MEDIUM_BLOB + FIELD_TYPE_LONG_BLOB* = TYPE_LONG_BLOB + FIELD_TYPE_BLOB* = TYPE_BLOB + FIELD_TYPE_VAR_STRING* = TYPE_VAR_STRING + FIELD_TYPE_STRING* = TYPE_STRING + FIELD_TYPE_CHAR* = TYPE_TINY + FIELD_TYPE_INTERVAL* = TYPE_ENUM + FIELD_TYPE_GEOMETRY* = TYPE_GEOMETRY + FIELD_TYPE_BIT* = TYPE_BIT # Shutdown/kill enums and constants + # Bits for THD::killable. + SHUTDOWN_KILLABLE_CONNECT* = chr(1 shl 0) + SHUTDOWN_KILLABLE_TRANS* = chr(1 shl 1) + SHUTDOWN_KILLABLE_LOCK_TABLE* = chr(1 shl 2) + SHUTDOWN_KILLABLE_UPDATE* = chr(1 shl 3) + +type + Enum_shutdown_level* = enum + SHUTDOWN_DEFAULT = 0, SHUTDOWN_WAIT_CONNECTIONS = 1, + SHUTDOWN_WAIT_TRANSACTIONS = 2, SHUTDOWN_WAIT_UPDATES = 8, + SHUTDOWN_WAIT_ALL_BUFFERS = 16, SHUTDOWN_WAIT_CRITICAL_BUFFERS = 17, + KILL_QUERY = 254, KILL_CONNECTION = 255 + Enum_cursor_type* = enum # options for mysql_set_option + CURSOR_TYPE_NO_CURSOR = 0, CURSOR_TYPE_READ_ONLY = 1, + CURSOR_TYPE_FOR_UPDATE = 2, CURSOR_TYPE_SCROLLABLE = 4 + Enum_mysql_set_option* = enum + OPTION_MULTI_STATEMENTS_ON, OPTION_MULTI_STATEMENTS_OFF +{.deprecated: [Tenum_shutdown_level: Enum_shutdown_level, + Tenum_cursor_type: Enum_cursor_type, + Tenum_mysql_set_option: Enum_mysql_set_option].} + +proc my_net_init*(net: PNET, vio: PVIO): my_bool{.cdecl, dynlib: lib, + importc: "my_net_init".} +proc my_net_local_init*(net: PNET){.cdecl, dynlib: lib, + importc: "my_net_local_init".} +proc net_end*(net: PNET){.cdecl, dynlib: lib, importc: "net_end".} +proc net_clear*(net: PNET){.cdecl, dynlib: lib, importc: "net_clear".} +proc net_realloc*(net: PNET, len: int): my_bool{.cdecl, dynlib: lib, + importc: "net_realloc".} +proc net_flush*(net: PNET): my_bool{.cdecl, dynlib: lib, importc: "net_flush".} +proc my_net_write*(net: PNET, packet: cstring, length: int): my_bool{.cdecl, + dynlib: lib, importc: "my_net_write".} +proc net_write_command*(net: PNET, command: char, header: cstring, + head_len: int, packet: cstring, length: int): my_bool{. + cdecl, dynlib: lib, importc: "net_write_command".} +proc net_real_write*(net: PNET, packet: cstring, length: int): cint{.cdecl, + dynlib: lib, importc: "net_real_write".} +proc my_net_read*(net: PNET): int{.cdecl, dynlib: lib, importc: "my_net_read".} + # The following function is not meant for normal usage + # Currently it's used internally by manager.c +type + Psockaddr* = ptr Sockaddr + Sockaddr*{.final.} = object # undefined structure +{.deprecated: [Tsockaddr: Sockaddr].} + +proc my_connect*(s: my_socket, name: Psockaddr, namelen: cuint, timeout: cuint): cint{. + cdecl, dynlib: lib, importc: "my_connect".} +type + Prand_struct* = ptr Rand_struct + Rand_struct*{.final.} = object # The following is for user defined functions + seed1*: int + seed2*: int + max_value*: int + max_value_dbl*: cdouble + + Item_result* = enum + STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT + PItem_result* = ptr Item_result + Pst_udf_args* = ptr St_udf_args + St_udf_args*{.final.} = object + arg_count*: cuint # Number of arguments + arg_type*: PItem_result # Pointer to item_results + args*: cstringArray # Pointer to item_results + lengths*: ptr int # Length of string arguments + maybe_null*: cstring # Length of string arguments + attributes*: cstringArray # Pointer to attribute name + attribute_lengths*: ptr int # Length of attribute arguments + + UDF_ARGS* = St_udf_args + PUDF_ARGS* = ptr UDF_ARGS # This holds information about the result + Pst_udf_init* = ptr St_udf_init + St_udf_init*{.final.} = object + maybe_null*: my_bool # 1 if function can return NULL + decimals*: cuint # for real functions + max_length*: int # For string functions + theptr*: cstring # free pointer for function data + const_item*: my_bool # free pointer for function data + + UDF_INIT* = St_udf_init + PUDF_INIT* = ptr UDF_INIT # Constants when using compression +{.deprecated: [Trand_stuct: Rand_struct, TItem_result: Item_result, + Tst_udf_args: St_udf_args, TUDF_ARGS: UDF_ARGS, + Tst_udf_init: St_udf_init, TUDF_INIT: UDF_INIT].} + +const + NET_HEADER_SIZE* = 4 # standard header size + COMP_HEADER_SIZE* = 3 # compression header extra size + # Prototypes to password functions + # These functions are used for authentication by client and server and + # implemented in sql/password.c + +proc randominit*(para1: Prand_struct, seed1: int, seed2: int){.cdecl, + dynlib: lib, importc: "randominit".} +proc my_rnd*(para1: Prand_struct): cdouble{.cdecl, dynlib: lib, + importc: "my_rnd".} +proc create_random_string*(fto: cstring, len: cuint, rand_st: Prand_struct){. + cdecl, dynlib: lib, importc: "create_random_string".} +proc hash_password*(fto: int, password: cstring, password_len: cuint){.cdecl, + dynlib: lib, importc: "hash_password".} +proc make_scrambled_password_323*(fto: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "make_scrambled_password_323".} +proc scramble_323*(fto: cstring, message: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "scramble_323".} +proc check_scramble_323*(para1: cstring, message: cstring, salt: int): my_bool{. + cdecl, dynlib: lib, importc: "check_scramble_323".} +proc get_salt_from_password_323*(res: ptr int, password: cstring){.cdecl, + dynlib: lib, importc: "get_salt_from_password_323".} +proc make_password_from_salt_323*(fto: cstring, salt: ptr int){.cdecl, + dynlib: lib, importc: "make_password_from_salt_323".} +proc octet2hex*(fto: cstring, str: cstring, length: cuint): cstring{.cdecl, + dynlib: lib, importc: "octet2hex".} +proc make_scrambled_password*(fto: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "make_scrambled_password".} +proc scramble*(fto: cstring, message: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "scramble".} +proc check_scramble*(reply: cstring, message: cstring, hash_stage2: pointer): my_bool{. + cdecl, dynlib: lib, importc: "check_scramble".} +proc get_salt_from_password*(res: pointer, password: cstring){.cdecl, + dynlib: lib, importc: "get_salt_from_password".} +proc make_password_from_salt*(fto: cstring, hash_stage2: pointer){.cdecl, + dynlib: lib, importc: "make_password_from_salt".} + # end of password.c +proc get_tty_password*(opt_message: cstring): cstring{.cdecl, dynlib: lib, + importc: "get_tty_password".} +proc errno_to_sqlstate*(errno: cuint): cstring{.cdecl, dynlib: lib, + importc: "mysql_errno_to_sqlstate".} + # Some other useful functions +proc modify_defaults_file*(file_location: cstring, option: cstring, + option_value: cstring, section_name: cstring, + remove_option: cint): cint{.cdecl, dynlib: lib, + importc: "load_defaults".} +proc load_defaults*(conf_file: cstring, groups: cstringArray, argc: ptr cint, + argv: ptr cstringArray): cint{.cdecl, dynlib: lib, + importc: "load_defaults".} +proc my_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_init".} +proc my_thread_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_thread_init".} +proc my_thread_end*(){.cdecl, dynlib: lib, importc: "my_thread_end".} +const + NULL_LENGTH*: int = int(not (0)) # For net_store_length + +const + STMT_HEADER* = 4 + LONG_DATA_HEADER* = 6 # ------------ Stop of declaration in "mysql_com.h" ----------------------- + # $include "mysql_time.h" + # $include "mysql_version.h" + # $include "typelib.h" + # $include "my_list.h" /* for LISTs used in 'MYSQL' and 'MYSQL_STMT' */ + # var + # mysql_port : cuint;cvar;external; + # mysql_unix_port : Pchar;cvar;external; + +const + CLIENT_NET_READ_TIMEOUT* = 365 * 24 * 3600 # Timeout on read + CLIENT_NET_WRITE_TIMEOUT* = 365 * 24 * 3600 # Timeout on write + +type + Pst_mysql_field* = ptr St_mysql_field + St_mysql_field*{.final.} = object + name*: cstring # Name of column + org_name*: cstring # Original column name, if an alias + table*: cstring # Table of column if column was a field + org_table*: cstring # Org table name, if table was an alias + db*: cstring # Database for table + catalog*: cstring # Catalog for table + def*: cstring # Default value (set by mysql_list_fields) + len*: int # Width of column (create length) + max_length*: int # Max width for selected set + name_length*: cuint + org_name_length*: cuint + table_length*: cuint + org_table_length*: cuint + db_length*: cuint + catalog_length*: cuint + def_length*: cuint + flags*: cuint # Div flags + decimals*: cuint # Number of decimals in field + charsetnr*: cuint # Character set + ftype*: Enum_field_types # Type of field. See mysql_com.h for types + extension*: pointer + + FIELD* = St_mysql_field + PFIELD* = ptr FIELD + PROW* = ptr ROW # return data as array of strings + ROW* = cstringArray + PFIELD_OFFSET* = ptr FIELD_OFFSET # offset to current field + FIELD_OFFSET* = cuint +{.deprecated: [Tst_mysql_field: St_mysql_field, TFIELD: FIELD, TROW: ROW, + TFIELD_OFFSET: FIELD_OFFSET].} + +proc IS_PRI_KEY*(n: int32): bool +proc IS_NOT_NULL*(n: int32): bool +proc IS_BLOB*(n: int32): bool +proc IS_NUM*(t: Enum_field_types): bool +proc INTERNAL_NUM_FIELD*(f: Pst_mysql_field): bool +proc IS_NUM_FIELD*(f: Pst_mysql_field): bool +type + my_ulonglong* = int64 + Pmy_ulonglong* = ptr my_ulonglong + +const + COUNT_ERROR* = not (my_ulonglong(0)) + +type + Pst_mysql_rows* = ptr St_mysql_rows + St_mysql_rows*{.final.} = object + next*: Pst_mysql_rows # list of rows + data*: ROW + len*: int + + ROWS* = St_mysql_rows + PROWS* = ptr ROWS + PROW_OFFSET* = ptr ROW_OFFSET # offset to current row + ROW_OFFSET* = ROWS +{.deprecated: [Tst_mysql_rows: St_mysql_rows, TROWS: ROWS, + TROW_OFFSET: ROW_OFFSET].} + +const + ALLOC_MAX_BLOCK_TO_DROP* = 4096 + ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP* = 10 # struct for once_alloc (block) + +type + Pst_used_mem* = ptr St_used_mem + St_used_mem*{.final.} = object + next*: Pst_used_mem # Next block in use + left*: cuint # memory left in block + size*: cuint # size of block + + USED_MEM* = St_used_mem + PUSED_MEM* = ptr USED_MEM + Pst_mem_root* = ptr St_mem_root + St_mem_root*{.final.} = object + free*: PUSED_MEM # blocks with free memory in it + used*: PUSED_MEM # blocks almost without free memory + pre_alloc*: PUSED_MEM # preallocated block + min_malloc*: cuint # if block have less memory it will be put in 'used' list + block_size*: cuint # initial block size + block_num*: cuint # allocated blocks counter + # first free block in queue test counter (if it exceed + # MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list) + first_block_usage*: cuint + error_handler*: proc (){.cdecl.} + + MEM_ROOT* = St_mem_root + PMEM_ROOT* = ptr MEM_ROOT # ------------ Stop of declaration in "my_alloc.h" ---------------------- +{.deprecated: [Tst_used_mem: St_used_mem, TUSED_MEM: USED_MEM, + Tst_mem_root: St_mem_root, TMEM_ROOT: MEM_ROOT].} + +type + Pst_mysql_data* = ptr St_mysql_data + St_mysql_data*{.final.} = object + rows*: my_ulonglong + fields*: cuint + data*: PROWS + alloc*: MEM_ROOT + prev_ptr*: ptr PROWS + + DATA* = St_mysql_data + PDATA* = ptr DATA + Option* = enum + OPT_CONNECT_TIMEOUT, OPT_COMPRESS, OPT_NAMED_PIPE, INIT_COMMAND, + READ_DEFAULT_FILE, READ_DEFAULT_GROUP, SET_CHARSET_DIR, SET_CHARSET_NAME, + OPT_LOCAL_INFILE, OPT_PROTOCOL, SHARED_MEMORY_BASE_NAME, OPT_READ_TIMEOUT, + OPT_WRITE_TIMEOUT, OPT_USE_RESULT, OPT_USE_REMOTE_CONNECTION, + OPT_USE_EMBEDDED_CONNECTION, OPT_GUESS_CONNECTION, SET_CLIENT_IP, + SECURE_AUTH, REPORT_DATA_TRUNCATION, OPT_RECONNECT +{.deprecated: [Tst_mysql_data: St_mysql_data, TDATA: DATA, Toption: Option].} + +const + MAX_MYSQL_MANAGER_ERR* = 256 + MAX_MYSQL_MANAGER_MSG* = 256 + MANAGER_OK* = 200 + MANAGER_INFO* = 250 + MANAGER_ACCESS* = 401 + MANAGER_CLIENT_ERR* = 450 + MANAGER_INTERNAL_ERR* = 500 + +type + St_dynamic_array*{.final.} = object + buffer*: cstring + elements*: cuint + max_element*: cuint + alloc_increment*: cuint + size_of_element*: cuint + + DYNAMIC_ARRAY* = St_dynamic_array + Pst_dynamic_array* = ptr St_dynamic_array + Pst_mysql_options* = ptr St_mysql_options + St_mysql_options*{.final.} = object + connect_timeout*: cuint + read_timeout*: cuint + write_timeout*: cuint + port*: cuint + protocol*: cuint + client_flag*: int + host*: cstring + user*: cstring + password*: cstring + unix_socket*: cstring + db*: cstring + init_commands*: Pst_dynamic_array + my_cnf_file*: cstring + my_cnf_group*: cstring + charset_dir*: cstring + charset_name*: cstring + ssl_key*: cstring # PEM key file + ssl_cert*: cstring # PEM cert file + ssl_ca*: cstring # PEM CA file + ssl_capath*: cstring # PEM directory of CA-s? + ssl_cipher*: cstring # cipher to use + shared_memory_base_name*: cstring + max_allowed_packet*: int + use_ssl*: my_bool # if to use SSL or not + compress*: my_bool + named_pipe*: my_bool # On connect, find out the replication role of the server, and + # establish connections to all the peers + rpl_probe*: my_bool # Each call to mysql_real_query() will parse it to tell if it is a read + # or a write, and direct it to the slave or the master + rpl_parse*: my_bool # If set, never read from a master, only from slave, when doing + # a read that is replication-aware + no_master_reads*: my_bool + separate_thread*: my_bool + methods_to_use*: Option + client_ip*: cstring + secure_auth*: my_bool # Refuse client connecting to server if it uses old (pre-4.1.1) protocol + report_data_truncation*: my_bool # 0 - never report, 1 - always report (default) + # function pointers for local infile support + local_infile_init*: proc (para1: var pointer, para2: cstring, para3: pointer): cint{. + cdecl.} + local_infile_read*: proc (para1: pointer, para2: cstring, para3: cuint): cint + local_infile_end*: proc (para1: pointer) + local_infile_error*: proc (para1: pointer, para2: cstring, para3: cuint): cint + local_infile_userdata*: pointer + + Status* = enum + STATUS_READY, STATUS_GET_RESULT, STATUS_USE_RESULT + Protocol_type* = enum # There are three types of queries - the ones that have to go to + # the master, the ones that go to a slave, and the administrative + # type which must happen on the pivot connection + PROTOCOL_DEFAULT, PROTOCOL_TCP, PROTOCOL_SOCKET, PROTOCOL_PIPE, + PROTOCOL_MEMORY + Rpl_type* = enum + RPL_MASTER, RPL_SLAVE, RPL_ADMIN + Charset_info_st*{.final.} = object + number*: cuint + primary_number*: cuint + binary_number*: cuint + state*: cuint + csname*: cstring + name*: cstring + comment*: cstring + tailoring*: cstring + ftype*: cstring + to_lower*: cstring + to_upper*: cstring + sort_order*: cstring + contractions*: ptr int16 + sort_order_big*: ptr ptr int16 + tab_to_uni*: ptr int16 + tab_from_uni*: pointer # was ^MY_UNI_IDX + state_map*: cstring + ident_map*: cstring + strxfrm_multiply*: cuint + mbminlen*: cuint + mbmaxlen*: cuint + min_sort_char*: int16 + max_sort_char*: int16 + escape_with_backslash_is_dangerous*: my_bool + cset*: pointer # was ^MY_CHARSET_HANDLER + coll*: pointer # was ^MY_COLLATION_HANDLER; + + CHARSET_INFO* = Charset_info_st + Pcharset_info_st* = ptr Charset_info_st + Pcharacter_set* = ptr Character_set + Character_set*{.final.} = object + number*: cuint + state*: cuint + csname*: cstring + name*: cstring + comment*: cstring + dir*: cstring + mbminlen*: cuint + mbmaxlen*: cuint + + MY_CHARSET_INFO* = Character_set + PMY_CHARSET_INFO* = ptr MY_CHARSET_INFO + Pst_mysql_methods* = ptr St_mysql_methods + Pst_mysql* = ptr St_mysql + St_mysql*{.final.} = object + net*: NET # Communication parameters + connector_fd*: gptr # ConnectorFd for SSL + host*: cstring + user*: cstring + passwd*: cstring + unix_socket*: cstring + server_version*: cstring + host_info*: cstring + info*: cstring + db*: cstring + charset*: Pcharset_info_st + fields*: PFIELD + field_alloc*: MEM_ROOT + affected_rows*: my_ulonglong + insert_id*: my_ulonglong # id if insert on table with NEXTNR + extra_info*: my_ulonglong # Used by mysqlshow, not used by mysql 5.0 and up + thread_id*: int # Id for connection in server + packet_length*: int + port*: cuint + client_flag*: int + server_capabilities*: int + protocol_version*: cuint + field_count*: cuint + server_status*: cuint + server_language*: cuint + warning_count*: cuint + options*: St_mysql_options + status*: Status + free_me*: my_bool # If free in mysql_close + reconnect*: my_bool # set to 1 if automatic reconnect + scramble*: array[0..(SCRAMBLE_LENGTH + 1) - 1, char] # session-wide random string + # Set if this is the original connection, not a master or a slave we have + # added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave() + rpl_pivot*: my_bool # Pointers to the master, and the next slave connections, points to + # itself if lone connection. + master*: Pst_mysql + next_slave*: Pst_mysql + last_used_slave*: Pst_mysql # needed for round-robin slave pick + last_used_con*: Pst_mysql # needed for send/read/store/use result to work correctly with replication + stmts*: pointer # was PList, list of all statements + methods*: Pst_mysql_methods + thd*: pointer # Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag + # from mysql_stmt_close if close had to cancel result set of this object. + unbuffered_fetch_owner*: Pmy_bool + + MySQL* = St_mysql + PMySQL* = ptr MySQL + Pst_mysql_res* = ptr St_mysql_res + St_mysql_res*{.final.} = object + row_count*: my_ulonglong + fields*: PFIELD + data*: PDATA + data_cursor*: PROWS + lengths*: ptr int # column lengths of current row + handle*: PMySQL # for unbuffered reads + field_alloc*: MEM_ROOT + field_count*: cuint + current_field*: cuint + row*: ROW # If unbuffered read + current_row*: ROW # buffer to current row + eof*: my_bool # Used by mysql_fetch_row + unbuffered_fetch_cancelled*: my_bool # mysql_stmt_close() had to cancel this result + methods*: Pst_mysql_methods + + RES* = St_mysql_res + PRES* = ptr RES + Pst_mysql_stmt* = ptr St_mysql_stmt + PSTMT* = ptr STMT + St_mysql_methods*{.final.} = object + read_query_result*: proc (MySQL: PMySQL): my_bool{.cdecl.} + advanced_command*: proc (MySQL: PMySQL, command: Enum_server_command, header: cstring, + header_length: int, arg: cstring, arg_length: int, + skip_check: my_bool): my_bool + read_rows*: proc (MySQL: PMySQL, fields: PFIELD, fields_count: cuint): PDATA + use_result*: proc (MySQL: PMySQL): PRES + fetch_lengths*: proc (fto: ptr int, column: ROW, field_count: cuint) + flush_use_result*: proc (MySQL: PMySQL) + list_fields*: proc (MySQL: PMySQL): PFIELD + read_prepare_result*: proc (MySQL: PMySQL, stmt: PSTMT): my_bool + stmt_execute*: proc (stmt: PSTMT): cint + read_binary_rows*: proc (stmt: PSTMT): cint + unbuffered_fetch*: proc (MySQL: PMySQL, row: cstringArray): cint + free_embedded_thd*: proc (MySQL: PMySQL) + read_statistics*: proc (MySQL: PMySQL): cstring + next_result*: proc (MySQL: PMySQL): my_bool + read_change_user_result*: proc (MySQL: PMySQL, buff: cstring, passwd: cstring): cint + read_rowsfrom_cursor*: proc (stmt: PSTMT): cint + + METHODS* = St_mysql_methods + PMETHODS* = ptr METHODS + Pst_mysql_manager* = ptr St_mysql_manager + St_mysql_manager*{.final.} = object + net*: NET + host*: cstring + user*: cstring + passwd*: cstring + port*: cuint + free_me*: my_bool + eof*: my_bool + cmd_status*: cint + last_errno*: cint + net_buf*: cstring + net_buf_pos*: cstring + net_data_end*: cstring + net_buf_size*: cint + last_error*: array[0..(MAX_MYSQL_MANAGER_ERR) - 1, char] + + MANAGER* = St_mysql_manager + PMANAGER* = ptr MANAGER + Pst_mysql_parameters* = ptr St_mysql_parameters + St_mysql_parameters*{.final.} = object + p_max_allowed_packet*: ptr int + p_net_buffer_length*: ptr int + + PARAMETERS* = St_mysql_parameters + PPARAMETERS* = ptr PARAMETERS + Enum_mysql_stmt_state* = enum + STMT_INIT_DONE = 1, STMT_PREPARE_DONE, STMT_EXECUTE_DONE, STMT_FETCH_DONE + Pst_mysql_bind* = ptr St_mysql_bind + St_mysql_bind*{.final.} = object + len*: int # output length pointer + is_null*: Pmy_bool # Pointer to null indicator + buffer*: pointer # buffer to get/put data + error*: Pmy_bool # set this if you want to track data truncations happened during fetch + buffer_type*: Enum_field_types # buffer type + buffer_length*: int # buffer length, must be set for str/binary + # Following are for internal use. Set by mysql_stmt_bind_param + row_ptr*: ptr byte # for the current data position + offset*: int # offset position for char/binary fetch + length_value*: int # Used if length is 0 + param_number*: cuint # For null count and error messages + pack_length*: cuint # Internal length for packed data + error_value*: my_bool # used if error is 0 + is_unsigned*: my_bool # set if integer type is unsigned + long_data_used*: my_bool # If used with mysql_send_long_data + is_null_value*: my_bool # Used if is_null is 0 + store_param_func*: proc (net: PNET, param: Pst_mysql_bind){.cdecl.} + fetch_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte) + skip_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte) + + BIND* = St_mysql_bind + PBIND* = ptr BIND # statement handler + St_mysql_stmt*{.final.} = object + mem_root*: MEM_ROOT # root allocations + mysql*: PMySQL # connection handle + params*: PBIND # input parameters + `bind`*: PBIND # input parameters + fields*: PFIELD # result set metadata + result*: DATA # cached result set + data_cursor*: PROWS # current row in cached result + affected_rows*: my_ulonglong # copy of mysql->affected_rows after statement execution + insert_id*: my_ulonglong + read_row_func*: proc (stmt: Pst_mysql_stmt, row: PPByte): cint{.cdecl.} + stmt_id*: int # Id for prepared statement + flags*: int # i.e. type of cursor to open + prefetch_rows*: int # number of rows per one COM_FETCH + server_status*: cuint # Copied from mysql->server_status after execute/fetch to know + # server-side cursor status for this statement. + last_errno*: cuint # error code + param_count*: cuint # input parameter count + field_count*: cuint # number of columns in result set + state*: Enum_mysql_stmt_state # statement state + last_error*: array[0..(ERRMSG_SIZE) - 1, char] # error message + sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char] + send_types_to_server*: my_bool # Types of input parameters should be sent to server + bind_param_done*: my_bool # input buffers were supplied + bind_result_done*: char # output buffers were supplied + unbuffered_fetch_cancelled*: my_bool + update_max_length*: my_bool + + STMT* = St_mysql_stmt + + Enum_stmt_attr_type* = enum + STMT_ATTR_UPDATE_MAX_LENGTH, STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS +{.deprecated: [Tst_dynamic_array: St_dynamic_array, Tst_mysql_options: St_mysql_options, + TDYNAMIC_ARRAY: DYNAMIC_ARRAY, Tprotocol_type: Protocol_type, + Trpl_type: Rpl_type, Tcharset_info_st: Charset_info_st, + TCHARSET_INFO: CHARSET_INFO, Tcharacter_set: Character_set, + TMY_CHARSET_INFO: MY_CHARSET_INFO, Tst_mysql: St_mysql, + Tst_mysql_methods: St_mysql_methods, TMySql: MySql, + Tst_mysql_res: St_mysql_res, TMETHODS: METHODS, TRES: RES, + Tst_mysql_manager: St_mysql_manager, TMANAGER: MANAGER, + Tst_mysql_parameters: St_mysql_parameters, TPARAMETERS: PARAMETERS, + Tenum_mysql_stmt_state: Enum_mysql_stmt_state, + Tst_mysql_bind: St_mysql_bind, TBIND: BIND, Tst_mysql_stmt: St_mysql_stmt, + TSTMT: STMT, Tenum_stmt_attr_type: Enum_stmt_attr_type, + Tstatus: Status].} + +proc server_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. + cdecl, dynlib: lib, importc: "mysql_server_init".} +proc server_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} + # mysql_server_init/end need to be called when using libmysqld or + # libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so + # you don't need to call it explicitly; but you need to call + # mysql_server_end() to free memory). The names are a bit misleading + # (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general + # names which suit well whether you're using libmysqld or libmysqlclient. We + # intend to promote these aliases over the mysql_server* ones. +proc library_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. + cdecl, dynlib: lib, importc: "mysql_server_init".} +proc library_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} +proc get_parameters*(): PPARAMETERS{.stdcall, dynlib: lib, + importc: "mysql_get_parameters".} + # Set up and bring down a thread; these function should be called + # for each thread in an application which opens at least one MySQL + # connection. All uses of the connection(s) should be between these + # function calls. +proc thread_init*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_thread_init".} +proc thread_end*(){.stdcall, dynlib: lib, importc: "mysql_thread_end".} + # Functions to get information from the MYSQL and MYSQL_RES structures + # Should definitely be used if one uses shared libraries. +proc num_rows*(res: PRES): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_num_rows".} +proc num_fields*(res: PRES): cuint{.stdcall, dynlib: lib, + importc: "mysql_num_fields".} +proc eof*(res: PRES): my_bool{.stdcall, dynlib: lib, importc: "mysql_eof".} +proc fetch_field_direct*(res: PRES, fieldnr: cuint): PFIELD{.stdcall, + dynlib: lib, importc: "mysql_fetch_field_direct".} +proc fetch_fields*(res: PRES): PFIELD{.stdcall, dynlib: lib, + importc: "mysql_fetch_fields".} +proc row_tell*(res: PRES): ROW_OFFSET{.stdcall, dynlib: lib, + importc: "mysql_row_tell".} +proc field_tell*(res: PRES): FIELD_OFFSET{.stdcall, dynlib: lib, + importc: "mysql_field_tell".} +proc field_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_field_count".} +proc affected_rows*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_affected_rows".} +proc insert_id*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_insert_id".} +proc errno*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, importc: "mysql_errno".} +proc error*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_error".} +proc sqlstate*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_sqlstate".} +proc warning_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_warning_count".} +proc info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_info".} +proc thread_id*(MySQL: PMySQL): int{.stdcall, dynlib: lib, importc: "mysql_thread_id".} +proc character_set_name*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, + importc: "mysql_character_set_name".} +proc set_character_set*(MySQL: PMySQL, csname: cstring): int32{.stdcall, dynlib: lib, + importc: "mysql_set_character_set".} +proc init*(MySQL: PMySQL): PMySQL{.stdcall, dynlib: lib, importc: "mysql_init".} +proc ssl_set*(MySQL: PMySQL, key: cstring, cert: cstring, ca: cstring, capath: cstring, + cipher: cstring): my_bool{.stdcall, dynlib: lib, + importc: "mysql_ssl_set".} +proc change_user*(MySQL: PMySQL, user: cstring, passwd: cstring, db: cstring): my_bool{. + stdcall, dynlib: lib, importc: "mysql_change_user".} +proc real_connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring, + db: cstring, port: cuint, unix_socket: cstring, + clientflag: int): PMySQL{.stdcall, dynlib: lib, + importc: "mysql_real_connect".} +proc select_db*(MySQL: PMySQL, db: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_select_db".} +proc query*(MySQL: PMySQL, q: cstring): cint{.stdcall, dynlib: lib, importc: "mysql_query".} +proc send_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, + importc: "mysql_send_query".} +proc real_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, + importc: "mysql_real_query".} +proc store_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, + importc: "mysql_store_result".} +proc use_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, importc: "mysql_use_result".} + # perform query on master +proc master_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, + importc: "mysql_master_query".} +proc master_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, + dynlib: lib, importc: "mysql_master_send_query".} + # perform query on slave +proc slave_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, + importc: "mysql_slave_query".} +proc slave_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, + dynlib: lib, importc: "mysql_slave_send_query".} +proc get_character_set_info*(MySQL: PMySQL, charset: PMY_CHARSET_INFO){.stdcall, + dynlib: lib, importc: "mysql_get_character_set_info".} + # local infile support +const + LOCAL_INFILE_ERROR_LEN* = 512 + +# procedure mysql_set_local_infile_handler(mysql:PMYSQL; local_infile_init:function (para1:Ppointer; para2:Pchar; para3:pointer):longint; local_infile_read:function (para1:pointer; para2:Pchar; para3:dword):longint; local_infile_end:procedure (_pa +# para6:pointer);cdecl;external mysqllib name 'mysql_set_local_infile_handler'; + +proc set_local_infile_default*(MySQL: PMySQL){.cdecl, dynlib: lib, + importc: "mysql_set_local_infile_default".} + # enable/disable parsing of all queries to decide if they go on master or + # slave +proc enable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, + importc: "mysql_enable_rpl_parse".} +proc disable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, + importc: "mysql_disable_rpl_parse".} + # get the value of the parse flag +proc rpl_parse_enabled*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, + importc: "mysql_rpl_parse_enabled".} + # enable/disable reads from master +proc enable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, + importc: "mysql_enable_reads_from_master".} +proc disable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, importc: "mysql_disable_reads_from_master".} + # get the value of the master read flag +proc reads_from_master_enabled*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_reads_from_master_enabled".} +proc rpl_query_type*(q: cstring, length: cint): Rpl_type{.stdcall, dynlib: lib, + importc: "mysql_rpl_query_type".} + # discover the master and its slaves +proc rpl_probe*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rpl_probe".} + # set the master, close/free the old one, if it is not a pivot +proc set_master*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. + stdcall, dynlib: lib, importc: "mysql_set_master".} +proc add_slave*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. + stdcall, dynlib: lib, importc: "mysql_add_slave".} +proc shutdown*(MySQL: PMySQL, shutdown_level: Enum_shutdown_level): cint{.stdcall, + dynlib: lib, importc: "mysql_shutdown".} +proc dump_debug_info*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, + importc: "mysql_dump_debug_info".} +proc refresh*(sql: PMySQL, refresh_options: cuint): cint{.stdcall, dynlib: lib, + importc: "mysql_refresh".} +proc kill*(MySQL: PMySQL, pid: int): cint{.stdcall, dynlib: lib, importc: "mysql_kill".} +proc set_server_option*(MySQL: PMySQL, option: Enum_mysql_set_option): cint{.stdcall, + dynlib: lib, importc: "mysql_set_server_option".} +proc ping*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_ping".} +proc stat*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_stat".} +proc get_server_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, + importc: "mysql_get_server_info".} +proc get_client_info*(): cstring{.stdcall, dynlib: lib, + importc: "mysql_get_client_info".} +proc get_client_version*(): int{.stdcall, dynlib: lib, + importc: "mysql_get_client_version".} +proc get_host_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, + importc: "mysql_get_host_info".} +proc get_server_version*(MySQL: PMySQL): int{.stdcall, dynlib: lib, + importc: "mysql_get_server_version".} +proc get_proto_info*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_get_proto_info".} +proc list_dbs*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, + importc: "mysql_list_dbs".} +proc list_tables*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, + importc: "mysql_list_tables".} +proc list_processes*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, + importc: "mysql_list_processes".} +proc options*(MySQL: PMySQL, option: Option, arg: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_options".} +proc free_result*(result: PRES){.stdcall, dynlib: lib, + importc: "mysql_free_result".} +proc data_seek*(result: PRES, offset: my_ulonglong){.stdcall, dynlib: lib, + importc: "mysql_data_seek".} +proc row_seek*(result: PRES, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, + dynlib: lib, importc: "mysql_row_seek".} +proc field_seek*(result: PRES, offset: FIELD_OFFSET): FIELD_OFFSET{.stdcall, + dynlib: lib, importc: "mysql_field_seek".} +proc fetch_row*(result: PRES): ROW{.stdcall, dynlib: lib, + importc: "mysql_fetch_row".} +proc fetch_lengths*(result: PRES): ptr int{.stdcall, dynlib: lib, + importc: "mysql_fetch_lengths".} +proc fetch_field*(result: PRES): PFIELD{.stdcall, dynlib: lib, + importc: "mysql_fetch_field".} +proc list_fields*(MySQL: PMySQL, table: cstring, wild: cstring): PRES{.stdcall, + dynlib: lib, importc: "mysql_list_fields".} +proc escape_string*(fto: cstring, `from`: cstring, from_length: int): int{. + stdcall, dynlib: lib, importc: "mysql_escape_string".} +proc hex_string*(fto: cstring, `from`: cstring, from_length: int): int{.stdcall, + dynlib: lib, importc: "mysql_hex_string".} +proc real_escape_string*(MySQL: PMySQL, fto: cstring, `from`: cstring, len: int): int{. + stdcall, dynlib: lib, importc: "mysql_real_escape_string".} +proc debug*(debug: cstring){.stdcall, dynlib: lib, importc: "mysql_debug".} + # function mysql_odbc_escape_string(mysql:PMYSQL; fto:Pchar; to_length:dword; from:Pchar; from_length:dword; + # param:pointer; extend_buffer:function (para1:pointer; to:Pchar; length:Pdword):Pchar):Pchar;stdcall;external mysqllib name 'mysql_odbc_escape_string'; +proc myodbc_remove_escape*(MySQL: PMySQL, name: cstring){.stdcall, dynlib: lib, + importc: "myodbc_remove_escape".} +proc thread_safe*(): cuint{.stdcall, dynlib: lib, importc: "mysql_thread_safe".} +proc embedded*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_embedded".} +proc manager_init*(con: PMANAGER): PMANAGER{.stdcall, dynlib: lib, + importc: "mysql_manager_init".} +proc manager_connect*(con: PMANAGER, host: cstring, user: cstring, + passwd: cstring, port: cuint): PMANAGER{.stdcall, + dynlib: lib, importc: "mysql_manager_connect".} +proc manager_close*(con: PMANAGER){.stdcall, dynlib: lib, + importc: "mysql_manager_close".} +proc manager_command*(con: PMANAGER, cmd: cstring, cmd_len: cint): cint{. + stdcall, dynlib: lib, importc: "mysql_manager_command".} +proc manager_fetch_line*(con: PMANAGER, res_buf: cstring, res_buf_size: cint): cint{. + stdcall, dynlib: lib, importc: "mysql_manager_fetch_line".} +proc read_query_result*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_read_query_result".} +proc stmt_init*(MySQL: PMySQL): PSTMT{.stdcall, dynlib: lib, importc: "mysql_stmt_init".} +proc stmt_prepare*(stmt: PSTMT, query: cstring, len: int): cint{.stdcall, + dynlib: lib, importc: "mysql_stmt_prepare".} +proc stmt_execute*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_execute".} +proc stmt_fetch*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_fetch".} +proc stmt_fetch_column*(stmt: PSTMT, `bind`: PBIND, column: cuint, offset: int): cint{. + stdcall, dynlib: lib, importc: "mysql_stmt_fetch_column".} +proc stmt_store_result*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_store_result".} +proc stmt_param_count*(stmt: PSTMT): int{.stdcall, dynlib: lib, + importc: "mysql_stmt_param_count".} +proc stmt_attr_set*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{. + stdcall, dynlib: lib, importc: "mysql_stmt_attr_set".} +proc stmt_attr_get*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{. + stdcall, dynlib: lib, importc: "mysql_stmt_attr_get".} +proc stmt_bind_param*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_param".} +proc stmt_bind_result*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_result".} +proc stmt_close*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_close".} +proc stmt_reset*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_reset".} +proc stmt_free_result*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_free_result".} +proc stmt_send_long_data*(stmt: PSTMT, param_number: cuint, data: cstring, + len: int): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_send_long_data".} +proc stmt_result_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, + importc: "mysql_stmt_result_metadata".} +proc stmt_param_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, + importc: "mysql_stmt_param_metadata".} +proc stmt_errno*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, + importc: "mysql_stmt_errno".} +proc stmt_error*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, + importc: "mysql_stmt_error".} +proc stmt_sqlstate*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, + importc: "mysql_stmt_sqlstate".} +proc stmt_row_seek*(stmt: PSTMT, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, + dynlib: lib, importc: "mysql_stmt_row_seek".} +proc stmt_row_tell*(stmt: PSTMT): ROW_OFFSET{.stdcall, dynlib: lib, + importc: "mysql_stmt_row_tell".} +proc stmt_data_seek*(stmt: PSTMT, offset: my_ulonglong){.stdcall, dynlib: lib, + importc: "mysql_stmt_data_seek".} +proc stmt_num_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_stmt_num_rows".} +proc stmt_affected_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_stmt_affected_rows".} +proc stmt_insert_id*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_stmt_insert_id".} +proc stmt_field_count*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, + importc: "mysql_stmt_field_count".} +proc commit*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_commit".} +proc rollback*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rollback".} +proc autocommit*(MySQL: PMySQL, auto_mode: my_bool): my_bool{.stdcall, dynlib: lib, + importc: "mysql_autocommit".} +proc more_results*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_more_results".} +proc next_result*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_next_result".} +proc close*(sock: PMySQL){.stdcall, dynlib: lib, importc: "mysql_close".} + # status return codes +const + NO_DATA* = 100 + DATA_TRUNCATED* = 101 + +proc reload*(x: PMySQL): cint +when defined(USE_OLD_FUNCTIONS): + proc connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring): PMySQL{.stdcall, + dynlib: lib, importc: "mysql_connect".} + proc create_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_create_db".} + proc drop_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_drop_db".} +proc net_safe_read*(MySQL: PMySQL): cuint{.cdecl, dynlib: lib, importc: "net_safe_read".} + +proc IS_PRI_KEY(n: int32): bool = + result = (n and PRI_KEY_FLAG) != 0 + +proc IS_NOT_NULL(n: int32): bool = + result = (n and NOT_NULL_FLAG) != 0 + +proc IS_BLOB(n: int32): bool = + result = (n and BLOB_FLAG) != 0 + +proc IS_NUM_FIELD(f: Pst_mysql_field): bool = + result = (f.flags and NUM_FLAG) != 0 + +proc IS_NUM(t: Enum_field_types): bool = + result = (t <= FIELD_TYPE_INT24) or (t == FIELD_TYPE_YEAR) or + (t == FIELD_TYPE_NEWDECIMAL) + +proc INTERNAL_NUM_FIELD(f: Pst_mysql_field): bool = + result = (f.ftype <= FIELD_TYPE_INT24) and + ((f.ftype != FIELD_TYPE_TIMESTAMP) or (f.len == 14) or (f.len == 8)) or + (f.ftype == FIELD_TYPE_YEAR) + +proc reload(x: PMySQL): cint = + result = refresh(x, REFRESH_GRANT) + +{.pop.} +when defined(nimHasStyleChecks): + {.pop.} diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim new file mode 100644 index 000000000..4e37e1a17 --- /dev/null +++ b/lib/wrappers/odbcsql.nim @@ -0,0 +1,841 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +when not defined(ODBCVER): + const + ODBCVER = 0x0351 ## define ODBC version 3.51 by default + +when defined(windows): + {.push callconv: stdcall.} + const odbclib = "odbc32.dll" +else: + {.push callconv: cdecl.} + const odbclib = "libodbc.so" + +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +# DATA TYPES CORRESPONDENCE +# BDE fields ODBC types +# ---------- ------------------ +# ftBlob SQL_BINARY +# ftBoolean SQL_BIT +# ftDate SQL_TYPE_DATE +# ftTime SQL_TYPE_TIME +# ftDateTime SQL_TYPE_TIMESTAMP +# ftInteger SQL_INTEGER +# ftSmallint SQL_SMALLINT +# ftFloat SQL_DOUBLE +# ftString SQL_CHAR +# ftMemo SQL_BINARY // SQL_VARCHAR +# + +type + TSqlChar* = char + TSqlSmallInt* = int16 + SqlUSmallInt* = int16 + SqlHandle* = pointer + SqlHEnv* = SqlHandle + SqlHDBC* = SqlHandle + SqlHStmt* = SqlHandle + SqlHDesc* = SqlHandle + TSqlInteger* = int32 + SqlUInteger* = int32 + TSqlLen* = int + TSqlULen* = uint + SqlPointer* = pointer + TSqlReal* = cfloat + TSqlDouble* = cdouble + TSqlFloat* = cdouble + SqlHWND* = pointer + PSQLCHAR* = cstring + PSQLINTEGER* = ptr TSqlInteger + PSQLUINTEGER* = ptr SqlUInteger + PSQLSMALLINT* = ptr TSqlSmallInt + PSQLUSMALLINT* = ptr SqlUSmallInt + PSQLREAL* = ptr TSqlReal + PSQLDOUBLE* = ptr TSqlDouble + PSQLFLOAT* = ptr TSqlFloat + PSQLHANDLE* = ptr SqlHandle + +const # SQL data type codes + SQL_UNKNOWN_TYPE* = 0 + SQL_LONGVARCHAR* = (- 1) + SQL_BINARY* = (- 2) + SQL_VARBINARY* = (- 3) + SQL_LONGVARBINARY* = (- 4) + SQL_BIGINT* = (- 5) + SQL_TINYINT* = (- 6) + SQL_BIT* = (- 7) + SQL_WCHAR* = (- 8) + SQL_WVARCHAR* = (- 9) + SQL_WLONGVARCHAR* = (- 10) + SQL_CHAR* = 1 + SQL_NUMERIC* = 2 + SQL_DECIMAL* = 3 + SQL_INTEGER* = 4 + SQL_SMALLINT* = 5 + SQL_FLOAT* = 6 + SQL_REAL* = 7 + SQL_DOUBLE* = 8 + SQL_DATETIME* = 9 + SQL_VARCHAR* = 12 + SQL_TYPE_DATE* = 91 + SQL_TYPE_TIME* = 92 + SQL_TYPE_TIMESTAMP* = 93 + SQL_DATE* = 9 + SQL_TIME* = 10 + SQL_TIMESTAMP* = 11 + SQL_INTERVAL* = 10 + SQL_GUID* = - 11 # interval codes + +when ODBCVER >= 0x0300: + const + SQL_CODE_YEAR* = 1 + SQL_CODE_MONTH* = 2 + SQL_CODE_DAY* = 3 + SQL_CODE_HOUR* = 4 + SQL_CODE_MINUTE* = 5 + SQL_CODE_SECOND* = 6 + SQL_CODE_YEAR_TO_MONTH* = 7 + SQL_CODE_DAY_TO_HOUR* = 8 + SQL_CODE_DAY_TO_MINUTE* = 9 + SQL_CODE_DAY_TO_SECOND* = 10 + SQL_CODE_HOUR_TO_MINUTE* = 11 + SQL_CODE_HOUR_TO_SECOND* = 12 + SQL_CODE_MINUTE_TO_SECOND* = 13 + SQL_INTERVAL_YEAR* = 100 + SQL_CODE_YEAR + SQL_INTERVAL_MONTH* = 100 + SQL_CODE_MONTH + SQL_INTERVAL_DAY* = 100 + SQL_CODE_DAY + SQL_INTERVAL_HOUR* = 100 + SQL_CODE_HOUR + SQL_INTERVAL_MINUTE* = 100 + SQL_CODE_MINUTE + SQL_INTERVAL_SECOND* = 100 + SQL_CODE_SECOND + SQL_INTERVAL_YEAR_TO_MONTH* = 100 + SQL_CODE_YEAR_TO_MONTH + SQL_INTERVAL_DAY_TO_HOUR* = 100 + SQL_CODE_DAY_TO_HOUR + SQL_INTERVAL_DAY_TO_MINUTE* = 100 + SQL_CODE_DAY_TO_MINUTE + SQL_INTERVAL_DAY_TO_SECOND* = 100 + SQL_CODE_DAY_TO_SECOND + SQL_INTERVAL_HOUR_TO_MINUTE* = 100 + SQL_CODE_HOUR_TO_MINUTE + SQL_INTERVAL_HOUR_TO_SECOND* = 100 + SQL_CODE_HOUR_TO_SECOND + SQL_INTERVAL_MINUTE_TO_SECOND* = 100 + SQL_CODE_MINUTE_TO_SECOND +else: + const + SQL_INTERVAL_YEAR* = - 80 + SQL_INTERVAL_MONTH* = - 81 + SQL_INTERVAL_YEAR_TO_MONTH* = - 82 + SQL_INTERVAL_DAY* = - 83 + SQL_INTERVAL_HOUR* = - 84 + SQL_INTERVAL_MINUTE* = - 85 + SQL_INTERVAL_SECOND* = - 86 + SQL_INTERVAL_DAY_TO_HOUR* = - 87 + SQL_INTERVAL_DAY_TO_MINUTE* = - 88 + SQL_INTERVAL_DAY_TO_SECOND* = - 89 + SQL_INTERVAL_HOUR_TO_MINUTE* = - 90 + SQL_INTERVAL_HOUR_TO_SECOND* = - 91 + SQL_INTERVAL_MINUTE_TO_SECOND* = - 92 + + +when ODBCVER < 0x0300: + const + SQL_UNICODE* = - 95 + SQL_UNICODE_VARCHAR* = - 96 + SQL_UNICODE_LONGVARCHAR* = - 97 + SQL_UNICODE_CHAR* = SQL_UNICODE +else: + # The previous definitions for SQL_UNICODE_ are historical and obsolete + const + SQL_UNICODE* = SQL_WCHAR + SQL_UNICODE_VARCHAR* = SQL_WVARCHAR + SQL_UNICODE_LONGVARCHAR* = SQL_WLONGVARCHAR + SQL_UNICODE_CHAR* = SQL_WCHAR +const # C datatype to SQL datatype mapping + SQL_C_CHAR* = SQL_CHAR + SQL_C_LONG* = SQL_INTEGER + SQL_C_SHORT* = SQL_SMALLINT + SQL_C_FLOAT* = SQL_REAL + SQL_C_DOUBLE* = SQL_DOUBLE + SQL_C_NUMERIC* = SQL_NUMERIC + SQL_C_DEFAULT* = 99 + SQL_SIGNED_OFFSET* = - 20 + SQL_UNSIGNED_OFFSET* = - 22 + SQL_C_DATE* = SQL_DATE + SQL_C_TIME* = SQL_TIME + SQL_C_TIMESTAMP* = SQL_TIMESTAMP + SQL_C_TYPE_DATE* = SQL_TYPE_DATE + SQL_C_TYPE_TIME* = SQL_TYPE_TIME + SQL_C_TYPE_TIMESTAMP* = SQL_TYPE_TIMESTAMP + SQL_C_INTERVAL_YEAR* = SQL_INTERVAL_YEAR + SQL_C_INTERVAL_MONTH* = SQL_INTERVAL_MONTH + SQL_C_INTERVAL_DAY* = SQL_INTERVAL_DAY + SQL_C_INTERVAL_HOUR* = SQL_INTERVAL_HOUR + SQL_C_INTERVAL_MINUTE* = SQL_INTERVAL_MINUTE + SQL_C_INTERVAL_SECOND* = SQL_INTERVAL_SECOND + SQL_C_INTERVAL_YEAR_TO_MONTH* = SQL_INTERVAL_YEAR_TO_MONTH + SQL_C_INTERVAL_DAY_TO_HOUR* = SQL_INTERVAL_DAY_TO_HOUR + SQL_C_INTERVAL_DAY_TO_MINUTE* = SQL_INTERVAL_DAY_TO_MINUTE + SQL_C_INTERVAL_DAY_TO_SECOND* = SQL_INTERVAL_DAY_TO_SECOND + SQL_C_INTERVAL_HOUR_TO_MINUTE* = SQL_INTERVAL_HOUR_TO_MINUTE + SQL_C_INTERVAL_HOUR_TO_SECOND* = SQL_INTERVAL_HOUR_TO_SECOND + SQL_C_INTERVAL_MINUTE_TO_SECOND* = SQL_INTERVAL_MINUTE_TO_SECOND + SQL_C_BINARY* = SQL_BINARY + SQL_C_BIT* = SQL_BIT + SQL_C_SBIGINT* = SQL_BIGINT + SQL_SIGNED_OFFSET # SIGNED BIGINT + SQL_C_UBIGINT* = SQL_BIGINT + SQL_UNSIGNED_OFFSET # UNSIGNED BIGINT + SQL_C_TINYINT* = SQL_TINYINT + SQL_C_SLONG* = SQL_C_LONG + SQL_SIGNED_OFFSET # SIGNED INTEGER + SQL_C_SSHORT* = SQL_C_SHORT + SQL_SIGNED_OFFSET # SIGNED SMALLINT + SQL_C_STINYINT* = SQL_TINYINT + SQL_SIGNED_OFFSET # SIGNED TINYINT + SQL_C_ULONG* = SQL_C_LONG + SQL_UNSIGNED_OFFSET # UNSIGNED INTEGER + SQL_C_USHORT* = SQL_C_SHORT + SQL_UNSIGNED_OFFSET # UNSIGNED SMALLINT + SQL_C_UTINYINT* = SQL_TINYINT + SQL_UNSIGNED_OFFSET # UNSIGNED TINYINT + SQL_C_BOOKMARK* = SQL_C_ULONG # BOOKMARK + SQL_C_GUID* = SQL_GUID + SQL_TYPE_NULL* = 0 + +when ODBCVER < 0x0300: + const + SQL_TYPE_MIN* = SQL_BIT + SQL_TYPE_MAX* = SQL_VARCHAR + +const + SQL_C_VARBOOKMARK* = SQL_C_BINARY + SQL_API_SQLDESCRIBEPARAM* = 58 + SQL_NO_TOTAL* = - 4 + +type + SQL_DATE_STRUCT* {.final, pure.} = object + Year*: TSqlSmallInt + Month*: SqlUSmallInt + Day*: SqlUSmallInt + + PSQL_DATE_STRUCT* = ptr SQL_DATE_STRUCT + SQL_TIME_STRUCT* {.final, pure.} = object + Hour*: SqlUSmallInt + Minute*: SqlUSmallInt + Second*: SqlUSmallInt + + PSQL_TIME_STRUCT* = ptr SQL_TIME_STRUCT + SQL_TIMESTAMP_STRUCT* {.final, pure.} = object + Year*: SqlUSmallInt + Month*: SqlUSmallInt + Day*: SqlUSmallInt + Hour*: SqlUSmallInt + Minute*: SqlUSmallInt + Second*: SqlUSmallInt + Fraction*: SqlUInteger + + PSQL_TIMESTAMP_STRUCT* = ptr SQL_TIMESTAMP_STRUCT + +const + SQL_NAME_LEN* = 128 + SQL_OV_ODBC3* = 3 + SQL_OV_ODBC2* = 2 + SQL_ATTR_ODBC_VERSION* = 200 # Options for SQLDriverConnect + SQL_DRIVER_NOPROMPT* = 0 + SQL_DRIVER_COMPLETE* = 1 + SQL_DRIVER_PROMPT* = 2 + SQL_DRIVER_COMPLETE_REQUIRED* = 3 + SQL_IS_POINTER* = (- 4) # whether an attribute is a pointer or not + SQL_IS_UINTEGER* = (- 5) + SQL_IS_INTEGER* = (- 6) + SQL_IS_USMALLINT* = (- 7) + SQL_IS_SMALLINT* = (- 8) # SQLExtendedFetch "fFetchType" values + SQL_FETCH_BOOKMARK* = 8 + SQL_SCROLL_OPTIONS* = 44 # SQL_USE_BOOKMARKS options + SQL_UB_OFF* = 0 + SQL_UB_ON* = 1 + SQL_UB_DEFAULT* = SQL_UB_OFF + SQL_UB_FIXED* = SQL_UB_ON + SQL_UB_VARIABLE* = 2 # SQL_SCROLL_OPTIONS masks + SQL_SO_FORWARD_ONLY* = 0x00000001 + SQL_SO_KEYSET_DRIVEN* = 0x00000002 + SQL_SO_DYNAMIC* = 0x00000004 + SQL_SO_MIXED* = 0x00000008 + SQL_SO_STATIC* = 0x00000010 + SQL_BOOKMARK_PERSISTENCE* = 82 + SQL_STATIC_SENSITIVITY* = 83 # SQL_BOOKMARK_PERSISTENCE values + SQL_BP_CLOSE* = 0x00000001 + SQL_BP_DELETE* = 0x00000002 + SQL_BP_DROP* = 0x00000004 + SQL_BP_TRANSACTION* = 0x00000008 + SQL_BP_UPDATE* = 0x00000010 + SQL_BP_OTHER_HSTMT* = 0x00000020 + SQL_BP_SCROLL* = 0x00000040 + SQL_DYNAMIC_CURSOR_ATTRIBUTES1* = 144 + SQL_DYNAMIC_CURSOR_ATTRIBUTES2* = 145 + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1* = 146 + SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2* = 147 + SQL_INDEX_KEYWORDS* = 148 + SQL_INFO_SCHEMA_VIEWS* = 149 + SQL_KEYSET_CURSOR_ATTRIBUTES1* = 150 + SQL_KEYSET_CURSOR_ATTRIBUTES2* = 151 + SQL_STATIC_CURSOR_ATTRIBUTES1* = 167 + SQL_STATIC_CURSOR_ATTRIBUTES2* = 168 # supported SQLFetchScroll FetchOrientation's + SQL_CA1_NEXT* = 1 + SQL_CA1_ABSOLUTE* = 2 + SQL_CA1_RELATIVE* = 4 + SQL_CA1_BOOKMARK* = 8 # supported SQLSetPos LockType's + SQL_CA1_LOCK_NO_CHANGE* = 0x00000040 + SQL_CA1_LOCK_EXCLUSIVE* = 0x00000080 + SQL_CA1_LOCK_UNLOCK* = 0x00000100 # supported SQLSetPos Operations + SQL_CA1_POS_POSITION* = 0x00000200 + SQL_CA1_POS_UPDATE* = 0x00000400 + SQL_CA1_POS_DELETE* = 0x00000800 + SQL_CA1_POS_REFRESH* = 0x00001000 # positioned updates and deletes + SQL_CA1_POSITIONED_UPDATE* = 0x00002000 + SQL_CA1_POSITIONED_DELETE* = 0x00004000 + SQL_CA1_SELECT_FOR_UPDATE* = 0x00008000 # supported SQLBulkOperations operations + SQL_CA1_BULK_ADD* = 0x00010000 + SQL_CA1_BULK_UPDATE_BY_BOOKMARK* = 0x00020000 + SQL_CA1_BULK_DELETE_BY_BOOKMARK* = 0x00040000 + SQL_CA1_BULK_FETCH_BY_BOOKMARK* = 0x00080000 # supported values for SQL_ATTR_SCROLL_CONCURRENCY + SQL_CA2_READ_ONLY_CONCURRENCY* = 1 + SQL_CA2_LOCK_CONCURRENCY* = 2 + SQL_CA2_OPT_ROWVER_CONCURRENCY* = 4 + SQL_CA2_OPT_VALUES_CONCURRENCY* = 8 # sensitivity of the cursor to its own inserts, deletes, and updates + SQL_CA2_SENSITIVITY_ADDITIONS* = 0x00000010 + SQL_CA2_SENSITIVITY_DELETIONS* = 0x00000020 + SQL_CA2_SENSITIVITY_UPDATES* = 0x00000040 # semantics of SQL_ATTR_MAX_ROWS + SQL_CA2_MAX_ROWS_SELECT* = 0x00000080 + SQL_CA2_MAX_ROWS_INSERT* = 0x00000100 + SQL_CA2_MAX_ROWS_DELETE* = 0x00000200 + SQL_CA2_MAX_ROWS_UPDATE* = 0x00000400 + SQL_CA2_MAX_ROWS_CATALOG* = 0x00000800 + SQL_CA2_MAX_ROWS_AFFECTS_ALL* = (SQL_CA2_MAX_ROWS_SELECT or + SQL_CA2_MAX_ROWS_INSERT or SQL_CA2_MAX_ROWS_DELETE or + SQL_CA2_MAX_ROWS_UPDATE or SQL_CA2_MAX_ROWS_CATALOG) # semantics of + # SQL_DIAG_CURSOR_ROW_COUNT + SQL_CA2_CRC_EXACT* = 0x00001000 + SQL_CA2_CRC_APPROXIMATE* = 0x00002000 # the kinds of positioned statements that can be simulated + SQL_CA2_SIMULATE_NON_UNIQUE* = 0x00004000 + SQL_CA2_SIMULATE_TRY_UNIQUE* = 0x00008000 + SQL_CA2_SIMULATE_UNIQUE* = 0x00010000 # Operations in SQLBulkOperations + SQL_ADD* = 4 + SQL_SETPOS_MAX_OPTION_VALUE* = SQL_ADD + SQL_UPDATE_BY_BOOKMARK* = 5 + SQL_DELETE_BY_BOOKMARK* = 6 + SQL_FETCH_BY_BOOKMARK* = 7 # Operations in SQLSetPos + SQL_POSITION* = 0 + SQL_REFRESH* = 1 + SQL_UPDATE* = 2 + SQL_DELETE* = 3 # Lock options in SQLSetPos + SQL_LOCK_NO_CHANGE* = 0 + SQL_LOCK_EXCLUSIVE* = 1 + SQL_LOCK_UNLOCK* = 2 # SQLExtendedFetch "rgfRowStatus" element values + SQL_ROW_SUCCESS* = 0 + SQL_ROW_DELETED* = 1 + SQL_ROW_UPDATED* = 2 + SQL_ROW_NOROW* = 3 + SQL_ROW_ADDED* = 4 + SQL_ROW_ERROR* = 5 + SQL_ROW_SUCCESS_WITH_INFO* = 6 + SQL_ROW_PROCEED* = 0 + SQL_ROW_IGNORE* = 1 + SQL_MAX_DSN_LENGTH* = 32 # maximum data source name size + SQL_MAX_OPTION_STRING_LENGTH* = 256 + SQL_ODBC_CURSORS* = 110 + SQL_ATTR_ODBC_CURSORS* = SQL_ODBC_CURSORS # SQL_ODBC_CURSORS options + SQL_CUR_USE_IF_NEEDED* = 0 + SQL_CUR_USE_ODBC* = 1 + SQL_CUR_USE_DRIVER* = 2 + SQL_CUR_DEFAULT* = SQL_CUR_USE_DRIVER + SQL_PARAM_TYPE_UNKNOWN* = 0 + SQL_PARAM_INPUT* = 1 + SQL_PARAM_INPUT_OUTPUT* = 2 + SQL_RESULT_COL* = 3 + SQL_PARAM_OUTPUT* = 4 + SQL_RETURN_VALUE* = 5 # special length/indicator values + SQL_NULL_DATA* = (- 1) + SQL_DATA_AT_EXEC* = (- 2) + SQL_SUCCESS* = 0 + SQL_SUCCESS_WITH_INFO* = 1 + SQL_NO_DATA* = 100 + SQL_ERROR* = (- 1) + SQL_INVALID_HANDLE* = (- 2) + SQL_STILL_EXECUTING* = 2 + SQL_NEED_DATA* = 99 # flags for null-terminated string + SQL_NTS* = (- 3) # maximum message length + SQL_MAX_MESSAGE_LENGTH* = 512 # date/time length constants + SQL_DATE_LEN* = 10 + SQL_TIME_LEN* = 8 # add P+1 if precision is nonzero + SQL_TIMESTAMP_LEN* = 19 # add P+1 if precision is nonzero + # handle type identifiers + SQL_HANDLE_ENV* = 1 + SQL_HANDLE_DBC* = 2 + SQL_HANDLE_STMT* = 3 + SQL_HANDLE_DESC* = 4 # environment attribute + SQL_ATTR_OUTPUT_NTS* = 10001 # connection attributes + SQL_ATTR_AUTO_IPD* = 10001 + SQL_ATTR_METADATA_ID* = 10014 # statement attributes + SQL_ATTR_APP_ROW_DESC* = 10010 + SQL_ATTR_APP_PARAM_DESC* = 10011 + SQL_ATTR_IMP_ROW_DESC* = 10012 + SQL_ATTR_IMP_PARAM_DESC* = 10013 + SQL_ATTR_CURSOR_SCROLLABLE* = (- 1) + SQL_ATTR_CURSOR_SENSITIVITY* = (- 2) + SQL_QUERY_TIMEOUT* = 0 + SQL_MAX_ROWS* = 1 + SQL_NOSCAN* = 2 + SQL_MAX_LENGTH* = 3 + SQL_ASYNC_ENABLE* = 4 # same as SQL_ATTR_ASYNC_ENABLE */ + SQL_BIND_TYPE* = 5 + SQL_CURSOR_TYPE* = 6 + SQL_CONCURRENCY* = 7 + SQL_KEYSET_SIZE* = 8 + SQL_ROWSET_SIZE* = 9 + SQL_SIMULATE_CURSOR* = 10 + SQL_RETRIEVE_DATA* = 11 + SQL_USE_BOOKMARKS* = 12 + SQL_GET_BOOKMARK* = 13 # GetStmtOption Only */ + SQL_ROW_NUMBER* = 14 # GetStmtOption Only */ + SQL_ATTR_CURSOR_TYPE* = SQL_CURSOR_TYPE + SQL_ATTR_CONCURRENCY* = SQL_CONCURRENCY + SQL_ATTR_FETCH_BOOKMARK_PTR* = 16 + SQL_ATTR_ROW_STATUS_PTR* = 25 + SQL_ATTR_ROWS_FETCHED_PTR* = 26 + SQL_AUTOCOMMIT* = 102 + SQL_ATTR_AUTOCOMMIT* = SQL_AUTOCOMMIT + SQL_ATTR_ROW_NUMBER* = SQL_ROW_NUMBER + SQL_TXN_ISOLATION* = 108 + SQL_ATTR_TXN_ISOLATION* = SQL_TXN_ISOLATION + SQL_ATTR_MAX_ROWS* = SQL_MAX_ROWS + SQL_ATTR_USE_BOOKMARKS* = SQL_USE_BOOKMARKS #* connection attributes */ + SQL_ACCESS_MODE* = 101 # SQL_AUTOCOMMIT =102; + SQL_LOGIN_TIMEOUT* = 103 + SQL_OPT_TRACE* = 104 + SQL_OPT_TRACEFILE* = 105 + SQL_TRANSLATE_DLL* = 106 + SQL_TRANSLATE_OPTION* = 107 # SQL_TXN_ISOLATION =108; + SQL_CURRENT_QUALIFIER* = 109 # SQL_ODBC_CURSORS =110; + SQL_QUIET_MODE* = 111 + SQL_PACKET_SIZE* = 112 #* connection attributes with new names */ + SQL_ATTR_ACCESS_MODE* = SQL_ACCESS_MODE # SQL_ATTR_AUTOCOMMIT =SQL_AUTOCOMMIT; + SQL_ATTR_CONNECTION_DEAD* = 1209 #* GetConnectAttr only */ + SQL_ATTR_CONNECTION_TIMEOUT* = 113 + SQL_ATTR_CURRENT_CATALOG* = SQL_CURRENT_QUALIFIER + SQL_ATTR_DISCONNECT_BEHAVIOR* = 114 + SQL_ATTR_ENLIST_IN_DTC* = 1207 + SQL_ATTR_ENLIST_IN_XA* = 1208 + SQL_ATTR_LOGIN_TIMEOUT* = SQL_LOGIN_TIMEOUT # SQL_ATTR_ODBC_CURSORS =SQL_ODBC_CURSORS; + SQL_ATTR_PACKET_SIZE* = SQL_PACKET_SIZE + SQL_ATTR_QUIET_MODE* = SQL_QUIET_MODE + SQL_ATTR_TRACE* = SQL_OPT_TRACE + SQL_ATTR_TRACEFILE* = SQL_OPT_TRACEFILE + SQL_ATTR_TRANSLATE_LIB* = SQL_TRANSLATE_DLL + SQL_ATTR_TRANSLATE_OPTION* = SQL_TRANSLATE_OPTION # SQL_ATTR_TXN_ISOLATION =SQL_TXN_ISOLATION; + #* SQL_ACCESS_MODE options */ + SQL_MODE_READ_WRITE* = 0 + SQL_MODE_READ_ONLY* = 1 + SQL_MODE_DEFAULT* = SQL_MODE_READ_WRITE #* SQL_AUTOCOMMIT options */ + SQL_AUTOCOMMIT_OFF* = 0 + SQL_AUTOCOMMIT_ON* = 1 + SQL_AUTOCOMMIT_DEFAULT* = SQL_AUTOCOMMIT_ON # SQL_ATTR_CURSOR_SCROLLABLE values + SQL_NONSCROLLABLE* = 0 + SQL_SCROLLABLE* = 1 # SQL_CURSOR_TYPE options + SQL_CURSOR_FORWARD_ONLY* = 0 + SQL_CURSOR_KEYSET_DRIVEN* = 1 + SQL_CURSOR_DYNAMIC* = 2 + SQL_CURSOR_STATIC* = 3 + SQL_CURSOR_TYPE_DEFAULT* = SQL_CURSOR_FORWARD_ONLY # Default value + # SQL_CONCURRENCY options + SQL_CONCUR_READ_ONLY* = 1 + SQL_CONCUR_LOCK* = 2 + SQL_CONCUR_ROWVER* = 3 + SQL_CONCUR_VALUES* = 4 + SQL_CONCUR_DEFAULT* = SQL_CONCUR_READ_ONLY # Default value + # identifiers of fields in the SQL descriptor + SQL_DESC_COUNT* = 1001 + SQL_DESC_TYPE* = 1002 + SQL_DESC_LENGTH* = 1003 + SQL_DESC_OCTET_LENGTH_PTR* = 1004 + SQL_DESC_PRECISION* = 1005 + SQL_DESC_SCALE* = 1006 + SQL_DESC_DATETIME_INTERVAL_CODE* = 1007 + SQL_DESC_NULLABLE* = 1008 + SQL_DESC_INDICATOR_PTR* = 1009 + SQL_DESC_DATA_PTR* = 1010 + SQL_DESC_NAME* = 1011 + SQL_DESC_UNNAMED* = 1012 + SQL_DESC_OCTET_LENGTH* = 1013 + SQL_DESC_ALLOC_TYPE* = 1099 # identifiers of fields in the diagnostics area + SQL_DIAG_RETURNCODE* = 1 + SQL_DIAG_NUMBER* = 2 + SQL_DIAG_ROW_COUNT* = 3 + SQL_DIAG_SQLSTATE* = 4 + SQL_DIAG_NATIVE* = 5 + SQL_DIAG_MESSAGE_TEXT* = 6 + SQL_DIAG_DYNAMIC_FUNCTION* = 7 + SQL_DIAG_CLASS_ORIGIN* = 8 + SQL_DIAG_SUBCLASS_ORIGIN* = 9 + SQL_DIAG_CONNECTION_NAME* = 10 + SQL_DIAG_SERVER_NAME* = 11 + SQL_DIAG_DYNAMIC_FUNCTION_CODE* = 12 # dynamic function codes + SQL_DIAG_ALTER_TABLE* = 4 + SQL_DIAG_CREATE_INDEX* = (- 1) + SQL_DIAG_CREATE_TABLE* = 77 + SQL_DIAG_CREATE_VIEW* = 84 + SQL_DIAG_DELETE_WHERE* = 19 + SQL_DIAG_DROP_INDEX* = (- 2) + SQL_DIAG_DROP_TABLE* = 32 + SQL_DIAG_DROP_VIEW* = 36 + SQL_DIAG_DYNAMIC_DELETE_CURSOR* = 38 + SQL_DIAG_DYNAMIC_UPDATE_CURSOR* = 81 + SQL_DIAG_GRANT* = 48 + SQL_DIAG_INSERT* = 50 + SQL_DIAG_REVOKE* = 59 + SQL_DIAG_SELECT_CURSOR* = 85 + SQL_DIAG_UNKNOWN_STATEMENT* = 0 + SQL_DIAG_UPDATE_WHERE* = 82 # Statement attribute values for cursor sensitivity + SQL_UNSPECIFIED* = 0 + SQL_INSENSITIVE* = 1 + SQL_SENSITIVE* = 2 # GetTypeInfo() request for all data types + SQL_ALL_TYPES* = 0 # Default conversion code for SQLBindCol(), SQLBindParam() and SQLGetData() + SQL_DEFAULT* = 99 # SQLGetData() code indicating that the application row descriptor + # specifies the data type + SQL_ARD_TYPE* = (- 99) # SQL date/time type subcodes + SQL_CODE_DATE* = 1 + SQL_CODE_TIME* = 2 + SQL_CODE_TIMESTAMP* = 3 # CLI option values + SQL_FALSE* = 0 + SQL_TRUE* = 1 # values of NULLABLE field in descriptor + SQL_NO_NULLS* = 0 + SQL_NULLABLE* = 1 # Value returned by SQLGetTypeInfo() to denote that it is + # not known whether or not a data type supports null values. + SQL_NULLABLE_UNKNOWN* = 2 + SQL_CLOSE* = 0 + SQL_DROP* = 1 + SQL_UNBIND* = 2 + SQL_RESET_PARAMS* = 3 # Codes used for FetchOrientation in SQLFetchScroll(), + # and in SQLDataSources() + SQL_FETCH_NEXT* = 1 + SQL_FETCH_FIRST* = 2 + SQL_FETCH_FIRST_USER* = 31 + SQL_FETCH_FIRST_SYSTEM* = 32 # Other codes used for FetchOrientation in SQLFetchScroll() + SQL_FETCH_LAST* = 3 + SQL_FETCH_PRIOR* = 4 + SQL_FETCH_ABSOLUTE* = 5 + SQL_FETCH_RELATIVE* = 6 + SQL_NULL_HENV* = SqlHEnv(nil) + SQL_NULL_HDBC* = SqlHDBC(nil) + SQL_NULL_HSTMT* = SqlHStmt(nil) + SQL_NULL_HDESC* = SqlHDesc(nil) #* null handle used in place of parent handle when allocating HENV */ + SQL_NULL_HANDLE* = SqlHandle(nil) #* Values that may appear in the result set of SQLSpecialColumns() */ + SQL_SCOPE_CURROW* = 0 + SQL_SCOPE_TRANSACTION* = 1 + SQL_SCOPE_SESSION* = 2 #* Column types and scopes in SQLSpecialColumns. */ + SQL_BEST_ROWID* = 1 + SQL_ROWVER* = 2 + SQL_ROW_IDENTIFIER* = 1 #* Reserved values for UNIQUE argument of SQLStatistics() */ + SQL_INDEX_UNIQUE* = 0 + SQL_INDEX_ALL* = 1 #* Reserved values for RESERVED argument of SQLStatistics() */ + SQL_QUICK* = 0 + SQL_ENSURE* = 1 #* Values that may appear in the result set of SQLStatistics() */ + SQL_TABLE_STAT* = 0 + SQL_INDEX_CLUSTERED* = 1 + SQL_INDEX_HASHED* = 2 + SQL_INDEX_OTHER* = 3 + SQL_SCROLL_CONCURRENCY* = 43 + SQL_TXN_CAPABLE* = 46 + SQL_TRANSACTION_CAPABLE* = SQL_TXN_CAPABLE + SQL_USER_NAME* = 47 + SQL_TXN_ISOLATION_OPTION* = 72 + SQL_TRANSACTION_ISOLATION_OPTION* = SQL_TXN_ISOLATION_OPTION + SQL_OJ_CAPABILITIES* = 115 + SQL_OUTER_JOIN_CAPABILITIES* = SQL_OJ_CAPABILITIES + SQL_XOPEN_CLI_YEAR* = 10000 + SQL_CURSOR_SENSITIVITY* = 10001 + SQL_DESCRIBE_PARAMETER* = 10002 + SQL_CATALOG_NAME* = 10003 + SQL_COLLATION_SEQ* = 10004 + SQL_MAX_IDENTIFIER_LEN* = 10005 + SQL_MAXIMUM_IDENTIFIER_LENGTH* = SQL_MAX_IDENTIFIER_LEN + SQL_SCCO_READ_ONLY* = 1 + SQL_SCCO_LOCK* = 2 + SQL_SCCO_OPT_ROWVER* = 4 + SQL_SCCO_OPT_VALUES* = 8 #* SQL_TXN_CAPABLE values */ + SQL_TC_NONE* = 0 + SQL_TC_DML* = 1 + SQL_TC_ALL* = 2 + SQL_TC_DDL_COMMIT* = 3 + SQL_TC_DDL_IGNORE* = 4 #* SQL_TXN_ISOLATION_OPTION bitmasks */ + SQL_TXN_READ_UNCOMMITTED* = 1 + SQL_TRANSACTION_READ_UNCOMMITTED* = SQL_TXN_READ_UNCOMMITTED + SQL_TXN_READ_COMMITTED* = 2 + SQL_TRANSACTION_READ_COMMITTED* = SQL_TXN_READ_COMMITTED + SQL_TXN_REPEATABLE_READ* = 4 + SQL_TRANSACTION_REPEATABLE_READ* = SQL_TXN_REPEATABLE_READ + SQL_TXN_SERIALIZABLE* = 8 + SQL_TRANSACTION_SERIALIZABLE* = SQL_TXN_SERIALIZABLE + SQL_SS_ADDITIONS* = 1 + SQL_SS_DELETIONS* = 2 + SQL_SS_UPDATES* = 4 # SQLColAttributes defines + SQL_COLUMN_COUNT* = 0 + SQL_COLUMN_NAME* = 1 + SQL_COLUMN_TYPE* = 2 + SQL_COLUMN_LENGTH* = 3 + SQL_COLUMN_PRECISION* = 4 + SQL_COLUMN_SCALE* = 5 + SQL_COLUMN_DISPLAY_SIZE* = 6 + SQL_COLUMN_NULLABLE* = 7 + SQL_COLUMN_UNSIGNED* = 8 + SQL_COLUMN_MONEY* = 9 + SQL_COLUMN_UPDATABLE* = 10 + SQL_COLUMN_AUTO_INCREMENT* = 11 + SQL_COLUMN_CASE_SENSITIVE* = 12 + SQL_COLUMN_SEARCHABLE* = 13 + SQL_COLUMN_TYPE_NAME* = 14 + SQL_COLUMN_TABLE_NAME* = 15 + SQL_COLUMN_OWNER_NAME* = 16 + SQL_COLUMN_QUALIFIER_NAME* = 17 + SQL_COLUMN_LABEL* = 18 + SQL_COLATT_OPT_MAX* = SQL_COLUMN_LABEL + SQL_COLUMN_DRIVER_START* = 1000 + SQL_DESC_ARRAY_SIZE* = 20 + SQL_DESC_ARRAY_STATUS_PTR* = 21 + SQL_DESC_AUTO_UNIQUE_VALUE* = SQL_COLUMN_AUTO_INCREMENT + SQL_DESC_BASE_COLUMN_NAME* = 22 + SQL_DESC_BASE_TABLE_NAME* = 23 + SQL_DESC_BIND_OFFSET_PTR* = 24 + SQL_DESC_BIND_TYPE* = 25 + SQL_DESC_CASE_SENSITIVE* = SQL_COLUMN_CASE_SENSITIVE + SQL_DESC_CATALOG_NAME* = SQL_COLUMN_QUALIFIER_NAME + SQL_DESC_CONCISE_TYPE* = SQL_COLUMN_TYPE + SQL_DESC_DATETIME_INTERVAL_PRECISION* = 26 + SQL_DESC_DISPLAY_SIZE* = SQL_COLUMN_DISPLAY_SIZE + SQL_DESC_FIXED_PREC_SCALE* = SQL_COLUMN_MONEY + SQL_DESC_LABEL* = SQL_COLUMN_LABEL + SQL_DESC_LITERAL_PREFIX* = 27 + SQL_DESC_LITERAL_SUFFIX* = 28 + SQL_DESC_LOCAL_TYPE_NAME* = 29 + SQL_DESC_MAXIMUM_SCALE* = 30 + SQL_DESC_MINIMUM_SCALE* = 31 + SQL_DESC_NUM_PREC_RADIX* = 32 + SQL_DESC_PARAMETER_TYPE* = 33 + SQL_DESC_ROWS_PROCESSED_PTR* = 34 + SQL_DESC_SCHEMA_NAME* = SQL_COLUMN_OWNER_NAME + SQL_DESC_SEARCHABLE* = SQL_COLUMN_SEARCHABLE + SQL_DESC_TYPE_NAME* = SQL_COLUMN_TYPE_NAME + SQL_DESC_TABLE_NAME* = SQL_COLUMN_TABLE_NAME + SQL_DESC_UNSIGNED* = SQL_COLUMN_UNSIGNED + SQL_DESC_UPDATABLE* = SQL_COLUMN_UPDATABLE #* SQLEndTran() options */ + SQL_COMMIT* = 0 + SQL_ROLLBACK* = 1 + SQL_ATTR_ROW_ARRAY_SIZE* = 27 #* SQLConfigDataSource() options */ + ODBC_ADD_DSN* = 1 + ODBC_CONFIG_DSN* = 2 + ODBC_REMOVE_DSN* = 3 + ODBC_ADD_SYS_DSN* = 4 + ODBC_CONFIG_SYS_DSN* = 5 + ODBC_REMOVE_SYS_DSN* = 6 + + SQL_ACTIVE_CONNECTIONS* = 0 # SQLGetInfo + SQL_DATA_SOURCE_NAME* = 2 + SQL_DATA_SOURCE_READ_ONLY* = 25 + SQL_DATABASE_NAME* = 2 + SQL_DBMS_NAME* = 17 + SQL_DBMS_VERSION* = 18 + SQL_DRIVER_HDBC* = 3 + SQL_DRIVER_HENV* = 4 + SQL_DRIVER_HSTMT* = 5 + SQL_DRIVER_NAME* = 6 + SQL_DRIVER_VER* = 7 + SQL_FETCH_DIRECTION* = 8 + SQL_ODBC_VER* = 10 + SQL_DRIVER_ODBC_VER* = 77 + SQL_SERVER_NAME* = 13 + SQL_ACTIVE_ENVIRONMENTS* = 116 + SQL_ACTIVE_STATEMENTS* = 1 + SQL_SQL_CONFORMANCE* = 118 + SQL_DATETIME_LITERALS* = 119 + SQL_ASYNC_MODE* = 10021 + SQL_BATCH_ROW_COUNT* = 120 + SQL_BATCH_SUPPORT* = 121 + SQL_CATALOG_LOCATION* = 114 + #SQL_CATALOG_NAME* = 10003 + SQL_CATALOG_NAME_SEPARATOR* = 41 + SQL_CATALOG_TERM* = 42 + SQL_CATALOG_USAGE* = 92 + #SQL_COLLATION_SEQ* = 10004 + SQL_COLUMN_ALIAS* = 87 + #SQL_USER_NAME* = 47 + +proc SQLAllocHandle*(HandleType: TSqlSmallInt, InputHandle: SqlHandle, + OutputHandlePtr: var SqlHandle): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLSetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger, + Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLGetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger, + Value: SqlPointer, BufferLength: TSqlInteger, + StringLength: PSQLINTEGER): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLFreeHandle*(HandleType: TSqlSmallInt, Handle: SqlHandle): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLGetDiagRec*(HandleType: TSqlSmallInt, Handle: SqlHandle, + RecNumber: TSqlSmallInt, Sqlstate: PSQLCHAR, + NativeError: var TSqlInteger, MessageText: PSQLCHAR, + BufferLength: TSqlSmallInt, TextLength: var TSqlSmallInt): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLGetDiagField*(HandleType: TSqlSmallInt, Handle: SqlHandle, + RecNumber: TSqlSmallInt, DiagIdentifier: TSqlSmallInt, + DiagInfoPtr: SqlPointer, BufferLength: TSqlSmallInt, + StringLengthPtr: var TSqlSmallInt): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLConnect*(ConnectionHandle: SqlHDBC, ServerName: PSQLCHAR, + NameLength1: TSqlSmallInt, UserName: PSQLCHAR, + NameLength2: TSqlSmallInt, Authentication: PSQLCHAR, + NameLength3: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLDisconnect*(ConnectionHandle: SqlHDBC): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLDriverConnect*(hdbc: SqlHDBC, hwnd: SqlHWND, szCsin: cstring, + szCLen: TSqlSmallInt, szCsout: cstring, + cbCSMax: TSqlSmallInt, cbCsOut: var TSqlSmallInt, + f: SqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLBrowseConnect*(hdbc: SqlHDBC, szConnStrIn: PSQLCHAR, + cbConnStrIn: TSqlSmallInt, szConnStrOut: PSQLCHAR, + cbConnStrOutMax: TSqlSmallInt, + cbConnStrOut: var TSqlSmallInt): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLExecDirect*(StatementHandle: SqlHStmt, StatementText: PSQLCHAR, + TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLExecDirectW*(StatementHandle: SqlHStmt, StatementText: WideCString, + TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLPrepare*(StatementHandle: SqlHStmt, StatementText: PSQLCHAR, + TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLPrepareW*(StatementHandle: SqlHStmt, StatementText: WideCString, + TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLCloseCursor*(StatementHandle: SqlHStmt): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLExecute*(StatementHandle: SqlHStmt): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLFetch*(StatementHandle: SqlHStmt): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLNumResultCols*(StatementHandle: SqlHStmt, ColumnCount: var TSqlSmallInt): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLDescribeCol*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, + ColumnName: PSQLCHAR, BufferLength: TSqlSmallInt, + NameLength: var TSqlSmallInt, DataType: var TSqlSmallInt, + ColumnSize: var TSqlULen, + DecimalDigits: var TSqlSmallInt, Nullable: var TSqlSmallInt): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLFetchScroll*(StatementHandle: SqlHStmt, FetchOrientation: TSqlSmallInt, + FetchOffset: TSqlLen): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLExtendedFetch*(hstmt: SqlHStmt, fFetchType: SqlUSmallInt, + irow: TSqlLen, pcrow: var TSqlULen, + rgfRowStatus: PSQLUSMALLINT): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLGetData*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, + TargetType: TSqlSmallInt, TargetValue: SqlPointer, + BufferLength: TSqlLen, StrLen_or_Ind: ptr TSqlLen): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLSetStmtAttr*(StatementHandle: SqlHStmt, Attribute: TSqlInteger, + Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLGetStmtAttr*(StatementHandle: SqlHStmt, Attribute: TSqlInteger, + Value: SqlPointer, BufferLength: TSqlInteger, + StringLength: PSQLINTEGER): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLGetInfo*(ConnectionHandle: SqlHDBC, InfoType: SqlUSmallInt, + InfoValue: SqlPointer, BufferLength: TSqlSmallInt, + StringLength: PSQLSMALLINT): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLBulkOperations*(StatementHandle: SqlHStmt, Operation: SqlUSmallInt): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLPutData*(StatementHandle: SqlHStmt, Data: SqlPointer, + StrLen_or_Ind: TSQLLEN): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLBindCol*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, + TargetType: TSqlSmallInt, TargetValue: SqlPointer, + BufferLength: TSqlLEN, StrLen_or_Ind: PSQLINTEGER): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLSetPos*(hstmt: SqlHStmt, irow: SqlUSmallInt, fOption: SqlUSmallInt, + fLock: SqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLDataSources*(EnvironmentHandle: SqlHEnv, Direction: SqlUSmallInt, + ServerName: PSQLCHAR, BufferLength1: TSqlSmallInt, + NameLength1: PSQLSMALLINT, Description: PSQLCHAR, + BufferLength2: TSqlSmallInt, NameLength2: PSQLSMALLINT): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLDrivers*(EnvironmentHandle: SqlHEnv, Direction: SqlUSmallInt, + DriverDescription: PSQLCHAR, BufferLength1: TSqlSmallInt, + DescriptionLength1: PSQLSMALLINT, DriverAttributes: PSQLCHAR, + BufferLength2: TSqlSmallInt, AttributesLength2: PSQLSMALLINT): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLSetConnectAttr*(ConnectionHandle: SqlHDBC, Attribute: TSqlInteger, + Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLGetCursorName*(StatementHandle: SqlHStmt, CursorName: PSQLCHAR, + BufferLength: TSqlSmallInt, NameLength: PSQLSMALLINT): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLSetCursorName*(StatementHandle: SqlHStmt, CursorName: PSQLCHAR, + NameLength: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLRowCount*(StatementHandle: SqlHStmt, RowCount: var TSQLLEN): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLBindParameter*(hstmt: SqlHStmt, ipar: SqlUSmallInt, + fParamType: TSqlSmallInt, fCType: TSqlSmallInt, + fSqlType: TSqlSmallInt, cbColDef: TSQLULEN, + ibScale: TSqlSmallInt, rgbValue: SqlPointer, + cbValueMax: TSQLLEN, pcbValue: var TSQLLEN): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLFreeStmt*(StatementHandle: SqlHStmt, Option: SqlUSmallInt): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLColAttribute*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, + FieldIdentifier: SqlUSmallInt, + CharacterAttribute: PSQLCHAR, BufferLength: TSqlSmallInt, + StringLength: PSQLSMALLINT, + NumericAttribute: TSQLLEN): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLEndTran*(HandleType: TSqlSmallInt, Handle: SqlHandle, + CompletionType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLTables*(hstmt: SqlHStmt, szTableQualifier: PSQLCHAR, + cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR, + cbTableOwner: TSqlSmallInt, szTableName: PSQLCHAR, + cbTableName: TSqlSmallInt, szTableType: PSQLCHAR, + cbTableType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLColumns*(hstmt: SqlHStmt, szTableQualifier: PSQLCHAR, + cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR, + cbTableOwner: TSqlSmallInt, szTableName: PSQLCHAR, + cbTableName: TSqlSmallInt, szColumnName: PSQLCHAR, + cbColumnName: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.} +proc SQLSpecialColumns*(StatementHandle: SqlHStmt, IdentifierType: SqlUSmallInt, + CatalogName: PSQLCHAR, NameLength1: TSqlSmallInt, + SchemaName: PSQLCHAR, NameLength2: TSqlSmallInt, + TableName: PSQLCHAR, NameLength3: TSqlSmallInt, + Scope: SqlUSmallInt, + Nullable: SqlUSmallInt): TSqlSmallInt{. + dynlib: odbclib, importc.} +proc SQLProcedures*(hstmt: SqlHStmt, szTableQualifier: PSQLCHAR, + cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR, + cbTableOwner: TSqlSmallInt, szTableName: PSQLCHAR, + cbTableName: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLPrimaryKeys*(hstmt: SqlHStmt, CatalogName: PSQLCHAR, + NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR, + NameLength2: TSqlSmallInt, TableName: PSQLCHAR, + NameLength3: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLProcedureColumns*(hstmt: SqlHStmt, CatalogName: PSQLCHAR, + NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR, + NameLength2: TSqlSmallInt, ProcName: PSQLCHAR, + NameLength3: TSqlSmallInt, ColumnName: PSQLCHAR, + NameLength4: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, + importc.} +proc SQLStatistics*(hstmt: SqlHStmt, CatalogName: PSQLCHAR, + NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR, + NameLength2: TSqlSmallInt, TableName: PSQLCHAR, + NameLength3: TSqlSmallInt, Unique: SqlUSmallInt, + Reserved: SqlUSmallInt): TSqlSmallInt {. + dynlib: odbclib, importc.} +proc SQLErr*(henv: SqlHEnv, hdbc: SqlHDBC, hstmt: SqlHStmt, + szSqlState, pfNativeError, szErrorMsg: PSQLCHAR, + cbErrorMsgMax: TSqlSmallInt, + pcbErrorMsg: PSQLSMALLINT): TSqlSmallInt {. + dynlib: odbclib, importc: "SQLError".} + +{.pop.} +when defined(nimHasStyleChecks): + {.pop.} diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index fcf52a8d9..694152db1 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -7,44 +7,33 @@ # distribution, for details about the copyright. # -## OpenSSL wrapper. Supports OpenSSL >= 1.1.0 dynamically (as default) or statically linked -## using `--dynlibOverride:ssl`. +## OpenSSL support ## -## `-d:sslVersion=1.2.3` can be used to force an SSL version. -## This version must be included in the library name. -## `-d:useOpenssl3` may be set for OpenSSL 3 instead. -## -## There is also limited support for OpenSSL 1.0.x which may require `-d:openssl10`. +## 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``. ## ## 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) +const useWinVersion = defined(Windows) or defined(nimdoc) -# To force openSSL version use -d:sslVersion=1.2.3 +# To force openSSL version use -d:sslVersion=1.0.0 # 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 @@ -63,31 +52,21 @@ when sslVersion != "": from posix import SocketHandle elif useWinVersion: - when defined(openssl10) or defined(nimOldDlls): - when defined(cpu64): - const - DLLSSLName* = "(ssleay32|ssleay64).dll" - DLLUtilName* = "(libeay32|libeay64).dll" - else: - const - DLLSSLName* = "ssleay32.dll" - DLLUtilName* = "libeay32.dll" - elif defined(cpu64): + when not defined(nimOldDlls) and defined(cpu64): const DLLSSLName* = "(libssl-1_1-x64|ssleay64|libssl64).dll" DLLUtilName* = "(libcrypto-1_1-x64|libeay64).dll" else: const DLLSSLName* = "(libssl-1_1|ssleay32|libssl32).dll" - DLLUtilName* = "(libcrypto-1_1|libeay32).dll" + DLLUtilName* = "(libcrypto-1_1|libeay32).dll" from winlean import SocketHandle else: - # same list of versions but ordered differently? when defined(osx): - 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|)" + 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|)" else: - 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|)" + 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|)" when defined(macosx): const @@ -105,9 +84,6 @@ else: import dynlib -{.pragma: lcrypto, cdecl, dynlib: DLLUtilName, importc.} -{.pragma: lssl, cdecl, dynlib: DLLSSLName, importc.} - type SslStruct {.final, pure.} = object SslPtr* = ptr SslStruct @@ -117,6 +93,7 @@ type PSTACK* = SslPtr PX509* = SslPtr PX509_NAME* = SslPtr + PEVP_MD* = SslPtr PBIO_METHOD* = SslPtr BIO* = SslPtr EVP_PKEY* = SslPtr @@ -280,9 +257,7 @@ 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 -const useStaticLink = compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks) - -when useStaticLink: +when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): # Static linking when defined(openssl10): @@ -293,8 +268,6 @@ when useStaticLink: proc getOpenSSLVersion*(): culong = SSLeay() - - proc ERR_load_BIO_strings*() {.cdecl, dynlib: DLLUtilName, importc.} else: proc OPENSSL_init_ssl*(opts: uint64, settings: uint8): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.} proc SSL_library_init*(): cint {.discardable.} = @@ -317,13 +290,10 @@ when useStaticLink: # Static linking against OpenSSL < 1.1.0 is not supported discard - proc ERR_load_BIO_strings*() = - discard - when defined(libressl) or defined(openssl10): 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 + SSl_state(ssl) and SSL_ST_INIT else: proc SSL_in_init*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.} proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.} @@ -333,10 +303,9 @@ when useStaticLink: 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.} - proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLUtilName, importc.} else: - # Here we're trying to stay compatible between openssl versions. Some + # Here we're trying to stay compatible with openssl 1.0.* and 1.1.*. Some # symbols are loaded dynamically and we don't use them if not found. proc thisModule(): LibHandle {.inline.} = var thisMod {.global.}: LibHandle @@ -344,12 +313,9 @@ else: result = thisMod - proc sslModule(): LibHandle {.inline, raises: [LibraryError], tags:[RootEffect].} = + proc sslModule(): LibHandle {.inline.} = var sslMod {.global.}: LibHandle - try: - if sslMod.isNil: sslMod = loadLibPattern(DLLSSLName) - except: - raise newException(LibraryError, "Could not load SSL using " & DLLSSLName) + if sslMod.isNil: sslMod = loadLibPattern(DLLSSLName) result = sslMod @@ -374,30 +340,26 @@ else: if result.isNil and alternativeName.len > 0: result = symAddr(thisDynlib, alternativeName) - proc sslSymNullable(name: string, alternativeName = ""): pointer {.raises: [LibraryError], tags:[RootEffect].} = + proc sslSymNullable(name: string, alternativeName = ""): pointer = sslModule().symNullable(name, alternativeName) - proc sslSymThrows(name: string, alternativeName = ""): pointer {.raises: [LibraryError].} = + proc sslSymThrows(name: string, alternativeName = ""): pointer = 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 {.raises: [LibraryError], tags:[RootEffect].} = + proc loadPSSLMethod(method1, method2: string): PSSL_METHOD = ## 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, raises: [].}](methodSym) + let method2Proc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe.}](methodSym) return method2Proc() - proc CRYPTO_set_mem_functions(a,b,c: pointer) = - let theProc = cast[proc(a,b,c: pointer) {.cdecl.}](utilModule().symNullable("CRYPTO_set_mem_functions")) - if not theProc.isNil: theProc(a, b, c) - proc SSL_library_init*(): cint {.discardable.} = ## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 otherwise ## SSL_library_init @@ -414,10 +376,6 @@ else: let theProc = cast[proc() {.cdecl.}](sslSymNullable("SSL_load_error_strings")) if not theProc.isNil: theProc() - proc ERR_load_BIO_strings*() = - let theProc = cast[proc() {.cdecl.}](utilModule().symNullable("ERR_load_BIO_strings")) - if not theProc.isNil: theProc() - proc SSLv23_client_method*(): PSSL_METHOD = loadPSSLMethod("SSLv23_client_method", "TLS_client_method") @@ -467,10 +425,12 @@ else: raiseInvalidLibrary MainProc proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint = - var theProc {.global.}: proc(ctx: SslCtx, str: cstring): cint {.cdecl, gcsafe.} + var theProc {.global.}: proc(ctx: SslCtx, str: cstring) {.cdecl, gcsafe.} if theProc.isNil: theProc = cast[typeof(theProc)](sslSymThrows("SSL_CTX_set_ciphersuites")) - result = theProc(ctx, str) + theProc(ctx, str) + +proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.} proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.} proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.} @@ -515,11 +475,11 @@ proc SSL_accept*(ssl: SslPtr): cint{.cdecl, dynlib: DLLSSLName, importc.} proc SSL_pending*(ssl: SslPtr): cint{.cdecl, dynlib: DLLSSLName, importc.} proc BIO_new_mem_buf*(data: pointer, len: cint): BIO{.cdecl, - dynlib: DLLUtilName, importc.} + dynlib: DLLSSLName, importc.} proc BIO_new_ssl_connect*(ctx: SslCtx): BIO{.cdecl, dynlib: DLLSSLName, importc.} proc BIO_ctrl*(bio: BIO, cmd: cint, larg: int, arg: cstring): int{.cdecl, - dynlib: DLLUtilName, importc.} + dynlib: DLLSSLName, importc.} proc BIO_get_ssl*(bio: BIO, ssl: ptr SslPtr): int = return BIO_ctrl(bio, BIO_C_GET_SSL, 0, cast[cstring](ssl)) proc BIO_set_conn_hostname*(bio: BIO, name: cstring): int = @@ -537,30 +497,30 @@ when not defined(nimfix): proc BIO_free*(b: BIO): cint{.cdecl, dynlib: DLLUtilName, importc.} -proc ERR_print_errors_fp*(fp: File){.cdecl, dynlib: DLLUtilName, importc.} +proc ERR_print_errors_fp*(fp: File){.cdecl, dynlib: DLLSSLName, importc.} proc ERR_error_string*(e: culong, buf: cstring): cstring{.cdecl, dynlib: DLLUtilName, importc.} proc ERR_get_error*(): culong{.cdecl, dynlib: DLLUtilName, importc.} proc ERR_peek_last_error*(): culong{.cdecl, dynlib: DLLUtilName, importc.} -proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLUtilName, importc.} +proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.} proc OPENSSL_sk_num*(stack: PSTACK): int {.cdecl, dynlib: DLLSSLName, importc.} proc OPENSSL_sk_value*(stack: PSTACK, index: int): pointer {.cdecl, dynlib: DLLSSLName, importc.} -proc d2i_X509*(px: ptr PX509, i: ptr ptr uint8, len: cint): PX509 {.cdecl, - dynlib: DLLUtilName, importc.} +proc d2i_X509*(px: ptr PX509, i: ptr ptr cuchar, len: cint): PX509 {.cdecl, + dynlib: DLLSSLName, importc.} -proc i2d_X509*(cert: PX509; o: ptr ptr uint8): cint {.cdecl, - dynlib: DLLUtilName, importc.} +proc i2d_X509*(cert: PX509; o: ptr ptr cuchar): cint {.cdecl, + dynlib: DLLSSLName, importc.} proc d2i_X509*(b: string): PX509 = ## decode DER/BER bytestring into X.509 certificate struct var bb = b.cstring - let i = cast[ptr ptr uint8](addr bb) + let i = cast[ptr ptr cuchar](addr bb) let ret = d2i_X509(addr result, i, b.len.cint) if ret.isNil: raise newException(Exception, "X.509 certificate decoding failed") @@ -570,15 +530,15 @@ proc i2d_X509*(cert: PX509): string = let encoded_length = i2d_X509(cert, nil) result = newString(encoded_length) var q = result.cstring - let o = cast[ptr ptr uint8](addr q) + let o = cast[ptr ptr cuchar](addr q) let length = i2d_X509(cert, o) if length.int <= 0: raise newException(Exception, "X.509 certificate encoding failed") -const - useNimsAlloc = not defined(nimNoAllocForSSL) and not defined(gcDestructors) +when not useWinVersion and not defined(macosx) and not defined(android) and not defined(nimNoAllocForSSL): + proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, + dynlib: DLLUtilName, importc.} -when not useWinVersion and not defined(macosx) and not defined(android) and useNimsAlloc: proc allocWrapper(size: int): pointer {.cdecl.} = allocShared(size) proc reallocWrapper(p: pointer; newSize: int): pointer {.cdecl.} = if p == nil: @@ -588,11 +548,9 @@ when not useWinVersion and not defined(macosx) and not defined(android) and useN proc deallocWrapper(p: pointer) {.cdecl.} = if p != nil: deallocShared(p) - proc CRYPTO_malloc_init*() = +proc CRYPTO_malloc_init*() = + when not useWinVersion and not defined(macosx) and not defined(android) and not defined(nimNoAllocForSSL): CRYPTO_set_mem_functions(allocWrapper, reallocWrapper, deallocWrapper) -else: - proc CRYPTO_malloc_init*() = - discard proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cint, larg: clong, parg: pointer): clong{. cdecl, dynlib: DLLSSLName, importc.} @@ -607,9 +565,10 @@ proc SSL_ctrl*(ssl: SslPtr, cmd: cint, larg: int, parg: pointer): int{. cdecl, dynlib: DLLSSLName, importc.} proc SSL_set_tlsext_host_name*(ssl: SslPtr, name: cstring): int = + result = SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name) ## Set the SNI server name extension to be used in a client hello. ## Returns 1 if SNI was set, 0 if current SSL configuration doesn't support SNI. - result = SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name) + proc SSL_get_servername*(ssl: SslPtr, typ: cint = TLSEXT_NAMETYPE_host_name): cstring {.cdecl, dynlib: DLLSSLName, importc.} ## Retrieve the server name requested in the client hello. This can be used @@ -627,16 +586,16 @@ proc SSL_CTX_set_tlsext_servername_callback*(ctx: SslCtx, cb: proc(ssl: SslPtr, result = SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cast[PFunction](cb)) proc SSL_CTX_set_tlsext_servername_arg*(ctx: SslCtx, arg: pointer): int = - ## Set the pointer to be used in the callback registered to `SSL_CTX_set_tlsext_servername_callback`. + ## Set the pointer to be used in the callback registered to ``SSL_CTX_set_tlsext_servername_callback``. result = SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, arg) type PskClientCallback* = proc (ssl: SslPtr; - hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr uint8; + hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar; max_psk_len: cuint): cuint {.cdecl.} PskServerCallback* = proc (ssl: SslPtr; - identity: cstring; psk: ptr uint8; max_psk_len: cint): cuint {.cdecl.} + identity: cstring; psk: ptr cuchar; max_psk_len: cint): cuint {.cdecl.} proc SSL_CTX_set_psk_client_callback*(ctx: SslCtx; callback: PskClientCallback) {.cdecl, dynlib: DLLSSLName, importc.} ## Set callback called when OpenSSL needs PSK (for client). @@ -687,78 +646,78 @@ proc sslDoHandshake*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc: "SSL_do_handshake".} + proc ErrClearError*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_clear_error".} proc ErrFreeStrings*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_free_strings".} proc ErrRemoveState*(pid: cint){.cdecl, dynlib: DLLUtilName, importc: "ERR_remove_state".} proc PEM_read_bio_RSA_PUBKEY*(bp: BIO, x: ptr PRSA, pw: pem_password_cb, u: pointer): PRSA {.cdecl, - dynlib: DLLUtilName, importc.} -proc PEM_read_RSA_PUBKEY*(fp: pointer; x: ptr PRSA; cb: pem_password_cb, u: pointer): PRSA {.cdecl, - dynlib: DLLUtilName, importc.} + dynlib: DLLSSLName, importc.} + proc RSA_verify*(kind: cint, origMsg: pointer, origMsgLen: cuint, signature: pointer, - signatureLen: cuint, rsa: PRSA): cint {.cdecl, dynlib: DLLUtilName, importc.} + signatureLen: cuint, rsa: PRSA): cint {.cdecl, dynlib: DLLSSLName, importc.} proc PEM_read_RSAPrivateKey*(fp: pointer; x: ptr PRSA; cb: pem_password_cb, u: pointer): PRSA {.cdecl, - dynlib: DLLUtilName, importc.} + dynlib: DLLSSLName, importc.} proc PEM_read_RSAPublicKey*(fp: pointer; x: ptr PRSA; cb: pem_password_cb, u: pointer): PRSA {.cdecl, - dynlib: DLLUtilName, importc.} + dynlib: DLLSSLName, importc.} proc PEM_read_bio_RSAPublicKey*(bp: BIO, x: ptr PRSA, cb: pem_password_cb, u: pointer): PRSA {.cdecl, - dynlib: DLLUtilName, importc.} + dynlib: DLLSSLName, importc.} proc PEM_read_bio_RSAPrivateKey*(bp: BIO, x: ptr PRSA, cb: pem_password_cb, u: pointer): PRSA {.cdecl, - dynlib: DLLUtilName, importc.} -proc RSA_private_encrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl, - dynlib: DLLUtilName, importc.} -proc RSA_public_encrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl, - dynlib: DLLUtilName, importc.} -proc RSA_private_decrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl, - dynlib: DLLUtilName, importc.} -proc RSA_public_decrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl, - dynlib: DLLUtilName, importc.} -proc RSA_free*(rsa: PRSA) {.cdecl, dynlib: DLLUtilName, importc.} -proc RSA_size*(rsa: PRSA): cint {.cdecl, dynlib: DLLUtilName, importc.} + dynlib: DLLSSLName, importc.} +proc RSA_private_encrypt*(flen: cint, fr: ptr cuchar, to: ptr cuchar, rsa: PRSA, padding: PaddingType): cint {.cdecl, + dynlib: DLLSSLName, importc.} +proc RSA_public_encrypt*(flen: cint, fr: ptr cuchar, to: ptr cuchar, rsa: PRSA, padding: PaddingType): cint {.cdecl, + dynlib: DLLSSLName, importc.} +proc RSA_private_decrypt*(flen: cint, fr: ptr cuchar, to: ptr cuchar, rsa: PRSA, padding: PaddingType): cint {.cdecl, + dynlib: DLLSSLName, importc.} +proc RSA_public_decrypt*(flen: cint, fr: ptr cuchar, to: ptr cuchar, rsa: PRSA, padding: PaddingType): cint {.cdecl, + dynlib: DLLSSLName, importc.} +proc RSA_free*(rsa: PRSA) {.cdecl, dynlib: DLLSSLName, importc.} +proc RSA_size*(rsa: PRSA): cint {.cdecl, dynlib: DLLSSLName, importc.} # sha types -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.} +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.} # 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 {.lcrypto.} +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.} # RSA key functions -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.} +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.} when defined(macosx) or defined(windows): - 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.} + 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.} else: # some times you will need this instead: - 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.} + 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".} # type @@ -777,8 +736,8 @@ type proc md5_Init*(c: var MD5_CTX): cint{.importc: "MD5_Init".} proc md5_Update*(c: var MD5_CTX; data: pointer; len: csize_t): cint{.importc: "MD5_Update".} proc md5_Final*(md: cstring; c: var MD5_CTX): cint{.importc: "MD5_Final".} -proc md5*(d: ptr uint8; n: csize_t; md: ptr uint8): ptr uint8{.importc: "MD5".} -proc md5_Transform*(c: var MD5_CTX; b: ptr uint8){.importc: "MD5_Transform".} +proc md5*(d: ptr cuchar; n: csize_t; md: ptr cuchar): ptr cuchar{.importc: "MD5".} +proc md5_Transform*(c: var MD5_CTX; b: ptr cuchar){.importc: "MD5_Transform".} {.pop.} from strutils import toHex, toLowerAscii @@ -801,13 +760,13 @@ proc md5_File*(file: string): string {.raises: [IOError,Exception].} = ctx: MD5_CTX discard md5_Init(ctx) - while (let bytes = f.readChars(buf); bytes > 0): + while(let bytes = f.readChars(buf, 0, sz); bytes > 0): discard md5_Update(ctx, buf[0].addr, cast[csize_t](bytes)) - discard md5_Final(cast[cstring](buf[0].addr), ctx) + discard md5_Final(buf[0].addr, ctx) f.close - result = hexStr(cast[cstring](addr buf)) + result = hexStr(addr buf) proc md5_Str*(str: string): string = ## Generate MD5 hash for a string. Result is a 32 character @@ -824,8 +783,8 @@ proc md5_Str*(str: string): string = discard md5_Update(ctx, input[i].addr, cast[csize_t](L)) i += L - discard md5_Final(cast[cstring](addr res), ctx) - result = hexStr(cast[cstring](addr res)) + discard md5_Final(addr res, ctx) + result = hexStr(addr res) when defined(nimHasStyleChecks): {.pop.} @@ -835,21 +794,8 @@ when defined(nimHasStyleChecks): # On old openSSL version some of these symbols are not available when not defined(nimDisableCertificateValidation) and not defined(windows): - # SSL_get_peer_certificate removed in 3.0 - # SSL_get1_peer_certificate added in 3.0 - 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) - elif useStaticLink: - proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc.} - else: - proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 = - let methodSym = sslSymNullable("SSL_get_peer_certificate", "SSL_get1_peer_certificate") - if methodSym.isNil: - raise newException(LibraryError, "Could not load SSL_get_peer_certificate or SSL_get1_peer_certificate") - let method2Proc = cast[proc(ssl: SslCtx): PX509 {.cdecl, gcsafe, raises: [].}](methodSym) - return method2Proc(ssl) + 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.} @@ -863,8 +809,6 @@ when not defined(nimDisableCertificateValidation) and not defined(windows): proc X509_check_host*(cert: PX509, name: cstring, namelen: cint, flags:cuint, peername: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.} - proc X509_free*(cert: PX509) {.cdecl, dynlib: DLLSSLName, importc.} - # Certificates store type PX509_STORE* = SslPtr @@ -888,24 +832,8 @@ when not defined(nimDisableCertificateValidation) and not defined(windows): {.pop.} when isMainModule: - when defined(nimPreviewSlimSystem): - import std/assertions # A simple certificate test let certbytes = readFile("certificate.der") let cert = d2i_X509(certbytes) let encoded = cert.i2d_X509() assert encoded == certbytes - -# Application Layer Protocol Negociation extension (TLS-ALPN, RFC7301) -# Available in at least OpenSSL 1.1.1 and later, not sure if earlier -# --Iced Quinn - -proc SSL_CTX_set_alpn_protos*(ctx: SslCtx; protos: cstring; protos_len: cuint): cint {.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_set_alpn_protos*(ssl: SslPtr; protos: cstring; protos_len: cuint): cint {.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_set_alpn_select_cb*(ctx: SslCtx; cb: proc(ssl: SslPtr; out_proto: ptr cstring; outlen: cstring; in_proto: cstring; inlen: cuint; arg: pointer): cint {.cdecl.}; arg: pointer): cint {.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_get0_alpn_selected*(ssl: SslPtr; data: ptr cstring; len: ptr cuint) {.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_set_next_protos_advertised_cb*(ctx: SslCtx; cb: proc(ssl: SslPtr; out_proto: ptr cstring; outlen: ptr cuint; arg: pointer): cint {.cdecl.}; arg: pointer) {.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_CTX_set_next_proto_select_cb*(ctx: SslCtx; cb: proc(s: SslPtr; out_proto: cstring; outlen: cstring; in_proto: cstring; inlen: cuint; arg: pointer): cint {.cdecl.}; arg: pointer) {.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_select_next_proto*(out_proto: ptr cstring; outlen: cstring; server: cstring; server_len: cuint; client: cstring; client_len: cuint): cint {.cdecl, dynlib: DLLSSLName, importc.} -proc SSL_get0_next_proto_negotiated*(s: SslPtr; data: ptr cstring; len: ptr cuint) {.cdecl, dynlib: DLLSSLName, importc.} - diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim new file mode 100644 index 000000000..24eac54f9 --- /dev/null +++ b/lib/wrappers/postgres.nim @@ -0,0 +1,372 @@ +# +# +# 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 contains the definitions for structures and externs for +# functions used by frontend postgres applications. It is based on +# Postgresql's libpq-fe.h. +# +# It is for postgreSQL version 7.4 and higher with support for the v3.0 +# connection-protocol. +# + +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +when defined(windows): + const + dllName = "libpq.dll" +elif defined(macosx): + const + dllName = "libpq.dylib" +else: + const + dllName = "libpq.so(.5|)" +type + POid* = ptr Oid + Oid* = int32 + +const + ERROR_MSG_LENGTH* = 4096 + CMDSTATUS_LEN* = 40 + +type + SockAddr* = array[1..112, int8] + PGresAttDesc*{.pure, final.} = object + name*: cstring + adtid*: Oid + adtsize*: int + + PPGresAttDesc* = ptr PGresAttDesc + PPPGresAttDesc* = ptr PPGresAttDesc + PGresAttValue*{.pure, final.} = object + length*: int32 + value*: cstring + + PPGresAttValue* = ptr PGresAttValue + PPPGresAttValue* = ptr PPGresAttValue + PExecStatusType* = ptr ExecStatusType + ExecStatusType* = enum + PGRES_EMPTY_QUERY = 0, PGRES_COMMAND_OK, PGRES_TUPLES_OK, PGRES_COPY_OUT, + PGRES_COPY_IN, PGRES_BAD_RESPONSE, PGRES_NONFATAL_ERROR, PGRES_FATAL_ERROR + PGlobjfuncs*{.pure, final.} = object + fn_lo_open*: Oid + fn_lo_close*: Oid + fn_lo_creat*: Oid + fn_lo_unlink*: Oid + fn_lo_lseek*: Oid + fn_lo_tell*: Oid + fn_lo_read*: Oid + fn_lo_write*: Oid + + PPGlobjfuncs* = ptr PGlobjfuncs + PConnStatusType* = ptr ConnStatusType + ConnStatusType* = enum + CONNECTION_OK, CONNECTION_BAD, CONNECTION_STARTED, CONNECTION_MADE, + CONNECTION_AWAITING_RESPONSE, CONNECTION_AUTH_OK, CONNECTION_SETENV, + CONNECTION_SSL_STARTUP, CONNECTION_NEEDED + PGconn*{.pure, final.} = object + pghost*: cstring + pgtty*: cstring + pgport*: cstring + pgoptions*: cstring + dbName*: cstring + status*: ConnStatusType + errorMessage*: array[0..(ERROR_MSG_LENGTH) - 1, char] + Pfin*: File + Pfout*: File + Pfdebug*: File + sock*: int32 + laddr*: SockAddr + raddr*: SockAddr + salt*: array[0..(2) - 1, char] + asyncNotifyWaiting*: int32 + notifyList*: pointer + pguser*: cstring + pgpass*: cstring + lobjfuncs*: PPGlobjfuncs + + PPGconn* = ptr PGconn + PGresult*{.pure, final.} = object + ntups*: int32 + numAttributes*: int32 + attDescs*: PPGresAttDesc + tuples*: PPPGresAttValue + tupArrSize*: int32 + resultStatus*: ExecStatusType + cmdStatus*: array[0..(CMDSTATUS_LEN) - 1, char] + binary*: int32 + conn*: PPGconn + + PPGresult* = ptr PGresult + PPostgresPollingStatusType* = ptr PostgresPollingStatusType + PostgresPollingStatusType* = enum + PGRES_POLLING_FAILED = 0, PGRES_POLLING_READING, PGRES_POLLING_WRITING, + PGRES_POLLING_OK, PGRES_POLLING_ACTIVE + PPGTransactionStatusType* = ptr PGTransactionStatusType + PGTransactionStatusType* = enum + PQTRANS_IDLE, PQTRANS_ACTIVE, PQTRANS_INTRANS, PQTRANS_INERROR, + PQTRANS_UNKNOWN + PPGVerbosity* = ptr PGVerbosity + PGVerbosity* = enum + PQERRORS_TERSE, PQERRORS_DEFAULT, PQERRORS_VERBOSE + PPGNotify* = ptr pgNotify + pgNotify*{.pure, final.} = object + relname*: cstring + be_pid*: int32 + extra*: cstring + + PQnoticeReceiver* = proc (arg: pointer, res: PPGresult){.cdecl.} + PQnoticeProcessor* = proc (arg: pointer, message: cstring){.cdecl.} + Ppqbool* = ptr pqbool + pqbool* = char + PPQprintOpt* = ptr PQprintOpt + PQprintOpt*{.pure, final.} = object + header*: pqbool + align*: pqbool + standard*: pqbool + html3*: pqbool + expanded*: pqbool + pager*: pqbool + fieldSep*: cstring + tableOpt*: cstring + caption*: cstring + fieldName*: ptr cstring + + PPQconninfoOption* = ptr PQconninfoOption + PQconninfoOption*{.pure, final.} = object + keyword*: cstring + envvar*: cstring + compiled*: cstring + val*: cstring + label*: cstring + dispchar*: cstring + dispsize*: int32 + + PPQArgBlock* = ptr PQArgBlock + PQArgBlock*{.pure, final.} = object + length*: int32 + isint*: int32 + p*: pointer + +proc pqinitOpenSSL*(do_ssl: int32, do_crypto: int32) {.cdecl, dynlib: dllName, + importc: "PQinitOpenSSL".} +proc pqconnectStart*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, + importc: "PQconnectStart".} +proc pqconnectPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, + dynlib: dllName, importc: "PQconnectPoll".} +proc pqconnectdb*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, + importc: "PQconnectdb".} +proc pqsetdbLogin*(pghost: cstring, pgport: cstring, pgoptions: cstring, + pgtty: cstring, dbName: cstring, login: cstring, pwd: cstring): PPGconn{. + cdecl, dynlib: dllName, importc: "PQsetdbLogin".} +proc pqsetdb*(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPGconn +proc pqfinish*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQfinish".} +proc pqconndefaults*(): PPQconninfoOption{.cdecl, dynlib: dllName, + importc: "PQconndefaults".} +proc pqconninfoFree*(connOptions: PPQconninfoOption){.cdecl, dynlib: dllName, + importc: "PQconninfoFree".} +proc pqresetStart*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQresetStart".} +proc pqresetPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, + dynlib: dllName, importc: "PQresetPoll".} +proc pqreset*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQreset".} +proc pqrequestCancel*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQrequestCancel".} +proc pqdb*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQdb".} +proc pquser*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQuser".} +proc pqpass*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQpass".} +proc pqhost*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQhost".} +proc pqport*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQport".} +proc pqtty*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQtty".} +proc pqoptions*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, + importc: "PQoptions".} +proc pqstatus*(conn: PPGconn): ConnStatusType{.cdecl, dynlib: dllName, + importc: "PQstatus".} +proc pqtransactionStatus*(conn: PPGconn): PGTransactionStatusType{.cdecl, + dynlib: dllName, importc: "PQtransactionStatus".} +proc pqparameterStatus*(conn: PPGconn, paramName: cstring): cstring{.cdecl, + dynlib: dllName, importc: "PQparameterStatus".} +proc pqserverVersion*(conn: PPGconn): int32{.cdecl, + dynlib: dllName, importc: "PQserverVersion".} +proc pqprotocolVersion*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQprotocolVersion".} +proc pqerrorMessage*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, + importc: "PQerrorMessage".} +proc pqsocket*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQsocket".} +proc pqbackendPID*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQbackendPID".} +proc pqconnectionNeedsPassword*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQconnectionNeedsPassword".} +proc pqconnectionUsedPassword*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQconnectionUsedPassword".} +proc pqclientEncoding*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQclientEncoding".} +proc pqsetClientEncoding*(conn: PPGconn, encoding: cstring): int32{.cdecl, + dynlib: dllName, importc: "PQsetClientEncoding".} +when defined(USE_SSL): + # Get the SSL structure associated with a connection + proc pqgetssl*(conn: PPGconn): PSSL{.cdecl, dynlib: dllName, + importc: "PQgetssl".} +proc pqsetErrorVerbosity*(conn: PPGconn, verbosity: PGVerbosity): PGVerbosity{. + cdecl, dynlib: dllName, importc: "PQsetErrorVerbosity".} +proc pqtrace*(conn: PPGconn, debug_port: File){.cdecl, dynlib: dllName, + importc: "PQtrace".} +proc pquntrace*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQuntrace".} +proc pqsetNoticeReceiver*(conn: PPGconn, theProc: PQnoticeReceiver, arg: pointer): PQnoticeReceiver{. + cdecl, dynlib: dllName, importc: "PQsetNoticeReceiver".} +proc pqsetNoticeProcessor*(conn: PPGconn, theProc: PQnoticeProcessor, + arg: pointer): PQnoticeProcessor{.cdecl, + dynlib: dllName, importc: "PQsetNoticeProcessor".} +proc pqexec*(conn: PPGconn, query: cstring): PPGresult{.cdecl, dynlib: dllName, + importc: "PQexec".} +proc pqexecParams*(conn: PPGconn, command: cstring, nParams: int32, + paramTypes: POid, paramValues: cstringArray, + paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{. + cdecl, dynlib: dllName, importc: "PQexecParams".} +proc pqprepare*(conn: PPGconn, stmtName, query: cstring, nParams: int32, + paramTypes: POid): PPGresult{.cdecl, dynlib: dllName, importc: "PQprepare".} +proc pqexecPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, + paramValues: cstringArray, + paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{. + cdecl, dynlib: dllName, importc: "PQexecPrepared".} +proc pqsendQuery*(conn: PPGconn, query: cstring): int32{.cdecl, dynlib: dllName, + importc: "PQsendQuery".} +proc pqsendQueryParams*(conn: PPGconn, command: cstring, nParams: int32, + paramTypes: POid, paramValues: cstringArray, + paramLengths, paramFormats: ptr int32, + resultFormat: int32): int32{.cdecl, dynlib: dllName, + importc: "PQsendQueryParams".} +proc pqsendQueryPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, + paramValues: cstringArray, + paramLengths, paramFormats: ptr int32, + resultFormat: int32): int32{.cdecl, dynlib: dllName, + importc: "PQsendQueryPrepared".} +proc pqgetResult*(conn: PPGconn): PPGresult{.cdecl, dynlib: dllName, + importc: "PQgetResult".} +proc pqisBusy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQisBusy".} +proc pqconsumeInput*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQconsumeInput".} +proc pqnotifies*(conn: PPGconn): PPGNotify{.cdecl, dynlib: dllName, + importc: "PQnotifies".} +proc pqputCopyData*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{. + cdecl, dynlib: dllName, importc: "PQputCopyData".} +proc pqputCopyEnd*(conn: PPGconn, errormsg: cstring): int32{.cdecl, + dynlib: dllName, importc: "PQputCopyEnd".} +proc pqgetCopyData*(conn: PPGconn, buffer: cstringArray, async: int32): int32{. + cdecl, dynlib: dllName, importc: "PQgetCopyData".} +proc pqgetline*(conn: PPGconn, str: cstring, len: int32): int32{.cdecl, + dynlib: dllName, importc: "PQgetline".} +proc pqputline*(conn: PPGconn, str: cstring): int32{.cdecl, dynlib: dllName, + importc: "PQputline".} +proc pqgetlineAsync*(conn: PPGconn, buffer: cstring, bufsize: int32): int32{. + cdecl, dynlib: dllName, importc: "PQgetlineAsync".} +proc pqputnbytes*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.cdecl, + dynlib: dllName, importc: "PQputnbytes".} +proc pqendcopy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQendcopy".} +proc pqsetnonblocking*(conn: PPGconn, arg: int32): int32{.cdecl, + dynlib: dllName, importc: "PQsetnonblocking".} +proc pqisnonblocking*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQisnonblocking".} +proc pqflush*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQflush".} +proc pqfn*(conn: PPGconn, fnid: int32, result_buf, result_len: ptr int32, + result_is_int: int32, args: PPQArgBlock, nargs: int32): PPGresult{. + cdecl, dynlib: dllName, importc: "PQfn".} +proc pqresultStatus*(res: PPGresult): ExecStatusType{.cdecl, dynlib: dllName, + importc: "PQresultStatus".} +proc pqresStatus*(status: ExecStatusType): cstring{.cdecl, dynlib: dllName, + importc: "PQresStatus".} +proc pqresultErrorMessage*(res: PPGresult): cstring{.cdecl, dynlib: dllName, + importc: "PQresultErrorMessage".} +proc pqresultErrorField*(res: PPGresult, fieldcode: int32): cstring{.cdecl, + dynlib: dllName, importc: "PQresultErrorField".} +proc pqntuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, + importc: "PQntuples".} +proc pqnfields*(res: PPGresult): int32{.cdecl, dynlib: dllName, + importc: "PQnfields".} +proc pqbinaryTuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, + importc: "PQbinaryTuples".} +proc pqfname*(res: PPGresult, field_num: int32): cstring{.cdecl, + dynlib: dllName, importc: "PQfname".} +proc pqfnumber*(res: PPGresult, field_name: cstring): int32{.cdecl, + dynlib: dllName, importc: "PQfnumber".} +proc pqftable*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, + importc: "PQftable".} +proc pqftablecol*(res: PPGresult, field_num: int32): int32{.cdecl, + dynlib: dllName, importc: "PQftablecol".} +proc pqfformat*(res: PPGresult, field_num: int32): int32{.cdecl, + dynlib: dllName, importc: "PQfformat".} +proc pqftype*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, + importc: "PQftype".} +proc pqfsize*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, + importc: "PQfsize".} +proc pqfmod*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, + importc: "PQfmod".} +proc pqcmdStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, + importc: "PQcmdStatus".} +proc pqoidStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, + importc: "PQoidStatus".} +proc pqoidValue*(res: PPGresult): Oid{.cdecl, dynlib: dllName, + importc: "PQoidValue".} +proc pqcmdTuples*(res: PPGresult): cstring{.cdecl, dynlib: dllName, + importc: "PQcmdTuples".} +proc pqgetvalue*(res: PPGresult, tup_num: int32, field_num: int32): cstring{. + cdecl, dynlib: dllName, importc: "PQgetvalue".} +proc pqgetlength*(res: PPGresult, tup_num: int32, field_num: int32): int32{. + cdecl, dynlib: dllName, importc: "PQgetlength".} +proc pqgetisnull*(res: PPGresult, tup_num: int32, field_num: int32): int32{. + cdecl, dynlib: dllName, importc: "PQgetisnull".} +proc pqclear*(res: PPGresult){.cdecl, dynlib: dllName, importc: "PQclear".} +proc pqfreemem*(p: pointer){.cdecl, dynlib: dllName, importc: "PQfreemem".} +proc pqmakeEmptyPGresult*(conn: PPGconn, status: ExecStatusType): PPGresult{. + cdecl, dynlib: dllName, importc: "PQmakeEmptyPGresult".} +proc pqescapeString*(till, `from`: cstring, len: int): int{.cdecl, + dynlib: dllName, importc: "PQescapeString".} +proc pqescapeBytea*(bintext: cstring, binlen: int, bytealen: var int): cstring{. + cdecl, dynlib: dllName, importc: "PQescapeBytea".} +proc pqunescapeBytea*(strtext: cstring, retbuflen: var int): cstring{.cdecl, + dynlib: dllName, importc: "PQunescapeBytea".} +proc pqprint*(fout: File, res: PPGresult, ps: PPQprintOpt){.cdecl, + dynlib: dllName, importc: "PQprint".} +proc pqdisplayTuples*(res: PPGresult, fp: File, fillAlign: int32, + fieldSep: cstring, printHeader: int32, quiet: int32){. + cdecl, dynlib: dllName, importc: "PQdisplayTuples".} +proc pqprintTuples*(res: PPGresult, fout: File, printAttName: int32, + terseOutput: int32, width: int32){.cdecl, dynlib: dllName, + importc: "PQprintTuples".} +proc lo_open*(conn: PPGconn, lobjId: Oid, mode: int32): int32{.cdecl, + dynlib: dllName, importc: "lo_open".} +proc lo_close*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName, + importc: "lo_close".} +proc lo_read*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{. + cdecl, dynlib: dllName, importc: "lo_read".} +proc lo_write*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{. + cdecl, dynlib: dllName, importc: "lo_write".} +proc lo_lseek*(conn: PPGconn, fd: int32, offset: int32, whence: int32): int32{. + cdecl, dynlib: dllName, importc: "lo_lseek".} +proc lo_creat*(conn: PPGconn, mode: int32): Oid{.cdecl, dynlib: dllName, + importc: "lo_creat".} +proc lo_tell*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName, + importc: "lo_tell".} +proc lo_unlink*(conn: PPGconn, lobjId: Oid): int32{.cdecl, dynlib: dllName, + importc: "lo_unlink".} +proc lo_import*(conn: PPGconn, filename: cstring): Oid{.cdecl, dynlib: dllName, + importc: "lo_import".} +proc lo_export*(conn: PPGconn, lobjId: Oid, filename: cstring): int32{.cdecl, + dynlib: dllName, importc: "lo_export".} +proc pqmblen*(s: cstring, encoding: int32): int32{.cdecl, dynlib: dllName, + importc: "PQmblen".} +proc pqenv2encoding*(): int32{.cdecl, dynlib: dllName, importc: "PQenv2encoding".} +proc pqsetdb(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPGconn = + result = pqsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, "", "") + +when defined(nimHasStyleChecks): + {.pop.} diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim new file mode 100644 index 000000000..6fc6c7aa7 --- /dev/null +++ b/lib/wrappers/sqlite3.nim @@ -0,0 +1,392 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +when defined(windows): + when defined(nimOldDlls): + const Lib = "sqlite3.dll" + elif defined(cpu64): + const Lib = "sqlite3_64.dll" + else: + const Lib = "sqlite3_32.dll" +elif defined(macosx): + const + Lib = "libsqlite3(|.0).dylib" +else: + const + Lib = "libsqlite3.so(|.0)" + +when defined(staticSqlite): + {.pragma: mylib.} + {.compile: "sqlite3.c".} +else: + {.pragma: mylib, dynlib: Lib.} + +const + SQLITE_INTEGER* = 1 + SQLITE_FLOAT* = 2 + SQLITE_BLOB* = 4 + SQLITE_NULL* = 5 + SQLITE_TEXT* = 3 + SQLITE_UTF8* = 1 + SQLITE_UTF16LE* = 2 + SQLITE_UTF16BE* = 3 # Use native byte order + SQLITE_UTF16* = 4 # sqlite3_create_function only + SQLITE_ANY* = 5 #sqlite_exec return values + SQLITE_OK* = 0 + SQLITE_ERROR* = 1 # SQL error or missing database + SQLITE_INTERNAL* = 2 # An internal logic error in SQLite + SQLITE_PERM* = 3 # Access permission denied + SQLITE_ABORT* = 4 # Callback routine requested an abort + SQLITE_BUSY* = 5 # The database file is locked + SQLITE_LOCKED* = 6 # A table in the database is locked + SQLITE_NOMEM* = 7 # A malloc() failed + SQLITE_READONLY* = 8 # Attempt to write a readonly database + SQLITE_INTERRUPT* = 9 # Operation terminated by sqlite3_interrupt() + SQLITE_IOERR* = 10 # Some kind of disk I/O error occurred + SQLITE_CORRUPT* = 11 # The database disk image is malformed + SQLITE_NOTFOUND* = 12 # (Internal Only) Table or record not found + SQLITE_FULL* = 13 # Insertion failed because database is full + SQLITE_CANTOPEN* = 14 # Unable to open the database file + SQLITE_PROTOCOL* = 15 # Database lock protocol error + SQLITE_EMPTY* = 16 # Database is empty + SQLITE_SCHEMA* = 17 # The database schema changed + SQLITE_TOOBIG* = 18 # Too much data for one row of a table + SQLITE_CONSTRAINT* = 19 # Abort due to constraint violation + SQLITE_MISMATCH* = 20 # Data type mismatch + SQLITE_MISUSE* = 21 # Library used incorrectly + SQLITE_NOLFS* = 22 # Uses OS features not supported on host + SQLITE_AUTH* = 23 # Authorization denied + SQLITE_FORMAT* = 24 # Auxiliary database format error + SQLITE_RANGE* = 25 # 2nd parameter to sqlite3_bind out of range + SQLITE_NOTADB* = 26 # File opened that is not a database file + SQLITE_ROW* = 100 # sqlite3_step() has another row ready + SQLITE_DONE* = 101 # sqlite3_step() has finished executing + SQLITE_COPY* = 0 + SQLITE_CREATE_INDEX* = 1 + SQLITE_CREATE_TABLE* = 2 + SQLITE_CREATE_TEMP_INDEX* = 3 + SQLITE_CREATE_TEMP_TABLE* = 4 + SQLITE_CREATE_TEMP_TRIGGER* = 5 + SQLITE_CREATE_TEMP_VIEW* = 6 + SQLITE_CREATE_TRIGGER* = 7 + SQLITE_CREATE_VIEW* = 8 + SQLITE_DELETE* = 9 + SQLITE_DROP_INDEX* = 10 + SQLITE_DROP_TABLE* = 11 + SQLITE_DROP_TEMP_INDEX* = 12 + SQLITE_DROP_TEMP_TABLE* = 13 + SQLITE_DROP_TEMP_TRIGGER* = 14 + SQLITE_DROP_TEMP_VIEW* = 15 + SQLITE_DROP_TRIGGER* = 16 + SQLITE_DROP_VIEW* = 17 + SQLITE_INSERT* = 18 + SQLITE_PRAGMA* = 19 + SQLITE_READ* = 20 + SQLITE_SELECT* = 21 + SQLITE_TRANSACTION* = 22 + SQLITE_UPDATE* = 23 + SQLITE_ATTACH* = 24 + SQLITE_DETACH* = 25 + SQLITE_ALTER_TABLE* = 26 + SQLITE_REINDEX* = 27 + SQLITE_DENY* = 1 + SQLITE_IGNORE* = 2 # Original from sqlite3.h: + #define SQLITE_STATIC ((void(*)(void *))0) + #define SQLITE_TRANSIENT ((void(*)(void *))-1) + SQLITE_DETERMINISTIC* = 0x800 + +type + Sqlite3 {.pure, final.} = object + PSqlite3* = ptr Sqlite3 + PPSqlite3* = ptr PSqlite3 + Sqlite3_Backup {.pure, final.} = object + PSqlite3_Backup* = ptr Sqlite3_Backup + PPSqlite3_Backup* = ptr PSqlite3_Backup + Context{.pure, final.} = object + Pcontext* = ptr Context + TStmt{.pure, final.} = object + PStmt* = ptr TStmt + Value{.pure, final.} = object + PValue* = ptr Value + PValueArg* = array[0..127, PValue] + + Callback* = proc (para1: pointer, para2: int32, para3, + para4: cstringArray): int32{.cdecl.} + Tbind_destructor_func* = proc (para1: pointer){.cdecl, locks: 0, tags: [], gcsafe.} + Create_function_step_func* = proc (para1: Pcontext, para2: int32, + para3: PValueArg){.cdecl.} + Create_function_func_func* = proc (para1: Pcontext, para2: int32, + para3: PValueArg){.cdecl.} + Create_function_final_func* = proc (para1: Pcontext){.cdecl.} + Result_func* = proc (para1: pointer){.cdecl.} + Create_collation_func* = proc (para1: pointer, para2: int32, para3: pointer, + para4: int32, para5: pointer): int32{.cdecl.} + Collation_needed_func* = proc (para1: pointer, para2: PSqlite3, eTextRep: int32, + para4: cstring){.cdecl.} + +const + SQLITE_STATIC* = nil + SQLITE_TRANSIENT* = cast[Tbind_destructor_func](-1) + +proc close*(para1: PSqlite3): int32{.cdecl, mylib, importc: "sqlite3_close".} +proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer, + errmsg: var cstring): int32{.cdecl, mylib, + importc: "sqlite3_exec".} +proc last_insert_rowid*(para1: PSqlite3): int64{.cdecl, mylib, + importc: "sqlite3_last_insert_rowid".} +proc changes*(para1: PSqlite3): int32{.cdecl, mylib, importc: "sqlite3_changes".} +proc total_changes*(para1: PSqlite3): int32{.cdecl, mylib, + importc: "sqlite3_total_changes".} +proc interrupt*(para1: PSqlite3){.cdecl, mylib, importc: "sqlite3_interrupt".} +proc complete*(sql: cstring): int32{.cdecl, mylib, + importc: "sqlite3_complete".} +proc complete16*(sql: pointer): int32{.cdecl, mylib, + importc: "sqlite3_complete16".} +proc busy_handler*(para1: PSqlite3, + para2: proc (para1: pointer, para2: int32): int32{.cdecl.}, + para3: pointer): int32{.cdecl, mylib, + importc: "sqlite3_busy_handler".} +proc busy_timeout*(para1: PSqlite3, ms: int32): int32{.cdecl, mylib, + importc: "sqlite3_busy_timeout".} +proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray, + nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl, + mylib, importc: "sqlite3_get_table".} +proc free_table*(result: cstringArray){.cdecl, mylib, + importc: "sqlite3_free_table".} + # Todo: see how translate sqlite3_mprintf, sqlite3_vmprintf, sqlite3_snprintf + # function sqlite3_mprintf(_para1:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_mprintf'; +proc mprintf*(para1: cstring): cstring{.cdecl, varargs, mylib, + importc: "sqlite3_mprintf".} + #function sqlite3_vmprintf(_para1:Pchar; _para2:va_list):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_vmprintf'; +proc free*(z: cstring){.cdecl, mylib, importc: "sqlite3_free".} + #function sqlite3_snprintf(_para1:longint; _para2:Pchar; _para3:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_snprintf'; +proc snprintf*(para1: int32, para2: cstring, para3: cstring): cstring{.cdecl, + mylib, varargs, importc: "sqlite3_snprintf".} +proc set_authorizer*(para1: PSqlite3, xAuth: proc (para1: pointer, para2: int32, + para3: cstring, para4: cstring, para5: cstring, para6: cstring): int32{. + cdecl.}, pUserData: pointer): int32{.cdecl, mylib, + importc: "sqlite3_set_authorizer".} +proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.}, + para3: pointer): pointer{.cdecl, mylib, + importc: "sqlite3_trace".} +proc progress_handler*(para1: PSqlite3, para2: int32, + para3: proc (para1: pointer): int32{.cdecl.}, + para4: pointer){.cdecl, mylib, + importc: "sqlite3_progress_handler".} +proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.}, + para3: pointer): pointer{.cdecl, mylib, + importc: "sqlite3_commit_hook".} +proc open*(filename: cstring, ppDb: var PSqlite3): int32{.cdecl, mylib, + importc: "sqlite3_open".} +proc open16*(filename: pointer, ppDb: var PSqlite3): int32{.cdecl, mylib, + importc: "sqlite3_open16".} +proc errcode*(db: PSqlite3): int32{.cdecl, mylib, importc: "sqlite3_errcode".} +proc errmsg*(para1: PSqlite3): cstring{.cdecl, mylib, importc: "sqlite3_errmsg".} +proc errmsg16*(para1: PSqlite3): pointer{.cdecl, mylib, + importc: "sqlite3_errmsg16".} +proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var PStmt, + pzTail: ptr cstring): int32{.cdecl, mylib, + importc: "sqlite3_prepare".} + +proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var PStmt, + pzTail: ptr cstring): cint {. + importc: "sqlite3_prepare_v2", cdecl, mylib.} + +proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var PStmt, + pzTail: var pointer): int32{.cdecl, mylib, + importc: "sqlite3_prepare16".} +proc bind_blob*(para1: PStmt, para2: int32, para3: pointer, n: int32, + para5: Tbind_destructor_func): int32{.cdecl, mylib, + importc: "sqlite3_bind_blob".} +proc bind_double*(para1: PStmt, para2: int32, para3: float64): int32{.cdecl, + mylib, importc: "sqlite3_bind_double".} +proc bind_int*(para1: PStmt, para2: int32, para3: int32): int32{.cdecl, + mylib, importc: "sqlite3_bind_int".} +proc bind_int64*(para1: PStmt, para2: int32, para3: int64): int32{.cdecl, + mylib, importc: "sqlite3_bind_int64".} +proc bind_null*(para1: PStmt, para2: int32): int32{.cdecl, mylib, + importc: "sqlite3_bind_null".} +proc bind_text*(para1: PStmt, para2: int32, para3: cstring, n: int32, + para5: Tbind_destructor_func): int32{.cdecl, mylib, + importc: "sqlite3_bind_text".} +proc bind_text16*(para1: PStmt, para2: int32, para3: pointer, para4: int32, + para5: Tbind_destructor_func): int32{.cdecl, mylib, + importc: "sqlite3_bind_text16".} + #function sqlite3_bind_value(_para1:Psqlite3_stmt; _para2:longint; _para3:Psqlite3_value):longint;cdecl; external Sqlite3Lib name 'sqlite3_bind_value'; + #These overloaded functions were introduced to allow the use of SQLITE_STATIC and SQLITE_TRANSIENT + #It's the c world man ;-) +proc bind_blob*(para1: PStmt, para2: int32, para3: pointer, n: int32, + para5: int32): int32{.cdecl, mylib, + importc: "sqlite3_bind_blob".} +proc bind_text*(para1: PStmt, para2: int32, para3: cstring, n: int32, + para5: int32): int32{.cdecl, mylib, + importc: "sqlite3_bind_text".} +proc bind_text16*(para1: PStmt, para2: int32, para3: pointer, para4: int32, + para5: int32): int32{.cdecl, mylib, + importc: "sqlite3_bind_text16".} +proc bind_parameter_count*(para1: PStmt): int32{.cdecl, mylib, + importc: "sqlite3_bind_parameter_count".} +proc bind_parameter_name*(para1: PStmt, para2: int32): cstring{.cdecl, + mylib, importc: "sqlite3_bind_parameter_name".} +proc bind_parameter_index*(para1: PStmt, zName: cstring): int32{.cdecl, + mylib, importc: "sqlite3_bind_parameter_index".} +proc clear_bindings*(para1: PStmt): int32 {.cdecl, + mylib, importc: "sqlite3_clear_bindings".} +proc column_count*(PStmt: PStmt): int32{.cdecl, mylib, + importc: "sqlite3_column_count".} +proc column_name*(para1: PStmt, para2: int32): cstring{.cdecl, mylib, + importc: "sqlite3_column_name".} +proc column_table_name*(para1: PStmt; para2: int32): cstring{.cdecl, mylib, + importc: "sqlite3_column_table_name".} +proc column_name16*(para1: PStmt, para2: int32): pointer{.cdecl, mylib, + importc: "sqlite3_column_name16".} +proc column_decltype*(para1: PStmt, i: int32): cstring{.cdecl, mylib, + importc: "sqlite3_column_decltype".} +proc column_decltype16*(para1: PStmt, para2: int32): pointer{.cdecl, + mylib, importc: "sqlite3_column_decltype16".} +proc step*(para1: PStmt): int32{.cdecl, mylib, importc: "sqlite3_step".} +proc data_count*(PStmt: PStmt): int32{.cdecl, mylib, + importc: "sqlite3_data_count".} +proc column_blob*(para1: PStmt, iCol: int32): pointer{.cdecl, mylib, + importc: "sqlite3_column_blob".} +proc column_bytes*(para1: PStmt, iCol: int32): int32{.cdecl, mylib, + importc: "sqlite3_column_bytes".} +proc column_bytes16*(para1: PStmt, iCol: int32): int32{.cdecl, mylib, + importc: "sqlite3_column_bytes16".} +proc column_double*(para1: PStmt, iCol: int32): float64{.cdecl, mylib, + importc: "sqlite3_column_double".} +proc column_int*(para1: PStmt, iCol: int32): int32{.cdecl, mylib, + importc: "sqlite3_column_int".} +proc column_int64*(para1: PStmt, iCol: int32): int64{.cdecl, mylib, + importc: "sqlite3_column_int64".} +proc column_text*(para1: PStmt, iCol: int32): cstring{.cdecl, mylib, + importc: "sqlite3_column_text".} +proc column_text16*(para1: PStmt, iCol: int32): pointer{.cdecl, mylib, + importc: "sqlite3_column_text16".} +proc column_type*(para1: PStmt, iCol: int32): int32{.cdecl, mylib, + importc: "sqlite3_column_type".} +proc finalize*(PStmt: PStmt): int32{.cdecl, mylib, + importc: "sqlite3_finalize".} +proc reset*(PStmt: PStmt): int32{.cdecl, mylib, importc: "sqlite3_reset".} +proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32, + eTextRep: int32, para5: pointer, + xFunc: Create_function_func_func, + xStep: Create_function_step_func, + xFinal: Create_function_final_func): int32{.cdecl, + mylib, importc: "sqlite3_create_function".} +proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32, + eTextRep: int32, para5: pointer, + xFunc: Create_function_func_func, + xStep: Create_function_step_func, + xFinal: Create_function_final_func): int32{.cdecl, + mylib, importc: "sqlite3_create_function16".} +proc aggregate_count*(para1: Pcontext): int32{.cdecl, mylib, + importc: "sqlite3_aggregate_count".} +proc value_blob*(para1: PValue): pointer{.cdecl, mylib, + importc: "sqlite3_value_blob".} +proc value_bytes*(para1: PValue): int32{.cdecl, mylib, + importc: "sqlite3_value_bytes".} +proc value_bytes16*(para1: PValue): int32{.cdecl, mylib, + importc: "sqlite3_value_bytes16".} +proc value_double*(para1: PValue): float64{.cdecl, mylib, + importc: "sqlite3_value_double".} +proc value_int*(para1: PValue): int32{.cdecl, mylib, + importc: "sqlite3_value_int".} +proc value_int64*(para1: PValue): int64{.cdecl, mylib, + importc: "sqlite3_value_int64".} +proc value_text*(para1: PValue): cstring{.cdecl, mylib, + importc: "sqlite3_value_text".} +proc value_text16*(para1: PValue): pointer{.cdecl, mylib, + importc: "sqlite3_value_text16".} +proc value_text16le*(para1: PValue): pointer{.cdecl, mylib, + importc: "sqlite3_value_text16le".} +proc value_text16be*(para1: PValue): pointer{.cdecl, mylib, + importc: "sqlite3_value_text16be".} +proc value_type*(para1: PValue): int32{.cdecl, mylib, + importc: "sqlite3_value_type".} +proc aggregate_context*(para1: Pcontext, nBytes: int32): pointer{.cdecl, + mylib, importc: "sqlite3_aggregate_context".} +proc user_data*(para1: Pcontext): pointer{.cdecl, mylib, + importc: "sqlite3_user_data".} +proc get_auxdata*(para1: Pcontext, para2: int32): pointer{.cdecl, mylib, + importc: "sqlite3_get_auxdata".} +proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer, + para4: proc (para1: pointer){.cdecl.}){.cdecl, mylib, + importc: "sqlite3_set_auxdata".} +proc result_blob*(para1: Pcontext, para2: pointer, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_blob".} +proc result_double*(para1: Pcontext, para2: float64){.cdecl, mylib, + importc: "sqlite3_result_double".} +proc result_error*(para1: Pcontext, para2: cstring, para3: int32){.cdecl, + mylib, importc: "sqlite3_result_error".} +proc result_error16*(para1: Pcontext, para2: pointer, para3: int32){.cdecl, + mylib, importc: "sqlite3_result_error16".} +proc result_int*(para1: Pcontext, para2: int32){.cdecl, mylib, + importc: "sqlite3_result_int".} +proc result_int64*(para1: Pcontext, para2: int64){.cdecl, mylib, + importc: "sqlite3_result_int64".} +proc result_null*(para1: Pcontext){.cdecl, mylib, + importc: "sqlite3_result_null".} +proc result_text*(para1: Pcontext, para2: cstring, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_text".} +proc result_text16*(para1: Pcontext, para2: pointer, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_text16".} +proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_text16le".} +proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_text16be".} +proc result_value*(para1: Pcontext, para2: PValue){.cdecl, mylib, + importc: "sqlite3_result_value".} +proc create_collation*(para1: PSqlite3, zName: cstring, eTextRep: int32, + para4: pointer, xCompare: Create_collation_func): int32{. + cdecl, mylib, importc: "sqlite3_create_collation".} +proc create_collation16*(para1: PSqlite3, zName: cstring, eTextRep: int32, + para4: pointer, xCompare: Create_collation_func): int32{. + cdecl, mylib, importc: "sqlite3_create_collation16".} +proc collation_needed*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{. + cdecl, mylib, importc: "sqlite3_collation_needed".} +proc collation_needed16*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{. + cdecl, mylib, importc: "sqlite3_collation_needed16".} +proc libversion*(): cstring{.cdecl, mylib, importc: "sqlite3_libversion".} + #Alias for allowing better code portability (win32 is not working with external variables) +proc version*(): cstring{.cdecl, mylib, importc: "sqlite3_libversion".} + # Not published functions +proc libversion_number*(): int32{.cdecl, mylib, + importc: "sqlite3_libversion_number".} + +proc backup_init*(pDest: PSqlite3, zDestName: cstring, pSource: PSqlite3, zSourceName: cstring): PSqlite3_Backup {. + cdecl, mylib, importc: "sqlite3_backup_init".} + +proc backup_step*(pBackup: PSqlite3_Backup, nPage: int32): int32 {.cdecl, mylib, importc: "sqlite3_backup_step".} + +proc backup_finish*(pBackup: PSqlite3_Backup): int32 {.cdecl, mylib, importc: "sqlite3_backup_finish".} + +proc backup_pagecount*(pBackup: PSqlite3_Backup): int32 {.cdecl, mylib, importc: "sqlite3_backup_pagecount".} + +proc backup_remaining*(pBackup: PSqlite3_Backup): int32 {.cdecl, mylib, importc: "sqlite3_backup_remaining".} + +proc sqlite3_sleep*(t: int64): int64 {.cdecl, mylib, importc: "sqlite3_sleep".} + + #function sqlite3_key(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_key'; + #function sqlite3_rekey(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_rekey'; + #function sqlite3_sleep(_para1:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_sleep'; + #function sqlite3_expired(_para1:Psqlite3_stmt):longint;cdecl; external Sqlite3Lib name 'sqlite3_expired'; + #function sqlite3_global_recover:longint;cdecl; external Sqlite3Lib name 'sqlite3_global_recover'; +# implementation + +when defined(nimHasStyleChecks): + {.pop.} diff --git a/nim.nimble b/nim.nimble deleted file mode 100644 index 380ffbce8..000000000 --- a/nim.nimble +++ /dev/null @@ -1,15 +0,0 @@ -include "lib/system/compilation.nim" -version = $NimMajor & "." & $NimMinor & "." & $NimPatch -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_v2" , "drnim" , "nimdoc", "testament"] - -before install: - when defined(windows): - exec "build_all.bat" - else: - exec "./build_all.sh" diff --git a/nimdoc/extlinks/project/doc/manual.md b/nimdoc/extlinks/project/doc/manual.md deleted file mode 100644 index d44b5ca39..000000000 --- a/nimdoc/extlinks/project/doc/manual.md +++ /dev/null @@ -1,17 +0,0 @@ -=================== -Nothing User Manual -=================== - -.. importdoc:: ../main.nim, .. / sub / submodule.nim, ../../util.nim - -First section -============= - -Second *section* & -================== - -Ref. [``] or [submoduleInt] from [module nimdoc/extlinks/project/sub/submodule]. - -Ref. [proc mainfunction*(): int]. - -Ref. [utilfunction(x: int)]. diff --git a/nimdoc/extlinks/project/expected/_._/util.html b/nimdoc/extlinks/project/expected/_._/util.html deleted file mode 100644 index 9b9b29a4f..000000000 --- a/nimdoc/extlinks/project/expected/_._/util.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - -nimdoc/extlinks/util - - - - - - - - - - - - -
    -
    -

    nimdoc/extlinks/util

    -
    -
    -
    - - -
    - -
    - Search: -
    -
    - Group by: - -
    - - -
    -
    - -
    - -

    -
    -

    Procs

    -
    -
    -
    -
    proc utilfunction(x: int): int {....raises: [], tags: [], forbids: [].}
    -
    - - - -
    -
    - -
    - -
    -
    - -
    -
    - - -
    -
    - - - - - - diff --git a/nimdoc/extlinks/project/expected/_._/util.idx b/nimdoc/extlinks/project/expected/_._/util.idx deleted file mode 100644 index d83d8c97d..000000000 --- a/nimdoc/extlinks/project/expected/_._/util.idx +++ /dev/null @@ -1,2 +0,0 @@ -nimTitle util _._/util.html module nimdoc/extlinks/util 0 -nim utilfunction _._/util.html#utilfunction,int proc utilfunction(x: int): int 1 diff --git a/nimdoc/extlinks/project/expected/doc/manual.html b/nimdoc/extlinks/project/expected/doc/manual.html deleted file mode 100644 index 7b964f4ac..000000000 --- a/nimdoc/extlinks/project/expected/doc/manual.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - -Nothing User Manual - - - - - - - - - - - - -
    -
    -

    Nothing User Manual

    - -

    First section

    -

    Second section &

    Ref. submodule: `</a>` or submodule: submoduleInt from module nimdoc/extlinks/project/sub/submodule.

    -

    Ref. main: proc mainfunction*(): int.

    -

    Ref. util: utilfunction(x: int).

    - - - - -
    -
    - - - - - - diff --git a/nimdoc/extlinks/project/expected/doc/manual.idx b/nimdoc/extlinks/project/expected/doc/manual.idx deleted file mode 100644 index 158a758f0..000000000 --- a/nimdoc/extlinks/project/expected/doc/manual.idx +++ /dev/null @@ -1,3 +0,0 @@ -markupTitle Nothing User Manual doc/manual.html Nothing User Manual 0 -heading First section doc/manual.html#first-section First section 0 -heading Second section & doc/manual.html#second-section-amp Second section & 0 diff --git a/nimdoc/extlinks/project/expected/main.html b/nimdoc/extlinks/project/expected/main.html deleted file mode 100644 index cf7982fde..000000000 --- a/nimdoc/extlinks/project/expected/main.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - -nimdoc/extlinks/project/main - - - - - - - - - - - - -
    -
    -

    nimdoc/extlinks/project/main

    -
    -
    -
    - - -
    - -
    - Search: -
    -
    - Group by: - -
    - - -
    -
    - -
    - -

    -

    my heading

    See also module nimdoc/extlinks/util or nimdoc/extlinks/project/sub/submodule module.

    -

    Ref. submodule: `</a>` proc.

    -

    Ref. Nothing User Manual: First section or Nothing User Manual: Second section & from Nothing User Manual.

    -

    - -
    -

    Types

    -
    -
    -
    A = object
    -  
    -
    - - - -
    -
    - -
    -
    -
    -

    Procs

    -
    -
    -
    -
    proc mainfunction(): int {....raises: [], tags: [], forbids: [].}
    -
    - - - -
    -
    - -
    - -
    -
    - -
    -
    - - -
    -
    - - - - - - diff --git a/nimdoc/extlinks/project/expected/main.idx b/nimdoc/extlinks/project/expected/main.idx deleted file mode 100644 index d01f2e4c5..000000000 --- a/nimdoc/extlinks/project/expected/main.idx +++ /dev/null @@ -1,4 +0,0 @@ -nimTitle main main.html module nimdoc/extlinks/project/main 0 -nim A main.html#A object A 17 -nim mainfunction main.html#mainfunction proc mainfunction(): int 20 -heading my heading main.html#my-heading my heading 0 diff --git a/nimdoc/extlinks/project/expected/sub/submodule.html b/nimdoc/extlinks/project/expected/sub/submodule.html deleted file mode 100644 index 913138d6e..000000000 --- a/nimdoc/extlinks/project/expected/sub/submodule.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - -nimdoc/extlinks/project/sub/submodule - - - - - - - - - - - - -
    -
    -

    nimdoc/extlinks/project/sub/submodule

    -
    -
    -
    - - -
    - -
    - Search: -
    -
    - Group by: - -
    - - -
    -
    - -
    - -

    Ref. main: object A from module nimdoc/extlinks/project/main.

    -

    Ref. util: utilfunction(x: int).

    -

    Ref. Nothing User Manual: Second section & from Nothing User Manual.

    -

    -
    -

    Types

    -
    -
    -
    submoduleInt = distinct int
    -
    - - - -
    -
    - -
    -
    -
    -

    Procs

    -
    -
    -
    -
    proc `</a>`(x, y: int): bool {....raises: [], tags: [], forbids: [].}
    -
    - - Attempt to break HTML formatting. - -
    -
    - -
    - -
    -
    - -
    -
    - - -
    -
    - - - - - - diff --git a/nimdoc/extlinks/project/expected/sub/submodule.idx b/nimdoc/extlinks/project/expected/sub/submodule.idx deleted file mode 100644 index 2b02c889e..000000000 --- a/nimdoc/extlinks/project/expected/sub/submodule.idx +++ /dev/null @@ -1,3 +0,0 @@ -nimTitle submodule sub/submodule.html module nimdoc/extlinks/project/sub/submodule 0 -nim `` sub/submodule.html#,int,int proc ``(x, y: int): bool 9 -nim submoduleInt sub/submodule.html#submoduleInt type submoduleInt 13 diff --git a/nimdoc/extlinks/project/expected/theindex.html b/nimdoc/extlinks/project/expected/theindex.html deleted file mode 100644 index 9d2f6723c..000000000 --- a/nimdoc/extlinks/project/expected/theindex.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - -Index - - - - - - - - - - - - - - - - - - - diff --git a/nimdoc/extlinks/project/main.nim b/nimdoc/extlinks/project/main.nim deleted file mode 100644 index 36b778af6..000000000 --- a/nimdoc/extlinks/project/main.nim +++ /dev/null @@ -1,23 +0,0 @@ -## my heading -## ========== -## -## .. importdoc:: sub/submodule.nim, ../util.nim, doc/manual.md -## -## .. See also [Second&&&] and particularly [first section] and [Second section &]. -## -## See also [module nimdoc/extlinks/util] or [nimdoc/extlinks/project/sub/submodule module]. -## -## Ref. [`` proc]. -## -## Ref. [First section] or [Second section &] from [Nothing User Manual]. - - -import ../util, sub/submodule - -type A* = object - x: int - -proc mainfunction*(): int = - # just to suppress "not used" warnings: - if ``(1, 2): - result = utilfunction(0) diff --git a/nimdoc/extlinks/project/sub/submodule.nim b/nimdoc/extlinks/project/sub/submodule.nim deleted file mode 100644 index 876e00684..000000000 --- a/nimdoc/extlinks/project/sub/submodule.nim +++ /dev/null @@ -1,13 +0,0 @@ -## .. importdoc:: ../../util.nim, ../main.nim, ../doc/manual.md -## -## Ref. [object A] from [module nimdoc/extlinks/project/main]. -## -## Ref. [utilfunction(x: int)]. -## -## Ref. [Second section &] from [Nothing User Manual]. - -proc ``*(x, y: int): bool = - ## Attempt to break HTML formatting. - result = x < y - -type submoduleInt* = distinct int diff --git a/nimdoc/extlinks/util.nim b/nimdoc/extlinks/util.nim deleted file mode 100644 index f208f98c1..000000000 --- a/nimdoc/extlinks/util.nim +++ /dev/null @@ -1,2 +0,0 @@ -proc utilfunction*(x: int): int = - x + 42 diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 72f6453f2..23d192009 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -1,53 +1,99 @@ - + - + + -Not a Nim Manual + + + + +Not a Nim Manual - + + + -
    -
    -

    Not a Nim Manual

    -
    +
    +
    +

    Not a Nim Manual

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

    - +
    + +

    Authors:Andreas Rumpf, Zahary Karadjov
    Version:|nimversion|
    +
    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

    About this document

    Note: This document is a draft! Several of Nim's features may need more precise wording. This manual is constantly evolving into a proper specification.

    @@ -82,7 +128,8 @@

    Note: Assignments, moves, and destruction are specified in the destructors document.

    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 optional a. Parentheses may be used to group elements.

    & is the lookahead operator; &a means that an a is expected but not consumed. It will be consumed in the following rule.

    -

    Non-terminals start with a lowercase letter, abstract terminal symbols are in UPPERCASE. Verbatim terminal symbols (including keywords) are quoted with '. An example:

    ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
    +

    Non-terminals start with a lowercase letter, abstract terminal symbols are in UPPERCASE. Verbatim terminal symbols (including keywords) are quoted with '. An example:

    +ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?

    In a typical Nim program, most of the code is compiled into the executable. However, some of the code may be executed at compile-time. 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 for details. We use the term runtime to cover both compile-time execution and code execution in the executable.

    var a: array[0..1, char]
    @@ -94,7 +141,8 @@
     

    Encoding

    All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other encodings are not supported. Any of the standard platform line termination sequences can be used - the Unix form using ASCII LF (linefeed), the Windows form using the ASCII sequence CR LF (return followed by linefeed), or the old Macintosh form using the ASCII CR (return) character. All of these forms can be used equally, regardless of the platform.

    Indentation

    Nim's standard grammar describes an indentation sensitive language. This means that all the control structures are recognized by indentation. Indentation consists only of spaces; tabulators are not allowed.

    -

    With this notation we can now easily define the core of the grammar: A block of statements (simplified example):

    ifStmt = 'if' expr ':' stmt
    +

    With this notation we can now easily define the core of the grammar: A block of statements (simplified example):

    +ifStmt = 'if' expr ':' stmt
              (IND{=} 'elif' expr ':' stmt)*
              (IND{=} 'else' ':' stmt)?
     
    @@ -121,15 +169,18 @@ stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
     \x HHcharacter with hex value HH; exactly two hex digits are allowed
     \u HHHHunicode codepoint with hex value HHHH; exactly four hex digits are allowed
     \u {H+}unicode codepoint; all hex digits enclosed in {} are used for the codepoint
    -
    """"long string within quotes""""

    Produces:

    "long string within quotes"
    +
    """"long string within quotes""""

    Produces:

    +"long string within quotes"

    -

    Operators

    Nim allows user defined operators. An operator is any combination of the following characters:

    =     +     -     *     /     <     >
    +

    Operators

    Nim allows user defined operators. An operator is any combination of the following characters:

    +=     +     -     *     /     <     >
     @     $     ~     &     %     |
     !     ?     ^     .     :     \

    (The grammar uses the terminal OPR to refer to operator symbols as defined here.)

    -

    The following strings denote other tokens:

    `   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:
    +

    The following strings denote other tokens:

    +`   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:

    Otherwise, precedence is determined by the first character.

    @@ -162,7 +213,7 @@ stmt = IND{>} stmt ^+ IND{=} DED # list of statements

    Let T's be p's return type. NRVO applies for T if sizeof(T) >= N (where N is implementation dependent), in other words, it applies for "big" structures.

    Apart from built-in operations like array indexing, memory allocation, etc. the raise statement is the only way to raise an exception.

    -

    typedesc used as a parameter type also introduces an implicit generic. typedesc has its own set of rules:

    +

    typedesc used as a parameter type also introduces an implicit generic. typedesc has its own set of rules:

    The !=, >, >=, in, notin, isnot operators are in fact templates:

    a > b is transformed into b < a.
    a in b is transformed into contains(b, a).
    notin and isnot have the obvious meanings.

    A template where every parameter is untyped is called an immediate template. For historical reasons templates can be explicitly annotated with an immediate pragma and then these templates do not take part in overloading resolution and the parameters' types are ignored by the compiler. Explicit immediate templates are now deprecated.

    @@ -246,28 +297,27 @@ stmt = IND{>} stmt ^+ IND{=} DED # list of statements

    Introduction

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

    This document is a tutorial for the programming language Nim. This tutorial assumes that you are familiar with basic programming concepts like variables, types, or statements but is kept very basic. The manual contains 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.

    -

    However, this does not work. The problem is that the procedure should not only return, but return and continue after an iteration has 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:

    +

    However, this does not work. The problem is that the procedure should not only return, but return and continue after an iteration has 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:

    Precedence levelOperatorsFirst characterTerminal symbol
    - +
    A1 headerA2 | not fooled
    C1C2 bold
    D1 code \|D2
    D1 code |D2
    E1 | text
    F2 without pipe

    not in table

    - +
    +
    - - - - +
    + diff --git a/nimdoc/rst2html/source/rst_examples.rst b/nimdoc/rst2html/source/rst_examples.rst index 7fa20de6c..54f0124c8 100644 --- a/nimdoc/rst2html/source/rst_examples.rst +++ b/nimdoc/rst2html/source/rst_examples.rst @@ -5,10 +5,6 @@ Not a Nim Manual :Authors: Andreas Rumpf, Zahary Karadjov :Version: |nimversion| -.. role:: nim(code) - :language: nim -.. default-role:: nim - .. contents:: diff --git a/nimdoc/rsttester.nim b/nimdoc/rsttester.nim index a0bdfca1e..6d41ffb86 100644 --- a/nimdoc/rsttester.nim +++ b/nimdoc/rsttester.nim @@ -1,14 +1,8 @@ -# 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) = @@ -25,7 +19,7 @@ proc testRst2Html(fixup = false) = exec("$1 rst2html $2" % [nimExe, sourceFile]) let producedHtml = expectedHtml.replace('\\', '/').replace("/expected/", "/source/htmldocs/") if readFile(expectedHtml) != readFile(producedHtml): - echo diffFiles(expectedHtml, producedHtml).output + discard execShellCmd("diff -uNdr " & expectedHtml & " " & producedHtml) inc failures if fixup: copyFile(producedHtml, expectedHtml) @@ -34,7 +28,7 @@ proc testRst2Html(fixup = false) = if failures == 0: removeDir(baseDir / "source/htmldocs") -testRst2Html(fixup) +testRst2Html(defined(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 deleted file mode 100644 index 01ad6e5a0..000000000 --- a/nimdoc/test_doctype/expected/test_doctype.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - -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 deleted file mode 100644 index 28d35fc1e..000000000 --- a/nimdoc/test_doctype/test_doctype.nim +++ /dev/null @@ -1,15 +0,0 @@ -# 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/foo.idx b/nimdoc/test_out_index_dot_html/expected/foo.idx index ac76aa532..a8dabb67e 100644 --- a/nimdoc/test_out_index_dot_html/expected/foo.idx +++ b/nimdoc/test_out_index_dot_html/expected/foo.idx @@ -1,2 +1 @@ -nimTitle foo index.html module nimdoc/test_out_index_dot_html/foo 0 -nim foo index.html#foo proc foo() 1 +foo index.html#foo foo: foo() diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 22d5daa35..35a5dfb56 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -1,104 +1,143 @@ - + - + + -nimdoc/test_out_index_dot_html/foo + + + + +foo - + + + -
    -
    -

    nimdoc/test_out_index_dot_html/foo

    -
    +
    +
    +

    foo

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

    -
    -

    Procs

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

    +
    +

    Procs

    +
    + +
    proc foo() {...}{.raises: [], tags: [].}
    +
    -
    +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 ea3486d4b..aa9ca2645 100644 --- a/nimdoc/test_out_index_dot_html/expected/theindex.html +++ b/nimdoc/test_out_index_dot_html/expected/theindex.html @@ -1,42 +1,85 @@ - + - + + -Index + + + + +Index - + + + -
    -
    -

    Index

    - Modules: index.

    API symbols

    +
    +
    +

    Index

    + Modules: index.

    API symbols

    foo:
    • foo: proc foo()
    • + data-doc-search-tag="foo: foo()" href="index.html#foo">foo: foo()
    +
    - - - - +
    + diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim index e94caae7c..15dd32ec7 100644 --- a/nimdoc/tester.nim +++ b/nimdoc/tester.nim @@ -1,27 +1,19 @@ # Small program that runs the test cases for 'nim doc'. -# To run this, cd to the git repo root, and run "nim r nimdoc/tester.nim". -# to change expected results (after carefully verifying everything), use -d:nimTestsNimdocFixup +# To run this, cd to the git repo root, and run "nim c -r nimdoc/tester.nim". +# to change expected results (after carefully verifying everything), use -d:fixup import strutils, os -from std/private/gitutils import diffFiles - -const fixup = defined(nimTestsNimdocFixup) var failures = 0 const baseDir = "nimdoc" -let - baseDirAbs = getCurrentDir() / baseDir type NimSwitches = object doc: seq[string] - docStage2: seq[string] buildIndex: seq[string] - md2html: seq[string] - md2htmlStage2: seq[string] proc exec(cmd: string) = if execShellCmd(cmd) != 0: @@ -30,9 +22,6 @@ proc exec(cmd: string) = proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) = let nimDocSwitches = switches.doc.join(" ") - nimDocStage2Switches = switches.docStage2.join(" ") - nimMd2HtmlSwitches = switches.md2html.join(" ") - nimMd2HtmlStage2Switches = switches.md2htmlStage2.join(" ") nimBuildIndexSwitches = switches.buildIndex.join(" ") putEnv("SOURCE_DATE_EPOCH", "100000") @@ -40,32 +29,18 @@ proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) = if nimDocSwitches != "": exec("$1 doc $2" % [nimExe, nimDocSwitches]) - echo("$1 doc $2" % [nimExe, nimDocSwitches]) - - if nimMd2HtmlSwitches != "": - exec("$1 md2html $2" % [nimExe, nimMd2HtmlSwitches]) - echo("$1 md2html $2" % [nimExe, nimMd2HtmlSwitches]) - - if nimDocStage2Switches != "": - exec("$1 doc $2" % [nimExe, nimDocStage2Switches]) - echo("$1 doc $2" % [nimExe, nimDocStage2Switches]) - - if nimMd2HtmlStage2Switches != "": - exec("$1 md2html $2" % [nimExe, nimMd2HtmlStage2Switches]) - echo("$1 md2html $2" % [nimExe, nimMd2HtmlStage2Switches]) if nimBuildIndexSwitches != "": exec("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches]) - echo("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches]) - for expected in walkDirRec(prjDir / "expected/", checkDir=true): + for expected in walkDirRec(prjDir / "expected/"): let produced = expected.replace('\\', '/').replace("/expected/", "/$1/" % [docsDir]) if not fileExists(produced): echo "FAILURE: files not found: ", produced inc failures elif readFile(expected) != readFile(produced): echo "FAILURE: files differ: ", produced - echo diffFiles(expected, produced).output + discard execShellCmd("diff -uNdr " & expected & " " & produced) inc failures if fixup: copyFile(produced, expected) @@ -86,7 +61,7 @@ let "$1/$2.nim" % [test1Dir, test1PrjName]], buildIndex: @["--out:$1/$2/theindex.html" % [test1Dir, test1DocsDir], "$1/$2" % [test1Dir, test1DocsDir]]) -testNimDoc(test1Dir, test1DocsDir, test1Switches, fixup) +testNimDoc(test1Dir, test1DocsDir, test1Switches, defined(fixup)) # Test "nim doc --out:.. --index:on .." let @@ -99,57 +74,7 @@ let "$1/$2.nim" % [test2Dir, test2PrjName]], buildIndex: @["--out:$1/$2/theindex.html" % [test2Dir, test2DocsDir], "$1/$2" % [test2Dir, test2DocsDir]]) -testNimDoc(test2Dir, test2DocsDir, test2Switches, fixup) +testNimDoc(test2Dir, test2DocsDir, test2Switches, defined(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) - - -# Test concise external links (RFC#125) that work with `.idx` files. -# extlinks -# ├── project -# │   ├── main.nim -# │   ├── manual.md -# │   └── sub -# │   └── submodule.nim -# └── util.nim -# -# `main.nim` imports `submodule.nim` and `../utils.nim`. -# `main.nim`, `submodule.nim`, `manual.md` do importdoc and reference each other. -let - test4PrjName = "extlinks/project" - test4Dir = baseDir / test4PrjName - test4DirAbs = baseDirAbs / test4PrjName - test4MainModule = "main" - test4MarkupDoc = "doc" / "manual.md" - test4DocsDir = "htmldocs" - # 1st stage is with --index:only, 2nd is final - test4Switches = NimSwitches( - doc: @["--project", - "--outdir:$1/$2" % [test4Dir, test4DocsDir], - "--index:only", - "$1/$2.nim" % [test4Dir, test4MainModule]], - md2html: - @["--outdir:$1/$2" % [test4Dir, test4DocsDir], - "--docroot:$1" % [test4DirAbs], - "--index:only", - "$1/$2" % [test4Dir, test4MarkupDoc]], - docStage2: - @["--project", - "--outdir:$1/$2" % [test4Dir, test4DocsDir], - "$1/$2.nim" % [test4Dir, test4MainModule]], - md2htmlStage2: - @["--outdir:$1/$2" % [test4Dir, test4DocsDir], - "--docroot:$1" % [test4DirAbs], - "$1/$2" % [test4Dir, test4MarkupDoc]], - ) -testNimDoc(test4Dir, test4DocsDir, test4Switches, fixup) - -if failures > 0: - quit "$# failures occurred; see note in nimdoc/tester.nim regarding -d:nimTestsNimdocFixup" % $failures +# Check for failures +if failures > 0: quit($failures & " failures occurred.") diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css deleted file mode 100644 index 1417d9eff..000000000 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ /dev/null @@ -1,1026 +0,0 @@ -/* -Stylesheet for use with Docutils/rst2html. - -See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to -customize this style sheet. - -Modified from Chad Skeeters' rst2html-style -https://bitbucket.org/cskeeters/rst2html-style/ - -Modified by Boyd Greenfield and narimiran -*/ - -:root { - --primary-background: #fff; - --secondary-background: ghostwhite; - --third-background: #e8e8e8; - --info-background: #50c050; - --warning-background: #c0a000; - --error-background: #e04040; - --border: #dde; - --text: #222; - --anchor: #07b; - --anchor-focus: #607c9f; - --input-focus: #1fa0eb; - --strong: #3c3c3c; - --hint: #9A9A9A; - --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAAA9CAYAAADCt9ebAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjAzOjQ4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1NGQtOTVjNy1iOTgxMDFlMDFhMmEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzMzNGYwMWEtMjAxMS1hNTRkLTk1YzctYjk4MTAxZTAxYTJhIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MzMzNGYwMWEtMjAxMS1hNTRkLTk1YzctYjk4MTAxZTAxYTJhIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1NGQtOTVjNy1iOTgxMDFlMDFhMmEiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MDM6NDgrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4PsixkAAAJ5klEQVR4nO2dfbBUZR3HP3vvxVD0zo0ACXxBuQMoQjJ1DfMl0NIhNcuSZqQhfGt6UWtK06xJexkrmywVRTQlHCIdtclC0zBJvYIvvEUgZpc3XyC7RVbKlQu1/fHdbc+uu2fPOfs85+y55/nMnBl2z+5zfnc5v/M8z+8119XVRYroAG4HfgvMT1YUR4MMAa4HLkhakCRoSVqAELwLeBY4C7gF+D6QS1QiR1ROAJ4Dzk9akKQwoXhtwL4GxvHjU8AKoNPz3leAu4HBFq+bAyZZHD9rDAK+BywDDklYlkQxoXhfAtYAEw2MVckQYBHwU6or99nA08BBFq49GngUeBIYaWH8rNEJdAOXA60Jy5I4jSreSOBKYDzwBPCJhiUqcSjwe2BWnc9NLnxuvMFrnwqsAqYBBwBfNzh2FpmNfs9jkhakWcg1aFxZiH5UL3cDnwf+Xue7BwFjgFHAOwuv24tyob3cO0LIshP4EbCn8Pq/wKvA9sLxMvCvOmPsA1yDZnHv/nEv2mM+F0IeR4m8z7lM7tMbUbzj0CxX7YfbAXwaWFJ4PRrNIu9FS9KJyEIZN68CG4DnkRJtLBw7gHHAYuDdNb77EDAjBhkHIk7xKoiqeK3IwjilzuceQJvoZjdQ/AMZaeoZiWYgBXSEwyleBW0Rv3cR9ZUO4LSI48fN2wN+bi5wJNBvUZaBSCaVy48oxpVhwDdMC5ISxpJRh6/DLGEUrxXt29YBQ+2IkwquR76ofZIWxJFegireNLSnm48skFmmDfmiVgJHJyuKI620ADOpbWEcDPwYOZKD7OmyxCTkXL+wzueOiEEWR8poQb60V4A7kLm/yFjgKeALuM1xLfYDbkX+zEGe98cAX0Oui6viF8vR7OS6urragW2UZr21wK+Aiwlu7XPoN3sYOAd4H6WH1SnA0qSEcjQnRT/e1bgnsw16kGPez4/lyCBF48oNwL+TFGSAsgCndI4qFBVvJ0owdZhjL3CnxfHzBo8+YBMyol0CHBijrKbHS/LoA7Yio9sPgJNr/QHekLGR6MffL+KP4SjnHmQxtoXNmbQP+CHyV75hYDzTIWNpWkU8iR5mq71vVsZqXgtcFqNQ/wG2IOtfD8oi6AX+Ujj+isKz8sBrnu+1okyGdmD/wnEgcDClTIdRyJRvI1cvCMciq7At4rj5eoCPAusbHCfLigda/VyKgi+AtyreMGAzykGzQQ/wO+BxSlkCuy1dq8hw5OieUjimYT+x9bHCdWwS1823Ez1EXmhgjKwrXpHzkduuanbCtzGX+NkPPAj8GincNkPjNkIO5dadUjiOB95m+BonopQpm8R58/0JJbHWy2eshVM8sRvdbyurKV4Hmoka2WA/iwwLP6d+QmzSdKC92GzK/W9R+Q3woQbHCELcN991wJcjftcpXolngKm18vFmoVonYcgDv0Qz5pqGREuOTuA8lPYUZbndh0LJNpkUqgZx33xvomim7RG+6xSvnOm1gqQXoyiMoKxFs8VZpFfpQHvQK4HDUPnAsBa9bxGP0tUjF+IYCkxFew+/G3owdq20pgjzt3uPRscs/o43IaOhH2f4ZaAPRyZQP6vgbuCbyGext87F0sgIZFI/N8BnlwBnolovcWAjq/uzwM0+55cBJ0UYN84ZL+rfbnLMM4FfUDv7Z1XlCe8FetETbleNL7+CZrnvMjCVDuTOOA84Hf+96ga0PC8qXY50FQsuMg+41+d8p885R4n7gdt8zo+qvDkmUF4fZQXwEbS+99KDMhlWkw0eALqQglXyDDCdcovf+4lv5jPNXJ9zWc/FDMMdPudGVCreRlTWwVtWbynwYVQQCFSp61Q042WJLUjB1nneuw8tvXo97x1Lugvg+j1Mo9boySLVHtJFWqsthx5GlbSGeN5bigrHdqPl52Zj4qWLXvTQWY4KOX2ccgPMBLRcuy9+0YzhguXN4GuYq2Zc2R/NZg+hfYt3/9ZCepdQthmB4vIWIYOTbWyWzGt2Y0izG1fqjlltxnsdpbPMRMmd3lqTTumqMw7FZY5G5mSHw5dalreiRWYGWjbZ7gYUlFa0xOtIWA4vk1E6zWEoI+FvyYrjSAO1FG8DCmQGKd+DJFsGogWVVFiP/GWbga9Svg9NgtPQvnd04fUNCcriSBF+vqZ5nn9PQ+Xs4q401oI6EP0R+BkyXoAeAtcgBfwidnvkVaMVFTO6n1JoWTfqiONw1MVP8e6l3GVwOPJZXW5VItGGiuduAu5CZdOrMQJ1CHqpIFccS+LxaD/3Hcr7vF0Xw7UdAwQ/xduLGkJ6aUMhVAuwU006B3wM+ZLmozJ5QRhWkGs9yjKw1fhwDsq8eE/F+y+i1CeHIxD1wppupXrA5xyUOjQHMzU3cyjTeS2aaaN2Fzoc1bhch3xspuqBTkDulQVUz1q4mYEbNuewQD3FexGFS1VjOLoRHwOOinj9HAooXY2CSidHHKeSI5GFcRWNdSxqR7VH1iHHeTV24R+X53C8hSCBvPPqnD8B+AOygn6OYAm0ORSGthLl8B0d4DtRmIKsoMsJF1U/Hi1dt6DusIN8PrsIlUdwOAITpDFlC6q3MTbgmHm011qGepOvQSXPipyOCujW6rxqk0dRWYsVFe8PRSn5JxWOoEvdfOGzfnF5tnCRK+bGi33MoB1hL0U5d1H5J5oVD6A5mp8sQS6KSWh5e0jEcR4BPmhKqJA4xTM3XuxjBlW8DuRacDU3y0myNbNTPHPjxT5m0GTN15A/zVFiI+HKYzgc/ydMlrRfgmQWuYn0F91xJEQYxVuDnMcOrQAWJi2EI72ErQviwqLEQpQ+5XBEIqzi3YWLwF+BMiMcjshEqYR1Gdk1KmxBsaR9SQviSDdRFK8fxVU+YliWZmcbcq7vSFoQR/qJWvuxD0WgLDYoSzPzAqowtjVhORwDhEaKru4GPoliGgcyy4Hj0DLT4TBCo9WO88jQ8Bns97lLghvRTOfqqDiMYqrM+HyUYdBtaLykeRmlK12C9rQOh1FM1vd/HqUIzaT5e+LVoh/VxByHShs6HFaw0VjjHhTxP5d0LT+fRnu5q3HuAodlbHW02Q5cDByM+sw1642cRylCx6PeZiuTFScUFxK+f19QovaRS+t4tsasxhvABbZbSfUCV6CM7qtQl6Fm4E1U22UqcAYqvZ42fgJMxH6vdYc5nkBlSW6Pq4fbS6hb6jg0u9yGug7FyS5U1+UcVBbwbFSuMM1sQ1bXK4A9CcviqM0e9H80HdUxCpwIa4McygA/GfgAcCJqmGKKXUixupEv7nHsLc2agWNQ0d9OzC+PHNHIo1XeLCoe8kkqXiUtwKFoWXoEKqk3BpWLaC8cXsV8HT1J+tFTZKvn+DMqFZi1knvtyKg1O2lBHADcCVxEedNSAP4HJcsr0NNWHVUAAAAASUVORK5CYII="); - - --keyword: #5e8f60; - --identifier: #222; - --comment: #484a86; - --operator: #155da4; - --punctuation: black; - --other: black; - --escapeSequence: #c4891b; - --number: #252dbe; - --literal: #a4255b; - --program: #6060c0; - --option: #508000; - --raw-data: #a4255b; - - --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' 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: black' 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) -} - -[data-theme="dark"] { - --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); -} - -@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); - } -} - -.theme-select-wrapper { - display: flex; - align-items: center; -} - -html { - font-size: 100%; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; } - -body { - font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; - font-weight: 400; - font-size: 1.125em; - line-height: 1.5; - color: var(--text); - background-color: var(--primary-background); } - -/* Skeleton grid */ -.container { - position: relative; - width: 100%; - max-width: 1050px; - margin: 0 auto; - padding: 0; - box-sizing: border-box; } - -.column, .columns { - width: 100%; - float: left; - box-sizing: border-box; - margin-left: 1%; } - -@media print { - #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby { - display:none; - } - .columns { - width:100% !important; - } -} - -.column:first-child, .columns:first-child { - margin-left: 0; } - -.container .row { - display: flex; } - -.three.columns { - width: 25.0%; - height: 100vh; - position: sticky; - top: 0px; - overflow-y: auto; - padding: 2px; -} - -.nine.columns { - width: 75.0%; - padding-left: 1.5em; } - -.twelve.columns { - width: 100%; - margin-left: 0; } - -@media screen and (max-width: 860px) { - .three.columns { - display: none; - } - .nine.columns { - width: 98.0%; - } - body { - font-size: 1em; - line-height: 1.35; - } -} - -cite { - font-style: italic !important; } - - -/* Nim search input */ -div#searchInputDiv { - margin-bottom: 1em; -} -input#searchInput { - width: 80%; -} - -/* - * Some custom formatting for input forms. - * This also fixes input form colors on Firefox with a dark system theme on Linux. - */ -input { - -moz-appearance: none; - background-color: var(--secondary-background); - color: var(--text); - border: 1px solid var(--border); - font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; - font-size: 0.9em; - padding: 6px; -} - -input:focus { - border: 1px solid var(--input-focus); - box-shadow: 0 0 3px var(--input-focus); -} - -select { - -moz-appearance: none; - background-color: var(--secondary-background); - color: var(--text); - border: 1px solid var(--border); - font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; - font-size: 0.9em; - padding: 6px; -} - -select:focus { - border: 1px solid var(--input-focus); - box-shadow: 0 0 3px var(--input-focus); -} - -/* Docgen styles */ - -:target { - border: 2px solid #B5651D; - border-style: dotted; -} - -/* Links */ -a { - color: var(--anchor); - text-decoration: none; -} - -a span.Identifier { - text-decoration: underline; - text-decoration-color: #aab; -} - -a.reference-toplevel { - font-weight: bold; -} - -a.nimdoc { - word-spacing: 0.3em; -} - -a.toc-backref { - text-decoration: none; - color: var(--text); -} - -a.link-seesrc { - color: #607c9f; - font-size: 0.9em; - font-style: italic; -} - -a:hover, a:focus { - color: var(--anchor-focus); - text-decoration: underline; -} - -a:hover span.Identifier { - color: var(--anchor); -} - - -sub, sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; } - -sup { - top: -0.5em; } - -sub { - bottom: -0.25em; } - -img { - width: auto; - height: auto; - max-width: 100%; - vertical-align: middle; - border: 0; - -ms-interpolation-mode: bicubic; } - -@media print { - * { - color: black !important; - text-shadow: none !important; - background: transparent !important; - box-shadow: none !important; } - - a, a:visited { - text-decoration: underline; } - - a[href]:after { - content: " (" attr(href) ")"; } - - abbr[title]:after { - content: " (" attr(title) ")"; } - - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; } - - pre, blockquote { - border: 1px solid #999; - page-break-inside: avoid; } - - thead { - display: table-header-group; } - - tr, img { - page-break-inside: avoid; } - - img { - max-width: 100% !important; } - - @page { - margin: 0.5cm; } - - h1 { - page-break-before: always; } - - h1.title { - page-break-before: avoid; } - - p, h2, h3 { - orphans: 3; - widows: 3; } - - h2, h3 { - page-break-after: avoid; } -} - - -p { - margin-top: 0.5em; - margin-bottom: 0.5em; } - -small { - font-size: 85%; } - -strong { - font-weight: 600; - font-size: 0.95em; - color: var(--strong); } - -em { - font-style: italic; } - -h1 { - font-size: 1.8em; - font-weight: 400; - padding-bottom: .25em; - border-bottom: 6px solid var(--third-background); - margin-top: 2.5em; - margin-bottom: 1em; - line-height: 1.2em; } - -h1.title { - padding-bottom: 1em; - border-bottom: 0px; - font-size: 2.5em; - text-align: center; - font-weight: 900; - margin-top: 0.75em; - margin-bottom: 0em; } - -h2 { - font-size: 1.3em; - margin-top: 2em; } - -h2.subtitle { - margin-top: 0em; - text-align: center; } - -h3 { - font-size: 1.125em; - font-style: italic; - margin-top: 1.5em; } - -h4 { - font-size: 1.125em; - margin-top: 1em; } - -h5 { - font-size: 1.125em; - margin-top: 0.75em; } - -h6 { - font-size: 1.1em; } - - -ul, ol { - padding: 0; - margin-top: 0.5em; - margin-left: 0.75em; } - -ul ul, ul ol, ol ol, ol ul { - margin-bottom: 0; - margin-left: 1.25em; } - -ul.simple > li { - list-style-type: circle; } - -ul.simple-boot li { - 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; } - -ul.simple-toc { - list-style: none; - font-size: 0.9em; - margin-left: -0.3em; - margin-top: 1em; } - -ul.simple-toc > li { - list-style-type: none; } - -ul.simple-toc-section { - list-style-type: circle; - margin-left: 0.8em; - color: #6c9aae; } - -ul.nested-toc-section { - list-style-type: circle; - margin-left: -0.75em; - color: var(--text); } - -ul.nested-toc-section > li { - margin-left: 1.25em; } - - -ol.arabic { - list-style: decimal; } - -ol.loweralpha { - list-style: lower-alpha; } - -ol.upperalpha { - list-style: upper-alpha; } - -ol.lowerroman { - list-style: lower-roman; } - -ol.upperroman { - list-style: upper-roman; } - -ul.auto-toc { - list-style-type: none; } - - -dl { - margin-bottom: 1.5em; } - -dt { - margin-bottom: -0.5em; - margin-left: 0.0em; } - -dd { - margin-left: 2.0em; - margin-bottom: 3.0em; - margin-top: 0.5em; } - - -hr { - margin: 2em 0; - border: 0; - border-top: 1px solid #aaa; } - -hr.footnote { - width: 25%; - border-top: 0.15em solid #999; - margin-bottom: 0.15em; - margin-top: 0.15em; -} -div.footnote-group { - margin-left: 1em; -} -div.footnote-label { - display: inline-block; - min-width: 1.7em; -} - -div.option-list { - border: 0.1em solid var(--border); -} -div.option-list-item { - padding-left: 12em; - padding-right: 0; - padding-bottom: 0.3em; - padding-top: 0.3em; -} -div.odd { - background-color: var(--secondary-background); -} -div.option-list-label { - margin-left: -11.5em; - margin-right: 0em; - min-width: 11.5em; - display: inline-block; - vertical-align: top; -} -div.option-list-description { - width: calc(100% - 1em); - padding-left: 1em; - padding-right: 0; - display: inline-block; -} - -blockquote { - font-size: 0.9em; - font-style: italic; - padding-left: 0.5em; - margin-left: 0; - border-left: 5px solid #bbc; -} - -blockquote.markdown-quote { - font-size: 0.9rem; /* use rem to avoid recursion */ - font-style: normal; -} - -.pre, span.tok { - font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; - font-weight: 500; - font-size: 0.85em; - color: var(--text); - background-color: var(--third-background); - padding-left: 3px; - padding-right: 3px; - border-radius: 4px; -} - -span.tok { - border: 1px solid #808080; - padding-bottom: 0.1em; - margin-right: 0.2em; -} - -.copyToClipBoard { - position: relative; -} - -pre { - font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; - color: var(--text); - font-weight: 500; - display: inline-block; - box-sizing: border-box; - min-width: 100%; - padding: 0.5em; - margin-top: 0.5em; - margin-bottom: 0.5em; - font-size: 0.85em; - white-space: pre !important; - overflow-y: hidden; - overflow-x: visible; - background-color: var(--secondary-background); - border: 1px solid var(--border); - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} - -.copyToClipBoardBtn { - visibility: hidden; - position: absolute; - width: 24px; - border-radius: 4px; - background-image: var(--clipboard-image); - right: 5px; - top: 13px; - background-color: var(--secondary-background); - padding: 11px; - border: 0; -} - -.copyToClipBoard:hover .copyToClipBoardBtn { - visibility: visible; -} - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; } - - -/* Nim line-numbered tables */ -.line-nums-table { - width: 100%; - table-layout: fixed; } - -table.line-nums-table { - border-radius: 4px; - border: 1px solid #cccccc; - background-color: ghostwhite; - border-collapse: separate; - margin-top: 15px; - margin-bottom: 25px; } - -.line-nums-table tbody { - border: none; } - -.line-nums-table td pre { - border: none; - background-color: transparent; } - -.line-nums-table td.blob-line-nums { - width: 28px; } - -.line-nums-table td.blob-line-nums pre { - color: #b0b0b0; - -webkit-filter: opacity(75%); - filter: opacity(75%); - text-align: right; - border-color: transparent; - background-color: transparent; - padding-left: 0px; - margin-left: 0px; - padding-right: 0px; - margin-right: 0px; } - - -table { - max-width: 100%; - background-color: transparent; - margin-top: 0.5em; - margin-bottom: 1.5em; - border-collapse: collapse; - border-color: var(--third-background); - border-spacing: 0; - font-size: 0.9em; -} - -table th, table td { - padding: 0px 0.5em 0px; - border-color: var(--third-background); -} - -table th { - background-color: var(--third-background); - border-color: var(--third-background); - font-weight: bold; } - -table th.docinfo-name { - background-color: transparent; - text-align: right; -} - -table tr:hover { - background-color: var(--third-background); } - - -/* rst2html default used to remove borders from tables and images */ -.borderless, table.borderless td, table.borderless th { - border: 0; } - -table.borderless td, table.borderless th { - /* Override padding for "table.docutils td" with "! important". - The right padding separates the table cells. */ - 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); -} -.admonition-info { - border-color: var(--info-background); -} -.admonition-info-text { - color: var(--info-background); -} -.admonition-warning { - border-color: var(--warning-background); -} -.admonition-warning-text { - color: var(--warning-background); -} -.admonition-error { - border-color: var(--error-background); -} -.admonition-error-text { - color: var(--error-background); -} - -.first { - /* Override more specific margin styles with "! important". */ - margin-top: 0 !important; } - -.last, .with-subtitle { - margin-bottom: 0 !important; } - -.hidden { - display: none; } - -blockquote.epigraph { - margin: 2em 5em; } - -dl.docutils dd { - margin-bottom: 0.5em; } - -object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { - overflow: hidden; } - - -div.figure { - margin-left: 2em; - margin-right: 2em; } - -div.footer, div.header { - clear: both; - text-align: center; - color: #666; - font-size: smaller; } - -div.footer { - padding-top: 5em; } - -div.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; } - -div.line-block div.line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; } - -div.topic { - margin: 2em; } - -div.search_results { - background-color: var(--third-background); - margin: 3em; - padding: 1em; - border: 1px solid #4d4d4d; } - -div#global-links ul { - margin-left: 0; - list-style-type: none; } - -div#global-links > simple-boot { - margin-left: 3em; } - -hr.docutils { - width: 75%; } - -img.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; } - -img.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; } - -img.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; } - -.align-left { - text-align: left; } - -.align-center { - clear: both; - text-align: center; } - -.align-right { - text-align: right; } - -/* reset inner alignment in figures */ -div.align-right { - text-align: inherit; } - -p.attribution { - text-align: right; - margin-left: 50%; } - -p.caption { - font-style: italic; } - -p.credits { - font-style: italic; - font-size: smaller; } - -p.label { - white-space: nowrap; } - -p.rubric { - font-weight: bold; - font-size: larger; - color: maroon; - text-align: center; } - -p.topic-title { - font-weight: bold; } - -pre.address { - margin-bottom: 0; - margin-top: 0; - font: inherit; } - -pre.literal-block, pre.doctest-block, pre.math, pre.code { - margin-left: 2em; - margin-right: 2em; } - -pre.code .ln { - color: grey; } - -/* line numbers */ -pre.code, code { - background-color: #eeeeee; } - -pre.code .comment, code .comment { - color: #5c6576; } - -pre.code .keyword, code .keyword { - color: #3B0D06; - font-weight: bold; } - -pre.code .literal.string, code .literal.string { - color: #0c5404; } - -pre.code .name.builtin, code .name.builtin { - color: #352b84; } - -pre.code .deleted, code .deleted { - background-color: #DEB0A1; } - -pre.code .inserted, code .inserted { - background-color: #A3D289; } - -span.classifier { - font-style: oblique; } - -span.classifier-delimiter { - font-weight: bold; } - -span.problematic { - color: #b30000; } - -span.section-subtitle { - /* font-size relative to parent (h1..h6 element) */ - font-size: 80%; } - -span.DecNumber { - color: var(--number); } - -span.BinNumber { - color: var(--number); } - -span.HexNumber { - color: var(--number); } - -span.OctNumber { - color: var(--number); } - -span.FloatNumber { - color: var(--number); } - -span.Identifier { - color: var(--identifier); } - -span.Keyword { - font-weight: 600; - color: var(--keyword); } - -span.StringLit { - color: var(--literal); } - -span.LongStringLit { - color: var(--literal); } - -span.CharLit { - color: var(--literal); } - -span.EscapeSequence { - color: var(--escapeSequence); } - -span.Operator { - color: var(--operator); } - -span.Punctuation { - color: var(--punctuation); } - -span.Comment, span.LongComment { - font-style: italic; - font-weight: 400; - color: var(--comment); } - -span.RegularExpression { - color: darkviolet; } - -span.TagStart { - color: darkviolet; } - -span.TagEnd { - color: darkviolet; } - -span.Key { - color: #252dbe; } - -span.Value { - color: #252dbe; } - -span.RawData { - color: var(--raw-data); } - -span.Assembler { - color: #252dbe; } - -span.Preprocessor { - color: #252dbe; } - -span.Directive { - color: #252dbe; } - -span.option { - font-weight: bold; - font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; - color: var(--option); } - -span.Prompt { - font-weight: bold; - color: red; } - -span.ProgramOutput { - font-weight: bold; - color: #808080; } - -span.program { - font-weight: bold; - color: var(--program); - text-decoration: underline; - text-decoration-color: var(--hint); - text-decoration-thickness: 0.05em; - text-underline-offset: 0.15em; } - -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 */ -dt pre > span.Identifier, dt pre > span.Operator { - color: var(--identifier); - font-weight: 700; } - -dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier, -dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier { - color: var(--identifier); - font-weight: inherit; } - -/* Nim sprite for the footer (taken from main page favicon) */ -.nim-sprite { - display: inline-block; - width: 51px; - height: 14px; - background-position: 0 0; - background-size: 51px 14px; - -webkit-filter: opacity(50%); - filter: opacity(50%); - background-repeat: no-repeat; - background-image: var(--nim-sprite-base64); - margin-bottom: 5px; } - -span.pragmadots { - /* Position: relative frees us up to make the dots - look really nice without fucking up the layout and - causing bulging in the parent container */ - position: relative; - /* 1px down looks slightly nicer */ - top: 1px; - padding: 2px; - background-color: var(--third-background); - border-radius: 4px; - margin: 0 2px; - cursor: pointer; - font-size: 0.8em; } - -span.pragmadots:hover { - background-color: var(--hint); } - -span.pragmawrap { - display: none; } - -span.attachedType { - display: none; - visibility: hidden; } diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index ee2618ab3..27cef017b 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -1,209 +1,144 @@ - + - + + -subdir/subdir_b/utils + + + + +subdir/subdir_b/utils - + + + -
    -
    -

    subdir/subdir_b/utils

    -
    +
    +
    +

    subdir/subdir_b/utils

    +
    -
    - - -
    - -
    - Search: -
    -
    - Group by: - -
    -
  • -
    - Iterators -
    -
  • -
  • -
    - Templates - -
    +
  • +
    - -
    - -

    This is a description of the utils module.

    -

    Links work:

    - - +
    + +

    This is now a header

    Next header

    And so on

    @@ -215,391 +150,73 @@
    1. Other case value
    2. Second case.
    -

    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]): TRef. []= 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). -

    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.

    - -

    Symbols documentation

    Let us repeat auto links from symbols section below:

    -

    There is also variant f(G[string]). See also f(G[int]).

    -
    -

    Types

    -
    -
    -
    G[T] = object
    -  
    -
    - - - -
    -
    -
    -
    SomeType = enum
    +  
    +

    Types

    +
    + +
    SomeType = enum
       enumValueA, enumValueB, enumValueC
    -
    - - - -
    -
    +
    -
    -
    + + + + +
    -

    Procs

    -
    -
    -
    -
    proc `$`[T](a: G[T]): string
    -
    - - - -
    -
    -
    -
    proc `$`[T](a: ref SomeType): string
    -
    - - - -
    -
    +

    Procs

    +
    + +
    proc someType(): SomeType {...}{.raises: [], tags: [].}
    +
    -
    -
    -
    -
    func `'big`(a: string): SomeType {....raises: [], tags: [], forbids: [].}
    -
    - - - -
    -
    +constructor. -
    -
    -
    -
    proc `[]`[T](x: G[T]): T
    -
    - - - -
    -
    + -
    -
    -
    -
    proc `[]=`[T](a: var G[T]; index: int; value: T)
    -
    - - - -
    -
    - -
    -
    -
    -
    proc binarySearch[T, K](a: openArray[T]; key: K;
    -                        cmp: proc (x: T; y: K): int {.closure.}): int
    -
    - - - -
    -
    - -
    -
    -
    -
    proc f(x: G[int]) {....raises: [], tags: [], forbids: [].}
    -
    - - There is also variant f(G[string]) - -
    -
    -
    -
    proc f(x: G[string]) {....raises: [], tags: [], forbids: [].}
    -
    - - See also f(G[int]). - -
    -
    - -
    -
    -
    -
    proc fn[T; U, V: SomeFloat]()
    -
    - - - -
    -
    - -
    -
    -
    -
    proc fn2() {....raises: [], tags: [], forbids: [].}
    -
    - - comment - -
    -
    -
    -
    proc fn2(x: int) {....raises: [], tags: [], forbids: [].}
    -
    - - fn2 comment - -
    -
    -
    -
    proc fn2(x: int; y: float) {....raises: [], tags: [], forbids: [].}
    -
    - - - -
    -
    - -
    -
    -
    -
    proc fn3(): auto {....raises: [], tags: [], forbids: [].}
    -
    - - comment - -
    -
    - -
    -
    -
    -
    proc fn4(): auto {....raises: [], tags: [], forbids: [].}
    -
    - - comment - -
    -
    - -
    -
    -
    -
    proc fn5() {....raises: [], tags: [], forbids: [].}
    -
    - - comment - -
    -
    - -
    -
    -
    -
    proc fn6() {....raises: [], tags: [], forbids: [].}
    -
    - - comment - -
    -
    - -
    -
    -
    -
    proc fn7() {....raises: [], tags: [], forbids: [].}
    -
    - - comment - -
    -
    - -
    -
    -
    -
    proc fn8(): auto {....raises: [], tags: [], forbids: [].}
    -
    - - comment - -
    -
    - -
    -
    -
    -
    func fn9(a: int): int {....raises: [], tags: [], forbids: [].}
    -
    - - comment - -
    -
    - -
    -
    -
    -
    func fn10(a: int): int {....raises: [], tags: [], forbids: [].}
    -
    - - comment - -
    -
    - -
    -
    -
    -
    func fN11() {....raises: [], tags: [], forbids: [].}
    -
    - - - -
    -
    -
    -
    func fN11(x: int) {....raises: [], tags: [], forbids: [].}
    -
    - - - -
    -
    - -
    -
    -
    -
    proc funWithGenerics[T, U: SomeFloat](a: T; b: U)
    -
    - - - -
    -
    - -
    -
    -
    -
    proc someType(): SomeType {....raises: [], tags: [], forbids: [].}
    -
    - - constructor. - -
    -
    - -
    - -
    -
    -
    -

    Iterators

    -
    -
    -
    -
    iterator fooBar(a: seq[SomeType]): int {....raises: [], tags: [], forbids: [].}
    -
    - - - -
    -
    - -
    - -
    -
    +
    -

    Templates

    -
    -
    -
    -
    template aEnum(): untyped
    -
    - - - -
    -
    +

    Templates

    +
    + +
    template aEnum(): untyped
    +
    -
    -
    -
    -
    template bEnum(): untyped
    -
    - - - -
    -
    -
    -
    -
    -
    template fromUtilsGen(): untyped
    -
    - - should be shown in utils.html only + +
    + +
    template bEnum(): untyped
    +
    + + + +
    + +
    template fromUtilsGen(): untyped
    +
    + +this should be shown in utils.html

    Example:

    -
    discard "should be in utils.html only, not in module that calls fromUtilsGen"
    ditto - -
    -
    +
    assert 3*2 == 6
    ditto -
    + -
    -
    + +
    - - - - + + diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx index 81c2103e4..b49a777c8 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx @@ -1,46 +1,13 @@ -nimTitle utils subdir/subdir_b/utils.html module subdir/subdir_b/utils 0 -nim funWithGenerics subdir/subdir_b/utils.html#funWithGenerics,T,U proc funWithGenerics[T, U: SomeFloat](a: T; b: U) 1 -nim enumValueA subdir/subdir_b/utils.html#enumValueA SomeType.enumValueA 45 -nim enumValueB subdir/subdir_b/utils.html#enumValueB SomeType.enumValueB 45 -nim enumValueC subdir/subdir_b/utils.html#enumValueC SomeType.enumValueC 45 -nim SomeType subdir/subdir_b/utils.html#SomeType enum SomeType 45 -nim G subdir/subdir_b/utils.html#G object G 49 -nim someType subdir/subdir_b/utils.html#someType_2 proc someType(): SomeType 52 -nim fn2 subdir/subdir_b/utils.html#fn2 proc fn2() 57 -nim fn2 subdir/subdir_b/utils.html#fn2,int proc fn2(x: int) 58 -nim fn2 subdir/subdir_b/utils.html#fn2,int,float proc fn2(x: int; y: float) 61 -nim binarySearch subdir/subdir_b/utils.html#binarySearch,openArray[T],K,proc(T,K) proc binarySearch[T, K](a: openArray[T]; key: K;\n cmp: proc (x: T; y: K): int {.closure.}): int 63 -nim fn3 subdir/subdir_b/utils.html#fn3 proc fn3(): auto 66 -nim fn4 subdir/subdir_b/utils.html#fn4 proc fn4(): auto 67 -nim fn5 subdir/subdir_b/utils.html#fn5 proc fn5() 68 -nim fn6 subdir/subdir_b/utils.html#fn6 proc fn6() 70 -nim fn7 subdir/subdir_b/utils.html#fn7 proc fn7() 72 -nim fn8 subdir/subdir_b/utils.html#fn8 proc fn8(): auto 75 -nim fn9 subdir/subdir_b/utils.html#fn9,int proc fn9(a: int): int 78 -nim fn10 subdir/subdir_b/utils.html#fn10,int proc fn10(a: int): int 79 -nim fN11 subdir/subdir_b/utils.html#fN11 proc fN11() 85 -nim fN11 subdir/subdir_b/utils.html#fN11,int proc fN11(x: int) 86 -nim aEnum subdir/subdir_b/utils.html#aEnum.t template aEnum(): untyped 90 -nim bEnum subdir/subdir_b/utils.html#bEnum.t template bEnum(): untyped 95 -nim fromUtilsGen subdir/subdir_b/utils.html#fromUtilsGen.t template fromUtilsGen(): untyped 106 -nim f subdir/subdir_b/utils.html#f,G[int] proc f(x: G[int]) 130 -nim f subdir/subdir_b/utils.html#f,G[string] proc f(x: G[string]) 133 -nim `[]` subdir/subdir_b/utils.html#[],G[T] proc `[]`[T](x: G[T]): T 140 -nim `[]=` subdir/subdir_b/utils.html#[]=,G[T],int,T proc `[]=`[T](a: var G[T]; index: int; value: T) 144 -nim `$` subdir/subdir_b/utils.html#$,G[T] proc `$`[T](a: G[T]): string 148 -nim `$` subdir/subdir_b/utils.html#$,ref.SomeType proc `$`[T](a: ref SomeType): string 152 -nim fooBar subdir/subdir_b/utils.html#fooBar.i,seq[SomeType] iterator fooBar(a: seq[SomeType]): int 156 -nim fn subdir/subdir_b/utils.html#fn proc fn[T; U, V: SomeFloat]() 160 -nim `'big` subdir/subdir_b/utils.html#'big,string proc `'big`(a: string): SomeType 164 -nimgrp $ subdir/subdir_b/utils.html#$-procs-all proc 148 -nimgrp fn11 subdir/subdir_b/utils.html#fN11-procs-all proc 85 -nimgrp fn2 subdir/subdir_b/utils.html#fn2-procs-all proc 57 -nimgrp f subdir/subdir_b/utils.html#f-procs-all proc 130 -heading This is now a header subdir/subdir_b/utils.html#this-is-now-a-header This is now a header 0 -heading Next header subdir/subdir_b/utils.html#this-is-now-a-header-next-header Next header 0 -heading And so on subdir/subdir_b/utils.html#next-header-and-so-on And so on 0 -heading More headers subdir/subdir_b/utils.html#more-headers More headers 0 -heading Up to level 6 subdir/subdir_b/utils.html#more-headers-up-to-level-6 Up to level 6 0 -heading Pandoc Markdown subdir/subdir_b/utils.html#pandoc-markdown Pandoc Markdown 0 -heading Link name syntax subdir/subdir_b/utils.html#pandoc-markdown-link-name-syntax Link name syntax 0 -heading Symbols documentation subdir/subdir_b/utils.html#pandoc-markdown-symbols-documentation Symbols documentation 0 +This is now a header subdir/subdir_b/utils.html#this-is-now-a-header This is now a header +Next header subdir/subdir_b/utils.html#this-is-now-a-header-next-header Next header +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 +enumValueA subdir/subdir_b/utils.html#enumValueA SomeType.enumValueA +enumValueB subdir/subdir_b/utils.html#enumValueB SomeType.enumValueB +enumValueC subdir/subdir_b/utils.html#enumValueC SomeType.enumValueC +SomeType subdir/subdir_b/utils.html#SomeType utils: SomeType +someType subdir/subdir_b/utils.html#someType_2 utils: someType(): SomeType +aEnum subdir/subdir_b/utils.html#aEnum.t utils: aEnum(): untyped +bEnum subdir/subdir_b/utils.html#bEnum.t utils: bEnum(): untyped +fromUtilsGen subdir/subdir_b/utils.html#fromUtilsGen.t utils: fromUtilsGen(): untyped diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 45a53bb93..2bf8855c7 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -1,494 +1,683 @@ - + - + + -testproject + + + + +testproject - + + + -
    -
    -

    testproject

    -
    +
    +
    +

    testproject

    +
    -
    - - -
    - -
    - Search: -
    -
    - Group by: - -
    -
  • -
    - Consts -
    +
  • -
    - Procs -
    +
  • -
    - Methods -
    +
  • -
    - Iterators -
    +
  • -
    - Macros -
    +
  • -
    - Templates -
    +
  • +
    - -
    - -

    This is the top level module. +

    + +

    This is the top level module.

    Example:

    -
    import testproject
    -import subdir / subdir_b / utils
    -doAssert bar(3, 4) == 7
    -foo(enumValueA, enumValueB)
    -# bug #11078
    -for x in "xx": discard
    top2 +
    import subdir / subdir_b / utils
    +doAssert bar(3, 4) == 7
    +foo(enumValueA, enumValueB)
    +# bug #11078
    +for x in "xx": discard
    top2

    Example:

    -
    import testproject
    -discard "in top2"
    top2 after +
    discard "in top2"
    top2 after

    Example:

    -
    import testproject
    -discard "in top3"
    top3 after

    - +
    discard "in top3"
    top3 after +

    Example:

    +
    assert 3*2 == 6

    +
    -

    Types

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

    Types

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

    Vars

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

    Vars

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

    Consts

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

    Consts

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

    Procs

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

    Procs

    +
    + +
    proc bar[T](a, b: T): T
    +
    + + + +
    + +
    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. + +
    + +
    func someFunc() {...}{.raises: [], tags: [].}
    +
    + +My someFunc. Stuff in quotes here. Some link + +
    + +
    proc fromUtils3() {...}{.raises: [], tags: [].}
    +
    + +came form utils but should be shown where fromUtilsGen is called

    Example:

    -
    discard "foo() = " & $[1]
    -#[
    -0: let's also add some broken html to make sure this won't break in future
    +
    discard 1
    + +
    + +
    proc isValid[T](x: T): bool
    +
    + + + +
    + +
    proc z1(): Foo {...}{.raises: [], tags: [].}
    +
    + +cz1 + +
    + +
    proc z2() {...}{.raises: [], tags: [].}
    +
    + +cz2 +

    Example:

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

    Example:

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

    Example: cmd: -d:foobar

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

    Example:

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

    Example:

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

    Example:

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

    Example:

    +
    discard 1
    rest + +
    + +
    proc p1() {...}{.raises: [], tags: [].}
    +
    + +cp1 +

    Example:

    +
    doAssert 1 == 1 # regular comments work here
    c4 +

    Example:

    +
    # c5 regular comments before 1st token work
    +# regular comment
    +#[
    +nested regular comment
    +]#
    +doAssert 2 == 2 # c8
    +## this is a non-nested doc comment
    +
    +##[
    +this is a nested doc comment
    +]##
    +discard "c9"
    +# also work after
    + +
    + +
    proc addfBug14485() {...}{.raises: [], tags: [].}
    +
    + +Some proc +

    Example:

    +
    discard "foo() = " & $[1]
    +#[
    +0: let's also add some broken html to make sure this won't break in future
     1: </span>
     2: </span>
     3: </span
    @@ -497,712 +686,299 @@
     6: </script
     7: end of broken html
     ]#
    - -
    -
    -
    -
    -
    -
    proc anything() {....raises: [], tags: [], forbids: [].}
    -
    - - There is no block quote after blank lines at the beginning. - -
    -
    + + +
    proc c_printf(frmt: cstring): cint {...}{.importc: "printf", header: "<stdio.h>",
    +                                     varargs, discardable.}
    +
    -
    -
    -
    -
    proc asyncFun1(): Future[int] {....stackTrace: false,
    -                                raises: [Exception, ValueError],
    -                                tags: [RootEffect], forbids: [].}
    -
    - - ok1 - -
    -
    +the c printf. etc. -
    -
    -
    -
    proc asyncFun2(): owned(Future[void]) {....stackTrace: false, raises: [Exception],
    -                                        tags: [RootEffect], forbids: [].}
    -
    - - - -
    -
    + + +
    proc c_nonexistant(frmt: cstring): cint {...}{.importc: "nonexistant",
    +    header: "<stdio.h>", varargs, discardable.}
    +
    -
    -
    -
    -
    proc asyncFun3(): owned(Future[void]) {....stackTrace: false, raises: [Exception],
    -                                        tags: [RootEffect], forbids: [].}
    -
    - - -

    Example:

    -
    discard
    ok1 - -
    -
    -
    -
    -
    -
    proc bar(f: FooBuzz) {....raises: [], tags: [], forbids: [].}
    -
    - - - -
    -
    -
    -
    proc bar[T](a, b: T): T
    -
    - - - -
    -
    -
    -
    -
    -
    proc baz() {....raises: [], tags: [], forbids: [].}
    -
    - - - -
    -
    -
    -
    proc baz[T](a, b: T): T {....deprecated.}
    -
    -
    - Deprecated -
    +
    + +
    proc low[T: Ordinal | enum | range](x: T): T {...}{.magic: "Low", noSideEffect.}
    +
    - 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 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: [],
    -                                     forbids: [].}
    -
    - - the c printf. etc. - -
    -
    - -
    -
    -
    -
    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 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.

    +

    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: [], forbids: [].}
    -
    - -

    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.}
    +
    + +

    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"
    - -
    -
    +
    discard "in low2"
    + + + +
    proc tripleStrLitTest() {...}{.raises: [], tags: [].}
    +
    + -
    -
    -
    -
    proc p1() {....raises: [], tags: [], forbids: [].}
    -
    - - cp1

    Example:

    -
    doAssert 1 == 1 # regular comments work here
    c4 -

    Example:

    -
    # c5 regular comments before 1st token work
    -# regular comment
    -#[
    -nested regular comment
    -]#
    -doAssert 2 == 2 # c8
    -## this is a non-nested doc comment
    -
    -##[
    -this is a nested doc comment
    -]##
    -discard "c9"
    -# also work after
    - -
    -
    - -
    -
    -
    -
    func someFunc() {....raises: [], tags: [], forbids: [].}
    -
    - - My someFunc. Stuff in quotes here. Some link - -
    -
    - -
    -
    -
    -
    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
    -let s1a = """
    +
    ## mullitline string litterals are tricky as their indentation can span
    +## below that of the runnableExamples
    +let s1a = """
     should appear at indent 0
       at indent 2
     at indent 0
    -"""
    -# make sure this works too
    -let s1b = """start at same line
    +"""
    +# make sure this works too
    +let s1b = """start at same line
       at indent 2
     at indent 0
    -""" # comment after
    -let s2 = """sandwich """
    -let s3 = """"""
    -when false:
    -  let s5 = """
    -        in s5 """
    +""" # comment after
    +let s2 = """sandwich """
    +let s3 = """"""
    +when false:
    +  let s5 = """
    +        in s5 """
     
    -let s3b = ["""
    +let s3b = ["""
     %!? #[...] # inside a multiline ...
    -""", "foo"]
    +""", "foo"]
     
    -## make sure handles trailing spaces
    -let s4 = """ 
    -"""
    +## make sure handles trailing spaces
    +let s4 = """ 
    +"""
     
    -let s5 = """ x
    -"""
    -let s6 = """ ""
    -"""
    -let s7 = """"""""""
    -let s8 = ["""""""""", """
    -  """ ]
    -discard
    -# should be in
    - -
    -
    +let s5 = """ x +""" +let s6 = """ "" +""" +let s7 = """""""""" +let s8 = ["""""""""", """ + """ ] +discard +# should be in + + + +
    proc asyncFun1(): Future[int] {...}{.raises: [Exception, ValueError],
    +                                tags: [RootEffect].}
    +
    + +ok1 + +
    + +
    proc asyncFun2(): owned(Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}
    +
    + + + +
    + +
    proc asyncFun3(): owned(Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}
    +
    -
    -
    -
    -
    proc z1(): Foo {....raises: [], tags: [], forbids: [].}
    -
    - - cz1 - -
    -
    -
    -
    -
    -
    proc z2() {....raises: [], tags: [], forbids: [].}
    -
    - - cz2

    Example:

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

    Example:

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

    Example: cmd: -d:foobar

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

    Example:

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

    Example:

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

    Example:

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

    Example:

    -
    discard 1
    rest - -
    -
    - -
    - -
    -
    +
    -

    Methods

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

    Methods

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

    Iterators

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

    Iterators

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

    Example:

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

    Example:

    -
    discard # bar
    - -
    -
    +
    discard # bar
    -
    + -
    -
    +
    -

    Macros

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

    Macros

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

    Example:

    -
    discard 1
    cz16 after +
    discard 1
    cz16 after

    Example:

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

    Templates

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

    Templates

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

    Example:

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

    Example:

    -
    import std/strutils
    -## issue #8871 preserve formatting
    -## line doc comment
    -# bar
    -doAssert "'foo" == "'foo"
    -##[
    +
    import std/strutils
    +## issue #8871 preserve formatting
    +## line doc comment
    +# bar
    +doAssert "'foo" == "'foo"
    +##[
     foo
     bar
    -]##
    +]##
     
    -doAssert: not "foo".startsWith "ba"
    -block:
    -  discard 0xff # elu par cette crapule
    -# should be in
    should be still in - -
    -
    +doAssert: not "foo".startsWith "ba" +block: + discard 0xff # elu par cette crapule +# should be inshould be still in -
    -
    -
    -
    template testNimDocTrailingExample()
    -
    - - -

    Example:

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

    Example:

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

    Example:

    discard

    Example:

    -
    discard 3
    +
    discard 3

    Example:

    -
    discard 4
    ok5 ok5b +
    discard 4
    ok5 ok5b

    Example:

    -
    assert true
    -

    Example:

    -
    discard 1
    in or out? - -
    -
    +
    assert true
    in or out? -
    + + +
    template testNimDocTrailingExample()
    +
    -
    -
    + +

    Example:

    +
    discard 2
    + + + + +
    - - - - + + diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index 46ffaee72..5714efac0 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -1,68 +1,61 @@ -nimTitle testproject testproject.html module testproject 0 -nim someVariable testproject.html#someVariable var someVariable 13 -nim C_A testproject.html#C_A const C_A 26 -nim C_B testproject.html#C_B const C_B 27 -nim C_C testproject.html#C_C const C_C 28 -nim C_D testproject.html#C_D const C_D 29 -nim bar testproject.html#bar,T,T proc bar[T](a, b: T): T 31 -nim baz testproject.html#baz,T,T proc baz[T](a, b: T): T 34 -nim buzz testproject.html#buzz,T,T proc buzz[T](a, b: T): T 38 -nim FooBuzz testproject.html#FooBuzz type FooBuzz 43 -nim bar testproject.html#bar proc bar(f: FooBuzz) 47 -nim aVariable testproject.html#aVariable var aVariable 52 -nim A testproject.html#A enum A 92 -nim B testproject.html#B enum B 97 -nim someFunc testproject.html#someFunc proc someFunc() 56 -nim fromUtils1 testproject.html#fromUtils1.i iterator fromUtils1(): int 112 -nim fromUtils2 testproject.html#fromUtils2.t template fromUtils2() 119 -nim fromUtils3 testproject.html#fromUtils3 proc fromUtils3() 57 -nim isValid testproject.html#isValid,T proc isValid[T](x: T): bool 59 -nim enumValueA2 testproject.html#enumValueA2 Foo.enumValueA2 66 -nim Foo testproject.html#Foo enum Foo 66 -nim z1 testproject.html#z1 proc z1(): Foo 69 -nim z2 testproject.html#z2 proc z2() 73 -nim z3 testproject.html#z3 proc z3() 78 -nim z4 testproject.html#z4 proc z4() 81 -nim z5 testproject.html#z5 proc z5(): int 87 -nim z6 testproject.html#z6 proc z6(): int 91 -nim z6t testproject.html#z6t.t template z6t(): int 95 -nim z7 testproject.html#z7 proc z7(): int 99 -nim z8 testproject.html#z8 proc z8(): int 103 -nim z9 testproject.html#z9 proc z9() 111 -nim z10 testproject.html#z10 proc z10() 114 -nim z11 testproject.html#z11 proc z11() 119 -nim z12 testproject.html#z12 proc z12(): int 124 -nim z13 testproject.html#z13 proc z13() 129 -nim baz testproject.html#baz proc baz() 134 -nim z17 testproject.html#z17 proc z17() 144 -nim p1 testproject.html#p1 proc p1() 156 -nim addfBug14485 testproject.html#addfBug14485 proc addfBug14485() 177 -nim c_printf testproject.html#c_printf,cstring proc c_printf(frmt: cstring): cint 193 -nim c_nonexistent testproject.html#c_nonexistent,cstring proc c_nonexistent(frmt: cstring): cint 197 -nim low testproject.html#low,T proc low[T: Ordinal | enum | range](x: T): T 200 -nim low2 testproject.html#low2,T proc low2[T: Ordinal | enum | range](x: T): T 210 -nim tripleStrLitTest testproject.html#tripleStrLitTest proc tripleStrLitTest() 223 -nim method1 testproject.html#method1.e,Moo method method1(self: Moo) 264 -nim method2 testproject.html#method2.e,Moo method method2(self: Moo): int 266 -nim method3 testproject.html#method3.e,Moo method method3(self: Moo): int 269 -nim iter1 testproject.html#iter1.i,int iterator iter1(n: int): int 274 -nim iter2 testproject.html#iter2.i,int iterator iter2(n: int): int 278 -nim bar testproject.html#bar.m macro bar(): untyped 285 -nim z16 testproject.html#z16.m macro z16() 288 -nim z18 testproject.html#z18.m macro z18(): int 297 -nim foo testproject.html#foo.t,SomeType,SomeType template foo(a, b: SomeType) 302 -nim myfn testproject.html#myfn.t template myfn() 307 -nim z14 testproject.html#z14.t template z14() 328 -nim z15 testproject.html#z15.t template z15() 333 -nim asyncFun1 testproject.html#asyncFun1 proc asyncFun1(): Future[int] 358 -nim asyncFun2 testproject.html#asyncFun2 proc asyncFun2(): owned(Future[void]) 361 -nim asyncFun3 testproject.html#asyncFun3 proc asyncFun3(): owned(Future[void]) 362 -nim testNimDocTrailingExample testproject.html#testNimDocTrailingExample.t template testNimDocTrailingExample() 371 -nim Circle testproject.html#Circle Shapes.Circle 380 -nim Triangle testproject.html#Triangle Shapes.Triangle 380 -nim Rectangle testproject.html#Rectangle Shapes.Rectangle 380 -nim Shapes testproject.html#Shapes enum Shapes 380 -nim anything testproject.html#anything proc anything() 387 -nim T19396 testproject.html#T19396 object T19396 392 -nimgrp bar testproject.html#bar-procs-all proc 31 -nimgrp baz testproject.html#baz-procs-all proc 34 +someVariable testproject.html#someVariable testproject: someVariable +C_A testproject.html#C_A testproject: C_A +C_B testproject.html#C_B testproject: C_B +C_C testproject.html#C_C testproject: C_C +C_D testproject.html#C_D testproject: C_D +bar testproject.html#bar,T,T testproject: bar[T](a, b: T): T +baz testproject.html#baz,T,T testproject: baz[T](a, b: T): T +buzz testproject.html#buzz,T,T testproject: buzz[T](a, b: T): T +aVariable testproject.html#aVariable testproject: aVariable +A testproject.html#A testproject: A +B testproject.html#B testproject: B +someFunc testproject.html#someFunc testproject: someFunc() +fromUtils1 testproject.html#fromUtils1.i testproject: fromUtils1(): int +fromUtils2 testproject.html#fromUtils2.t testproject: fromUtils2() +fromUtils3 testproject.html#fromUtils3 testproject: fromUtils3() +isValid testproject.html#isValid,T testproject: isValid[T](x: T): bool +enumValueA2 testproject.html#enumValueA2 Foo.enumValueA2 +Foo testproject.html#Foo testproject: Foo +z1 testproject.html#z1 testproject: z1(): Foo +z2 testproject.html#z2 testproject: z2() +z3 testproject.html#z3 testproject: z3() +z4 testproject.html#z4 testproject: z4() +z5 testproject.html#z5 testproject: z5(): int +z6 testproject.html#z6 testproject: z6(): int +z6t testproject.html#z6t.t testproject: z6t(): int +z7 testproject.html#z7 testproject: z7(): int +z8 testproject.html#z8 testproject: z8(): int +z9 testproject.html#z9 testproject: z9() +z10 testproject.html#z10 testproject: z10() +z11 testproject.html#z11 testproject: z11() +z12 testproject.html#z12 testproject: z12(): int +z13 testproject.html#z13 testproject: z13() +baz testproject.html#baz testproject: baz() +z17 testproject.html#z17 testproject: z17() +p1 testproject.html#p1 testproject: p1() +addfBug14485 testproject.html#addfBug14485 testproject: addfBug14485() +c_printf testproject.html#c_printf,cstring testproject: c_printf(frmt: cstring): cint +c_nonexistant testproject.html#c_nonexistant,cstring testproject: c_nonexistant(frmt: cstring): cint +low testproject.html#low,T testproject: low[T: Ordinal | enum | range](x: T): T +low2 testproject.html#low2,T testproject: low2[T: Ordinal | enum | range](x: T): T +tripleStrLitTest testproject.html#tripleStrLitTest testproject: tripleStrLitTest() +method1 testproject.html#method1.e,Moo testproject: method1(self: Moo) +method2 testproject.html#method2.e,Moo testproject: method2(self: Moo): int +method3 testproject.html#method3.e,Moo testproject: method3(self: Moo): int +iter1 testproject.html#iter1.i,int testproject: iter1(n: int): int +iter2 testproject.html#iter2.i,int testproject: iter2(n: int): int +bar testproject.html#bar.m testproject: bar(): untyped +z16 testproject.html#z16.m testproject: z16() +z18 testproject.html#z18.m testproject: z18(): int +foo testproject.html#foo.t,SomeType,SomeType testproject: foo(a, b: SomeType) +myfn testproject.html#myfn.t testproject: myfn() +z14 testproject.html#z14.t testproject: z14() +z15 testproject.html#z15.t testproject: z15() +asyncFun1 testproject.html#asyncFun1 testproject: asyncFun1(): Future[int] +asyncFun2 testproject.html#asyncFun2 testproject: asyncFun2(): owned(Future[void]) +asyncFun3 testproject.html#asyncFun3 testproject: asyncFun3(): owned(Future[void]) +testNimDocTrailingExample testproject.html#testNimDocTrailingExample.t testproject: testNimDocTrailingExample() +Circle testproject.html#Circle Shapes.Circle +Triangle testproject.html#Triangle Shapes.Triangle +Rectangle testproject.html#Rectangle Shapes.Rectangle +Shapes testproject.html#Shapes testproject: Shapes diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index f9488eb2d..978560e0f 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -1,416 +1,353 @@ - + - + + -Index + + + + +Index - + + + -
    -
    -

    Index

    - Modules: subdir/subdir_b/utils, testproject.

    API symbols

    -
    `$`:
      +
      +
      +

      Index

      + Modules: subdir/subdir_b/utils, testproject.

      API symbols

      +
      A:
      -
      `'big`:
      -
      `[]=`:
      -
      `[]`:
      -
      A:
        -
      • testproject: enum A
      • + data-doc-search-tag="testproject: A" href="testproject.html#A">testproject: A
      addfBug14485:
      aEnum:
      -
      anything:
        -
      • testproject: proc anything()
      • + data-doc-search-tag="utils: aEnum(): untyped" href="subdir/subdir_b/utils.html#aEnum.t">utils: aEnum(): untyped
      asyncFun1:
      asyncFun2:
      asyncFun3:
      aVariable:
      • testproject: var aVariable
      • + data-doc-search-tag="testproject: aVariable" href="testproject.html#aVariable">testproject: aVariable
      B:
      • testproject: enum B
      • + data-doc-search-tag="testproject: B" href="testproject.html#B">testproject: B
      bar:
      baz:
      bEnum:
      -
      binarySearch:
      buzz:
      C_A:
      • testproject: const C_A
      • + data-doc-search-tag="testproject: C_A" href="testproject.html#C_A">testproject: C_A
      C_B:
      • testproject: const C_B
      • + data-doc-search-tag="testproject: C_B" href="testproject.html#C_B">testproject: C_B
      C_C:
      • testproject: const C_C
      • + data-doc-search-tag="testproject: C_C" href="testproject.html#C_C">testproject: C_C
      C_D:
      • testproject: const C_D
      • + data-doc-search-tag="testproject: C_D" href="testproject.html#C_D">testproject: C_D
      Circle:
      -
      c_nonexistent:
      +
      - - - - +
      + diff --git a/nimdoc/testproject/subdir/subdir_b/utils.nim b/nimdoc/testproject/subdir/subdir_b/utils.nim index f535d7f74..128e8e481 100644 --- a/nimdoc/testproject/subdir/subdir_b/utils.nim +++ b/nimdoc/testproject/subdir/subdir_b/utils.nim @@ -1,7 +1,5 @@ ##[ -.. include:: ./utils_overview.rst - # This is now a header ## Next header @@ -21,70 +19,18 @@ More text. 1. Other case value 2. Second case. -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_ ]## -include ./utils_helpers - type SomeType* = enum enumValueA, enumValueB, enumValueC - G*[T] = object - val: T proc someType*(): SomeType = ## constructor. SomeType(2) - -proc fn2*() = discard ## comment -proc fn2*(x: int) = - ## fn2 comment - discard -proc fn2*(x: int, y: float) = - discard -proc binarySearch*[T, K](a: openArray[T]; key: K; - cmp: proc (x: T; y: K): int {.closure.}): int = - discard -proc fn3*(): auto = 1 ## comment -proc fn4*(): auto = 2 * 3 + 4 ## comment -proc fn5*() ## comment -proc fn5*() = discard -proc fn6*() = - ## comment -proc fn7*() = - ## comment - discard -proc fn8*(): auto = - ## comment - 1+1 -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_ -# (or [fN11] or [fn11] in Markdown Syntax): - -func fN11*() = discard -func fN11*(x: int) = discard - # bug #9235 template aEnum*(): untyped = @@ -104,9 +50,9 @@ template bEnum*(): untyped = discard template fromUtilsGen*(): untyped = - ## should be shown in utils.html only + ## this should be shown in utils.html runnableExamples: - discard "should be in utils.html only, not in module that calls fromUtilsGen" + assert 3*2 == 6 ## ditto iterator fromUtils1*(): int = @@ -118,96 +64,7 @@ template fromUtilsGen*(): untyped = template fromUtils2*() = ## ok3 - runnableExamples: - discard """should be shown as examples for fromUtils2 - in module calling fromUtilsGen""" proc fromUtils3*() = ## came form utils but should be shown where `fromUtilsGen` is called - runnableExamples: discard """should be shown as examples for fromUtils3 - in module calling fromUtilsGen""" - -proc f*(x: G[int]) = - ## There is also variant `f(G[string])`_ - discard -proc f*(x: G[string]) = - ## See also `f(G[int])`_. - discard - -## Ref. `[]`_ is the same as `proc \`[]\`(G[T])`_ because there are no -## overloads. The full form: `proc \`[]\`*[T](x: G[T]): T`_ - -proc `[]`*[T](x: G[T]): T = x.val - -## Ref. `[]=`_ aka `\`[]=\`(G[T], int, T)`_. - -proc `[]=`*[T](a: var G[T], index: int, value: T) = discard - -## Ref. `$`_ aka `proc $`_ or `proc \`$\``_. - -proc `$`*[T](a: G[T]): string = "" - -## Ref. `$(a: ref SomeType)`_. - -proc `$`*[T](a: ref SomeType): string = "" - -## Ref. foo_bar_ aka `iterator foo_bar_`_. - -iterator fooBar*(a: seq[SomeType]): int = discard - -## Ref. `fn[T; U,V: SomeFloat]()`_. - -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])]. - -]## + runnableExamples: discard 1 diff --git a/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim b/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim deleted file mode 100644 index 2c45ffb83..000000000 --- a/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim +++ /dev/null @@ -1 +0,0 @@ -proc funWithGenerics*[T, U: SomeFloat](a: T, b: U) = discard diff --git a/nimdoc/testproject/subdir/subdir_b/utils_overview.rst b/nimdoc/testproject/subdir/subdir_b/utils_overview.rst deleted file mode 100644 index 58ce930bf..000000000 --- a/nimdoc/testproject/subdir/subdir_b/utils_overview.rst +++ /dev/null @@ -1,8 +0,0 @@ -This is a description of the utils module. - -Links work: - -* other module: `iterators `_ (not in this dir, just an example) -* internal: `fn2(x)`_ -* internal included from another module: `funWithGenerics*[T, U: - SomeFloat](a: T, b: U)`_. diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index b5fa2ac37..eea399f82 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -39,14 +39,6 @@ proc buzz*[T](a, b: T): T {.deprecated: "since v0.20".} = ## This is deprecated with a message. result = a + b -type - FooBuzz* {.deprecated: "FooBuzz msg".} = int - -using f: FooBuzz - -proc bar*(f) = # `f` should be expanded to `f: FooBuzz` - discard - import std/macros var aVariable*: array[1, int] @@ -139,7 +131,6 @@ when true: # BUG: this currently this won't be run since not exported # but probably should doAssert false - if false: bazNonExported() # silence XDeclaredButNotUsed proc z17*() = # BUG: a comment before 1st doc comment currently doesn't prevent @@ -194,7 +185,7 @@ when true: # procs without `=` (using comment field) ## the c printf. ## etc. - proc c_nonexistent*(frmt: cstring): cint {.importc: "nonexistent", header: "", varargs, discardable.} + proc c_nonexistant*(frmt: cstring): cint {.importc: "nonexistant", header: "", varargs, discardable.} when true: # tests RST inside comments proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect.} @@ -221,7 +212,7 @@ when true: # tests RST inside comments when true: # multiline string litterals proc tripleStrLitTest*() = - runnableExamples("--hint:XDeclaredButNotUsed:off"): + runnableExamples: ## mullitline string litterals are tricky as their indentation can span ## below that of the runnableExamples let s1a = """ @@ -261,12 +252,12 @@ at indent 0 when true: # methods; issue #14691 type Moo = object - method method1*(self: Moo) {.base.} = + method method1*(self: Moo) = ## foo1 - method method2*(self: Moo): int {.base.} = + method method2*(self: Moo): int = ## foo2 result = 1 - method method3*(self: Moo): int {.base.} = + method method3*(self: Moo): int = ## foo3 1 @@ -339,18 +330,20 @@ when true: # (most) templates ## ok5 ## ok5b runnableExamples: assert true - runnableExamples: discard 1 ## in or out? + # this is an edge case; a newline separate last runnableExamples from + # next doc comment but AST isnt' aware of it; this could change in future discard 8 ## out + runnableExamples: discard 1 when true: # issue #14473 import std/[sequtils] template doit(): untyped = ## doit ## return output only - toSeq(["D20210427T172228"]) # make it searcheable at least until we figure out a way to avoid echo + toSeq([1,2]) echo doit() # using doAssert or similar to avoid echo would "hide" the original bug when true: # issue #14846 @@ -382,13 +375,3 @@ when true: # issue #15702 Circle, ## A circle Triangle, ## A three-sided shape Rectangle ## A four-sided shape - -when true: # issue #15184 - proc anything* = - ## - ## There is no block quote after blank lines at the beginning. - discard - -type T19396* = object # bug #19396 - a*: int - b: float diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim index e3b4c9a3a..a7940349d 100644 --- a/nimpretty/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -69,8 +69,8 @@ proc main = var backup = false # when `on`, create a backup file of input in case - # `prettyPrint` could overwrite it (note that the backup may happen even - # if input is not actually overwritten, when nimpretty is a noop). + # `prettyPrint` could over-write it (note that the backup may happen even + # if input is not actually over-written, when nimpretty is a noop). # --backup was un-documented (rely on git instead). var opt = PrettyOptions(indWidth: 0, maxLineLen: 80) diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim index b1f15aee6..d646b25ce 100644 --- a/nimpretty/tester.nim +++ b/nimpretty/tester.nim @@ -1,10 +1,9 @@ # Small program that runs the test cases import strutils, os, sequtils -from std/private/gitutils import diffFiles const - dir = "nimpretty/tests" + dir = "nimpretty/tests/" outputdir = dir / "outputdir" var @@ -27,7 +26,7 @@ proc test(infile, ext: string) = let produced = dir / nimFile.changeFileExt(ext) if readFile(expected) != readFile(produced): echo "FAILURE: files differ: ", nimFile - echo diffFiles(expected, produced).output + discard execShellCmd("diff -uNdr " & expected & " " & produced) failures += 1 else: echo "SUCCESS: files identical: ", nimFile @@ -44,7 +43,7 @@ proc testTogether(infiles: seq[string]) = let produced = dir / "outputdir" / infile if readFile(expected) != readFile(produced): echo "FAILURE: files differ: ", nimFile - echo diffFiles(expected, produced).output + discard execShellCmd("diff -uNdr " & expected & " " & produced) failures += 1 else: echo "SUCCESS: files identical: ", nimFile diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 53ff0ea4d..2ba885d9a 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -267,7 +267,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = if not em.endsInWhite: wr(" ") wr(tok.ident.s) template isUnary(tok): bool = - tok.strongSpaceB == tsNone and tok.strongSpaceA + tok.strongSpaceB == 0 and tok.strongSpaceA > 0 if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: wr(" ") @@ -418,9 +418,9 @@ proc isValid1*[A](s: HashSet[A]): bool {.deprecated: result = s.data.len > 0 # bug #11468 -assert $typeof(a) == "Option[system.int]" -foo(a, $typeof(b), c) -foo(typeof(b), c) # this is ok +assert $type(a) == "Option[system.int]" +foo(a, $type(b), c) +foo(type(b), c) # this is ok proc `<`*[A](s, t: A): bool = discard proc `==`*[A](s, t: HashSet[A]): bool = discard @@ -843,7 +843,3 @@ type SpinnyEvent2 = tuple kind: EventKind payload: string - - -proc hid_open*(vendor_id: cushort; product_id: cushort; serial_number: cstring): ptr HidDevice {. - importc: "hid_open", dynlib: hidapi.} diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index 266bcae06..cfe9a43fa 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -272,7 +272,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = if not em.endsInWhite: wr(" ") wr(tok.ident.s) template isUnary(tok): bool = - tok.strongSpaceB == tsNone and tok.strongSpaceA + tok.strongSpaceB == 0 and tok.strongSpaceA > 0 if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: wr(" ") @@ -423,9 +423,9 @@ proc isValid1*[A](s: HashSet[A]): bool {.deprecated: result = s.data.len > 0 # bug #11468 -assert $typeof(a) == "Option[system.int]" -foo(a, $typeof(b), c) -foo(typeof(b), c) # this is ok +assert $type(a) == "Option[system.int]" +foo(a, $type(b), c) +foo(type(b), c) # this is ok proc `<`*[A](s, t: A): bool = discard proc `==`*[A](s, t: HashSet[A]): bool = discard @@ -856,8 +856,3 @@ type SpinnyEvent2 = tuple kind: EventKind payload: string - - -proc hid_open*(vendor_id: cushort; product_id: cushort; - serial_number: cstring): ptr HidDevice {. - importc: "hid_open", dynlib: hidapi.} diff --git a/nimpretty/tests/expected/simple.nim b/nimpretty/tests/expected/simple.nim index e711eb3b6..d13558621 100644 --- a/nimpretty/tests/expected/simple.nim +++ b/nimpretty/tests/expected/simple.nim @@ -16,14 +16,3 @@ proc a() = discard ## comment 3 discard # comment 4 - - -# bug #20553 - -let `'hello` = 12 -echo `'hello` - - -proc `'u4`(n: string) = - # The leading ' is required. - discard diff --git a/nimpretty/tests/simple.nim b/nimpretty/tests/simple.nim index 2a01a176e..435bd6bd2 100644 --- a/nimpretty/tests/simple.nim +++ b/nimpretty/tests/simple.nim @@ -16,14 +16,3 @@ proc a() = discard## comment 3 discard # comment 4 - - -# bug #20553 - -let `'hello` = 12 -echo `'hello` - - -proc `'u4`(n: string) = - # The leading ' is required. - discard diff --git a/nimsuggest/config.nims b/nimsuggest/config.nims deleted file mode 100644 index ee19f9893..000000000 --- a/nimsuggest/config.nims +++ /dev/null @@ -1,2 +0,0 @@ -# xxx not sure why this flag isn't needed: switch("processing", "filenames") -switch("filenames", "canonical") diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index ce9681975..2e28a09ee 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -7,13 +7,6 @@ # distribution, for details about the copyright. # -import compiler/renderer -import strformat -import algorithm -import tables -import std/sha1 -import times - ## Nimsuggest is a tool that helps to give editors IDE like capabilities. when not defined(nimcore): @@ -23,14 +16,11 @@ import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp # Do NOT import suggest. It will lead to weird bugs with # suggestionResultHook, because suggest.nim is included by sigmatch. # So we import that one instead. -import compiler / [options, commands, modules, +import compiler / [options, commands, modules, sem, passes, passaux, msgs, sigmatch, ast, idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper, - pathutils, condsyms, syntaxes] - -when defined(nimPreviewSlimSystem): - import std/typedthreads + pathutils] when defined(windows): import winlean @@ -53,8 +43,6 @@ 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 @@ -88,9 +76,6 @@ var requests: Channel[string] results: Channel[Suggest] -proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; tag: string, - graph: ModuleGraph); - proc writelnToChannel(line: string) = results.send(Suggest(section: ideMsg, doc: line)) @@ -140,9 +125,6 @@ proc sexp(s: Suggest): SexpNode = ]) if s.section == ideSug: result.add convertSexp(s.prefix) - if s.section in {ideOutline, ideExpand} and s.version == 3: - result.add convertSexp(s.endLine.int) - result.add convertSexp(s.endCol) proc sexp(s: seq[Suggest]): SexpNode = result = newSList() @@ -155,7 +137,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", "globalSymbols", "recompile", "saved", "chkFile", "declaration"]: + for command in ["sug", "con", "def", "use", "dus", "chk", "mod"]: let cmd = sexp(command) methodDesc = newSList() @@ -178,25 +160,9 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym = if m != nil and m.ast != nil: result = findNode(m.ast, trackPos) -template benchmark(benchmarkName: untyped, 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 executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, tag: string, +proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; graph: ModuleGraph) = let conf = graph.config - - if conf.suggestVersion == 3: - let command = fmt "cmd = {cmd} {file}:{line}:{col}" - benchmark command: - executeNoHooksV3(cmd, file, dirtyfile, line, col, tag, graph) - return - myLog("cmd: " & $cmd & ", file: " & file.string & ", dirtyFile: " & dirtyfile.string & "[" & $line & ":" & $col & "]") @@ -229,14 +195,11 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, if conf.ideCmd in {ideUse, ideDus}: let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym if u != nil: - listUsages(graph, u) + listUsages(conf, u) else: localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos)) -proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, graph: ModuleGraph) = - executeNoHooks(cmd, file, dirtyfile, line, col, graph) - -proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; tag: string, +proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; graph: ModuleGraph) = if cmd == ideChk: graph.config.structuredErrorHook = errorHook @@ -244,7 +207,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; tag: st else: graph.config.structuredErrorHook = nil graph.config.writelnHook = myLog - executeNoHooks(cmd, file, dirtyfile, line, col, tag, graph) + executeNoHooks(cmd, file, dirtyfile, line, col, graph) proc executeEpc(cmd: IdeCmd, args: SexpNode; graph: ModuleGraph) = @@ -255,7 +218,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode; var dirtyfile = AbsoluteFile"" if len(args) > 3: dirtyfile = AbsoluteFile args[3].getStr("") - execute(cmd, file, dirtyfile, int(line), int(column), args[3].getStr, graph) + execute(cmd, file, dirtyfile, int(line), int(column), graph) proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string, returnSymbol = "return") = @@ -356,7 +319,7 @@ proc replTcp(x: ThreadParams) {.thread.} = else: server.bindAddr(x.port, x.address) server.listen() - var inp = "" + var inp = "".TaintedString var stdoutSocket: Socket while true: accept(server, stdoutSocket) @@ -473,13 +436,6 @@ 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 "declaration": conf.ideCmd = ideDeclaration - of "expand": conf.ideCmd = ideExpand - of "chkfile": conf.ideCmd = ideChkFile - of "recompile": conf.ideCmd = ideRecompile - of "type": conf.ideCmd = ideType else: err() var dirtyfile = "" var orig = "" @@ -496,7 +452,6 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) = i += parseInt(cmd, line, i) i += skipWhile(cmd, seps, i) i += parseInt(cmd, col, i) - let tag = substr(cmd, i) if conf.ideCmd == ideKnown: results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, AbsoluteFile orig)))) @@ -505,23 +460,23 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) = else: if conf.ideCmd == ideChk: for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) - execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, tag, graph) + execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, graph) sentinel() proc recompileFullProject(graph: ModuleGraph) = - benchmark "Recompilation(clean)": - graph.resetForBackend() - graph.resetSystemArtifacts() - graph.vm = nil - graph.resetAllModules() - GC_fullCollect() - graph.compileProject() + #echo "recompiling full project" + resetSystemArtifacts(graph) + graph.vm = nil + graph.resetAllModules() + GC_fullCollect() + compileProject(graph) + #echo GC_getStatistics() proc mainThread(graph: ModuleGraph) = let conf = graph.config - myLog "searchPaths: " - for it in conf.searchPaths: - myLog(" " & it.string) + if gLogging: + for it in conf.searchPaths: + log(it.string) proc wrHook(line: string) {.closure.} = if gMode == mepc: @@ -544,7 +499,7 @@ proc mainThread(graph: ModuleGraph) = else: os.sleep 250 idle += 1 - if idle == 20 and gRefresh and conf.suggestVersion != 3: + if idle == 20 and gRefresh: # we use some nimsuggest activity to enable a lazy recompile: conf.ideCmd = ideChk conf.writelnHook = proc (s: string) = discard @@ -563,7 +518,6 @@ proc mainCommand(graph: ModuleGraph) = registerPass graph, verbosePass registerPass graph, semPass conf.setCmd cmdIdeTools - defineSymbol(conf.symbols, $conf.backend) wantMainModule(conf) if not fileExists(conf.projectFull): @@ -573,18 +527,12 @@ 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 = 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 + conf.writelnHook = myLog + conf.structuredErrorHook = nil # compile the project before showing any input so that we already # can answer questions right away: - benchmark "Initial compilation": - compileProject(graph) + compileProject(graph) open(requests) open(results) @@ -636,9 +584,8 @@ 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 "v1": conf.suggestVersion = 1 of "v2": conf.suggestVersion = 0 - of "v3": conf.suggestVersion = 3 + of "v1": conf.suggestVersion = 1 of "tester": gMode = mstdin gEmitEof = true @@ -700,352 +647,6 @@ 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()}" - -func deduplicateSymInfoPair[SymInfoPair](xs: seq[SymInfoPair]): seq[SymInfoPair] = - # xs contains duplicate items and we want to filter them by range because the - # sym may not match. This can happen when xs contains the same definition but - # with different signature because suggestSym might be called multiple times - # for the same symbol (e. g. including/excluding the pragma) - result = @[] - for itm in xs.reversed: - var found = false - for res in result: - if res.info.exactEquals(itm.info): - found = true - break - if not found: - result.add(itm) - result.reverse() - -proc findSymData(graph: ModuleGraph, trackPos: TLineInfo): - ref SymInfoPair = - for s in graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair: - if isTracked(s.info, trackPos, s.sym.name.s.len): - new(result) - result[] = s - break - -proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int): - ref SymInfoPair = - let - fileIdx = fileInfoIdx(graph.config, file) - trackPos = newLineInfo(fileIdx, line, col) - result = findSymData(graph, trackPos) - -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, endLine: uint16 = 0, endCol = 0) = - 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, - endLine = endLine, endCol = endCol) - suggestResult(graph.config, suggest) - -const - # kinds for ideOutline and ideGlobalSymbols - searchableSymKinds = {skField, skEnumField, skIterator, skMethod, skFunc, skProc, skConverter, skTemplate} - -proc symbolEqual(left, right: PSym): bool = - # More relaxed symbol comparison - return left.info.exactEquals(right.info) and left.name == right.name - -proc findDef(n: PNode, line: uint16, col: int16): PNode = - if n.kind in {nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkMacroDef}: - if n.info.line == line: - return n - else: - for i in 0 ..< safeLen(n): - let res = findDef(n[i], line, col) - if res != nil: return res - -proc findByTLineInfo(trackPos: TLineInfo, infoPairs: seq[SymInfoPair]): - ref SymInfoPair = - for s in infoPairs: - if s.info.exactEquals trackPos: - new(result) - result[] = s - break - -proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: seq[SymInfoPair]): bool = - proc checkSymbol(sym: PSym, info: TLineInfo): bool = - result = (sym.owner.kind in {skModule, skType} or sym.kind in {skProc, skMethod, skIterator, skTemplate, skType}) - - if n.kind == nkSym and n.sym.checkSymbol(n.info): - graph.suggestResult(n.sym, n.sym.info, ideOutline, endInfo.line, endInfo.col) - return true - elif n.kind == nkIdent: - let symData = findByTLineInfo(n.info, infoPairs) - if symData != nil and symData.sym.checkSymbol(symData.info): - let sym = symData.sym - graph.suggestResult(sym, sym.info, ideOutline, endInfo.line, endInfo.col) - return true - -proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: seq[SymInfoPair]): bool = - for child in n: - if child.kind in {nkIdent, nkSym}: - if graph.outlineNode(child, endInfo, infoPairs): - return true - elif child.kind == nkPostfix: - if graph.handleIdentOrSym(child, endInfo, infoPairs): - return true - -proc iterateOutlineNodes(graph: ModuleGraph, n: PNode, infoPairs: seq[SymInfoPair]) = - var matched = true - if n.kind == nkIdent: - let symData = findByTLineInfo(n.info, infoPairs) - if symData != nil and symData.sym.kind == skEnumField and symData.info.exactEquals(symData.sym.info): - let sym = symData.sym - graph.suggestResult(sym, sym.info, ideOutline, n.endInfo.line, n.endInfo.col) - elif (n.kind in {nkFuncDef, nkProcDef, nkTypeDef, nkMacroDef, nkTemplateDef, nkConverterDef, nkEnumFieldDef, nkConstDef}): - matched = handleIdentOrSym(graph, n, n.endInfo, infoPairs) - else: - matched = false - - if n.kind != nkFormalParams: - for child in n: - graph.iterateOutlineNodes(child, infoPairs) - -proc calculateExpandRange(n: PNode, info: TLineInfo): TLineInfo = - if ((n.kind in {nkFuncDef, nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkConverterDef} and - n.info.exactEquals(info)) or - (n.kind in {nkCall, nkCommand} and n[0].info.exactEquals(info))): - result = n.endInfo - else: - for child in n: - result = child.calculateExpandRange(info) - if result != unknownLineInfo: - return result - result = unknownLineInfo - -proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; tag: string, - 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}, tag: {tag}" - - 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, ideDeclaration, ideExpand} 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: - var res: seq[SymInfoPair] = @[] - for s in graph.suggestSymbolsIter: - if s.sym.symbolEqual(symbol.sym): - res.add(s) - for s in res.deduplicateSymInfoPair(): - 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 n = parseFile(fileIndex, graph.cache, graph.config) - graph.iterateOutlineNodes(n, graph.fileSymbols(fileIndex).deduplicateSymInfoPair) - 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 - res: seq[SymInfoPair] = @[] - - 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 - res = res.filterIt(not it.info.exactEquals(s.info)) - res.add s - # stop after first 1000 matches... - if counter > 1000: - break - - # ... then sort them by weight ... - res.sort() do (left, right: SymInfoPair) -> int: - let - leftString = left.sym.name.s - rightString = right.sym.name.s - leftIndex = leftString.find(file.string) - rightIndex = rightString.find(file.string) - - if leftIndex == rightIndex: - result = cmp(toLowerAscii(leftString), - toLowerAscii(rightString)) - else: - result = cmp(leftIndex, rightIndex) - - # ... and send first 100 results - if res.len > 0: - for i in 0 .. min(100, res.len - 1): - let s = res[i] - graph.suggestResult(s.sym, s.info) - - of ideDeclaration: - let s = graph.findSymData(file, line, col) - if not s.isNil: - # find first mention of the symbol in the file containing the definition. - # It is either the definition or the declaration. - var first: SymInfoPair - for symbol in graph.fileSymbols(s.sym.info.fileIndex).deduplicateSymInfoPair: - if s.sym.symbolEqual(symbol.sym): - first = symbol - break - - if s.info.exactEquals(first.info): - # we are on declaration, go to definition - graph.suggestResult(first.sym, first.sym.info, ideDeclaration) - else: - # we are on definition or usage, look for declaration - graph.suggestResult(first.sym, first.info, ideDeclaration) - of ideExpand: - var level: int = high(int) - let index = skipWhitespace(tag, 0); - let trimmed = substr(tag, index) - if not (trimmed == "" or trimmed == "all"): - discard parseInt(trimmed, level, 0) - - conf.expandPosition = newLineInfo(fileIndex, line, col) - conf.expandLevels = level - conf.expandProgress = false - conf.expandNodeResult = "" - - graph.markDirty fileIndex - graph.markClientsDirty fileIndex - graph.recompilePartially() - var suggest = Suggest() - suggest.section = ideExpand - suggest.version = 3 - suggest.line = line - suggest.column = col - suggest.doc = graph.config.expandNodeResult - if suggest.doc != "": - let - n = parseFile(fileIndex, graph.cache, graph.config) - endInfo = n.calculateExpandRange(conf.expandPosition) - - suggest.endLine = endInfo.line - suggest.endCol = endInfo.col - - suggestResult(graph.config, suggest) - - graph.markDirty fileIndex - graph.markClientsDirty fileIndex - else: - myLog fmt "Discarding {cmd}" - -# v3 end when isMainModule: handleCmdLine(newIdentCache(), newConfigRef()) else: @@ -1062,12 +663,10 @@ else: proc mockCommand(graph: ModuleGraph) = retval = graph let conf = graph.config - conf.setCmd cmdIdeTools - defineSymbol(conf.symbols, $conf.backend) clearPasses(graph) registerPass graph, verbosePass registerPass graph, semPass - + conf.setCmd cmdIdeTools wantMainModule(conf) if not fileExists(conf.projectFull): @@ -1127,9 +726,8 @@ else: if self.loadConfigsAndProcessCmdLine(cache, conf, graph): mockCommand(graph) if gLogging: - myLog("Search paths:") for it in conf.searchPaths: - myLog(" " & 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 03369ccb7..cee538b6e 100644 --- a/nimsuggest/sexp.nim +++ b/nimsuggest/sexp.nim @@ -14,9 +14,6 @@ import import std/private/decode_helpers -when defined(nimPreviewSlimSystem): - import std/[assertions, formatfloat] - type SexpEventKind* = enum ## enumeration of all events that may occur when parsing sexpError, ## an error occurred during parsing @@ -291,6 +288,10 @@ 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) @@ -314,6 +315,10 @@ 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`. ## @@ -404,7 +409,7 @@ macro convertSexp*(x: untyped): untyped = ## `%` for every element. result = toSexp(x) -func `==`* (a, b: SexpNode): bool = +proc `==`* (a,b: SexpNode): bool = ## Check two nodes for equality if a.isNil: if b.isNil: return true @@ -591,7 +596,8 @@ 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 = SexpNode(kind: SString, str: move p.a) + result = newSStringMove(p.a) + p.a = "" discard getTok(p) of tkInt: result = newSInt(parseBiggestInt(p.a)) @@ -603,7 +609,8 @@ proc parseSexp(p: var SexpParser): SexpNode = result = newSNil() discard getTok(p) of tkSymbol: - result = SexpNode(kind: SSymbol, symbol: move p.a) + result = newSSymbolMove(p.a) + p.a = "" discard getTok(p) of tkParensLe: result = newSList() diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim index 060335959..019181810 100644 --- a/nimsuggest/tester.nim +++ b/nimsuggest/tester.nim @@ -2,11 +2,8 @@ # Every test file can have a #[!]# comment that is deleted from the input # before 'nimsuggest' is invoked to ensure this token doesn't make a # crucial difference for Nim's parser. -# When debugging, to run a single test, use for e.g.: -# `nim r nimsuggest/tester.nim nimsuggest/tests/tsug_accquote.nim` import os, osproc, strutils, streams, re, sexp, net -from sequtils import toSeq type Test = object @@ -16,17 +13,15 @@ type disabled: bool const + curDir = when defined(windows): "" else: "" DummyEof = "!EOF!" - tpath = "nimsuggest/tests" - # we could also use `stdtest/specialpaths` -import std/compilesettings +template tpath(): untyped = getAppDir() / "tests" proc parseTest(filename: string; epcMode=false): Test = const cursorMarker = "#[!]#" - let nimsug = "bin" / addFileExt("nimsuggest_testing", ExeExt) - doAssert nimsug.fileExists, nimsug - const libpath = querySetting(libPath) + let nimsug = curDir & addFileExt("nimsuggest", ExeExt) + let libpath = findExe("nim").splitFile().dir /../ "lib" result.filename = filename result.dest = getTempDir() / extractFilename(filename) result.cmd = nimsug & " --tester " & result.dest @@ -66,7 +61,7 @@ proc parseTest(filename: string; epcMode=false): Test = elif x.startsWith(">"): # since 'markers' here are not complete yet, we do the $substitutions # afterwards - result.script.add((x.substr(1).replaceWord("$path", tpath).replaceWord("$file", filename), "")) + result.script.add((x.substr(1).replaceWord("$path", tpath()), "")) elif x.len > 0: # expected output line: let x = x % ["file", filename, "lib", libpath] @@ -107,7 +102,7 @@ proc parseCmd(c: string): seq[string] = proc edit(tmpfile: string; x: seq[string]) = if x.len != 3 and x.len != 4: quit "!edit takes two or three arguments" - let f = if x.len >= 4: tpath / x[3] else: tmpfile + let f = if x.len >= 4: tpath() / x[3] else: tmpfile try: let content = readFile(f) let newcontent = content.replace(x[1], x[2]) @@ -124,12 +119,12 @@ proc exec(x: seq[string]) = proc copy(x: seq[string]) = if x.len != 3: quit "!copy takes two arguments" - let rel = tpath + let rel = tpath() copyFile(rel / x[1], rel / x[2]) proc del(x: seq[string]) = if x.len != 2: quit "!del takes one argument" - removeFile(tpath / x[1]) + removeFile(tpath() / x[1]) proc runCmd(cmd, dest: string): bool = result = cmd[0] == '!' @@ -218,12 +213,7 @@ proc sexpToAnswer(s: SexpNode): string = result.add doc result.add '\t' result.addInt a[8].getNum - if a.len >= 11: - result.add '\t' - result.addInt a[9].getNum - result.add '\t' - result.addInt a[10].getNum - elif a.len >= 10: + if a.len >= 10: result.add '\t' result.add a[9].getStr result.add '\L' @@ -234,8 +224,8 @@ proc doReport(filename, answer, resp: string; report: var string) = var hasDiff = false for i in 0..min(resp.len-1, answer.len-1): if resp[i] != answer[i]: - report.add "\n Expected:\n" & resp - report.add "\n But got:\n" & answer + report.add "\n Expected: " & resp.substr(i, i+200) + report.add "\n But got: " & answer.substr(i, i+200) hasDiff = true break if not hasDiff: @@ -257,15 +247,13 @@ proc runEpcTest(filename: string): int = for cmd in s.startup: if not runCmd(cmd, s.dest): quit "invalid command: " & cmd - let epccmd = if s.cmd.contains("--v3"): - s.cmd.replace("--tester", "--epc --log") - else: - s.cmd.replace("--tester", "--epc --v2 --log") + let epccmd = s.cmd.replace("--tester", "--epc --v2 --log") let cl = parseCmdLine(epccmd) var p = startProcess(command=cl[0], args=cl[1 .. ^1], options={poStdErrToStdOut, poUsePath, poInteractive, poDaemon}) let outp = p.outputStream + let inp = p.inputStream var report = "" var socket = newSocket() try: @@ -281,20 +269,15 @@ proc runEpcTest(filename: string): int = let a = outp.readAll().strip() let port = parseInt(a) socket.connect("localhost", Port(port)) - for req, resp in items(s.script): if not runCmd(req, s.dest): socket.sendEpcStr(req) let sx = parseSexp(socket.recvEpc()) if not req.startsWith("mod "): - let answer = if sx[2].kind == SNil: "" else: sexpToAnswer(sx) + let answer = sexpToAnswer(sx) doReport(filename, answer, resp, report) - - socket.sendEpcStr "return arg" - # bugfix: this was in `finally` block, causing the original error to be - # potentially masked by another one in case `socket.sendEpcStr` raises - # (e.g. if socket couldn't connect in the 1st place) finally: + socket.sendEpcStr "return arg" close(p) if report.len > 0: echo "==== EPC ========================================" @@ -330,12 +313,8 @@ proc runTest(filename: string): int = answer.add '\L' doReport(filename, answer, resp, report) finally: - try: - inp.writeLine("quit") - inp.flush() - except IOError, OSError: - # assume it's SIGPIPE, ie, the child already died - discard + inp.writeLine("quit") + inp.flush() close(p) if report.len > 0: echo "==== STDIN ======================================" @@ -347,16 +326,11 @@ proc main() = if os.paramCount() > 0: let x = os.paramStr(1) let xx = expandFilename x - # run only stdio when running single test failures += runTest(xx) + failures += runEpcTest(xx) else: - let files = toSeq(walkFiles(tpath / "t*.nim")) - for i, x in files: - echo "$#/$# test: $#" % [$i, $files.len, x] - when defined(i386): - if x == "nimsuggest/tests/tmacro_highlight.nim": - echo "skipping" # workaround bug #17945 - continue + for x in walkFiles(getAppDir() / "tests/t*.nim"): + echo "Test ", x let xx = expandFilename x when not defined(windows): # XXX Windows IO redirection seems bonkers: diff --git a/nimsuggest/tests/t20440.nim b/nimsuggest/tests/t20440.nim deleted file mode 100644 index 0456aa074..000000000 --- a/nimsuggest/tests/t20440.nim +++ /dev/null @@ -1,7 +0,0 @@ -when not defined(js): - {.fatal: "Crash".} -echo 4 - -discard """ -$nimsuggest --v3 --tester $file -""" diff --git a/nimsuggest/tests/t20440.nims b/nimsuggest/tests/t20440.nims deleted file mode 100644 index 1336be3d4..000000000 --- a/nimsuggest/tests/t20440.nims +++ /dev/null @@ -1 +0,0 @@ -switch("backend", "js") diff --git a/nimsuggest/tests/t21185.nim b/nimsuggest/tests/t21185.nim deleted file mode 100644 index bf5a0e3cc..000000000 --- a/nimsuggest/tests/t21185.nim +++ /dev/null @@ -1,18 +0,0 @@ - -# Reduced case of 21185. Issue was first parameter being static -proc foo(x: static[int]) = discard - -type - Person = object - name: string - age: int - -let p = Person() -p.#[!]# - -discard """ -$nimsuggest --tester --v3 --maxresults:2 $file ->sug $1 -sug;;skField;;age;;int;;$file;;8;;4;;"";;100;;None -sug;;skField;;name;;string;;$file;;7;;4;;"";;100;;None -""" diff --git a/nimsuggest/tests/tchk1.nim b/nimsuggest/tests/tchk1.nim index be6115c1c..c28b88b9b 100644 --- a/nimsuggest/tests/tchk1.nim +++ b/nimsuggest/tests/tchk1.nim @@ -17,11 +17,11 @@ proc main = discard """ $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk1.nim [Processing]";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tchk1 [Processing]";;0 chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but got \'keyword template\'";;0 chk;;skUnknown;;;;Error;;$file;;14;;0;;"nestable statement requires indentation";;0 -chk;;skUnknown;;;;Error;;$file;;17;;0;;"invalid indentation";;0 chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0 +chk;;skUnknown;;;;Error;;$file;;17;;0;;"invalid indentation";;0 chk;;skUnknown;;;;Hint;;$file;;12;;9;;"\'foo\' is declared but not used [XDeclaredButNotUsed]";;0 chk;;skUnknown;;;;Hint;;$file;;14;;5;;"\'main\' is declared but not used [XDeclaredButNotUsed]";;0 """ diff --git a/nimsuggest/tests/tchk_compiles.nim b/nimsuggest/tests/tchk_compiles.nim index c8a3daac4..887a947b5 100644 --- a/nimsuggest/tests/tchk_compiles.nim +++ b/nimsuggest/tests/tchk_compiles.nim @@ -4,5 +4,5 @@ discard compiles(2 + "hello") discard """ $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk_compiles.nim [Processing]";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tchk_compiles [Processing]";;0 """ diff --git a/nimsuggest/tests/tcon1.nim b/nimsuggest/tests/tcon1.nim index 627e7f400..262dd5151 100644 --- a/nimsuggest/tests/tcon1.nim +++ b/nimsuggest/tests/tcon1.nim @@ -1,43 +1,13 @@ -## Test Invocation `con`text in various situations - -## various of this proc are used as the basis for these tests proc test(s: string; a: int) = discard - -## This overload should be used to ensure the lower airity `test` doesn't match -proc test(s: string; a: string, b: int) = discard - -## similar signature but different name to ensure `con` doesn't get greedy proc testB(a, b: string) = discard - -# with a param already specified test("hello here", #[!]#) - -# as first param testB(#[!]# -# dot expressions -"from behind".test(#[!]# - -# two params matched, so disqualify the lower airity `test` -# TODO: this doesn't work, because dot exprs, overloads, etc aren't currently -# handled by suggest.suggestCall. sigmatch.partialMatch by way of -# sigmatch.matchesAux. Doesn't use the operand before the dot as part of -# the formal parameters. Changing this is tricky because it's used by -# the proper compilation sem pass and that's a big change all in one go. -"and again".test("more", #[!]# - discard """ $nimsuggest --tester $file >con $1 -con;;skProc;;tcon1.test;;proc (s: string, a: int);;$file;;4;;5;;"";;100 -con;;skProc;;tcon1.test;;proc (s: string, a: string, b: int);;$file;;7;;5;;"";;100 +con;;skProc;;tcon1.test;;proc (s: string, a: int);;$file;;1;;5;;"";;100 >con $2 -con;;skProc;;tcon1.testB;;proc (a: string, b: string);;$file;;10;;5;;"";;100 ->con $3 -con;;skProc;;tcon1.test;;proc (s: string, a: string, b: int);;$file;;7;;5;;"";;100 -con;;skProc;;tcon1.test;;proc (s: string, a: int);;$file;;4;;5;;"";;100 ->con $4 -con;;skProc;;tcon1.test;;proc (s: string, a: int);;$file;;4;;5;;"";;100 -con;;skProc;;tcon1.test;;proc (s: string, a: string, b: int);;$file;;7;;5;;"";;100 +con;;skProc;;tcon1.testB;;proc (a: string, b: string);;$file;;2;;5;;"";;100 """ diff --git a/nimsuggest/tests/tdef1.nim b/nimsuggest/tests/tdef1.nim index 5c86923b6..2cd040ea1 100644 --- a/nimsuggest/tests/tdef1.nim +++ b/nimsuggest/tests/tdef1.nim @@ -1,9 +1,9 @@ discard """ $nimsuggest --tester $file >def $1 -def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe.};;$file;;9;;5;;"Return hello";;100 +def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, locks: 0.};;$file;;9;;5;;"Return hello";;100 >def $1 -def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe.};;$file;;9;;5;;"Return hello";;100 +def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, locks: 0.};;$file;;9;;5;;"Return hello";;100 """ proc hello(): string = diff --git a/nimsuggest/tests/tdef_let.nim b/nimsuggest/tests/tdef_let.nim deleted file mode 100644 index 3e9456d2f..000000000 --- a/nimsuggest/tests/tdef_let.nim +++ /dev/null @@ -1,7 +0,0 @@ -discard """ -$nimsuggest --tester $file ->def $1 -def;;skLet;;tdef_let.intVar;;int;;$file;;7;;4;;"";;100 -""" - -let int#[!]#Var = 10 diff --git a/nimsuggest/tests/tdot4.nim b/nimsuggest/tests/tdot4.nim index 8681d045a..e1ff96553 100644 --- a/nimsuggest/tests/tdot4.nim +++ b/nimsuggest/tests/tdot4.nim @@ -15,7 +15,7 @@ discard """ $nimsuggest --tester --maxresults:2 $file >sug $1 sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;6;;5;;"";;100;;None -sug;;skFunc;;mstrutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe.};;*fixtures/mstrutils.nim;;9;;5;;"this is a test version of strutils.replace, it simply returns `by`";;100;;None +sug;;skFunc;;mstrutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;*fixtures/mstrutils.nim;;9;;5;;"this is a test version of strutils.replace, it simply returns `by`";;100;;None """ # TODO - determine appropriate behaviour for further suggest output and test it diff --git a/nimsuggest/tests/tgeneric_highlight.nim b/nimsuggest/tests/tgeneric_highlight.nim index f351ab705..334323613 100644 --- a/nimsuggest/tests/tgeneric_highlight.nim +++ b/nimsuggest/tests/tgeneric_highlight.nim @@ -8,10 +8,12 @@ $nimsuggest --tester $file highlight;;skType;;1;;7;;3 highlight;;skProc;;1;;0;;6 highlight;;skProc;;1;;0;;6 +highlight;;skType;;1;;7;;3 highlight;;skProc;;1;;0;;6 highlight;;skType;;2;;14;;3 highlight;;skProc;;2;;7;;6 highlight;;skProc;;2;;7;;6 +highlight;;skType;;2;;14;;3 highlight;;skProc;;2;;7;;6 highlight;;skTemplate;;3;;0;;8 highlight;;skType;;3;;9;;3 diff --git a/nimsuggest/tests/tinclude.nim b/nimsuggest/tests/tinclude.nim index 66726f907..b67440b9e 100644 --- a/nimsuggest/tests/tinclude.nim +++ b/nimsuggest/tests/tinclude.nim @@ -11,7 +11,7 @@ go() discard """ $nimsuggest --tester $file >def $path/tinclude.nim:7:14 -def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe.};;*fixtures/minclude_include.nim;;3;;5;;"";;100 +def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe, locks: 0.};;*fixtures/minclude_include.nim;;3;;5;;"";;100 >def $path/fixtures/minclude_include.nim:3:71 def;;skType;;minclude_types.Greet;;Greet;;*fixtures/minclude_types.nim;;4;;2;;"";;100 >def $path/fixtures/minclude_include.nim:3:71 diff --git a/nimsuggest/tests/tqualified_highlight.nim b/nimsuggest/tests/tqualified_highlight.nim index b83669e72..3b521ecc1 100644 --- a/nimsuggest/tests/tqualified_highlight.nim +++ b/nimsuggest/tests/tqualified_highlight.nim @@ -10,5 +10,5 @@ highlight;;skProc;;1;;7;;4 highlight;;skTemplate;;2;;7;;4 highlight;;skTemplate;;2;;7;;4 highlight;;skTemplate;;2;;7;;4 -highlight;;skFunc;;3;;8;;1 +highlight;;skProc;;3;;8;;1 """ diff --git a/nimsuggest/tests/tsug_enum.nim b/nimsuggest/tests/tsug_enum.nim deleted file mode 100644 index 97a225f16..000000000 --- a/nimsuggest/tests/tsug_enum.nim +++ /dev/null @@ -1,18 +0,0 @@ -## suggestions for enums - -type - LogLevel {.pure.} = enum - debug, log, warn, error - - FooBar = enum - fbFoo, fbBar - -echo fbFoo, fbBar - -echo LogLevel.deb#[!]# - -discard """ -$nimsuggest --tester $file ->sug $1 -sug;;skEnumField;;debug;;LogLevel;;*nimsuggest/tests/tsug_enum.nim;;5;;4;;"";;100;;Prefix -""" \ No newline at end of file diff --git a/nimsuggest/tests/tsug_regression.nim b/nimsuggest/tests/tsug_regression.nim index 2aff3fe94..ba2034bd3 100644 --- a/nimsuggest/tests/tsug_regression.nim +++ b/nimsuggest/tests/tsug_regression.nim @@ -16,19 +16,15 @@ proc main = cfg = loadConfig("file") map0.#[!]# -# the maxresults are limited as it seems there is sort or some other -# instability that causes the suggestions to slightly differ between 32 bit -# and 64 bit versions of nimsuggest - discard """ -disabled:true -$nimsuggest --tester --maxresults:4 $file +$nimsuggest --tester $file >sug $1 -sug;;skProc;;tables.hasKey;;proc (t: Table[hasKey.A, hasKey.B], key: A): bool;;*/lib/pure/collections/tables.nim;;374;;5;;"Returns true *";;100;;None -sug;;skProc;;tables.clear;;proc (t: var Table[clear.A, clear.B]);;*/lib/pure/collections/tables.nim;;567;;5;;"Resets the table so that it is empty*";;100;;None -sug;;skProc;;tables.contains;;proc (t: Table[contains.A, contains.B], key: A): bool;;*/lib/pure/collections/tables.nim;;*;;5;;"Alias of *";;100;;None -sug;;skProc;;tables.del;;proc (t: var Table[del.A, del.B], key: A);;*/lib/pure/collections/tables.nim;;*;;5;;"*";;100;;None +sug;;skProc;;tables.hasKey;;proc (t: Table[hasKey.A, hasKey.B], key: A): bool;;*/lib/pure/collections/tables.nim;;374;;5;;"Returns true if*";;100;;None +sug;;skProc;;tables.add;;proc (t: var Table[add.A, add.B], key: A, val: sink B);;*/lib/pure/collections/tables.nim;;505;;5;;"Puts a new*";;100;;None +sug;;skIterator;;tables.allValues;;iterator (t: Table[allValues.A, allValues.B], key: A): B{.inline.};;*/lib/pure/collections/tables.nim;;769;;9;;"Iterates over any*";;100;;None +sug;;skProc;;tables.clear;;proc (t: var Table[clear.A, clear.B]);;*/lib/pure/collections/tables.nim;;567;;5;;"Resets the table so that it is empty.*";;100;;None +sug;;skProc;;tables.contains;;proc (t: Table[contains.A, contains.B], key: A): bool;;*/lib/pure/collections/tables.nim;;392;;5;;"Alias of `hasKey*";;100;;None +* """ -# TODO enable the tests # TODO: test/fix suggestion sorting - deprecated suggestions should rank lower diff --git a/nimsuggest/tests/tsug_template.nim b/nimsuggest/tests/tsug_template.nim index a2558c81c..0f258fcc6 100644 --- a/nimsuggest/tests/tsug_template.nim +++ b/nimsuggest/tests/tsug_template.nim @@ -6,7 +6,7 @@ tmp#[!]# discard """ $nimsuggest --tester $file >sug $1 -sug;;skMacro;;tsug_template.tmpb;;macro (){.noSideEffect, gcsafe.};;$file;;2;;6;;"";;100;;Prefix -sug;;skConverter;;tsug_template.tmpc;;converter ();;$file;;3;;10;;"";;100;;Prefix -sug;;skTemplate;;tsug_template.tmpa;;template ();;$file;;1;;9;;"";;100;;Prefix +sug;;skMacro;;tsug_template.tmpb;;macro (){.noSideEffect, gcsafe, locks: 0.};;$file;;2;;6;;"";;0;;Prefix +sug;;skConverter;;tsug_template.tmpc;;converter ();;$file;;3;;10;;"";;0;;Prefix +sug;;skTemplate;;tsug_template.tmpa;;template ();;$file;;1;;9;;"";;0;;Prefix """ diff --git a/nimsuggest/tests/tsug_typedecl.nim b/nimsuggest/tests/tsug_typedecl.nim deleted file mode 100644 index 2a510929d..000000000 --- a/nimsuggest/tests/tsug_typedecl.nim +++ /dev/null @@ -1,26 +0,0 @@ -# suggestions for type declarations - -from system import string, int, bool - -type - super = int - someType = bool - -let str = "hello" - -proc main() = - let a: s#[!]# - -# This output show seq, even though that's not imported. This is due to the -# entire symbol table, regardless of import visibility is currently being -# scanned. This is hardly ideal, but changing it with the current level of test -# coverage is unwise as it might break more than it fixes. - -discard """ -$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/basic_types.nim;;*;;*;;*;;100;;Prefix -sug;;skType;;system.seq;;seq;;*lib/system.nim;;*;;*;;*;;100;;Prefix -""" diff --git a/nimsuggest/tests/ttempl_inst.nim b/nimsuggest/tests/ttempl_inst.nim index 5f5b10fe9..ed04a67ce 100644 --- a/nimsuggest/tests/ttempl_inst.nim +++ b/nimsuggest/tests/ttempl_inst.nim @@ -7,7 +7,7 @@ foo() discard """ $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/ttempl_inst.nim [Processing]";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;"ttempl_inst [Processing]";;0 chk;;skUnknown;;;;Hint;;$file;;4;;3;;"template/generic instantiation from here";;0 chk;;skUnknown;;;;Warning;;$file;;2;;11;;"foo [User]";;0 """ diff --git a/nimsuggest/tests/ttype_decl.nim b/nimsuggest/tests/ttype_decl.nim index 61d8c26cd..d7ed63ed0 100644 --- a/nimsuggest/tests/ttype_decl.nim +++ b/nimsuggest/tests/ttype_decl.nim @@ -1,9 +1,9 @@ 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/basic_types.nim;;23;;2;;"";;100;;None +sug;;skType;;ttype_decl.Other;;Other;;$file;;10;;2;;"";;0;;None +sug;;skType;;system.int;;int;;*/lib/system/basic_types.nim;;2;;2;;"";;0;;None +sug;;skType;;system.string;;string;;*/lib/system.nim;;34;;2;;"";;0;;None """ import strutils type diff --git a/nimsuggest/tests/tuse.nim b/nimsuggest/tests/tuse.nim deleted file mode 100644 index deaf81ef2..000000000 --- a/nimsuggest/tests/tuse.nim +++ /dev/null @@ -1,22 +0,0 @@ -# basic tests for use - -# bug #58 -proc someOtherProc() = - discard - -someOtherProc() - -proc #[!]#someProc*() = - discard - -#[!]#someProc() - -discard """ -$nimsuggest --tester $file ->use $1 -def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe.};;$file;;9;;5;;"";;100 -use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe.};;$file;;12;;0;;"";;100 ->use $2 -def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe.};;$file;;9;;5;;"";;100 -use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe.};;$file;;12;;0;;"";;100 -""" diff --git a/nimsuggest/tests/tuse_structure.nim b/nimsuggest/tests/tuse_structure.nim deleted file mode 100644 index f65ab9060..000000000 --- a/nimsuggest/tests/tuse_structure.nim +++ /dev/null @@ -1,15 +0,0 @@ -# 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 deleted file mode 100644 index fd736a1d8..000000000 --- a/nimsuggest/tests/tv3.nim +++ /dev/null @@ -1,27 +0,0 @@ -# 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 ->sug $1 -sug skField bar string $file 5 4 "" 100 Prefix ->globalSymbols test -def skProc tv3.test proc (f: Foo){.gcsafe.} $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 deleted file mode 100644 index 03684b7cd..000000000 --- a/nimsuggest/tests/tv3_definition.nim +++ /dev/null @@ -1,9 +0,0 @@ - -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_forward_definition.nim b/nimsuggest/tests/tv3_forward_definition.nim deleted file mode 100644 index 392e019ff..000000000 --- a/nimsuggest/tests/tv3_forward_definition.nim +++ /dev/null @@ -1,23 +0,0 @@ -proc de#[!]#mo(): int - -proc de#[!]#mo(): int = 5 - -let a = de#[!]#mo() - -discard """ -$nimsuggest --v3 --tester $file ->use $1 -use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 1 5 "" 100 -def skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 3 5 "" 100 -use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 5 8 "" 100 ->use $2 -use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 1 5 "" 100 -def skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 3 5 "" 100 -use skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 5 8 "" 100 ->declaration $1 -declaration skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 3 5 "" 100 ->declaration $2 -declaration skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 1 5 "" 100 ->declaration $3 -declaration skProc tv3_forward_definition.demo proc (): int{.noSideEffect, gcsafe.} $file 1 5 "" 100 -""" diff --git a/nimsuggest/tests/tv3_globalSymbols.nim b/nimsuggest/tests/tv3_globalSymbols.nim deleted file mode 100644 index f965a07aa..000000000 --- a/nimsuggest/tests/tv3_globalSymbols.nim +++ /dev/null @@ -1,14 +0,0 @@ -# Tests the order of the matches -proc Btoken(): int = 5 -proc tokenA(): int = 5 -proc token(): int = 5 -proc BBtokenA(): int = 5 - -discard """ -$nimsuggest --v3 --tester $file ->globalSymbols token -def skProc tv3_globalSymbols.token proc (): int{.noSideEffect, gcsafe.} $file 4 5 "" 100 -def skProc tv3_globalSymbols.tokenA proc (): int{.noSideEffect, gcsafe.} $file 3 5 "" 100 -def skProc tv3_globalSymbols.Btoken proc (): int{.noSideEffect, gcsafe.} $file 2 5 "" 100 -def skProc tv3_globalSymbols.BBtokenA proc (): int{.noSideEffect, gcsafe.} $file 5 5 "" 100 -""" diff --git a/nimsuggest/tests/tv3_import.nim b/nimsuggest/tests/tv3_import.nim deleted file mode 100644 index 3c128f85b..000000000 --- a/nimsuggest/tests/tv3_import.nim +++ /dev/null @@ -1,7 +0,0 @@ -import tv#[!]#3 - -discard """ -$nimsuggest --v3 --tester $file ->def $1 -def skModule tv3 */tv3.nim 1 0 "" 100 -""" diff --git a/nimsuggest/tests/tv3_outline.nim b/nimsuggest/tests/tv3_outline.nim deleted file mode 100644 index 6370948d9..000000000 --- a/nimsuggest/tests/tv3_outline.nim +++ /dev/null @@ -1,45 +0,0 @@ -# tests v3 outline - -type - Foo* = ref object of RootObj - bar*: string - FooEnum = enum value1, value2 - FooPrivate = ref object of RootObj - barPrivate: string - -macro m(arg: untyped): untyped = discard -template t(arg: untyped): untyped = discard -proc p(): void = discard -iterator i(): int = discard -converter c(s: string): int = discard -method m(f: Foo): void = discard -func f(): void = discard - -let a = 1 -var b = 2 -const con = 2 - -proc outer(): void = - proc inner() = discard - -proc procWithLocal(): void = - let local = 10 - -discard """ -$nimsuggest --v3 --tester $file ->outline $file -outline skType tv3_outline.Foo Foo $file 4 2 "" 100 5 16 -outline skType tv3_outline.FooEnum FooEnum $file 6 2 "" 100 6 31 -outline skEnumField tv3_outline.FooEnum.value1 FooEnum $file 6 17 "" 100 6 23 -outline skEnumField tv3_outline.FooEnum.value2 FooEnum $file 6 25 "" 100 6 31 -outline skType tv3_outline.FooPrivate FooPrivate $file 7 2 "" 100 8 22 -outline skMacro tv3_outline.m macro (arg: untyped): untyped{.noSideEffect, gcsafe.} $file 10 6 "" 100 10 40 -outline skTemplate tv3_outline.t template (arg: untyped): untyped $file 11 9 "" 100 11 43 -outline skProc tv3_outline.p proc (){.noSideEffect, gcsafe.} $file 12 5 "" 100 12 24 -outline skConverter tv3_outline.c converter (s: string): int{.noSideEffect, gcsafe.} $file 14 10 "" 100 14 37 -outline skFunc tv3_outline.f proc (){.noSideEffect, gcsafe.} $file 16 5 "" 100 16 24 -outline skConst tv3_outline.con int literal(2) $file 20 6 "" 100 20 13 -outline skProc tv3_outline.outer proc (){.noSideEffect, gcsafe.} $file 22 5 "" 100 23 24 -outline skProc tv3_outline.outer.inner proc (){.noSideEffect, gcsafe.} $file 23 7 "" 100 23 24 -outline skProc tv3_outline.procWithLocal proc (){.noSideEffect, gcsafe.} $file 25 5 "" 100 26 16 -""" diff --git a/nimsuggest/tests/tv3_typeDefinition.nim b/nimsuggest/tests/tv3_typeDefinition.nim deleted file mode 100644 index f86d12cc6..000000000 --- a/nimsuggest/tests/tv3_typeDefinition.nim +++ /dev/null @@ -1,32 +0,0 @@ -# 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/nimsuggest/tests/twithin_macro.nim b/nimsuggest/tests/twithin_macro.nim index d79dae1be..2f5e278c4 100644 --- a/nimsuggest/tests/twithin_macro.nim +++ b/nimsuggest/tests/twithin_macro.nim @@ -41,11 +41,15 @@ echo r.age_human_yrs() echo r discard """ -$nimsuggest --tester --maxresults:5 $file +disabled:true +$nimsuggest --tester $file >sug $1 sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;None sug;;skField;;name;;string;;$file;;5;;6;;"";;100;;None sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int;;$file;;8;;9;;"";;100;;None sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string;;$file;;7;;9;;"";;100;;None sug;;skMethod;;twithin_macro.vocalize;;proc (self: Rabbit): string;;$file;;23;;9;;"";;100;;None +sug;;skMacro;;twithin_macro.class;;proc (head: untyped, body: untyped): untyped{.gcsafe, locks: .};;$file;;4;;6;;"";;50;;None* """ + +# TODO: disabled due to semantic error reporting in nimsuggest results diff --git a/nimsuggest/tests/twithin_macro_prefix.nim b/nimsuggest/tests/twithin_macro_prefix.nim index dd3810818..1c06397c5 100644 --- a/nimsuggest/tests/twithin_macro_prefix.nim +++ b/nimsuggest/tests/twithin_macro_prefix.nim @@ -46,3 +46,10 @@ $nimsuggest --tester $file sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;Prefix sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int;;$file;;8;;9;;"";;100;;Prefix """ + +#[ +TODO: additional calls to `>sug $1` produces different output with errors, + possibly related to cached results from the first analysis, which refers + to expanded macros/templates which rely on imported symbols from `system` + module that are not present in this module. +]# diff --git a/readme.md b/readme.md index 325e8fff9..fa7a2d86d 100644 --- a/readme.md +++ b/readme.md @@ -11,24 +11,22 @@ the latest release, check out [Nim's website][nim-site] or [bleeding edge docs]( [![Join the IRC chat][badge-nim-irc]][nim-irc] [![Join the Discord server][badge-nim-discord]][nim-discord] [![Join the Gitter chat][badge-nim-gitter]][nim-gitter] -[![Join the Matrix room](https://img.shields.io/matrix/nim-lang:matrix.org?color=blue&style=flat&logo=matrix)](https://matrix.to/#/#nim-lang:matrix.org) [![Get help][badge-nim-forum-gethelp]][nim-forum] [![View Nim posts on Stack Overflow][badge-nim-stackoverflow]][nim-stackoverflow-newest] [![Follow @nim_lang on Twitter][badge-nim-twitter]][nim-twitter] * The [forum][nim-forum] - the best place to ask questions and to discuss Nim. -* [#nim IRC Channel (Libera Chat)][nim-irc] - a place to discuss Nim in real-time. +* [#nim IRC Channel (Freenode)][nim-irc] - a place to discuss Nim in real-time. Also where most development decisions get made. * [Discord][nim-discord] - an additional place to discuss Nim in real-time. Most channels there are bridged to IRC. * [Gitter][nim-gitter] - an additional place to discuss Nim in real-time. There is a bridge between Gitter and the IRC channel. -* [Matrix][nim-matrix] - the main room to discuss Nim in real-time. [Matrix space][nim-matrix-space] contains a list of rooms, most of them are bridged to IRC. * [Telegram][nim-telegram] - an additional place to discuss Nim in real-time. There is the official Telegram channel. Not bridged to IRC. * [Stack Overflow][nim-stackoverflow] - a popular Q/A site for programming related topics that includes posts about Nim. -* [GitHub Wiki][nim-wiki] - Misc user-contributed content. +* [Github Wiki][nim-wiki] - Misc user-contributed content. ## Compiling @@ -37,7 +35,7 @@ architecture combinations: * Windows (Windows XP or greater) - x86 and x86_64 * Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l - * Mac OS X (10.04 or greater) - x86, x86_64, ppc64 and Apple Silicon (based on the ARM64 architecture) + * Mac OS X (10.04 or greater) - x86, x86_64 and ppc64 More platforms are supported, however, they are not tested regularly and they may not be as stable as the above-listed platforms. @@ -47,12 +45,12 @@ Compiling the Nim compiler is quite straightforward if you follow these steps: First, the C source of an older version of the Nim compiler is needed to bootstrap the latest version because the Nim compiler itself is written in the Nim programming language. Those C sources are available within the -[``nim-lang/csources_v2``][csources-v2-repo] repository. +[``nim-lang/csources``][csources-repo] repository. Next, to build from source you will need: - * 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 + * 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 later. * Either ``git`` or ``wget`` to download the needed source repositories. * The ``build-essential`` package when using ``gcc`` on Ubuntu (and likely @@ -73,7 +71,8 @@ the installation instructions on the website to do so: https://nim-lang.org/inst For package maintainers: see [packaging guidelines](https://nim-lang.github.io/Nim/packaging.html). -First, get Nim from GitHub: + +First, get Nim from github: ``` git clone https://github.com/nim-lang/Nim.git @@ -85,12 +84,14 @@ Next, run the appropriate build shell script for your platform: * `build_all.sh` (Linux, Mac) * `build_all.bat` (Windows) +Windows requires a number of other dependencies that you may need to install including +PCRE and OpenSSL. Nim hosts a zip package containing known working versions of the +required DLLs [here](https://nim-lang.org/download/dlls.zip). + Finally, once you have finished the build steps (on Windows, Mac, or Linux) you should add the ``bin`` directory to your PATH. -See also [bootstrapping the compiler](https://nim-lang.github.io/Nim/intern.html#bootstrapping-the-compiler). - -See also [reproducible builds](https://nim-lang.github.io/Nim/intern.html#bootstrapping-the-compiler-reproducible-builds). +See also [rebuilding the compiler](doc/intern.rst#rebuilding-the-compiler). ## Koch @@ -104,7 +105,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.md](https://nim-lang.github.io/Nim/koch.html) file. +within the [doc/koch.rst](doc/koch.rst) file. ## Nimble @@ -141,7 +142,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`` (mostly invoked via +* ``tools/`` - the tools including ``niminst`` and ``nimweb`` (mostly invoked via ``koch``). * ``koch.nim`` - the tool used to bootstrap Nim, generate C sources, build the website, and generate the documentation. @@ -151,7 +152,7 @@ read [this guide][pull-request-instructions]. Ideally, you should make sure that all tests pass before submitting a pull request. However, if you are short on time, you can just run the tests specific to your -changes by only running the corresponding categories of tests. CI verifies +changes by only running the corresponding categories of tests. Travis CI verifies that all tests pass before allowing the pull request to be accepted, so only running specific tests should be harmless. Integration tests should go in ``tests/untestable``. @@ -203,27 +204,25 @@ Nim. You are explicitly permitted to develop commercial applications using Nim. Please read the [copying.txt](copying.txt) file for more details. -Copyright © 2006-2023 Andreas Rumpf, all rights reserved. +Copyright © 2006-2021 Andreas Rumpf, all rights reserved. [nim-site]: https://nim-lang.org [nim-forum]: https://forum.nim-lang.org [nim-issues]: https://github.com/nim-lang/Nim/issues [nim-issues-easy]: https://github.com/nim-lang/Nim/labels/Easy -[nim-irc]: https://web.libera.chat/#nim +[nim-irc]: https://webchat.freenode.net/?channels=nim [nim-twitter]: https://twitter.com/nim_lang [nim-stackoverflow]: https://stackoverflow.com/questions/tagged/nim-lang [nim-stackoverflow-newest]: https://stackoverflow.com/questions/tagged/nim-lang?sort=newest&pageSize=15 [nim-discord]: https://discord.gg/nim [nim-gitter]: https://gitter.im/nim-lang/Nim -[nim-matrix]: https://matrix.to/#/#nim-lang:matrix.org -[nim-matrix-space]: https://matrix.to/#/#nim:envs.net [nim-telegram]: https://t.me/nim_lang [nim-bountysource]: https://www.bountysource.com/teams/nim [nim-bitcoin]: https://blockchain.info/address/1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ [nimble-repo]: https://github.com/nim-lang/nimble [nimsuggest-repo]: https://github.com/nim-lang/nimsuggest -[csources-repo-deprecated]: https://github.com/nim-lang/csources -[csources-v2-repo]: https://github.com/nim-lang/csources_v2 +[csources-repo]: https://github.com/nim-lang/csources +[badge-nim-travisci]: https://img.shields.io/travis/nim-lang/Nim/devel.svg?style=flat-square [badge-nim-irc]: https://img.shields.io/badge/chat-on_irc-blue.svg?style=flat-square [badge-nim-discord]: https://img.shields.io/discord/371759389889003530?color=blue&label=discord&logo=discord&logoColor=gold&style=flat-square [badge-nim-gitter]: https://img.shields.io/badge/chat-on_gitter-blue.svg?style=flat-square diff --git a/security.md b/security.md index 72a1a3e3d..e8d31b1b9 100644 --- a/security.md +++ b/security.md @@ -2,10 +2,7 @@ ## Supported Versions -Security advisories are published at: -https://github.com/nim-lang/security/security/advisories?state=published - -Security fixes are provided in new releases and in bugfix releases. +Security fixes are provided in new releases and bugfix releases. We do not backport security fixes to older releases. @@ -13,8 +10,8 @@ We do not backport security fixes to older releases. ## Reporting a Vulnerability -If you have discovered a vulnerability, please notify us about it via +Please do not report vulnerabilities via GitHub issues. + +If you have discovered a vulnerability, it is best to notify us about it via security@nim-lang.org in order to set up a meeting where we can discuss the next steps. - -Please do not report vulnerabilities via GitHub issues. diff --git a/testament/backend.nim b/testament/backend.nim index 1770c6657..0a6d7d8b9 100644 --- a/testament/backend.nim +++ b/testament/backend.nim @@ -13,6 +13,7 @@ type CommitId = distinct string proc `$`*(id: MachineId): string {.borrow.} +#proc `$`(id: CommitId): string {.borrow.} # not used var thisMachine: MachineId @@ -20,10 +21,10 @@ var thisBranch: string proc getMachine*(): MachineId = - var name = execProcess("hostname").strip + var name = execProcess("hostname").string.strip if name.len == 0: - name = when defined(posix): getEnv("HOSTNAME") - else: getEnv("COMPUTERNAME") + name = when defined(posix): getEnv("HOSTNAME").string + else: getEnv("COMPUTERNAME").string if name.len == 0: quit "cannot determine the machine name" @@ -31,8 +32,8 @@ proc getMachine*(): MachineId = proc getCommit(): CommitId = const commLen = "commit ".len - let hash = execProcess("git log -n 1").strip[commLen..commLen+10] - thisBranch = execProcess("git symbolic-ref --short HEAD").strip + let hash = execProcess("git log -n 1").string.strip[commLen..commLen+10] + thisBranch = execProcess("git symbolic-ref --short HEAD").string.strip if hash.len == 0 or thisBranch.len == 0: quit "cannot determine git HEAD" result = CommitId(hash) diff --git a/testament/caasdriver.nim b/testament/caasdriver.nim index 01e402e07..30383bddb 100644 --- a/testament/caasdriver.nim +++ b/testament/caasdriver.nim @@ -62,7 +62,7 @@ proc doCaasCommand(session: var NimSession, command: string): string = result = "" while true: - var line = "" + var line = TaintedString("") if session.nim.outputStream.readLine(line): if line.string == "": break result.add(line.string & "\n") @@ -78,7 +78,7 @@ proc doProcCommand(session: var NimSession, command: string): string = var process = startProcess(NimBin, args = session.replaceVars(command).split) stream = outputStream(process) - line = "" + line = TaintedString("") result = "" while stream.readLine(line): @@ -113,12 +113,12 @@ proc doScenario(script: string, output: Stream, mode: TRunMode, verbose: bool): result = true var f = open(script) - var project = "" + var project = TaintedString("") if f.readLine(project): var s = startNimSession(script.parentDir / project.string, script, mode) - tline = "" + tline = TaintedString("") ln = 1 while f.readLine(tline): @@ -175,7 +175,7 @@ when isMainModule: verbose = false for i in 0..paramCount() - 1: - let param = paramStr(i + 1) + let param = string(paramStr(i + 1)) case param of "verbose": verbose = true else: filter = param diff --git a/testament/categories.nim b/testament/categories.nim index c428ffc04..39f0aaa27 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -13,8 +13,6 @@ # included from testament.nim import important_packages -import std/[strformat, strutils] -from std/sequtils import filterIt const specialCategories = [ @@ -23,21 +21,25 @@ const "debugger", "dll", "examples", + "flags", "gc", "io", "js", "ic", "lib", + "longgc", "manyloc", - "nimble-packages", + "nimble-packages-1", + "nimble-packages-2", "niminaction", "threads", - "untestable", # see trunner_special + "untestable", "testdata", "nimcache", "coroutines", "osproc", "shouldfail", + "dir with space", "destructor" ] @@ -45,63 +47,120 @@ proc isTestFile*(file: string): bool = let (_, name, ext) = splitFile(file) result = ext == ".nim" and name.startsWith("t") +# ---------------- IC tests --------------------------------------------- + +# xxx deadcode +proc icTests(r: var TResults; testsDir: string, cat: Category, options: string) = + const + tooltests = ["compiler/nim.nim", "tools/nimgrep.nim"] + writeOnly = " --incremental:writeonly " + readOnly = " --incremental:readonly " + incrementalOn = " --incremental:on " + + template test(x: untyped) = + testSpecWithNimcache(r, makeRawTest(file, x & options, cat), nimcache) + + template editedTest(x: untyped) = + var test = makeTest(file, x & options, cat) + test.spec.targets = {getTestSpecTarget()} + testSpecWithNimcache(r, test, nimcache) + + const tempExt = "_temp.nim" + for it in walkDirRec(testsDir / "ic"): + if isTestFile(it) and not it.endsWith(tempExt): + let nimcache = nimcacheDir(it, options, getTestSpecTarget()) + removeDir(nimcache) + + let content = readFile(it) + for fragment in content.split("#!EDIT!#"): + let file = it.replace(".nim", tempExt) + writeFile(file, fragment) + let oldPassed = r.passed + editedTest incrementalOn + if r.passed != oldPassed+1: break + + for file in tooltests: + let nimcache = nimcacheDir(file, options, getTestSpecTarget()) + removeDir(nimcache) + + let oldPassed = r.passed + test writeOnly + + if r.passed == oldPassed+1: + test readOnly + if r.passed == oldPassed+2: + test readOnly & "-d:nimBackendAssumesChange " + +# --------------------- flags tests ------------------------------------------- + +proc flagTests(r: var TResults, cat: Category, options: string) = + # --genscript + const filename = testsDir/"flags"/"tgenscript" + const genopts = " --genscript" + let nimcache = nimcacheDir(filename, genopts, targetC) + testSpec r, makeTest(filename, genopts, cat) + + when defined(windows): + testExec r, makeTest(filename, " cmd /c cd " & nimcache & + " && compile_tgenscript.bat", cat) + + elif defined(posix): + testExec r, makeTest(filename, " sh -c \"cd " & nimcache & + " && sh compile_tgenscript.sh\"", cat) + + # Run + testExec r, makeTest(filename, " " & nimcache / "tgenscript", cat) + # --------------------- DLL generation tests ---------------------------------- -proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string, isOrc = false) = +proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = const rpath = when defined(macosx): " --passL:-rpath --passL:@loader_path" else: "" - if not defined(windows) or not isOrc: # todo fix me on windows - var test1 = makeTest("lib/nimrtl.nim", options & " --outdir:tests/dll", cat) - test1.spec.action = actionCompile - testSpec c, test1 + var test1 = makeTest("lib/nimrtl.nim", options & " --outdir:tests/dll", cat) + test1.spec.action = actionCompile + testSpec c, test1 var test2 = makeTest("tests/dll/server.nim", options & " --threads:on" & rpath, cat) test2.spec.action = actionCompile testSpec c, test2 - - if not isOrc: - 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 & " --threads:off --app:lib" & rpath, cat) + var test3 = makeTest("lib/nimhcr.nim", options & " --outdir:tests/dll" & rpath, cat) + test3.spec.action = actionCompile + testSpec c, test3 + var test4 = makeTest("tests/dll/visibility.nim", options & " --app:lib" & rpath, cat) test4.spec.action = actionCompile testSpec c, test4 # windows looks in the dir of the exe (yay!): - when not defined(windows): + when not defined(Windows): # posix relies on crappy LD_LIBRARY_PATH (ugh!): const libpathenv = when defined(haiku): "LIBRARY_PATH" else: "LD_LIBRARY_PATH" - var libpath = getEnv(libpathenv) + var libpath = getEnv(libpathenv).string # Temporarily add the lib directory to LD_LIBRARY_PATH: putEnv(libpathenv, "tests/dll" & (if libpath.len > 0: ":" & libpath else: "")) defer: putEnv(libpathenv, libpath) - if not isOrc: - testSpec r, makeTest("tests/dll/client.nim", options & " --threads:on" & 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) + 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) 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 & " --threads:off --forceBuild --hotCodeReloading:on" & rpath, cat) + options & " --forceBuild --hotCodeReloading:on" & rpath, cat) let nimcache = nimcacheDir(hcri.name, hcri.options, getTestSpecTarget()) - let cmd = prepareTestCmd(hcri.spec.getCmd, hcri.name, + hcri.args = prepareTestArgs(hcri.spec.getCmd, hcri.name, hcri.options, nimcache, getTestSpecTarget()) - hcri.testArgs = cmd.parseCmdLine testSpec r, hcri proc dllTests(r: var TResults, cat: Category, options: string) = # dummy compile result: var c = initResults() - runBasicDLLTest c, r, cat, options & " --mm:refc" - runBasicDLLTest c, r, cat, options & " -d:release --mm:refc" - runBasicDLLTest c, r, cat, options, isOrc = true - runBasicDLLTest c, r, cat, options & " -d:release", isOrc = true + runBasicDLLTest c, r, cat, options + runBasicDLLTest c, r, cat, options & " -d:release" when not defined(windows): # still cannot find a recent Windows version of boehm.dll: runBasicDLLTest c, r, cat, options & " --gc:boehm" @@ -111,9 +170,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 & "--mm:refc", cat) + testSpec r, makeTest("tests/gc" / filename, options, cat) testSpec r, makeTest("tests/gc" / filename, options & - " -d:release -d:useRealtimeGC --mm:refc", cat) + " -d:release -d:useRealtimeGC", cat) when filename != "gctest": testSpec r, makeTest("tests/gc" / filename, options & " --gc:orc", cat) @@ -158,7 +217,19 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "stackrefleak" test "cyclecollector" - testWithoutBoehm "trace_globals" + +proc longGCTests(r: var TResults, cat: Category, options: string) = + when defined(windows): + let cOptions = "-ldl -DWIN" + else: + let cOptions = "-ldl" + + var c = initResults() + # According to ioTests, this should compile the file + testSpec c, makeTest("tests/realtimeGC/shared", options, cat) + # ^- why is this not appended to r? Should this be discarded? + testC r, makeTest("tests/realtimeGC/cmain", cOptions, cat), actionRun + testSpec r, makeTest("tests/realtimeGC/nmain", options & "--threads: on", cat) # ------------------------- threading tests ----------------------------------- @@ -177,11 +248,6 @@ proc ioTests(r: var TResults, cat: Category, options: string) = # dummy compile result: var c = initResults() testSpec c, makeTest("tests/system/helpers/readall_echo", options, cat) - # ^- why is this not appended to r? Should this be discarded? - # EDIT: this should be replaced by something like in D20210524T180826, - # likewise in similar instances where `testSpec c` is used, or more generally - # when a test depends on another test, as it makes tests non-independent, - # creating complications for batching and megatest logic. testSpec r, makeTest("tests/system/tio", options, cat) # ------------------------- async tests --------------------------------------- @@ -199,7 +265,7 @@ proc debuggerTests(r: var TResults, cat: Category, options: string) = t.spec.action = actionCompile # force target to C because of MacOS 10.15 SDK headers bug # https://github.com/nim-lang/Nim/pull/15612#issuecomment-712471879 - t.spec.targets = {targetC} + t.spec.targets = { targetC } testSpec r, t # ------------------------- JS tests ------------------------------------------ @@ -226,6 +292,8 @@ proc jsTests(r: var TResults, cat: Category, options: string) = # ------------------------- nim in action ----------- proc testNimInAction(r: var TResults, cat: Category, options: string) = + let options = options & " --nilseqs:on" + template test(filename: untyped) = testSpec r, makeTest(filename, options, cat) @@ -316,7 +384,7 @@ proc findMainFile(dir: string): string = var nimFiles = 0 for kind, file in os.walkDir(dir): if kind == pcFile: - if file.endsWith(cfgExt): return file[0..^(cfgExt.len+1)] & ".nim" + if file.endsWith(cfgExt): return file[.. ^(cfgExt.len+1)] & ".nim" elif file.endsWith(".nim"): if result.len == 0: result = file inc nimFiles @@ -361,7 +429,7 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = files.sort # reproducible order for testFile in files: - let contents = readFile(testFile) + let contents = readFile(testFile).string var testObj = makeTest(testFile, options, cat) #[ todo: @@ -376,165 +444,131 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = testSpec r, testObj # ----------------------------- nimble ---------------------------------------- -proc listPackagesAll(): seq[NimblePackage] = - var nimbleDir = getEnv("NIMBLE_DIR") - if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble" - let packageIndex = nimbleDir / "packages_official.json" + +var nimbleDir = getEnv("NIMBLE_DIR").string +if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble" +let + nimbleExe = findExe("nimble") + packageIndex = nimbleDir / "packages_official.json" + +type + PkgPart = enum + ppOne + ppTwo + +iterator listPackages(part: PkgPart): tuple[name, cmd, url: string, useHead: bool] = let packageList = parseFile(packageIndex) - proc findPackage(name: string): JsonNode = - for a in packageList: - if a["name"].str == name: return a - for pkg in important_packages.packages.items: - var pkg = pkg - if pkg.url.len == 0: - let pkg2 = findPackage(pkg.name) - if pkg2 == nil: - raise newException(ValueError, "Cannot find package '$#'." % pkg.name) - pkg.url = pkg2["url"].str - result.add pkg - -proc listPackages(packageFilter: string): seq[NimblePackage] = - let pkgs = listPackagesAll() - if packageFilter.len != 0: - # xxx document `packageFilter`, seems like a bad API, - # at least should be a regex; a substring match makes no sense. - result = pkgs.filterIt(packageFilter in it.name) - else: - if testamentData0.batchArg == "allowed_failures": - result = pkgs.filterIt(it.allowFailure) - elif testamentData0.testamentNumBatch == 0: - result = pkgs + let importantList = + case part + of ppOne: important_packages.packages1 + of ppTwo: important_packages.packages2 + for n, cmd, url, useHead in importantList.items: + if url.len != 0: + yield (n, cmd, url, useHead) else: - let pkgs2 = pkgs.filterIt(not it.allowFailure) - for i in 0.. 0: outputExpected.add runSpec.output if not runSpec.output.endsWith "\n": outputExpected.add '\n' - if buf != outputExpected: + if buf.string != outputExpected: writeFile(outputExceptedFile, outputExpected) - echo diffFiles(outputGottenFile, outputExceptedFile).output - echo failString & "megatest output different, see $1 vs $2" % [outputGottenFile, outputExceptedFile] + discard execShellCmd("diff -uNdr $1 $2" % [outputExceptedFile, outputGottenFile]) + echo failString & "megatest output different!" # outputGottenFile, outputExceptedFile not removed on purpose for debugging. quit 1 else: echo "megatest output OK" - + when false: # no point removing those, always good for debugging + removeFile(outputGottenFile) + removeFile(megatestFile) # keep it around + #testSpec r, makeTest("megatest", options, cat) # --------------------------------------------------------------------------- proc processCategory(r: var TResults, cat: Category, options, testsDir: string, runJoinableTests: bool) = - let cat2 = cat.string.normalize - var handled = false - if isNimRepoTests(): - handled = true - case cat2 - of "js": - # only run the JS tests on Windows or Linux because Travis is bad - # and other OSes like Haiku might lack nodejs: - if not defined(linux) and isTravis: - discard - else: - jsTests(r, cat, options) - of "dll": - dllTests(r, cat, options) - of "gc": - gcTests(r, cat, options) - of "debugger": - debuggerTests(r, cat, options) - of "manyloc": - manyLoc r, cat, options - of "threads": - threadTests r, cat, options & " --threads:on" - of "io": - ioTests r, cat, options - of "async": - asyncTests r, cat, options - of "lib": - testStdlib(r, "lib/pure/", options, cat) - testStdlib(r, "lib/packages/docutils/", options, cat) - of "examples": - compileExample(r, "examples/*.nim", options, cat) - compileExample(r, "examples/gtk/*.nim", options, cat) - compileExample(r, "examples/talk/*.nim", options, cat) - of "nimble-packages": - testNimblePackages(r, cat, options) - of "niminaction": - testNimInAction(r, cat, options) - of "ic": - icTests(r, testsDir / cat2, cat, options, isNavigatorTest=false) - of "navigator": - icTests(r, testsDir / cat2, cat, options, isNavigatorTest=true) - of "untestable": - # These require special treatment e.g. because they depend on a third party - # dependency; see `trunner_special` which runs some of those. + case cat.string.normalize + of "ic": + when false: + icTests(r, testsDir, cat, options) + of "js": + # only run the JS tests on Windows or Linux because Travis is bad + # and other OSes like Haiku might lack nodejs: + if not defined(linux) and isTravis: discard else: - handled = false - if not handled: - case cat2 - of "megatest": - runJoinedTest(r, cat, testsDir, options) - if isNimRepoTests(): - runJoinedTest(r, cat, testsDir, options & " --mm:refc") - else: - var testsRun = 0 - var files: seq[string] - for file in walkDirRec(testsDir &.? cat.string): - if isTestFile(file): files.add file - files.sort # give reproducible order - for i, name in files: - var test = makeTest(name, options, cat) - if runJoinableTests or not isJoinableSpec(test.spec) or cat.string in specialCategories: - discard "run the test" - else: - test.spec.err = reJoined - testSpec r, test - inc testsRun - if testsRun == 0: - const whiteListedDirs = ["deps", "htmldocs", "pkgs"] - # `pkgs` because bug #16556 creates `pkgs` dirs and this can affect some users - # that try an old version of choosenim. - doAssert cat.string in whiteListedDirs, - "Invalid category specified: '$#' not in whilelist: $#" % [cat.string, $whiteListedDirs] + jsTests(r, cat, options) + of "dll": + dllTests(r, cat, options) + of "flags": + flagTests(r, cat, options) + of "gc": + gcTests(r, cat, options) + of "longgc": + longGCTests(r, cat, options) + of "debugger": + debuggerTests(r, cat, options) + of "manyloc": + manyLoc r, cat, options + of "threads": + threadTests r, cat, options & " --threads:on" + of "io": + ioTests r, cat, options + of "async": + asyncTests r, cat, options + of "lib": + testStdlib(r, "lib/pure/", options, cat) + testStdlib(r, "lib/packages/docutils/", options, cat) + of "examples": + compileExample(r, "examples/*.nim", options, cat) + compileExample(r, "examples/gtk/*.nim", options, cat) + compileExample(r, "examples/talk/*.nim", options, cat) + of "nimble-packages-1": + testNimblePackages(r, cat, options, ppOne) + of "nimble-packages-2": + testNimblePackages(r, cat, options, ppTwo) + of "niminaction": + testNimInAction(r, cat, options) + of "untestable": + # We can't test it because it depends on a third party. + discard # TODO: Move untestable tests to someplace else, i.e. nimble repo. + of "megatest": + runJoinedTest(r, cat, testsDir) + else: + var testsRun = 0 + var files: seq[string] + for file in walkDirRec(testsDir &.? cat.string): + if isTestFile(file): files.add file + files.sort # give reproducible order + + for i, name in files: + var test = makeTest(name, options, cat) + if runJoinableTests or not isJoinableSpec(test.spec) or cat.string in specialCategories: + discard "run the test" + else: + test.spec.err = reJoined + testSpec r, test + inc testsRun + if testsRun == 0: + const whiteListedDirs = ["deps", "htmldocs", "pkgs"] + # `pkgs` because bug #16556 creates `pkgs` dirs and this can affect some users + # that try an old version of choosenim. + doAssert cat.string in whiteListedDirs, + "Invalid category specified: '$#' not in whilelist: $#" % [cat.string, $whiteListedDirs] proc processPattern(r: var TResults, pattern, options: string; simulate: bool) = var testsRun = 0 diff --git a/testament/important_packages.nim b/testament/important_packages.nim index e59198e67..dca8c5a4f 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -1,174 +1,140 @@ -##[ -## note 1 -`useHead` should ideally be used as the default but lots of packages (e.g. `chronos`) -don't have release tags (or have really old ones compared to HEAD), making it -impossible to test them reliably here. +template pkg1(name: string; cmd = "nimble test"; url = "", useHead = true): untyped = + packages1.add((name, cmd, url, useHead)) -packages listed here should ideally have regularly updated release tags, so that: -* we're testing recent versions of the package -* the version that's tested is stable enough even if HEAD may occasionally break +template pkg2(name: string; cmd = "nimble test"; url = "", useHead = true): untyped = + packages2.add((name, cmd, url, useHead)) -## note 2: D20210308T165435:here -nimble packages should be testable as follows: -git clone $url $dir && cd $dir -NIMBLE_DIR=$TMP_NIMBLE_DIR XDG_CONFIG_HOME= nimble install --depsOnly -y -NIMBLE_DIR=$TMP_NIMBLE_DIR XDG_CONFIG_HOME= nimble test +var packages1*: seq[tuple[name, cmd: string; url: string, useHead: bool]] = @[] +var packages2*: seq[tuple[name, cmd: string; url: string, useHead: bool]] = @[] -if this fails (e.g. nimcrypto), it could be because a package lacks a `tests/nim.cfg` with `--path:..`, -so the above commands would've worked by accident with `nimble install` but not with `nimble install --depsOnly`. -When this is the case, a workaround is to test this package here by adding `--path:$srcDir` on the test `cmd`. -]## - -type NimblePackage* = object - name*, cmd*, url*: string - useHead*: bool - allowFailure*: bool - ## When true, we still run the test but the test is allowed to fail. - ## This is useful for packages that currently fail but that we still want to - ## run in CI, e.g. so that we can monitor when they start working again and - ## are reminded about those failures without making CI fail for unrelated PRs. - -var packages*: seq[NimblePackage] - -proc pkg(name: string; cmd = "nimble test"; url = "", useHead = true, allowFailure = false) = - packages.add NimblePackage(name: name, cmd: cmd, url: url, useHead: useHead, allowFailure: allowFailure) - -pkg "alea" -pkg "argparse" -pkg "arraymancer", "nim c tests/tests_cpu.nim" -pkg "ast_pattern_matching", "nim c -r tests/test1.nim" -pkg "asyncftpclient", "nimble compileExample" -pkg "asyncthreadpool", "nimble test --mm:refc" -pkg "awk" -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 -pkg "bncurve" -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", allowFailure = true -pkg "c2nim", "nim c testsuite/tester.nim" -pkg "cascade" -pkg "cello", url = "https://github.com/nim-lang/cello", useHead = true -pkg "chroma" -pkg "chronicles", "nim c -o:chr -r chronicles.nim", url = "https://github.com/nim-lang/nim-chronicles" -pkg "chronos", "nim c -r -d:release tests/testall" -pkg "cligen", "nim c --path:. -r cligen.nim" -pkg "combparser", "nimble test --gc:orc" -pkg "compactdict" -pkg "comprehension", "nimble test", "https://github.com/alehander92/comprehension" -pkg "criterion", allowFailure = true # pending https://github.com/disruptek/criterion/issues/3 (wrongly closed) -pkg "datamancer" -pkg "dashing", "nim c tests/functional.nim" -pkg "delaunay" -pkg "docopt" -pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" -pkg "elvis" -pkg "fidget" -pkg "fragments", "nim c -r fragments/dsl.nim", allowFailure = true # pending https://github.com/nim-lang/packages/issues/2115 -pkg "fusion" -pkg "gara" -pkg "glob" -pkg "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" -pkg "gittyup", "nimble test", "https://github.com/disruptek/gittyup", allowFailure = true -pkg "gnuplot", "nim c gnuplot.nim" -# pkg "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" +# packages A-M +# pkg1 "alea" +pkg1 "argparse" +pkg1 "arraymancer", "nim c tests/tests_cpu.nim" +# pkg1 "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim" +pkg1 "awk" +pkg1 "bigints", url = "https://github.com/Araq/nim-bigints" +pkg1 "binaryheap", "nim c -r binaryheap.nim" +pkg1 "BipBuffer" +# pkg1 "blscurve" # pending https://github.com/status-im/nim-blscurve/issues/39 +pkg1 "bncurve" +pkg1 "brainfuck", "nim c -d:release -r tests/compile.nim" +pkg1 "bump", "nim c --gc:arc -r tests/tbump.nim", "https://github.com/disruptek/bump" +pkg1 "c2nim", "nim c testsuite/tester.nim" +pkg1 "cascade" +pkg1 "cello" +pkg1 "chroma" +pkg1 "chronicles", "nim c -o:chr -r chronicles.nim" +when not defined(osx): # testdatagram.nim(560, 54): Check failed + pkg1 "chronos", "nim c -r -d:release tests/testall" +pkg1 "cligen", "nim c --path:. -r cligen.nim" +pkg1 "combparser", "nimble test --gc:orc" +pkg1 "compactdict" +pkg1 "comprehension", "nimble test", "https://github.com/alehander42/comprehension" +# pkg1 "criterion" # pending https://github.com/disruptek/criterion/issues/3 (wrongly closed) +pkg1 "dashing", "nim c tests/functional.nim" +pkg1 "delaunay" +pkg1 "docopt" +pkg1 "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" +pkg1 "elvis" +pkg1 "fidget" +pkg1 "fragments", "nim c -r fragments/dsl.nim" +pkg1 "gara" +pkg1 "glob" +pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" +# pkg1 "gittyup", "nimble test", "https://github.com/disruptek/gittyup" +pkg1 "gnuplot", "nim c gnuplot.nim" +# pkg1 "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" # pending https://github.com/nim-lang/Nim/issues/16509 -pkg "hts", "nim c -o:htss src/hts.nim" -pkg "httpauth" -pkg "illwill", "nimble examples" -pkg "inim" -pkg "itertools", "nim doc src/itertools.nim" -pkg "iterutils" -pkg "jstin" -pkg "karax", "nim c -r tests/tester.nim" -pkg "kdtree", "nimble test -d:nimLegacyRandomInitRand", "https://github.com/jblindsay/kdtree" -pkg "loopfusion", url = "https://github.com/nim-lang/loop-fusion" -pkg "lockfreequeues" -pkg "macroutils" -pkg "manu" -pkg "markdown" -pkg "measuremancer", "nimble testDeps; nimble -y test" -pkg "memo" -pkg "msgpack4nim", "nim c -r tests/test_spec.nim" -pkg "nake", "nim c nakefile.nim" -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 "nimfp", "nim c -o:nfp -r src/fp.nim" -pkg "nimgame2", "nim c --mm:refc nimgame2/nimgame.nim" +pkg1 "hts", "nim c -o:htss src/hts.nim" +# pkg1 "httpauth" +pkg1 "illwill", "nimble examples" +pkg1 "inim" +pkg1 "itertools", "nim doc src/itertools.nim" +pkg1 "iterutils" +pkg1 "jstin" +pkg1 "karax", "nim c -r tests/tester.nim" +pkg1 "kdtree", "nimble test", "https://github.com/jblindsay/kdtree" +pkg1 "loopfusion" +pkg1 "macroutils" +pkg1 "manu" +pkg1 "markdown" +pkg1 "memo" +pkg1 "msgpack4nim", "nim c -r tests/test_spec.nim" + +# these two are special snowflakes +pkg1 "nimcrypto", "nim c -r tests/testall.nim" +pkg1 "stint", "nim c -o:stintt -r stint.nim" + + +# packages N-Z +pkg2 "nake", "nim c nakefile.nim" +pkg2 "neo", "nim c -d:blas=openblas tests/all.nim" +# pkg2 "nesm", "nimble tests" # notice plural 'tests' +# pkg2 "nico" +pkg2 "nicy", "nim c -r src/nicy.nim" +pkg2 "nigui", "nim c -o:niguii -r src/nigui.nim" +pkg2 "NimData", "nim c -o:nimdataa src/nimdata.nim" +pkg2 "nimes", "nim c src/nimes.nim" +pkg2 "nimfp", "nim c -o:nfp -r src/fp.nim" +when false: + pkg2 "nimgame2", "nim c 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 "nimib" -pkg "nimlsp" -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 "nimPNG", useHead = true -pkg "nimpy", "nim c -r tests/nimfrompy.nim" -pkg "nimquery" -pkg "nimsl" -pkg "nimsvg" -pkg "nimterop", "nimble minitest", url = "https://github.com/nim-lang/nimterop" -pkg "nimwc", "nim c nimwc.nim", allowFailure = true -pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true -pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" -pkg "norm", "testament r tests/common/tmodel.nim" -pkg "npeg", "nimble testarc" -pkg "numericalnim", "nimble nimCI" -pkg "optionsutils" -pkg "ormin", "nim c -o:orminn ormin.nim" -pkg "parsetoml" -pkg "patty" -pkg "pixie" -pkg "plotly", "nim c examples/all.nim" -pkg "pnm" -pkg "polypbren" -pkg "prologue", "nimble tcompile" -pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim" -pkg "pylib" -pkg "rbtree" -pkg "react", "nimble example" -pkg "regex", "nim c src/regex", url = "https://github.com/nim-lang/nim-regex" -pkg "result", "nim c -r result.nim" -pkg "RollingHash", "nim c -r tests/test_cyclichash.nim" -pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim" -pkg "sdl1", "nim c -r src/sdl.nim" -pkg "sdl2_nim", "nim c -r sdl2/sdl.nim" -pkg "sigv4", "nim c --gc:arc -r sigv4.nim", "https://github.com/disruptek/sigv4" -pkg "sim" -pkg "smtp", "nimble compileExample" -pkg "snip", "nimble test", "https://github.com/genotrance/snip" -pkg "stew" -pkg "stint", "nim r stint.nim" -pkg "strslice" -pkg "strunicode", "nim c -r --mm:refc src/strunicode.nim" -pkg "supersnappy" -pkg "synthesis" -pkg "taskpools" -pkg "telebot", "nim c -o:tbot -r src/telebot.nim" -pkg "tempdir" -pkg "templates" -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" -pkg "timezones" -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", useHead = true -pkg "websocket", "nim c websocket.nim" -pkg "winim", "nim c winim.nim" -pkg "with" -pkg "ws", allowFailure = true -pkg "yaml", "nim c -r test/tserialization.nim" -pkg "zero_functional", "nim c -r -d:nimNoLentIterators test.nim" -pkg "zippy" +pkg2 "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" +pkg2 "nimlsp" +pkg2 "nimly", "nim c -r tests/test_readme_example.nim" +# pkg2 "nimongo", "nimble test_ci" +# pkg2 "nimph", "nimble test", "https://github.com/disruptek/nimph" +pkg2 "nimpy", "nim c -r tests/nimfrompy.nim" +pkg2 "nimquery" +pkg2 "nimsl" +pkg2 "nimsvg" +pkg2 "nimterop", "nimble minitest" +pkg2 "nimwc", "nim c nimwc.nim" +# pkg2 "nimx", "nim c --threads:on test/main.nim" +# pkg2 "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" +pkg2 "norm", "nim c -r tests/sqlite/trows.nim" +pkg2 "npeg", "nimble testarc" +pkg2 "numericalnim", "nim c -r tests/test_integrate.nim" +pkg2 "optionsutils" +pkg2 "ormin", "nim c -o:orminn ormin.nim" +pkg2 "parsetoml" +pkg2 "patty" +pkg2 "plotly", "nim c examples/all.nim" +pkg2 "pnm" +pkg2 "polypbren" +pkg2 "prologue", "nimble tcompile" +pkg2 "protobuf", "nim c -o:protobuff -r src/protobuf.nim" +pkg2 "pylib" +pkg2 "rbtree" +pkg2 "react", "nimble example" +pkg2 "regex", "nim c src/regex" +pkg2 "result", "nim c -r result.nim" +pkg2 "RollingHash", "nim c -r tests/test_cyclichash.nim" +pkg2 "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim" +pkg2 "sdl1", "nim c -r src/sdl.nim" +pkg2 "sdl2_nim", "nim c -r sdl2/sdl.nim" +pkg2 "sigv4", "nim c --gc:arc -r sigv4.nim", "https://github.com/disruptek/sigv4" +pkg2 "snip", "nimble test", "https://github.com/genotrance/snip" +pkg2 "strslice" +pkg2 "strunicode", "nim c -r src/strunicode.nim" +pkg2 "synthesis" +pkg2 "telebot", "nim c -o:tbot -r src/telebot.nim" +pkg2 "tempdir" +pkg2 "templates" +pkg2 "tensordsl", "nim c -r tests/tests.nim", "https://krux02@bitbucket.org/krux02/tensordslnim.git" +pkg2 "terminaltables", "nim c src/terminaltables.nim" +pkg2 "termstyle", "nim c -r termstyle.nim" +pkg2 "timeit" +pkg2 "timezones" +pkg2 "tiny_sqlite" +pkg2 "unicodedb", "nim c -d:release -r tests/tests.nim" +pkg2 "unicodeplus", "nim c -d:release -r tests/tests.nim" +pkg2 "unpack" +pkg2 "websocket", "nim c websocket.nim" +# pkg2 "winim" +pkg2 "with" +pkg2 "ws" +pkg2 "yaml", "nim build" +pkg2 "zero_functional", "nim c -r -d:nimWorkaround14447 test.nim" +pkg2 "zippy" diff --git a/testament/lib/stdtest/netutils.nim b/testament/lib/stdtest/netutils.nim index 5115390e0..eb913a56a 100644 --- a/testament/lib/stdtest/netutils.nim +++ b/testament/lib/stdtest/netutils.nim @@ -1,7 +1,6 @@ import std/[nativesockets, asyncdispatch, os] proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port = - ## See also `asynchttpserver.getPort`. block: var name: Sockaddr_in name.sin_family = typeof(name.sin_family)(toInt(AF_INET)) @@ -9,5 +8,5 @@ proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port = name.sin_addr.s_addr = htonl(INADDR_ANY) if bindAddr(handle, cast[ptr SockAddr](addr(name)), sizeof(name).Socklen) < 0'i32: - raiseOSError(osLastError(), $port) + raiseOSError(osLastError()) result = getLocalAddr(handle, AF_INET)[1] diff --git a/testament/lib/stdtest/specialpaths.nim b/testament/lib/stdtest/specialpaths.nim index 7df63666f..42f656d76 100644 --- a/testament/lib/stdtest/specialpaths.nim +++ b/testament/lib/stdtest/specialpaths.nim @@ -1,6 +1,6 @@ #[ todo: move findNimStdLibCompileTime, findNimStdLib here -xxx: factor pending https://github.com/timotheecour/Nim/issues/616 +xxx: consider moving this to $nim/compiler/relpaths.nim to get relocatable paths ## note: $lib vs $nim note: these can resolve to 3 different paths if running via `nim c --lib:lib foo`, @@ -13,8 +13,6 @@ 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. @@ -26,30 +24,13 @@ const # robust way to derive other paths here # We don't depend on PATH so this is robust to having multiple nim binaries nimRootDir* = sourcePath.parentDir.parentDir.parentDir.parentDir ## root of Nim repo - testsFname* = "tests" stdlibDir* = nimRootDir / "lib" systemPath* = stdlibDir / "system.nim" - testsDir* = nimRootDir / testsFname + testsDir* = nimRootDir / "tests" buildDir* = nimRootDir / "build" ## refs #10268: all testament generated files should go here to avoid ## polluting .gitignore -proc splitTestFile*(file: string): tuple[cat: string, path: string] = - ## At least one directory is required in the path, to use as a category name - runnableExamples: - doAssert splitTestFile("tests/fakedir/tfakename.nim") == ("fakedir", "tests/fakedir/tfakename.nim".unixToNativePath) - for p in file.parentDirs(inclusive = false): - let parent = p.parentDir - if parent.lastPathPart == testsFname: - result.cat = p.lastPathPart - let dir = getCurrentDir() - if file.isRelativeTo(dir): - result.path = file.relativePath(dir) - else: - result.path = file - return result - doAssert false, "file must match this pattern: '/pathto/tests/dir/**/tfile.nim', got: '" & file & "'" - static: # sanity check doAssert fileExists(systemPath) diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index 12e6a56ab..34aa3b751 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -1,9 +1,5 @@ import std/private/miscdollars -when defined(nimscript): - import std/os # xxx investigate why needed -else: - from std/os import getEnv -import std/[macros, genasts] +import std/strutils template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = ## API to deal with flaky or failing tests. This avoids disabling entire tests @@ -29,93 +25,12 @@ template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = msg2.add $expr & " " & msg echo msg2 -when not defined(js) and not defined(nimscript): - import std/strutils - - proc greedyOrderedSubsetLines*(lhs, rhs: string, allowPrefixMatch = false): bool = - ## Returns true if each stripped line in `lhs` appears in rhs, using a greedy matching. - # xxx improve error reporting by showing the last matched pair - iterator splitLinesClosure(): string {.closure.} = - for line in splitLines(rhs.strip): - yield line - template isMatch(lhsi, rhsi): bool = - if allowPrefixMatch: - startsWith(rhsi, lhsi): - else: - lhsi == rhsi - - var rhsIter = splitLinesClosure - var currentLine = strip(rhsIter()) - - for line in lhs.strip.splitLines: - let line = line.strip - if line.len != 0: - while not isMatch(line, currentLine): - currentLine = strip(rhsIter()) - if rhsIter.finished: - return false - - if rhsIter.finished: - return false - return true - -template enableRemoteNetworking*: bool = - ## Allows contolling whether to run some test at a statement-level granularity. - ## Using environment variables simplifies propagating this all the way across - ## process calls, e.g. `testament all` calls itself, which in turns invokes - ## a `nim` invocation (possibly via additional intermediate processes). - getEnv("NIM_TESTAMENT_REMOTE_NETWORKING") == "1" - -template whenRuntimeJs*(bodyIf, bodyElse) = - ##[ - Behaves as `when defined(js) and not nimvm` (which isn't legal yet). - pending improvements to `nimvm`, this sugar helps; use as follows: - - whenRuntimeJs: - doAssert defined(js) - when nimvm: doAssert false - else: discard - do: - discard - ]## - when nimvm: bodyElse - else: - when defined(js): bodyIf - else: bodyElse - -template whenVMorJs*(bodyIf, bodyElse) = - ## Behaves as: `when defined(js) or nimvm` - when nimvm: bodyIf - else: - when defined(js): bodyIf - else: bodyElse - -template accept*(a) = - doAssert compiles(a) - -template reject*(a) = - doAssert not compiles(a) - -template disableVm*(body) = - when nimvm: discard - else: body - -macro assertAll*(body) = - ## works in VM, unlike `check`, `require` - runnableExamples: - assertAll: - 1+1 == 2 - var a = @[1, 2] # statements work - a.len == 2 - # remove this once these support VM, pending #10129 (closed but not yet fixed) - result = newStmtList() - for a in body: - result.add genAst(a, a2 = a.repr, info = lineInfo(a)) do: - # D20210421T014713:here - # xxx pending https://github.com/nim-lang/Nim/issues/12030, - # `typeof` should introduce its own scope, so that this - # is sufficient: `typeof(a)` instead of `typeof(block: a)` - when typeof(block: a) is void: a - else: - if not a: - raise newException(AssertionDefect, info & " " & a2) +proc greedyOrderedSubsetLines*(lhs, rhs: string): bool = + ## returns true if each stripped line in `lhs` appears in rhs, using a greedy matching. + let rhs = rhs.strip + var currentPos = 0 + for line in lhs.strip.splitLines: + currentPos = rhs.find(line.strip, currentPos) + if currentPos < 0: + return false + return true diff --git a/testament/lib/stdtest/unittest_light.nim b/testament/lib/stdtest/unittest_light.nim index 4ab1d7543..bf254c11f 100644 --- a/testament/lib/stdtest/unittest_light.nim +++ b/testament/lib/stdtest/unittest_light.nim @@ -1,6 +1,3 @@ -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 @@ -14,24 +11,26 @@ proc mismatch*[T](lhs: T, rhs: T): string = proc quoted(s: string): string = result.addQuoted s - result.add '\n' + result.add "\n" result.add "lhs:{" & replaceInvisible( $lhs) & "}\nrhs:{" & replaceInvisible($rhs) & "}\n" when compiles(lhs.len): if lhs.len != rhs.len: - result.add "lhs.len: " & $lhs.len & " rhs.len: " & $rhs.len & '\n' + result.add "lhs.len: " & $lhs.len & " rhs.len: " & $rhs.len & "\n" when compiles(lhs[0]): var i = 0 while i < lhs.len and i < rhs.len: if lhs[i] != rhs[i]: break i.inc - result.add "first mismatch index: " & $i & '\n' + result.add "first mismatch index: " & $i & "\n" if i < lhs.len and i < rhs.len: result.add "lhs[i]: {" & quoted($lhs[i]) & "}\nrhs[i]: {" & quoted( $rhs[i]) & "}\n" result.add "lhs[0.. 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() - parseCaret() + if result < s.len and s[result] == '^': + caret = (line-1, col) + inc result + inc col + skipWhitespace() + var msg = "" while result < s.len-1: if s[result] == '\n': - msg.add '\n' inc result inc line col = 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 + while msg.len > 0 and msg[^1] in Whitespace: + setLen msg, msg.len - 1 + + inc result inc col, 2 - addInlineError() + if kind == "Error": spec.action = actionReject + spec.unjoinable = true + spec.inlineErrors.add InlineError(kind: kind, msg: msg, line: caret[0], col: caret[1]) 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 = "\"\"\"" - specStart = "discard " & tripleQuote - var s = readFile(filename) + var s = readFile(filename).string var i = 0 var a = -1 @@ -264,36 +188,30 @@ proc extractSpec(filename: string; spec: var TSpec): string = var line = 1 var col = 1 while i < s.len: - if (i == 0 or s[i-1] != ' ') and s.continuesWith(specStart, i): - # `s[i-1] == '\n'` would not work because of `tests/stdlib/tbase64.nim` which contains BOM (https://en.wikipedia.org/wiki/Byte_order_mark) - const lineMax = 10 - if a != -1: - raise newException(ValueError, "testament spec violation: duplicate `specStart` found: " & $(filename, a, b, line)) - elif line > lineMax: - # not overly restrictive, but prevents mistaking some `specStart` as spec if deeep inside a test file - raise newException(ValueError, "testament spec violation: `specStart` should be before line $1, or be indented; info: $2" % [$lineMax, $(filename, a, b, line)]) - i += specStart.len - a = i - elif a > -1 and b == -1 and s.continuesWith(tripleQuote, i): - b = i - i += tripleQuote.len + if s.continuesWith(tripleQuote, i): + if a < 0: a = i + elif b < 0: b = i + inc i, 2 + inc col elif s[i] == '\n': inc line - inc i col = 1 elif s.continuesWith(inlineErrorMarker, i): i = extractErrorMsg(s, i, line, col, spec) else: inc col - inc i + inc i - if a >= 0 and b > a: - result = s.substr(a, b-1).multiReplace({"'''": tripleQuote, "\\31": "\31"}) - elif a >= 0: - raise newException(ValueError, "testament spec violation: `specStart` found but not trailing `tripleQuote`: $1" % $(filename, a, b, line)) + # look for """ only in the first section + if a >= 0 and b > a and a < 40: + result = s.substr(a+3, b-1).replace("'''", tripleQuote) else: + #echo "warning: file does not contain spec: " & filename result = "" +when not defined(nimhygiene): + {.pragma: inject.} + proc parseTargets*(value: string): set[TTarget] = for v in value.normalize.splitWhitespace: case v @@ -303,10 +221,19 @@ 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 -proc isCurrentBatch*(testamentData: TestamentData; filename: string): bool = +proc isCurrentBatch(testamentData: TestamentData, filename: string): bool = if testamentData.testamentNumBatch != 0: hash(filename) mod testamentData.testamentNumBatch == testamentData.testamentBatch else: @@ -314,13 +241,11 @@ 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 open(p, ss, filename, 1) var flags: HashSet[string] - var nimoutFound = false while true: var e = next(p) case e.kind @@ -355,6 +280,12 @@ proc parseSpec*(filename: string): TSpec = if result.msg.len == 0 and result.nimout.len == 0: result.parseErrors.addLine "errormsg or msg needs to be specified before column" discard parseInt(e.value, result.column) + of "tfile": + result.tfile = e.value + of "tline": + discard parseInt(e.value, result.tline) + of "tcolumn": + discard parseInt(e.value, result.tcolumn) of "output": if result.outputCheck != ocSubstr: result.outputCheck = ocEqual @@ -366,20 +297,21 @@ proc parseSpec*(filename: string): TSpec = result.output = strip(e.value) of "sortoutput": try: - result.sortoutput = parseCfgBool(e.value) + result.sortoutput = parseCfgBool(e.value) except: result.parseErrors.addLine getCurrentExceptionMsg() of "exitcode": discard parseInt(e.value, result.exitCode) result.action = actionRun + of "msg": + result.msg = e.value + if result.action != actionRun: + result.action = actionCompile of "errormsg": result.msg = e.value result.action = actionReject of "nimout": result.nimout = e.value - nimoutFound = true - of "nimoutfull": - result.nimoutFull = parseCfgBool(e.value) of "batchable": result.unbatchable = not parseCfgBool(e.value) of "joinable": @@ -396,71 +328,42 @@ proc parseSpec*(filename: string): TSpec = # Valgrind only supports OSX <= 17.x result.useValgrind = disabled of "disabled": - let value = e.value.normalize - case value + case e.value.normalize of "y", "yes", "true", "1", "on": result.err = reDisabled of "n", "no", "false", "0", "off": discard - # These values are defined in `compiler/options.isDefined` - of "win": + of "win", "windows": when defined(windows): result.err = reDisabled of "linux": when defined(linux): result.err = reDisabled of "bsd": when defined(bsd): result.err = reDisabled - of "osx": - when defined(osx): result.err = reDisabled - of "unix", "posix": + of "macosx": + when defined(macosx): result.err = reDisabled + of "unix": + when defined(unix): result.err = reDisabled + of "posix": when defined(posix): result.err = reDisabled - of "freebsd": - when defined(freebsd): result.err = reDisabled - of "littleendian": - when defined(littleendian): result.err = reDisabled - of "bigendian": - when defined(bigendian): result.err = reDisabled - of "cpu8", "8bit": - when defined(cpu8): result.err = reDisabled - of "cpu16", "16bit": - when defined(cpu16): result.err = reDisabled - of "cpu32", "32bit": - when defined(cpu32): result.err = reDisabled - of "cpu64", "64bit": - when defined(cpu64): result.err = reDisabled - # These values are for CI environments - of "travis": # deprecated + of "travis": if isTravis: result.err = reDisabled - of "appveyor": # deprecated + of "appveyor": if isAppVeyor: result.err = reDisabled of "azure": if isAzure: result.err = reDisabled + of "32bit": + if sizeof(int) == 4: + result.err = reDisabled + of "freebsd": + when defined(freebsd): result.err = reDisabled + of "arm64": + when defined(arm64): result.err = reDisabled + of "i386": + when defined(i386): result.err = reDisabled + of "openbsd": + when defined(openbsd): result.err = reDisabled + of "netbsd": + when defined(netbsd): result.err = reDisabled else: - # Check whether the value exists as an OS or CPU that is - # defined in `compiler/platform`. - block checkHost: - for os in platform.OS: - # Check if the value exists as OS. - if value == os.name.normalize: - # The value exists; is it the same as the current host? - if value == hostOS.normalize: - # The value exists and is the same as the current host, - # so disable the test. - result.err = reDisabled - # The value was defined, so there is no need to check further - # values or raise an error. - break checkHost - for cpu in platform.CPU: - # Check if the value exists as CPU. - if value == cpu.name.normalize: - # The value exists; is it the same as the current host? - if value == hostCPU.normalize: - # The value exists and is the same as the current host, - # so disable the test. - result.err = reDisabled - # The value was defined, so there is no need to check further - # values or raise an error. - break checkHost - # The value doesn't exist as an OS, CPU, or any previous value - # defined in this case statement, so raise an error. - result.parseErrors.addLine "cannot interpret as a bool: ", e.value + result.parseErrors.addLine "cannot interpret as a bool: ", e.value of "cmd": if e.value.startsWith("nim "): result.cmd = compilerPrefix & e.value[3..^1] @@ -499,21 +402,6 @@ proc parseSpec*(filename: string): TSpec = if skips.anyIt(it in result.file): result.err = reDisabled - if nimoutFound and result.nimout.len == 0 and not result.nimoutFull: - result.parseErrors.addLine "empty `nimout` is vacuously true, use `nimoutFull:true` if intentional" - 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 296c313aa..5686da81c 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -10,94 +10,74 @@ ## This program verifies Nim against the testcases. import - strutils, pegs, os, osproc, streams, json, std/exitprocs, + strutils, pegs, os, osproc, streams, json, backend, parseopt, specs, htmlgen, browsers, terminal, - algorithm, times, md5, azure, intsets, macros + algorithm, times, md5, sequtils, azure, intsets from std/sugar import dup import compiler/nodejs import lib/stdtest/testutils -from lib/stdtest/specialpaths import splitTestFile -from std/private/gitutils import diffStrings - -proc trimUnitSep(x: var string) = - let L = x.len - if L > 0 and x[^1] == '\31': - setLen x, L-1 var useColors = true var backendLogging = true var simulate = false -var optVerbose = false -var useMegatest = true -var valgrindEnabled = true - -proc verboseCmd(cmd: string) = - if optVerbose: - echo "executing: ", cmd const failString* = "FAIL: " # ensures all failures can be searched with 1 keyword in CI logs testsDir = "tests" & DirSep resultsFile = "testresults.html" + #jsonFile = "testresults.json" # not used Usage = """Usage: testament [options] command [arguments] Command: p|pat|pattern run all the tests matching the given pattern - all run all tests in category folders + all run all tests c|cat|category run all the tests of a certain category r|run run single test file html generate $1 from the database + stats generate statistics about test cases Arguments: arguments are passed to the compiler Options: - --print print results to the console - --verbose print commands (compiling and running tests) + --print also print results to the console --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: c) + --targets:"c cpp js objc" run tests for specified targets (default: all) --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. --megatest:on|off Enable or disable megatest. Default is on. - --valgrind:on|off Enable or disable valgrind support. Default is on. --skipFrom:file Read tests to skip from `file` - one test per line, # comments ignored On Azure Pipelines, testament will also publish test results via Azure Pipelines' Test Management API provided that System.AccessToken is made available via the environment variable SYSTEM_ACCESSTOKEN. - -Experimental: using environment variable `NIM_TESTAMENT_REMOTE_NETWORKING=1` enables -tests with remote networking (as in CI). """ % resultsFile -proc isNimRepoTests(): bool = - # this logic could either be specific to cwd, or to some file derived from - # the input file, eg testament r /pathto/tests/foo/tmain.nim; we choose - # the former since it's simpler and also works with `testament all`. - let file = "testament"/"testament.nim.cfg" - result = file.fileExists - type Category = distinct string TResults = object - total, passed, failedButAllowed, skipped: int - ## xxx rename passed to passedOrAllowedFailure + total, passed, skipped: int data: string TTest = object name: string cat: Category options: string - testArgs: seq[string] + args: seq[string] spec: TSpec startTime: float - debugInfo: string # ---------------------------------------------------------------------------- let pegLineError = peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' ('Error') ':' \s* {.*}" + + pegLineTemplate = + peg""" + {[^(]*} '(' {\d+} ', ' {\d+} ') ' + 'template/generic instantiation' ( ' of `' [^`]+ '`' )? ' from here' .* + """ pegOtherError = peg"'Error:' \s* {.*}" pegOfInterest = pegLineError / pegOtherError @@ -106,13 +86,15 @@ var targetsSet = false proc isSuccess(input: string): bool = # not clear how to do the equivalent of pkg/regex's: re"FOO(.*?)BAR" in pegs - # note: this doesn't handle colors, eg: `\e[1m\e[0m\e[32mHint:`; while we - # could handle colors, there would be other issues such as handling other flags - # that may appear in user config (eg: `--filenames`). - # Passing `XDG_CONFIG_HOME= testament args...` can be used to ignore user config - # stored in XDG_CONFIG_HOME, refs https://wiki.archlinux.org/index.php/XDG_Base_Directory input.startsWith("Hint: ") and input.endsWith("[SuccessX]") +when false: # deadcode + proc normalizeMsg(s: string): string = + result = newStringOfCap(s.len+1) + for x in splitLines(s): + if result.len > 0: result.add '\L' + result.add x.strip + proc getFileDir(filename: string): string = result = filename.splitFile().dir if not result.isAbsolute(): @@ -120,7 +102,7 @@ proc getFileDir(filename: string): string = proc execCmdEx2(command: string, args: openArray[string]; workingDir, input: string = ""): tuple[ cmdLine: string, - output: string, + output: TaintedString, exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = @@ -128,9 +110,8 @@ proc execCmdEx2(command: string, args: openArray[string]; workingDir, input: str for arg in args: result.cmdLine.add ' ' result.cmdLine.add quoteShell(arg) - verboseCmd(result.cmdLine) - var p = startProcess(command, workingDir = workingDir, args = args, - options = {poStdErrToStdOut, poUsePath}) + var p = startProcess(command, workingDir=workingDir, args=args, + options={poStdErrToStdOut, poUsePath}) var outp = outputStream(p) # There is no way to provide input for the child process @@ -140,12 +121,12 @@ proc execCmdEx2(command: string, args: openArray[string]; workingDir, input: str instream.write(input) close instream - result.exitCode = -1 - var line = newStringOfCap(120) + result.exitCode = -1 + var line = newStringOfCap(120).TaintedString while true: if outp.readLine(line): - result.output.add line - result.output.add '\n' + result.output.string.add(line.string) + result.output.string.add("\n") else: result.exitCode = peekExitCode(p) if result.exitCode != -1: break @@ -156,39 +137,40 @@ proc nimcacheDir(filename, options: string, target: TTarget): string = let hashInput = options & $target result = "nimcache" / (filename & '_' & hashInput.getMD5) -proc prepareTestCmd(cmdTemplate, filename, options, nimcache: string, - target: TTarget, extraOptions = ""): string = - var options = target.defaultOptions & ' ' & options - if nimcache.len > 0: options.add(" --nimCache:$#" % nimcache.quoteShell) - options.add ' ' & extraOptions - # we avoid using `parseCmdLine` which is buggy, refs bug #14343 - result = cmdTemplate % ["target", targetToCmd[target], +proc prepareTestArgs(cmdTemplate, filename, options, nimcache: string, + target: TTarget, extraOptions = ""): seq[string] = + var options = target.defaultOptions & " " & options + # improve pending https://github.com/nim-lang/Nim/issues/14343 + if nimcache.len > 0: options.add " " & ("--nimCache:" & nimcache).quoteShell + options.add " " & extraOptions + result = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], "options", options, "file", filename.quoteShell, - "filedir", filename.getFileDir(), "nim", compilerPrefix] + "filedir", filename.getFileDir(), "nim", compilerPrefix]) -proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, - target: TTarget, extraOptions = ""): TSpec = - result.cmd = prepareTestCmd(cmdTemplate, filename, options, nimcache, target, +proc callCompiler(cmdTemplate, filename, options, nimcache: string, + target: TTarget, extraOptions = ""): TSpec = + let c = prepareTestArgs(cmdTemplate, filename, options, nimcache, target, extraOptions) - verboseCmd(result.cmd) - var p = startProcess(command = result.cmd, - options = {poStdErrToStdOut, poUsePath, poEvalCommand}) + result.cmd = quoteShellCommand(c) + var p = startProcess(command=c[0], args=c[1 .. ^1], + options={poStdErrToStdOut, poUsePath}) let outp = p.outputStream - var foundSuccessMsg = false - var foundErrorMsg = false + var suc = "" var err = "" + var tmpl = "" var x = newStringOfCap(120) result.nimout = "" while true: - if outp.readLine(x): - trimUnitSep x - result.nimout.add(x & '\n') + if outp.readLine(x.TaintedString): + result.nimout.add(x & "\n") if x =~ pegOfInterest: - # `err` should contain the last error message + # `err` should contain the last error/warning message err = x - foundErrorMsg = true + elif x =~ pegLineTemplate and err == "": + # `tmpl` contains the last template expansion before the error + tmpl = x elif x.isSuccess: - foundSuccessMsg = true + suc = x elif not running(p): break close(p) @@ -197,23 +179,14 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, result.output = "" result.line = 0 result.column = 0 - + result.tfile = "" + result.tline = 0 + result.tcolumn = 0 result.err = reNimcCrash - let exitCode = p.peekExitCode - case exitCode - of 0: - if foundErrorMsg: - result.debugInfo.add " compiler exit code was 0 but some Error's were found." - else: - result.err = reSuccess - of 1: - if not foundErrorMsg: - result.debugInfo.add " compiler exit code was 1 but no Error's were found." - if foundSuccessMsg: - result.debugInfo.add " compiler exit code was 1 but no `isSuccess` was true." - else: - result.debugInfo.add " expected compiler exit code 0 or 1, got $1." % $exitCode - + if tmpl =~ pegLineTemplate: + result.tfile = extractFilename(matches[0]) + result.tline = parseInt(matches[1]) + result.tcolumn = parseInt(matches[2]) if err =~ pegLineError: result.file = extractFilename(matches[0]) result.line = parseInt(matches[1]) @@ -221,15 +194,38 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, result.msg = matches[3] elif err =~ pegOtherError: result.msg = matches[0] - trimUnitSep result.msg + elif suc.isSuccess: + result.err = reSuccess + +proc callCCompiler(cmdTemplate, filename, options: string, + target: TTarget): TSpec = + let c = prepareTestArgs(cmdTemplate, filename, options, nimcache = "", target) + var p = startProcess(command="gcc", args=c[5 .. ^1], + options={poStdErrToStdOut, poUsePath}) + let outp = p.outputStream + var x = newStringOfCap(120) + result.nimout = "" + result.msg = "" + result.file = "" + result.output = "" + result.line = -1 + while true: + if outp.readLine(x.TaintedString): + result.nimout.add(x & "\n") + elif not running(p): + break + close(p) + if p.peekExitCode == 0: + result.err = reSuccess proc initResults: TResults = result.total = 0 result.passed = 0 - result.failedButAllowed = 0 result.skipped = 0 result.data = "" +import macros + macro ignoreStyleEcho(args: varargs[typed]): untyped = let typForegroundColor = bindSym"ForegroundColor".getType let typBackgroundColor = bindSym"BackgroundColor".getType @@ -254,29 +250,18 @@ template maybeStyledEcho(args: varargs[untyped]): untyped = proc `$`(x: TResults): string = - result = """ -Tests passed or allowed to fail: $2 / $1
      -Tests failed and allowed to fail: $3 / $1
      -Tests skipped: $4 / $1
      -""" % [$x.total, $x.passed, $x.failedButAllowed, $x.skipped] - -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() + result = ("Tests passed: $1 / $3
      \n" & + "Tests skipped: $2 / $3
      \n") % + [$x.passed, $x.skipped, $x.total] 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. + expected, given: string, successOrig: TResultEnum) = # 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) + var name = test.name.replace(DirSep, '/') + name.add " " & $target + if test.options.len > 0: name.add " " & test.options + let duration = epochTime() - test.startTime let success = if test.spec.timeout > 0.0 and duration > test.spec.timeout: reTimeout else: successOrig @@ -291,22 +276,18 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, expected = expected, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) - template dispNonSkipped(color, outcome) = - maybeStyledEcho color, outcome, fgCyan, test.debugInfo, alignLeft(name, 60), fgBlue, " (", durationStr, " sec)" template disp(msg) = - maybeStyledEcho styleDim, fgYellow, msg & ' ', styleBright, fgCyan, name + maybeStyledEcho styleDim, fgYellow, msg & " ", styleBright, fgCyan, name if success == reSuccess: - dispNonSkipped(fgGreen, "PASS: ") + maybeStyledEcho fgGreen, "PASS: ", fgCyan, alignLeft(name, 60), fgBlue, " (", durationStr, " sec)" elif success == reDisabled: if test.spec.inCurrentBatch: disp("SKIP:") else: disp("NOTINBATCH:") elif success == reJoined: disp("JOINED:") else: - dispNonSkipped(fgRed, failString) + maybeStyledEcho styleBright, fgRed, failString, fgCyan, name maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\"" maybeStyledEcho styleBright, fgRed, "Failure: ", $success - if givenSpec != nil and givenSpec.debugInfo.len > 0: - echo "debugInfo: " & givenSpec.debugInfo if success in {reBuildFailed, reNimcCrash, reInstallFailed}: # expected is empty, no reason to print it. echo given @@ -315,7 +296,7 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, maybeStyledEcho styleBright, expected, "\n" maybeStyledEcho fgYellow, "Gotten:" maybeStyledEcho styleBright, given, "\n" - echo diffStrings(expected, given).output + if backendLogging and (isAppVeyor or isAzure): let (outcome, msg) = @@ -325,66 +306,86 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, of reDisabled, reJoined: ("Skipped", "") of reBuildFailed, reNimcCrash, reInstallFailed: - ("Failed", "Failure: " & $success & '\n' & given) + ("Failed", "Failure: " & $success & "\n" & given) else: ("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given) if isAzure: azure.addTestResult(name, test.cat.string, int(duration * 1000), msg, success) else: - var p = startProcess("appveyor", args = ["AddTest", test.name.replace("\\", "/") & test.options, + var p = startProcess("appveyor", args=["AddTest", test.name.replace("\\", "/") & test.options, "-Framework", "nim-testament", "-FileName", test.cat.string, "-Outcome", outcome, "-ErrorMessage", msg, - "-Duration", $(duration * 1000).int], - options = {poStdErrToStdOut, poUsePath, poParentStreams}) + "-Duration", $(duration*1000).int], + options={poStdErrToStdOut, poUsePath, poParentStreams}) discard waitForExit(p) close(p) -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 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): -proc inlineErrorsMsgs(expected: TSpec): string = - for inlineError in expected.inlineErrors.items: - result.addLine inlineError.toString(expected.filename) + 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] -proc checkForInlineErrors(expected, given: TSpec): bool = - for inlineError in expected.inlineErrors: - if inlineError.toString(expected.filename) notin given.nimout: - return false - true + 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 -proc nimoutCheck(expected, given: TSpec): bool = - result = true - if expected.nimoutFull: - if expected.nimout != given.nimout: - result = false - elif expected.nimout.len > 0 and not greedyOrderedSubsetLines(expected.nimout, given.nimout): - result = false + 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 -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) + r.addResult(test, target, e, given.nimout, reMsgsDiffer) + break coverCheck + + r.addResult(test, target, "", given.msg, reSuccess) + inc(r.passed) + +proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = + if expected.inlineErrors.len > 0: + checkForInlineErrors(r, expected, given, test, target) elif strip(expected.msg) notin strip(given.msg): - r.addResult(test, target, extraOptions, expected.msg, given.msg, reMsgsDiffer) - elif not nimoutCheck(expected, given): - r.addResult(test, target, extraOptions, expected.nimout, given.nimout, reMsgsDiffer) - elif extractFilename(expected.file) != extractFilename(given.file) and + r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer) + elif expected.nimout.len > 0 and not greedyOrderedSubsetLines(expected.nimout, given.nimout): + r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer) + elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and "internal error:" notin expected.msg: - r.addResult(test, target, extraOptions, expected.filename, given.file, reFilesDiffer) + r.addResult(test, target, expected.file, 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, extraOptions, $expected.line & ':' & $expected.column, - $given.line & ':' & $given.column, reLinesDiffer) + r.addResult(test, target, $expected.line & ':' & $expected.column, + $given.line & ':' & $given.column, + reLinesDiffer) + elif expected.tfile != "" and extractFilename(expected.tfile) != extractFilename(given.tfile) and + "internal error:" notin expected.msg: + r.addResult(test, target, expected.tfile, given.tfile, reFilesDiffer) + elif expected.tline != given.tline and expected.tline != 0 or + expected.tcolumn != given.tcolumn and expected.tcolumn != 0: + r.addResult(test, target, $expected.tline & ':' & $expected.tcolumn, + $given.tline & ':' & $given.tcolumn, + reLinesDiffer) else: - r.addResult(test, target, extraOptions, expected.msg, given.msg, reSuccess) + r.addResult(test, target, expected.msg, given.msg, reSuccess) inc(r.passed) proc generatedFile(test: TTest, target: TTarget): string = @@ -402,7 +403,7 @@ proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var st given: var TSpec) = try: let genFile = generatedFile(test, target) - let contents = readFile(genFile) + let contents = readFile(genFile).string for check in spec.ccodeCheck: if check.len > 0 and check[0] == '\\': # little hack to get 'match' support: @@ -422,30 +423,43 @@ proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var st given.err = reCodeNotFound echo getCurrentExceptionMsg() -proc compilerOutputTests(test: TTest, target: TTarget, extraOptions: string, - given: var TSpec, expected: TSpec; r: var TResults) = +proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) = + if not greedyOrderedSubsetLines(expectedNimout, given.nimout): + given.err = reMsgsDiffer + +proc compilerOutputTests(test: TTest, target: TTarget, 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) or - not checkForInlineErrors(expected, given): - given.err = reMsgsDiffer - expectedmsg = expected.nimout & inlineErrorsMsgs(expected) + if expected.nimout.len > 0: + expectedmsg = expected.nimout givenmsg = given.nimout.strip + nimoutCheck(test, expectedmsg, given) else: - givenmsg = "$ " & given.cmd & '\n' & given.nimout + givenmsg = "$ " & given.cmd & "\n" & given.nimout if given.err == reSuccess: inc(r.passed) - r.addResult(test, target, extraOptions, expectedmsg, givenmsg, given.err) + r.addResult(test, target, expectedmsg, givenmsg, given.err) proc getTestSpecTarget(): TTarget = - if getEnv("NIM_COMPILE_TO_CPP", "false") == "true": + if getEnv("NIM_COMPILE_TO_CPP", "false").string == "true": result = targetCpp 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 = @@ -453,43 +467,38 @@ 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, extraOptions: string, nimcache: string) = + target: TTarget, nimcache: string, extraOptions = "") = test.startTime = epochTime() - 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: - compilerOutputTests(test, target, extraOptions, given, expected, r) + var given = callCompiler(expected.getCmd, test.name, test.options, nimcache, target, + extraOptions = " --stdout --hint[Path]:off --hint[Processing]:off") + compilerOutputTests(test, target, given, expected, r) of actionRun: + var given = callCompiler(expected.getCmd, test.name, test.options, + nimcache, target, extraOptions) if given.err != reSuccess: - r.addResult(test, target, extraOptions, "", "$ " & given.cmd & '\n' & given.nimout, given.err, givenSpec = given.addr) + r.addResult(test, target, "", "$ " & given.cmd & "\n" & given.nimout, given.err) else: let isJsTarget = target == targetJS var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt) if not fileExists(exeFile): - r.addResult(test, target, extraOptions, expected.output, + r.addResult(test, target, expected.output, "executable not found: " & exeFile, reExeNotFound) else: let nodejs = if isJsTarget: findNodeJs() else: "" if isJsTarget and nodejs == "": - r.addResult(test, target, extraOptions, expected.output, "nodejs binary not in PATH", + r.addResult(test, target, expected.output, "nodejs binary not in PATH", reExeNotFound) else: var exeCmd: string - var args = test.testArgs + var args = test.args if isJsTarget: exeCmd = nodejs - # see D20210217T215950 - args = @["--unhandled-rejections=strict", exeFile] & args + args = concat(@[exeFile], args) else: exeCmd = exeFile.dup(normalizeExe) - if valgrindEnabled and expected.useValgrind != disabled: + if expected.useValgrind != disabled: var valgrindOptions = @["--error-exitcode=1"] if expected.useValgrind != leaking: valgrindOptions.add "--leak-check=yes" @@ -501,31 +510,33 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, if exitCode != 0: exitCode = 1 let bufB = if expected.sortoutput: - var buf2 = buf + var buf2 = buf.string buf2.stripLineEnd var x = splitLines(buf2) sort(x, system.cmp) - join(x, "\n") & '\n' + join(x, "\n") & "\n" else: - buf + buf.string if exitCode != expected.exitCode: - given.err = reExitcodesDiffer - r.addResult(test, target, extraOptions, "exitcode: " & $expected.exitCode, + r.addResult(test, target, "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, extraOptions, expected.output, bufB, reOutputsDiffer) - compilerOutputTests(test, target, extraOptions, given, expected, r) + r.addResult(test, target, expected.output, bufB, reOutputsDiffer) + else: + compilerOutputTests(test, target, given, expected, r) of actionReject: - cmpMsgs(r, expected, given, test, target, extraOptions) + var given = callCompiler(expected.getCmd, test.name, test.options, + nimcache, target) + cmpMsgs(r, expected, given, test, target) -proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: string) = +proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions = "") = for target in expected.targets: inc(r.total) if target notin gTargets: - r.addResult(test, target, extraOptions, "", "", reDisabled) + r.addResult(test, target, "", "", reDisabled) inc(r.skipped) elif simulate: inc count @@ -533,15 +544,16 @@ proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: s else: let nimcache = nimcacheDir(test.name, test.options, target) var testClone = test - testSpecHelper(r, testClone, expected, target, extraOptions, nimcache) + testSpecHelper(r, testClone, expected, target, nimcache, extraOptions) 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 @@ -551,13 +563,46 @@ 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.} = +proc testSpecWithNimcache(r: var TResults, test: TTest; nimcache: string) = + 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 testC(r: var TResults, test: TTest, action: TTestAction) = + # runs C code. Doesn't support any specs, just goes by exit code. + if not checkDisabled(r, test): return + + let tname = test.name.addFileExt(".c") + inc(r.total) + maybeStyledEcho "Processing ", fgCyan, extractFilename(tname) + var given = callCCompiler(getCmd(TSpec()), test.name & ".c", test.options, targetC) + if given.err != reSuccess: + r.addResult(test, targetC, "", given.msg, given.err) + elif action == actionRun: + let exeFile = changeFileExt(test.name, ExeExt) + var (_, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUsePath}) + if exitCode != 0: given.err = reExitcodesDiffer + if given.err == reSuccess: inc(r.passed) + +proc testExec(r: var TResults, test: TTest) = + # runs executable or script, just goes by exit code + if not checkDisabled(r, test): return + + inc(r.total) + let (outp, errC) = execCmdEx(test.options.strip()) + var given: TSpec + if errC == 0: + given.err = reSuccess + else: + given.err = reExitcodesDiffer + given.msg = outp.string + + if given.err == reSuccess: inc(r.passed) + r.addResult(test, targetC, "", given.msg, given.err) proc makeTest(test, options: string, cat: Category): TTest = result.cat = cat @@ -566,17 +611,19 @@ proc makeTest(test, options: string, cat: Category): TTest = result.spec = parseSpec(addFileExt(test, ".nim")) result.startTime = epochTime() -proc makeRawTest(test, options: string, cat: Category): TTest {.used.} = +proc makeRawTest(test, options: string, cat: Category): TTest = result.cat = cat result.name = test result.options = options result.spec = initSpec(addFileExt(test, ".nim")) + result.startTime = epochTime() result.spec.action = actionCompile result.spec.targets = {getTestSpecTarget()} - result.startTime = epochTime() # TODO: fix these files const disabledFilesDefault = @[ + "LockFreeHash.nim", + "sharedstrings.nim", "tableimpl.nim", "setimpl.nim", "hashcommon.nim", @@ -611,7 +658,7 @@ proc loadSkipFrom(name: string): seq[string] = # used by `nlvm` (at least) for line in lines(name): let sline = line.strip() - if sline.len > 0 and not sline.startsWith('#'): + if sline.len > 0 and not sline.startsWith("#"): result.add sline proc main() = @@ -622,25 +669,25 @@ proc main() = var targetsStr = "" var isMainProcess = true var skipFrom = "" + var useMegatest = true var p = initOptParser() p.next() while p.kind in {cmdLongOption, cmdShortOption}: - case p.key.normalize - of "print": optPrintResults = true - of "verbose": optVerbose = true + case p.key.string.normalize + of "print", "verbose": optPrintResults = true of "failing": optFailing = true - of "pedantic": discard # deadcode refs https://github.com/nim-lang/Nim/issues/16731 + of "pedantic": discard "now always enabled" of "targets": - targetsStr = p.val + targetsStr = p.val.string gTargets = parseTargets(targetsStr) targetsSet = true of "nim": - compilerPrefix = addFileExt(p.val.absolutePath, ExeExt) + compilerPrefix = addFileExt(p.val.string.absolutePath, ExeExt) of "directory": - setCurrentDir(p.val) + setCurrentDir(p.val.string) of "colors": - case p.val: + case p.val.string: of "on": useColors = true of "off": @@ -649,7 +696,7 @@ proc main() = quit Usage of "batch": testamentData0.batchArg = p.val - if p.val != "_" and p.val.len > 0 and p.val[0] in {'0'..'9'}: + if p.val != "_": let s = p.val.split("_") doAssert s.len == 2, $(p.val, s) testamentData0.testamentBatch = s[0].parseInt @@ -659,23 +706,15 @@ proc main() = of "simulate": simulate = true of "megatest": - case p.val: + case p.val.string: of "on": useMegatest = true of "off": useMegatest = false else: quit Usage - of "valgrind": - case p.val: - of "on": - valgrindEnabled = true - of "off": - valgrindEnabled = false - else: - quit Usage of "backendlogging": - case p.val: + case p.val.string: of "on": backendLogging = true of "off": @@ -683,18 +722,18 @@ proc main() = else: quit Usage of "skipfrom": - skipFrom = p.val + skipFrom = p.val.string else: quit Usage p.next() if p.kind != cmdArgument: quit Usage - var action = p.key.normalize + var action = p.key.string.normalize p.next() var r = initResults() case action of "all": - #processCategory(r, Category"megatest", p.cmdLineRest, testsDir, runJoinableTests = false) + #processCategory(r, Category"megatest", p.cmdLineRest.string, testsDir, runJoinableTests = false) var myself = quoteShell(getAppFilename()) if targetsStr.len > 0: @@ -708,14 +747,13 @@ proc main() = myself &= " " & quoteShell("--skipFrom:" & skipFrom) var cats: seq[string] - let rest = if p.cmdLineRest.len > 0: " " & p.cmdLineRest else: "" + let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: "" for kind, dir in walkDir(testsDir): assert testsDir.startsWith(testsDir) let cat = dir[testsDir.len .. ^1] if kind == pcDir and cat notin ["testdata", "nimcache"]: cats.add cat - if isNimRepoTests(): - cats.add AdditionalCategories + cats.add AdditionalCategories if useMegatest: cats.add MegaTestCat var cmds: seq[string] @@ -730,14 +768,14 @@ proc main() = skips = loadSkipFrom(skipFrom) for i, cati in cats: progressStatus(i) - processCategory(r, Category(cati), p.cmdLineRest, testsDir, runJoinableTests = false) + processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, runJoinableTests = false) else: - addExitProc azure.finalize + addQuitProc azure.finalize quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, beforeRunEvent = progressStatus) of "c", "cat", "category": skips = loadSkipFrom(skipFrom) var cat = Category(p.key) - processCategory(r, cat, p.cmdLineRest, testsDir, runJoinableTests = true) + processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = true) of "pcat": skips = loadSkipFrom(skipFrom) # 'pcat' is used for running a category in parallel. Currently the only @@ -746,15 +784,23 @@ proc main() = isMainProcess = false var cat = Category(p.key) p.next - processCategory(r, cat, p.cmdLineRest, testsDir, runJoinableTests = false) + processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = false) of "p", "pat", "pattern": skips = loadSkipFrom(skipFrom) let pattern = p.key p.next - processPattern(r, pattern, p.cmdLineRest, simulate) + processPattern(r, pattern, p.cmdLineRest.string, simulate) of "r", "run": - let (cat, path) = splitTestFile(p.key) - processSingleTest(r, cat.Category, p.cmdLineRest, path, gTargets, targetsSet) + # "/pathto/tests/stdlib/nre/captures.nim" -> "stdlib" + "tests/stdlib/nre/captures.nim" + var subPath = p.key.string + let nimRoot = currentSourcePath / "../.." + # makes sure points to this regardless of cwd or which nim is used to compile this. + doAssert dirExists(nimRoot/testsDir) # sanity check + if subPath.isAbsolute: subPath = subPath.relativePath(nimRoot) + # at least one directory is required in the path, to use as a category name + let pathParts = subPath.relativePath(testsDir).split({DirSep, AltSep}) + let cat = Category(pathParts[0]) + processSingleTest(r, cat, p.cmdLineRest.string, subPath, gTargets, targetsSet) of "html": generateHtml(resultsFile, optFailing) else: diff --git a/testament/testament.nim.cfg b/testament/testament.nim.cfg index c97284129..f1fcd95f3 100644 --- a/testament/testament.nim.cfg +++ b/testament/testament.nim.cfg @@ -1,5 +1,3 @@ -# don't move this file without updating the logic in `isNimRepoTests` - path = "$nim" # For compiler/nodejs -d:ssl # For azure # my SSL doesn't have this feature and I don't care: diff --git a/testament/tests/shouldfail/tccodecheck.nim b/testament/tests/shouldfail/tccodecheck.nim index 477da1e23..a8d216a5b 100644 --- a/testament/tests/shouldfail/tccodecheck.nim +++ b/testament/tests/shouldfail/tccodecheck.nim @@ -1,5 +1,5 @@ discard """ - ccodecheck: "baz" +ccodecheck: "baz" """ proc foo(): void {.exportc: "bar".}= diff --git a/testament/tests/shouldfail/tcolumn.nim b/testament/tests/shouldfail/tcolumn.nim index 809ddec74..89482e673 100644 --- a/testament/tests/shouldfail/tcolumn.nim +++ b/testament/tests/shouldfail/tcolumn.nim @@ -1,7 +1,7 @@ discard """ - errormsg: "undeclared identifier: 'undeclared'" - line: 9 - column: 7 +errormsg: "undeclared identifier: 'undeclared'" +line: 8 +column: 7 """ # test should fail because the line directive is wrong diff --git a/testament/tests/shouldfail/terrormsg.nim b/testament/tests/shouldfail/terrormsg.nim index 6c130d107..dbbdf5021 100644 --- a/testament/tests/shouldfail/terrormsg.nim +++ b/testament/tests/shouldfail/terrormsg.nim @@ -1,7 +1,7 @@ discard """ - errormsg: "wrong error message" - line: 9 - column: 6 +errormsg: "wrong error message" +line: 8 +column: 6 """ # test should fail because the line directive is wrong diff --git a/testament/tests/shouldfail/texitcode1.nim b/testament/tests/shouldfail/texitcode1.nim index 605f046db..1b38b4f2e 100644 --- a/testament/tests/shouldfail/texitcode1.nim +++ b/testament/tests/shouldfail/texitcode1.nim @@ -1,3 +1,3 @@ discard """ - exitcode: 1 +exitcode: 1 """ diff --git a/testament/tests/shouldfail/tfile.nim b/testament/tests/shouldfail/tfile.nim index b40a4f44f..20d4bd1f3 100644 --- a/testament/tests/shouldfail/tfile.nim +++ b/testament/tests/shouldfail/tfile.nim @@ -1,6 +1,6 @@ discard """ - errormsg: "undeclared identifier: 'undefined'" - file: "notthisfile.nim" +errormsg: "undeclared identifier: 'undefined'" +file: "notthisfile.nim" """ echo undefined diff --git a/testament/tests/shouldfail/tline.nim b/testament/tests/shouldfail/tline.nim index fe782eb03..f7a09875c 100644 --- a/testament/tests/shouldfail/tline.nim +++ b/testament/tests/shouldfail/tline.nim @@ -1,7 +1,7 @@ discard """ - errormsg: "undeclared identifier: 'undeclared'" - line: 10 - column: 6 +errormsg: "undeclared identifier: 'undeclared'" +line: 9 +column: 6 """ # test should fail because the line directive is wrong diff --git a/testament/tests/shouldfail/tmaxcodesize.nim b/testament/tests/shouldfail/tmaxcodesize.nim index 92022ee97..9879e4181 100644 --- a/testament/tests/shouldfail/tmaxcodesize.nim +++ b/testament/tests/shouldfail/tmaxcodesize.nim @@ -1,5 +1,5 @@ discard """ - maxcodesize: 1 +maxcodesize: 1 """ echo "Hello World" diff --git a/testament/tests/shouldfail/tmsg.nim b/testament/tests/shouldfail/tmsg.nim new file mode 100644 index 000000000..4ad17fa95 --- /dev/null +++ b/testament/tests/shouldfail/tmsg.nim @@ -0,0 +1,6 @@ +discard """ +msg: "Hello World" +""" + +static: + echo "something else" diff --git a/testament/tests/shouldfail/tnimout.nim b/testament/tests/shouldfail/tnimout.nim index 0a65bfb70..c0e332053 100644 --- a/testament/tests/shouldfail/tnimout.nim +++ b/testament/tests/shouldfail/tnimout.nim @@ -1,6 +1,6 @@ discard """ - nimout: "Hello World!" - action: compile +nimout: "Hello World!" +action: compile """ static: diff --git a/testament/tests/shouldfail/tnimoutfull.nim b/testament/tests/shouldfail/tnimoutfull.nim deleted file mode 100644 index 4fc93f6d2..000000000 --- a/testament/tests/shouldfail/tnimoutfull.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - nimout: ''' -msg1 -msg2 -''' - action: compile - nimoutFull: true -""" - -# should fail because `msg3` is not in nimout and `nimoutFill: true` was given -static: - echo "msg1" - echo "msg2" - echo "msg3" diff --git a/testament/tests/shouldfail/toutput.nim b/testament/tests/shouldfail/toutput.nim index eaf9e8652..ac0bc7a46 100644 --- a/testament/tests/shouldfail/toutput.nim +++ b/testament/tests/shouldfail/toutput.nim @@ -1,7 +1,7 @@ discard """ - output: ''' - done - ''' +output: ''' +done +''' """ echo "broken" diff --git a/testament/tests/shouldfail/toutputsub.nim b/testament/tests/shouldfail/toutputsub.nim index 47324ecee..7cc51ee8d 100644 --- a/testament/tests/shouldfail/toutputsub.nim +++ b/testament/tests/shouldfail/toutputsub.nim @@ -1,5 +1,5 @@ discard """ - outputsub: "something else" +outputsub: "something else" """ echo "Hello World!" diff --git a/testament/tests/shouldfail/treject.nim b/testament/tests/shouldfail/treject.nim index 1e7258f70..aaf2b4a63 100644 --- a/testament/tests/shouldfail/treject.nim +++ b/testament/tests/shouldfail/treject.nim @@ -1,5 +1,5 @@ discard """ - action: "reject" +action: "reject" """ # Because we set action="reject", we expect this line not to compile. But the diff --git a/testament/tests/shouldfail/tsortoutput.nim b/testament/tests/shouldfail/tsortoutput.nim index 69dfbc0a0..4ce9ce26d 100644 --- a/testament/tests/shouldfail/tsortoutput.nim +++ b/testament/tests/shouldfail/tsortoutput.nim @@ -1,6 +1,6 @@ discard """ - sortoutput: true - output: ''' +sortoutput: true +output: ''' 2 1 ''' diff --git a/testament/tests/shouldfail/tvalgrind.nim b/testament/tests/shouldfail/tvalgrind.nim index d551ff12e..4f699fd3b 100644 --- a/testament/tests/shouldfail/tvalgrind.nim +++ b/testament/tests/shouldfail/tvalgrind.nim @@ -1,6 +1,6 @@ discard """ - valgrind: true - cmd: "nim $target --gc:arc -d:useMalloc $options $file" +valgrind: true +cmd: "nim $target --gc:arc -d:useMalloc $options $file" """ # this is the same check used by testament/specs.nim whether or not valgrind diff --git a/tests/alias/t19349.nim b/tests/alias/t19349.nim deleted file mode 100644 index 1e1e58264..000000000 --- a/tests/alias/t19349.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - action: "compile" -""" - -type - Vec3[T: SomeNumber] = object - arr: array[3, T] - -var - cfloatArr: array[3, array[3, cfloat]] - cfloatSeq = newSeq[Vec3[cfloat]]() -for row in cfloatArr: - cfloatSeq.add(Vec3[float32](arr: [row[0], row[1], row[2]])) - -var - cuintArr: array[3, array[3, cuint]] - cuintSeq = newSeq[Vec3[cuint]]() -for row in cuintArr: - cuintSeq.add(Vec3[uint32](arr: [row[0], row[1], row[2]])) diff --git a/tests/ambsym/tambprocvar.nim b/tests/ambsym/tambprocvar.nim deleted file mode 100644 index 2a9921bad..000000000 --- a/tests/ambsym/tambprocvar.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - action: reject - cmd: "nim check $file" - nimout: ''' -tambprocvar.nim(15, 11) Error: ambiguous identifier 'foo' -- use one of the following: - tambprocvar.foo: proc (x: int){.noSideEffect, gcsafe.} - tambprocvar.foo: proc (x: float){.noSideEffect, gcsafe.} -''' -""" - -block: - proc foo(x: int) = discard - proc foo(x: float) = discard - - let x = foo - -block: - let x = `+` #[tt.Error - ^ ambiguous identifier '+' -- use one of the following:]# diff --git a/tests/ambsym/tambsym3.nim b/tests/ambsym/tambsym3.nim index e50f9c461..0558517bd 100644 --- a/tests/ambsym/tambsym3.nim +++ b/tests/ambsym/tambsym3.nim @@ -1,15 +1,12 @@ discard """ - errormsg: "ambiguous enum field" + errormsg: "ambiguous identifier" file: "tambsym3.nim" - line: 14 + line: 11 """ # Test ambiguous symbols import mambsym1, times -{.hint[AmbiguousEnum]: on.} -{.hintAsError[AmbiguousEnum]: on.} - var v = mDec #ERROR_MSG ambiguous identifier diff --git a/tests/arc/t14383.nim b/tests/arc/t14383.nim index 96b505166..85f90d1c8 100644 --- a/tests/arc/t14383.nim +++ b/tests/arc/t14383.nim @@ -125,93 +125,4 @@ proc main = let rankdef = avals echo avals.len, " ", rankdef.len -main() - - -#------------------------------------------------------------------------------ -# Issue #16722, ref on distinct type, wrong destructors called -#------------------------------------------------------------------------------ - -type - Obj = object of RootObj - ObjFinal = object - ObjRef = ref Obj - ObjFinalRef = ref ObjFinal - D = distinct Obj - DFinal = distinct ObjFinal - DRef = ref D - DFinalRef = ref DFinal - -proc `=destroy`(o: var Obj) = - doAssert false, "no Obj is constructed in this sample" - -proc `=destroy`(o: var ObjFinal) = - doAssert false, "no ObjFinal is constructed in this sample" - -var dDestroyed: int -proc `=destroy`(d: var D) = - dDestroyed.inc - -proc `=destroy`(d: var DFinal) = - dDestroyed.inc - -func newD(): DRef = - DRef ObjRef() - -func newDFinal(): DFinalRef = - DFinalRef ObjFinalRef() - -proc testRefs() = - discard newD() - discard newDFinal() - -testRefs() - -doAssert(dDestroyed == 2) - - -#------------------------------------------------------------------------------ -# Issue #16185, complex self-assingment elimination -#------------------------------------------------------------------------------ - -type - CpuStorage*[T] = ref CpuStorageObj[T] - CpuStorageObj[T] = object - size*: int - raw_buffer*: ptr UncheckedArray[T] - Tensor[T] = object - buf*: CpuStorage[T] - TestObject = object - x: Tensor[float] - -proc `=destroy`[T](s: var CpuStorageObj[T]) = - if s.raw_buffer != nil: - s.raw_buffer.deallocShared() - s.size = 0 - s.raw_buffer = nil - -proc `=`[T](a: var CpuStorageObj[T]; b: CpuStorageObj[T]) {.error.} - -proc allocCpuStorage[T](s: var CpuStorage[T], size: int) = - new(s) - s.raw_buffer = cast[ptr UncheckedArray[T]](allocShared0(sizeof(T) * size)) - s.size = size - -proc newTensor[T](size: int): Tensor[T] = - allocCpuStorage(result.buf, size) - -proc `[]`[T](t: Tensor[T], idx: int): T = t.buf.raw_buffer[idx] -proc `[]=`[T](t: Tensor[T], idx: int, val: T) = t.buf.raw_buffer[idx] = val - -proc toTensor[T](s: seq[T]): Tensor[T] = - result = newTensor[T](s.len) - for i, x in s: - result[i] = x - -proc main2() = - var t: TestObject - t.x = toTensor(@[1.0, 2, 3, 4]) - t.x = t.x - doAssert(t.x.buf != nil) # self-assignment above should be eliminated - -main2() +main() \ No newline at end of file diff --git a/tests/arc/t16458.nim b/tests/arc/t16458.nim deleted file mode 100644 index 6ae114287..000000000 --- a/tests/arc/t16458.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - matrix: "--gc:orc --d:useNimRtl" - action: "compile" -""" - -echo 134 \ No newline at end of file diff --git a/tests/arc/t16558.nim b/tests/arc/t16558.nim deleted file mode 100644 index 0dbe02b33..000000000 --- a/tests/arc/t16558.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - matrix: "--gc:arc" - errormsg: "expression cannot be cast to 'int'" -""" - -block: # bug #16558 - var value = "hi there" - var keepInt: int - keepInt = cast[int](value) diff --git a/tests/arc/t17025.nim b/tests/arc/t17025.nim deleted file mode 100644 index a64c59ac1..000000000 --- a/tests/arc/t17025.nim +++ /dev/null @@ -1,56 +0,0 @@ -discard """ - cmd: "nim c --gc:arc $file" - output: ''' -{"Package": {"name": "hello"}, "Author": {"name": "name", "qq": "123456789", "email": "email"}} -hello -name -123456789 -email -hello -name2 -987654321 -liame -''' -""" - -import parsecfg, streams, tables - -const cfg = """[Package] -name=hello -[Author] -name=name -qq=123456789 -email="email"""" - -proc main() = - let stream = newStringStream(cfg) - let dict = loadConfig(stream) - var pname = dict.getSectionValue("Package","name") - var name = dict.getSectionValue("Author","name") - var qq = dict.getSectionValue("Author","qq") - var email = dict.getSectionValue("Author","email") - echo dict[] - echo pname & "\n" & name & "\n" & qq & "\n" & email - stream.close() - -main() - -proc getDict(): OrderedTableRef[string, OrderedTableRef[string, string]] = - result = newOrderedTable[string, OrderedTableRef[string, string]]() - result["Package"] = newOrderedTable[string, string]() - result["Package"]["name"] = "hello" - result["Author"] = newOrderedTable[string, string]() - result["Author"]["name"] = "name2" - result["Author"]["qq"] = "987654321" - result["Author"]["email"] = "liame" - -proc main2() = - let dict = getDict() - var pname = dict.getSectionValue("Package","name") - var name = dict.getSectionValue("Author","name") - var qq = dict.getSectionValue("Author","qq") - var email = dict.getSectionValue("Author","email") - echo pname & "\n" & name & "\n" & qq & "\n" & email - -main2() - diff --git a/tests/arc/t17173.nim b/tests/arc/t17173.nim deleted file mode 100644 index 0acd886a2..000000000 --- a/tests/arc/t17173.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - matrix: "--gc:refc; --gc:arc" -""" - -import std/strbasics - - -var a = " vhellov " -strip(a) -doAssert a == "vhellov" diff --git a/tests/arc/t17812.nim b/tests/arc/t17812.nim deleted file mode 100644 index bcd5f3a93..000000000 --- a/tests/arc/t17812.nim +++ /dev/null @@ -1,29 +0,0 @@ -discard """ - targets: "c js" - matrix: "--gc:refc; --gc:arc" -""" - -import std/times - -block: # bug #17812 - block: - type - Task = object - cb: proc () - - proc hello() = discard - - - let t = Task(cb: hello) - - doAssert t.repr.len > 0 - - - block: - type MyObj = object - field: DateTime - - - proc `$`(o: MyObj): string = o.repr - - doAssert ($MyObj()).len > 0 diff --git a/tests/arc/t18645.nim b/tests/arc/t18645.nim deleted file mode 100644 index c5fddd4bb..000000000 --- a/tests/arc/t18645.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - matrix: "--gc:arc; --gc:refc" - output: ''' -1 -2 -3 -''' -""" - -proc bitTypeIdUnion() = - var bitId {.global.} = block: - 0 - inc bitId - echo bitId - -bitTypeIdUnion() -bitTypeIdUnion() -bitTypeIdUnion() diff --git a/tests/arc/t18971.nim b/tests/arc/t18971.nim deleted file mode 100644 index 9b587d956..000000000 --- a/tests/arc/t18971.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - cmd: "nim c --gc:arc $file" -""" - -type MyObj = ref object - -var o = MyObj() -proc x: var MyObj = o - -var o2 = x() diff --git a/tests/arc/t19231.nim b/tests/arc/t19231.nim deleted file mode 100644 index 40fcf277c..000000000 --- a/tests/arc/t19231.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - matrix: "--mm:orc" - targets: "c cpp" -""" - -type - Game* = ref object - -proc free*(game: Game) = - var mixNumOpened:cint = 0 - for i in 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 deleted file mode 100644 index 6d3f57692..000000000 --- a/tests/arc/t19862.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - matrix: "--gc:refc; --gc:arc" -""" - -import std/widestrs - -# 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/t20456.nim b/tests/arc/t20456.nim deleted file mode 100644 index ace79255a..000000000 --- a/tests/arc/t20456.nim +++ /dev/null @@ -1,7 +0,0 @@ -discard """ - cmd: "nim check $file" - action: "compile" -""" - -when not defined(gcOrc): - {.error: "orc".} diff --git a/tests/arc/t20588.nim b/tests/arc/t20588.nim deleted file mode 100644 index d747c656d..000000000 --- a/tests/arc/t20588.nim +++ /dev/null @@ -1,22 +0,0 @@ -discard """ - cmd: "nim check --warnings:off --hints:off $file" - errormsg: "" - nimout: ''' -t20588.nim(20, 12) Error: illegal type conversion to 'auto' -t20588.nim(21, 14) Error: illegal type conversion to 'typed' -t20588.nim(22, 16) Error: illegal type conversion to 'untyped' -''' -""" - - - - - - - - - - -discard 0.0.auto -discard typed("abc") -discard untyped(4) diff --git a/tests/arc/t21184.nim b/tests/arc/t21184.nim deleted file mode 100644 index 91d0c42c7..000000000 --- a/tests/arc/t21184.nim +++ /dev/null @@ -1,77 +0,0 @@ -discard """ - matrix: "--mm:orc" -""" - -import std/[with] - -type - Node* {.acyclic.} = ref object of RootObj - name: string - data: pointer - children: seq[Node] - TextNode = ref object of Node - text: string - -proc fakeEcho(s: string) = - if s.len < 0: - echo s - -proc newNode[T: Node](parent: Node): T = - new result - result.data = alloc0(250) - parent.children.add(result) - -proc newRootNode(): Node = - new result - result.data = alloc0(250) - -method printNode(node: Node) {.base.} = - fakeEcho node.name - -method printNode(node: TextNode) = - procCall printNode(Node(node)) - fakeEcho node.text - -proc printChildren(node: Node) = - for child in node.children: - child.printNode() - printChildren(child) - -proc free(node: Node) = - for child in node.children: - free(child) - dealloc(node.data) - -template node(parent: Node, body: untyped): untyped = - var node = newNode[Node](parent) - with node: - body - -proc textNode(parent: Node, text: string) = - var node = newNode[TextNode](parent) - node.text = text - -template withRootNode(body: untyped): untyped = - var root = newRootNode() - root.name = "root" - with root: - body - root.printNode() - printChildren(root) - root.free() - -proc doTest() = - withRootNode: - node: - name = "child1" - node: - name = "child2" - node: - name = "child3" - textNode "Hello, world!" - - -# bug #21171 -if isMainModule: - for i in 0..100000: - doTest() diff --git a/tests/arc/t9650.nim b/tests/arc/t9650.nim deleted file mode 100644 index a8182db68..000000000 --- a/tests/arc/t9650.nim +++ /dev/null @@ -1,87 +0,0 @@ -discard """ - matrix: "--gc:arc" -""" - -import typetraits - -# bug #9650 -type - SharedPtr*[T] = object - val: ptr tuple[atomicCounter: int, value: T] - - Node*[T] = object - value: T - next: SharedPtr[Node[T]] - - ForwardList*[T] = object - first: SharedPtr[Node[T]] - -proc `=destroy`*[T](p: var SharedPtr[T]) = - if p.val != nil: - let c = atomicDec(p.val[].atomicCounter) - if c == 0: - when not supportsCopyMem(T): - `=destroy`(p.val[]) - dealloc(p.val) - p.val = nil - -proc `=`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) = - if dest.val != src.val: - if dest.val != nil: - `=destroy`(dest) - if src.val != nil: - discard atomicInc(src.val[].atomicCounter) - dest.val = src.val - -proc `=sink`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) = - if dest.val != nil and dest.val != src.val: - `=destroy`(dest) - dest.val = src.val - - -proc newSharedPtr*[T](val: sink T): SharedPtr[T] = - result.val = cast[type(result.val)](alloc(sizeof(result.val[]))) - reset(result.val[]) - result.val.atomicCounter = 1 - result.val.value = val - -proc isNil*[T](p: SharedPtr[T]): bool = - p.val == nil - -template `->`*[T](p: SharedPtr[T], name: untyped): untyped = - p.val.value.name - -proc createNode[T](val: T): SharedPtr[ Node[T] ]= - result = newSharedPtr(Node[T](value: val)) - -proc push_front*[T](list: var ForwardList[T], val: T) = - var newElem = createNode(val) - newElem->next = list.first - list.first = newElem - -proc pop_front*[T](list: var ForwardList[T]) = - let head = list.first - list.first = head->next - -proc toString*[T](list: ForwardList[T]): string = - result = "[" - var head = list.first - while not head.isNil: - result &= $(head->value) & ", " - head = head->next - result &= ']' - -block: - var x: ForwardList[int] - x.push_front(1) - x.push_front(2) - x.push_front(3) - - doAssert toString(x) == "[3, 2, 1, ]" - - x.pop_front() - x.pop_front() - doAssert toString(x) == "[1, ]" - - x.pop_front() - doAssert toString(x) == "[]" diff --git a/tests/arc/tarc_macro.nim b/tests/arc/tarc_macro.nim deleted file mode 100644 index ea7d279fd..000000000 --- a/tests/arc/tarc_macro.nim +++ /dev/null @@ -1,46 +0,0 @@ -import macros - -var destroyCalled = false -macro bar() = - let s = newTree(nnkAccQuoted, ident"=destroy") - # let s = ident"`=destroy`" # this would not work - result = quote do: - type Foo = object - # proc `=destroy`(a: var Foo) = destroyCalled = true # this would not work - proc `s`(a: var Foo) = destroyCalled = true - block: - let a = Foo() -bar() -doAssert destroyCalled - -# custom `op` -var destroyCalled2 = false -macro bar(ident) = - var x = 1.5 - result = quote("@") do: - type Foo = object - let `@ident` = 0 # custom op interpolated symbols need quoted (``) - proc `=destroy`(a: var Foo) = - doAssert @x == 1.5 - doAssert compiles(@x == 1.5) - let b1 = @[1,2] - let b2 = @@[1,2] - doAssert $b1 == "[1, 2]" - doAssert $b2 == "@[1, 2]" - destroyCalled2 = true - block: - let a = Foo() -bar(someident) -doAssert destroyCalled2 - -proc `&%`(x: int): int = 1 -proc `&%`(x, y: int): int = 2 - -macro bar2() = - var x = 3 - result = quote("&%") do: - var y = &%x # quoting operator - doAssert &%&%y == 1 # unary operator => need to escape - doAssert y &% y == 2 # binary operator => no need to escape - doAssert y == 3 -bar2() \ No newline at end of file diff --git a/tests/arc/tarc_orc.nim b/tests/arc/tarc_orc.nim deleted file mode 100644 index 981567350..000000000 --- a/tests/arc/tarc_orc.nim +++ /dev/null @@ -1,47 +0,0 @@ -discard """ - targets: "c cpp" - matrix: "--mm:arc; --mm:orc" -""" - -block: - type - PublicKey = array[32, uint8] - PrivateKey = array[64, uint8] - - proc ed25519_create_keypair(publicKey: ptr PublicKey; privateKey: ptr PrivateKey) = - publicKey[][0] = uint8(88) - - type - KeyPair = object - public: PublicKey - private: PrivateKey - - proc initKeyPair(): KeyPair = - ed25519_create_keypair(result.public.addr, result.private.addr) - - 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() - -proc main() = # todo bug with templates - block: # bug #11267 - var a: seq[char] = block: @[] - doAssert a == @[] - # 2 - proc b: seq[string] = - discard - @[] - doAssert b() == @[] -static: main() -main() diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index a03da6211..55803085f 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -1,6 +1,5 @@ discard """ output: ''' -=destroy called 123xyzabc destroyed: false destroyed: false @@ -27,66 +26,14 @@ new line after - @['a'] finalizer aaaaa hello -true -copying -123 -42 ok +closed destroying variable: 20 destroying variable: 10 -closed ''' - cmd: "nim c --gc:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file" + cmd: "nim c --gc:arc --deepcopy:on $file" """ -# bug #9401 - -type - MyObj = object - len: int - data: ptr UncheckedArray[float] - -proc `=destroy`*(m: var MyObj) = - - echo "=destroy called" - - if m.data != nil: - deallocShared(m.data) - m.data = nil - -type - MyObjDistinct = distinct MyObj - -proc `=copy`*(m: var MyObj, m2: MyObj) = - if m.data == m2.data: return - if m.data != nil: - `=destroy`(m) - m.len = m2.len - if m.len > 0: - m.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * m.len)) - copyMem(m.data, m2.data, sizeof(float) * m.len) - - -proc `=sink`*(m: var MyObj, m2: MyObj) = - if m.data != m2.data: - if m.data != nil: - `=destroy`(m) - m.len = m2.len - m.data = m2.data - -proc newMyObj(len: int): MyObj = - result.len = len - result.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * len)) - -proc newMyObjDistinct(len: int): MyObjDistinct = - MyObjDistinct(newMyObj(len)) - -proc fooDistinct = - doAssert newMyObjDistinct(2).MyObj.len == 2 - -fooDistinct() - - proc takeSink(x: sink string): bool = true proc b(x: sink string): string = @@ -124,7 +71,7 @@ proc test(count: int) = test(3) proc test2(count: int) = - block: #XXX: Fails with block currently + #block: #XXX: Fails with block currently var v {.global.} = newVariable(20) var count = count - 1 @@ -443,118 +390,3 @@ proc newPixelBuffer(): PixelBuffer = discard newPixelBuffer() - -# bug #17199 - -proc passSeq(data: seq[string]) = - # used the system.& proc initially - let wat = data & "hello" - -proc test2 = - let name = @["hello", "world"] - passSeq(name) - doAssert name == @["hello", "world"] - -static: test2() # was buggy -test2() - -proc merge(x: sink seq[string], y: sink string): seq[string] = - newSeq(result, x.len + 1) - for i in 0..x.len-1: - result[i] = move(x[i]) - result[x.len] = move(y) - -proc passSeq2(data: seq[string]) = - # used the system.& proc initially - let wat = merge(data, "hello") - -proc test3 = - let name = @["hello", "world"] - passSeq2(name) - doAssert name == @["hello", "world"] - -static: test3() # was buggy -test3() - -# bug #17712 -proc t17712 = - var ppv = new int - discard @[ppv] - var el: ref int - el = [ppv][0] - echo el != nil - -t17712() - -# bug #18030 - -type - Foo = object - n: int - -proc `=copy`(dst: var Foo, src: Foo) = - echo "copying" - dst.n = src.n - -proc `=sink`(dst: var Foo, src: Foo) = - echo "sinking" - dst.n = src.n - -var a: Foo - -proc putValue[T](n: T) - -proc useForward = - putValue(123) - -proc putValue[T](n: T) = - var b = Foo(n:n) - a = b - echo b.n - -useForward() - - -# bug #17319 -type - BrokenObject = ref object - brokenType: seq[int] - -proc use(obj: BrokenObject) = - discard - -method testMethod(self: BrokenObject) {.base.} = - iterator testMethodIter() {.closure.} = - use(self) - - var nameIterVar = testMethodIter - nameIterVar() - -let mikasa = BrokenObject() -mikasa.testMethod() - -# bug #19205 -type - InputSectionBase* = object of RootObj - relocations*: seq[int] # traced reference. string has a similar SIGSEGV. - InputSection* = object of InputSectionBase - -proc fooz(sec: var InputSectionBase) = - if sec of InputSection: # this line SIGSEGV. - echo 42 - -var sec = create(InputSection) -sec[] = InputSection(relocations: newSeq[int]()) -fooz sec[] - -block: - type - Data = ref object - id: int - proc main = - var x = Data(id: 99) - var y = x - x[] = Data(id: 778)[] - doAssert y.id == 778 - doAssert x[].id == 778 - main() diff --git a/tests/arc/tasyncawait.nim b/tests/arc/tasyncawait.nim index 91a7453cd..75d9bc9b5 100644 --- a/tests/arc/tasyncawait.nim +++ b/tests/arc/tasyncawait.nim @@ -40,7 +40,7 @@ proc readMessages(client: AsyncFD) {.async.} = clientCount.inc break else: - if line.startsWith("Message "): + if line.startswith("Message "): msgCount.inc else: doAssert false diff --git a/tests/arc/tasyncleak2.nim b/tests/arc/tasyncleak2.nim index 87d7e73f9..a8d71f1ee 100644 --- a/tests/arc/tasyncleak2.nim +++ b/tests/arc/tasyncleak2.nim @@ -57,7 +57,7 @@ proc main(): Future[void] = template await[T](f_gensym12: Future[T]): auto {.used.} = var internalTmpFuture_gensym12: FutureBase = f_gensym12 yield internalTmpFuture_gensym12 - (cast[typeof(f_gensym12)](internalTmpFuture_gensym12)).read() + (cast[type(f_gensym12)](internalTmpFuture_gensym12)).read() var retFuture = newFuture[void]("main") iterator mainIter(): FutureBase {.closure.} = diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim index 26c122384..a967a509b 100644 --- a/tests/arc/tcaseobj.nim +++ b/tests/arc/tcaseobj.nim @@ -10,7 +10,6 @@ begin end prevented (ok: true, value: "ok") -@[(kind: P, pChildren: @[])] myobj destroyed ''' """ @@ -245,88 +244,3 @@ 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() - -# bug #21023 -block: - block: - type - MGErrorKind = enum - mgeUnexpected, mgeNotFound - - type Foo = object - kind: MGErrorKind - ex: Exception - - type Boo = object - a: seq[int] - - type - Result2 = object - case o: bool - of false: - e: Foo - of true: - v: Boo - - proc startSessionSync(): Result2 = - return Result2(o: true) - - proc mainSync = - let ff = startSessionSync() - doAssert ff.o == true - - mainSync() - - block: - type - MGErrorKind = enum - mgeUnexpected, mgeNotFound - - type Foo = object - kind: MGErrorKind - ex: Exception - - type Boo = object - a: seq[int] - - type - Result2 = object - case o: bool - of false: - e: Foo - of true: - v: Boo - s: int - - proc startSessionSync(): Result2 = - return Result2(o: true, s: 12) - - proc mainSync = - let ff = startSessionSync() - doAssert ff.s == 12 - - mainSync() diff --git a/tests/arc/tcomputedgoto.nim b/tests/arc/tcomputedgoto.nim index 541a748c6..8af17b56e 100644 --- a/tests/arc/tcomputedgoto.nim +++ b/tests/arc/tcomputedgoto.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c --gc:arc $file''' + cmd: '''nim c --newruntime $file''' output: '''2 2''' """ diff --git a/tests/arc/tcomputedgotocopy.nim b/tests/arc/tcomputedgotocopy.nim index 8337123ba..78cb6c5c0 100644 --- a/tests/arc/tcomputedgotocopy.nim +++ b/tests/arc/tcomputedgotocopy.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c --gc:arc $file''' + cmd: '''nim c --newruntime $file''' output: '''2 2''' """ diff --git a/tests/arc/tconst_to_sink.nim b/tests/arc/tconst_to_sink.nim index 25f659341..ddcc46e67 100644 --- a/tests/arc/tconst_to_sink.nim +++ b/tests/arc/tconst_to_sink.nim @@ -1,7 +1,6 @@ discard """ output: '''@[(s1: "333", s2: ""), (s1: "abc", s2: "def"), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "lastone", s2: "")]''' - matrix: "--gc:arc" - targets: "c cpp" + cmd: "nim c --gc:arc $file" """ # bug #13240 diff --git a/tests/arc/tcstring.nim b/tests/arc/tcstring.nim new file mode 100644 index 000000000..79c8a7fcf --- /dev/null +++ b/tests/arc/tcstring.nim @@ -0,0 +1,16 @@ +discard """ + cmd: "nim c --gc:arc -r $file" + nimout: '''hello +h +o +''' +""" + +# Issue #13321: [codegen] --gc:arc does not properly emit cstring, results in SIGSEGV + +let a = "hello".cstring +echo a +echo a[0] +echo a[4] +doAssert a[a.len] == '\0' + diff --git a/tests/arc/tcursor_field_obj_constr.nim b/tests/arc/tcursor_field_obj_constr.nim deleted file mode 100644 index b87f734bd..000000000 --- a/tests/arc/tcursor_field_obj_constr.nim +++ /dev/null @@ -1,44 +0,0 @@ -discard """ - output: '''a -b -c''' - cmd: "nim c --gc:arc $file" -""" - -# bug #18469 - -type - Edge = object - neighbor {.cursor.}: Node - - NodeObj = object - neighbors: seq[Edge] - label: string - visited: bool - Node = ref NodeObj - - Graph = object - nodes: seq[Node] - -proc `=destroy`(x: var NodeObj) = - echo x.label - `=destroy`(x.neighbors) - `=destroy`(x.label) - -proc addNode(self: var Graph; label: string): Node = - self.nodes.add(Node(label: label)) - result = self.nodes[^1] - -proc addEdge(self: Graph; source, neighbor: Node) = - source.neighbors.add(Edge(neighbor: neighbor)) - -proc main = - var graph: Graph - let nodeA = graph.addNode("a") - let nodeB = graph.addNode("b") - let nodeC = graph.addNode("c") - - graph.addEdge(nodeA, neighbor = nodeB) - graph.addEdge(nodeA, neighbor = nodeC) - -main() diff --git a/tests/arc/tcursor_on_localvar.nim b/tests/arc/tcursor_on_localvar.nim index 0f53c5efa..81b48271f 100644 --- a/tests/arc/tcursor_on_localvar.nim +++ b/tests/arc/tcursor_on_localvar.nim @@ -4,10 +4,7 @@ discard """ Section: local Param: Str Param: Bool - Param: Floats2 -destroy Foo -destroy Foo -''' + Param: Floats2''' cmd: '''nim c --gc:arc $file''' """ @@ -126,36 +123,4 @@ when isMainModule: for p in cfg.params(s): echo " Param: " & p -# bug #16437 - -type - Foo = object - FooRef = ref Foo - - Bar = ref object - f: FooRef - -proc `=destroy`(o: var Foo) = - echo "destroy Foo" - -proc testMe(x: Bar) = - var c = (if x != nil: x.f else: nil) - assert c != nil - -proc main = - var b = Bar(f: FooRef()) - testMe(b) - -main() - -proc testMe2(x: Bar) = - var c: FooRef - c = (if x != nil: x.f else: nil) - assert c != nil - -proc main2 = - var b = Bar(f: FooRef()) - testMe2(b) - -main2() diff --git a/tests/arc/tcursorloop.nim b/tests/arc/tcursorloop.nim deleted file mode 100644 index a37a6a036..000000000 --- a/tests/arc/tcursorloop.nim +++ /dev/null @@ -1,45 +0,0 @@ -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/tcustomtrace.nim b/tests/arc/tcustomtrace.nim deleted file mode 100644 index 5e0ecfb24..000000000 --- a/tests/arc/tcustomtrace.nim +++ /dev/null @@ -1,240 +0,0 @@ -discard """ - outputsub: '''1 -2 -3 -4 -5 -6 -89 -90 -90 -0 0 1 -0 1 2 -0 2 3 -1 0 4 -1 1 5 -1 2 6 -1 3 7 -after 6 6 -MEM 0''' -joinable: false - cmd: "nim c --gc:orc -d:useMalloc $file" - valgrind: "true" -""" - -import typetraits - -type - myseq*[T] = object - len, cap: int - data: ptr UncheckedArray[T] - -# XXX make code memory safe for overflows in '*' -var - allocCount, deallocCount: int - -proc `=destroy`*[T](x: var myseq[T]) = - if x.data != nil: - when not supportsCopyMem(T): - for i in 0..= x.cap: resize(x) - result = addr(x.data[x.len]) - inc x.len - -template add*[T](x: var myseq[T]; y: T) = - reserveSlot(x)[] = y - -proc shrink*[T](x: var myseq[T]; newLen: int) = - assert newLen <= x.len - assert newLen >= 0 - when not supportsCopyMem(T): - for i in countdown(x.len - 1, newLen - 1): - `=destroy`(x.data[i]) - x.len = newLen - -proc grow*[T](x: var myseq[T]; newLen: int; value: T) = - if newLen <= x.len: return - assert newLen >= 0 - let oldCap = x.cap - if oldCap == 0: x.cap = newLen - else: x.cap = max(newLen, (oldCap * 3) shr 1) - if x.data == nil: inc allocCount - x.data = cast[type(x.data)](realloc0(x.data, oldCap * sizeof(T), x.cap * sizeof(T))) - for i in x.len.. 0'u64: - bi.digits.add cry.uint32 - -proc `$`(x: BigInt): string = - if x.digits.len == 0 or (x.digits.len == 1 and x.digits[0] == 0'u32): - return "0" - result = ""; var n = x; var msd = n.digits.high - while msd >= 0: - if n.digits[msd] == 0'u32: msd.dec; continue - var brw = 0.uint64 - for i in countdown(msd, 0): - let dvdnd = n.digits[i].uint64 + (brw shl 32) - let q = dvdnd div 10'u64; brw = dvdnd - q * 10'u64 - n.digits[i] = q.uint32 - result &= $brw - for i in 0 .. result.high shr 1: # reverse result string in place - let tmp = result[^(i + 1)] - result[^(i + 1)] = result[i] - result[i] = tmp - -type TriVal = (uint32, uint32, uint32) -type LogRep = (float64, TriVal) -type LogRepf = proc(x: LogRep): LogRep -const one: LogRep = (0.0'f64, (0'u32, 0'u32, 0'u32)) -proc `<`(me: LogRep, othr: LogRep): bool = me[0] < othr[0] - -proc convertTriVal2BigInt(tpl: TriVal): BigInt = - result = oneBigInt - let (x2, x3, x5) = tpl - for _ in 1 .. x2: result.shladd 1, zeroBigInt - for _ in 1 .. x3: result.shladd 1, result - for _ in 1 .. x5: result.shladd 2, result - -const lb2 = 1.0'f64 -const lb3 = 3.0'f64.log2 -const lb5 = 5.0'f64.log2 - -proc mul2(me: LogRep): LogRep = - let (lr, tpl) = me; let (x2, x3, x5) = tpl - (lr + lb2, (x2 + 1, x3, x5)) - -proc mul3(me: LogRep): LogRep = - let (lr, tpl) = me; let (x2, x3, x5) = tpl - (lr + lb3, (x2, x3 + 1, x5)) - -proc mul5(me: LogRep): LogRep = - let (lr, tpl) = me; let (x2, x3, x5) = tpl - (lr + lb5, (x2, x3, x5 + 1)) - -type - LazyListObj = object - hd: LogRep - tlf: proc(): LazyList {.closure.} - tl: LazyList - LazyList = ref LazyListObj - -var destroyed = 0 - -proc `=destroy`(ll: var LazyListObj) = - destroyed += 1 - if ll.tlf == nil and ll.tl == nil: return - - when defined(trace20): - echo "destroying: ", (destroyed, ll.hd[1].convertTriVal2BigInt) - if ll.tlf != nil: ll.tlf.`=destroy` - if ll.tl != nil: ll.tl.`=destroy` - #wasMoved(ll) - -proc rest(ll: LazyList): LazyList = # not thread-safe; needs lock on thunk - if ll.tlf != nil: ll.tl = ll.tlf(); ll.tlf = nil - ll.tl - -var created = 0 -iterator hammings(until: int): TriVal = - proc merge(x, y: LazyList): LazyList = - let xh = x.hd; let yh = y.hd; created += 1 - when defined(trace20): - echo "merge create: ", (created - 1, (if xh < yh: xh else: yh)[1].convertTriVal2BigInt) - if xh < yh: LazyList(hd: xh, tlf: proc(): auto = merge x.rest, y) - else: LazyList(hd: yh, tlf: proc(): auto = merge x, y.rest) - proc smult(mltf: LogRepf; s: LazyList): LazyList = - proc smults(ss: LazyList): LazyList = - when defined(trace20): - echo "mult create: ", (created, ss.hd.mltf[1].convertTriVal2BigInt) - created += 1; LazyList(hd: ss.hd.mltf, tlf: proc(): auto = ss.rest.smults) - s.smults - proc unnsm(s: LazyList, mltf: LogRepf): LazyList = - var r: LazyList = nil - when defined(trace20): - echo "first create: ", (created, one[1].convertTriVal2BigInt) - let frst = LazyList(hd: one, tlf: proc(): LazyList = r); created += 1 - r = if s == nil: smult(mltf, frst) else: s.merge smult(mltf, frst) - r - yield one[1] - var hmpll: LazyList = ((nil.unnsm mul5).unnsm mul3).unnsm mul2 - for _ in 2 .. until: - yield hmpll.hd[1]; hmpll = hmpll.rest # almost forever - -proc main = - var s = "" - for h in hammings(20): s &= $h.convertTrival2BigInt & " " - doAssert s == "1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 ", - "Algorithmic error finding first 20 Hamming numbers!!!" - - when not defined(trace20): - var lsth: TriVal - for h in hammings(200): lsth = h - doAssert $lsth.convertTriVal2BigInt == "16200", - "Algorithmic error finding 200th Hamming number!!!" - -let mem = getOccupiedMem() -main() -GC_FullCollect() -let mb = getOccupiedMem() - mem -doAssert mb == 0, "Found memory leak of " & $mb & " bytes!!!" - -echo getAllocStats() -echo "created ", created, " destroyed ", destroyed diff --git a/tests/arc/thard_alignment.nim b/tests/arc/thard_alignment.nim index baa964c77..e644572f0 100644 --- a/tests/arc/thard_alignment.nim +++ b/tests/arc/thard_alignment.nim @@ -18,7 +18,7 @@ type proc set1(x: float): m256d {.importc: "_mm256_set1_pd", header: "immintrin.h".} func `+`(a,b: m256d): m256d {.importc: "_mm256_add_pd", header: "immintrin.h".} proc `$`(a: m256d): string = - result = $(cast[ptr float](a.addr)[]) + result = $(cast[ptr float](a.unsafeAddr)[]) var res: seq[seq[m256d]] diff --git a/tests/arc/tkeys_lent.nim b/tests/arc/tkeys_lent.nim deleted file mode 100644 index 2c92350b1..000000000 --- a/tests/arc/tkeys_lent.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - output: '''{"string": 2}''' - cmd: "nim c --gc:orc $file" -""" - -import tables - -proc use(x: int) = echo x - -proc main = - var tab = {"string": 1}.toTable - for keyAAA in tab.keys(): - template alias(): untyped = tab[keyAAA] - alias() = 2 - echo tab - -main() diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 8ad7c955c..61105b44e 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -73,43 +73,6 @@ bye () () () -1 -destroy -1 -destroy -1 -destroy -copy (self-assign) -1 -destroy -1 -destroy -1 -destroy -destroy -copy -@[(f: 2), (f: 2), (f: 3)] -destroy -destroy -destroy -sink -sink -destroy -copy -(f: 1) -destroy -destroy -part-to-whole assigment: -sink -(children: @[]) -destroy -sink -(children: @[]) -destroy -copy -destroy -(f: 1) -destroy ''' """ @@ -293,7 +256,7 @@ when false: # bug #13456 -iterator combinations[T](s: openArray[T], k: int): seq[T] = +iterator combinations[T](s: openarray[T], k: int): seq[T] = let n = len(s) assert k >= 0 and k <= n var pos = newSeq[int](k) @@ -456,7 +419,7 @@ initFoo7(2) # bug #14902 -iterator zip[T](s: openArray[T]): (T, T) = +iterator zip[T](s: openarray[T]): (T, T) = var i = 0 while i < 10: yield (s[i mod 2], s[i mod 2 + 1]) @@ -593,249 +556,4 @@ let w2 = newWrapper2(1) echo $w2[] let w3 = newWrapper2(-1) -echo $w3[] - - -#-------------------------------------------------------------------- -#self-assignments - -# Self-assignments that are not statically determinable will get -# turned into `=copy` calls as caseBracketExprCopy demonstrates. -# (`=copy` handles self-assignments at runtime) - -type - OO = object - f: int - W = object - o: OO - -proc `=destroy`(x: var OO) = - if x.f != 0: - echo "destroy" - x.f = 0 - -proc `=sink`(x: var OO, y: OO) = - `=destroy`(x) - echo "sink" - x.f = y.f - -proc `=copy`(x: var OO, y: OO) = - if x.f != y.f: - `=destroy`(x) - echo "copy" - x.f = y.f - else: - echo "copy (self-assign)" - -proc caseSym = - var o = OO(f: 1) - o = o # NOOP - echo o.f # "1" - # "destroy" - -caseSym() - -proc caseDotExpr = - var w = W(o: OO(f: 1)) - w.o = w.o # NOOP - echo w.o.f # "1" - # "destroy" - -caseDotExpr() - -proc caseBracketExpr = - var w = [0: OO(f: 1)] - w[0] = w[0] # NOOP - echo w[0].f # "1" - # "destroy" - -caseBracketExpr() - -proc caseBracketExprCopy = - var w = [0: OO(f: 1)] - let i = 0 - w[i] = w[0] # "copy (self-assign)" - echo w[0].f # "1" - # "destroy" - -caseBracketExprCopy() - -proc caseDotExprAddr = - var w = W(o: OO(f: 1)) - w.o = addr(w.o)[] # NOOP - echo w.o.f # "1" - # "destroy" - -caseDotExprAddr() - -proc caseBracketExprAddr = - var w = [0: OO(f: 1)] - addr(w[0])[] = addr(addr(w[0])[])[] # NOOP - echo w[0].f # "1" - # "destroy" - -caseBracketExprAddr() - -proc caseNotAConstant = - var i = 0 - proc rand: int = - result = i - inc i - var s = @[OO(f: 1), OO(f: 2), OO(f: 3)] - s[rand()] = s[rand()] # "destroy" "copy" - echo s # @[(f: 2), (f: 2), (f: 3)] - -caseNotAConstant() - -proc potentialSelfAssign(i: var int) = - var a: array[2, OO] - a[i] = OO(f: 1) # turned into a memcopy - a[1] = OO(f: 2) - a[i+1] = a[i] # This must not =sink, but =copy - inc i - echo a[i-1] # (f: 1) - -potentialSelfAssign (var xi = 0; xi) - - -#-------------------------------------------------------------------- -echo "part-to-whole assigment:" - -type - Tree = object - children: seq[Tree] - - TreeDefaultHooks = object - children: seq[TreeDefaultHooks] - -proc `=destroy`(x: var Tree) = echo "destroy" -proc `=sink`(x: var Tree, y: Tree) = echo "sink" -proc `=copy`(x: var Tree, y: Tree) = echo "copy" - -proc partToWholeSeq = - var t = Tree(children: @[Tree()]) - t = t.children[0] # This should be sunk, but with the special transform (tmp = t.children[0]; wasMoved(0); `=sink`(t, tmp)) - - var tc = TreeDefaultHooks(children: @[TreeDefaultHooks()]) - tc = tc.children[0] # Ditto; if this were sunk with the normal transform (`=sink`(t, t.children[0]); wasMoved(t.children[0])) - echo tc # then it would crash because t.children[0] does not exist after the call to `=sink` - -partToWholeSeq() - -proc partToWholeSeqRTIndex = - var i = 0 - var t = Tree(children: @[Tree()]) - t = t.children[i] # See comment in partToWholeSeq - - var tc = TreeDefaultHooks(children: @[TreeDefaultHooks()]) - tc = tc.children[i] # See comment in partToWholeSeq - echo tc - -partToWholeSeqRTIndex() - -type List = object - next: ref List - -proc `=destroy`(x: var List) = echo "destroy" -proc `=sink`(x: var List, y: List) = echo "sink" -proc `=copy`(x: var List, y: List) = echo "copy" - -proc partToWholeUnownedRef = - var t = List(next: new List) - t = t.next[] # Copy because t.next is not an owned ref, and thus t.next[] cannot be moved - -partToWholeUnownedRef() - - -#-------------------------------------------------------------------- -# test that nodes that get copied during the transformation -# (like dot exprs) don't loose their firstWrite/lastRead property - -type - OOO = object - initialized: bool - - C = object - o: OOO - -proc `=destroy`(o: var OOO) = - doAssert o.initialized, "OOO was destroyed before initialization!" - -proc initO(): OOO = - OOO(initialized: true) - -proc initC(): C = - C(o: initO()) - -proc pair(): tuple[a: C, b: C] = - result = (a: initC(), b: initC())# <- when firstWrite tries to find this node to start its analysis it fails, because injectdestructors uses copyTree/shallowCopy - -discard pair() - - -# bug #17450 -proc noConsume(x: OO) {.nosinks.} = echo x - -proc main3 = - var i = 1 - noConsume: - block: - OO(f: i) - -main3() - -# misc -proc smoltest(x: bool): bool = - while true: - if true: return x - -discard smoltest(true) - -# bug #18002 -type - TTypeAttachedOp = enum - attachedAsgn - attachedSink - attachedTrace - - PNode = ref object - discard - -proc genAddrOf(n: PNode) = - assert n != nil, "moved?!" - -proc atomicClosureOp = - let x = PNode() - - genAddrOf: - block: - x - - case attachedTrace - of attachedSink: discard - of attachedAsgn: discard - of attachedTrace: genAddrOf(x) - -atomicClosureOp() - - -template assertEq(a, b: untyped): untyped = - block: - let - aval = a - bval = b - - if aval != bval: - quit "bug!" - -proc convoluted = - let _ = (; - var val1: string; - if true: val1 = "22" - true - ) - - assertEq val1, "22" - assertEq val1, "22" - -convoluted() +echo $w3[] \ No newline at end of file diff --git a/tests/arc/tmovebugcopy.nim b/tests/arc/tmovebugcopy.nim index ec4315777..7c5228147 100644 --- a/tests/arc/tmovebugcopy.nim +++ b/tests/arc/tmovebugcopy.nim @@ -253,7 +253,7 @@ when false: # bug #13456 -iterator combinations[T](s: openArray[T], k: int): seq[T] = +iterator combinations[T](s: openarray[T], k: int): seq[T] = let n = len(s) assert k >= 0 and k <= n var pos = newSeq[int](k) @@ -416,7 +416,7 @@ initFoo7(2) # bug #14902 -iterator zip[T](s: openArray[T]): (T, T) = +iterator zip[T](s: openarray[T]): (T, T) = var i = 0 while i < 10: yield (s[i mod 2], s[i mod 2 + 1]) diff --git a/tests/arc/tnewseq_legacy.nim b/tests/arc/tnewseq_legacy.nim deleted file mode 100644 index 4730d2c2b..000000000 --- a/tests/arc/tnewseq_legacy.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - output: "(allocCount: 201, deallocCount: 201)" - cmd: "nim c --gc:orc -d:nimAllocStats $file" -""" - -proc main(prefix: string) = - var c: seq[string] - for i in 0..<100: - newSeq(c, 100) - c[i] = prefix & $i - -main("abc") -echo getAllocStats() diff --git a/tests/arc/topenarray.nim b/tests/arc/topenarray.nim deleted file mode 100644 index 0e45f3ec7..000000000 --- a/tests/arc/topenarray.nim +++ /dev/null @@ -1,52 +0,0 @@ -discard """ - input: "hi" - output: ''' -hi -Nim -''' - matrix: "--gc:arc -d:useMalloc; --gc:arc" -""" -{.experimental: "views".} - -block: # bug 18627 - proc setPosition(params: openArray[string]) = - for i in params.toOpenArray(0, params.len - 1): - echo i - - proc uciLoop() = - let params = @[readLine(stdin)] - setPosition(params) - - uciLoop() - - proc uciLoop2() = - let params = @["Nim"] - for i in params.toOpenArray(0, params.len - 1): - echo i - uciLoop2() - -when defined(nimPreviewSlimSystem): - import std/assertions - -block: # bug #20954 - block: - doAssertRaises(IndexDefect): - var v: array[10, int] - - echo len(toOpenArray(v, 20, 30)) - - block: - doAssertRaises(IndexDefect): - var v: seq[int] - - echo len(toOpenArray(v, 20, 30)) - -# bug #20422 - -proc f(a: var string) = - var v = a.toOpenArray(1, 3) - v[0] = 'a' - -var a = "Hello" -f(a) -doAssert a == "Hallo" diff --git a/tests/arc/topt_cursor.nim b/tests/arc/topt_cursor.nim index 794132921..a8020e72b 100644 --- a/tests/arc/topt_cursor.nim +++ b/tests/arc/topt_cursor.nim @@ -4,18 +4,21 @@ discard """ nimout: '''--expandArc: main var - x_cursor :tmpD + :tmpD_1 + :tmpD_2 try: - x_cursor = ("hi", 5) - if cond: - x_cursor = ("different", 54) else: - x_cursor = ("string here", 80) + var x_cursor = ("hi", 5) + x_cursor = if cond: + :tmpD = ("different", 54) + :tmpD else: + :tmpD_1 = ("string here", 80) + :tmpD_1 echo [ - :tmpD = `$$`(x_cursor) - :tmpD] + :tmpD_2 = `$`(x_cursor) + :tmpD_2] finally: - `=destroy`(:tmpD) + `=destroy`(:tmpD_2) -- end of expandArc ------------------------ --expandArc: sio @@ -25,7 +28,7 @@ block :tmp: try: var res try: - res = newStringOfCap(80) + res = TaintedString(newStringOfCap(80)) block :tmp_1: while readLine(f, res): x_cursor = res diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 50dfa26ac..3d37e6269 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -1,24 +1,30 @@ discard """ - nimoutFull: true - cmd: '''nim c -r --warnings:off --hints:off --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 + output: '''(repo: "", package: "meo", ext: "") +doing shady stuff... +3 +6 +(@[1], @[2])''' + cmd: '''nim c --gc:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off $file''' + nimout: '''--expandArc: newTarget var splat :tmp :tmp_1 -splat = splitDrive do: - let blitTmp = path - blitTmp -:tmp = splat.drive -`=wasMoved`(splat.drive) -:tmp_1 = splat.path_1 -`=wasMoved`(splat.path_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) result = ( - let blitTmp_1 = :tmp + let blitTmp = :tmp + blitTmp, + let blitTmp_1 = :tmp_1 blitTmp_1, - let blitTmp_2 = :tmp_1 + let blitTmp_2 = :tmp_2 blitTmp_2) `=destroy`(splat) -- end of expandArc ------------------------ @@ -53,125 +59,35 @@ lnext = _[1] --expandArc: tt var - it_cursor a :tmpD :tmpD_1 :tmpD_2 try: - it_cursor = x + var it_cursor = x a = ( - `=wasMoved`(:tmpD) + wasMoved(:tmpD) `=copy`(:tmpD, it_cursor.key) :tmpD, - `=wasMoved`(:tmpD_1) + wasMoved(:tmpD_1) `=copy`(:tmpD_1, it_cursor.val) :tmpD_1) echo [ - :tmpD_2 = `$$`(a) + :tmpD_2 = `$`(a) :tmpD_2] finally: `=destroy`(:tmpD_2) `=destroy_1`(a) --- end of expandArc ------------------------ ---expandArc: extractConfig - -var lan_ip -try: - lan_ip = "" - block :tmp: - var line - var i = 0 - let L = len(txt) - block :tmp_1: - while i < L: - var splitted - try: - line = txt[i] - splitted = split(line, " ", -1) - if splitted[0] == "opt": - `=copy`(lan_ip, splitted[1]) - echo [lan_ip] - echo [splitted[1]] - inc(i, 1) - finally: - `=destroy`(splitted) -finally: - `=destroy_1`(lan_ip) --- end of expandArc ------------------------ ---expandArc: mergeShadowScope - -var shadowScope -`=copy`(shadowScope, c.currentScope) -rawCloseScope(c) -block :tmp: - var sym - var i = 0 - let L = len(shadowScope.symbols) - block :tmp_1: - while i < L: - var :tmpD - sym = shadowScope.symbols[i] - addInterfaceDecl(c): - `=wasMoved`(:tmpD) - `=copy_1`(:tmpD, sym) - :tmpD - inc(i, 1) -`=destroy`(shadowScope) --- end of expandArc ------------------------ ---expandArc: check - -var par -this.isValid = fileExists(this.value) -if dirExists(this.value): - var :tmpD - par = (dir: - `=wasMoved`(:tmpD) - `=copy`(:tmpD, this.value) - :tmpD, front: "") else: - var - :tmpD_1 - :tmpD_2 - :tmpD_3 - par = (dir_1: parentDir(this.value), front_1: - `=wasMoved`(:tmpD_1) - `=copy`(:tmpD_1, - :tmpD_3 = splitDrive do: - `=wasMoved`(:tmpD_2) - `=copy`(:tmpD_2, this.value) - :tmpD_2 - :tmpD_3.path) - :tmpD_1) - `=destroy`(:tmpD_3) -if dirExists(par.dir): - `=sink`(this.matchDirs, getSubDirs(par.dir, par.front)) -else: - `=sink`(this.matchDirs, []) -`=destroy`(par) --- end of expandArc ------------------------ ---expandArc: check - -check(this) --- end of expandArc ------------------------ -(package: "", ext: "meo") -doing shady stuff... -3 -6 -(@[1], @[2]) -192.168.0.1 -192.168.0.1 -192.168.0.1 -192.168.0.1 -''' +-- end of expandArc ------------------------''' """ -import os, std/private/ntpath +import os -type Target = tuple[package, ext: string] +type Target = tuple[repo, package, ext: string] proc newTarget*(path: string): Target = - let splat = path.splitDrive - result = (package: splat.drive, ext: splat.path) + let splat = path.splitFile + result = (repo: splat.dir, package: splat.name, ext: splat.ext) echo newTarget("meo") @@ -278,101 +194,3 @@ proc plus(input: string) = (rvalue, rnext) = rresult plus("123;") - -func substrEq(s: string, pos: int, substr: string): bool = - var i = 0 - var length = substr.len - while i < length and pos+i < s.len and s[pos+i] == substr[i]: - inc i - return i == length - -template stringHasSep(s: string, index: int, sep: string): bool = - s.substrEq(index, sep) - -template splitCommon(s, sep, maxsplit, sepLen) = - var last = 0 - var splits = maxsplit - - while last <= len(s): - var first = last - while last < len(s) and not stringHasSep(s, last, sep): - inc(last) - if splits == 0: last = len(s) - yield substr(s, first, last-1) - if splits == 0: break - dec(splits) - inc(last, sepLen) - -iterator split(s: string, sep: string, maxsplit = -1): string = - splitCommon(s, sep, maxsplit, sep.len) - -template accResult(iter: untyped) = - result = @[] - for x in iter: add(result, x) - -func split*(s: string, sep: string, maxsplit = -1): seq[string] = - accResult(split(s, sep, maxsplit)) - - -let txt = @["opt 192.168.0.1", "static_lease 192.168.0.1"] - -# bug #17033 - -proc extractConfig() = - var lan_ip = "" - - for line in txt: - let splitted = line.split(" ") - if splitted[0] == "opt": - lan_ip = splitted[1] # "borrow" is conditional and inside a loop. - # Not good enough... - # we need a flag that live-ranges are disjoint - echo lan_ip - echo splitted[1] # Without this line everything works - -extractConfig() - - -type - Symbol = ref object - name: string - - Scope = ref object - parent: Scope - symbols: seq[Symbol] - - PContext = ref object - currentScope: Scope - -proc rawCloseScope(c: PContext) = - c.currentScope = c.currentScope.parent - -proc addInterfaceDecl(c: PContext; s: Symbol) = - c.currentScope.symbols.add s - -proc mergeShadowScope*(c: PContext) = - let shadowScope = c.currentScope - c.rawCloseScope - for sym in shadowScope.symbols: - c.addInterfaceDecl(sym) - -mergeShadowScope(PContext(currentScope: Scope(parent: Scope()))) - -type - Foo = ref object - isValid*: bool - value*: string - matchDirs*: seq[string] - -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: splitDrive(this.value).path) - if dirExists(par.dir): - this.matchDirs = getSubDirs(par.dir, par.front) - else: - this.matchDirs = @[] - -check(Foo()) diff --git a/tests/arc/topt_refcursors.nim b/tests/arc/topt_refcursors.nim index 8c638a4a1..372fc18bf 100644 --- a/tests/arc/topt_refcursors.nim +++ b/tests/arc/topt_refcursors.nim @@ -1,35 +1,22 @@ discard """ output: '''''' cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file''' - nimout: ''' ---expandArc: traverse + nimout: '''--expandArc: traverse -var - it_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 ------------------------ -''' +var it_cursor = root +block :tmp: + while ( + not (it_cursor == nil)): + echo [it_cursor.s] + it_cursor = it_cursor.ri +var jt_cursor = root +block :tmp_1: + while ( + not (jt_cursor == nil)): + let ri_1_cursor = jt_cursor.ri + echo [jt_cursor.s] + jt_cursor = ri_1_cursor +-- end of expandArc ------------------------''' """ type diff --git a/tests/arc/topt_wasmoved_destroy_pairs.nim b/tests/arc/topt_wasmoved_destroy_pairs.nim index 6577d6787..2f971f112 100644 --- a/tests/arc/topt_wasmoved_destroy_pairs.nim +++ b/tests/arc/topt_wasmoved_destroy_pairs.nim @@ -1,8 +1,7 @@ discard """ output: '''''' cmd: '''nim c --gc:arc --expandArc:main --expandArc:tfor --hint:Performance:off $file''' - nimout: ''' ---expandArc: main + nimout: '''--expandArc: main var a @@ -30,7 +29,6 @@ try: x = f() block :tmp: var i_cursor - mixin inc var i_1 = 0 block :tmp_1: while i_1 < 4: @@ -39,26 +37,25 @@ try: if i_cursor == 2: return add(a): - `=wasMoved`(:tmpD) + wasMoved(:tmpD) `=copy`(:tmpD, x) :tmpD inc i_1, 1 if cond: add(a): let blitTmp = x - `=wasMoved`(x) + wasMoved(x) blitTmp else: add(b): let blitTmp_1 = x - `=wasMoved`(x) + wasMoved(x) blitTmp_1 finally: `=destroy`(x) `=destroy_1`(b) `=destroy_1`(a) --- end of expandArc ------------------------ -''' +-- end of expandArc ------------------------''' """ proc f(): seq[int] = diff --git a/tests/arc/torcmisc.nim b/tests/arc/torcmisc.nim deleted file mode 100644 index e41ad7c77..000000000 --- a/tests/arc/torcmisc.nim +++ /dev/null @@ -1,66 +0,0 @@ -discard """ - output: '''success''' - cmd: "nim c --gc:orc -d:release $file" -""" - -# bug #17170 - -when true: - import asyncdispatch - - type - Flags = ref object - returnedEof, reading: bool - - proc dummy(): Future[string] {.async.} = - result = "foobar" - - proc hello(s: Flags) {.async.} = - let buf = - try: - await dummy() - except CatchableError as exc: - # When an exception happens here, the Bufferstream is effectively - # broken and no more reads will be valid - for now, return EOF if it's - # called again, though this is not completely true - EOF represents an - # "orderly" shutdown and that's not what happened here.. - s.returnedEof = true - raise exc - finally: - s.reading = false - - waitFor hello(Flags()) - echo "success" - -# bug #18240 -import tables - -type - TopicHandler* = proc(topic: string, - data: seq[byte]): Future[void] {.gcsafe, raises: [Defect].} - - PeerID* = object - data*: seq[byte] - - PeerInfo* = ref object of RootObj - peerId*: PeerID - - Connection* = ref object of RootObj - peerInfo*: PeerInfo - - PubSubPeer* = ref object of RootObj - codec*: string - - PubSub* = ref object of RootObj - topics*: Table[string, seq[TopicHandler]] - peers*: Table[PeerID, PubSubPeer] - -proc getOrCreatePeer*(myParam: PubSub, peerId: PeerID, protos: seq[string]): PubSubPeer = - myParam.peers.withValue(peerId, peer): - return peer[] - -method handleConn*(myParam: PubSub, - conn: Connection, - proto: string) {.base, async.} = - myParam.peers.withValue(conn.peerInfo.peerId, peer): - let peerB = peer[] diff --git a/tests/arc/tref_cast_error.nim b/tests/arc/tref_cast_error.nim index 20139c1be..b0d2faf77 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/arc/trepr.nim b/tests/arc/trepr.nim index abf28330a..50d433208 100644 --- a/tests/arc/trepr.nim +++ b/tests/arc/trepr.nim @@ -9,7 +9,6 @@ nil 2 Obj(member: ref @["hello"]) ref (member: ref @["hello"]) -ObjUa(v: 0, a: [...]) ''' """ @@ -88,10 +87,3 @@ macro extract(): untyped = test(parseExpr("discard")) extract() - -type - ObjUa = ref object - v: int - a: UncheckedArray[char] - -echo ObjUa().repr diff --git a/tests/arc/trtree.nim b/tests/arc/trtree.nim index 683403a62..5659a5bdc 100644 --- a/tests/arc/trtree.nim +++ b/tests/arc/trtree.nim @@ -71,8 +71,8 @@ proc newRStarTree*[M, D: Dim; RT, LT](minFill: range[30 .. 50] = 40): RStarTree[ result.p = M * 30 div 100 result.root = newLeaf[M, D, RT, LT]() -proc center(r: Box): auto =#BoxCenter[r.len, typeof(r[0].a)] = - var res: BoxCenter[r.len, typeof(r[0].a)] +proc center(r: Box): auto =#BoxCenter[r.len, type(r[0].a)] = + var res: BoxCenter[r.len, type(r[0].a)] for i in 0 .. r.high: when r[0].a is SomeInteger: res[i] = (r[i].a + r[i].b) div 2 @@ -82,13 +82,13 @@ proc center(r: Box): auto =#BoxCenter[r.len, typeof(r[0].a)] = return res proc distance(c1, c2: BoxCenter): auto = - var res: typeof(c1[0]) + var res: type(c1[0]) for i in 0 .. c1.high: res += (c1[i] - c2[i]) * (c1[i] - c2[i]) return res proc overlap(r1, r2: Box): auto = - result = typeof(r1[0].a)(1) + result = type(r1[0].a)(1) for i in 0 .. r1.high: result *= (min(r1[i].b, r2[i].b) - max(r1[i].a, r2[i].a)) if result <= 0: return 0 @@ -104,13 +104,13 @@ proc intersect(r1, r2: Box): bool = return false return true -proc area(r: Box): auto = #typeof(r[0].a) = - result = typeof(r[0].a)(1) +proc area(r: Box): auto = #type(r[0].a) = + result = type(r[0].a)(1) for i in 0 .. r.high: result *= r[i].b - r[i].a -proc margin(r: Box): auto = #typeof(r[0].a) = - result = typeof(r[0].a)(0) +proc margin(r: Box): auto = #type(r[0].a) = + result = type(r[0].a)(0) for i in 0 .. r.high: result += r[i].b - r[i].a @@ -142,7 +142,7 @@ proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; lev while it.level > level: let nn = Node[M, D, RT, LT](it) var i0 = 0 # selected index - var minLoss = typeof(b[0].a).high + var minLoss = type(b[0].a).high if it.level == 1: # childreen are leaves -- determine the minimum overlap costs for i in 0 ..< it.numEntries: let nx = union(nn.a[i].b, b) @@ -179,8 +179,8 @@ proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; lev proc pickSeeds[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; bx: Box[D, RT]): (int, int) = var i0, j0: int - var bi, bj: typeof(bx) - var largestWaste = typeof(bx[0].a).low + var bi, bj: type(bx) + var largestWaste = type(bx[0].a).low for i in -1 .. n.a.high: for j in 0 .. n.a.high: if unlikely(i == j): continue @@ -200,7 +200,7 @@ proc pickSeeds[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: Node[M, D, RT, LT] proc pickNext[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n0, n1, n2: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; b1, b2: Box[D, RT]): int = let a1 = area(b1) let a2 = area(b2) - var d = typeof(a1).low + var d = type(a1).low for i in 0 ..< n0.numEntries: let d1 = area(union(b1, n0.a[i].b)) - a1 let d2 = area(union(b2, n0.a[i].b)) - a2 @@ -220,15 +220,15 @@ proc sortPlus[T](a: var openArray[T], ax: var T, cmp: proc (x, y: T): int {.clos a.sort(cmp, order) # R*TREE procs -proc rstarSplit[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) = - type NL = typeof(lx) - var nBest: typeof(n) +proc rstarSplit[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): type(n) = + type NL = type(lx) + var nBest: type(n) new nBest var lx = lx when n is Node[M, D, RT, LT]: lx.n.parent = n - var lxbest: typeof(lx) - var m0 = lx.b[0].a.typeof.high + var lxbest: type(lx) + var m0 = lx.b[0].a.high for d2 in 0 ..< 2 * D: let d = d2 div 2 if d2 mod 2 == 0: @@ -251,8 +251,8 @@ proc rstarSplit[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, lxbest = lx m0 = m var i0 = -1 - var o0 = lx.b[0].a.typeof.high - for i in t.m - 1 .. n.a.typeof.high - t.m + 1: + var o0 = lx.b[0].a.high + for i in t.m - 1 .. n.a.high - t.m + 1: var b1 = lxbest.b for j in 0 ..< i: b1 = union(nbest.a[j].b, b1) @@ -277,8 +277,8 @@ proc rstarSplit[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, for i in 0 ..< result.numEntries: result.a[i].n.parent = result -proc quadraticSplit[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) = - var n1, n2: typeof(n) +proc quadraticSplit[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): type(n) = + var n1, n2: type(n) var s1, s2: int new n1 new n2 @@ -341,7 +341,7 @@ proc quadraticSplit[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: var Node[M, D, n[] = n1[] return n2 -proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) +proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): type(n) proc adjustTree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; l, ll: H[M, D, RT, LT]; hb: Box[D, RT]) = var n = l @@ -361,7 +361,7 @@ proc adjustTree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; l, ll: H[M, D, RT, LT var i = 0 while p.a[i].n != n: inc(i) - var b: typeof(p.a[0].b) + var b: type(p.a[0].b) if n of Leaf[M, D, RT, LT]: when false:#if likely(nn.isNil): # no performance gain b = union(p.a[i].b, Leaf[M, D, RT, LT](n).a[n.numEntries - 1].b) @@ -427,9 +427,9 @@ proc insert*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | proc rsinsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int) proc reInsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]) = - type NL = typeof(lx) + type NL = type(lx) var lx = lx - var buf: typeof(n.a) + var buf: type(n.a) let p = Node[M, D, RT, LT](n.parent) var i = 0 while p.a[i].n != n: @@ -449,7 +449,7 @@ proc reInsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, R for i in M - t.p + 1 .. n.a.high: rsinsert(t, buf[i], n.level) -proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): typeof(n) = +proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): type(n) = if n.level != t.root.level and t.firstOverflow[n.level]: t.firstOverflow[n.level] = false reInsert(t, n, lx) diff --git a/tests/arc/tshared_ptr_crash.nim b/tests/arc/tshared_ptr_crash.nim deleted file mode 100644 index 1794834db..000000000 --- a/tests/arc/tshared_ptr_crash.nim +++ /dev/null @@ -1,67 +0,0 @@ -discard """ - cmd: "nim c --threads:on --gc:arc $file" - action: compile -""" - -# bug #17893 - -type - SharedPtr*[T] = object - val: ptr tuple[value: T, atomicCounter: int] - -proc `=destroy`*[T](p: var SharedPtr[T]) = - mixin `=destroy` - if p.val != nil: - if atomicLoadN(addr p.val[].atomicCounter, AtomicConsume) == 0: - `=destroy`(p.val[]) - deallocShared(p.val) - else: - discard atomicDec(p.val[].atomicCounter) - -proc `=copy`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) = - if src.val != nil: - discard atomicInc(src.val[].atomicCounter) - if dest.val != nil: - `=destroy`(dest) - dest.val = src.val - -proc newSharedPtr*[T](val: sink T): SharedPtr[T] {.nodestroy.} = - result.val = cast[typeof(result.val)](allocShared(sizeof(result.val[]))) - result.val.atomicCounter = 0 - result.val.value = val - -proc isNil*[T](p: SharedPtr[T]): bool {.inline.} = - p.val == nil - -proc `[]`*[T](p: SharedPtr[T]): var T {.inline.} = - when compileOption("boundChecks"): - doAssert(p.val != nil, "deferencing nil shared pointer") - result = p.val.value - -type - Sender*[T] = object - queue: SharedPtr[seq[T]] - -proc newSender*[T](queue: sink SharedPtr[seq[T]]): Sender[T] = - result = Sender[T](queue: queue) - -proc send*[T](self: Sender[T]; t: sink T) = - self.queue[].add t - -proc newChannel*(): Sender[int] = - let queue = newSharedPtr(newSeq[int]()) - result = newSender(queue) - - -var - p: Thread[Sender[int]] - -proc threadFn(tx: Sender[int]) = - send tx, 0 - -proc multiThreadedChannel = - let tx = newChannel() - createThread(p, threadFn, tx) - joinThread(p) - -multiThreadedChannel() diff --git a/tests/arc/tunref_cycle.nim b/tests/arc/tunref_cycle.nim deleted file mode 100644 index 82551b7f7..000000000 --- a/tests/arc/tunref_cycle.nim +++ /dev/null @@ -1,26 +0,0 @@ -discard """ - outputsub: '''inside closure -hello world''' - cmd: "nim c --gc:orc -d:useMalloc $file" - valgrind: true -""" - -# bug #18579 - -var fp: proc (env: pointer) {.cdecl.} -var env: pointer - -proc store(f: proc (){.closure.}) = - proc closeOver() = - echo "inside closure" - f() - (fp,env) = (cast[proc(env: pointer){.cdecl.}](rawProc closeOver), rawEnv closeOver) - GC_ref(cast[RootRef](env)) - -proc run() = - fp(env) - GC_unref(cast[RootRef](env)) - -store(proc() = echo "hello world") -run() -GC_fullCollect() diff --git a/tests/arc/tweave.nim b/tests/arc/tweave.nim index 1c60ac403..220d65f97 100644 --- a/tests/arc/tweave.nim +++ b/tests/arc/tweave.nim @@ -8,9 +8,6 @@ discard """ import std/atomics -when defined(nimPreviewSlimSystem): - import std/[assertions, typedthreads] - const MemBlockSize = 256 type diff --git a/tests/arc/twrong_sinkinference.nim b/tests/arc/twrong_sinkinference.nim index ecf09d28a..59930a9fa 100644 --- a/tests/arc/twrong_sinkinference.nim +++ b/tests/arc/twrong_sinkinference.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim c --gc:arc $file" - errormsg: "type mismatch: got " + errormsg: "type mismatch: got " line: 18 """ diff --git a/tests/arithm/t1.nim b/tests/arithm/t1.nim deleted file mode 100644 index 36402da07..000000000 --- a/tests/arithm/t1.nim +++ /dev/null @@ -1,14 +0,0 @@ -doAssert typeOf(1.int64 + 1.int) is int64 -doAssert typeOf(1.uint64 + 1.uint) is uint64 -doAssert int64 is SomeNumber -doAssert int64 is (SomeNumber and not(uint32|uint64|uint|int)) -doAssert int64 is (not(uint32|uint64|uint|int)) -doAssert int isnot int64 -doAssert int64 isnot int -var myInt16 = 5i16 -var myInt: int -doAssert typeOf(myInt16 + 34) is int16 # of type `int16` -doAssert typeOf(myInt16 + myInt) is int # of type `int` -doAssert typeOf(myInt16 + 2i32) is int32 # of type `int32` -doAssert int32 isnot int64 -doAssert int32 isnot int diff --git a/tests/arithm/tarithm.nim b/tests/arithm/tarithm.nim index d0943d225..99306b3e8 100644 --- a/tests/arithm/tarithm.nim +++ b/tests/arithm/tarithm.nim @@ -22,10 +22,10 @@ import typetraits block tand: # bug #5216 - echo(name typeof((0x0A'i8 and 0x7F'i32) shl 7'i32)) + echo(name type((0x0A'i8 and 0x7F'i32) shl 7'i32)) let i8 = 0x0A'i8 - echo(name typeof((i8 and 0x7F'i32) shl 7'i32)) + echo(name type((i8 and 0x7F'i32) shl 7'i32)) echo((0x0A'i8 and 0x7F'i32) shl 7'i32) diff --git a/tests/arithm/tdiv.nim b/tests/arithm/tdiv.nim deleted file mode 100644 index 5d8eed84d..000000000 --- a/tests/arithm/tdiv.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - targets: "c js" -""" - - -block divUint64: - proc divTest() = - let x1 = 12'u16 - let y = x1 div 5'u16 - let x2 = 1345567'u32 - let z = x2 div 5'u32 - let a = 1345567'u64 div uint64(x1) - doAssert y == 2 - doAssert z == 269113 - doAssert a == 112130 - - static: divTest() - divTest() - diff --git a/tests/array/t15117.nim b/tests/array/t15117.nim deleted file mode 100644 index 157b04bee..000000000 --- a/tests/array/t15117.nim +++ /dev/null @@ -1,27 +0,0 @@ -discard """ - matrix: "--cc:vcc" - disabled: "linux" - disabled: "bsd" - disabled: "osx" - disabled: "unix" - disabled: "posix" -""" -{.experimental: "views".} - -let a: array[0, byte] = [] -discard a - -type B = object - a:int -let b: array[0, B] = [] -let c: array[0, ptr B] = [] -let d: array[0, ref B] = [] -discard b -discard c -discard d - -discard default(array[0, B]) - -type - View1 = openArray[byte] -discard default(View1) diff --git a/tests/array/t20248.nim b/tests/array/t20248.nim deleted file mode 100644 index 66142548b..000000000 --- a/tests/array/t20248.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ -cmd: "nim check --hints:off $file" -errormsg: "ordinal type expected; given: Error Type" -nimout: ''' -t20248.nim(10, 36) Error: ordinal type expected; given: Error Type -t20248.nim(14, 20) Error: ordinal type expected; given: Error Type -''' -""" - -type Vec[N: static[int]] = array[0 ..< N, float] - -var v: Vec[32] - -var stuff: array[0 ..< 16, int] diff --git a/tests/array/t9932.nim b/tests/array/t9932.nim index e3c8abba3..1e09c487b 100644 --- a/tests/array/t9932.nim +++ b/tests/array/t9932.nim @@ -1,9 +1,9 @@ discard """ cmd: "nim check $file" -errormsg: "invalid type: 'typedesc[int]' in this context: 'array[0..0, typedesc[int]]' for var" +errormsg: "invalid type: 'type int' in this context: 'array[0..0, type int]' for var" nimout: ''' t9932.nim(10, 5) Error: invalid type: 'type' in this context: 'array[0..0, type]' for var -t9932.nim(11, 5) Error: invalid type: 'typedesc[int]' in this context: 'array[0..0, typedesc[int]]' for var +t9932.nim(11, 5) Error: invalid type: 'type int' in this context: 'array[0..0, type int]' for var ''' """ diff --git a/tests/array/tarray.nim b/tests/array/tarray.nim index e9f330e3b..eadb53ac1 100644 --- a/tests/array/tarray.nim +++ b/tests/array/tarray.nim @@ -44,7 +44,7 @@ block tarray: arr: TMyarray - proc sum(a: openArray[int]): int = + proc sum(a: openarray[int]): int = result = 0 var i = 0 while i < len(a): @@ -344,11 +344,11 @@ block troofregression: if $a != b: echo "Failure ", a, " != ", b - check typeof(4 ...< 1), "HSlice[system.int, system.int]" - check typeof(4 ...< ^1), "HSlice[system.int, system.BackwardsIndex]" - check typeof(4 ... pred(^1)), "HSlice[system.int, system.BackwardsIndex]" - check typeof(4 ... mypred(8)), "HSlice[system.int, system.int]" - check typeof(4 ... mypred(^1)), "HSlice[system.int, system.BackwardsIndex]" + check type(4 ...< 1), "HSlice[system.int, system.int]" + check type(4 ...< ^1), "HSlice[system.int, system.BackwardsIndex]" + check type(4 ... pred(^1)), "HSlice[system.int, system.BackwardsIndex]" + check type(4 ... mypred(8)), "HSlice[system.int, system.int]" + check type(4 ... mypred(^1)), "HSlice[system.int, system.BackwardsIndex]" var rot = 8 @@ -587,21 +587,3 @@ block t12466: a[0'u16 + i] = i for i in 0'u16 ..< 8'u16: a[0'u16 + i] = i - -block t17705: - # https://github.com/nim-lang/Nim/pull/17705 - var a = array[0, int].low - a = int(a) - var b = array[0, int].high - b = int(b) - -block t18643: - # https://github.com/nim-lang/Nim/issues/18643 - let a: array[0, int] = [] - var caught = false - let b = 9999999 - try: - echo a[b] - except IndexDefect: - caught = true - doAssert caught, "IndexDefect not caught!" diff --git a/tests/assert/panicoverride.nim b/tests/assert/panicoverride.nim deleted file mode 100644 index 53ad64215..000000000 --- a/tests/assert/panicoverride.nim +++ /dev/null @@ -1,15 +0,0 @@ -# panicoverride.nim - -proc printf(fmt: cstring) {.varargs, importc, header:"stdio.h".} -proc exit(code: cint) {.importc, header:"stdlib.h".} - -{.push stack_trace: off, profiler:off.} - -proc rawoutput(s: cstring) = - printf("RAW: %s\n", s) - -proc panic(s: cstring) {.noreturn.} = - printf("PANIC: %s\n", s) - exit(0) - -{.pop.} \ No newline at end of file diff --git a/tests/assert/t21195.nim b/tests/assert/t21195.nim deleted file mode 100644 index b690d22a0..000000000 --- a/tests/assert/t21195.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - matrix: "--verbosity:0 --os:standalone --mm:none" -""" -# bug #21195 -var n = 11 -assert(n == 12) diff --git a/tests/assert/tassert2.nim b/tests/assert/tassert2.nim index e32ab72e1..5d849aaad 100644 --- a/tests/assert/tassert2.nim +++ b/tests/assert/tassert2.nim @@ -24,7 +24,7 @@ except AssertionDefect as e: try: assert false # assert test with no msg except AssertionDefect as e: - assert e.msg.endsWith "tassert2.nim(25, 3) `false` " + assert e.msg.endsWith "tassert2.nim(25, 10) `false` " try: let a = 1 @@ -100,7 +100,7 @@ block: ## checks for issue https://github.com/nim-lang/Nim/issues/9301 doAssert 1 + 1 == 3 except AssertionDefect as e: # used to const fold as false - assert e.msg.endsWith "tassert2.nim(100, 5) `1 + 1 == 3` " + assert e.msg.endsWith "tassert2.nim(100, 14) `1 + 1 == 3` " block: ## checks AST isn't transformed as it used to let a = 1 @@ -108,4 +108,4 @@ block: ## checks AST isn't transformed as it used to doAssert a > 1 except AssertionDefect as e: # used to rewrite as `1 < a` - assert e.msg.endsWith "tassert2.nim(108, 5) `a > 1` " + assert e.msg.endsWith "tassert2.nim(108, 14) `a > 1` " diff --git a/tests/assert/tassert_c.nim b/tests/assert/tassert_c.nim index e3e3b8147..024175cbf 100644 --- a/tests/assert/tassert_c.nim +++ b/tests/assert/tassert_c.nim @@ -1,14 +1,14 @@ discard """ - matrix: "-d:nimPreviewSlimSystem --stackTrace:on --excessiveStackTrace:off" + cmd: "nim $target $options --excessiveStackTrace:off $file" output: '''true''' """ -import std/assertions + const expected = """ tassert_c.nim(35) tassert_c tassert_c.nim(34) foo -assertions.nim(*) failedAssertImpl -assertions.nim(*) raiseAssert -""" +assertions.nim(30) failedAssertImpl +assertions.nim(23) raiseAssert +fatal.nim(53) sysFatal""" proc tmatch(x, p: string): bool = var i = 0 diff --git a/tests/assign/moverload_asgn2.nim b/tests/assign/moverload_asgn2.nim index cfea48cd1..6620adbeb 100644 --- a/tests/assign/moverload_asgn2.nim +++ b/tests/assign/moverload_asgn2.nim @@ -1,7 +1,3 @@ -discard """ - matrix: "--mm:refc" -""" - type Concrete* = object a*, b*: string diff --git a/tests/assign/tassign.nim b/tests/assign/tassign.nim index fdec04d22..c95114015 100644 --- a/tests/assign/tassign.nim +++ b/tests/assign/tassign.nim @@ -80,7 +80,7 @@ block tcopy: block tgenericassign: type - TAny {.pure.} = object + TAny = object {.pure.} value: pointer rawType: pointer @@ -178,7 +178,7 @@ when false: proc `=`[T](d: var GenericT[T]; src: GenericT[T]) = shallowCopy(d.a, src.a) shallowCopy(d.b, src.b) - echo "GenericT[T] '=' ", typeof(T).name + echo "GenericT[T] '=' ", type(T).name var ag: GenericT[int] var bg: GenericT[int] diff --git a/tests/assign/tobject_assign.nim b/tests/assign/tobject_assign.nim deleted file mode 100644 index 8e39ead53..000000000 --- a/tests/assign/tobject_assign.nim +++ /dev/null @@ -1,49 +0,0 @@ -# bug #16706 - -block: # reduced example - type - A = object of RootObj - a0: string - B = object - b0: seq[A] - var c = newSeq[A](2) - var d = B(b0: c) - -when true: # original example - import std/[options, tables, times] - - type - Data* = object - shifts*: OrderedTable[int64, Shift] - balance*: float - - Shift* = object - quoted*: bool - date*: DateTime - description*: string - start*: Option[DateTime] - finish*: Option[DateTime] - breakTime*: Option[Duration] - rate*: float - qty: Option[float] - id*: int64 - - let shift = Shift( - quoted: true, - date: parse("2000-01-01", "yyyy-MM-dd"), - description: "abcdef", - start: none(DateTime), - finish: none(DateTime), - breakTime: none(Duration), - rate: 462.11, - qty: some(10.0), - id: getTime().toUnix() - ) - - var shifts: OrderedTable[int64, Shift] - shifts[shift.id] = shift - - discard Data( - shifts: shifts, - balance: 0.00 - ) diff --git a/tests/ast_pattern_matching.nim b/tests/ast_pattern_matching.nim index 0ba0aa57a..c08234b9e 100644 --- a/tests/ast_pattern_matching.nim +++ b/tests/ast_pattern_matching.nim @@ -133,8 +133,8 @@ proc matchLengthKind*(arg: NimNode; kind: NimNodeKind; length: int): MatchingErr matchLengthKind(arg, {kind}, length) proc matchValue(arg: NimNode; kind: set[NimNodeKind]; value: SomeInteger): MatchingError {.compileTime.} = - template kindFail: bool = not(kind.card == 0 or arg.kind in kind) - template valueFail: bool = arg.intVal != int(value) + let kindFail = not(kind.card == 0 or arg.kind in kind) + let valueFail = arg.intVal != int(value) if kindFail or valueFail: result.node = arg result.kind = WrongKindValue @@ -561,7 +561,7 @@ when isMainModule: echo "got the ident m" testRecCase: - type Obj[T] {.inheritable.} = object + type Obj[T] = object {.inheritable.} name: string case isFat: bool of true: diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim index c99d8ec79..3413d32f3 100644 --- a/tests/astspec/tastspec.nim +++ b/tests/astspec/tastspec.nim @@ -327,43 +327,19 @@ static: testArrayAccessOperator(x[y]) + + ## Parentheses scope: + let ast = myquote: - (a + b) * c + (1, 2, (3)) ast.matchAst: - of nnkInfix(ident"*", nnkPar(nnkInfix(ident"+", ident"a", ident"b")), ident"c"): - echo "parentheses ok" + of nnkPar(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkPar(nnkIntLit(intVal = 3))): + echo "ok" - ## Tuple Constructors - - scope: - let ast = myquote: - (1, 2, 3) - (a: 1, b: 2, c: 3) - (1,) - (a: 1) - () - - for it in ast: - echo it.lispRepr - it.matchAst: - of nnkTupleConstr(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)): - echo "simple tuple ok" - of nnkTupleConstr( - nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1)), - nnkExprColonExpr(ident"b", nnkIntLit(intVal = 2)), - nnkExprColonExpr(ident"c", nnkIntLit(intVal = 3)) - ): - echo "named tuple ok" - of nnkTupleConstr(nnkIntLit(intVal = 1)): - echo "one tuple ok" - of nnkTupleConstr(nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1))): - echo "named one tuple ok" - of nnkTupleConstr(): - echo "empty tuple ok" ## Curly braces @@ -871,20 +847,11 @@ static: scope: macro testRecCase(ast: untyped): untyped = - ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst: - of nnkTypeDef( - nnkPragmaExpr( - ident"Obj", - nnkPragma(ident"inheritable") + ast.peelOff({nnkStmtList, nnkTypeSection})[2].matchAst: + of nnkObjectTy( + nnkPragma( + ident"inheritable" ), - nnkGenericParams( - nnkIdentDefs( - ident"T", - nnkEmpty(), - nnkEmpty()) - ), - nnkObjectTy( - nnkEmpty(), nnkEmpty(), nnkRecList( # list of object parameters nnkIdentDefs( @@ -923,7 +890,6 @@ static: ident"T" ), nnkEmpty() - ) ) ) ) @@ -932,8 +898,10 @@ static: ): echo "ok" + + testRecCase: - type Obj[T] {.inheritable.} = object + type Obj[T] = object {.inheritable.} name: string case isFat: bool of true: @@ -961,7 +929,7 @@ static: (x & y & z) is string ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst: - of nnkTypeDef(_, _, nnkTypeClassTy(nnkArgList, _, _, nnkStmtList)): + of nnkTypeDef(_, _, nnkTypeClassTy(nnkArglist, _, _, nnkStmtList)): # note this isn't nnkConceptTy! echo "ok" diff --git a/tests/async/nim.cfg b/tests/async/nim.cfg deleted file mode 100644 index be50f572c..000000000 --- a/tests/async/nim.cfg +++ /dev/null @@ -1 +0,0 @@ ---experimental:strictEffects diff --git a/tests/async/t11558.nim b/tests/async/t11558.nim deleted file mode 100644 index 99961e7b6..000000000 --- a/tests/async/t11558.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ -output: "Hello\n9" -""" -import std/asyncdispatch - -proc foo(): Future[string] {.async.} = - "Hello" - -proc bar(): Future[int] {.async.} = - result = 9 - -echo waitFor foo() -echo waitFor bar() diff --git a/tests/async/t20111.nim b/tests/async/t20111.nim deleted file mode 100644 index 0aaa7d886..000000000 --- a/tests/async/t20111.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - action: "run" -""" -import asyncdispatch -type - Sync = object - Async = object - SyncRes = (Sync, string) - AsyncRes = (Async, string) - -proc foo(val: Sync | Async): Future[(Async, string) | (Sync, string)] {.multisync.} = - return (val, "hello") - -let - myAsync = Async() - mySync = Sync() - -doAssert typeof(waitFor foo(myAsync)) is AsyncRes -doAssert typeof(foo(mySync)) is SyncRes diff --git a/tests/async/t21447.nim b/tests/async/t21447.nim deleted file mode 100644 index e4f7ae31f..000000000 --- a/tests/async/t21447.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - action: "compile" - cmd: "nim c -d:release -d:futureLogging $file" -""" - -import std/asyncdispatch diff --git a/tests/async/tasync_gcunsafe.nim b/tests/async/tasync_gcunsafe.nim index f3e6bc691..00c92b109 100644 --- a/tests/async/tasync_gcunsafe.nim +++ b/tests/async/tasync_gcunsafe.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'anotherGCSafeAsyncProc (Async)' is not GC-safe as it calls 'asyncGCUnsafeProc'" + errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'" cmd: "nim c --threads:on $file" file: "asyncmacro.nim" """ diff --git a/tests/async/tasync_noasync.nim b/tests/async/tasync_noasync.nim index 0927148bf..54f4f597f 100644 --- a/tests/async/tasync_noasync.nim +++ b/tests/async/tasync_noasync.nim @@ -1,42 +1,13 @@ discard """ - cmd: "nim check --hints:off --warnings:off $file" - action: "reject" - nimout: ''' -tasync_noasync.nim(21, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead -tasync_noasync.nim(25, 12) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead -tasync_noasync.nim(28, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead -tasync_noasync.nim(31, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead -tasync_noasync.nim(35, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead -tasync_noasync.nim(38, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead -tasync_noasync.nim(40, 8) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead -''' + errormsg: "'yield' only allowed in an iterator" + cmd: "nim c $file" + file: "asyncmacro.nim" """ import async proc a {.async.} = discard -# Bad await usage -proc nonAsyncProc = - await a() - -proc nestedNonAsyncProc {.async.} = - proc nested = - await a() - -iterator customIterator: int = - await a() - -macro awaitInMacro = - await a() - -type DummyRef = ref object of RootObj -method awaitInMethod(_: DummyRef) {.base.} = - await a() - -proc improperMultisync {.multisync.} = - await a() - await a() # if we overload a fallback handler to get diff --git a/tests/async/tasync_traceback.nim b/tests/async/tasync_traceback.nim index 98f71b192..9f787929b 100644 --- a/tests/async/tasync_traceback.nim +++ b/tests/async/tasync_traceback.nim @@ -1,5 +1,6 @@ discard """ exitcode: 0 + disabled: "windows" output: "Matched" """ import asyncdispatch, strutils @@ -67,24 +68,53 @@ import re const expected = """ b failure Async traceback: - tasync_traceback\.nim\(\d+?\) tasync_traceback - tasync_traceback\.nim\(\d+?\) a \(Async\) - tasync_traceback\.nim\(\d+?\) b \(Async\) + tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback + asyncmacro\.nim\(\d+?\)\s+?a + asyncmacro\.nim\(\d+?\)\s+?aNimAsyncContinue + ## Resumes an async procedure + tasync_traceback\.nim\(\d+?\)\s+?aIter + asyncmacro\.nim\(\d+?\)\s+?b + asyncmacro\.nim\(\d+?\)\s+?bNimAsyncContinue + ## Resumes an async procedure + tasync_traceback\.nim\(\d+?\)\s+?bIter + #\[ + tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback + asyncmacro\.nim\(\d+?\)\s+?a + asyncmacro\.nim\(\d+?\)\s+?aNimAsyncContinue + ## Resumes an async procedure + asyncmacro\.nim\(\d+?\)\s+?aIter + asyncfutures\.nim\(\d+?\)\s+?read + \]# Exception message: b failure - +Exception type: bar failure Async traceback: - tasync_traceback\.nim\(\d+?\) tasync_traceback - asyncdispatch\.nim\(\d+?\) waitFor - asyncdispatch\.nim\(\d+?\) poll + tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback + asyncdispatch\.nim\(\d+?\)\s+?waitFor + asyncdispatch\.nim\(\d+?\)\s+?poll ## Processes asynchronous completion events - asyncdispatch\.nim\(\d+?\) runOnce - asyncdispatch\.nim\(\d+?\) processPendingCallbacks + asyncdispatch\.nim\(\d+?\)\s+?runOnce + asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks ## Executes pending callbacks - tasync_traceback\.nim\(\d+?\) bar \(Async\) + asyncmacro\.nim\(\d+?\)\s+?barNimAsyncContinue + ## Resumes an async procedure + tasync_traceback\.nim\(\d+?\)\s+?barIter + #\[ + tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback + asyncdispatch\.nim\(\d+?\)\s+?waitFor + asyncdispatch\.nim\(\d+?\)\s+?poll + ## Processes asynchronous completion events + asyncdispatch\.nim\(\d+?\)\s+?runOnce + asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks + ## Executes pending callbacks + asyncmacro\.nim\(\d+?\)\s+?fooNimAsyncContinue + ## Resumes an async procedure + asyncmacro\.nim\(\d+?\)\s+?fooIter + asyncfutures\.nim\(\d+?\)\s+?read + \]# Exception message: bar failure - +Exception type: """ # TODO: is asyncmacro good enough location for fooIter traceback/debugging? just put the callsite info for all? @@ -92,31 +122,28 @@ Exception message: bar failure let resLines = splitLines(result.strip) let expLines = splitLines(expected.strip) -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) +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 +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: +if ok: echo("Matched") +else: + quit(QuitFailure) diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index e86542b2d..f658a15ed 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -34,7 +34,7 @@ proc readMessages(client: AsyncFD) {.async.} = clientCount.inc break else: - if line.startsWith("Message "): + if line.startswith("Message "): msgCount.inc else: doAssert false diff --git a/tests/async/tasyncdial.nim b/tests/async/tasyncdial.nim index f0377dfd5..a755de74e 100644 --- a/tests/async/tasyncdial.nim +++ b/tests/async/tasyncdial.nim @@ -3,6 +3,7 @@ discard """ OK AF_INET OK AF_INET6 ''' + disabled: "travis" """ import diff --git a/tests/async/tasyncintemplate.nim b/tests/async/tasyncintemplate.nim deleted file mode 100644 index 4bddb1d18..000000000 --- a/tests/async/tasyncintemplate.nim +++ /dev/null @@ -1,62 +0,0 @@ -discard """ - output: ''' -42 -43 -43 -1 -2 -3 -4 -''' -""" - -# xxx move to tests/async/tasyncintemplate.nim -import asyncdispatch - -block: # bug #16159 - template foo() = - proc temp(): Future[int] {.async.} = return 42 - proc tempVoid(): Future[void] {.async.} = echo await temp() - foo() - waitFor tempVoid() - -block: # aliasing `void` - template foo() = - type Foo = void - proc temp(): Future[int] {.async.} = return 43 - proc tempVoid(): Future[Foo] {.async.} = echo await temp() - proc tempVoid2() {.async.} = echo await temp() - foo() - waitFor tempVoid() - waitFor tempVoid2() - -block: # sanity check - template foo() = - proc bad(): int {.async.} = discard - doAssert not compiles(bad()) - -block: # bug #16786 - block: - proc main(a: int|string)= - proc bar(b: int|string) = echo b - bar(a) - main(1) - - block: - proc main(a: int) : Future[void] {.async.} = - proc bar(b: int): Future[void] {.async.} = echo b - await bar(a) - waitFor main(2) - - block: - proc main(a: int) : Future[void] {.async.} = - proc bar(b: int | string): Future[void] {.async.} = echo b - await bar(a) - waitFor main(3) - - block: - # bug - proc main(a: int|string) = - proc bar(b: int): Future[void] {.async.} = echo b - waitFor bar(a) - main(4) diff --git a/tests/async/tasyncnetudp.nim b/tests/async/tasyncnetudp.nim index dade96fb2..ef6dfc5e1 100644 --- a/tests/async/tasyncnetudp.nim +++ b/tests/async/tasyncnetudp.nim @@ -59,7 +59,7 @@ proc readMessages(server: AsyncSocket) {.async.} = while i < maxResponses: let (data, fromIp, fromPort) = await recvFrom(server, 16384) - if data.startsWith("Message ") and fromIp == "127.0.0.1": + if data.startswith("Message ") and fromIp == "127.0.0.1": await sendTo(server, fromIp, fromPort, data) inc(msgCount) diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim index 222aaa3a1..a582818eb 100644 --- a/tests/async/tasyncssl.nim +++ b/tests/async/tasyncssl.nim @@ -36,7 +36,7 @@ when defined(ssl): inc(clientCount) break else: - if line.startsWith("Message "): + if line.startswith("Message "): inc(msgCount) else: doAssert false diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim index 77d03f8f6..f7487525a 100644 --- a/tests/async/tioselectors.nim +++ b/tests/async/tioselectors.nim @@ -234,7 +234,7 @@ when not defined(windows): if fd == -1: raiseOsError(osLastError()) let length = len(data).cint - if posix.write(fd, cast[pointer](addr data[0]), + if posix.write(fd, cast[pointer](unsafeAddr data[0]), len(data).cint) != length: raiseOsError(osLastError()) if posix.close(fd) == -1: @@ -288,8 +288,8 @@ when not defined(windows): events: set[Event] proc vnode_test(): bool = - proc validate(test: openArray[ReadyKey], - check: openArray[valType]): bool = + proc validate(test: openarray[ReadyKey], + check: openarray[valType]): bool = result = false if len(test) == len(check): for checkItem in check: diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim index 68de796a0..76462c21d 100644 --- a/tests/async/tnewasyncudp.nim +++ b/tests/async/tnewasyncudp.nim @@ -58,7 +58,7 @@ proc launchSwarm(name: ptr SockAddr) {.async.} = 16384, cast[ptr SockAddr](addr saddr), addr slen) size = 0 - var grammString = $cast[cstring](addr buffer) + var grammString = $cstring(addr buffer) if grammString == message: saveSendingPort(sockport) inc(recvCount) @@ -80,8 +80,8 @@ proc readMessages(server: AsyncFD) {.async.} = 16384, cast[ptr SockAddr](addr(saddr)), addr(slen)) size = 0 - var grammString = $cast[cstring](addr buffer) - if grammString.startsWith("Message ") and + var grammString = $cstring(addr buffer) + if grammString.startswith("Message ") and saddr.sin_addr.s_addr == nativesockets.ntohl(INADDR_LOOPBACK.uint32): await sendTo(server, addr grammString[0], len(grammString), cast[ptr SockAddr](addr saddr), slen) diff --git a/tests/async/twinasyncrw.nim b/tests/async/twinasyncrw.nim index ad27ca38f..69b092607 100644 --- a/tests/async/twinasyncrw.nim +++ b/tests/async/twinasyncrw.nim @@ -220,7 +220,7 @@ when defined(windows): clientCount.inc break else: - if line.startsWith("Message "): + if line.startswith("Message "): msgCount.inc else: doAssert false diff --git a/tests/avr/nim.cfg b/tests/avr/nim.cfg deleted file mode 100644 index d6eba8eda..000000000 --- a/tests/avr/nim.cfg +++ /dev/null @@ -1,12 +0,0 @@ -avr.standalone.gcc.path = "/usr/bin" -avr.standalone.gcc.exe = "avr-gcc" -avr.standalone.gcc.linkerexe = "avr-gcc" -passC = "-Os" -passC = "-DF_CPU=16000000UL" -passC = "-mmcu=atmega328p" -passL = "-mmcu=atmega328p" -passC = "-flto" -passL = "-flto" -cpu = "avr" -deadCodeElim = "on" -gc = "arc" diff --git a/tests/avr/panicoverride.nim b/tests/avr/panicoverride.nim deleted file mode 100644 index 770933ddd..000000000 --- a/tests/avr/panicoverride.nim +++ /dev/null @@ -1,13 +0,0 @@ -proc printf(frmt: cstring) {.varargs, importc, header: "", cdecl.} -proc exit(code: int) {.importc, header: "", cdecl.} - -{.push stack_trace: off, profiler:off.} - -proc rawoutput(s: string) = - printf("%s\n", s) - -proc panic(s: string) = - rawoutput(s) - exit(1) - -{.pop.} diff --git a/tests/avr/thello.nim b/tests/avr/thello.nim deleted file mode 100644 index 7ebaeae5f..000000000 --- a/tests/avr/thello.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - cmd: "nim c --compileOnly --os:standalone --exceptions:quirky -d:noSignalHandler -d:danger --threads:off $file" - action: "compile" -""" - -echo "hi" diff --git a/tests/benchmarks/readme.md b/tests/benchmarks/readme.md deleted file mode 100644 index 1e744fc40..000000000 --- a/tests/benchmarks/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -# Collection of benchmarks - -In future work, benchmarks can be added to CI, but for now we provide benchmarks that can be run locally. - -See RFC: https://github.com/timotheecour/Nim/issues/425 diff --git a/tests/benchmarks/ttls.nim b/tests/benchmarks/ttls.nim deleted file mode 100644 index f5314850f..000000000 --- a/tests/benchmarks/ttls.nim +++ /dev/null @@ -1,30 +0,0 @@ -discard """ - action: compile -""" - -#[ -## on osx -nim r -d:danger --threads --tlsEmulation:off tests/benchmarks/ttls.nim -9.999999999992654e-07 - -ditto with `--tlsEmulation:on`: -0.216999 -]# - -import times - -proc main2(): int = - var g0 {.threadvar.}: int - g0.inc - result = g0 - -proc main = - let n = 100_000_000 - var c = 0 - let t = cpuTime() - for i in 0.." + errormsg: "type mismatch: got " line: 10 """ diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/bind/tnicerrorforsymchoice.nim index 6001b6b09..684b83239 100644 --- a/tests/bind/tnicerrorforsymchoice.nim +++ b/tests/bind/tnicerrorforsymchoice.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch: got " + errormsg: "type mismatch: got " line: 23 """ diff --git a/tests/borrow/tborrow.nim b/tests/borrow/tborrow.nim index 35652e2e0..9c403021e 100644 --- a/tests/borrow/tborrow.nim +++ b/tests/borrow/tborrow.nim @@ -33,59 +33,3 @@ let b = Radians(1.0) a -= b echo a.float64 - -block: #14449 - type - Foo[T] = object - foo: T - - Bar[T] {.borrow:`.`.} = distinct Foo[T] - SomeThing {.borrow:`.`.} = distinct Foo[float] - OtherThing {.borrow:`.`.} = distinct SomeThing - - var - a: Bar[int] - b: SomeThing - c: OtherThing - a.foo = 300 - b.foo = 400 - c.foo = 42 - assert a.foo == 300 - assert b.foo == 400d - assert c.foo == 42d - -block: # Borrow from muliple aliasses #16666 - type - AImpl = object - i: int - - A = AImpl - - B {.borrow: `.`.} = distinct A - C = B - D {.borrow: `.`.} = distinct C - E {.borrow: `.`.} = distinct D - - let - b = default(B) - d = default(D) - e = default(E) - - assert b.i == 0 - assert d.i == 0 - assert e.i == 0 - -block: # Borrow from generic alias - type - AImpl[T] = object - i: T - B[T] = AImpl[T] - C {.borrow: `.`.} = distinct B[int] - D = B[float] - E {.borrow: `.`.} = distinct D - - let - c = default(C) - e = default(E) - assert c.i == int(0) - assert e.i == 0d \ No newline at end of file diff --git a/tests/borrow/tinvalidborrow.nim b/tests/borrow/tinvalidborrow.nim index 08148608d..89aa4e2e8 100644 --- a/tests/borrow/tinvalidborrow.nim +++ b/tests/borrow/tinvalidborrow.nim @@ -1,25 +1,16 @@ discard """ - cmd: "nim check --hints:off --warnings:off $file" - action: "reject" - nimout:''' -tinvalidborrow.nim(18, 3) Error: only a 'distinct' type can borrow `.` -tinvalidborrow.nim(19, 3) Error: only a 'distinct' type can borrow `.` -tinvalidborrow.nim(20, 1) Error: no symbol to borrow from found -''' + errormsg: "no symbol to borrow from found" + line: 11 """ # bug #516 type TAtom = culong - Test {.borrow:`.`.} = distinct int - Foo[T] = object - a: int - Bar[T] {.borrow:`.`.} = Foo[T] - OtherFoo {.borrow:`.`.} = Foo[int] + proc `==`*(a, b: TAtom): bool {.borrow.} var d, e: TAtom -discard( $(d == e) ) +echo( $(d == e) ) diff --git a/tests/borrow/typeclassborrow.nim b/tests/borrow/typeclassborrow.nim deleted file mode 100644 index ee0b07829..000000000 --- a/tests/borrow/typeclassborrow.nim +++ /dev/null @@ -1,48 +0,0 @@ -type - Foo = distinct seq[int] - Bar[N: static[int]] = distinct seq[int] - Baz = distinct Bar[10] - -proc newSeq(s: var Foo, n: Natural) {.borrow.} -proc newSeq(s: var Bar, n: Natural) {.borrow.} -proc newSeq(s: var Baz, n: Natural) {.borrow.} - - -proc `$`(s: Foo): string {.borrow.} -proc `$`(s: Bar): string {.borrow.} -proc `$`(s: Baz): string {.borrow.} - -proc doThing(b: Bar) = discard -proc doThing(b: Baz) {.borrow.} - -var - foo: Foo - bar: Bar[10] - baz: Baz - -newSeq(foo, 100) -newSeq(bar, bar.N) -newSeq(baz, 10) - -bar.doThing() -baz.doThing() - -assert $seq[int](foo) == $foo -assert $seq[int](bar) == $bar -assert $seq[int](baz) == $baz - -type - Fine* = distinct string - -proc `==`*(x, y: Fine): bool {.borrow.} = - ## Here is the documentation - runnableExamples: - var x = Fine("1234") - var y = Fine("1234") - doAssert x == y - doAssert false - - -var x = Fine("1234") -var y = Fine("1234") -doAssert x == y diff --git a/tests/casestmt/t18964.nim b/tests/casestmt/t18964.nim deleted file mode 100644 index 1d2de2bbc..000000000 --- a/tests/casestmt/t18964.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ -errormsg: "invalid order of case branches" -""" - -import macros - -macro genCase(val: string): untyped = - result = nnkCaseStmt.newTree(val, - nnkElse.newTree(quote do: echo "else"), - nnkOfBranch.newTree(newLit("miauz"), quote do: echo "first branch")) - -genCase("miauz") diff --git a/tests/casestmt/tcasestmt.nim b/tests/casestmt/tcasestmt.nim index 3a4907494..53cccdb64 100644 --- a/tests/casestmt/tcasestmt.nim +++ b/tests/casestmt/tcasestmt.nim @@ -287,14 +287,3 @@ 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 deleted file mode 100644 index 288373402..000000000 --- a/tests/casestmt/tcstring.nim +++ /dev/null @@ -1,52 +0,0 @@ -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/tincompletecaseobject.nim b/tests/casestmt/tincompletecaseobject.nim index aa5deda7a..909ee4e1c 100644 --- a/tests/casestmt/tincompletecaseobject.nim +++ b/tests/casestmt/tincompletecaseobject.nim @@ -1,6 +1,6 @@ discard """ errormsg: ''' -not all cases are covered; missing: {nnkComesFrom, nnkDotCall, nnkHiddenCallConv, nnkVarTuple, nnkCurlyExpr, nnkRange, nnkCheckedFieldExpr, nnkDerefExpr, nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkBind, nnkClosedSymChoice, nnkHiddenSubConv, nnkConv, nnkStaticExpr, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, nnkStringToCString, nnkCStringToString, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit, nnkImportAs, nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse, nnkAsmStmt, nnkTypeDef, nnkFinally, nnkContinueStmt, nnkImportStmt, nnkImportExceptStmt, nnkExportStmt, nnkExportExceptStmt, nnkFromStmt, nnkIncludeStmt, nnkUsingStmt, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkWith, nnkWithout, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkVarTy, nnkConstTy, nnkMutableTy, nnkDistinctTy, nnkProcTy, nnkIteratorTy, nnkSharedTy, nnkEnumTy, nnkEnumFieldDef, nnkArgList, nnkPattern, nnkReturnToken, nnkClosure, nnkGotoState, nnkState, nnkBreakState, nnkFuncDef, nnkTupleConstr} +not all cases are covered; missing: {nnkComesFrom, nnkDotCall, nnkHiddenCallConv, nnkVarTuple, nnkCurlyExpr, nnkRange, nnkCheckedFieldExpr, nnkDerefExpr, nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkBind, nnkClosedSymChoice, nnkHiddenSubConv, nnkConv, nnkStaticExpr, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, nnkStringToCString, nnkCStringToString, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit, nnkImportAs, nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse, nnkAsmStmt, nnkTypeDef, nnkFinally, nnkContinueStmt, nnkImportStmt, nnkImportExceptStmt, nnkExportStmt, nnkExportExceptStmt, nnkFromStmt, nnkIncludeStmt, nnkUsingStmt, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkWith, nnkWithout, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkVarTy, nnkConstTy, nnkMutableTy, nnkDistinctTy, nnkProcTy, nnkIteratorTy, nnkSharedTy, nnkEnumTy, nnkEnumFieldDef, nnkArglist, nnkPattern, nnkReturnToken, nnkClosure, nnkGotoState, nnkState, nnkBreakState, nnkFuncDef, nnkTupleConstr} ''' """ @@ -62,7 +62,7 @@ type nnkSharedTy, # 'shared T' nnkEnumTy, nnkEnumFieldDef, - nnkArgList, nnkPattern + nnkArglist, nnkPattern nnkReturnToken, nnkClosure, nnkGotoState, diff --git a/tests/casestmt/tincompletecaseobject2.nim b/tests/casestmt/tincompletecaseobject2.nim index bbeae1909..c080cfeb1 100644 --- a/tests/casestmt/tincompletecaseobject2.nim +++ b/tests/casestmt/tincompletecaseobject2.nim @@ -1,5 +1,12 @@ 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 @@ -8,19 +15,15 @@ type AliasRangeABC = RangeABC PrintableChars = range[' ' .. '~'] -case PrintableChars 'x': #[tt.Error -^ not all cases are covered; missing: {' ', '!', '\"', '#', '$$', '%', '&', '\'', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~'}]# +case PrintableChars 'x': of '0'..'9', 'A'..'Z', 'a'..'z': discard of '(', ')': discard -case AliasABCD A: #[tt.Error -^ not all cases are covered; missing: {B, C, D}]# +case AliasABCD A: of A: discard -case RangeABC A: #[tt.Error -^ not all cases are covered; missing: {A, C}]# +case RangeABC A: of B: discard -case AliasRangeABC A: #[tt.Error -^ not all cases are covered; missing: {A, B}]# +case AliasRangeABC A: of C: discard diff --git a/tests/ccgbugs/m1/defs.nim b/tests/ccgbugs/m1/defs.nim deleted file mode 100644 index ed78d8b72..000000000 --- a/tests/ccgbugs/m1/defs.nim +++ /dev/null @@ -1,4 +0,0 @@ -type MyObj* = object - field1*: int - s*: string - ch*: char diff --git a/tests/ccgbugs/m19445.c b/tests/ccgbugs/m19445.c deleted file mode 100644 index 74c23d4b4..000000000 --- a/tests/ccgbugs/m19445.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "m19445.h" - -const Foo f = {10, 20, 30, 40}; \ No newline at end of file diff --git a/tests/ccgbugs/m2/defs.nim b/tests/ccgbugs/m2/defs.nim deleted file mode 100644 index 798d1fea8..000000000 --- a/tests/ccgbugs/m2/defs.nim +++ /dev/null @@ -1,4 +0,0 @@ -type MyObj* = object - s*: string - field1*: int - ch*: char diff --git a/tests/ccgbugs/t10128.nim b/tests/ccgbugs/t10128.nim deleted file mode 100644 index 48970916f..000000000 --- a/tests/ccgbugs/t10128.nim +++ /dev/null @@ -1,18 +0,0 @@ -# bug #10128 -let data = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" -var seq2 = newSeq[char](data.len) -for i in 0.. 0: - BlockSlotId.init(x.bid, x.slot) - else: - BlockSlotId.init(x.bid, x.slot) - doAssert x.bid.root.data[0] == 42 - -bug19613() diff --git a/tests/ccgbugs/tcodegenbug1.nim b/tests/ccgbugs/tcodegenbug1.nim index d2ab97ede..c62bae1ef 100644 --- a/tests/ccgbugs/tcodegenbug1.nim +++ b/tests/ccgbugs/tcodegenbug1.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" output: '''obj = (inner: (kind: Just, id: 7)) obj.inner.id = 7 id = 7 diff --git a/tests/ccgbugs/tctypes.nim b/tests/ccgbugs/tctypes.nim deleted file mode 100644 index be6009115..000000000 --- a/tests/ccgbugs/tctypes.nim +++ /dev/null @@ -1,43 +0,0 @@ -discard """ - targets: "c cpp" - matrix: "--gc:refc; --gc:arc" -""" - -# bug #7308 -proc foo(x: seq[int32]) = - var y = newSeq[cint](1) - -proc bar = - var t = newSeq[int32](1) - foo(t) - -bar() - - -# bug #16246 - -proc testWeirdTypeAliases() = - var values = newSeq[cuint](8) - # var values: seq[cuint] does not produce codegen error - var drawCb = proc(): seq[uint32] = - result = newSeq[uint32](10) - -testWeirdTypeAliases() - -block: # bug #11797 - block: - type cdouble2 = cdouble - type Foo1 = seq[cdouble] - type Foo2 = seq[cdouble2] - static: doAssert Foo1 is Foo2 - var a1: Foo1 - var a2: Foo2 - doAssert a1 == @[] - doAssert a2 == @[] - - block: - proc foo[T: int|cint](fun: proc(): T) = discard - proc foo1(): cint = 1 - proc foo3(): int32 = 2 - foo(proc(): cint = foo1()) - foo(proc(): int32 = foo3()) diff --git a/tests/ccgbugs/tdeepcopy_addr_rval.nim b/tests/ccgbugs/tdeepcopy_addr_rval.nim index 4a0b0deaa..07fb8f8ef 100644 --- a/tests/ccgbugs/tdeepcopy_addr_rval.nim +++ b/tests/ccgbugs/tdeepcopy_addr_rval.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc; --mm:orc --deepcopy:on" output: "3" """ diff --git a/tests/ccgbugs/tderefblock.nim b/tests/ccgbugs/tderefblock.nim deleted file mode 100644 index fd21a19b8..000000000 --- a/tests/ccgbugs/tderefblock.nim +++ /dev/null @@ -1,25 +0,0 @@ -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 d62c36b18..74fbae303 100644 --- a/tests/ccgbugs/tforward_decl_only.nim +++ b/tests/ccgbugs/tforward_decl_only.nim @@ -1,6 +1,7 @@ discard """ ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')" ccodecheck: "\\i !@('mymoduleInit')" +ccodecheck: "\\i @('mymoduleDatInit')" output: "hello" """ diff --git a/tests/ccgbugs/thtiobj.nim b/tests/ccgbugs/thtiobj.nim index 6db24dad0..7a656905f 100644 --- a/tests/ccgbugs/thtiobj.nim +++ b/tests/ccgbugs/thtiobj.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" targets: "c cpp" """ diff --git a/tests/ccgbugs/tissues.nim b/tests/ccgbugs/tissues.nim index a0c402cc0..7a8ede958 100644 --- a/tests/ccgbugs/tissues.nim +++ b/tests/ccgbugs/tissues.nim @@ -26,13 +26,3 @@ type var troz: fooObj[string] echo bazObj[string](troz).x - - -# bug #14880 -type step = object - exec: proc () - -const pipeline = @[step()] - -let crash = pipeline[0] - diff --git a/tests/ccgbugs/tmangle.nim b/tests/ccgbugs/tmangle.nim deleted file mode 100644 index 0050cef92..000000000 --- a/tests/ccgbugs/tmangle.nim +++ /dev/null @@ -1,16 +0,0 @@ -block: - proc hello() = - let NAN_INFINITY = 12 - doAssert NAN_INFINITY == 12 - let INF = "2.0" - doAssert INF == "2.0" - let NAN = 2.3 - doAssert NAN == 2.3 - - hello() - -block: - proc hello(NAN: float) = - doAssert NAN == 2.0 - - hello(2.0) diff --git a/tests/ccgbugs/tmissingbracket.nim b/tests/ccgbugs/tmissingbracket.nim index 2919efe0e..468e13366 100644 --- a/tests/ccgbugs/tmissingbracket.nim +++ b/tests/ccgbugs/tmissingbracket.nim @@ -11,7 +11,7 @@ type className* : string TClassOfTobj = object of TClassOfTCustomObject nil - TCustomObject {.inheritable.} = ref object + TCustomObject = ref object {.inheritable.} class* : ptr TClassOfTCustomObject TObj = ref object of TCustomObject data: int diff --git a/tests/ccgbugs/tmissinginit.nim b/tests/ccgbugs/tmissinginit.nim index 9eb58221c..8806a2f21 100644 --- a/tests/ccgbugs/tmissinginit.nim +++ b/tests/ccgbugs/tmissinginit.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" output: '''0 0 0 diff --git a/tests/ccgbugs/tmissingvolatile.nim b/tests/ccgbugs/tmissingvolatile.nim index b877eff71..1eccdc6b1 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 --mm:refc -d:release $file" + cmd: r"nim c --hints:on $options -d:release $file" ccodecheck: "'NI volatile state;'" targets: "c" """ diff --git a/tests/ccgbugs/tnoalias.nim b/tests/ccgbugs/tnoalias.nim index 96c3d390b..0c6e84e44 100644 --- a/tests/ccgbugs/tnoalias.nim +++ b/tests/ccgbugs/tnoalias.nim @@ -1,5 +1,5 @@ discard """ - ccodecheck: "\\i@'NI* NIM_NOALIAS field;' @'NIM_CHAR* NIM_NOALIAS x__0qEngDE9aYoYsF8tWnyPacw,' @'void* NIM_NOALIAS q'" + ccodecheck: "\\i@'NI* NIM_NOALIAS field;' @'NIM_CHAR* NIM_NOALIAS x,' @'void* NIM_NOALIAS q'" """ type @@ -7,7 +7,7 @@ type field {.noalias.}: ptr UncheckedArray[int] proc p(x {.noalias.}: openArray[char]) = - var q {.noalias.}: pointer = addr(x[0]) + var q {.noalias.}: pointer = unsafeAddr(x[0]) var bn: BigNum p "abc" diff --git a/tests/ccgbugs/tsamename3.nim b/tests/ccgbugs/tsamename3.nim deleted file mode 100644 index a69391e5c..000000000 --- a/tests/ccgbugs/tsamename3.nim +++ /dev/null @@ -1,111 +0,0 @@ -block: # bug #15526 - block: - type Foo = ref object - x1: int - let f1 = Foo(x1: 1) - block: - type Foo = ref object - x2: int - let f2 = Foo(x2: 2) - -block: # ditto - template fn() = - block: - type Foo = ref object - x1: int - let f1 = Foo(x1: 1) - doAssert f1.x1 == 1 - block: - type Foo = ref object - x2: int - let f2 = Foo(x2: 2) - doAssert f2.x2 == 2 - static: fn() - fn() - -block: # bug #17162 - template fn = - var ret: string - block: - type A = enum a0, a1, a2 - for ai in A: - ret.add $ai - block: - type A = enum b0, b1, b2, b3 - for ai in A: - ret.add $ai - doAssert ret == "a0a1a2b0b1b2b3" - - static: fn() # ok - fn() # was bug - -block: # ditto - proc fn = - var ret: string - block: - type A = enum a0, a1, a2 - for ai in A: - ret.add $ai - block: - type A = enum b0, b1, b2, b3 - for ai in A: - ret.add $ai - doAssert ret == "a0a1a2b0b1b2b3" - - static: fn() # ok - fn() # was bug - -block: # bug #5170 - block: - type Foo = object - x1: int - let f1 = Foo(x1: 1) - block: - type Foo = object - x2: int - let f2 = Foo(x2: 2) - -block: # ditto - block: - type Foo = object - bar: bool - var f1: Foo - - block: - type Foo = object - baz: int - var f2: Foo - doAssert f2.baz == 0 - - block: - template fn() = - block: - type Foo = object - x1: int - let f1 = Foo(x1: 1) - doAssert f1.x1 == 1 - block: - type Foo = object - x2: int - let f2 = Foo(x2: 2) - doAssert f2.x2 == 2 - static: fn() - fn() - -when true: # ditto, refs https://github.com/nim-lang/Nim/issues/5170#issuecomment-582712132 - type Foo1 = object # at top level - bar: bool - var f1: Foo1 - - block: - type Foo1 = object - baz: int - var f2: Foo1 - doAssert f2.baz == 0 - -block: # make sure `hashType` doesn't recurse infinitely - type - PFoo = ref object - a, b: PFoo - c: int - var a: PFoo diff --git a/tests/ccgbugs/tuple_canon.nim b/tests/ccgbugs/tuple_canon.nim index fbb971861..aa9605d4b 100644 --- a/tests/ccgbugs/tuple_canon.nim +++ b/tests/ccgbugs/tuple_canon.nim @@ -68,7 +68,7 @@ template odd*(i: int) : untyped = proc vidx(hg: HexGrid; col, row: int; i: HexVtxIndex) : Index = #NOTE: this variation compiles - #var offset : typeof(evenSharingOffsets[i]) + #var offset : type(evenSharingOffsets[i]) # #if odd(col): # offset = oddSharingOffsets[i] diff --git a/tests/ccgbugs/twrong_setconstr.nim b/tests/ccgbugs/twrong_setconstr.nim new file mode 100644 index 000000000..8be0b82b5 --- /dev/null +++ b/tests/ccgbugs/twrong_setconstr.nim @@ -0,0 +1,146 @@ +discard """ + output: "" +""" + +# bug #2880 + +type + TMsgKind* = enum + errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated, + errXCompilerDoesNotSupportCpp, errStringLiteralExpected, + errIntLiteralExpected, errInvalidCharacterConstant, + errClosingTripleQuoteExpected, errClosingQuoteExpected, + errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, + errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange, + errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote, + errIdentifierExpected, errNewlineExpected, errInvalidModuleName, + errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, + errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, + errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, + errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, + errExceptionExpected, errExceptionAlreadyHandled, + errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, + errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, + errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, + errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, + errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, + errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound, + errNoneBoehmRefcExpectedButXFound, + errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, + errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, + errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, + errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected, + errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, + errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, + errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, + errConstantDivisionByZero, errOrdinalTypeExpected, + errOrdinalOrFloatTypeExpected, errOverOrUnderflow, + errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, + errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, + errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, + errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, + errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, + errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected, + errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, + errMagicOnlyInSystem, errPowerOfTwoExpected, + errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, + errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, + errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, + errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, + errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, + errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, + errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, + errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, + errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, + errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, + errVarForOutParamNeeded, + errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, + errAmbiguousCallXYZ, errWrongNumberOfArguments, + errXCannotBePassedToProcVar, + errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, + errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, + errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, + errInvalidOrderInArrayConstructor, + errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, + errOptionExpected, errXisNoLabel, errNotAllCasesCovered, + errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, + errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, + errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, + errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, + errXNotAllowedHere, errInvalidControlFlowX, + errXisNoType, errCircumNeedsPointer, errInvalidExpression, + errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, + errNamedExprNotAllowed, errXExpectsOneTypeParam, + errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, + errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, + errNoReturnTypeDeclared, + errInvalidCommandX, errXOnlyAtModuleScope, + errXNeedsParamObjectType, + errTemplateInstantiationTooNested, errInstantiationFrom, + errInvalidIndexValueForTuple, errCommandExpectsFilename, + errMainModuleMustBeSpecified, + errXExpected, + errTIsNotAConcreteType, + errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, + errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, + errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, + errOnlyACallOpCanBeDelegator, errUsingNoSymbol, + errMacroBodyDependsOnGenericTypes, + errDestructorNotGenericEnough, + errInlineIteratorsAsProcParams, + errXExpectsTwoArguments, + errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, + errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, + errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, + errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, + errXhasSideEffects, errIteratorExpected, errLetNeedsInit, + errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, + errXCannotBeClosure, errXMustBeCompileTime, + errCannotInferTypeOfTheLiteral, + errCannotInferReturnType, + errGenericLambdaNotAllowed, + errCompilerDoesntSupportTarget, + errUser, + warnCannotOpenFile, + warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, + warnDeprecated, warnConfigDeprecated, + warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, + warnUnknownSubstitutionX, warnLanguageXNotSupported, + warnFieldXNotSupported, warnCommentXIgnored, + warnNilStatement, warnTypelessParam, + warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, + warnEachIdentIsTuple + warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, + warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, + warnUser, + hintSuccess, hintSuccessX, + hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, + hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, + hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, + hintConditionAlwaysTrue, hintName, hintPattern, + hintExecuting, hintLinking, hintDependency, + hintSource, hintStackTrace, hintGCStats, + hintUser + +const + warnMin = warnCannotOpenFile + hintMax = high(TMsgKind) + +type + TNoteKind = range[warnMin..hintMax] # "notes" are warnings or hints + TNoteKinds = set[TNoteKind] + +const + NotesVerbosityConst: array[0..0, TNoteKinds] = [ + {low(TNoteKind)..high(TNoteKind)} - {hintGCStats}] + fuckyou = NotesVerbosityConst[0] + +var + gNotesFromConst: TNoteKinds = NotesVerbosityConst[0] + gNotesFromConst2: TNoteKinds = fuckyou + +if hintGCStats in gNotesFromConst: + echo "hintGCStats in gNotesFromConst A" + +if hintGCStats in gNotesFromConst2: + echo "hintGCStats in gNotesFromConst B" diff --git a/tests/ccgbugs2/tcodegen.nim b/tests/ccgbugs2/tcodegen.nim deleted file mode 100644 index 84cd76e2f..000000000 --- a/tests/ccgbugs2/tcodegen.nim +++ /dev/null @@ -1,28 +0,0 @@ -discard """ - targets: "c cpp" -""" - -# bug #19094 -type - X = object - filler: array[2048, int] - innerAddress: uint - -proc initX(): X = - result.innerAddress = cast[uint](result.addr) - -proc initXInPlace(x: var X) = - x.innerAddress = cast[uint](x.addr) - -block: # NRVO1 - var x = initX() - let innerAddress = x.innerAddress - let outerAddress = cast[uint](x.addr) - doAssert(innerAddress == outerAddress) # [OK] - -block: # NRVO2 - var x: X - initXInPlace(x) - let innerAddress = x.innerAddress - let outerAddress = cast[uint](x.addr) - doAssert(innerAddress == outerAddress) # [OK] diff --git a/tests/closure/t11042.nim b/tests/closure/t11042.nim deleted file mode 100644 index 6a3928316..000000000 --- a/tests/closure/t11042.nim +++ /dev/null @@ -1,55 +0,0 @@ -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/closure/t20152.nim b/tests/closure/t20152.nim deleted file mode 100644 index 484ea0741..000000000 --- a/tests/closure/t20152.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - action: compile -""" - -proc foo() = - iterator it():int {.closure.} = - yield 1 - proc useIter() {.nimcall.} = - var iii = it # <-- illegal capture - doAssert iii() == 1 - useIter() -foo() - -proc foo2() = - proc bar() = # Local function, but not a closure, because no captures - echo "hi" - proc baz() {.nimcall.} = # Calls local function - bar() - baz() -foo2() diff --git a/tests/closure/tclosure.nim b/tests/closure/tclosure.nim index fa1f79ffe..546d7026d 100644 --- a/tests/closure/tclosure.nim +++ b/tests/closure/tclosure.nim @@ -38,10 +38,10 @@ joinable: false block tclosure: - proc map(n: var openArray[int], fn: proc (x: int): int {.closure}) = + proc map(n: var openarray[int], fn: proc (x: int): int {.closure}) = for i in 0..n.len-1: n[i] = fn(n[i]) - proc each(n: openArray[int], fn: proc(x: int) {.closure.}) = + proc each(n: openarray[int], fn: proc(x: int) {.closure.}) = for i in 0..n.len-1: fn(n[i]) @@ -65,7 +65,7 @@ block tclosure: # bug #5015 - type Mutator = proc(matched: string): string {.noSideEffect, gcsafe.} + type Mutator = proc(matched: string): string {.noSideEffect, gcsafe, locks: 0.} proc putMutated( MutatorCount: static[int], @@ -239,19 +239,19 @@ block doNotation: b.onClick do (e: Event): echo "click at ", e.x, ",", e.y - b.onFocusLost do (): + b.onFocusLost: echo "lost focus 1" - b.onFocusLost do (): + b.onFocusLost do: echo "lost focus 2" - b.onUserEvent("UserEvent 1") do (): + b.onUserEvent("UserEvent 1") do: discard - onUserEvent(b, "UserEvent 2") do (): + b.onUserEvent "UserEvent 2": discard - b.onUserEvent("UserEvent 3") do (): + b.onUserEvent("UserEvent 3"): discard b.onUserEvent("UserEvent 4", () => echo "event 4") diff --git a/tests/closure/tclosure_issues.nim b/tests/closure/tclosure_issues.nim index b1a2d7c6b..4688834de 100644 --- a/tests/closure/tclosure_issues.nim +++ b/tests/closure/tclosure_issues.nim @@ -71,12 +71,12 @@ block tissue7104: proc sp(cb: proc())= cb() - sp do (): + sp: var i = 0 echo "ok ", i - sp do (): + sp(): inc i echo "ok ", i - sp do (): + sp do: inc i echo "ok ", i diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim index 37d0f68a2..47f3f105f 100644 --- a/tests/closure/tinvalidclosure.nim +++ b/tests/closure/tinvalidclosure.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch: got " + errormsg: "type mismatch: got " line: 12 """ diff --git a/tests/closure/tinvalidclosure5.nim b/tests/closure/tinvalidclosure5.nim index 3b5f46a40..d03d93867 100644 --- a/tests/closure/tinvalidclosure5.nim +++ b/tests/closure/tinvalidclosure5.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch: got but expected 'A = proc (){.nimcall.}'" + errormsg: "type mismatch: got but expected 'A = proc (){.nimcall.}'" line: 9 """ diff --git a/tests/closure/tnested.nim b/tests/closure/tnested.nim index 31963ea86..7a1881a60 100644 --- a/tests/closure/tnested.nim +++ b/tests/closure/tnested.nim @@ -33,7 +33,7 @@ py py px 6 -proc (){.closure, noSideEffect, gcsafe.} +proc (){.closure, gcsafe, locks: 0.} ''' """ @@ -190,7 +190,7 @@ proc foo() = let f = (proc() = myDiscard (proc() = echo a) ) - echo name(typeof(f)) + echo name(type(f)) foo() diff --git a/tests/closure/tstmtlist.nim b/tests/closure/tstmtlist.nim deleted file mode 100644 index 6a1390617..000000000 --- a/tests/closure/tstmtlist.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - action: compile -""" - -proc foo(x: proc()) = x() -foo: echo "a" #[tt.Warning - ^ statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead [StmtListLambda]]# -foo do: echo "b" #[tt.Warning - ^ statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead [StmtListLambda]]# diff --git a/tests/collections/tseq.nim b/tests/collections/tseq.nim index 39f066f4c..88d6dc79b 100644 --- a/tests/collections/tseq.nim +++ b/tests/collections/tseq.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc; --mm:orc" output: ''' Hithere, what's your name?Hathere, what's your name? fA13msg1falsefB14msg2truefC15msg3false @@ -12,6 +11,8 @@ 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 ''' """ @@ -157,43 +158,41 @@ block tsequtils: echo someObjSeq -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 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] - when defined(gcRefc): - shallow(nilSeq) - t = nilSeq - doAssert t == @[] - block: - var t = @[1,2,3] - when defined(gcRefc): - 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() +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 @@ -202,7 +201,7 @@ block ttoseq: stdout.write(x) for x in items(toSeq(countup(2, 6))): stdout.write(x) - var y: typeof("a b c".split) + var y: type("a b c".split) y = "xzy" stdout.write("\n") diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 95f9418a0..61197e9f0 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -7,11 +7,7 @@ And we get here 3 ''' joinable: false -targets: "c cpp js" """ - -# xxx wrap in a template to test in VM, see https://github.com/timotheecour/Nim/issues/534#issuecomment-769565033 - import hashes, sequtils, tables, algorithm proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted @@ -171,20 +167,22 @@ block tableconstr: # NEW: doAssert 56 in 50..100 - doAssert 56 in 0..60 + doAssert 56 in ..60 block ttables2: proc TestHashIntInt() = var tab = initTable[int,int]() - let n = 10 + let n = 100_000 for i in 1..n: tab[i] = i for i in 1..n: var x = tab[i] if x != i : echo "not found ", i - TestHashIntInt() + proc run1() = + for i in 1 .. 50: + TestHashIntInt() # bug #2107 @@ -196,6 +194,7 @@ block ttables2: delTab[5] = 5 + run1() echo "2" block tablesref: @@ -445,13 +444,3 @@ block emptyOrdered: var t2: OrderedTable[int, string] doAssert t1 == t2 -block: # Table[ref, int] - type A = ref object - x: int - var t: OrderedTable[A, int] - let a1 = A(x: 3) - let a2 = A(x: 3) - t[a1] = 10 - t[a2] = 11 - doAssert t[a1] == 10 - doAssert t[a2] == 11 diff --git a/tests/compiler/samplelib.nim b/tests/compiler/samplelib.nim deleted file mode 100644 index c70971722..000000000 --- a/tests/compiler/samplelib.nim +++ /dev/null @@ -1,2 +0,0 @@ -# Sample library used by tcmdlineclib.nim -proc test(): int {.cdecl, exportc, dynlib.} = 123 diff --git a/tests/compiler/t12684.nim b/tests/compiler/t12684.nim deleted file mode 100644 index a5f33d2c9..000000000 --- a/tests/compiler/t12684.nim +++ /dev/null @@ -1,7 +0,0 @@ -discard """ - cmd: "nim check --hints:off --warnings:off $file" - errormsg: "undeclared identifier: 'Undeclared'" -""" - -var x: Undeclared -import compiler/nimeval diff --git a/tests/compiler/tasm.nim b/tests/compiler/tasm.nim deleted file mode 100644 index 9f60231e0..000000000 --- a/tests/compiler/tasm.nim +++ /dev/null @@ -1,15 +0,0 @@ -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/tbtrees.nim b/tests/compiler/tbrees.nim similarity index 99% rename from tests/compiler/tbtrees.nim rename to tests/compiler/tbrees.nim index 973c26420..5f6482ed9 100644 --- a/tests/compiler/tbtrees.nim +++ b/tests/compiler/tbrees.nim @@ -68,7 +68,7 @@ proc main = when true: var b2 = initBTree[int, string]() var t2 = initTable[int, string]() - const iters = 100 + const iters = 100_000 for i in 1..iters: let x = rand(high(int)) if not t2.hasKey(x): diff --git a/tests/compiler/tcmdlineclib.nim b/tests/compiler/tcmdlineclib.nim deleted file mode 100644 index c06d6f103..000000000 --- a/tests/compiler/tcmdlineclib.nim +++ /dev/null @@ -1,3 +0,0 @@ -proc test(): int {.importc, cdecl.} - -doAssert test() == 123 diff --git a/tests/compiler/tcmdlineclib.nims b/tests/compiler/tcmdlineclib.nims deleted file mode 100644 index f7899d92e..000000000 --- a/tests/compiler/tcmdlineclib.nims +++ /dev/null @@ -1,10 +0,0 @@ -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/compiler/tcmdlineoswin.nim b/tests/compiler/tcmdlineoswin.nim index 602671e98..6642c5ac6 100644 --- a/tests/compiler/tcmdlineoswin.nim +++ b/tests/compiler/tcmdlineoswin.nim @@ -2,7 +2,7 @@ discard """ cmd: "nim $target $options --os:windows $file" disabled: "linux" disabled: "bsd" - disabled: "osx" + disabled: "macosx" disabled: "unix" disabled: "posix" """ diff --git a/tests/compiler/tdebugutils.nim b/tests/compiler/tdebugutils.nim deleted file mode 100644 index 50b15cb78..000000000 --- a/tests/compiler/tdebugutils.nim +++ /dev/null @@ -1,38 +0,0 @@ -discard """ - joinable: false - disabled: true -""" - -#[ -This test illustrates some features of `debugutils` to debug the compiler. - -## example -this shows how to enable compiler logging just for a section of user code, -without generating tons of unrelated log messages for code you're not interested -in debugging. - -```sh -# enable some debugging code, e.g. the `when false:` block in `semExpr` -nim c -o:bin/nim_temp --stacktrace -d:debug -d:nimDebugUtils compiler/nim -bin/nim_temp c tests/compiler/tdebugutils.nim -``` - -(use --filenames:abs for abs files) - -## result -("<", "tdebugutils.nim(16, 3)", {.define(nimCompilerDebug).}, nil) -(">", "tdebugutils.nim(17, 3)", let a = 2.5 * 3, {}, nkLetSection) -(">", "tdebugutils.nim(17, 15)", 2.5 * 3, {efAllowDestructor, efWantValue}, nkInfix) -(">", "tdebugutils.nim(17, 11)", 2.5, {efAllowStmt, efDetermineType, efOperand}, nkFloatLit) -("<", "tdebugutils.nim(17, 11)", 2.5, float64) -(">", "tdebugutils.nim(17, 17)", 3, {efAllowStmt, efDetermineType, efOperand}, nkIntLit) -("<", "tdebugutils.nim(17, 17)", 3, int literal(3)) -("<", "tdebugutils.nim(17, 15)", 2.5 * 3, float) -("<", "tdebugutils.nim(17, 3)", let a = 2.5 * 3, nil) -(">", "tdebugutils.nim(18, 3)", {.undef(nimCompilerDebug).}, {}, nkPragma) -]# - -proc main = - {.define(nimCompilerDebug).} - let a = 2.5 * 3 - {.undef(nimCompilerDebug).} diff --git a/tests/compiler/tgrammar.nim b/tests/compiler/tgrammar.nim deleted file mode 100644 index 772d0f0cc..000000000 --- a/tests/compiler/tgrammar.nim +++ /dev/null @@ -1,7 +0,0 @@ -discard """ - matrix: "-d:nimTestGrammar" -""" - -import compiler/parser - -checkSameGrammar() diff --git a/tests/compiler/tnimblecmd.nim b/tests/compiler/tnimblecmd.nim index 53bce4625..d39179be1 100644 --- a/tests/compiler/tnimblecmd.nim +++ b/tests/compiler/tnimblecmd.nim @@ -1,75 +1,26 @@ -include compiler/[nimblecmd], sets +include compiler/[nimblecmd] proc v(s: string): Version = s.newVersion +# #head is special in the sense that it's assumed to always be newest. +doAssert v"1.0" < v"#head" +doAssert v"1.0" < v"1.1" +doAssert v"1.0.1" < v"1.1" +doAssert v"1" < v"1.1" +doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer. +doAssert v"#a111" < v"#head" -proc testVersionsComparison = - # #head is special in the sense that it's assumed to always be newest. - doAssert v"1.0" < v"#head" - doAssert v"1.0" < v"1.1" - doAssert v"1.0.1" < v"1.1" - doAssert v"1" < v"1.1" - doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer. - doAssert v"#a111" < v"#head" +let conf = newConfigRef() +var rr = newStringTable() +addPackage conf, rr, "irc-#a111", unknownLineInfo +addPackage conf, rr, "irc-#head", unknownLineInfo +addPackage conf, rr, "irc-0.1.0", unknownLineInfo +#addPackage conf, rr, "irc", unknownLineInfo +#addPackage conf, rr, "another", unknownLineInfo +addPackage conf, rr, "another-0.1", unknownLineInfo -proc testAddPackageWithoutChecksum = - ## For backward compatibility it is not required all packages to have a - ## sha1 checksum at the end of the name of the Nimble cache directory. - ## This way a new compiler will be able to work with an older Nimble. +addPackage conf, rr, "ab-0.1.3", unknownLineInfo +addPackage conf, rr, "ab-0.1", unknownLineInfo +addPackage conf, rr, "justone-1.0", unknownLineInfo - let conf = newConfigRef() - var rr: PackageInfo - - addPackage conf, rr, "irc-#a111", unknownLineInfo - addPackage conf, rr, "irc-#head", unknownLineInfo - addPackage conf, rr, "irc-0.1.0", unknownLineInfo - - addPackage conf, rr, "another-0.1", unknownLineInfo - - addPackage conf, rr, "ab-0.1.3", unknownLineInfo - addPackage conf, rr, "ab-0.1", unknownLineInfo - addPackage conf, rr, "justone-1.0", unknownLineInfo - - doAssert toSeq(rr.chosen).toHashSet == - ["irc-#head", "another-0.1", "ab-0.1.3", "justone-1.0"].toHashSet - -proc testAddPackageWithChecksum = - let conf = newConfigRef() - var rr: PackageInfo - - # in the case of packages with the same version, but different checksums for - # now the first one will be chosen - - addPackage conf, rr, "irc-#a111-DBC1F902CB79946E990E38AF51F0BAD36ACFABD9", - unknownLineInfo - addPackage conf, rr, "irc-#head-042D4BE2B90ED0672E717D71850ABDB0A2D19CD1", - unknownLineInfo - addPackage conf, rr, "irc-#head-042D4BE2B90ED0672E717D71850ABDB0A2D19CD2", - unknownLineInfo - addPackage conf, rr, "irc-0.1.0-6EE6DE936B32E82C7DBE526DA3463574F6568FAF", - unknownLineInfo - - addPackage conf, rr, "another-0.1", unknownLineInfo - addPackage conf, rr, "another-0.1-F07EE6040579F0590608A8FD34F5F2D91D859340", - unknownLineInfo - - addPackage conf, rr, "ab-0.1.3-34BC3B72CE46CF5A496D1121CFEA7369385E9EA2", - unknownLineInfo - addPackage conf, rr, "ab-0.1.3-24BC3B72CE46CF5A496D1121CFEA7369385E9EA2", - unknownLineInfo - addPackage conf, rr, "ab-0.1-A3CFFABDC4759F7779D541F5E031AED17169390A", - unknownLineInfo - - # lower case hex digits is also a valid sha1 checksum - addPackage conf, rr, "justone-1.0-f07ee6040579f0590608a8fd34f5f2d91d859340", - unknownLineInfo - - doAssert toSeq(rr.chosen).toHashSet == [ - "irc-#head-042D4BE2B90ED0672E717D71850ABDB0A2D19CD1", - "another-0.1", - "ab-0.1.3-34BC3B72CE46CF5A496D1121CFEA7369385E9EA2", - "justone-1.0-f07ee6040579f0590608a8fd34f5f2d91d859340" - ].toHashSet - -testVersionsComparison() -testAddPackageWithoutChecksum() -testAddPackageWithChecksum() +doAssert toSeq(rr.chosen) == + @["irc-#head", "ab-0.1.3", "justone-1.0", "another-0.1"] diff --git a/tests/compiler/tprefixmatches.nim b/tests/compiler/tprefixmatches.nim index 6a3186729..a89a6f613 100644 --- a/tests/compiler/tprefixmatches.nim +++ b/tests/compiler/tprefixmatches.nim @@ -5,7 +5,7 @@ macro check(val, body: untyped): untyped = result = newStmtList() expectKind body, nnkStmtList for b in body: - expectKind b, nnkTupleConstr + expectKind b, nnkPar expectLen b, 2 let p = b[0] let s = b[1] diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim index 828dcd0d9..ab995cb6d 100644 --- a/tests/compilerapi/tcompilerapi.nim +++ b/tests/compilerapi/tcompilerapi.nim @@ -23,7 +23,7 @@ proc initInterpreter(script: string): Interpreter = proc main() = let i = initInterpreter("myscript.nim") - i.implementRoutine("nim", "exposed", "addFloats", proc (a: VmArgs) = + i.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) = setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2)) ) i.evalScript() diff --git a/tests/compilerfeatures/texpandmacro.nim b/tests/compilerfeatures/texpandmacro.nim index fea8b571f..76b0263ae 100644 --- a/tests/compilerfeatures/texpandmacro.nim +++ b/tests/compilerfeatures/texpandmacro.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim c --expandMacro:foo $file" - nimout: '''texpandmacro.nim(17, 1) Hint: expanded macro: + nimout: '''Hint: expanded macro: echo ["injected echo"] var x = 4 [ExpandMacro] ''' diff --git a/tests/compiles/mstaticlib.nim b/tests/compiles/mstaticlib.nim deleted file mode 100644 index 6ed593691..000000000 --- a/tests/compiles/mstaticlib.nim +++ /dev/null @@ -1 +0,0 @@ -echo 1234 \ No newline at end of file diff --git a/tests/compiles/t8630.nim b/tests/compiles/t8630.nim index 8b447d061..aa2be11cd 100644 --- a/tests/compiles/t8630.nim +++ b/tests/compiles/t8630.nim @@ -7,7 +7,7 @@ bar proc test(strings: seq[string]) = for s in strings: - var p3 = addr(s) + var p3 = unsafeAddr(s) echo p3[] test(@["foo", "bar"]) diff --git a/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim index ab31ad0f5..88d3faeed 100644 --- a/tests/compiles/trecursive_generic_in_compiles.nim +++ b/tests/compiles/trecursive_generic_in_compiles.nim @@ -50,7 +50,7 @@ proc `==`*[T](xs, ys: List[T]): bool = proc asList*[T](xs: varargs[T]): List[T] = ## Creates list from varargs - proc initListImpl(i: int, xs: openArray[T]): List[T] = + proc initListImpl(i: int, xs: openarray[T]): List[T] = if i > high(xs): Nil[T]() else: diff --git a/tests/compiles/tstaticlib.nim b/tests/compiles/tstaticlib.nim deleted file mode 100644 index a18b59204..000000000 --- a/tests/compiles/tstaticlib.nim +++ /dev/null @@ -1,22 +0,0 @@ -import std/[os, osproc, strformat] - - -const dir = "tests/compiles" -const fileName = dir / "mstaticlib.nim" -const nim = getCurrentCompilerExe() - -block: # bug #18578 - const libName = dir / "tstaticlib1.a" - let (_, status) = execCmdEx(fmt"{nim} c -o:{libName} --app:staticlib {fileName}") - doAssert status == 0 - doAssert fileExists(libName) - removeFile(libName) - -block: # bug #16947 - const libName = dir / "tstaticlib2.a" - writeFile(libName, "echo 124") - doAssert fileExists(libName) - let (_, status) = execCmdEx(fmt"{nim} c -o:{libName} --app:staticlib {fileName}") - doAssert status == 0 - doAssert fileExists(libName) - removeFile(libName) diff --git a/tests/concepts/t18409.nim b/tests/concepts/t18409.nim deleted file mode 100644 index 0edba2d31..000000000 --- a/tests/concepts/t18409.nim +++ /dev/null @@ -1,37 +0,0 @@ -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 deleted file mode 100644 index 575d45dda..000000000 --- a/tests/concepts/t19730.nim +++ /dev/null @@ -1,20 +0,0 @@ -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/t20237.nim b/tests/concepts/t20237.nim deleted file mode 100644 index db9002421..000000000 --- a/tests/concepts/t20237.nim +++ /dev/null @@ -1,3 +0,0 @@ -type Foo = concept - ## doc comment - proc foo(x: Self) diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim index 901f8d2f4..b92af5485 100644 --- a/tests/concepts/t3330.nim +++ b/tests/concepts/t3330.nim @@ -1,5 +1,4 @@ discard """ -matrix: "--mm:refc" errormsg: "type mismatch: got " nimout: ''' t3330.nim(70, 4) Error: type mismatch: got @@ -49,6 +48,7 @@ expression: test(bar)''' + ## line 60 type Foo[T] = concept k diff --git a/tests/concepts/tconcepts.nim b/tests/concepts/tconcepts.nim index acdff6f24..d0bc76c20 100644 --- a/tests/concepts/tconcepts.nim +++ b/tests/concepts/tconcepts.nim @@ -98,7 +98,7 @@ block tconceptinclosure: block overload_precedence: type ParameterizedType[T] = object - type CustomTypeClass = concept c + type CustomTypeClass = concept true # 3 competing procs diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim index 3efb9964a..65f6d4660 100644 --- a/tests/concepts/tconcepts_issues.nim +++ b/tests/concepts/tconcepts_issues.nim @@ -27,8 +27,6 @@ false true -1 Meow -10 0.0 -1 2.0 ''' joinable: false """ @@ -326,15 +324,15 @@ block t6691: block t6782: type Reader = concept c - c.read(openArray[byte], int, int) is int + c.read(openarray[byte], int, int) is int Rdr = concept c - c.rd(openArray[byte], int, int) is int + c.rd(openarray[byte], int, int) is int type TestFile = object - proc read(r: TestFile, dest: openArray[byte], offset: int, limit: int): int = + proc read(r: TestFile, dest: openarray[byte], offset: int, limit: int): int = result = 0 - proc rd(r: TestFile, dest: openArray[byte], offset: int, limit: int): int = + proc rd(r: TestFile, dest: openarray[byte], offset: int, limit: int): int = result = 0 doAssert TestFile is Reader @@ -399,7 +397,7 @@ block misc_issues: echo p2.x is float and p2.y is float # true # https://github.com/nim-lang/Nim/issues/2018 - type ProtocolFollower = concept c + type ProtocolFollower = concept true # not a particularly involved protocol type ImplementorA = object @@ -469,59 +467,3 @@ block misc_issues: # anyway and will be an error soon. var a: Cat = Cat() a.sayHello() - - -# bug #16897 - -type - Fp[N: static int, T] = object - big: array[N, T] - -type - QuadraticExt* = concept x - ## Quadratic Extension concept (like complex) - type BaseField = auto - x.c0 is BaseField - x.c1 is BaseField -var address = pointer(nil) -proc prod(r: var QuadraticExt, b: QuadraticExt) = - if address == nil: - address = addr b - prod(r, b) - else: - assert address == addr b - -type - Fp2[N: static int, T] {.byref.} = object - c0, c1: Fp[N, T] - -# This should be passed by reference, -# but concepts do not respect the 24 bytes rule -# or `byref` pragma. -var r, b: Fp2[6, uint64] - -prod(r, b) - - -block: # bug #21263 - type - DateDayFraction = concept # no T, an atom - proc date(a: Self): int - proc fraction(b: Self): float - Date = distinct int - DateDayFractionImpl = object - date : int - fraction : float - - proc date(a: Date): int = a.int - proc fraction(a:Date): float = 0.0 - - proc date(a: DateDayFractionImpl): int = a.date - proc fraction(b: DateDayFractionImpl): float = b.fraction - - - proc print(a: DateDayFraction) = - echo a.date, " ", a.fraction - - print(10.Date) # ok - print(DateDayFractionImpl(date: 1, fraction: 2)) # error diff --git a/tests/concepts/tconcepts_overload_precedence.nim b/tests/concepts/tconcepts_overload_precedence.nim index c580d2688..9eed6256a 100644 --- a/tests/concepts/tconcepts_overload_precedence.nim +++ b/tests/concepts/tconcepts_overload_precedence.nim @@ -9,7 +9,7 @@ x as CustomTypeClass''' type ParameterizedType[T] = object -type CustomTypeClass = concept c +type CustomTypeClass = concept true # 3 competing procs diff --git a/tests/concepts/texplain.nim b/tests/concepts/texplain.nim index 8cf04ae82..de3d540cd 100644 --- a/tests/concepts/texplain.nim +++ b/tests/concepts/texplain.nim @@ -1,33 +1,45 @@ discard """ cmd: "nim c --verbosity:0 --colors:off $file" nimout: ''' -texplain.nim(162, 10) Hint: Non-matching candidates for e(y) +texplain.nim(164, 10) Hint: Non-matching candidates for e(y) proc e(i: int): int first type mismatch at position: 1 required type for i: int but expression 'y' is of type: MatchingType -texplain.nim(165, 7) Hint: Non-matching candidates for e(10) +texplain.nim(167, 7) Hint: Non-matching candidates for e(10) proc e(o: ExplainedConcept): int first type mismatch at position: 1 required type for o: ExplainedConcept but expression '10' is of type: int literal(10) -texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo' -texplain.nim(128, 5) ExplainedConcept: concept predicate failed -texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar' -texplain.nim(128, 5) ExplainedConcept: concept predicate failed +texplain.nim(130, 6) ExplainedConcept: undeclared field: 'foo' +texplain.nim(130, 6) ExplainedConcept: undeclared field: '.' +texplain.nim(130, 6) ExplainedConcept: expression '.' cannot be called +texplain.nim(130, 6) ExplainedConcept: expression '' has no type (or is ambiguous) +texplain.nim(130, 5) ExplainedConcept: concept predicate failed +texplain.nim(131, 6) ExplainedConcept: undeclared field: 'bar' +texplain.nim(131, 6) ExplainedConcept: undeclared field: '.' +texplain.nim(131, 6) ExplainedConcept: expression '.' cannot be called +texplain.nim(131, 6) ExplainedConcept: expression '' has no type (or is ambiguous) +texplain.nim(130, 5) ExplainedConcept: concept predicate failed -texplain.nim(168, 10) Hint: Non-matching candidates for e(10) +texplain.nim(170, 10) Hint: Non-matching candidates for e(10) proc e(o: ExplainedConcept): int first type mismatch at position: 1 required type for o: ExplainedConcept but expression '10' is of type: int literal(10) -texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo' -texplain.nim(128, 5) ExplainedConcept: concept predicate failed -texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar' -texplain.nim(128, 5) ExplainedConcept: concept predicate failed +texplain.nim(130, 6) ExplainedConcept: undeclared field: 'foo' +texplain.nim(130, 6) ExplainedConcept: undeclared field: '.' +texplain.nim(130, 6) ExplainedConcept: expression '.' cannot be called +texplain.nim(130, 6) ExplainedConcept: expression '' has no type (or is ambiguous) +texplain.nim(130, 5) ExplainedConcept: concept predicate failed +texplain.nim(131, 6) ExplainedConcept: undeclared field: 'bar' +texplain.nim(131, 6) ExplainedConcept: undeclared field: '.' +texplain.nim(131, 6) ExplainedConcept: expression '.' cannot be called +texplain.nim(131, 6) ExplainedConcept: expression '' has no type (or is ambiguous) +texplain.nim(130, 5) ExplainedConcept: concept predicate failed -texplain.nim(172, 20) Error: type mismatch: got +texplain.nim(174, 20) Error: type mismatch: got but expected one of: proc e(i: int): int first type mismatch at position: 1 @@ -37,11 +49,11 @@ proc e(o: ExplainedConcept): int first type mismatch at position: 1 required type for o: ExplainedConcept but expression 'n' is of type: NonMatchingType -texplain.nim(172, 9) template/generic instantiation of `assert` from here -texplain.nim(128, 5) ExplainedConcept: concept predicate failed +texplain.nim(174, 9) template/generic instantiation of `assert` from here +texplain.nim(130, 5) ExplainedConcept: concept predicate failed expression: e(n) -texplain.nim(173, 20) Error: type mismatch: got +texplain.nim(175, 20) Error: type mismatch: got but expected one of: proc r(i: string): int first type mismatch at position: 1 @@ -51,15 +63,15 @@ proc r(o: RegularConcept): int first type mismatch at position: 1 required type for o: RegularConcept but expression 'n' is of type: NonMatchingType -texplain.nim(173, 9) template/generic instantiation of `assert` from here -texplain.nim(132, 5) RegularConcept: concept predicate failed +texplain.nim(175, 9) template/generic instantiation of `assert` from here +texplain.nim(134, 5) RegularConcept: concept predicate failed proc r[T](a: SomeNumber; b: T; c: auto) first type mismatch at position: 1 required type for a: SomeNumber but expression 'n' is of type: NonMatchingType expression: r(n) -texplain.nim(174, 20) Hint: Non-matching candidates for r(y) +texplain.nim(176, 20) Hint: Non-matching candidates for r(y) proc r(i: string): int first type mismatch at position: 1 required type for i: string @@ -69,20 +81,28 @@ proc r[T](a: SomeNumber; b: T; c: auto) required type for a: SomeNumber but expression 'y' is of type: MatchingType -texplain.nim(182, 2) Error: type mismatch: got +texplain.nim(184, 2) Error: type mismatch: got but expected one of: proc f(o: NestedConcept) first type mismatch at position: 1 required type for o: NestedConcept but expression 'y' is of type: MatchingType -texplain.nim(132, 6) RegularConcept: undeclared field: 'foo' -texplain.nim(132, 5) RegularConcept: concept predicate failed -texplain.nim(133, 6) RegularConcept: undeclared field: 'bar' -texplain.nim(132, 5) RegularConcept: concept predicate failed -texplain.nim(136, 5) NestedConcept: concept predicate failed +texplain.nim(134, 6) RegularConcept: undeclared field: 'foo' +texplain.nim(134, 6) RegularConcept: undeclared field: '.' +texplain.nim(134, 6) RegularConcept: expression '.' cannot be called +texplain.nim(134, 6) RegularConcept: expression '' has no type (or is ambiguous) +texplain.nim(134, 5) RegularConcept: concept predicate failed +texplain.nim(135, 6) RegularConcept: undeclared field: 'bar' +texplain.nim(135, 6) RegularConcept: undeclared field: '.' +texplain.nim(135, 6) RegularConcept: expression '.' cannot be called +texplain.nim(135, 6) RegularConcept: expression '' has no type (or is ambiguous) +texplain.nim(134, 5) RegularConcept: concept predicate failed +texplain.nim(138, 5) NestedConcept: concept predicate failed expression: f(y)''' errormsg: "type mismatch: got " + line: 184 + """ @@ -103,25 +123,7 @@ expression: f(y)''' - - - - - - - - - - - - - - - - - - -# line 124 HERE +# line 120 HERE type ExplainedConcept {.explain.} = concept o diff --git a/tests/concepts/tspec.nim b/tests/concepts/tspec.nim deleted file mode 100644 index 52f13a40a..000000000 --- a/tests/concepts/tspec.nim +++ /dev/null @@ -1,105 +0,0 @@ -discard """ - output: '''4 -0 -4 -4 -1 -2 -3 -yes int -string int''' - joinable: false -""" - -import hashes - -type - Comparable = concept # no T, an atom - proc cmp(a, b: Self): int - - ToStringable = concept - proc `$`(a: Self): string - - Hashable = concept ## the most basic of identity assumptions - proc hash(x: Self): int - proc `==`(x, y: Self): bool - - Swapable = concept - proc swap(x, y: var Self) - - -proc h(x: Hashable) = - echo x - -h(4) - -when true: - proc compare(a: Comparable) = - echo cmp(a, a) - - compare(4) - -proc dollar(x: ToStringable) = - echo x - -when true: - dollar 4 - dollar "4" - -#type D = distinct int - -#dollar D(4) - -when true: - type - Iterable[Ix] = concept - iterator items(c: Self): Ix - - proc g[Tu](it: Iterable[Tu]) = - for x in it: - echo x - - g(@[1, 2, 3]) - -proc hs(x: Swapable) = - var y = x - swap y, y - -hs(4) - -type - Indexable[T] = concept # has a T, a collection - proc `[]`(a: Self; index: int): T # we need to describe how to infer 'T' - # and then we can use the 'T' and it must match: - proc `[]=`(a: var Self; index: int; value: T) - proc len(a: Self): int - -proc indexOf[T](a: Indexable[T]; value: T) = - echo "yes ", T - -block: - var x = @[1, 2, 3] - indexOf(x, 4) - -import tables, typetraits - -type - Dict[K, V] = concept - proc `[]`(s: Self; k: K): V - proc `[]=`(s: var Self; k: K; v: V) - -proc d[K2, V2](x: Dict[K2, V2]) = - echo K2, " ", V2 - -var x = initTable[string, int]() -d(x) - - -type Monoid = concept - proc `+`(x, y: Self): Self - proc z(t: typedesc[Self]): Self - -proc z(x: typedesc[int]): int = 0 - -doAssert int is Monoid - diff --git a/tests/concepts/tusertypeclasses.nim b/tests/concepts/tusertypeclasses.nim index 83e2b176e..c7104f2a6 100644 --- a/tests/concepts/tusertypeclasses.nim +++ b/tests/concepts/tusertypeclasses.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" output: '''Sortable Sortable Container @@ -10,8 +9,6 @@ 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/concepts/twrapconcept.nim b/tests/concepts/twrapconcept.nim index c3dea2ff9..377b63afe 100644 --- a/tests/concepts/twrapconcept.nim +++ b/tests/concepts/twrapconcept.nim @@ -1,6 +1,7 @@ discard """ errormsg: "type mismatch: got " - nimout: "twrapconcept.nim(10, 5) Foo: concept predicate failed" + line: 21 + nimout: "twrapconcept.nim(11, 5) Foo: concept predicate failed" """ # https://github.com/nim-lang/Nim/issues/5127 diff --git a/tests/config.nims b/tests/config.nims index 4bcd67143..e0b8502ee 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -6,42 +6,11 @@ switch("path", "$lib/../testament/lib") ## prevent common user config settings to interfere with testament expectations ## Indifidual tests can override this if needed to test for these options. switch("colors", "off") - +switch("listFullPaths", "off") switch("excessiveStackTrace", "off") -when (NimMajor, NimMinor, NimPatch) >= (1,5,1): - # to make it easier to test against older nim versions, (best effort only) - switch("filenames", "legacyRelProj") - switch("spellSuggest", "0") - # for std/unittest switch("define", "nimUnittestOutputLevel:PRINT_FAILURES") switch("define", "nimUnittestColor:off") switch("define", "nimLegacyTypeMismatch") - -hint("Processing", off) - # dots can cause annoyances; instead, a single test can test `hintProcessing` - -# uncomment to enable all flaky tests disabled by this flag -# (works through process calls, e.g. tests that invoke nim). -# switch("define", "nimTestsEnableFlaky") - -# switch("hint", "ConvFromXtoItselfNotNeeded") -# switch("warningAsError", "InheritFromException") # would require fixing a few tests - -# experimental APIs are enabled in testament, refs https://github.com/timotheecour/Nim/issues/575 -# sync with `kochdocs.docDefines` or refactor. -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") -switch("define", "nimPreviewRangeDefault") -when defined(windows): - switch("tlsEmulation", "off") - -switch("warningAserror", "UnnamedBreak") -switch("legacy", "verboseTypeMismatch") diff --git a/tests/constr/a.nim b/tests/constr/a.nim deleted file mode 100644 index 03788fc57..000000000 --- a/tests/constr/a.nim +++ /dev/null @@ -1,2 +0,0 @@ -type A* = object - a: uint8 \ No newline at end of file diff --git a/tests/constr/b.nim b/tests/constr/b.nim deleted file mode 100644 index 437dd0550..000000000 --- a/tests/constr/b.nim +++ /dev/null @@ -1,2 +0,0 @@ -type B* = object -proc A*(a, b: float): B = discard \ No newline at end of file diff --git a/tests/constr/t18990.nim b/tests/constr/t18990.nim deleted file mode 100644 index 2f60f3c2c..000000000 --- a/tests/constr/t18990.nim +++ /dev/null @@ -1,3 +0,0 @@ -import a, b -discard A(1f, 1f) # works -proc x(b = A(1f, 1f)) = discard # doesn't work \ No newline at end of file diff --git a/tests/constr/tnocompiletimefunc.nim b/tests/constr/tnocompiletimefunc.nim deleted file mode 100644 index a95648c0f..000000000 --- a/tests/constr/tnocompiletimefunc.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - errormsg: "request to generate code for .compileTime proc: foo" -""" - -# ensure compileTime funcs can't be called from runtime - -func foo(a: int): int {.compileTime.} = - a * a - -proc doAThing(): int = - for i in 0..2: - result += foo(i) - -echo doAThing() diff --git a/tests/constr/tnocompiletimefunclambda.nim b/tests/constr/tnocompiletimefunclambda.nim deleted file mode 100644 index d134eea40..000000000 --- a/tests/constr/tnocompiletimefunclambda.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - errormsg: "request to generate code for .compileTime proc: :anonymous" -""" - -let a = func(a: varargs[int]) {.compileTime, closure.} = - discard a[0] \ No newline at end of file diff --git a/tests/controlflow/tcontrolflow.nim b/tests/controlflow/tcontrolflow.nim index dd21a2bb6..258f3f50d 100644 --- a/tests/controlflow/tcontrolflow.nim +++ b/tests/controlflow/tcontrolflow.nim @@ -19,7 +19,7 @@ block tbreak: run = false block myblock: if true: - break myblock + break echo "leaving myblock" x = true doAssert(x) @@ -95,22 +95,3 @@ block tnestif: else: writeLine(stdout, "looks like Python") #OUT i == 2 - -# bug https://github.com/nim-lang/RFCs/issues/451 -for i in 1..2: # works - break - -block: # works - for i in 1..2: - break - -block: # works - block: - discard 12 + 3 - for i in 1..2: - break - -block named: # works - if true: - break named - doAssert false, "not reached" diff --git a/tests/controlflow/tunamedbreak.nim b/tests/controlflow/tunamedbreak.nim deleted file mode 100644 index 46113cabc..000000000 --- a/tests/controlflow/tunamedbreak.nim +++ /dev/null @@ -1,15 +0,0 @@ - -discard """ - cmd: "nim check $file" - action: "reject" - nimout: ''' -tunamedbreak.nim(12, 5) Error: Using an unnamed break in a block is deprecated; Use a named block with a named break instead [UnnamedBreak] -tunamedbreak.nim(15, 3) Error: Using an unnamed break in a block is deprecated; Use a named block with a named break instead [UnnamedBreak] - ''' -""" -for i in 1..2: # errors - block: - break - -block: # errors - break diff --git a/tests/controlflow/tunreachable.nim b/tests/controlflow/tunreachable.nim deleted file mode 100644 index 11c8595eb..000000000 --- a/tests/controlflow/tunreachable.nim +++ /dev/null @@ -1,32 +0,0 @@ -discard """ - cmd: "nim check --warningAsError:UnreachableCode $file" - action: "reject" - nimout: ''' -tunreachable.nim(23, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode] -tunreachable.nim(30, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode] -''' -""" - -# bug #9839 -template myquit1():untyped= - ## foo - quit(1) -template myquit2():untyped= - echo 123 - myquit1() - -proc main1()= - - # BUG: uncommenting this doesn't give `Error: unreachable statement` - myquit2() - - echo "after" - -main1() - -proc main2() = - myquit1() - - echo "after" - -main2() diff --git a/tests/converter/m18986.nim b/tests/converter/m18986.nim deleted file mode 100644 index 0ebf343ae..000000000 --- a/tests/converter/m18986.nim +++ /dev/null @@ -1,3 +0,0 @@ -import std/macros - -converter Lit*(x: uint): NimNode = newLit(x) diff --git a/tests/converter/mdontleak.nim b/tests/converter/mdontleak.nim deleted file mode 100644 index e55c3f87c..000000000 --- a/tests/converter/mdontleak.nim +++ /dev/null @@ -1,3 +0,0 @@ - -converter toBool(x: uint32): bool = x != 0 -# Note: This convertes is not exported! diff --git a/tests/converter/t18986.nim b/tests/converter/t18986.nim deleted file mode 100644 index ef300fa49..000000000 --- a/tests/converter/t18986.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - output: "Found a 0" -""" - -import m18986 except Lit -import std/macros - -# bug #18986 -var x = 0.uint -echo "Found a ", x diff --git a/tests/converter/t7097.nim b/tests/converter/t7097.nim deleted file mode 100644 index d8e953080..000000000 --- a/tests/converter/t7097.nim +++ /dev/null @@ -1,38 +0,0 @@ -type - Byte* = uint8 - Bytes* = seq[Byte] - - BytesRange* = object - bytes: Bytes - ibegin, iend: int - -proc initBytesRange*(s: var Bytes, ibegin = 0, iend = -1): BytesRange = - let e = if iend < 0: s.len + iend + 1 - else: iend - assert ibegin > 0 and e <= s.len - when defined(gcRefc): - shallow(s) - result.bytes = s - result.ibegin = ibegin - result.iend = e - -template `[]=`*(r: var BytesRange, i: int, v: Byte) = - r.bytes[r.ibegin + i] = v - -converter fromSeq*(s: Bytes): BytesRange = - var seqCopy = s - return initBytesRange(seqCopy) - -type - Reader* = object - data: BytesRange - position: int - -proc readerFromHex*(input: string): Reader = - let totalBytes = input.len div 2 - var backingStore = newSeq[Byte](totalBytes) - result.data = initBytesRange(backingStore) - - for i in 0 ..< totalBytes: - var nextByte = 0 - result.data[i] = Byte(nextByte) # <-------- instantiated from here diff --git a/tests/converter/t7098.nim b/tests/converter/t7098.nim index 30c9c1e25..8e7634882 100644 --- a/tests/converter/t7098.nim +++ b/tests/converter/t7098.nim @@ -14,8 +14,8 @@ proc initBytesRange*(s: var Bytes, ibegin = 0, iend = -1): BytesRange = let e = if iend < 0: s.len + iend + 1 else: iend assert ibegin >= 0 and e <= s.len - when defined(gcRefc): - shallow(s) + + shallow(s) result.bytes = s result.ibegin = ibegin result.iend = e diff --git a/tests/converter/tconverter.nim b/tests/converter/tconverter.nim deleted file mode 100644 index 0bf067c55..000000000 --- a/tests/converter/tconverter.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - output: '''fooo fooo''' -""" - -converter intToString[T](i: T): string = "fooo" - -let - foo: string = 1 - bar: string = intToString(2) - -echo foo, " ", bar \ No newline at end of file diff --git a/tests/converter/tconverter_with_varargs.nim b/tests/converter/tconverter_with_varargs.nim index fae83221b..6d7e31e85 100644 --- a/tests/converter/tconverter_with_varargs.nim +++ b/tests/converter/tconverter_with_varargs.nim @@ -8,7 +8,7 @@ type converter to_py*(i: int) : PPyRef = nil when false: - proc to_tuple*(vals: openArray[PPyRef]): PPyRef = + proc to_tuple*(vals: openarray[PPyRef]): PPyRef = discard proc abc(args: varargs[PPyRef]) = diff --git a/tests/converter/tdontleak.nim b/tests/converter/tdontleak.nim deleted file mode 100644 index 4965fa90a..000000000 --- a/tests/converter/tdontleak.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - output: '''5''' -joinable: false -""" - -import mdontleak -# bug #19213 - -let a = 5'u32 -echo a diff --git a/tests/converter/texplicit_conversion.nim b/tests/converter/texplicit_conversion.nim index e36d78ad5..6b2e96faf 100644 --- a/tests/converter/texplicit_conversion.nim +++ b/tests/converter/texplicit_conversion.nim @@ -11,9 +11,3 @@ converter toInt(s: string): int = let x = (int)"234" echo x - -block: # Test for nkconv - proc foo(o: var int) = - assert o == 0 - var a = 0 - foo(int(a)) \ No newline at end of file diff --git a/tests/converter/tgenericconverter.nim b/tests/converter/tgenericconverter.nim index cbbd2e1b0..e1c9f7c4c 100644 --- a/tests/converter/tgenericconverter.nim +++ b/tests/converter/tgenericconverter.nim @@ -28,27 +28,3 @@ aa.x = 666 p aa q aa - - -#------------------------------------------------------------- -# issue #16651 -type - PointTup = tuple - x: float32 - y: float32 - -converter tupleToPoint[T1, T2: SomeFloat](self: tuple[x: T1, y: T2]): PointTup = - result = (self.x.float32, self.y.float32) - -proc tupleToPointX(self: tuple[x: SomeFloat, y: SomeFloat]): PointTup = - result = (self.x.float32, self.y.float32) - -proc tupleToPointX2(self: tuple[x: SomeFloat, y: distinct SomeFloat]): PointTup = - result = (self.x.float32, self.y.float32) - -var t1: PointTup = tupleToPointX((1.0, 0.0)) -var t2: PointTup = tupleToPointX2((1.0, 0.0)) -var t3: PointTup = tupleToPointX2((1.0'f32, 0.0)) -var t4: PointTup = tupleToPointX2((1.0, 0.0'f32)) - -var x2: PointTup = (1.0, 0.0) \ No newline at end of file diff --git a/tests/coroutines/tgc.nim b/tests/coroutines/tgc.nim index e2f8b6469..311ec5efc 100644 --- a/tests/coroutines/tgc.nim +++ b/tests/coroutines/tgc.nim @@ -1,22 +1,19 @@ discard """ - matrix: "--gc:refc; --gc:arc; --gc:orc" - target: "c" + targets: "c" """ -when compileOption("gc", "refc") or not defined(openbsd): - # xxx openbsd gave: stdlib_coro.nim.c:406:22: error: array type 'jmp_buf' (aka 'long [11]') is not assignable (*dest).execContext = src.execContext; - import coro +import coro - var maxOccupiedMemory = 0 +var maxOccupiedMemory = 0 - proc testGC() = - var numbers = newSeq[int](100) - maxOccupiedMemory = max(maxOccupiedMemory, getOccupiedMem()) - suspend(0) +proc testGC() = + var numbers = newSeq[int](100) + maxOccupiedMemory = max(maxOccupiedMemory, getOccupiedMem()) + suspend(0) - start(testGC) - start(testGC) - run() +start(testGC) +start(testGC) +run() - GC_fullCollect() - doAssert(getOccupiedMem() < maxOccupiedMemory, "GC did not free any memory allocated in coroutines") +GC_fullCollect() +doAssert(getOccupiedMem() < maxOccupiedMemory, "GC did not free any memory allocated in coroutines") diff --git a/tests/coroutines/twait.nim b/tests/coroutines/twait.nim index 71782ece1..348007afb 100644 --- a/tests/coroutines/twait.nim +++ b/tests/coroutines/twait.nim @@ -1,28 +1,20 @@ discard """ output: "Exit 1\nExit 2" - matrix: "--gc:refc; --gc:arc; --gc:orc" - target: "c" + targets: "c" """ +import coro -when compileOption("gc", "refc") or not defined(openbsd): - # xxx openbsd failed, see tgc.nim - import coro +var coro1: CoroutineRef - var coro1: CoroutineRef - - proc testCoroutine1() = - for i in 0..<10: - suspend(0) - echo "Exit 1" - - proc testCoroutine2() = - coro1.wait() - echo "Exit 2" - - coro1 = coro.start(testCoroutine1) - coro.start(testCoroutine2) - run() -else: - # workaround +proc testCoroutine1() = + for i in 0..<10: + suspend(0) echo "Exit 1" + +proc testCoroutine2() = + coro1.wait() echo "Exit 2" + +coro1 = coro.start(testCoroutine1) +coro.start(testCoroutine2) +run() diff --git a/tests/cpp/t12946.nim b/tests/cpp/t12946.nim deleted file mode 100644 index 79cd56251..000000000 --- a/tests/cpp/t12946.nim +++ /dev/null @@ -1,8 +0,0 @@ -discard """ - targets: "c cpp" -""" - -import std/atomics -type Futex = distinct Atomic[int32] - -var x: Futex diff --git a/tests/cpp/t4834.nim b/tests/cpp/t4834.nim deleted file mode 100644 index 0275b1b70..000000000 --- a/tests/cpp/t4834.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - targets: "cpp" -""" - -# issue #4834 -block: - defer: - let x = 0 - - -proc main() = - block: - defer: - raise newException(Exception, "foo") - -doAssertRaises(Exception): - main() diff --git a/tests/cpp/t6986.nim b/tests/cpp/t6986.nim index 16e455c3b..ffd277adb 100644 --- a/tests/cpp/t6986.nim +++ b/tests/cpp/t6986.nim @@ -5,9 +5,6 @@ discard """ import sequtils, strutils -when defined(nimPreviewSlimSystem): - import std/syncio - let rules = toSeq(lines("input")) .mapIt(it.split(" => ").mapIt(it.replace("/", ""))) diff --git a/tests/cpp/tasync_cpp.nim b/tests/cpp/tasync_cpp.nim index 3f4ec6208..a68be6cd5 100644 --- a/tests/cpp/tasync_cpp.nim +++ b/tests/cpp/tasync_cpp.nim @@ -1,7 +1,7 @@ discard """ targets: "cpp" output: "hello" - cmd: "nim cpp --clearNimblePath --nimblePath:build/deps/pkgs2 $file" + cmd: "nim cpp --nilseqs:on --nimblePath:tests/deps $file" """ # bug #3299 diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim index 9d49f2cbd..49fe8015b 100644 --- a/tests/cpp/tcovariancerules.nim +++ b/tests/cpp/tcovariancerules.nim @@ -31,7 +31,7 @@ import macros macro skipElse(n: untyped): untyped = n[0] template acceptWithCovariance(x, otherwise): untyped = - when defined nimEnableCovariance: + when nimEnableCovariance: x else: reject(x) @@ -79,16 +79,16 @@ proc wantsCovariantSeq2(s: seq[AnimalRef]) = proc wantsCovariantSeq3(s: seq[RefAlias[Animal]]) = for a in s: echo a.x -proc wantsCovariantOpenArray(s: openArray[ref Animal]) = +proc wantsCovariantOpenArray(s: openarray[ref Animal]) = for a in s: echo a.x -proc modifiesCovariantOpenArray(s: var openArray[ref Animal]) = +proc modifiesCovariantOpenArray(s: var openarray[ref Animal]) = for a in s: echo a.x -proc modifiesDerivedOpenArray(s: var openArray[ref Dog]) = +proc modifiesDerivedOpenArray(s: var openarray[ref Dog]) = for a in s: echo a.x -proc wantsNonCovariantOpenArray(s: openArray[Animal]) = +proc wantsNonCovariantOpenArray(s: openarray[Animal]) = for a in s: echo a.x proc wantsCovariantArray(s: array[2, ref Animal]) = diff --git a/tests/cpp/tdont_init_instantiation.nim b/tests/cpp/tdont_init_instantiation.nim index a13a3f6b4..fe487fba0 100644 --- a/tests/cpp/tdont_init_instantiation.nim +++ b/tests/cpp/tdont_init_instantiation.nim @@ -20,7 +20,7 @@ template class C { }; """.} -type C[X] {.importcpp, header: "", nodecl.} = object +type C{.importcpp, header: "", nodecl.} [X] = 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 9170be079..852537518 100644 --- a/tests/cpp/temitlist.nim +++ b/tests/cpp/temitlist.nim @@ -1,14 +1,12 @@ discard """ targets: "cpp" - output: ''' -6.0 + output: '''6.0 0''' -disabled: "windows" # pending bug #18011 """ # bug #4730 -type Vector*[T] {.importcpp: "std::vector", header: "".} = object +type Vector* {.importcpp: "std::vector", header: "".}[T] = object template `[]=`*[T](v: var Vector[T], key: int, val: T) = {.emit: [v, "[", key, "] = ", val, ";"].} diff --git a/tests/cpp/tempty_generic_obj.nim b/tests/cpp/tempty_generic_obj.nim index 6125190b4..913c1ec3c 100644 --- a/tests/cpp/tempty_generic_obj.nim +++ b/tests/cpp/tempty_generic_obj.nim @@ -1,9 +1,7 @@ discard """ targets: "cpp" - output: ''' -int + output: '''int float''' -disabled: "windows" # pending bug #18011 """ import typetraits diff --git a/tests/cpp/torc.nim b/tests/cpp/torc.nim deleted file mode 100644 index 64c6c0e5f..000000000 --- a/tests/cpp/torc.nim +++ /dev/null @@ -1,41 +0,0 @@ -discard """ - targets: "cpp" - matrix: "--gc:orc" -""" - -import std/options - -# bug #18410 -type - O = object of RootObj - val: pointer - -proc p(): Option[O] = none(O) - -doAssert $p() == "none(O)" - -# bug #17351 -type - Foo = object of RootObj - Foo2 = object of Foo - Bar = object - x: Foo2 - -var b = Bar() -discard b - -# bug #4678 -{.emit: """/*TYPESECTION*/ -enum class SomeEnum {A,B,C}; -""".} -type - EnumVector[T: enum] {.importcpp: "std::vector", header: "vector".} = object - SomeEnum {.importcpp, nodecl.} = enum - A,B,C - -proc asVector*[T](t: T): EnumVector[T] = - discard -# Nim generates this signature here: -# N_NIMCALL(std::vector<> , asvector_106028_3197418230)(SomeEnum t0) - -discard asVector(SomeEnum.A) diff --git a/tests/cpp/tretvar.nim b/tests/cpp/tretvar.nim index 0c3765346..83c37721e 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_t {.importcpp: "(#.length())", header: "".} +proc len(a: stdString): csize {.importcpp: "(#.length())", header: "".} -proc setChar(a: var stdString, i: csize_t, c: char) {.importcpp: "(#[#] = #)", header: "".} +proc setChar(a: var stdString, i: csize, 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 c3886c547..4d686955f 100644 --- a/tests/cpp/tvector_iterator.nim +++ b/tests/cpp/tvector_iterator.nim @@ -12,8 +12,8 @@ struct Vector { """.} type - Vector[T] {.importcpp: "Vector".} = object - VectorIterator[T] {.importcpp: "Vector<'0>::Iterator".} = object + Vector {.importcpp: "Vector".} [T] = object + VectorIterator {.importcpp: "Vector<'0>::Iterator".} [T] = object var x: VectorIterator[void] diff --git a/tests/deprecated/tconst.nim b/tests/deprecated/tconst.nim deleted file mode 100644 index 51eb6cb0e..000000000 --- a/tests/deprecated/tconst.nim +++ /dev/null @@ -1,8 +0,0 @@ -discard """ - nimout: ''' -tconst.nim(8, 9) Warning: abcd; foo is deprecated [Deprecated] -''' -""" - -const foo* {.deprecated: "abcd".} = 42 -discard foo diff --git a/tests/deprecated/tequalhook.nim b/tests/deprecated/tequalhook.nim deleted file mode 100644 index 79ee835f8..000000000 --- a/tests/deprecated/tequalhook.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - errormsg: "Overriding `=` hook is deprecated; Override `=copy` hook instead" - matrix: "--warningAsError[Deprecated]:on" -""" - -type - SharedString = object - data: string - -proc `=`(x: var SharedString, y: SharedString) = - discard \ No newline at end of file diff --git a/tests/deprecated/tmodule1.nim b/tests/deprecated/tmodule1.nim index f26e4ce3f..954836889 100644 --- a/tests/deprecated/tmodule1.nim +++ b/tests/deprecated/tmodule1.nim @@ -1,23 +1,13 @@ discard """ - matrix: "--hint:all:off" - nimoutFull: true - nimout: ''' -tmodule1.nim(21, 8) Warning: goodbye; importme is deprecated [Deprecated] -tmodule1.nim(24, 10) Warning: Ty is deprecated [Deprecated] -tmodule1.nim(27, 10) Warning: hello; Ty1 is deprecated [Deprecated] -tmodule1.nim(30, 8) Warning: aVar is deprecated [Deprecated] -tmodule1.nim(32, 3) Warning: aProc is deprecated [Deprecated] -tmodule1.nim(33, 3) Warning: hello; aProc1 is deprecated [Deprecated] + nimout: '''tmodule1.nim(11, 8) Warning: goodbye; importme is deprecated [Deprecated] +tmodule1.nim(14, 10) Warning: Ty is deprecated [Deprecated] +tmodule1.nim(17, 10) Warning: hello; Ty1 is deprecated [Deprecated] +tmodule1.nim(20, 8) Warning: aVar is deprecated [Deprecated] +tmodule1.nim(22, 3) Warning: aProc is deprecated [Deprecated] +tmodule1.nim(23, 3) Warning: hello; aProc1 is deprecated [Deprecated] ''' """ - - - - - - -# line 20 import importme block: diff --git a/tests/deps/jester-#head/jester.nim b/tests/deps/jester-#head/jester.nim new file mode 100644 index 000000000..1118214c2 --- /dev/null +++ b/tests/deps/jester-#head/jester.nim @@ -0,0 +1,1385 @@ +# Copyright (C) 2015 Dominik Picheta +# MIT License - Look at license.txt for details. +import net, strtabs, re, tables, os, strutils, uri, + times, mimetypes, asyncnet, asyncdispatch, macros, md5, + logging, httpcore, asyncfile, macrocache, json, options, + strformat + +import jester/private/[errorpages, utils] +import jester/[request, patterns] + +from cgi import decodeData, decodeUrl, CgiError + +export request +export strtabs +export tables +export httpcore +export MultiData +export HttpMethod +export asyncdispatch + +export SameSite + +when useHttpBeast: + import httpbeast except Settings, Request + import options + from nativesockets import close +else: + import asynchttpserver except Request + +type + MatchProc* = proc (request: Request): Future[ResponseData] {.gcsafe, closure.} + MatchProcSync* = proc (request: Request): ResponseData{.gcsafe, closure.} + + Matcher = object + case async: bool + of false: + syncProc: MatchProcSync + of true: + asyncProc: MatchProc + + ErrorProc* = proc ( + request: Request, error: RouteError + ): Future[ResponseData] {.gcsafe, closure.} + + Jester* = object + when not useHttpBeast: + httpServer*: AsyncHttpServer + settings: Settings + matchers: seq[Matcher] + errorHandlers: seq[ErrorProc] + + MatchType* = enum + MRegex, MSpecial, MStatic + + RawHeaders* = seq[tuple[key, val: string]] + ResponseData* = tuple[ + action: CallbackAction, + code: HttpCode, + headers: Option[RawHeaders], + content: string, + matched: bool + ] + + CallbackAction* = enum + TCActionNothing, TCActionSend, TCActionRaw, TCActionPass + + RouteErrorKind* = enum + RouteException, RouteCode + RouteError* = object + case kind*: RouteErrorKind + of RouteException: + exc: ref Exception + of RouteCode: + data: ResponseData + +const jesterVer = "0.4.3" + +proc toStr(headers: Option[RawHeaders]): string = + return $newHttpHeaders(headers.get(@({:}))) + +proc createHeaders(headers: RawHeaders): string = + result = "" + if headers.len > 0: + for header in headers: + let (key, value) = header + result.add(key & ": " & value & "\c\L") + + result = result[0 .. ^3] # Strip trailing \c\L + +proc createResponse(status: HttpCode, headers: RawHeaders): string = + return "HTTP/1.1 " & $status & "\c\L" & createHeaders(headers) & "\c\L\c\L" + +proc unsafeSend(request: Request, content: string) = + when useHttpBeast: + request.getNativeReq.unsafeSend(content) + else: + # TODO: This may cause issues if we send too fast. + asyncCheck request.getNativeReq.client.send(content) + +proc send( + request: Request, code: HttpCode, headers: Option[RawHeaders], body: string +): Future[void] = + when useHttpBeast: + let h = + if headers.isNone: "" + else: headers.get().createHeaders + request.getNativeReq.send(code, body, h) + var fut = newFuture[void]() + complete(fut) + return fut + else: + return request.getNativeReq.respond( + code, body, newHttpHeaders(headers.get(@({:}))) + ) + +proc statusContent(request: Request, status: HttpCode, content: string, + headers: Option[RawHeaders]): Future[void] = + try: + result = send(request, status, headers, content) + when not defined(release): + logging.debug(" $1 $2" % [$status, toStr(headers)]) + except: + logging.error("Could not send response: $1" % osErrorMsg(osLastError())) + +# TODO: Add support for proper Future Streams instead of this weird raw mode. +template enableRawMode* = + # TODO: Use the effect system to make this implicit? + result.action = TCActionRaw + +proc send*(request: Request, content: string) = + ## Sends ``content`` immediately to the client socket. + ## + ## Routes using this procedure must enable raw mode. + unsafeSend(request, content) + +proc sendHeaders*(request: Request, status: HttpCode, + headers: RawHeaders) = + ## Sends ``status`` and ``headers`` to the client socket immediately. + ## The user is then able to send the content immediately to the client on + ## the fly through the use of ``response.client``. + let headerData = createResponse(status, headers) + try: + request.send(headerData) + logging.debug(" $1 $2" % [$status, $headers]) + except: + logging.error("Could not send response: $1" % [osErrorMsg(osLastError())]) + +proc sendHeaders*(request: Request, status: HttpCode) = + ## Sends ``status`` and ``Content-Type: text/html`` as the headers to the + ## client socket immediately. + let headers = @({"Content-Type": "text/html;charset=utf-8"}) + request.sendHeaders(status, headers) + +proc sendHeaders*(request: Request) = + ## Sends ``Http200`` and ``Content-Type: text/html`` as the headers to the + ## client socket immediately. + request.sendHeaders(Http200) + +proc send*(request: Request, status: HttpCode, headers: RawHeaders, + content: string) = + ## Sends out a HTTP response comprising of the ``status``, ``headers`` and + ## ``content`` specified. + var headers = headers & @({"Content-Length": $content.len}) + request.sendHeaders(status, headers) + request.send(content) + +# TODO: Cannot capture 'paths: varargs[string]' here. +proc sendStaticIfExists( + req: Request, paths: seq[string] +): Future[HttpCode] {.async.} = + result = Http200 + for p in paths: + if existsFile(p): + + var fp = getFilePermissions(p) + if not fp.contains(fpOthersRead): + return Http403 + + let fileSize = getFileSize(p) + let ext = p.splitFile.ext + let mimetype = req.settings.mimes.getMimetype( + if ext.len > 0: ext[1 .. ^1] + else: "" + ) + if fileSize < 10_000_000: # 10 mb + var file = readFile(p) + + var hashed = getMD5(file) + + # If the user has a cached version of this file and it matches our + # version, let them use it + if req.headers.hasKey("If-None-Match") and req.headers["If-None-Match"] == hashed: + await req.statusContent(Http304, "", none[RawHeaders]()) + else: + await req.statusContent(Http200, file, some(@({ + "Content-Type": mimetype, + "ETag": hashed + }))) + else: + let headers = @({ + "Content-Type": mimetype, + "Content-Length": $fileSize + }) + await req.statusContent(Http200, "", some(headers)) + + var fileStream = newFutureStream[string]("sendStaticIfExists") + var file = openAsync(p, fmRead) + # Let `readToStream` write file data into fileStream in the + # background. + asyncCheck file.readToStream(fileStream) + # The `writeFromStream` proc will complete once all the data in the + # `bodyStream` has been written to the file. + while true: + let (hasValue, value) = await fileStream.read() + if hasValue: + req.unsafeSend(value) + else: + break + file.close() + + return + + # If we get to here then no match could be found. + return Http404 + +proc close*(request: Request) = + ## Closes client socket connection. + ## + ## Routes using this procedure must enable raw mode. + let nativeReq = request.getNativeReq() + when useHttpBeast: + nativeReq.forget() + nativeReq.client.close() + +proc defaultErrorFilter(error: RouteError): ResponseData = + case error.kind + of RouteException: + let e = error.exc + let traceback = getStackTrace(e) + var errorMsg = e.msg + if errorMsg.len == 0: errorMsg = "(empty)" + + let error = traceback & errorMsg + logging.error(error) + result.headers = some(@({ + "Content-Type": "text/html;charset=utf-8" + })) + result.content = routeException( + error.replace("\n", "
      \n"), + jesterVer + ) + result.code = Http502 + result.matched = true + result.action = TCActionSend + of RouteCode: + result.headers = some(@({ + "Content-Type": "text/html;charset=utf-8" + })) + result.content = error( + $error.data.code, + jesterVer + ) + result.code = error.data.code + result.matched = true + result.action = TCActionSend + +proc initRouteError(exc: ref Exception): RouteError = + RouteError( + kind: RouteException, + exc: exc + ) + +proc initRouteError(data: ResponseData): RouteError = + RouteError( + kind: RouteCode, + data: data + ) + +proc dispatchError( + jes: Jester, + request: Request, + error: RouteError +): Future[ResponseData] {.async.} = + for errorProc in jes.errorHandlers: + let data = await errorProc(request, error) + if data.matched: + return data + + return defaultErrorFilter(error) + +proc dispatch( + self: Jester, + req: Request +): Future[ResponseData] {.async.} = + for matcher in self.matchers: + if matcher.async: + let data = await matcher.asyncProc(req) + if data.matched: + return data + else: + let data = matcher.syncProc(req) + if data.matched: + return data + +proc handleFileRequest( + jes: Jester, req: Request +): Future[ResponseData] {.async.} = + # Find static file. + # TODO: Caching. + let path = normalizedPath( + jes.settings.staticDir / cgi.decodeUrl(req.pathInfo) + ) + + # Verify that this isn't outside our static dir. + var status = Http400 + let pathDir = path.splitFile.dir / "" + let staticDir = jes.settings.staticDir / "" + if pathDir.startsWith(staticDir): + if existsDir(path): + status = await sendStaticIfExists( + req, + @[path / "index.html", path / "index.htm"] + ) + else: + status = await sendStaticIfExists(req, @[path]) + + # Http200 means that the data was sent so there is nothing else to do. + if status == Http200: + result[0] = TCActionRaw + when not defined(release): + logging.debug(" -> $1" % path) + return + + return (TCActionSend, status, none[seq[(string, string)]](), "", true) + +proc handleRequestSlow( + jes: Jester, + req: Request, + respDataFut: Future[ResponseData] | ResponseData, + dispatchedError: bool +): Future[void] {.async.} = + var dispatchedError = dispatchedError + var respData: ResponseData + + # httpReq.send(Http200, "Hello, World!", "") + try: + when respDataFut is Future[ResponseData]: + respData = await respDataFut + else: + respData = respDataFut + except: + # Handle any errors by showing them in the browser. + # TODO: Improve the look of this. + let exc = getCurrentException() + respData = await dispatchError(jes, req, initRouteError(exc)) + dispatchedError = true + + # TODO: Put this in a custom matcher? + if not respData.matched: + respData = await handleFileRequest(jes, req) + + case respData.action + of TCActionSend: + if (respData.code.is4xx or respData.code.is5xx) and + not dispatchedError and respData.content.len == 0: + respData = await dispatchError(jes, req, initRouteError(respData)) + + await statusContent( + req, + respData.code, + respData.content, + respData.headers + ) + else: + when not defined(release): + logging.debug(" $1" % [$respData.action]) + + # Cannot close the client socket. AsyncHttpServer may be keeping it alive. + +proc handleRequest(jes: Jester, httpReq: NativeRequest): Future[void] = + var req = initRequest(httpReq, jes.settings) + try: + when not defined(release): + logging.debug("$1 $2" % [$req.reqMethod, req.pathInfo]) + + if likely(jes.matchers.len == 1 and not jes.matchers[0].async): + let respData = jes.matchers[0].syncProc(req) + if likely(respData.matched): + return statusContent( + req, + respData.code, + respData.content, + respData.headers + ) + else: + return handleRequestSlow(jes, req, respData, false) + else: + return handleRequestSlow(jes, req, dispatch(jes, req), false) + except: + let exc = getCurrentException() + let respDataFut = dispatchError(jes, req, initRouteError(exc)) + return handleRequestSlow(jes, req, respDataFut, true) + + assert(not result.isNil, "Expected handleRequest to return a valid future.") + +proc newSettings*( + port = Port(5000), staticDir = getCurrentDir() / "public", + appName = "", bindAddr = "", reusePort = false, + futureErrorHandler: proc (fut: Future[void]) {.closure, gcsafe.} = nil +): Settings = + result = Settings( + staticDir: staticDir, + appName: appName, + port: port, + bindAddr: bindAddr, + reusePort: reusePort, + futureErrorHandler: futureErrorHandler + ) + +proc register*(self: var Jester, matcher: MatchProc) = + ## Adds the specified matcher procedure to the specified Jester instance. + self.matchers.add( + Matcher( + async: true, + asyncProc: matcher + ) + ) + +proc register*(self: var Jester, matcher: MatchProcSync) = + ## Adds the specified matcher procedure to the specified Jester instance. + self.matchers.add( + Matcher( + async: false, + syncProc: matcher + ) + ) + +proc register*(self: var Jester, errorHandler: ErrorProc) = + ## Adds the specified error handler procedure to the specified Jester instance. + self.errorHandlers.add(errorHandler) + +proc initJester*( + settings: Settings = newSettings() +): Jester = + result.settings = settings + result.settings.mimes = newMimetypes() + result.matchers = @[] + result.errorHandlers = @[] + +proc initJester*( + matcher: MatchProc, + settings: Settings = newSettings() +): Jester = + result = initJester(settings) + result.register(matcher) + +proc initJester*( + matcher: MatchProcSync, # TODO: Annoying nim bug: `MatchProc | MatchProcSync` doesn't work. + settings: Settings = newSettings() +): Jester = + result = initJester(settings) + result.register(matcher) + +proc serve*( + self: var Jester +) = + ## Creates a new async http server instance and registers + ## it with the dispatcher. + ## + ## The event loop is executed by this function, so it will block forever. + + # Ensure we have at least one logger enabled, defaulting to console. + if logging.getHandlers().len == 0: + addHandler(logging.newConsoleLogger()) + setLogFilter(when defined(release): lvlInfo else: lvlDebug) + + if self.settings.bindAddr.len > 0: + logging.info("Jester is making jokes at http://$1:$2$3" % + [ + self.settings.bindAddr, $self.settings.port, self.settings.appName + ] + ) + else: + when defined(windows): + logging.info("Jester is making jokes at http://127.0.0.1:$1$2 (all interfaces)" % + [$self.settings.port, self.settings.appName]) + else: + logging.info("Jester is making jokes at http://0.0.0.0:$1$2" % + [$self.settings.port, self.settings.appName]) + + var jes = self + when useHttpBeast: + run( + proc (req: httpbeast.Request): Future[void] = + {.gcsafe.}: + result = handleRequest(jes, req), + httpbeast.initSettings(self.settings.port, self.settings.bindAddr) + ) + else: + self.httpServer = newAsyncHttpServer(reusePort=self.settings.reusePort) + let serveFut = self.httpServer.serve( + self.settings.port, + proc (req: asynchttpserver.Request): Future[void] {.gcsafe, closure.} = + result = handleRequest(jes, req), + self.settings.bindAddr) + if not self.settings.futureErrorHandler.isNil: + serveFut.callback = self.settings.futureErrorHandler + else: + asyncCheck serveFut + runForever() + +template setHeader(headers: var Option[RawHeaders], key, value: string): typed = + bind isNone + if isNone(headers): + headers = some(@({key: value})) + else: + block outer: + # Overwrite key if it exists. + var h = headers.get() + for i in 0 ..< h.len: + if h[i][0] == key: + h[i][1] = value + headers = some(h) + break outer + + # Add key if it doesn't exist. + headers = some(h & @({key: value})) + +template resp*(code: HttpCode, + headers: openarray[tuple[key, val: string]], + content: string): typed = + ## Sets ``(code, headers, content)`` as the response. + bind TCActionSend + result = (TCActionSend, code, none[RawHeaders](), content, true) + for header in headers: + setHeader(result[2], header[0], header[1]) + break route + + +template resp*(content: string, contentType = "text/html;charset=utf-8"): typed = + ## Sets ``content`` as the response; ``Http200`` as the status code + ## and ``contentType`` as the Content-Type. + bind TCActionSend, newHttpHeaders, strtabs.`[]=` + result[0] = TCActionSend + result[1] = Http200 + setHeader(result[2], "Content-Type", contentType) + result[3] = content + # This will be set by our macro, so this is here for those not using it. + result.matched = true + break route + +template resp*(content: JsonNode): typed = + ## Serializes ``content`` as the response, sets ``Http200`` as status code + ## and "application/json" Content-Type. + resp($content, contentType="application/json") + +template resp*(code: HttpCode, content: string, + contentType = "text/html;charset=utf-8"): typed = + ## Sets ``content`` as the response; ``code`` as the status code + ## and ``contentType`` as the Content-Type. + bind TCActionSend, newHttpHeaders + result[0] = TCActionSend + result[1] = code + setHeader(result[2], "Content-Type", contentType) + result[3] = content + result.matched = true + break route + +template resp*(code: HttpCode): typed = + ## Responds with the specified ``HttpCode``. This ensures that error handlers + ## are called. + bind TCActionSend, newHttpHeaders + result[0] = TCActionSend + result[1] = code + result.matched = true + break route + +template redirect*(url: string): typed = + ## Redirects to ``url``. Returns from this request handler immediately. + ## Any set response headers are preserved for this request. + bind TCActionSend, newHttpHeaders + result[0] = TCActionSend + result[1] = Http303 + setHeader(result[2], "Location", url) + result[3] = "" + result.matched = true + break route + +template pass*(): typed = + ## Skips this request handler. + ## + ## If you want to stop this request from going further use ``halt``. + result.action = TCActionPass + break outerRoute + +template cond*(condition: bool): typed = + ## If ``condition`` is ``False`` then ``pass`` will be called, + ## i.e. this request handler will be skipped. + if not condition: break outerRoute + +template halt*(code: HttpCode, + headers: openarray[tuple[key, val: string]], + content: string): typed = + ## Immediately replies with the specified request. This means any further + ## code will not be executed after calling this template in the current + ## route. + bind TCActionSend, newHttpHeaders + result[0] = TCActionSend + result[1] = code + result[2] = some(@headers) + result[3] = content + result.matched = true + break allRoutes + +template halt*(): typed = + ## Halts the execution of this request immediately. Returns a 404. + ## All previously set values are **discarded**. + halt(Http404, {"Content-Type": "text/html;charset=utf-8"}, error($Http404, jesterVer)) + +template halt*(code: HttpCode): typed = + halt(code, {"Content-Type": "text/html;charset=utf-8"}, error($code, jesterVer)) + +template halt*(content: string): typed = + halt(Http404, {"Content-Type": "text/html;charset=utf-8"}, content) + +template halt*(code: HttpCode, content: string): typed = + halt(code, {"Content-Type": "text/html;charset=utf-8"}, content) + +template attachment*(filename = ""): typed = + ## Instructs the browser that the response should be stored on disk + ## rather than displayed in the browser. + var disposition = "attachment" + if filename != "": + disposition.add("; filename=\"" & extractFilename(filename) & "\"") + let ext = splitFile(filename).ext + let contentTypeSet = + isSome(result[2]) and result[2].get().toTable.hasKey("Content-Type") + if not contentTypeSet and ext != "": + setHeader(result[2], "Content-Type", getMimetype(request.settings.mimes, ext)) + setHeader(result[2], "Content-Disposition", disposition) + +template sendFile*(filename: string): typed = + ## Sends the file at the specified filename as the response. + result[0] = TCActionRaw + let sendFut = sendStaticIfExists(request, @[filename]) + yield sendFut + let status = sendFut.read() + if status != Http200: + raise newException(JesterError, "Couldn't send requested file: " & filename) + # This will be set by our macro, so this is here for those not using it. + result.matched = true + break route + +template `@`*(s: string): untyped = + ## Retrieves the parameter ``s`` from ``request.params``. ``""`` will be + ## returned if parameter doesn't exist. + if s in params(request): + # TODO: Why does request.params not work? :( + # TODO: This is some weird bug with macros/templates, I couldn't + # TODO: reproduce it easily. + params(request)[s] + else: + "" + +proc setStaticDir*(request: Request, dir: string) = + ## Sets the directory in which Jester will look for static files. It is + ## ``./public`` by default. + ## + ## The files will be served like so: + ## + ## ./public/css/style.css ``->`` http://example.com/css/style.css + ## + ## (``./public`` is not included in the final URL) + request.settings.staticDir = dir + +proc getStaticDir*(request: Request): string = + ## Gets the directory in which Jester will look for static files. + ## + ## ``./public`` by default. + return request.settings.staticDir + +proc makeUri*(request: Request, address = "", absolute = true, + addScriptName = true): string = + ## Creates a URI based on the current request. If ``absolute`` is true it will + ## add the scheme (Usually 'http://'), `request.host` and `request.port`. + ## If ``addScriptName`` is true `request.appName` will be prepended before + ## ``address``. + + # Check if address already starts with scheme:// + var uri = parseUri(address) + + if uri.scheme != "": return address + uri.path = "/" + uri.query = "" + uri.anchor = "" + if absolute: + uri.hostname = request.host + uri.scheme = (if request.secure: "https" else: "http") + if request.port != (if request.secure: 443 else: 80): + uri.port = $request.port + + if addScriptName: uri = uri / request.appName + if address != "": + uri = uri / address + else: + uri = uri / request.pathInfo + return $uri + +template uri*(address = "", absolute = true, addScriptName = true): untyped = + ## Convenience template which can be used in a route. + request.makeUri(address, absolute, addScriptName) + +proc daysForward*(days: int): DateTime = + ## Returns a DateTime object referring to the current time plus ``days``. + return getTime().utc + initTimeInterval(days = days) + +template setCookie*(name, value: string, expires="", + sameSite: SameSite=Lax, secure = false, + httpOnly = false, domain = "", path = "") = + ## Creates a cookie which stores ``value`` under ``name``. + ## + ## The SameSite argument determines the level of CSRF protection that + ## you wish to adopt for this cookie. It's set to Lax by default which + ## should protect you from most vulnerabilities. Note that this is only + ## supported by some browsers: + ## https://caniuse.com/#feat=same-site-cookie-attribute + let newCookie = makeCookie(name, value, expires, domain, path, secure, httpOnly, sameSite) + if isSome(result[2]) and + (let headers = result[2].get(); headers.toTable.hasKey("Set-Cookie")): + result[2] = some(headers & @({"Set-Cookie": newCookie})) + else: + setHeader(result[2], "Set-Cookie", newCookie) + +template setCookie*(name, value: string, expires: DateTime, + sameSite: SameSite=Lax, secure = false, + httpOnly = false, domain = "", path = "") = + ## Creates a cookie which stores ``value`` under ``name``. + setCookie(name, value, + format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"), + sameSite, secure, httpOnly, domain, path) + +proc normalizeUri*(uri: string): string = + ## Remove any trailing ``/``. + if uri[uri.len-1] == '/': result = uri[0 .. uri.len-2] + else: result = uri + +# -- Macro + +proc checkAction*(respData: var ResponseData): bool = + case respData.action + of TCActionSend, TCActionRaw: + result = true + of TCActionPass: + result = false + of TCActionNothing: + raise newException( + ValueError, + "Missing route action, did you forget to use `resp` in your route?" + ) + +proc skipDo(node: NimNode): NimNode {.compiletime.} = + if node.kind == nnkDo: + result = node[6] + else: + result = node + +proc ctParsePattern(pattern, pathPrefix: string): NimNode {.compiletime.} = + result = newNimNode(nnkPrefix) + result.add newIdentNode("@") + result.add newNimNode(nnkBracket) + + proc addPattNode(res: var NimNode, typ, text, + optional: NimNode) {.compiletime.} = + var objConstr = newNimNode(nnkObjConstr) + + objConstr.add bindSym("Node") + objConstr.add newNimNode(nnkExprColonExpr).add( + newIdentNode("typ"), typ) + objConstr.add newNimNode(nnkExprColonExpr).add( + newIdentNode("text"), text) + objConstr.add newNimNode(nnkExprColonExpr).add( + newIdentNode("optional"), optional) + + res[1].add objConstr + + var patt = parsePattern(pattern) + if pathPrefix.len > 0: + result.addPattNode( + bindSym("NodeText"), # Node kind + newStrLitNode(pathPrefix), # Text + newIdentNode("false") # Optional? + ) + + for node in patt: + result.addPattNode( + case node.typ + of NodeText: bindSym("NodeText") + of NodeField: bindSym("NodeField"), + newStrLitNode(node.text), + newIdentNode(if node.optional: "true" else: "false")) + +template setDefaultResp*() = + # TODO: bindSym this in the 'routes' macro and put it in each route + bind TCActionNothing, newHttpHeaders + result.action = TCActionNothing + result.code = Http200 + result.content = "" + +template declareSettings() {.dirty.} = + when not declaredInScope(settings): + var settings = newSettings() + +proc createJesterPattern( + routeNode, patternMatchSym: NimNode, + pathPrefix: string +): NimNode {.compileTime.} = + var ctPattern = ctParsePattern(routeNode[1].strVal, pathPrefix) + # -> let = .match(request.path) + return newLetStmt(patternMatchSym, + newCall(bindSym"match", ctPattern, parseExpr("request.pathInfo"))) + +proc escapeRegex(s: string): string = + result = "" + for i in s: + case i + # https://stackoverflow.com/a/400316/492186 + of '.', '^', '$', '*', '+', '?', '(', ')', '[', '{', '\\', '|': + result.add('\\') + result.add(i) + else: + result.add(i) + +proc createRegexPattern( + routeNode, reMatchesSym, patternMatchSym: NimNode, + pathPrefix: string +): NimNode {.compileTime.} = + # -> let = find(request.pathInfo, , ) + var strNode = routeNode[1].copyNimTree() + strNode[1].strVal = escapeRegex(pathPrefix) & strNode[1].strVal + return newLetStmt( + patternMatchSym, + newCall( + bindSym"find", + parseExpr("request.pathInfo"), + strNode, + reMatchesSym + ) + ) + +proc determinePatternType(pattern: NimNode): MatchType {.compileTime.} = + case pattern.kind + of nnkStrLit: + var patt = parsePattern(pattern.strVal) + if patt.len == 1 and patt[0].typ == NodeText: + return MStatic + else: + return MSpecial + of nnkCallStrLit: + expectKind(pattern[0], nnkIdent) + case ($pattern[0]).normalize + of "re": return MRegex + else: + macros.error("Invalid pattern type: " & $pattern[0]) + else: + macros.error("Unexpected node kind: " & $pattern.kind) + +proc createCheckActionIf(): NimNode = + var checkActionIf = parseExpr( + "if checkAction(result): result.matched = true; break routesList" + ) + checkActionIf[0][0][0] = bindSym"checkAction" + return checkActionIf + +proc createGlobalMetaRoute(routeNode, dest: NimNode) {.compileTime.} = + ## Creates a ``before`` or ``after`` route with no pattern, i.e. one which + ## will be always executed. + + # -> block route: + var innerBlockStmt = newStmtList( + newNimNode(nnkBlockStmt).add(newIdentNode("route"), routeNode[1].skipDo()) + ) + + # -> block outerRoute: + var blockStmt = newNimNode(nnkBlockStmt).add( + newIdentNode("outerRoute"), innerBlockStmt) + dest.add blockStmt + +proc createRoute( + routeNode, dest: NimNode, pathPrefix: string, isMetaRoute: bool = false +) {.compileTime.} = + ## Creates code which checks whether the current request path + ## matches a route. + ## + ## The `isMetaRoute` parameter determines whether the route to be created is + ## one of either a ``before`` or an ``after`` route. + + var patternMatchSym = genSym(nskLet, "patternMatchRet") + + # Only used for Regex patterns. + var reMatchesSym = genSym(nskVar, "reMatches") + var reMatches = parseExpr("var reMatches: array[20, string]") + reMatches[0][0] = reMatchesSym + reMatches[0][1][1] = bindSym("MaxSubpatterns") + + let patternType = determinePatternType(routeNode[1]) + case patternType + of MStatic: + discard + of MSpecial: + dest.add createJesterPattern(routeNode, patternMatchSym, pathPrefix) + of MRegex: + dest.add reMatches + dest.add createRegexPattern( + routeNode, reMatchesSym, patternMatchSym, pathPrefix + ) + + var ifStmtBody = newStmtList() + case patternType + of MStatic: discard + of MSpecial: + # -> setPatternParams(request, ret.params) + ifStmtBody.add newCall(bindSym"setPatternParams", newIdentNode"request", + newDotExpr(patternMatchSym, newIdentNode"params")) + of MRegex: + # -> setReMatches(request, ) + ifStmtBody.add newCall(bindSym"setReMatches", newIdentNode"request", + reMatchesSym) + + ifStmtBody.add routeNode[2].skipDo() + + let checkActionIf = + if isMetaRoute: + parseExpr("break routesList") + else: + createCheckActionIf() + # -> block route: ; + var innerBlockStmt = newStmtList( + newNimNode(nnkBlockStmt).add(newIdentNode("route"), ifStmtBody), + checkActionIf + ) + + let ifCond = + case patternType + of MStatic: + infix( + parseExpr("request.pathInfo"), + "==", + newStrLitNode(pathPrefix & routeNode[1].strVal) + ) + of MSpecial: + newDotExpr(patternMatchSym, newIdentNode("matched")) + of MRegex: + infix(patternMatchSym, "!=", newIntLitNode(-1)) + + # -> if .matched: + var ifStmt = newIfStmt((ifCond, innerBlockStmt)) + + # -> block outerRoute: + var blockStmt = newNimNode(nnkBlockStmt).add( + newIdentNode("outerRoute"), ifStmt) + dest.add blockStmt + +proc createError( + errorNode: NimNode, + httpCodeBranches, + exceptionBranches: var seq[tuple[cond, body: NimNode]] +) = + if errorNode.len != 3: + error("Missing error condition or body.", errorNode) + + let routeIdent = newIdentNode("route") + let outerRouteIdent = newIdentNode("outerRoute") + let checkActionIf = createCheckActionIf() + let exceptionIdent = newIdentNode("exception") + let errorIdent = newIdentNode("error") # TODO: Ugh. I shouldn't need these... + let errorCond = errorNode[1] + let errorBody = errorNode[2] + let body = quote do: + block `outerRouteIdent`: + block `routeIdent`: + `errorBody` + `checkActionIf` + + case errorCond.kind + of nnkIdent: + let name = errorCond.strVal + if name.len == 7 and name.startsWith("Http"): + # HttpCode. + httpCodeBranches.add( + ( + infix(parseExpr("error.data.code"), "==", errorCond), + body + ) + ) + else: + # Exception + exceptionBranches.add( + ( + infix(parseExpr("error.exc"), "of", errorCond), + quote do: + let `exceptionIdent` = (ref `errorCond`)(`errorIdent`.exc) + `body` + ) + ) + of nnkCurly: + expectKind(errorCond[0], nnkInfix) + httpCodeBranches.add( + ( + infix(parseExpr("error.data.code"), "in", errorCond), + body + ) + ) + else: + error("Expected exception type or set[HttpCode].", errorCond) + +const definedRoutes = CacheTable"jester.routes" + +proc processRoutesBody( + body: NimNode, + # For HTTP methods. + caseStmtGetBody, + caseStmtPostBody, + caseStmtPutBody, + caseStmtDeleteBody, + caseStmtHeadBody, + caseStmtOptionsBody, + caseStmtTraceBody, + caseStmtConnectBody, + caseStmtPatchBody: var NimNode, + # For `error`. + httpCodeBranches, + exceptionBranches: var seq[tuple[cond, body: NimNode]], + # For before/after stmts. + beforeStmts, + afterStmts: var NimNode, + # For other statements. + outsideStmts: var NimNode, + pathPrefix: string +) = + for i in 0.. 1: + extend[2].strVal + else: + "" + if prefix.len != 0 and prefix[0] != '/': + error("Path prefix for extended route must start with '/'", extend[2]) + + processRoutesBody( + definedRoutes[extend[1].strVal], + caseStmtGetBody, + caseStmtPostBody, + caseStmtPutBody, + caseStmtDeleteBody, + caseStmtHeadBody, + caseStmtOptionsBody, + caseStmtTraceBody, + caseStmtConnectBody, + caseStmtPatchBody, + httpCodeBranches, + exceptionBranches, + beforeStmts, + afterStmts, + outsideStmts, + pathPrefix & prefix + ) + else: + outsideStmts.add(body[i]) + of nnkCommentStmt: + discard + of nnkPragma: + if body[i][0].strVal.normalize notin ["async", "sync"]: + outsideStmts.add(body[i]) + else: + outsideStmts.add(body[i]) + +type + NeedsAsync = enum + ImplicitTrue, ImplicitFalse, ExplicitTrue, ExplicitFalse +proc needsAsync(node: NimNode): NeedsAsync = + result = ImplicitFalse + case node.kind + of nnkCommand, nnkCall: + if node[0].kind == nnkIdent: + case node[0].strVal.normalize + of "await", "sendfile": + return ImplicitTrue + of "resp", "halt", "attachment", "pass", "redirect", "cond", "get", + "post", "patch", "delete": + # This is just a simple heuristic. It's by no means meant to be + # exhaustive. + discard + else: + return ImplicitTrue + of nnkYieldStmt: + return ImplicitTrue + of nnkPragma: + if node[0].kind == nnkIdent: + case node[0].strVal.normalize + of "sync": + return ExplicitFalse + of "async": + return ExplicitTrue + else: discard + else: discard + + for c in node: + let r = needsAsync(c) + if r in {ImplicitTrue, ExplicitTrue, ExplicitFalse}: return r + +proc routesEx(name: string, body: NimNode): NimNode = + # echo(treeRepr(body)) + # echo(treeRepr(name)) + + # Save this route's body so that it can be incorporated into another route. + definedRoutes[name] = body.copyNimTree + + result = newStmtList() + + # -> declareSettings() + result.add newCall(bindSym"declareSettings") + + var outsideStmts = newStmtList() + + var matchBody = newNimNode(nnkStmtList) + let setDefaultRespIdent = bindSym"setDefaultResp" + matchBody.add newCall(setDefaultRespIdent) + # TODO: This diminishes the performance. Would be nice to only include it + # TODO: when setPatternParams or setReMatches is used. + matchBody.add parseExpr("var request = request") + + # HTTP router case statement nodes: + var caseStmt = newNimNode(nnkCaseStmt) + caseStmt.add parseExpr("request.reqMethod") + + var caseStmtGetBody = newNimNode(nnkStmtList) + var caseStmtPostBody = newNimNode(nnkStmtList) + var caseStmtPutBody = newNimNode(nnkStmtList) + var caseStmtDeleteBody = newNimNode(nnkStmtList) + var caseStmtHeadBody = newNimNode(nnkStmtList) + var caseStmtOptionsBody = newNimNode(nnkStmtList) + var caseStmtTraceBody = newNimNode(nnkStmtList) + var caseStmtConnectBody = newNimNode(nnkStmtList) + var caseStmtPatchBody = newNimNode(nnkStmtList) + + # Error handler nodes: + var httpCodeBranches: seq[tuple[cond, body: NimNode]] = @[] + var exceptionBranches: seq[tuple[cond, body: NimNode]] = @[] + + # Before/After nodes: + var beforeRoutes = newStmtList() + var afterRoutes = newStmtList() + + processRoutesBody( + body, + caseStmtGetBody, + caseStmtPostBody, + caseStmtPutBody, + caseStmtDeleteBody, + caseStmtHeadBody, + caseStmtOptionsBody, + caseStmtTraceBody, + caseStmtConnectBody, + caseStmtPatchBody, + httpCodeBranches, + exceptionBranches, + beforeRoutes, + afterRoutes, + outsideStmts, + "" + ) + + var ofBranchGet = newNimNode(nnkOfBranch) + ofBranchGet.add newIdentNode("HttpGet") + ofBranchGet.add caseStmtGetBody + caseStmt.add ofBranchGet + + var ofBranchPost = newNimNode(nnkOfBranch) + ofBranchPost.add newIdentNode("HttpPost") + ofBranchPost.add caseStmtPostBody + caseStmt.add ofBranchPost + + var ofBranchPut = newNimNode(nnkOfBranch) + ofBranchPut.add newIdentNode("HttpPut") + ofBranchPut.add caseStmtPutBody + caseStmt.add ofBranchPut + + var ofBranchDelete = newNimNode(nnkOfBranch) + ofBranchDelete.add newIdentNode("HttpDelete") + ofBranchDelete.add caseStmtDeleteBody + caseStmt.add ofBranchDelete + + var ofBranchHead = newNimNode(nnkOfBranch) + ofBranchHead.add newIdentNode("HttpHead") + ofBranchHead.add caseStmtHeadBody + caseStmt.add ofBranchHead + + var ofBranchOptions = newNimNode(nnkOfBranch) + ofBranchOptions.add newIdentNode("HttpOptions") + ofBranchOptions.add caseStmtOptionsBody + caseStmt.add ofBranchOptions + + var ofBranchTrace = newNimNode(nnkOfBranch) + ofBranchTrace.add newIdentNode("HttpTrace") + ofBranchTrace.add caseStmtTraceBody + caseStmt.add ofBranchTrace + + var ofBranchConnect = newNimNode(nnkOfBranch) + ofBranchConnect.add newIdentNode("HttpConnect") + ofBranchConnect.add caseStmtConnectBody + caseStmt.add ofBranchConnect + + var ofBranchPatch = newNimNode(nnkOfBranch) + ofBranchPatch.add newIdentNode("HttpPatch") + ofBranchPatch.add caseStmtPatchBody + caseStmt.add ofBranchPatch + + # Wrap the routes inside ``routesList`` blocks accordingly, and add them to + # the `match` procedure body. + let routesListIdent = newIdentNode("routesList") + matchBody.add( + quote do: + block `routesListIdent`: + `beforeRoutes` + ) + + matchBody.add( + quote do: + block `routesListIdent`: + `caseStmt` + ) + + matchBody.add( + quote do: + block `routesListIdent`: + `afterRoutes` + ) + + let matchIdent = newIdentNode(name) + let reqIdent = newIdentNode("request") + let needsAsync = needsAsync(body) + case needsAsync + of ImplicitFalse, ExplicitFalse: + hint(fmt"Synchronous route `{name}` has been optimised. Use `{{.async.}}` to change.") + of ImplicitTrue, ExplicitTrue: + hint(fmt"Asynchronous route: {name}.") + var matchProc = + if needsAsync in {ImplicitTrue, ExplicitTrue}: + quote do: + proc `matchIdent`( + `reqIdent`: Request + ): Future[ResponseData] {.async, gcsafe.} = + discard + else: + quote do: + proc `matchIdent`( + `reqIdent`: Request + ): ResponseData {.gcsafe.} = + discard + + # The following `block` is for `halt`. (`return` didn't work :/) + let allRoutesBlock = newTree( + nnkBlockStmt, + newIdentNode("allRoutes"), + matchBody + ) + matchProc[6] = newTree(nnkStmtList, allRoutesBlock) + result.add(outsideStmts) + result.add(matchProc) + + # Error handler proc + let errorHandlerIdent = newIdentNode(name & "ErrorHandler") + let errorIdent = newIdentNode("error") + let exceptionIdent = newIdentNode("exception") + let resultIdent = newIdentNode("result") + var errorHandlerProc = quote do: + proc `errorHandlerIdent`( + `reqIdent`: Request, `errorIdent`: RouteError + ): Future[ResponseData] {.gcsafe, async.} = + block `routesListIdent`: + `setDefaultRespIdent`() + case `errorIdent`.kind + of RouteException: + discard + of RouteCode: + discard + if exceptionBranches.len != 0: + var stmts = newStmtList() + for branch in exceptionBranches: + stmts.add(newIfStmt(branch)) + errorHandlerProc[6][0][1][^1][1][1][0] = stmts + if httpCodeBranches.len != 0: + var stmts = newStmtList() + for branch in httpCodeBranches: + stmts.add(newIfStmt(branch)) + errorHandlerProc[6][0][1][^1][2][1][0] = stmts + result.add(errorHandlerProc) + + # TODO: Replace `body`, `headers`, `code` in routes with `result[i]` to + # get these shortcuts back without sacrificing usability. + # TODO2: Make sure you replace what `guessAction` used to do for this. + + # echo toStrLit(result) + # echo treeRepr(result) + +macro routes*(body: untyped) = + result = routesEx("match", body) + let jesIdent = genSym(nskVar, "jes") + let matchIdent = newIdentNode("match") + let errorHandlerIdent = newIdentNode("matchErrorHandler") + let settingsIdent = newIdentNode("settings") + result.add( + quote do: + var `jesIdent` = initJester(`matchIdent`, `settingsIdent`) + `jesIdent`.register(`errorHandlerIdent`) + ) + result.add( + quote do: + serve(`jesIdent`) + ) + +macro router*(name: untyped, body: untyped) = + if name.kind != nnkIdent: + error("Need an ident.", name) + + routesEx(strVal(name), body) + +macro settings*(body: untyped) = + #echo(treeRepr(body)) + expectKind(body, nnkStmtList) + + result = newStmtList() + + # var settings = newSettings() + let settingsIdent = newIdentNode("settings") + result.add newVarStmt(settingsIdent, newCall("newSettings")) + + for asgn in body.children: + expectKind(asgn, nnkAsgn) + result.add newAssignment(newDotExpr(settingsIdent, asgn[0]), asgn[1]) diff --git a/tests/deps/jester-#head/jester.nimble b/tests/deps/jester-#head/jester.nimble new file mode 100644 index 000000000..a1306c525 --- /dev/null +++ b/tests/deps/jester-#head/jester.nimble @@ -0,0 +1,22 @@ +# Package + +version = "0.4.3" # Be sure to update jester.jesterVer too! +author = "Dominik Picheta" +description = "A sinatra-like web framework for Nim." +license = "MIT" + +skipFiles = @["todo.markdown"] +skipDirs = @["tests"] + +# Deps + +requires "nim >= 0.18.1" + +when not defined(windows): + requires "httpbeast >= 0.2.2" + +# For tests +requires "https://github.com/timotheecour/asynctools#pr_fix_compilation" + +task test, "Runs the test suite.": + exec "nimble c -y -r tests/tester" diff --git a/tests/deps/jester-#head/jester/patterns.nim b/tests/deps/jester-#head/jester/patterns.nim new file mode 100644 index 000000000..03d41796e --- /dev/null +++ b/tests/deps/jester-#head/jester/patterns.nim @@ -0,0 +1,141 @@ +# Copyright (C) 2012-2018 Dominik Picheta +# MIT License - Look at license.txt for details. +import parseutils, tables +type + NodeType* = enum + NodeText, NodeField + Node* = object + typ*: NodeType + text*: string + optional*: bool + + Pattern* = seq[Node] + +#/show/@id/? +proc parsePattern*(pattern: string): Pattern = + result = @[] + template addNode(result: var Pattern, theT: NodeType, theText: string, + isOptional: bool) = + block: + var newNode: Node + newNode.typ = theT + newNode.text = theText + newNode.optional = isOptional + result.add(newNode) + + template `{}`(s: string, i: int): char = + if i >= len(s): + '\0' + else: + s[i] + + var i = 0 + var text = "" + while i < pattern.len(): + case pattern[i] + of '@': + # Add the stored text. + if text != "": + result.addNode(NodeText, text, false) + text = "" + # Parse named parameter. + inc(i) # Skip @ + var nparam = "" + i += pattern.parseUntil(nparam, {'/', '?'}, i) + var optional = pattern{i} == '?' + result.addNode(NodeField, nparam, optional) + if pattern{i} == '?': inc(i) # Only skip ?. / should not be skipped. + of '?': + var optionalChar = text[^1] + setLen(text, text.len-1) # Truncate ``text``. + # Add the stored text. + if text != "": + result.addNode(NodeText, text, false) + text = "" + # Add optional char. + inc(i) # Skip ? + result.addNode(NodeText, $optionalChar, true) + of '\\': + inc i # Skip \ + if pattern[i] notin {'?', '@', '\\'}: + raise newException(ValueError, + "This character does not require escaping: " & pattern[i]) + text.add(pattern{i}) + inc i # Skip ``pattern[i]`` + else: + text.add(pattern{i}) + inc(i) + + if text != "": + result.addNode(NodeText, text, false) + +proc findNextText(pattern: Pattern, i: int, toNode: var Node): bool = + ## Finds the next NodeText in the pattern, starts looking from ``i``. + result = false + for n in i..pattern.len()-1: + if pattern[n].typ == NodeText: + toNode = pattern[n] + return true + +proc check(n: Node, s: string, i: int): bool = + let cutTo = (n.text.len-1)+i + if cutTo > s.len-1: return false + return s.substr(i, cutTo) == n.text + +proc match*(pattern: Pattern, s: string): + tuple[matched: bool, params: Table[string, string]] = + var i = 0 # Location in ``s``. + + result.matched = true + result.params = initTable[string, string]() + + for ncount, node in pattern: + case node.typ + of NodeText: + if node.optional: + if check(node, s, i): + inc(i, node.text.len) # Skip over this optional character. + else: + # If it's not there, we have nothing to do. It's optional after all. + discard + else: + if check(node, s, i): + inc(i, node.text.len) # Skip over this + else: + # No match. + result.matched = false + return + of NodeField: + var nextTxtNode: Node + var stopChar = '/' + if findNextText(pattern, ncount, nextTxtNode): + stopChar = nextTxtNode.text[0] + var matchNamed = "" + i += s.parseUntil(matchNamed, stopChar, i) + result.params[node.text] = matchNamed + if matchNamed == "" and not node.optional: + result.matched = false + return + + if s.len != i: + result.matched = false + +when isMainModule: + let f = parsePattern("/show/@id/test/@show?/?") + doAssert match(f, "/show/12/test/hallo/").matched + doAssert match(f, "/show/2131726/test/jjjuuwąąss").matched + doAssert(not match(f, "/").matched) + doAssert(not match(f, "/show//test//").matched) + doAssert(match(f, "/show/asd/test//").matched) + doAssert(not match(f, "/show/asd/asd/test/jjj/").matched) + doAssert(match(f, "/show/@łę¶ŧ←/test/asd/").params["id"] == "@łę¶ŧ←") + + let f2 = parsePattern("/test42/somefile.?@ext?/?") + doAssert(match(f2, "/test42/somefile/").params["ext"] == "") + doAssert(match(f2, "/test42/somefile.txt").params["ext"] == "txt") + doAssert(match(f2, "/test42/somefile.txt/").params["ext"] == "txt") + + let f3 = parsePattern(r"/test32/\@\\\??") + doAssert(match(f3, r"/test32/@\").matched) + doAssert(not match(f3, r"/test32/@\\").matched) + doAssert(match(f3, r"/test32/@\?").matched) diff --git a/tests/deps/jester-#head/jester/private/errorpages.nim b/tests/deps/jester-#head/jester/private/errorpages.nim new file mode 100644 index 000000000..d1e695040 --- /dev/null +++ b/tests/deps/jester-#head/jester/private/errorpages.nim @@ -0,0 +1,19 @@ +# Copyright (C) 2012 Dominik Picheta +# MIT License - Look at license.txt for details. +import htmlgen +proc error*(err, jesterVer: string): string = + return html(head(title(err)), + body(h1(err), + "
      ", + p("Jester " & jesterVer), + style = "text-align: center;" + ), + xmlns="http://www.w3.org/1999/xhtml") + +proc routeException*(error: string, jesterVer: string): string = + return html(head(title("Jester route exception")), + body( + h1("An error has occured in one of your routes."), + p(b("Detail: "), error) + ), + xmlns="http://www.w3.org/1999/xhtml") diff --git a/tests/deps/jester-#head/jester/private/utils.nim b/tests/deps/jester-#head/jester/private/utils.nim new file mode 100644 index 000000000..2d1390955 --- /dev/null +++ b/tests/deps/jester-#head/jester/private/utils.nim @@ -0,0 +1,194 @@ +# Copyright (C) 2012 Dominik Picheta +# MIT License - Look at license.txt for details. +import parseutils, strtabs, strutils, tables, net, mimetypes, asyncdispatch, os +from cgi import decodeUrl + +const + useHttpBeast* = false # not defined(windows) and not defined(useStdLib) + +type + MultiData* = OrderedTable[string, tuple[fields: StringTableRef, body: string]] + + Settings* = ref object + staticDir*: string # By default ./public + appName*: string + mimes*: MimeDb + port*: Port + bindAddr*: string + reusePort*: bool + futureErrorHandler*: proc (fut: Future[void]) {.closure, gcsafe.} + + JesterError* = object of Exception + +proc parseUrlQuery*(query: string, result: var Table[string, string]) + {.deprecated: "use stdlib cgi/decodeData".} = + var i = 0 + i = query.skip("?") + while i < query.len()-1: + var key = "" + var val = "" + i += query.parseUntil(key, '=', i) + if query[i] != '=': + raise newException(ValueError, "Expected '=' at " & $i & + " but got: " & $query[i]) + inc(i) # Skip = + i += query.parseUntil(val, '&', i) + inc(i) # Skip & + result[decodeUrl(key)] = decodeUrl(val) + +template parseContentDisposition(): typed = + var hCount = 0 + while hCount < hValue.len()-1: + var key = "" + hCount += hValue.parseUntil(key, {';', '='}, hCount) + if hValue[hCount] == '=': + var value = hvalue.captureBetween('"', start = hCount) + hCount += value.len+2 + inc(hCount) # Skip ; + hCount += hValue.skipWhitespace(hCount) + if key == "name": name = value + newPart[0][key] = value + else: + inc(hCount) + hCount += hValue.skipWhitespace(hCount) + +proc parseMultiPart*(body: string, boundary: string): MultiData = + result = initOrderedTable[string, tuple[fields: StringTableRef, body: string]]() + var mboundary = "--" & boundary + + var i = 0 + var partsLeft = true + while partsLeft: + var firstBoundary = body.skip(mboundary, i) + if firstBoundary == 0: + raise newException(ValueError, "Expected boundary. Got: " & body.substr(i, i+25)) + i += firstBoundary + i += body.skipWhitespace(i) + + # Headers + var newPart: tuple[fields: StringTableRef, body: string] = ({:}.newStringTable, "") + var name = "" + while true: + if body[i] == '\c': + inc(i, 2) # Skip \c\L + break + var hName = "" + i += body.parseUntil(hName, ':', i) + if body[i] != ':': + raise newException(ValueError, "Expected : in headers.") + inc(i) # Skip : + i += body.skipWhitespace(i) + var hValue = "" + i += body.parseUntil(hValue, {'\c', '\L'}, i) + if toLowerAscii(hName) == "content-disposition": + parseContentDisposition() + newPart[0][hName] = hValue + i += body.skip("\c\L", i) # Skip *one* \c\L + + # Parse body. + while true: + if body[i] == '\c' and body[i+1] == '\L' and + body.skip(mboundary, i+2) != 0: + if body.skip("--", i+2+mboundary.len) != 0: + partsLeft = false + break + break + else: + newPart[1].add(body[i]) + inc(i) + i += body.skipWhitespace(i) + + result.add(name, newPart) + +proc parseMPFD*(contentType: string, body: string): MultiData = + var boundaryEqIndex = contentType.find("boundary=")+9 + var boundary = contentType.substr(boundaryEqIndex, contentType.len()-1) + return parseMultiPart(body, boundary) + +proc parseCookies*(s: string): Table[string, string] = + ## parses cookies into a string table. + ## + ## The proc is meant to parse the Cookie header set by a client, not the + ## "Set-Cookie" header set by servers. + + result = initTable[string, string]() + var i = 0 + while true: + i += skipWhile(s, {' ', '\t'}, i) + var keystart = i + i += skipUntil(s, {'='}, i) + var keyend = i-1 + if i >= len(s): break + inc(i) # skip '=' + var valstart = i + i += skipUntil(s, {';'}, i) + result[substr(s, keystart, keyend)] = substr(s, valstart, i-1) + if i >= len(s): break + inc(i) # skip ';' + +type + SameSite* = enum + None, Lax, Strict + +proc makeCookie*(key, value, expires: string, domain = "", path = "", + secure = false, httpOnly = false, + sameSite = Lax): string = + result = "" + result.add key & "=" & value + if domain != "": result.add("; Domain=" & domain) + if path != "": result.add("; Path=" & path) + if expires != "": result.add("; Expires=" & expires) + if secure: result.add("; Secure") + if httpOnly: result.add("; HttpOnly") + if sameSite != None: + result.add("; SameSite=" & $sameSite) + +when not declared(tables.getOrDefault): + template getOrDefault*(tab, key): untyped = tab[key] + +when not declared(normalizePath) and not declared(normalizedPath): + proc normalizePath*(path: var string) = + ## Normalize a path. + ## + ## Consecutive directory separators are collapsed, including an initial double slash. + ## + ## On relative paths, double dot (..) sequences are collapsed if possible. + ## On absolute paths they are always collapsed. + ## + ## Warning: URL-encoded and Unicode attempts at directory traversal are not detected. + ## Triple dot is not handled. + let isAbs = isAbsolute(path) + var stack: seq[string] = @[] + for p in split(path, {DirSep}): + case p + of "", ".": + continue + of "..": + if stack.len == 0: + if isAbs: + discard # collapse all double dots on absoluta paths + else: + stack.add(p) + elif stack[^1] == "..": + stack.add(p) + else: + discard stack.pop() + else: + stack.add(p) + + if isAbs: + path = DirSep & join(stack, $DirSep) + elif stack.len > 0: + path = join(stack, $DirSep) + else: + path = "." + + proc normalizedPath*(path: string): string = + ## Returns a normalized path for the current OS. See `<#normalizePath>`_ + result = path + normalizePath(result) + +when false: + var r = {:}.newStringTable + parseUrlQuery("FirstName=Mickey", r) + echo r diff --git a/tests/deps/jester-#head/jester/request.nim b/tests/deps/jester-#head/jester/request.nim new file mode 100644 index 000000000..20e5c840f --- /dev/null +++ b/tests/deps/jester-#head/jester/request.nim @@ -0,0 +1,185 @@ +import uri, cgi, tables, logging, strutils, re, options + +import jester/private/utils + +when useHttpBeast: + import httpbeast except Settings + import options, httpcore + + type + NativeRequest* = httpbeast.Request +else: + import asynchttpserver + + type + NativeRequest* = asynchttpserver.Request + +type + Request* = object + req: NativeRequest + patternParams: Option[Table[string, string]] + reMatches: array[MaxSubpatterns, string] + settings*: Settings + +proc body*(req: Request): string = + ## Body of the request, only for POST. + ## + ## You're probably looking for ``formData`` + ## instead. + when useHttpBeast: + req.req.body.get("") + else: + req.req.body + +proc headers*(req: Request): HttpHeaders = + ## Headers received with the request. + ## Retrieving these is case insensitive. + when useHttpBeast: + if req.req.headers.isNone: + newHttpHeaders() + else: + req.req.headers.get() + else: + req.req.headers + +proc path*(req: Request): string = + ## Path of request without the query string. + when useHttpBeast: + let p = req.req.path.get("") + let queryStart = p.find('?') + if unlikely(queryStart != -1): + return p[0 .. queryStart-1] + else: + return p + else: + let u = req.req.url + return u.path + +proc reqMethod*(req: Request): HttpMethod = + ## Request method, e.g. HttpGet, HttpPost + when useHttpBeast: + req.req.httpMethod.get() + else: + req.req.reqMethod + +proc reqMeth*(req: Request): HttpMethod {.deprecated.} = + req.reqMethod + +proc ip*(req: Request): string = + ## IP address of the requesting client. + when useHttpBeast: + result = req.req.ip + else: + result = req.req.hostname + + let headers = req.headers + if headers.hasKey("REMOTE_ADDR"): + result = headers["REMOTE_ADDR"] + if headers.hasKey("x-forwarded-for"): + result = headers["x-forwarded-for"] + +proc params*(req: Request): Table[string, string] = + ## Parameters from the pattern and the query string. + if req.patternParams.isSome(): + result = req.patternParams.get() + else: + result = initTable[string, string]() + + when useHttpBeast: + let query = req.req.path.get("").parseUri().query + else: + let query = req.req.url.query + + try: + for key, val in cgi.decodeData(query): + result[key] = val + except CgiError: + logging.warn("Incorrect query. Got: $1" % [query]) + + let contentType = req.headers.getOrDefault("Content-Type") + if contentType.startswith("application/x-www-form-urlencoded"): + try: + parseUrlQuery(req.body, result) + except: + logging.warn("Could not parse URL query.") + +proc formData*(req: Request): MultiData = + let contentType = req.headers.getOrDefault("Content-Type") + if contentType.startsWith("multipart/form-data"): + result = parseMPFD(contentType, req.body) + +proc matches*(req: Request): array[MaxSubpatterns, string] = + req.reMatches + +proc secure*(req: Request): bool = + if req.headers.hasKey("x-forwarded-proto"): + let proto = req.headers["x-forwarded-proto"] + case proto.toLowerAscii() + of "https": + result = true + of "http": + result = false + else: + logging.warn("Unknown x-forwarded-proto ", proto) + +proc port*(req: Request): int = + if (let p = req.headers.getOrDefault("SERVER_PORT"); p != ""): + result = p.parseInt + else: + result = if req.secure: 443 else: 80 + +proc host*(req: Request): string = + req.headers.getOrDefault("HOST") + +proc appName*(req: Request): string = + ## This is set by the user in ``run``, it is + ## overriden by the "SCRIPT_NAME" scgi + ## parameter. + req.settings.appName + +proc stripAppName(path, appName: string): string = + result = path + if appname.len > 0: + var slashAppName = appName + if slashAppName[0] != '/' and path[0] == '/': + slashAppName = '/' & slashAppName + + if path.startsWith(slashAppName): + if slashAppName.len() == path.len: + return "/" + else: + return path[slashAppName.len .. path.len-1] + else: + raise newException(ValueError, + "Expected script name at beginning of path. Got path: " & + path & " script name: " & slashAppName) + +proc pathInfo*(req: Request): string = + ## This is ``.path`` without ``.appName``. + req.path.stripAppName(req.appName) + +# TODO: Can cookie keys be duplicated? +proc cookies*(req: Request): Table[string, string] = + ## Cookies from the browser. + if (let cookie = req.headers.getOrDefault("Cookie"); cookie != ""): + result = parseCookies(cookie) + else: + result = initTable[string, string]() + +#[ Protected procs ]# + +proc initRequest*(req: NativeRequest, settings: Settings): Request {.inline.} = + Request( + req: req, + settings: settings + ) + +proc getNativeReq*(req: Request): NativeRequest = + req.req + +#[ Only to be used by our route macro. ]# +proc setPatternParams*(req: var Request, p: Table[string, string]) = + req.patternParams = some(p) + +proc setReMatches*(req: var Request, r: array[MaxSubpatterns, string]) = + req.reMatches = r diff --git a/tests/deps/opengl-1.1.0/glu.nim b/tests/deps/opengl-1.1.0/glu.nim new file mode 100644 index 000000000..5594ef915 --- /dev/null +++ b/tests/deps/opengl-1.1.0/glu.nim @@ -0,0 +1,326 @@ +# +# +# Adaption of the delphi3d.net OpenGL units to FreePascal +# Sebastian Guenther (sg@freepascal.org) in 2002 +# These units are free to use +#****************************************************************************** +# Converted to Delphi by Tom Nuydens (tom@delphi3d.net) +# For the latest updates, visit Delphi3D: http://www.delphi3d.net +#****************************************************************************** + +import opengl + +{.deadCodeElim: on.} + +when defined(windows): + {.push, callconv: stdcall.} +else: + {.push, callconv: cdecl.} + +when defined(windows): + const + dllname = "glu32.dll" +elif defined(macosx): + const + dllname = "/System/Library/Frameworks/OpenGL.framework/Libraries/libGLU.dylib" +else: + const + dllname = "libGLU.so.1" + +type + ViewPortArray* = array[0..3, GLint] + T16dArray* = array[0..15, GLdouble] + CallBack* = proc () {.cdecl.} + T3dArray* = array[0..2, GLdouble] + T4pArray* = array[0..3, pointer] + T4fArray* = array[0..3, GLfloat] + +{.deprecated: [ + TViewPortArray: ViewPortArray, + TCallBack: CallBack, +].} + +type + GLUnurbs*{.final.} = ptr object + GLUquadric*{.final.} = ptr object + GLUtesselator*{.final.} = ptr object + GLUnurbsObj* = GLUnurbs + GLUquadricObj* = GLUquadric + GLUtesselatorObj* = GLUtesselator + GLUtriangulatorObj* = GLUtesselator + +proc gluErrorString*(errCode: GLenum): cstring{.dynlib: dllname, + importc: "gluErrorString".} +when defined(Windows): + proc gluErrorUnicodeStringEXT*(errCode: GLenum): ptr int16{.dynlib: dllname, + importc: "gluErrorUnicodeStringEXT".} +proc gluGetString*(name: GLenum): cstring{.dynlib: dllname, + importc: "gluGetString".} +proc gluOrtho2D*(left, right, bottom, top: GLdouble){.dynlib: dllname, + importc: "gluOrtho2D".} +proc gluPerspective*(fovy, aspect, zNear, zFar: GLdouble){.dynlib: dllname, + importc: "gluPerspective".} +proc gluPickMatrix*(x, y, width, height: GLdouble, viewport: var ViewPortArray){. + dynlib: dllname, importc: "gluPickMatrix".} +proc gluLookAt*(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz: GLdouble){. + dynlib: dllname, importc: "gluLookAt".} +proc gluProject*(objx, objy, objz: GLdouble, + modelMatrix, projMatrix: var T16dArray, + viewport: var ViewPortArray, winx, winy, winz: ptr GLdouble): int{. + dynlib: dllname, importc: "gluProject".} +proc gluUnProject*(winx, winy, winz: GLdouble, + modelMatrix, projMatrix: var T16dArray, + viewport: var ViewPortArray, objx, objy, objz: ptr GLdouble): int{. + dynlib: dllname, importc: "gluUnProject".} +proc gluScaleImage*(format: GLenum, widthin, heightin: GLint, typein: GLenum, + datain: pointer, widthout, heightout: GLint, + typeout: GLenum, dataout: pointer): int{.dynlib: dllname, + importc: "gluScaleImage".} +proc gluBuild1DMipmaps*(target: GLenum, components, width: GLint, + format, atype: GLenum, data: pointer): int{. + dynlib: dllname, importc: "gluBuild1DMipmaps".} +proc gluBuild2DMipmaps*(target: GLenum, components, width, height: GLint, + format, atype: GLenum, data: pointer): int{. + dynlib: dllname, importc: "gluBuild2DMipmaps".} +proc gluNewQuadric*(): GLUquadric{.dynlib: dllname, importc: "gluNewQuadric".} +proc gluDeleteQuadric*(state: GLUquadric){.dynlib: dllname, + importc: "gluDeleteQuadric".} +proc gluQuadricNormals*(quadObject: GLUquadric, normals: GLenum){. + dynlib: dllname, importc: "gluQuadricNormals".} +proc gluQuadricTexture*(quadObject: GLUquadric, textureCoords: GLboolean){. + dynlib: dllname, importc: "gluQuadricTexture".} +proc gluQuadricOrientation*(quadObject: GLUquadric, orientation: GLenum){. + dynlib: dllname, importc: "gluQuadricOrientation".} +proc gluQuadricDrawStyle*(quadObject: GLUquadric, drawStyle: GLenum){. + dynlib: dllname, importc: "gluQuadricDrawStyle".} +proc gluCylinder*(qobj: GLUquadric, baseRadius, topRadius, height: GLdouble, + slices, stacks: GLint){.dynlib: dllname, + importc: "gluCylinder".} +proc gluDisk*(qobj: GLUquadric, innerRadius, outerRadius: GLdouble, + slices, loops: GLint){.dynlib: dllname, importc: "gluDisk".} +proc gluPartialDisk*(qobj: GLUquadric, innerRadius, outerRadius: GLdouble, + slices, loops: GLint, startAngle, sweepAngle: GLdouble){. + dynlib: dllname, importc: "gluPartialDisk".} +proc gluSphere*(qobj: GLuquadric, radius: GLdouble, slices, stacks: GLint){. + dynlib: dllname, importc: "gluSphere".} +proc gluQuadricCallback*(qobj: GLUquadric, which: GLenum, fn: CallBack){. + dynlib: dllname, importc: "gluQuadricCallback".} +proc gluNewTess*(): GLUtesselator{.dynlib: dllname, importc: "gluNewTess".} +proc gluDeleteTess*(tess: GLUtesselator){.dynlib: dllname, + importc: "gluDeleteTess".} +proc gluTessBeginPolygon*(tess: GLUtesselator, polygon_data: pointer){. + dynlib: dllname, importc: "gluTessBeginPolygon".} +proc gluTessBeginContour*(tess: GLUtesselator){.dynlib: dllname, + importc: "gluTessBeginContour".} +proc gluTessVertex*(tess: GLUtesselator, coords: var T3dArray, data: pointer){. + dynlib: dllname, importc: "gluTessVertex".} +proc gluTessEndContour*(tess: GLUtesselator){.dynlib: dllname, + importc: "gluTessEndContour".} +proc gluTessEndPolygon*(tess: GLUtesselator){.dynlib: dllname, + importc: "gluTessEndPolygon".} +proc gluTessProperty*(tess: GLUtesselator, which: GLenum, value: GLdouble){. + dynlib: dllname, importc: "gluTessProperty".} +proc gluTessNormal*(tess: GLUtesselator, x, y, z: GLdouble){.dynlib: dllname, + importc: "gluTessNormal".} +proc gluTessCallback*(tess: GLUtesselator, which: GLenum, fn: CallBack){. + dynlib: dllname, importc: "gluTessCallback".} +proc gluGetTessProperty*(tess: GLUtesselator, which: GLenum, value: ptr GLdouble){. + dynlib: dllname, importc: "gluGetTessProperty".} +proc gluNewNurbsRenderer*(): GLUnurbs{.dynlib: dllname, + importc: "gluNewNurbsRenderer".} +proc gluDeleteNurbsRenderer*(nobj: GLUnurbs){.dynlib: dllname, + importc: "gluDeleteNurbsRenderer".} +proc gluBeginSurface*(nobj: GLUnurbs){.dynlib: dllname, + importc: "gluBeginSurface".} +proc gluBeginCurve*(nobj: GLUnurbs){.dynlib: dllname, importc: "gluBeginCurve".} +proc gluEndCurve*(nobj: GLUnurbs){.dynlib: dllname, importc: "gluEndCurve".} +proc gluEndSurface*(nobj: GLUnurbs){.dynlib: dllname, importc: "gluEndSurface".} +proc gluBeginTrim*(nobj: GLUnurbs){.dynlib: dllname, importc: "gluBeginTrim".} +proc gluEndTrim*(nobj: GLUnurbs){.dynlib: dllname, importc: "gluEndTrim".} +proc gluPwlCurve*(nobj: GLUnurbs, count: GLint, aarray: ptr GLfloat, + stride: GLint, atype: GLenum){.dynlib: dllname, + importc: "gluPwlCurve".} +proc gluNurbsCurve*(nobj: GLUnurbs, nknots: GLint, knot: ptr GLfloat, + stride: GLint, ctlarray: ptr GLfloat, order: GLint, + atype: GLenum){.dynlib: dllname, importc: "gluNurbsCurve".} +proc gluNurbsSurface*(nobj: GLUnurbs, sknot_count: GLint, sknot: ptr GLfloat, + tknot_count: GLint, tknot: ptr GLfloat, + s_stride, t_stride: GLint, ctlarray: ptr GLfloat, + sorder, torder: GLint, atype: GLenum){.dynlib: dllname, + importc: "gluNurbsSurface".} +proc gluLoadSamplingMatrices*(nobj: GLUnurbs, + modelMatrix, projMatrix: var T16dArray, + viewport: var ViewPortArray){.dynlib: dllname, + importc: "gluLoadSamplingMatrices".} +proc gluNurbsProperty*(nobj: GLUnurbs, aproperty: GLenum, value: GLfloat){. + dynlib: dllname, importc: "gluNurbsProperty".} +proc gluGetNurbsProperty*(nobj: GLUnurbs, aproperty: GLenum, value: ptr GLfloat){. + dynlib: dllname, importc: "gluGetNurbsProperty".} +proc gluNurbsCallback*(nobj: GLUnurbs, which: GLenum, fn: CallBack){. + dynlib: dllname, importc: "gluNurbsCallback".} + #*** Callback function prototypes *** +type # gluQuadricCallback + GLUquadricErrorProc* = proc (p: GLenum) # gluTessCallback + GLUtessBeginProc* = proc (p: GLenum) + GLUtessEdgeFlagProc* = proc (p: GLboolean) + GLUtessVertexProc* = proc (p: pointer) + GLUtessEndProc* = proc () + GLUtessErrorProc* = proc (p: GLenum) + GLUtessCombineProc* = proc (p1: var T3dArray, p2: T4pArray, p3: T4fArray, + p4: ptr pointer) + GLUtessBeginDataProc* = proc (p1: GLenum, p2: pointer) + GLUtessEdgeFlagDataProc* = proc (p1: GLboolean, p2: pointer) + GLUtessVertexDataProc* = proc (p1, p2: pointer) + GLUtessEndDataProc* = proc (p: pointer) + GLUtessErrorDataProc* = proc (p1: GLenum, p2: pointer) + GLUtessCombineDataProc* = proc (p1: var T3dArray, p2: var T4pArray, + p3: var T4fArray, p4: ptr pointer, p5: pointer) # + GLUnurbsErrorProc* = proc (p: GLenum) #*** Generic constants ****/ + +const # Version + GLU_VERSION_1_1* = 1 + GLU_VERSION_1_2* = 1 # Errors: (return value 0 = no error) + GLU_INVALID_ENUM* = 100900 + GLU_INVALID_VALUE* = 100901 + GLU_OUT_OF_MEMORY* = 100902 + GLU_INCOMPATIBLE_GL_VERSION* = 100903 # StringName + GLU_VERSION* = 100800 + GLU_EXTENSIONS* = 100801 # Boolean + GLU_TRUE* = GL_TRUE + GLU_FALSE* = GL_FALSE #*** Quadric constants ****/ + # QuadricNormal + GLU_SMOOTH* = 100000 + GLU_FLAT* = 100001 + GLU_NONE* = 100002 # QuadricDrawStyle + GLU_POINT* = 100010 + GLU_LINE* = 100011 + GLU_FILL* = 100012 + GLU_SILHOUETTE* = 100013 # QuadricOrientation + GLU_OUTSIDE* = 100020 + GLU_INSIDE* = 100021 # Callback types: + # GLU_ERROR = 100103; + #*** Tesselation constants ****/ + GLU_TESS_MAX_COORD* = 1.00000e+150 # TessProperty + GLU_TESS_WINDING_RULE* = 100140 + GLU_TESS_BOUNDARY_ONLY* = 100141 + GLU_TESS_TOLERANCE* = 100142 # TessWinding + GLU_TESS_WINDING_ODD* = 100130 + GLU_TESS_WINDING_NONZERO* = 100131 + GLU_TESS_WINDING_POSITIVE* = 100132 + GLU_TESS_WINDING_NEGATIVE* = 100133 + GLU_TESS_WINDING_ABS_GEQ_TWO* = 100134 # TessCallback + GLU_TESS_BEGIN* = 100100 # void (CALLBACK*)(GLenum type) + constGLU_TESS_VERTEX* = 100101 # void (CALLBACK*)(void *data) + GLU_TESS_END* = 100102 # void (CALLBACK*)(void) + GLU_TESS_ERROR* = 100103 # void (CALLBACK*)(GLenum errno) + GLU_TESS_EDGE_FLAG* = 100104 # void (CALLBACK*)(GLboolean boundaryEdge) + GLU_TESS_COMBINE* = 100105 # void (CALLBACK*)(GLdouble coords[3], + # void *data[4], + # GLfloat weight[4], + # void **dataOut) + GLU_TESS_BEGIN_DATA* = 100106 # void (CALLBACK*)(GLenum type, + # void *polygon_data) + GLU_TESS_VERTEX_DATA* = 100107 # void (CALLBACK*)(void *data, + # void *polygon_data) + GLU_TESS_END_DATA* = 100108 # void (CALLBACK*)(void *polygon_data) + GLU_TESS_ERROR_DATA* = 100109 # void (CALLBACK*)(GLenum errno, + # void *polygon_data) + GLU_TESS_EDGE_FLAG_DATA* = 100110 # void (CALLBACK*)(GLboolean boundaryEdge, + # void *polygon_data) + GLU_TESS_COMBINE_DATA* = 100111 # void (CALLBACK*)(GLdouble coords[3], + # void *data[4], + # GLfloat weight[4], + # void **dataOut, + # void *polygon_data) + # TessError + GLU_TESS_ERROR1* = 100151 + GLU_TESS_ERROR2* = 100152 + GLU_TESS_ERROR3* = 100153 + GLU_TESS_ERROR4* = 100154 + GLU_TESS_ERROR5* = 100155 + GLU_TESS_ERROR6* = 100156 + GLU_TESS_ERROR7* = 100157 + GLU_TESS_ERROR8* = 100158 + GLU_TESS_MISSING_BEGIN_POLYGON* = GLU_TESS_ERROR1 + GLU_TESS_MISSING_BEGIN_CONTOUR* = GLU_TESS_ERROR2 + GLU_TESS_MISSING_END_POLYGON* = GLU_TESS_ERROR3 + GLU_TESS_MISSING_END_CONTOUR* = GLU_TESS_ERROR4 + GLU_TESS_COORD_TOO_LARGE* = GLU_TESS_ERROR5 + GLU_TESS_NEED_COMBINE_CALLBACK* = GLU_TESS_ERROR6 #*** NURBS constants ****/ + # NurbsProperty + GLU_AUTO_LOAD_MATRIX* = 100200 + GLU_CULLING* = 100201 + GLU_SAMPLING_TOLERANCE* = 100203 + GLU_DISPLAY_MODE* = 100204 + GLU_PARAMETRIC_TOLERANCE* = 100202 + GLU_SAMPLING_METHOD* = 100205 + GLU_U_STEP* = 100206 + GLU_V_STEP* = 100207 # NurbsSampling + GLU_PATH_LENGTH* = 100215 + GLU_PARAMETRIC_ERROR* = 100216 + GLU_DOMAIN_DISTANCE* = 100217 # NurbsTrim + GLU_MAP1_TRIM_2* = 100210 + GLU_MAP1_TRIM_3* = 100211 # NurbsDisplay + # GLU_FILL = 100012; + GLU_OUTLINE_POLYGON* = 100240 + GLU_OUTLINE_PATCH* = 100241 # NurbsCallback + # GLU_ERROR = 100103; + # NurbsErrors + GLU_NURBS_ERROR1* = 100251 + GLU_NURBS_ERROR2* = 100252 + GLU_NURBS_ERROR3* = 100253 + GLU_NURBS_ERROR4* = 100254 + GLU_NURBS_ERROR5* = 100255 + GLU_NURBS_ERROR6* = 100256 + GLU_NURBS_ERROR7* = 100257 + GLU_NURBS_ERROR8* = 100258 + GLU_NURBS_ERROR9* = 100259 + GLU_NURBS_ERROR10* = 100260 + GLU_NURBS_ERROR11* = 100261 + GLU_NURBS_ERROR12* = 100262 + GLU_NURBS_ERROR13* = 100263 + GLU_NURBS_ERROR14* = 100264 + GLU_NURBS_ERROR15* = 100265 + GLU_NURBS_ERROR16* = 100266 + GLU_NURBS_ERROR17* = 100267 + GLU_NURBS_ERROR18* = 100268 + GLU_NURBS_ERROR19* = 100269 + GLU_NURBS_ERROR20* = 100270 + GLU_NURBS_ERROR21* = 100271 + GLU_NURBS_ERROR22* = 100272 + GLU_NURBS_ERROR23* = 100273 + GLU_NURBS_ERROR24* = 100274 + GLU_NURBS_ERROR25* = 100275 + GLU_NURBS_ERROR26* = 100276 + GLU_NURBS_ERROR27* = 100277 + GLU_NURBS_ERROR28* = 100278 + GLU_NURBS_ERROR29* = 100279 + GLU_NURBS_ERROR30* = 100280 + GLU_NURBS_ERROR31* = 100281 + GLU_NURBS_ERROR32* = 100282 + GLU_NURBS_ERROR33* = 100283 + GLU_NURBS_ERROR34* = 100284 + GLU_NURBS_ERROR35* = 100285 + GLU_NURBS_ERROR36* = 100286 + GLU_NURBS_ERROR37* = 100287 #*** Backwards compatibility for old tesselator ****/ + +proc gluBeginPolygon*(tess: GLUtesselator){.dynlib: dllname, + importc: "gluBeginPolygon".} +proc gluNextContour*(tess: GLUtesselator, atype: GLenum){.dynlib: dllname, + importc: "gluNextContour".} +proc gluEndPolygon*(tess: GLUtesselator){.dynlib: dllname, + importc: "gluEndPolygon".} +const # Contours types -- obsolete! + GLU_CW* = 100120 + GLU_CCW* = 100121 + GLU_INTERIOR* = 100122 + GLU_EXTERIOR* = 100123 + GLU_UNKNOWN* = 100124 # Names without "TESS_" prefix + GLU_BEGIN* = GLU_TESS_BEGIN + GLU_VERTEX* = constGLU_TESS_VERTEX + GLU_END* = GLU_TESS_END + GLU_ERROR* = GLU_TESS_ERROR + GLU_EDGE_FLAG* = GLU_TESS_EDGE_FLAG + +{.pop.} +# implementation diff --git a/tests/deps/opengl-1.1.0/glut.nim b/tests/deps/opengl-1.1.0/glut.nim new file mode 100644 index 000000000..55a5da0c7 --- /dev/null +++ b/tests/deps/opengl-1.1.0/glut.nim @@ -0,0 +1,366 @@ +# +# +# Adaption of the delphi3d.net OpenGL units to FreePascal +# Sebastian Guenther (sg@freepascal.org) in 2002 +# These units are free to use +# + +# Copyright (c) Mark J. Kilgard, 1994, 1995, 1996. +# This program is freely distributable without licensing fees and is +# provided without guarantee or warrantee expressed or implied. This +# program is -not- in the public domain. +#****************************************************************************** +# Converted to Delphi by Tom Nuydens (tom@delphi3d.net) +# Contributions by Igor Karpov (glygrik@hotbox.ru) +# For the latest updates, visit Delphi3D: http://www.delphi3d.net +#****************************************************************************** + +import opengl + +{.deadCodeElim: on.} + +when defined(windows): + const + dllname = "glut32.dll" +elif defined(macosx): + const + dllname = "/System/Library/Frameworks/GLUT.framework/GLUT" +else: + const + dllname = "libglut.so.3" +type + TGlutVoidCallback* = proc (){.cdecl.} + TGlut1IntCallback* = proc (value: cint){.cdecl.} + TGlut2IntCallback* = proc (v1, v2: cint){.cdecl.} + TGlut3IntCallback* = proc (v1, v2, v3: cint){.cdecl.} + TGlut4IntCallback* = proc (v1, v2, v3, v4: cint){.cdecl.} + TGlut1Char2IntCallback* = proc (c: int8, v1, v2: cint){.cdecl.} + TGlut1UInt3IntCallback* = proc (u, v1, v2, v3: cint){.cdecl.} + +{.deprecated: [Pointer: pointer].} + +const + GLUT_API_VERSION* = 3 + GLUT_XLIB_IMPLEMENTATION* = 12 # Display mode bit masks. + GLUT_RGB* = 0 + GLUT_RGBA* = GLUT_RGB + GLUT_INDEX* = 1 + GLUT_SINGLE* = 0 + GLUT_DOUBLE* = 2 + GLUT_ACCUM* = 4 + GLUT_ALPHA* = 8 + GLUT_DEPTH* = 16 + GLUT_STENCIL* = 32 + GLUT_MULTISAMPLE* = 128 + GLUT_STEREO* = 256 + GLUT_LUMINANCE* = 512 # Mouse buttons. + GLUT_LEFT_BUTTON* = 0 + GLUT_MIDDLE_BUTTON* = 1 + GLUT_RIGHT_BUTTON* = 2 # Mouse button state. + GLUT_DOWN* = 0 + GLUT_UP* = 1 # function keys + GLUT_KEY_F1* = 1 + GLUT_KEY_F2* = 2 + GLUT_KEY_F3* = 3 + GLUT_KEY_F4* = 4 + GLUT_KEY_F5* = 5 + GLUT_KEY_F6* = 6 + GLUT_KEY_F7* = 7 + GLUT_KEY_F8* = 8 + GLUT_KEY_F9* = 9 + GLUT_KEY_F10* = 10 + GLUT_KEY_F11* = 11 + GLUT_KEY_F12* = 12 # directional keys + GLUT_KEY_LEFT* = 100 + GLUT_KEY_UP* = 101 + GLUT_KEY_RIGHT* = 102 + GLUT_KEY_DOWN* = 103 + GLUT_KEY_PAGE_UP* = 104 + GLUT_KEY_PAGE_DOWN* = 105 + GLUT_KEY_HOME* = 106 + GLUT_KEY_END* = 107 + GLUT_KEY_INSERT* = 108 # Entry/exit state. + GLUT_LEFT* = 0 + GLUT_ENTERED* = 1 # Menu usage state. + GLUT_MENU_NOT_IN_USE* = 0 + GLUT_MENU_IN_USE* = 1 # Visibility state. + GLUT_NOT_VISIBLE* = 0 + GLUT_VISIBLE* = 1 # Window status state. + GLUT_HIDDEN* = 0 + GLUT_FULLY_RETAINED* = 1 + GLUT_PARTIALLY_RETAINED* = 2 + GLUT_FULLY_COVERED* = 3 # Color index component selection values. + GLUT_RED* = 0 + GLUT_GREEN* = 1 + GLUT_BLUE* = 2 # Layers for use. + GLUT_NORMAL* = 0 + GLUT_OVERLAY* = 1 + +when defined(Windows): + const # Stroke font constants (use these in GLUT program). + GLUT_STROKE_ROMAN* = cast[pointer](0) + GLUT_STROKE_MONO_ROMAN* = cast[pointer](1) # Bitmap font constants (use these in GLUT program). + GLUT_BITMAP_9_BY_15* = cast[pointer](2) + GLUT_BITMAP_8_BY_13* = cast[pointer](3) + GLUT_BITMAP_TIMES_ROMAN_10* = cast[pointer](4) + GLUT_BITMAP_TIMES_ROMAN_24* = cast[pointer](5) + GLUT_BITMAP_HELVETICA_10* = cast[pointer](6) + GLUT_BITMAP_HELVETICA_12* = cast[pointer](7) + GLUT_BITMAP_HELVETICA_18* = cast[pointer](8) +else: + var # Stroke font constants (use these in GLUT program). + GLUT_STROKE_ROMAN*: pointer + GLUT_STROKE_MONO_ROMAN*: pointer # Bitmap font constants (use these in GLUT program). + GLUT_BITMAP_9_BY_15*: pointer + GLUT_BITMAP_8_BY_13*: pointer + GLUT_BITMAP_TIMES_ROMAN_10*: pointer + GLUT_BITMAP_TIMES_ROMAN_24*: pointer + GLUT_BITMAP_HELVETICA_10*: pointer + GLUT_BITMAP_HELVETICA_12*: pointer + GLUT_BITMAP_HELVETICA_18*: pointer +const # glutGet parameters. + GLUT_WINDOW_X* = 100 + GLUT_WINDOW_Y* = 101 + GLUT_WINDOW_WIDTH* = 102 + GLUT_WINDOW_HEIGHT* = 103 + GLUT_WINDOW_BUFFER_SIZE* = 104 + GLUT_WINDOW_STENCIL_SIZE* = 105 + GLUT_WINDOW_DEPTH_SIZE* = 106 + GLUT_WINDOW_RED_SIZE* = 107 + GLUT_WINDOW_GREEN_SIZE* = 108 + GLUT_WINDOW_BLUE_SIZE* = 109 + GLUT_WINDOW_ALPHA_SIZE* = 110 + GLUT_WINDOW_ACCUM_RED_SIZE* = 111 + GLUT_WINDOW_ACCUM_GREEN_SIZE* = 112 + GLUT_WINDOW_ACCUM_BLUE_SIZE* = 113 + GLUT_WINDOW_ACCUM_ALPHA_SIZE* = 114 + GLUT_WINDOW_DOUBLEBUFFER* = 115 + GLUT_WINDOW_RGBA* = 116 + GLUT_WINDOW_PARENT* = 117 + GLUT_WINDOW_NUM_CHILDREN* = 118 + GLUT_WINDOW_COLORMAP_SIZE* = 119 + GLUT_WINDOW_NUM_SAMPLES* = 120 + GLUT_WINDOW_STEREO* = 121 + GLUT_WINDOW_CURSOR* = 122 + GLUT_SCREEN_WIDTH* = 200 + GLUT_SCREEN_HEIGHT* = 201 + GLUT_SCREEN_WIDTH_MM* = 202 + GLUT_SCREEN_HEIGHT_MM* = 203 + GLUT_MENU_NUM_ITEMS* = 300 + GLUT_DISPLAY_MODE_POSSIBLE* = 400 + GLUT_INIT_WINDOW_X* = 500 + GLUT_INIT_WINDOW_Y* = 501 + GLUT_INIT_WINDOW_WIDTH* = 502 + GLUT_INIT_WINDOW_HEIGHT* = 503 + constGLUT_INIT_DISPLAY_MODE* = 504 + GLUT_ELAPSED_TIME* = 700 + GLUT_WINDOW_FORMAT_ID* = 123 # glutDeviceGet parameters. + GLUT_HAS_KEYBOARD* = 600 + GLUT_HAS_MOUSE* = 601 + GLUT_HAS_SPACEBALL* = 602 + GLUT_HAS_DIAL_AND_BUTTON_BOX* = 603 + GLUT_HAS_TABLET* = 604 + GLUT_NUM_MOUSE_BUTTONS* = 605 + GLUT_NUM_SPACEBALL_BUTTONS* = 606 + GLUT_NUM_BUTTON_BOX_BUTTONS* = 607 + GLUT_NUM_DIALS* = 608 + GLUT_NUM_TABLET_BUTTONS* = 609 + GLUT_DEVICE_IGNORE_KEY_REPEAT* = 610 + GLUT_DEVICE_KEY_REPEAT* = 611 + GLUT_HAS_JOYSTICK* = 612 + GLUT_OWNS_JOYSTICK* = 613 + GLUT_JOYSTICK_BUTTONS* = 614 + GLUT_JOYSTICK_AXES* = 615 + GLUT_JOYSTICK_POLL_RATE* = 616 # glutLayerGet parameters. + GLUT_OVERLAY_POSSIBLE* = 800 + GLUT_LAYER_IN_USE* = 801 + GLUT_HAS_OVERLAY* = 802 + GLUT_TRANSPARENT_INDEX* = 803 + GLUT_NORMAL_DAMAGED* = 804 + GLUT_OVERLAY_DAMAGED* = 805 # glutVideoResizeGet parameters. + GLUT_VIDEO_RESIZE_POSSIBLE* = 900 + GLUT_VIDEO_RESIZE_IN_USE* = 901 + GLUT_VIDEO_RESIZE_X_DELTA* = 902 + GLUT_VIDEO_RESIZE_Y_DELTA* = 903 + GLUT_VIDEO_RESIZE_WIDTH_DELTA* = 904 + GLUT_VIDEO_RESIZE_HEIGHT_DELTA* = 905 + GLUT_VIDEO_RESIZE_X* = 906 + GLUT_VIDEO_RESIZE_Y* = 907 + GLUT_VIDEO_RESIZE_WIDTH* = 908 + GLUT_VIDEO_RESIZE_HEIGHT* = 909 # glutGetModifiers return mask. + GLUT_ACTIVE_SHIFT* = 1 + GLUT_ACTIVE_CTRL* = 2 + GLUT_ACTIVE_ALT* = 4 # glutSetCursor parameters. + # Basic arrows. + GLUT_CURSOR_RIGHT_ARROW* = 0 + GLUT_CURSOR_LEFT_ARROW* = 1 # Symbolic cursor shapes. + GLUT_CURSOR_INFO* = 2 + GLUT_CURSOR_DESTROY* = 3 + GLUT_CURSOR_HELP* = 4 + GLUT_CURSOR_CYCLE* = 5 + GLUT_CURSOR_SPRAY* = 6 + GLUT_CURSOR_WAIT* = 7 + GLUT_CURSOR_TEXT* = 8 + GLUT_CURSOR_CROSSHAIR* = 9 # Directional cursors. + GLUT_CURSOR_UP_DOWN* = 10 + GLUT_CURSOR_LEFT_RIGHT* = 11 # Sizing cursors. + GLUT_CURSOR_TOP_SIDE* = 12 + GLUT_CURSOR_BOTTOM_SIDE* = 13 + GLUT_CURSOR_LEFT_SIDE* = 14 + GLUT_CURSOR_RIGHT_SIDE* = 15 + GLUT_CURSOR_TOP_LEFT_CORNER* = 16 + GLUT_CURSOR_TOP_RIGHT_CORNER* = 17 + GLUT_CURSOR_BOTTOM_RIGHT_CORNER* = 18 + GLUT_CURSOR_BOTTOM_LEFT_CORNER* = 19 # Inherit from parent window. + GLUT_CURSOR_INHERIT* = 100 # Blank cursor. + GLUT_CURSOR_NONE* = 101 # Fullscreen crosshair (if available). + GLUT_CURSOR_FULL_CROSSHAIR* = 102 # GLUT device control sub-API. + # glutSetKeyRepeat modes. + GLUT_KEY_REPEAT_OFF* = 0 + GLUT_KEY_REPEAT_ON* = 1 + GLUT_KEY_REPEAT_DEFAULT* = 2 # Joystick button masks. + GLUT_JOYSTICK_BUTTON_A* = 1 + GLUT_JOYSTICK_BUTTON_B* = 2 + GLUT_JOYSTICK_BUTTON_C* = 4 + GLUT_JOYSTICK_BUTTON_D* = 8 # GLUT game mode sub-API. + # glutGameModeGet. + GLUT_GAME_MODE_ACTIVE* = 0 + GLUT_GAME_MODE_POSSIBLE* = 1 + GLUT_GAME_MODE_WIDTH* = 2 + GLUT_GAME_MODE_HEIGHT* = 3 + GLUT_GAME_MODE_PIXEL_DEPTH* = 4 + GLUT_GAME_MODE_REFRESH_RATE* = 5 + GLUT_GAME_MODE_DISPLAY_CHANGED* = 6 # GLUT initialization sub-API. + +{.push dynlib: dllname, importc.} +proc glutInit*(argcp: ptr cint, argv: pointer) + +proc glutInit*() = + ## version that passes `argc` and `argc` implicitely. + var + cmdLine {.importc: "cmdLine".}: array[0..255, cstring] + cmdCount {.importc: "cmdCount".}: cint + glutInit(addr(cmdCount), addr(cmdLine)) + +proc glutInitDisplayMode*(mode: int16) +proc glutInitDisplayString*(str: cstring) +proc glutInitWindowPosition*(x, y: int) +proc glutInitWindowSize*(width, height: int) +proc glutMainLoop*() + # GLUT window sub-API. +proc glutCreateWindow*(title: cstring): int +proc glutCreateSubWindow*(win, x, y, width, height: int): int +proc glutDestroyWindow*(win: int) +proc glutPostRedisplay*() +proc glutPostWindowRedisplay*(win: int) +proc glutSwapBuffers*() +proc glutSetWindow*(win: int) +proc glutSetWindowTitle*(title: cstring) +proc glutSetIconTitle*(title: cstring) +proc glutPositionWindow*(x, y: int) +proc glutReshapeWindow*(width, height: int) +proc glutPopWindow*() +proc glutPushWindow*() +proc glutIconifyWindow*() +proc glutShowWindow*() +proc glutHideWindow*() +proc glutFullScreen*() +proc glutSetCursor*(cursor: int) +proc glutWarpPointer*(x, y: int) + # GLUT overlay sub-API. +proc glutEstablishOverlay*() +proc glutRemoveOverlay*() +proc glutUseLayer*(layer: GLenum) +proc glutPostOverlayRedisplay*() +proc glutPostWindowOverlayRedisplay*(win: int) +proc glutShowOverlay*() +proc glutHideOverlay*() + # GLUT menu sub-API. +proc glutCreateMenu*(callback: TGlut1IntCallback): int +proc glutDestroyMenu*(menu: int) +proc glutSetMenu*(menu: int) +proc glutAddMenuEntry*(caption: cstring, value: int) +proc glutAddSubMenu*(caption: cstring, submenu: int) +proc glutChangeToMenuEntry*(item: int, caption: cstring, value: int) +proc glutChangeToSubMenu*(item: int, caption: cstring, submenu: int) +proc glutRemoveMenuItem*(item: int) +proc glutAttachMenu*(button: int) +proc glutDetachMenu*(button: int) + # GLUT window callback sub-API. +proc glutDisplayFunc*(f: TGlutVoidCallback) +proc glutReshapeFunc*(f: TGlut2IntCallback) +proc glutKeyboardFunc*(f: TGlut1Char2IntCallback) +proc glutMouseFunc*(f: TGlut4IntCallback) +proc glutMotionFunc*(f: TGlut2IntCallback) +proc glutPassiveMotionFunc*(f: TGlut2IntCallback) +proc glutEntryFunc*(f: TGlut1IntCallback) +proc glutVisibilityFunc*(f: TGlut1IntCallback) +proc glutIdleFunc*(f: TGlutVoidCallback) +proc glutTimerFunc*(millis: int16, f: TGlut1IntCallback, value: int) +proc glutMenuStateFunc*(f: TGlut1IntCallback) +proc glutSpecialFunc*(f: TGlut3IntCallback) +proc glutSpaceballMotionFunc*(f: TGlut3IntCallback) +proc glutSpaceballRotateFunc*(f: TGlut3IntCallback) +proc glutSpaceballButtonFunc*(f: TGlut2IntCallback) +proc glutButtonBoxFunc*(f: TGlut2IntCallback) +proc glutDialsFunc*(f: TGlut2IntCallback) +proc glutTabletMotionFunc*(f: TGlut2IntCallback) +proc glutTabletButtonFunc*(f: TGlut4IntCallback) +proc glutMenuStatusFunc*(f: TGlut3IntCallback) +proc glutOverlayDisplayFunc*(f: TGlutVoidCallback) +proc glutWindowStatusFunc*(f: TGlut1IntCallback) +proc glutKeyboardUpFunc*(f: TGlut1Char2IntCallback) +proc glutSpecialUpFunc*(f: TGlut3IntCallback) +proc glutJoystickFunc*(f: TGlut1UInt3IntCallback, pollInterval: int) + # GLUT color index sub-API. +proc glutSetColor*(cell: int, red, green, blue: GLfloat) +proc glutGetColor*(ndx, component: int): GLfloat +proc glutCopyColormap*(win: int) + # GLUT state retrieval sub-API. + # GLUT extension support sub-API +proc glutExtensionSupported*(name: cstring): int + # GLUT font sub-API +proc glutBitmapCharacter*(font: pointer, character: int) +proc glutBitmapWidth*(font: pointer, character: int): int +proc glutStrokeCharacter*(font: pointer, character: int) +proc glutStrokeWidth*(font: pointer, character: int): int +proc glutBitmapLength*(font: pointer, str: cstring): int +proc glutStrokeLength*(font: pointer, str: cstring): int + # GLUT pre-built models sub-API +proc glutWireSphere*(radius: GLdouble, slices, stacks: GLint) +proc glutSolidSphere*(radius: GLdouble, slices, stacks: GLint) +proc glutWireCone*(base, height: GLdouble, slices, stacks: GLint) +proc glutSolidCone*(base, height: GLdouble, slices, stacks: GLint) +proc glutWireCube*(size: GLdouble) +proc glutSolidCube*(size: GLdouble) +proc glutWireTorus*(innerRadius, outerRadius: GLdouble, sides, rings: GLint) +proc glutSolidTorus*(innerRadius, outerRadius: GLdouble, sides, rings: GLint) +proc glutWireDodecahedron*() +proc glutSolidDodecahedron*() +proc glutWireTeapot*(size: GLdouble) +proc glutSolidTeapot*(size: GLdouble) +proc glutWireOctahedron*() +proc glutSolidOctahedron*() +proc glutWireTetrahedron*() +proc glutSolidTetrahedron*() +proc glutWireIcosahedron*() +proc glutSolidIcosahedron*() + # GLUT video resize sub-API. +proc glutVideoResizeGet*(param: GLenum): int +proc glutSetupVideoResizing*() +proc glutStopVideoResizing*() +proc glutVideoResize*(x, y, width, height: int) +proc glutVideoPan*(x, y, width, height: int) + # GLUT debugging sub-API. +proc glutReportErrors*() + # GLUT device control sub-API. +proc glutIgnoreKeyRepeat*(ignore: int) +proc glutSetKeyRepeat*(repeatMode: int) +proc glutForceJoystickFunc*() + # GLUT game mode sub-API. + #example glutGameModeString('1280x1024:32@75'); +proc glutGameModeString*(AString: cstring) +proc glutLeaveGameMode*() +proc glutGameModeGet*(mode: GLenum): int +# implementation +{.pop.} # dynlib: dllname, importc diff --git a/tests/deps/opengl-1.1.0/glx.nim b/tests/deps/opengl-1.1.0/glx.nim new file mode 100644 index 000000000..6ad096c7d --- /dev/null +++ b/tests/deps/opengl-1.1.0/glx.nim @@ -0,0 +1,154 @@ +# +# +# Translation of the Mesa GLX headers for FreePascal +# Copyright (C) 1999 Sebastian Guenther +# +# +# Mesa 3-D graphics library +# Version: 3.0 +# Copyright (C) 1995-1998 Brian Paul +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import X, XLib, XUtil, opengl + +{.deadCodeElim: on.} + +when defined(windows): + const + dllname = "GL.dll" +elif defined(macosx): + const + dllname = "/usr/X11R6/lib/libGL.dylib" +else: + const + dllname = "libGL.so" +const + GLX_USE_GL* = 1'i32 + GLX_BUFFER_SIZE* = 2'i32 + GLX_LEVEL* = 3'i32 + GLX_RGBA* = 4'i32 + GLX_DOUBLEBUFFER* = 5'i32 + GLX_STEREO* = 6'i32 + GLX_AUX_BUFFERS* = 7'i32 + GLX_RED_SIZE* = 8'i32 + GLX_GREEN_SIZE* = 9'i32 + GLX_BLUE_SIZE* = 10'i32 + GLX_ALPHA_SIZE* = 11'i32 + GLX_DEPTH_SIZE* = 12'i32 + GLX_STENCIL_SIZE* = 13'i32 + GLX_ACCUM_RED_SIZE* = 14'i32 + GLX_ACCUM_GREEN_SIZE* = 15'i32 + GLX_ACCUM_BLUE_SIZE* = 16'i32 + GLX_ACCUM_ALPHA_SIZE* = 17'i32 # GLX_EXT_visual_info extension + GLX_X_VISUAL_TYPE_EXT* = 0x00000022 + GLX_TRANSPARENT_TYPE_EXT* = 0x00000023 + GLX_TRANSPARENT_INDEX_VALUE_EXT* = 0x00000024 + GLX_TRANSPARENT_RED_VALUE_EXT* = 0x00000025 + GLX_TRANSPARENT_GREEN_VALUE_EXT* = 0x00000026 + GLX_TRANSPARENT_BLUE_VALUE_EXT* = 0x00000027 + GLX_TRANSPARENT_ALPHA_VALUE_EXT* = 0x00000028 # Error codes returned by glXGetConfig: + GLX_BAD_SCREEN* = 1 + GLX_BAD_ATTRIBUTE* = 2 + GLX_NO_EXTENSION* = 3 + GLX_BAD_VISUAL* = 4 + GLX_BAD_CONTEXT* = 5 + GLX_BAD_VALUE* = 6 + GLX_BAD_ENUM* = 7 # GLX 1.1 and later: + GLX_VENDOR* = 1 + GLX_VERSION* = 2 + GLX_EXTENSIONS* = 3 # GLX_visual_info extension + GLX_TRUE_COLOR_EXT* = 0x00008002 + GLX_DIRECT_COLOR_EXT* = 0x00008003 + GLX_PSEUDO_COLOR_EXT* = 0x00008004 + GLX_STATIC_COLOR_EXT* = 0x00008005 + GLX_GRAY_SCALE_EXT* = 0x00008006 + GLX_STATIC_GRAY_EXT* = 0x00008007 + GLX_NONE_EXT* = 0x00008000 + GLX_TRANSPARENT_RGB_EXT* = 0x00008008 + GLX_TRANSPARENT_INDEX_EXT* = 0x00008009 + +type # From XLib: + XPixmap* = TXID + XFont* = TXID + XColormap* = TXID + GLXContext* = pointer + GLXPixmap* = TXID + GLXDrawable* = TXID + GLXContextID* = TXID + TXPixmap* = XPixmap + TXFont* = XFont + TXColormap* = XColormap + TGLXContext* = GLXContext + TGLXPixmap* = GLXPixmap + TGLXDrawable* = GLXDrawable + TGLXContextID* = GLXContextID + +proc glXChooseVisual*(dpy: PDisplay, screen: int, attribList: ptr int32): PXVisualInfo{. + cdecl, dynlib: dllname, importc: "glXChooseVisual".} +proc glXCreateContext*(dpy: PDisplay, vis: PXVisualInfo, shareList: GLXContext, + direct: bool): GLXContext{.cdecl, dynlib: dllname, + importc: "glXCreateContext".} +proc glXDestroyContext*(dpy: PDisplay, ctx: GLXContext){.cdecl, dynlib: dllname, + importc: "glXDestroyContext".} +proc glXMakeCurrent*(dpy: PDisplay, drawable: GLXDrawable, ctx: GLXContext): bool{. + cdecl, dynlib: dllname, importc: "glXMakeCurrent".} +proc glXCopyContext*(dpy: PDisplay, src, dst: GLXContext, mask: int32){.cdecl, + dynlib: dllname, importc: "glXCopyContext".} +proc glXSwapBuffers*(dpy: PDisplay, drawable: GLXDrawable){.cdecl, + dynlib: dllname, importc: "glXSwapBuffers".} +proc glXCreateGLXPixmap*(dpy: PDisplay, visual: PXVisualInfo, pixmap: XPixmap): GLXPixmap{. + cdecl, dynlib: dllname, importc: "glXCreateGLXPixmap".} +proc glXDestroyGLXPixmap*(dpy: PDisplay, pixmap: GLXPixmap){.cdecl, + dynlib: dllname, importc: "glXDestroyGLXPixmap".} +proc glXQueryExtension*(dpy: PDisplay, errorb, event: var int): bool{.cdecl, + dynlib: dllname, importc: "glXQueryExtension".} +proc glXQueryVersion*(dpy: PDisplay, maj, min: var int): bool{.cdecl, + dynlib: dllname, importc: "glXQueryVersion".} +proc glXIsDirect*(dpy: PDisplay, ctx: GLXContext): bool{.cdecl, dynlib: dllname, + importc: "glXIsDirect".} +proc glXGetConfig*(dpy: PDisplay, visual: PXVisualInfo, attrib: int, + value: var int): int{.cdecl, dynlib: dllname, + importc: "glXGetConfig".} +proc glXGetCurrentContext*(): GLXContext{.cdecl, dynlib: dllname, + importc: "glXGetCurrentContext".} +proc glXGetCurrentDrawable*(): GLXDrawable{.cdecl, dynlib: dllname, + importc: "glXGetCurrentDrawable".} +proc glXWaitGL*(){.cdecl, dynlib: dllname, importc: "glXWaitGL".} +proc glXWaitX*(){.cdecl, dynlib: dllname, importc: "glXWaitX".} +proc glXUseXFont*(font: XFont, first, count, list: int){.cdecl, dynlib: dllname, + importc: "glXUseXFont".} + # GLX 1.1 and later +proc glXQueryExtensionsString*(dpy: PDisplay, screen: int): cstring{.cdecl, + dynlib: dllname, importc: "glXQueryExtensionsString".} +proc glXQueryServerString*(dpy: PDisplay, screen, name: int): cstring{.cdecl, + dynlib: dllname, importc: "glXQueryServerString".} +proc glXGetClientString*(dpy: PDisplay, name: int): cstring{.cdecl, + dynlib: dllname, importc: "glXGetClientString".} + # Mesa GLX Extensions +proc glXCreateGLXPixmapMESA*(dpy: PDisplay, visual: PXVisualInfo, + pixmap: XPixmap, cmap: XColormap): GLXPixmap{. + cdecl, dynlib: dllname, importc: "glXCreateGLXPixmapMESA".} +proc glXReleaseBufferMESA*(dpy: PDisplay, d: GLXDrawable): bool{.cdecl, + dynlib: dllname, importc: "glXReleaseBufferMESA".} +proc glXCopySubBufferMESA*(dpy: PDisplay, drawbale: GLXDrawable, + x, y, width, height: int){.cdecl, dynlib: dllname, + importc: "glXCopySubBufferMESA".} +proc glXGetVideoSyncSGI*(counter: var int32): int{.cdecl, dynlib: dllname, + importc: "glXGetVideoSyncSGI".} +proc glXWaitVideoSyncSGI*(divisor, remainder: int, count: var int32): int{. + cdecl, dynlib: dllname, importc: "glXWaitVideoSyncSGI".} +# implementation diff --git a/tests/deps/opengl-1.1.0/opengl.nim b/tests/deps/opengl-1.1.0/opengl.nim new file mode 100644 index 000000000..55e009a46 --- /dev/null +++ b/tests/deps/opengl-1.1.0/opengl.nim @@ -0,0 +1,8481 @@ + +# +# +# Nimrod's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module is a wrapper around `opengl`:idx:. If you define the symbol +## ``useGlew`` this wrapper does not use Nimrod's ``dynlib`` mechanism, +## but `glew`:idx: instead. However, this shouldn't be necessary anymore; even +## extension loading for the different operating systems is handled here. +## +## You need to call ``loadExtensions`` after a rendering context has been +## created to load any extension proc that your code uses. + +{.deadCodeElim: on.} + +import macros, sequtils + +{.push warning[User]: off.} + +when defined(linux) and not defined(android) and not defined(emscripten): + import X, XLib, XUtil +elif defined(windows): + import winlean, os + +when defined(windows): + const + ogldll* = "OpenGL32.dll" + gludll* = "GLU32.dll" +elif defined(macosx): + #macosx has this notion of a framework, thus the path to the openGL dylib files + #is absolute + const + ogldll* = "/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries/libGL.dylib" + gludll* = "/System/Library/Frameworks/OpenGL.framework/Versions/Current/Libraries/libGLU.dylib" +else: + const + ogldll* = "libGL.so.1" + gludll* = "libGLU.so.1" + +when defined(useGlew): + {.pragma: ogl, header: "".} + {.pragma: oglx, header: "".} + {.pragma: wgl, header: "".} + {.pragma: glu, dynlib: gludll.} +elif defined(ios): + {.pragma: ogl.} + {.pragma: oglx.} + {.passC: "-framework OpenGLES", passL: "-framework OpenGLES".} +elif defined(android) or defined(js) or defined(emscripten): + {.pragma: ogl.} + {.pragma: oglx.} +else: + # quite complex ... thanks to extension support for various platforms: + import dynlib + + let oglHandle = loadLib(ogldll) + if isNil(oglHandle): quit("could not load: " & ogldll) + + when defined(windows): + var wglGetProcAddress = cast[proc (s: cstring): pointer {.stdcall.}]( + symAddr(oglHandle, "wglGetProcAddress")) + elif defined(linux): + var glxGetProcAddress = cast[proc (s: cstring): pointer {.cdecl.}]( + symAddr(oglHandle, "glxGetProcAddress")) + var glxGetProcAddressArb = cast[proc (s: cstring): pointer {.cdecl.}]( + symAddr(oglHandle, "glxGetProcAddressARB")) + + proc glGetProc(h: LibHandle; procName: cstring): pointer = + when defined(windows): + result = symAddr(h, procname) + if result != nil: return + if not isNil(wglGetProcAddress): result = wglGetProcAddress(procName) + elif defined(linux): + if not isNil(glxGetProcAddress): result = glxGetProcAddress(procName) + if result != nil: return + if not isNil(glxGetProcAddressArb): + result = glxGetProcAddressArb(procName) + if result != nil: return + result = symAddr(h, procname) + else: + result = symAddr(h, procName) + if result == nil: raiseInvalidLibrary(procName) + + var gluHandle: LibHandle + + proc gluGetProc(procname: cstring): pointer = + if gluHandle == nil: + gluHandle = loadLib(gludll) + if gluHandle == nil: quit("could not load: " & gludll) + result = glGetProc(gluHandle, procname) + + # undocumented 'dynlib' feature: the string literal is replaced by + # the imported proc name: + {.pragma: ogl, dynlib: glGetProc(oglHandle, "0").} + {.pragma: oglx, dynlib: glGetProc(oglHandle, "0").} + {.pragma: wgl, dynlib: glGetProc(oglHandle, "0").} + {.pragma: glu, dynlib: gluGetProc("").} + + proc nimLoadProcs0() {.importc.} + + template loadExtensions*() = + ## call this after your rendering context has been setup if you use + ## extensions. + bind nimLoadProcs0 + nimLoadProcs0() + +{.pop.} # warning[User]: off + +type + GLenum* = distinct uint32 + GLboolean* = bool + GLbitfield* = distinct uint32 + GLvoid* = pointer + GLbyte* = int8 + GLshort* = int64 + GLint* = int32 + GLclampx* = int32 + GLubyte* = uint8 + GLushort* = uint16 + GLuint* = uint32 + GLhandle* = GLuint + GLsizei* = int32 + GLfloat* = float32 + GLclampf* = float32 + GLdouble* = float64 + GLclampd* = float64 + GLeglImageOES* = distinct pointer + GLchar* = char + GLcharArb* = char + GLfixed* = int32 + GLhalfNv* = uint16 + GLvdpauSurfaceNv* = uint + GLintptr* = int + GLintptrArb* = int + GLint64EXT* = int64 + GLuint64EXT* = uint64 + GLint64* = int64 + GLsizeiptrArb* = int + GLsizeiptr* = int + GLsync* = distinct pointer + GLuint64* = uint64 + GLvectorub2* = array[0..1, GLubyte] + GLvectori2* = array[0..1, GLint] + GLvectorf2* = array[0..1, GLfloat] + GLvectord2* = array[0..1, GLdouble] + GLvectorp2* = array[0..1, pointer] + GLvectorb3* = array[0..2, GLbyte] + GLvectorub3* = array[0..2, GLubyte] + GLvectori3* = array[0..2, GLint] + GLvectorui3* = array[0..2, GLuint] + GLvectorf3* = array[0..2, GLfloat] + GLvectord3* = array[0..2, GLdouble] + GLvectorp3* = array[0..2, pointer] + GLvectors3* = array[0..2, GLshort] + GLvectorus3* = array[0..2, GLushort] + GLvectorb4* = array[0..3, GLbyte] + GLvectorub4* = array[0..3, GLubyte] + GLvectori4* = array[0..3, GLint] + GLvectorui4* = array[0..3, GLuint] + GLvectorf4* = array[0..3, GLfloat] + GLvectord4* = array[0..3, GLdouble] + GLvectorp4* = array[0..3, pointer] + GLvectors4* = array[0..3, GLshort] + GLvectorus4* = array[0..3, GLshort] + GLarray4f* = GLvectorf4 + GLarrayf3* = GLvectorf3 + GLarrayd3* = GLvectord3 + GLarrayi4* = GLvectori4 + GLarrayp4* = GLvectorp4 + GLmatrixub3* = array[0..2, array[0..2, GLubyte]] + GLmatrixi3* = array[0..2, array[0..2, GLint]] + GLmatrixf3* = array[0..2, array[0..2, GLfloat]] + GLmatrixd3* = array[0..2, array[0..2, GLdouble]] + GLmatrixub4* = array[0..3, array[0..3, GLubyte]] + GLmatrixi4* = array[0..3, array[0..3, GLint]] + GLmatrixf4* = array[0..3, array[0..3, GLfloat]] + GLmatrixd4* = array[0..3, array[0..3, GLdouble]] + ClContext* = distinct pointer + ClEvent* = distinct pointer + GLdebugProc* = proc ( + source: GLenum, + typ: GLenum, + id: GLuint, + severity: GLenum, + length: GLsizei, + message: ptr GLchar, + userParam: pointer) {.stdcall.} + GLdebugProcArb* = proc ( + source: GLenum, + typ: GLenum, + id: GLuint, + severity: GLenum, + len: GLsizei, + message: ptr GLchar, + userParam: pointer) {.stdcall.} + GLdebugProcAmd* = proc ( + id: GLuint, + category: GLenum, + severity: GLenum, + len: GLsizei, + message: ptr GLchar, + userParam: pointer) {.stdcall.} + GLdebugProcKhr* = proc ( + source, typ: GLenum, + id: GLuint, + severity: GLenum, + length: GLsizei, + message: ptr GLchar, + userParam: pointer) {.stdcall.} +type + GLerrorCode* {.size: GLenum.sizeof.} = enum # XXX: can't be evaluated when + # in the same type section as + # GLenum. + glErrNoError = (0, "no error") + glErrInvalidEnum = (0x0500, "invalid enum") + glErrInvalidValue = (0x0501, "invalid value") + glErrInvalidOperation = (0x0502, "invalid operation") + glErrStackOverflow = (0x0503, "stack overflow") + glErrStackUnderflow = (0x0504, "stack underflow") + glErrOutOfMem = (0x0505, "out of memory") + glErrInvalidFramebufferOperation = (0x0506, "invalid framebuffer operation") + glErrTableTooLarge = (0x8031, "table too large") + +const AllErrorCodes = { + glErrNoError, + glErrInvalidEnum, + glErrInvalidValue, + glErrInvalidOperation, + glErrStackOverflow, + glErrStackUnderflow, + glErrOutOfMem, + glErrInvalidFramebufferOperation, + glErrTableTooLarge, +} + +when defined(macosx): + type + GLhandleArb = pointer +else: + type + GLhandleArb = uint32 + +{.deprecated: [ + TGLerror: GLerrorCode, + TGLhandleARB: GLhandleArb, + TGLenum: GLenum, + TGLboolean: GLboolean, + TGLbitfield: GLbitfield, + TGLvoid: GLvoid, + TGLbyte: GLbyte, + TGLshort: GLshort, + TGLint: GLint, + TGLclampx: GLclampx, + TGLubyte: GLubyte, + TGLushort: GLushort, + TGLuint: GLuint, + TGLsizei: GLsizei, + TGLfloat: GLfloat, + TGLclampf: GLclampf, + TGLdouble: GLdouble, + TGLclampd: GLclampd, + TGLeglImageOES: GLeglImageOES, + TGLchar: GLchar, + TGLcharARB: GLcharArb, + TGLfixed: GLfixed, + TGLhalfNV: GLhalfNv, + TGLvdpauSurfaceNv: GLvdpauSurfaceNv, + TGLintptr: GLintptr, + TGLintptrARB: GLintptrArb, + TGLint64EXT: GLint64Ext, + TGLuint64EXT: GLuint64Ext, + TGLint64: GLint64, + TGLsizeiptrARB: GLsizeiptrArb, + TGLsizeiptr: GLsizeiptr, + TGLsync: GLsync, + TGLuint64: GLuint64, + TCL_context: ClContext, + TCL_event: ClEvent, + TGLdebugProc: GLdebugProc, + TGLDebugProcARB: GLdebugProcArb, + TGLDebugProcAMD: GLdebugProcAmd, + TGLDebugProcKHR: GLdebugProcKhr, + TGLVectorub2: GLvectorub2, + TGLVectori2: GLvectori2, + TGLVectorf2: GLvectorf2, + TGLVectord2: GLvectord2, + TGLVectorp2: GLvectorp2, + TGLVectorb3: GLvectorb3, + TGLVectorub3: GLvectorub3, + TGLVectori3: GLvectori3, + TGLVectorui3: GLvectorui3, + TGLVectorf3: GLvectorf3, + TGLVectord3: GLvectord3, + TGLVectorp3: GLvectorp3, + TGLVectors3: GLvectors3, + TGLVectorus3: GLvectorus3, + TGLVectorb4: GLvectorb4, + TGLVectorub4: GLvectorub4, + TGLVectori4: GLvectori4, + TGLVectorui4: GLvectorui4, + TGLVectorf4: GLvectorf4, + TGLVectord4: GLvectord4, + TGLVectorp4: GLvectorp4, + TGLVectors4: GLvectors4, + TGLVectorus4: GLvectorus4, + TGLArrayf4: GLarray4f, + TGLArrayf3: GLarrayf3, + TGLArrayd3: GLarrayd3, + TGLArrayi4: GLarrayi4, + TGLArrayp4: GLarrayp4, + TGLMatrixub3: GLmatrixub3, + TGLMatrixi3: GLmatrixi3, + TGLMatrixf3: GLmatrixf3, + TGLMatrixd3: GLmatrixd3, + TGLMatrixub4: GLmatrixub4, + TGLMatrixi4: GLmatrixi4, + TGLMatrixf4: GLmatrixf4, + TGLMatrixd4: GLmatrixd4, + TGLVector3d: GLvectord3, + TGLVector4i: GLvectori4, + TGLVector4f: GLvectorf4, + TGLVector4p: GLvectorp4, + TGLMatrix4f: GLmatrixf4, + TGLMatrix4d: GLmatrixd4, +].} + +proc `==`*(a, b: GLenum): bool {.borrow.} + +proc `==`*(a, b: GLbitfield): bool {.borrow.} + +proc `or`*(a, b: GLbitfield): GLbitfield {.borrow.} + +proc hash*(x: GLenum): int = + result = x.int + +proc glGetError*: GLenum {.stdcall, importc, ogl.} +proc getGLerrorCode*: GLerrorCode = glGetError().GLerrorCode + ## Like ``glGetError`` but returns an enumerator instead. + +type + GLerror* = object of Exception + ## An exception for OpenGL errors. + code*: GLerrorCode ## The error code. This might be invalid for two reasons: + ## an outdated list of errors or a bad driver. + +proc checkGLerror* = + ## Raise ``GLerror`` if the last call to an OpenGL function generated an error. + ## You might want to call this once every frame for example if automatic + ## error checking has been disabled. + let error = getGLerrorCode() + if error == glErrNoError: + return + + var + exc = new(GLerror) + for e in AllErrorCodes: + if e == error: + exc.msg = "OpenGL error: " & $e + raise exc + + exc.code = error + exc.msg = "OpenGL error: unknown (" & $error & ")" + raise exc + +{.push warning[User]: off.} + +const + NoAutoGLerrorCheck* = defined(noAutoGLerrorCheck) ##\ + ## This determines (at compile time) whether an exception should be raised + ## if an OpenGL call generates an error. No additional code will be generated + ## and ``enableAutoGLerrorCheck(bool)`` will have no effect when + ## ``noAutoGLerrorCheck`` is defined. + +{.pop.} # warning[User]: off + +var + gAutoGLerrorCheck = true + gInsideBeginEnd* = false # do not change manually. + +proc enableAutoGLerrorCheck*(yes: bool) = + ## This determines (at run time) whether an exception should be raised if an + ## OpenGL call generates an error. This has no effect when + ## ``noAutoGLerrorCheck`` is defined. + gAutoGLerrorCheck = yes + +macro wrapErrorChecking(f: stmt): stmt {.immediate.} = + f.expectKind nnkStmtList + result = newStmtList() + + for child in f.children: + if child.kind == nnkCommentStmt: + continue + child.expectKind nnkProcDef + + let params = toSeq(child.params.children) + var glProc = copy child + glProc.pragma = newNimNode(nnkPragma).add( + newNimNode(nnkExprColonExpr).add( + ident"importc" , newLit($child.name)) + ).add(ident"ogl") + + let rawGLprocName = $glProc.name + glProc.name = ident(rawGLprocName & "Impl") + var + body = newStmtList glProc + returnsSomething = child.params[0].kind != nnkEmpty + callParams = newSeq[when defined(nimnode): NimNode else: PNimrodNode]() + for param in params[1 ..< params.len]: + callParams.add param[0] + + let glCall = newCall(glProc.name, callParams) + body.add if returnsSomething: + newAssignment(ident"result", glCall) + else: + glCall + + if rawGLprocName == "glBegin": + body.add newAssignment(ident"gInsideBeginEnd", ident"true") + if rawGLprocName == "glEnd": + body.add newAssignment(ident"gInsideBeginEnd", ident"false") + + template errCheck: stmt = + when not (NoAutoGLerrorCheck): + if gAutoGLerrorCheck and not gInsideBeginEnd: + checkGLerror() + + body.add getAst(errCheck()) + + var procc = newProc(child.name, params, body) + procc.pragma = newNimNode(nnkPragma).add(ident"inline") + procc.name = postfix(procc.name, "*") + result.add procc + +{.push stdcall, hint[XDeclaredButNotUsed]: off, warning[SmallLshouldNotBeUsed]: off.} +wrapErrorChecking: + proc glMultiTexCoord2d(target: GLenum, s: GLdouble, t: GLdouble) {.importc.} + proc glDrawElementsIndirect(mode: GLenum, `type`: GLenum, indirect: pointer) {.importc.} + proc glEnableVertexArrayEXT(vaobj: GLuint, `array`: GLenum) {.importc.} + proc glDeleteFramebuffers(n: GLsizei, framebuffers: ptr GLuint) {.importc.} + proc glMultiTexCoord3dv(target: GLenum, v: ptr GLdouble) {.importc.} + proc glVertexAttrib4d(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glLoadPaletteFromModelViewMatrixOES() {.importc.} + proc glVertex3xvOES(coords: ptr GLfixed) {.importc.} + proc glNormalStream3sATI(stream: GLenum, nx: GLshort, ny: GLshort, nz: GLshort) {.importc.} + proc glMatrixFrustumEXT(mode: GLenum, left: GLdouble, right: GLdouble, bottom: GLdouble, top: GLdouble, zNear: GLdouble, zFar: GLdouble) {.importc.} + proc glUniformMatrix2fvARB(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glColor4dv(v: ptr GLdouble) {.importc.} + proc glColor3fv(v: ptr GLfloat) {.importc.} + proc glVertexAttribI1uiEXT(index: GLuint, x: GLuint) {.importc.} + proc glGetDebugMessageLogKHR(count: GLuint, bufsize: GLsizei, sources: ptr GLenum, types: ptr GLenum, ids: ptr GLuint, severities: ptr GLenum, lengths: ptr GLsizei, messageLog: cstring): GLuint {.importc.} + proc glVertexAttribI2iv(index: GLuint, v: ptr GLint) {.importc.} + proc glTexCoord1xvOES(coords: ptr GLfixed) {.importc.} + proc glVertex3hNV(x: GLhalfNv, y: GLhalfNv, z: GLhalfNv) {.importc.} + proc glIsShader(shader: GLuint): GLboolean {.importc.} + proc glDeleteRenderbuffersEXT(n: GLsizei, renderbuffers: ptr GLuint) {.importc.} + proc glVertex3hvNV(v: ptr GLhalfNv) {.importc.} + proc glGetPointervKHR(pname: GLenum, params: ptr pointer) {.importc.} + proc glProgramUniform3i64vNV(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint64Ext) {.importc.} + proc glNamedFramebufferTexture1DEXT(framebuffer: GLuint, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glGetNamedProgramLocalParameterfvEXT(program: GLuint, target: GLenum, index: GLuint, params: ptr GLfloat) {.importc.} + proc glGenRenderbuffersOES(n: GLsizei, renderbuffers: ptr GLuint) {.importc.} + proc glVertex4dv(v: ptr GLdouble) {.importc.} + proc glTexCoord2fColor4ubVertex3fvSUN(tc: ptr GLfloat, c: ptr GLubyte, v: ptr GLfloat) {.importc.} + proc glTexStorage2DEXT(target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glVertexAttrib2d(index: GLuint, x: GLdouble, y: GLdouble) {.importc.} + proc glVertexAttrib1dv(index: GLuint, v: ptr GLdouble) {.importc.} + proc glBindProgramARB(target: GLenum, program: GLuint) {.importc.} + proc glRasterPos2dv(v: ptr GLdouble) {.importc.} + proc glCompressedTextureSubImage2DEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, width: GLsizei, height: GLsizei, format: GLenum, imageSize: GLsizei, bits: pointer) {.importc.} + proc glNormalPointervINTEL(`type`: GLenum, `pointer`: ptr pointer) {.importc.} + proc glGetInteger64vAPPLE(pname: GLenum, params: ptr GLint64) {.importc.} + proc glPushMatrix() {.importc.} + proc glGetCompressedTexImageARB(target: GLenum, level: GLint, img: pointer) {.importc.} + proc glBindMaterialParameterEXT(face: GLenum, value: GLenum): GLuint {.importc.} + proc glBlendEquationIndexedAMD(buf: GLuint, mode: GLenum) {.importc.} + proc glGetObjectBufferfvATI(buffer: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glMakeNamedBufferNonResidentNV(buffer: GLuint) {.importc.} + proc glUniform2ui64NV(location: GLint, x: GLuint64Ext, y: GLuint64Ext) {.importc.} + proc glRasterPos4fv(v: ptr GLfloat) {.importc.} + proc glDeleteTextures(n: GLsizei, textures: ptr GLuint) {.importc.} + proc glSecondaryColorPointer(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glTextureSubImage1DEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, width: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glEndTilingQCOM(preserveMask: GLbitfield) {.importc.} + proc glBindBuffer(target: GLenum, buffer: GLuint) {.importc.} + proc glUniformMatrix3fvARB(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glSamplerParameterf(sampler: GLuint, pname: GLenum, param: GLfloat) {.importc.} + proc glSecondaryColor3d(red: GLdouble, green: GLdouble, blue: GLdouble) {.importc.} + proc glVertexAttrib4sARB(index: GLuint, x: GLshort, y: GLshort, z: GLshort, w: GLshort) {.importc.} + proc glNamedProgramLocalParameterI4iEXT(program: GLuint, target: GLenum, index: GLuint, x: GLint, y: GLint, z: GLint, w: GLint) {.importc.} + proc glProgramUniform2iEXT(program: GLuint, location: GLint, v0: GLint, v1: GLint) {.importc.} + proc glPopAttrib() {.importc.} + proc glGetnColorTableARB(target: GLenum, format: GLenum, `type`: GLenum, bufSize: GLsizei, table: pointer) {.importc.} + proc glMatrixLoadIdentityEXT(mode: GLenum) {.importc.} + proc glGetNamedProgramivEXT(program: GLuint, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glCopyTextureSubImage2DEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glUniform4i64NV(location: GLint, x: GLint64Ext, y: GLint64Ext, z: GLint64Ext, w: GLint64Ext) {.importc.} + proc glDeleteTexturesEXT(n: GLsizei, textures: ptr GLuint) {.importc.} + proc glMultiTexCoord1dv(target: GLenum, v: ptr GLdouble) {.importc.} + proc glMultiTexRenderbufferEXT(texunit: GLenum, target: GLenum, renderbuffer: GLuint) {.importc.} + proc glMultiDrawArraysIndirect(mode: GLenum, indirect: ptr pointer, drawcount: GLsizei, stride: GLsizei) {.importc.} + proc glGetUniformfvARB(programObj: GLhandleArb, location: GLint, params: ptr GLfloat) {.importc.} + proc glBufferDataARB(target: GLenum, size: GLsizeiptrArb, data: pointer, usage: GLenum) {.importc.} + proc glTexCoord2d(s: GLdouble, t: GLdouble) {.importc.} + proc glGetArrayObjectfvATI(`array`: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glShaderOp1EXT(op: GLenum, res: GLuint, arg1: GLuint) {.importc.} + proc glColor3s(red: GLshort, green: GLshort, blue: GLshort) {.importc.} + proc glStencilFuncSeparate(face: GLenum, fun: GLenum, `ref`: GLint, mask: GLuint) {.importc.} + proc glTextureImage2DMultisampleCoverageNV(texture: GLuint, target: GLenum, coverageSamples: GLsizei, colorSamples: GLsizei, internalFormat: GLint, width: GLsizei, height: GLsizei, fixedSampleLocations: GLboolean) {.importc.} + proc glMultiTexCoord2xvOES(texture: GLenum, coords: ptr GLfixed) {.importc.} + proc glGetVertexAttribLui64vNV(index: GLuint, pname: GLenum, params: ptr GLuint64Ext) {.importc.} + proc glNormal3xOES(nx: GLfixed, ny: GLfixed, nz: GLfixed) {.importc.} + proc glMapBufferRangeEXT(target: GLenum, offset: GLintptr, length: GLsizeiptr, access: GLbitfield): pointer {.importc.} + proc glCreateShader(`type`: GLenum): GLuint {.importc.} + proc glDrawRangeElementArrayAPPLE(mode: GLenum, start: GLuint, `end`: GLuint, first: GLint, count: GLsizei) {.importc.} + proc glVertex2bOES(x: GLbyte) {.importc.} + proc glGetMapxvOES(target: GLenum, query: GLenum, v: ptr GLfixed) {.importc.} + proc glRasterPos3sv(v: ptr GLshort) {.importc.} + proc glDeleteQueriesARB(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glProgramUniform1iv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glVertexStream2dvATI(stream: GLenum, coords: ptr GLdouble) {.importc.} + proc glBindVertexArrayOES(`array`: GLuint) {.importc.} + proc glLightModelfv(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glEvalCoord2dv(u: ptr GLdouble) {.importc.} + proc glColor3hNV(red: GLhalfNv, green: GLhalfNv, blue: GLhalfNv) {.importc.} + proc glSecondaryColor3iEXT(red: GLint, green: GLint, blue: GLint) {.importc.} + proc glBindTexture(target: GLenum, texture: GLuint) {.importc.} + proc glUniformBufferEXT(program: GLuint, location: GLint, buffer: GLuint) {.importc.} + proc glGetCombinerInputParameterfvNV(stage: GLenum, portion: GLenum, variable: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glUniform2ui64vNV(location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glMatrixMultTransposefEXT(mode: GLenum, m: ptr GLfloat) {.importc.} + proc glLineWidth(width: GLfloat) {.importc.} + proc glRotatef(angle: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glNormalStream3svATI(stream: GLenum, coords: ptr GLshort) {.importc.} + proc glTexCoordP4ui(`type`: GLenum, coords: GLuint) {.importc.} + proc glImageTransformParameterfvHP(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glUniform3uiEXT(location: GLint, v0: GLuint, v1: GLuint, v2: GLuint) {.importc.} + proc glGetInvariantIntegervEXT(id: GLuint, value: GLenum, data: ptr GLint) {.importc.} + proc glGetTransformFeedbackVaryingEXT(program: GLuint, index: GLuint, bufSize: GLsizei, length: ptr GLsizei, size: ptr GLsizei, `type`: ptr GLenum, name: cstring) {.importc.} + proc glSamplerParameterIuiv(sampler: GLuint, pname: GLenum, param: ptr GLuint) {.importc.} + proc glProgramUniform2fEXT(program: GLuint, location: GLint, v0: GLfloat, v1: GLfloat) {.importc.} + proc glMultiTexCoord2hvNV(target: GLenum, v: ptr GLhalfNv) {.importc.} + proc glDeleteRenderbuffersOES(n: GLsizei, renderbuffers: ptr GLuint) {.importc.} + proc glRenderbufferStorageMultisampleCoverageNV(target: GLenum, coverageSamples: GLsizei, colorSamples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glStencilClearTagEXT(stencilTagBits: GLsizei, stencilClearTag: GLuint) {.importc.} + proc glConvolutionParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glFenceSyncAPPLE(condition: GLenum, flags: GLbitfield): GLsync {.importc.} + proc glGetVariantArrayObjectivATI(id: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glProgramUniform4dvEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glPushDebugGroupKHR(source: GLenum, id: GLuint, length: GLsizei, message: cstring) {.importc.} + proc glFragmentLightivSGIX(light: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glFramebufferTexture2DEXT(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glGetActiveSubroutineUniformiv(program: GLuint, shadertype: GLenum, index: GLuint, pname: GLenum, values: ptr GLint) {.importc.} + proc glFrustumf(l: GLfloat, r: GLfloat, b: GLfloat, t: GLfloat, n: GLfloat, f: GLfloat) {.importc.} + proc glEndQueryIndexed(target: GLenum, index: GLuint) {.importc.} + proc glCompressedTextureSubImage3DEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, imageSize: GLsizei, bits: pointer) {.importc.} + proc glGetProgramPipelineInfoLogEXT(pipeline: GLuint, bufSize: GLsizei, length: ptr GLsizei, infoLog: cstring) {.importc.} + proc glGetVertexAttribfvNV(index: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertexArrayIndexOffsetEXT(vaobj: GLuint, buffer: GLuint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glDrawTexsvOES(coords: ptr GLshort) {.importc.} + proc glMultiTexCoord1hNV(target: GLenum, s: GLhalfNv) {.importc.} + proc glWindowPos2iv(v: ptr GLint) {.importc.} + proc glMultiTexCoordP1ui(texture: GLenum, `type`: GLenum, coords: GLuint) {.importc.} + proc glTexCoord1i(s: GLint) {.importc.} + proc glVertex4hvNV(v: ptr GLhalfNv) {.importc.} + proc glCallLists(n: GLsizei, `type`: GLenum, lists: pointer) {.importc.} + proc glIndexFormatNV(`type`: GLenum, stride: GLsizei) {.importc.} + proc glPointParameterfARB(pname: GLenum, param: GLfloat) {.importc.} + proc glProgramUniform1dv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glGetVertexAttribArrayObjectfvATI(index: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVDPAUUnmapSurfacesNV(numSurface: GLsizei, surfaces: ptr GLvdpauSurfaceNv) {.importc.} + proc glVertexAttribIFormat(attribindex: GLuint, size: GLint, `type`: GLenum, relativeoffset: GLuint) {.importc.} + proc glClearColorx(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed) {.importc.} + proc glColor3bv(v: ptr GLbyte) {.importc.} + proc glNamedProgramLocalParameter4dEXT(program: GLuint, target: GLenum, index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glVertexPointer(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glGetObjectLabelKHR(identifier: GLenum, name: GLuint, bufSize: GLsizei, length: ptr GLsizei, label: cstring) {.importc.} + proc glCombinerStageParameterfvNV(stage: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glNormal3hvNV(v: ptr GLhalfNv) {.importc.} + proc glUniform2i64NV(location: GLint, x: GLint64Ext, y: GLint64Ext) {.importc.} + proc glMultiTexCoord2iv(target: GLenum, v: ptr GLint) {.importc.} + proc glProgramUniform3i(program: GLuint, location: GLint, v0: GLint, v1: GLint, v2: GLint) {.importc.} + proc glDeleteAsyncMarkersSGIX(marker: GLuint, range: GLsizei) {.importc.} + proc glStencilOp(fail: GLenum, zfail: GLenum, zpass: GLenum) {.importc.} + proc glColorP4ui(`type`: GLenum, color: GLuint) {.importc.} + proc glFinishAsyncSGIX(markerp: ptr GLuint): GLint {.importc.} + proc glDrawTexsOES(x: GLshort, y: GLshort, z: GLshort, width: GLshort, height: GLshort) {.importc.} + proc glLineStipple(factor: GLint, pattern: GLushort) {.importc.} + proc glAlphaFragmentOp1ATI(op: GLenum, dst: GLuint, dstMod: GLuint, arg1: GLuint, arg1Rep: GLuint, arg1Mod: GLuint) {.importc.} + proc glMapTexture2DINTEL(texture: GLuint, level: GLint, access: GLbitfield, stride: ptr GLint, layout: ptr GLenum): pointer {.importc.} + proc glVertex4f(x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glFramebufferTextureARB(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glProgramUniform3ui64NV(program: GLuint, location: GLint, x: GLuint64Ext, y: GLuint64Ext, z: GLuint64Ext) {.importc.} + proc glMultTransposeMatrixxOES(m: ptr GLfixed) {.importc.} + proc glNormal3fv(v: ptr GLfloat) {.importc.} + proc glUniform4fARB(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat) {.importc.} + proc glBinormal3bEXT(bx: GLbyte, by: GLbyte, bz: GLbyte) {.importc.} + proc glGenProgramPipelinesEXT(n: GLsizei, pipelines: ptr GLuint) {.importc.} + proc glDispatchComputeIndirect(indirect: GLintptr) {.importc.} + proc glGetPerfMonitorCounterDataAMD(monitor: GLuint, pname: GLenum, dataSize: GLsizei, data: ptr GLuint, bytesWritten: ptr GLint) {.importc.} + proc glStencilOpValueAMD(face: GLenum, value: GLuint) {.importc.} + proc glTangent3fvEXT(v: ptr GLfloat) {.importc.} + proc glUniform3iARB(location: GLint, v0: GLint, v1: GLint, v2: GLint) {.importc.} + proc glMatrixScalefEXT(mode: GLenum, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glVertexAttrib2dARB(index: GLuint, x: GLdouble, y: GLdouble) {.importc.} + proc glIsVertexArray(`array`: GLuint): GLboolean {.importc.} + proc glGetMaterialx(face: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glMultiTexCoord1dARB(target: GLenum, s: GLdouble) {.importc.} + proc glColor3usv(v: ptr GLushort) {.importc.} + proc glVertexStream3svATI(stream: GLenum, coords: ptr GLshort) {.importc.} + proc glRasterPos3s(x: GLshort, y: GLshort, z: GLshort) {.importc.} + proc glMultiTexCoord2bOES(texture: GLenum, s: GLbyte, t: GLbyte) {.importc.} + proc glGetClipPlanefOES(plane: GLenum, equation: ptr GLfloat) {.importc.} + proc glFramebufferTextureEXT(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glVertexAttrib1dNV(index: GLuint, x: GLdouble) {.importc.} + proc glSampleCoverageOES(value: GLfixed, invert: GLboolean) {.importc.} + proc glCompressedTexSubImage2DARB(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, width: GLsizei, height: GLsizei, format: GLenum, imageSize: GLsizei, data: pointer) {.importc.} + proc glUniform1iv(location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glExtGetProgramsQCOM(programs: ptr GLuint, maxPrograms: GLint, numPrograms: ptr GLint) {.importc.} + proc glFogx(pname: GLenum, param: GLfixed) {.importc.} + proc glMultiTexCoord3hNV(target: GLenum, s: GLhalfNv, t: GLhalfNv, r: GLhalfNv) {.importc.} + proc glClipPlane(plane: GLenum, equation: ptr GLdouble) {.importc.} + proc glConvolutionParameterxvOES(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glInvalidateBufferData(buffer: GLuint) {.importc.} + proc glCheckNamedFramebufferStatusEXT(framebuffer: GLuint, target: GLenum): GLenum {.importc.} + proc glLinkProgram(program: GLuint) {.importc.} + proc glCheckFramebufferStatus(target: GLenum): GLenum {.importc.} + proc glBlendFunci(buf: GLuint, src: GLenum, dst: GLenum) {.importc.} + proc glProgramUniform4uiv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glConvolutionFilter2D(target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, image: pointer) {.importc.} + proc glVertex4bvOES(coords: ptr GLbyte) {.importc.} + proc glCopyTextureSubImage1DEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glColor4uiv(v: ptr GLuint) {.importc.} + proc glGetBufferParameteri64v(target: GLenum, pname: GLenum, params: ptr GLint64) {.importc.} + proc glGetLocalConstantBooleanvEXT(id: GLuint, value: GLenum, data: ptr GLboolean) {.importc.} + proc glCoverStrokePathNV(path: GLuint, coverMode: GLenum) {.importc.} + proc glScaled(x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glLightfv(light: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glTexParameterIiv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glMakeImageHandleResidentNV(handle: GLuint64, access: GLenum) {.importc.} + proc glWindowPos3iARB(x: GLint, y: GLint, z: GLint) {.importc.} + proc glListBase(base: GLuint) {.importc.} + proc glFlushMappedBufferRangeEXT(target: GLenum, offset: GLintptr, length: GLsizeiptr) {.importc.} + proc glNormal3dv(v: ptr GLdouble) {.importc.} + proc glProgramUniform4d(program: GLuint, location: GLint, v0: GLdouble, v1: GLdouble, v2: GLdouble, v3: GLdouble) {.importc.} + proc glCreateShaderProgramEXT(`type`: GLenum, string: cstring): GLuint {.importc.} + proc glGetLightxvOES(light: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glGetObjectPtrLabelKHR(`ptr`: ptr pointer, bufSize: GLsizei, length: ptr GLsizei, label: cstring) {.importc.} + proc glTransformPathNV(resultPath: GLuint, srcPath: GLuint, transformType: GLenum, transformValues: ptr GLfloat) {.importc.} + proc glMultTransposeMatrixf(m: ptr GLfloat) {.importc.} + proc glMapVertexAttrib2dAPPLE(index: GLuint, size: GLuint, u1: GLdouble, u2: GLdouble, ustride: GLint, uorder: GLint, v1: GLdouble, v2: GLdouble, vstride: GLint, vorder: GLint, points: ptr GLdouble) {.importc.} + proc glIsSync(sync: GLsync): GLboolean {.importc.} + proc glMultMatrixx(m: ptr GLfixed) {.importc.} + proc glInterpolatePathsNV(resultPath: GLuint, pathA: GLuint, pathB: GLuint, weight: GLfloat) {.importc.} + proc glEnableClientStateIndexedEXT(`array`: GLenum, index: GLuint) {.importc.} + proc glProgramEnvParameter4fARB(target: GLenum, index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glVertexAttrib2svARB(index: GLuint, v: ptr GLshort) {.importc.} + proc glLighti(light: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glSelectBuffer(size: GLsizei, buffer: ptr GLuint) {.importc.} + proc glReplacementCodeusvSUN(code: ptr GLushort) {.importc.} + proc glMapVertexAttrib1fAPPLE(index: GLuint, size: GLuint, u1: GLfloat, u2: GLfloat, stride: GLint, order: GLint, points: ptr GLfloat) {.importc.} + proc glMaterialx(face: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glDrawTransformFeedback(mode: GLenum, id: GLuint) {.importc.} + proc glWindowPos2i(x: GLint, y: GLint) {.importc.} + proc glMultiTexEnviEXT(texunit: GLenum, target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glProgramUniform1fv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glDrawBuffersARB(n: GLsizei, bufs: ptr GLenum) {.importc.} + proc glGetUniformLocationARB(programObj: GLhandleArb, name: cstring): GLint {.importc.} + proc glResumeTransformFeedback() {.importc.} + proc glMap1f(target: GLenum, u1: GLfloat, u2: GLfloat, stride: GLint, order: GLint, points: ptr GLfloat) {.importc.} + proc glVertex3xOES(x: GLfixed, y: GLfixed) {.importc.} + proc glPathCoordsNV(path: GLuint, numCoords: GLsizei, coordType: GLenum, coords: pointer) {.importc.} + proc glListParameterfSGIX(list: GLuint, pname: GLenum, param: GLfloat) {.importc.} + proc glGetUniformivARB(programObj: GLhandleArb, location: GLint, params: ptr GLint) {.importc.} + proc glBinormal3bvEXT(v: ptr GLbyte) {.importc.} + proc glVertexAttribP3ui(index: GLuint, `type`: GLenum, normalized: GLboolean, value: GLuint) {.importc.} + proc glGetVertexArrayPointeri_vEXT(vaobj: GLuint, index: GLuint, pname: GLenum, param: ptr pointer) {.importc.} + proc glProgramParameter4fvNV(target: GLenum, index: GLuint, v: ptr GLfloat) {.importc.} + proc glDiscardFramebufferEXT(target: GLenum, numAttachments: GLsizei, attachments: ptr GLenum) {.importc.} + proc glGetDebugMessageLogARB(count: GLuint, bufsize: GLsizei, sources: ptr GLenum, types: ptr GLenum, ids: ptr GLuint, severities: ptr GLenum, lengths: ptr GLsizei, messageLog: cstring): GLuint {.importc.} + proc glResolveMultisampleFramebufferAPPLE() {.importc.} + proc glGetIntegeri_vEXT(target: GLenum, index: GLuint, data: ptr GLint) {.importc.} + proc glDepthBoundsdNV(zmin: GLdouble, zmax: GLdouble) {.importc.} + proc glEnd() {.importc.} + proc glBindBufferBaseEXT(target: GLenum, index: GLuint, buffer: GLuint) {.importc.} + proc glVertexAttribDivisor(index: GLuint, divisor: GLuint) {.importc.} + proc glFogCoorddEXT(coord: GLdouble) {.importc.} + proc glFrontFace(mode: GLenum) {.importc.} + proc glVertexAttrib1hNV(index: GLuint, x: GLhalfNv) {.importc.} + proc glNamedProgramLocalParametersI4uivEXT(program: GLuint, target: GLenum, index: GLuint, count: GLsizei, params: ptr GLuint) {.importc.} + proc glTexCoord1dv(v: ptr GLdouble) {.importc.} + proc glBindVideoCaptureStreamTextureNV(video_capture_slot: GLuint, stream: GLuint, frame_region: GLenum, target: GLenum, texture: GLuint) {.importc.} + proc glWindowPos2iARB(x: GLint, y: GLint) {.importc.} + proc glVertexAttribFormatNV(index: GLuint, size: GLint, `type`: GLenum, normalized: GLboolean, stride: GLsizei) {.importc.} + proc glUniform1uivEXT(location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glGetVideoivNV(video_slot: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexAttrib3fvARB(index: GLuint, v: ptr GLfloat) {.importc.} + proc glVertexArraySecondaryColorOffsetEXT(vaobj: GLuint, buffer: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glSecondaryColor3bv(v: ptr GLbyte) {.importc.} + proc glDispatchComputeGroupSizeARB(num_groups_x: GLuint, num_groups_y: GLuint, num_groups_z: GLuint, group_size_x: GLuint, group_size_y: GLuint, group_size_z: GLuint) {.importc.} + proc glNamedCopyBufferSubDataEXT(readBuffer: GLuint, writeBuffer: GLuint, readOffset: GLintptr, writeOffset: GLintptr, size: GLsizeiptr) {.importc.} + proc glSampleCoverage(value: GLfloat, invert: GLboolean) {.importc.} + proc glGetnMapfvARB(target: GLenum, query: GLenum, bufSize: GLsizei, v: ptr GLfloat) {.importc.} + proc glVertexStream2svATI(stream: GLenum, coords: ptr GLshort) {.importc.} + proc glProgramParameters4fvNV(target: GLenum, index: GLuint, count: GLsizei, v: ptr GLfloat) {.importc.} + proc glVertexAttrib4fARB(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glIndexd(c: GLdouble) {.importc.} + proc glGetInteger64v(pname: GLenum, params: ptr GLint64) {.importc.} + proc glGetMultiTexImageEXT(texunit: GLenum, target: GLenum, level: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glLightModelx(pname: GLenum, param: GLfixed) {.importc.} + proc glMap2f(target: GLenum, u1: GLfloat, u2: GLfloat, ustride: GLint, uorder: GLint, v1: GLfloat, v2: GLfloat, vstride: GLint, vorder: GLint, points: ptr GLfloat) {.importc.} + proc glSecondaryColorPointerListIBM(size: GLint, `type`: GLenum, stride: GLint, `pointer`: ptr pointer, ptrstride: GLint) {.importc.} + proc glVertexArrayVertexAttribIOffsetEXT(vaobj: GLuint, buffer: GLuint, index: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glProgramUniformHandleui64vARB(program: GLuint, location: GLint, count: GLsizei, values: ptr GLuint64) {.importc.} + proc glActiveProgramEXT(program: GLuint) {.importc.} + proc glProgramUniformMatrix4x3fv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glCompressedTexSubImage3DARB(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, imageSize: GLsizei, data: pointer) {.importc.} + proc glBindProgramPipelineEXT(pipeline: GLuint) {.importc.} + proc glDetailTexFuncSGIS(target: GLenum, n: GLsizei, points: ptr GLfloat) {.importc.} + proc glSecondaryColor3ubEXT(red: GLubyte, green: GLubyte, blue: GLubyte) {.importc.} + proc glDrawArraysInstanced(mode: GLenum, first: GLint, count: GLsizei, instancecount: GLsizei) {.importc.} + proc glWindowPos3fARB(x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glNamedProgramLocalParameter4fEXT(program: GLuint, target: GLenum, index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glTextureParameterfvEXT(texture: GLuint, target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glProgramUniformHandleui64ARB(program: GLuint, location: GLint, value: GLuint64) {.importc.} + proc glHistogramEXT(target: GLenum, width: GLsizei, internalformat: GLenum, sink: GLboolean) {.importc.} + proc glResumeTransformFeedbackNV() {.importc.} + proc glGetMaterialxv(face: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glMultiTexCoord1sv(target: GLenum, v: ptr GLshort) {.importc.} + proc glReadInstrumentsSGIX(marker: GLint) {.importc.} + proc glTexCoord4hNV(s: GLhalfNv, t: GLhalfNv, r: GLhalfNv, q: GLhalfNv) {.importc.} + proc glVertexAttribL4i64vNV(index: GLuint, v: ptr GLint64Ext) {.importc.} + proc glEnableVariantClientStateEXT(id: GLuint) {.importc.} + proc glSyncTextureINTEL(texture: GLuint) {.importc.} + proc glGetObjectPtrLabel(`ptr`: ptr pointer, bufSize: GLsizei, length: ptr GLsizei, label: cstring) {.importc.} + proc glCopyTexSubImage1D(target: GLenum, level: GLint, xoffset: GLint, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glOrthofOES(l: GLfloat, r: GLfloat, b: GLfloat, t: GLfloat, n: GLfloat, f: GLfloat) {.importc.} + proc glWindowPos3sARB(x: GLshort, y: GLshort, z: GLshort) {.importc.} + proc glIsBufferARB(buffer: GLuint): GLboolean {.importc.} + proc glColor3sv(v: ptr GLshort) {.importc.} + proc glEvalMesh1(mode: GLenum, i1: GLint, i2: GLint) {.importc.} + proc glMultiDrawArrays(mode: GLenum, first: ptr GLint, count: ptr GLsizei, drawcount: GLsizei) {.importc.} + proc glGetMultiTexEnvfvEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glWindowPos3fMESA(x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glExtGetFramebuffersQCOM(framebuffers: ptr GLuint, maxFramebuffers: GLint, numFramebuffers: ptr GLint) {.importc.} + proc glTexSubImage3D(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glVertexAttrib4uiv(index: GLuint, v: ptr GLuint) {.importc.} + proc glProgramUniformui64NV(program: GLuint, location: GLint, value: GLuint64Ext) {.importc.} + proc glMultiTexCoord2ivARB(target: GLenum, v: ptr GLint) {.importc.} + proc glProgramUniform4i64NV(program: GLuint, location: GLint, x: GLint64Ext, y: GLint64Ext, z: GLint64Ext, w: GLint64Ext) {.importc.} + proc glWindowPos2svMESA(v: ptr GLshort) {.importc.} + proc glVertexAttrib3dv(index: GLuint, v: ptr GLdouble) {.importc.} + proc glColor4i(red: GLint, green: GLint, blue: GLint, alpha: GLint) {.importc.} + proc glClampColor(target: GLenum, clamp: GLenum) {.importc.} + proc glVertexP2ui(`type`: GLenum, value: GLuint) {.importc.} + proc glGenQueries(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glBindBufferOffsetNV(target: GLenum, index: GLuint, buffer: GLuint, offset: GLintptr) {.importc.} + proc glGetFragDataLocation(program: GLuint, name: cstring): GLint {.importc.} + proc glVertexAttribs2svNV(index: GLuint, count: GLsizei, v: ptr GLshort) {.importc.} + proc glGetPathLengthNV(path: GLuint, startSegment: GLsizei, numSegments: GLsizei): GLfloat {.importc.} + proc glVertexAttrib3dARB(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glMultiTexGenfvEXT(texunit: GLenum, coord: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glFlushPixelDataRangeNV(target: GLenum) {.importc.} + proc glReplacementCodeuiNormal3fVertex3fSUN(rc: GLuint, nx: GLfloat, ny: GLfloat, nz: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glPathParameteriNV(path: GLuint, pname: GLenum, value: GLint) {.importc.} + proc glVertexAttribI2iEXT(index: GLuint, x: GLint, y: GLint) {.importc.} + proc glPixelStorei(pname: GLenum, param: GLint) {.importc.} + proc glGetNamedFramebufferParameterivEXT(framebuffer: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetTexEnvxv(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glPathStringNV(path: GLuint, format: GLenum, length: GLsizei, pathString: pointer) {.importc.} + proc glDepthMask(flag: GLboolean) {.importc.} + proc glCopyTexImage1D(target: GLenum, level: GLint, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, border: GLint) {.importc.} + proc glDepthRangexOES(n: GLfixed, f: GLfixed) {.importc.} + proc glUniform2i64vNV(location: GLint, count: GLsizei, value: ptr GLint64Ext) {.importc.} + proc glSetFragmentShaderConstantATI(dst: GLuint, value: ptr GLfloat) {.importc.} + proc glAttachShader(program: GLuint, shader: GLuint) {.importc.} + proc glGetFramebufferParameterivEXT(framebuffer: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glPointParameteriNV(pname: GLenum, param: GLint) {.importc.} + proc glWindowPos2dMESA(x: GLdouble, y: GLdouble) {.importc.} + proc glGetTextureParameterfvEXT(texture: GLuint, target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glTexBumpParameterfvATI(pname: GLenum, param: ptr GLfloat) {.importc.} + proc glCompressedTexImage1DARB(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, border: GLint, imageSize: GLsizei, data: pointer) {.importc.} + proc glGetTexGendv(coord: GLenum, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glGetFragmentMaterialfvSGIX(face: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glBeginConditionalRenderNVX(id: GLuint) {.importc.} + proc glLightModelxOES(pname: GLenum, param: GLfixed) {.importc.} + proc glTexCoord2xOES(s: GLfixed, t: GLfixed) {.importc.} + proc glProgramUniformMatrix2x4fvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glRasterPos2xvOES(coords: ptr GLfixed) {.importc.} + proc glGetMapiv(target: GLenum, query: GLenum, v: ptr GLint) {.importc.} + proc glGetImageHandleARB(texture: GLuint, level: GLint, layered: GLboolean, layer: GLint, format: GLenum): GLuint64 {.importc.} + proc glVDPAURegisterVideoSurfaceNV(vdpSurface: pointer, target: GLenum, numTextureNames: GLsizei, textureNames: ptr GLuint): GLvdpauSurfaceNv {.importc.} + proc glVertexAttribL2dEXT(index: GLuint, x: GLdouble, y: GLdouble) {.importc.} + proc glVertexAttrib1dvNV(index: GLuint, v: ptr GLdouble) {.importc.} + proc glPollAsyncSGIX(markerp: ptr GLuint): GLint {.importc.} + proc glCullParameterfvEXT(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glMakeNamedBufferResidentNV(buffer: GLuint, access: GLenum) {.importc.} + proc glPointParameterfSGIS(pname: GLenum, param: GLfloat) {.importc.} + proc glGenLists(range: GLsizei): GLuint {.importc.} + proc glGetTexBumpParameterfvATI(pname: GLenum, param: ptr GLfloat) {.importc.} + proc glCompressedMultiTexSubImage2DEXT(texunit: GLenum, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, width: GLsizei, height: GLsizei, format: GLenum, imageSize: GLsizei, bits: pointer) {.importc.} + proc glFinishFenceNV(fence: GLuint) {.importc.} + proc glPointSize(size: GLfloat) {.importc.} + proc glCompressedTextureImage2DEXT(texture: GLuint, target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, border: GLint, imageSize: GLsizei, bits: pointer) {.importc.} + proc glGetUniformui64vNV(program: GLuint, location: GLint, params: ptr GLuint64Ext) {.importc.} + proc glGetMapControlPointsNV(target: GLenum, index: GLuint, `type`: GLenum, ustride: GLsizei, vstride: GLsizei, packed: GLboolean, points: pointer) {.importc.} + proc glGetPathColorGenfvNV(color: GLenum, pname: GLenum, value: ptr GLfloat) {.importc.} + proc glTexCoord2f(s: GLfloat, t: GLfloat) {.importc.} + proc glSampleMaski(index: GLuint, mask: GLbitfield) {.importc.} + proc glReadBufferIndexedEXT(src: GLenum, index: GLint) {.importc.} + proc glCoverFillPathNV(path: GLuint, coverMode: GLenum) {.importc.} + proc glColorTableParameterfvSGI(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glDeleteVertexArraysAPPLE(n: GLsizei, arrays: ptr GLuint) {.importc.} + proc glGetVertexAttribIiv(index: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glWeightbvARB(size: GLint, weights: ptr GLbyte) {.importc.} + proc glGetNamedBufferPointervEXT(buffer: GLuint, pname: GLenum, params: ptr pointer) {.importc.} + proc glTexCoordPointer(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glColor4fv(v: ptr GLfloat) {.importc.} + proc glGetnUniformfvARB(program: GLuint, location: GLint, bufSize: GLsizei, params: ptr GLfloat) {.importc.} + proc glMaterialxOES(face: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glGetFixedv(pname: GLenum, params: ptr GLfixed) {.importc.} + proc glMaterialf(face: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glVideoCaptureStreamParameterfvNV(video_capture_slot: GLuint, stream: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetDebugMessageLogAMD(count: GLuint, bufsize: GLsizei, categories: ptr GLenum, severities: ptr GLuint, ids: ptr GLuint, lengths: ptr GLsizei, message: cstring): GLuint {.importc.} + proc glProgramUniform2uiv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glMatrixMultTransposedEXT(mode: GLenum, m: ptr GLdouble) {.importc.} + proc glIsPointInStrokePathNV(path: GLuint, x: GLfloat, y: GLfloat): GLboolean {.importc.} + proc glDisable(cap: GLenum) {.importc.} + proc glCompileShader(shader: GLuint) {.importc.} + proc glLoadTransposeMatrixd(m: ptr GLdouble) {.importc.} + proc glGetMultiTexParameterIuivEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLuint) {.importc.} + proc glGetHistogram(target: GLenum, reset: GLboolean, format: GLenum, `type`: GLenum, values: pointer) {.importc.} + proc glMultiTexCoord3fvARB(target: GLenum, v: ptr GLfloat) {.importc.} + proc glColor4xvOES(components: ptr GLfixed) {.importc.} + proc glIsBuffer(buffer: GLuint): GLboolean {.importc.} + proc glVertex2dv(v: ptr GLdouble) {.importc.} + proc glNamedProgramLocalParameterI4uivEXT(program: GLuint, target: GLenum, index: GLuint, params: ptr GLuint) {.importc.} + proc glPixelTexGenParameteriSGIS(pname: GLenum, param: GLint) {.importc.} + proc glBindVertexBuffers(first: GLuint, count: GLsizei, buffers: ptr GLuint, offsets: ptr GLintptr, strides: ptr GLsizei) {.importc.} + proc glUniform1ui64vNV(location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glColor4ub(red: GLubyte, green: GLubyte, blue: GLubyte, alpha: GLubyte) {.importc.} + proc glConvolutionParameterfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glReplacementCodeuiColor4fNormal3fVertex3fSUN(rc: GLuint, r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat, nx: GLfloat, ny: GLfloat, nz: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glVertexAttribI2ui(index: GLuint, x: GLuint, y: GLuint) {.importc.} + proc glDeleteNamesAMD(identifier: GLenum, num: GLuint, names: ptr GLuint) {.importc.} + proc glPixelTransferxOES(pname: GLenum, param: GLfixed) {.importc.} + proc glVertexAttrib4ivARB(index: GLuint, v: ptr GLint) {.importc.} + proc glLightModeli(pname: GLenum, param: GLint) {.importc.} + proc glGetHistogramEXT(target: GLenum, reset: GLboolean, format: GLenum, `type`: GLenum, values: pointer) {.importc.} + proc glWindowPos3svMESA(v: ptr GLshort) {.importc.} + proc glRasterPos3iv(v: ptr GLint) {.importc.} + proc glCopyTextureSubImage3DEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glTextureStorage3DMultisampleEXT(texture: GLuint, target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, fixedsamplelocations: GLboolean) {.importc.} + proc glIsNameAMD(identifier: GLenum, name: GLuint): GLboolean {.importc.} + proc glProgramUniformMatrix3fv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glGetProgramParameterfvNV(target: GLenum, index: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glTexStorage3D(target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei) {.importc.} + proc glMultiTexCoord2xOES(texture: GLenum, s: GLfixed, t: GLfixed) {.importc.} + proc glWindowPos2fARB(x: GLfloat, y: GLfloat) {.importc.} + proc glGetProgramResourceIndex(program: GLuint, programInterface: GLenum, name: cstring): GLuint {.importc.} + proc glProgramUniform2uivEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glMakeImageHandleNonResidentNV(handle: GLuint64) {.importc.} + proc glNamedProgramLocalParameter4fvEXT(program: GLuint, target: GLenum, index: GLuint, params: ptr GLfloat) {.importc.} + proc glInvalidateFramebuffer(target: GLenum, numAttachments: GLsizei, attachments: ptr GLenum) {.importc.} + proc glTexStorage3DMultisample(target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, fixedsamplelocations: GLboolean) {.importc.} + proc glMapVertexAttrib2fAPPLE(index: GLuint, size: GLuint, u1: GLfloat, u2: GLfloat, ustride: GLint, uorder: GLint, v1: GLfloat, v2: GLfloat, vstride: GLint, vorder: GLint, points: ptr GLfloat) {.importc.} + proc glCombinerParameterfNV(pname: GLenum, param: GLfloat) {.importc.} + proc glCopyMultiTexImage2DEXT(texunit: GLenum, target: GLenum, level: GLint, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, height: GLsizei, border: GLint) {.importc.} + proc glBindVertexShaderEXT(id: GLuint) {.importc.} + proc glPathGlyphsNV(firstPathName: GLuint, fontTarget: GLenum, fontName: pointer, fontStyle: GLbitfield, numGlyphs: GLsizei, `type`: GLenum, charcodes: pointer, handleMissingGlyphs: GLenum, pathParameterTemplate: GLuint, emScale: GLfloat) {.importc.} + proc glProgramLocalParametersI4uivNV(target: GLenum, index: GLuint, count: GLsizei, params: ptr GLuint) {.importc.} + proc glMultiTexCoord3hvNV(target: GLenum, v: ptr GLhalfNv) {.importc.} + proc glMultiTexCoordP2uiv(texture: GLenum, `type`: GLenum, coords: ptr GLuint) {.importc.} + proc glDisableVariantClientStateEXT(id: GLuint) {.importc.} + proc glGetTexLevelParameterxvOES(target: GLenum, level: GLint, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glRasterPos2sv(v: ptr GLshort) {.importc.} + proc glWeightPathsNV(resultPath: GLuint, numPaths: GLsizei, paths: ptr GLuint, weights: ptr GLfloat) {.importc.} + proc glDrawBuffersNV(n: GLsizei, bufs: ptr GLenum) {.importc.} + proc glBindBufferARB(target: GLenum, buffer: GLuint) {.importc.} + proc glVariantbvEXT(id: GLuint, `addr`: ptr GLbyte) {.importc.} + proc glColorP3uiv(`type`: GLenum, color: ptr GLuint) {.importc.} + proc glBlendEquationEXT(mode: GLenum) {.importc.} + proc glProgramLocalParameterI4uivNV(target: GLenum, index: GLuint, params: ptr GLuint) {.importc.} + proc glRenderMode(mode: GLenum): GLint {.importc.} + proc glVertexStream4fATI(stream: GLenum, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glGetObjectLabelEXT(`type`: GLenum, `object`: GLuint, bufSize: GLsizei, length: ptr GLsizei, label: cstring) {.importc.} + proc glNamedFramebufferTexture3DEXT(framebuffer: GLuint, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint, zoffset: GLint) {.importc.} + proc glLoadMatrixf(m: ptr GLfloat) {.importc.} + proc glGetQueryObjectuivEXT(id: GLuint, pname: GLenum, params: ptr GLuint) {.importc.} + proc glBindVideoCaptureStreamBufferNV(video_capture_slot: GLuint, stream: GLuint, frame_region: GLenum, offset: GLintPtrArb) {.importc.} + proc glMatrixOrthoEXT(mode: GLenum, left: GLdouble, right: GLdouble, bottom: GLdouble, top: GLdouble, zNear: GLdouble, zFar: GLdouble) {.importc.} + proc glBlendFunc(sfactor: GLenum, dfactor: GLenum) {.importc.} + proc glTexGenxvOES(coord: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glMatrixMode(mode: GLenum) {.importc.} + proc glColorTableParameterivSGI(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetProgramInfoLog(program: GLuint, bufSize: GLsizei, length: ptr GLsizei, infoLog: cstring) {.importc.} + proc glGetSeparableFilter(target: GLenum, format: GLenum, `type`: GLenum, row: pointer, column: pointer, span: pointer) {.importc.} + proc glFogfv(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glDrawTexfvOES(coords: ptr GLfloat) {.importc.} + proc glClipPlanexIMG(p: GLenum, eqn: ptr GLfixed) {.importc.} + proc glResetHistogramEXT(target: GLenum) {.importc.} + proc glMemoryBarrier(barriers: GLbitfield) {.importc.} + proc glGetPixelMapusv(map: GLenum, values: ptr GLushort) {.importc.} + proc glEvalCoord2f(u: GLfloat, v: GLfloat) {.importc.} + proc glUniform4uiv(location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glIsProgramARB(program: GLuint): GLboolean {.importc.} + proc glPointParameterfv(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glTexBuffer(target: GLenum, internalformat: GLenum, buffer: GLuint) {.importc.} + proc glVertexAttrib1s(index: GLuint, x: GLshort) {.importc.} + proc glRenderbufferStorageMultisampleEXT(target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glMapNamedBufferEXT(buffer: GLuint, access: GLenum): pointer {.importc.} + proc glDebugMessageCallbackAMD(callback: GLdebugProcAmd, userParam: ptr pointer) {.importc.} + proc glGetTexEnvfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertexAttribI3uivEXT(index: GLuint, v: ptr GLuint) {.importc.} + proc glMultiTexEnvfEXT(texunit: GLenum, target: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glGetUniformiv(program: GLuint, location: GLint, params: ptr GLint) {.importc.} + proc glProgramLocalParameters4fvEXT(target: GLenum, index: GLuint, count: GLsizei, params: ptr GLfloat) {.importc.} + proc glStencilStrokePathInstancedNV(numPaths: GLsizei, pathNameType: GLenum, paths: pointer, pathBase: GLuint, reference: GLint, mask: GLuint, transformType: GLenum, transformValues: ptr GLfloat) {.importc.} + proc glBeginConditionalRender(id: GLuint, mode: GLenum) {.importc.} + proc glVertexAttribI3uiEXT(index: GLuint, x: GLuint, y: GLuint, z: GLuint) {.importc.} + proc glVDPAUMapSurfacesNV(numSurfaces: GLsizei, surfaces: ptr GLvdpauSurfaceNv) {.importc.} + proc glGetProgramResourceName(program: GLuint, programInterface: GLenum, index: GLuint, bufSize: GLsizei, length: ptr GLsizei, name: cstring) {.importc.} + proc glMultiTexCoord4f(target: GLenum, s: GLfloat, t: GLfloat, r: GLfloat, q: GLfloat) {.importc.} + proc glVertexAttrib2hNV(index: GLuint, x: GLhalfNv, y: GLhalfNv) {.importc.} + proc glDrawArraysInstancedNV(mode: GLenum, first: GLint, count: GLsizei, primcount: GLsizei) {.importc.} + proc glClearAccum(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat) {.importc.} + proc glVertexAttribI4usv(index: GLuint, v: ptr GLushort) {.importc.} + proc glGetProgramNamedParameterfvNV(id: GLuint, len: GLsizei, name: ptr GLubyte, params: ptr GLfloat) {.importc.} + proc glTextureLightEXT(pname: GLenum) {.importc.} + proc glPathSubCoordsNV(path: GLuint, coordStart: GLsizei, numCoords: GLsizei, coordType: GLenum, coords: pointer) {.importc.} + proc glBindImageTexture(unit: GLuint, texture: GLuint, level: GLint, layered: GLboolean, layer: GLint, access: GLenum, format: GLenum) {.importc.} + proc glGenVertexArraysAPPLE(n: GLsizei, arrays: ptr GLuint) {.importc.} + proc glFogCoordf(coord: GLfloat) {.importc.} + proc glFrameTerminatorGREMEDY() {.importc.} + proc glValidateProgramPipelineEXT(pipeline: GLuint) {.importc.} + proc glScalexOES(x: GLfixed, y: GLfixed, z: GLfixed) {.importc.} + proc glReplacementCodeuiColor3fVertex3fvSUN(rc: ptr GLuint, c: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glProgramNamedParameter4dNV(id: GLuint, len: GLsizei, name: ptr GLubyte, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glMultiDrawElementsIndirectCountARB(mode: GLenum, `type`: GLenum, indirect: GLintptr, drawcount: GLintptr, maxdrawcount: GLsizei, stride: GLsizei) {.importc.} + proc glReferencePlaneSGIX(equation: ptr GLdouble) {.importc.} + proc glNormalStream3iATI(stream: GLenum, nx: GLint, ny: GLint, nz: GLint) {.importc.} + proc glGetColorTableParameterfvEXT(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetAttribLocation(program: GLuint, name: cstring): GLint {.importc.} + proc glMultiTexParameterfEXT(texunit: GLenum, target: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glGenFencesNV(n: GLsizei, fences: ptr GLuint) {.importc.} + proc glUniform4dv(location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glGetTexLevelParameterfv(target: GLenum, level: GLint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glProgramUniform1ivEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glProgramUniform1dvEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glLoadTransposeMatrixdARB(m: ptr GLdouble) {.importc.} + proc glVertexAttrib2fvARB(index: GLuint, v: ptr GLfloat) {.importc.} + proc glMultiTexGendEXT(texunit: GLenum, coord: GLenum, pname: GLenum, param: GLdouble) {.importc.} + proc glProgramUniformMatrix4x3dvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glUniform4ui(location: GLint, v0: GLuint, v1: GLuint, v2: GLuint, v3: GLuint) {.importc.} + proc glTexSubImage2D(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glVertexAttrib3hNV(index: GLuint, x: GLhalfNv, y: GLhalfNv, z: GLhalfNv) {.importc.} + proc glRotatexOES(angle: GLfixed, x: GLfixed, y: GLfixed, z: GLfixed) {.importc.} + proc glGenTextures(n: GLsizei, textures: ptr GLuint) {.importc.} + proc glCheckFramebufferStatusOES(target: GLenum): GLenum {.importc.} + proc glGetVideoCaptureStreamdvNV(video_capture_slot: GLuint, stream: GLuint, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glCompressedTextureSubImage1DEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, width: GLsizei, format: GLenum, imageSize: GLsizei, bits: pointer) {.importc.} + proc glCurrentPaletteMatrixOES(matrixpaletteindex: GLuint) {.importc.} + proc glCompressedMultiTexSubImage1DEXT(texunit: GLenum, target: GLenum, level: GLint, xoffset: GLint, width: GLsizei, format: GLenum, imageSize: GLsizei, bits: pointer) {.importc.} + proc glNormal3d(nx: GLdouble, ny: GLdouble, nz: GLdouble) {.importc.} + proc glMultiTexCoord1fv(target: GLenum, v: ptr GLfloat) {.importc.} + proc glProgramUniform2uiEXT(program: GLuint, location: GLint, v0: GLuint, v1: GLuint) {.importc.} + proc glMultiTexCoord3fARB(target: GLenum, s: GLfloat, t: GLfloat, r: GLfloat) {.importc.} + proc glRasterPos3xOES(x: GLfixed, y: GLfixed, z: GLfixed) {.importc.} + proc glEGLImageTargetRenderbufferStorageOES(target: GLenum, image: GLeglImageOes) {.importc.} + proc glGetAttribLocationARB(programObj: GLhandleArb, name: cstring): GLint {.importc.} + proc glProgramNamedParameter4dvNV(id: GLuint, len: GLsizei, name: ptr GLubyte, v: ptr GLdouble) {.importc.} + proc glProgramLocalParameterI4uiNV(target: GLenum, index: GLuint, x: GLuint, y: GLuint, z: GLuint, w: GLuint) {.importc.} + proc glNamedFramebufferTextureFaceEXT(framebuffer: GLuint, attachment: GLenum, texture: GLuint, level: GLint, face: GLenum) {.importc.} + proc glIndexf(c: GLfloat) {.importc.} + proc glExtTexObjectStateOverrideiQCOM(target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glCoverageOperationNV(operation: GLenum) {.importc.} + proc glColorP4uiv(`type`: GLenum, color: ptr GLuint) {.importc.} + proc glDeleteSync(sync: GLsync) {.importc.} + proc glGetHistogramParameterfvEXT(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glTexCoord4fColor4fNormal3fVertex4fSUN(s: GLfloat, t: GLfloat, p: GLfloat, q: GLfloat, r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat, nx: GLfloat, ny: GLfloat, nz: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glEndPerfMonitorAMD(monitor: GLuint) {.importc.} + proc glGetInternalformati64v(target: GLenum, internalformat: GLenum, pname: GLenum, bufSize: GLsizei, params: ptr GLint64) {.importc.} + proc glGenNamesAMD(identifier: GLenum, num: GLuint, names: ptr GLuint) {.importc.} + proc glDrawElementsInstancedBaseVertexBaseInstance(mode: GLenum, count: GLsizei, `type`: GLenum, indices: ptr pointer, instancecount: GLsizei, basevertex: GLint, baseinstance: GLuint) {.importc.} + proc glMultiTexCoord4i(target: GLenum, s: GLint, t: GLint, r: GLint, q: GLint) {.importc.} + proc glVertexAttribL1dv(index: GLuint, v: ptr GLdouble) {.importc.} + proc glGetProgramNamedParameterdvNV(id: GLuint, len: GLsizei, name: ptr GLubyte, params: ptr GLdouble) {.importc.} + proc glSetLocalConstantEXT(id: GLuint, `type`: GLenum, `addr`: pointer) {.importc.} + proc glProgramBinary(program: GLuint, binaryFormat: GLenum, binary: pointer, length: GLsizei) {.importc.} + proc glVideoCaptureNV(video_capture_slot: GLuint, sequence_num: ptr GLuint, capture_time: ptr GLuint64Ext): GLenum {.importc.} + proc glDebugMessageEnableAMD(category: GLenum, severity: GLenum, count: GLsizei, ids: ptr GLuint, enabled: GLboolean) {.importc.} + proc glVertexAttribI1i(index: GLuint, x: GLint) {.importc.} + proc glVertexWeighthNV(weight: GLhalfNv) {.importc.} + proc glTextureParameterIivEXT(texture: GLuint, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glClipPlanefIMG(p: GLenum, eqn: ptr GLfloat) {.importc.} + proc glGetLightxv(light: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glGetAttachedObjectsARB(containerObj: GLhandleArb, maxCount: GLsizei, count: ptr GLsizei, obj: ptr GLhandleArb) {.importc.} + proc glVertexAttrib4fv(index: GLuint, v: ptr GLfloat) {.importc.} + proc glDisableVertexAttribArrayARB(index: GLuint) {.importc.} + proc glWindowPos3fvARB(v: ptr GLfloat) {.importc.} + proc glClearDepthdNV(depth: GLdouble) {.importc.} + proc glMapParameterivNV(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glEndConditionalRenderNVX() {.importc.} + proc glGetFragmentLightivSGIX(light: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glProgramUniformMatrix4fv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glVertexStream1iATI(stream: GLenum, x: GLint) {.importc.} + proc glColorP3ui(`type`: GLenum, color: GLuint) {.importc.} + proc glGetLightxOES(light: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glGetLightiv(light: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexStream3dATI(stream: GLenum, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glProgramUniform1iEXT(program: GLuint, location: GLint, v0: GLint) {.importc.} + proc glSecondaryColorFormatNV(size: GLint, `type`: GLenum, stride: GLsizei) {.importc.} + proc glDrawElementsBaseVertex(mode: GLenum, count: GLsizei, `type`: GLenum, indices: pointer, basevertex: GLint) {.importc.} + proc glGenFencesAPPLE(n: GLsizei, fences: ptr GLuint) {.importc.} + proc glBinormal3svEXT(v: ptr GLshort) {.importc.} + proc glUseProgramStagesEXT(pipeline: GLuint, stages: GLbitfield, program: GLuint) {.importc.} + proc glDebugMessageCallbackKHR(callback: GLdebugProcKhr, userParam: ptr pointer) {.importc.} + proc glCopyMultiTexSubImage3DEXT(texunit: GLenum, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glColor4hvNV(v: ptr GLhalfNv) {.importc.} + proc glFenceSync(condition: GLenum, flags: GLbitfield): GLsync {.importc.} + proc glTexCoordPointerListIBM(size: GLint, `type`: GLenum, stride: GLint, `pointer`: ptr pointer, ptrstride: GLint) {.importc.} + proc glPopName() {.importc.} + proc glColor3fVertex3fvSUN(c: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glGetUniformfv(program: GLuint, location: GLint, params: ptr GLfloat) {.importc.} + proc glMultiTexCoord2hNV(target: GLenum, s: GLhalfNv, t: GLhalfNv) {.importc.} + proc glLightxv(light: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glVideoCaptureStreamParameterivNV(video_capture_slot: GLuint, stream: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glEvalCoord1xvOES(coords: ptr GLfixed) {.importc.} + proc glGetProgramEnvParameterIivNV(target: GLenum, index: GLuint, params: ptr GLint) {.importc.} + proc glObjectPurgeableAPPLE(objectType: GLenum, name: GLuint, option: GLenum): GLenum {.importc.} + proc glRequestResidentProgramsNV(n: GLsizei, programs: ptr GLuint) {.importc.} + proc glIsImageHandleResidentNV(handle: GLuint64): GLboolean {.importc.} + proc glColor3hvNV(v: ptr GLhalfNv) {.importc.} + proc glMultiTexCoord2dARB(target: GLenum, s: GLdouble, t: GLdouble) {.importc.} + proc glDeletePathsNV(path: GLuint, range: GLsizei) {.importc.} + proc glVertexAttrib4Nsv(index: GLuint, v: ptr GLshort) {.importc.} + proc glTexEnvf(target: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glGlobalAlphaFactoriSUN(factor: GLint) {.importc.} + proc glBlendColorEXT(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat) {.importc.} + proc glSecondaryColor3usvEXT(v: ptr GLushort) {.importc.} + proc glProgramEnvParameterI4uiNV(target: GLenum, index: GLuint, x: GLuint, y: GLuint, z: GLuint, w: GLuint) {.importc.} + proc glTexImage4DSGIS(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, size4d: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glMatrixPushEXT(mode: GLenum) {.importc.} + proc glGetPixelTexGenParameterivSGIS(pname: GLenum, params: ptr GLint) {.importc.} + proc glVariantuivEXT(id: GLuint, `addr`: ptr GLuint) {.importc.} + proc glTexParameterfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetSubroutineUniformLocation(program: GLuint, shadertype: GLenum, name: cstring): GLint {.importc.} + proc glProgramUniformMatrix3fvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glDrawBuffersATI(n: GLsizei, bufs: ptr GLenum) {.importc.} + proc glGetVertexAttribivNV(index: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glMultiTexCoord4bvOES(texture: GLenum, coords: ptr GLbyte) {.importc.} + proc glCompressedTexSubImage1DARB(target: GLenum, level: GLint, xoffset: GLint, width: GLsizei, format: GLenum, imageSize: GLsizei, data: pointer) {.importc.} + proc glClientActiveTexture(texture: GLenum) {.importc.} + proc glVertexAttrib2fARB(index: GLuint, x: GLfloat, y: GLfloat) {.importc.} + proc glProgramUniform2fvEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glGetBufferParameterui64vNV(target: GLenum, pname: GLenum, params: ptr GLuint64Ext) {.importc.} + proc glVertexStream3dvATI(stream: GLenum, coords: ptr GLdouble) {.importc.} + proc glReplacementCodeuiNormal3fVertex3fvSUN(rc: ptr GLuint, n: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glVertexAttrib4svNV(index: GLuint, v: ptr GLshort) {.importc.} + proc glClearBufferSubData(target: GLenum, internalformat: GLenum, offset: GLintptr, size: GLsizeiptr, format: GLenum, `type`: GLenum, data: ptr pointer) {.importc.} + proc glVertexStream2sATI(stream: GLenum, x: GLshort, y: GLshort) {.importc.} + proc glTextureImage2DEXT(texture: GLuint, target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, height: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glGetListParameterfvSGIX(list: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glUniform3uiv(location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glIsTexture(texture: GLuint): GLboolean {.importc.} + proc glObjectUnpurgeableAPPLE(objectType: GLenum, name: GLuint, option: GLenum): GLenum {.importc.} + proc glGetVertexAttribdv(index: GLuint, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glGetPointeri_vEXT(pname: GLenum, index: GLuint, params: ptr pointer) {.importc.} + proc glSampleCoveragex(value: GLclampx, invert: GLboolean) {.importc.} + proc glColor3f(red: GLfloat, green: GLfloat, blue: GLfloat) {.importc.} + proc glGetnMapivARB(target: GLenum, query: GLenum, bufSize: GLsizei, v: ptr GLint) {.importc.} + proc glMakeTextureHandleResidentARB(handle: GLuint64) {.importc.} + proc glSecondaryColorP3ui(`type`: GLenum, color: GLuint) {.importc.} + proc glMultiTexCoord4sARB(target: GLenum, s: GLshort, t: GLshort, r: GLshort, q: GLshort) {.importc.} + proc glUniform3i64NV(location: GLint, x: GLint64Ext, y: GLint64Ext, z: GLint64Ext) {.importc.} + proc glVDPAUGetSurfaceivNV(surface: GLvdpauSurfaceNv, pname: GLenum, bufSize: GLsizei, length: ptr GLsizei, values: ptr GLint) {.importc.} + proc glTexBufferEXT(target: GLenum, internalformat: GLenum, buffer: GLuint) {.importc.} + proc glVertexAttribI4ubvEXT(index: GLuint, v: ptr GLubyte) {.importc.} + proc glDeleteFramebuffersOES(n: GLsizei, framebuffers: ptr GLuint) {.importc.} + proc glColor3fVertex3fSUN(r: GLfloat, g: GLfloat, b: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glCombinerInputNV(stage: GLenum, portion: GLenum, variable: GLenum, input: GLenum, mapping: GLenum, componentUsage: GLenum) {.importc.} + proc glPolygonOffsetEXT(factor: GLfloat, bias: GLfloat) {.importc.} + proc glWindowPos4dMESA(x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glVertex3f(x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glTexCoord3f(s: GLfloat, t: GLfloat, r: GLfloat) {.importc.} + proc glMultiTexCoord1fARB(target: GLenum, s: GLfloat) {.importc.} + proc glVertexAttrib4f(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glGetFragDataLocationEXT(program: GLuint, name: cstring): GLint {.importc.} + proc glFlushMappedNamedBufferRangeEXT(buffer: GLuint, offset: GLintptr, length: GLsizeiptr) {.importc.} + proc glVertexAttrib1sARB(index: GLuint, x: GLshort) {.importc.} + proc glBitmapxOES(width: GLsizei, height: GLsizei, xorig: GLfixed, yorig: GLfixed, xmove: GLfixed, ymove: GLfixed, bitmap: ptr GLubyte) {.importc.} + proc glEnableVertexArrayAttribEXT(vaobj: GLuint, index: GLuint) {.importc.} + proc glDeleteRenderbuffers(n: GLsizei, renderbuffers: ptr GLuint) {.importc.} + proc glFramebufferRenderbuffer(target: GLenum, attachment: GLenum, renderbuffertarget: GLenum, renderbuffer: GLuint) {.importc.} + proc glInvalidateTexImage(texture: GLuint, level: GLint) {.importc.} + proc glProgramUniform2i64NV(program: GLuint, location: GLint, x: GLint64Ext, y: GLint64Ext) {.importc.} + proc glTextureImage3DMultisampleNV(texture: GLuint, target: GLenum, samples: GLsizei, internalFormat: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, fixedSampleLocations: GLboolean) {.importc.} + proc glValidateProgram(program: GLuint) {.importc.} + proc glUniform1dv(location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glNormalStream3dvATI(stream: GLenum, coords: ptr GLdouble) {.importc.} + proc glMultiDrawElementsIndirect(mode: GLenum, `type`: GLenum, indirect: ptr pointer, drawcount: GLsizei, stride: GLsizei) {.importc.} + proc glVertexBlendARB(count: GLint) {.importc.} + proc glIsSampler(sampler: GLuint): GLboolean {.importc.} + proc glVariantdvEXT(id: GLuint, `addr`: ptr GLdouble) {.importc.} + proc glProgramUniformMatrix3x2fvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glVertexStream4fvATI(stream: GLenum, coords: ptr GLfloat) {.importc.} + proc glOrthoxOES(l: GLfixed, r: GLfixed, b: GLfixed, t: GLfixed, n: GLfixed, f: GLfixed) {.importc.} + proc glColorFormatNV(size: GLint, `type`: GLenum, stride: GLsizei) {.importc.} + proc glFogCoordPointer(`type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glVertexAttrib3dvARB(index: GLuint, v: ptr GLdouble) {.importc.} + proc glVertex3bOES(x: GLbyte, y: GLbyte) {.importc.} + proc glVertexAttribFormat(attribindex: GLuint, size: GLint, `type`: GLenum, normalized: GLboolean, relativeoffset: GLuint) {.importc.} + proc glTexCoord4fVertex4fSUN(s: GLfloat, t: GLfloat, p: GLfloat, q: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glEnableDriverControlQCOM(driverControl: GLuint) {.importc.} + proc glPointParameteri(pname: GLenum, param: GLint) {.importc.} + proc glVertexAttribI2i(index: GLuint, x: GLint, y: GLint) {.importc.} + proc glGetDriverControlStringQCOM(driverControl: GLuint, bufSize: GLsizei, length: ptr GLsizei, driverControlString: cstring) {.importc.} + proc glGetTexLevelParameteriv(target: GLenum, level: GLint, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetHandleARB(pname: GLenum): GLhandleArb {.importc.} + proc glIndexubv(c: ptr GLubyte) {.importc.} + proc glBlendFunciARB(buf: GLuint, src: GLenum, dst: GLenum) {.importc.} + proc glColor4usv(v: ptr GLushort) {.importc.} + proc glBlendEquationSeparateOES(modeRgb: GLenum, modeAlpha: GLenum) {.importc.} + proc glVertexAttribI4ui(index: GLuint, x: GLuint, y: GLuint, z: GLuint, w: GLuint) {.importc.} + proc glProgramUniform3f(program: GLuint, location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat) {.importc.} + proc glVertexAttribL3i64vNV(index: GLuint, v: ptr GLint64Ext) {.importc.} + proc glWeightdvARB(size: GLint, weights: ptr GLdouble) {.importc.} + proc glVertexArrayRangeAPPLE(length: GLsizei, `pointer`: pointer) {.importc.} + proc glMapGrid2d(un: GLint, u1: GLdouble, u2: GLdouble, vn: GLint, v1: GLdouble, v2: GLdouble) {.importc.} + proc glFogiv(pname: GLenum, params: ptr GLint) {.importc.} + proc glUniform2f(location: GLint, v0: GLfloat, v1: GLfloat) {.importc.} + proc glGetDoublei_v(target: GLenum, index: GLuint, data: ptr GLdouble) {.importc.} + proc glGetVertexAttribfv(index: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertexAttribI2ivEXT(index: GLuint, v: ptr GLint) {.importc.} + proc glIsProgramNV(id: GLuint): GLboolean {.importc.} + proc glTexCoord1hNV(s: GLhalfNv) {.importc.} + proc glMinSampleShadingARB(value: GLfloat) {.importc.} + proc glMultiDrawElements(mode: GLenum, count: ptr GLsizei, `type`: GLenum, indices: ptr pointer, drawcount: GLsizei) {.importc.} + proc glGetQueryObjectuiv(id: GLuint, pname: GLenum, params: ptr GLuint) {.importc.} + proc glReadBuffer(mode: GLenum) {.importc.} + proc glMultiTexCoordP3uiv(texture: GLenum, `type`: GLenum, coords: ptr GLuint) {.importc.} + proc glUniformMatrix3x2fv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glBindRenderbuffer(target: GLenum, renderbuffer: GLuint) {.importc.} + proc glBinormal3sEXT(bx: GLshort, by: GLshort, bz: GLshort) {.importc.} + proc glUniform4iARB(location: GLint, v0: GLint, v1: GLint, v2: GLint, v3: GLint) {.importc.} + proc glGetUniformOffsetEXT(program: GLuint, location: GLint): GLintptr {.importc.} + proc glDeleteLists(list: GLuint, range: GLsizei) {.importc.} + proc glVertexAttribI1iEXT(index: GLuint, x: GLint) {.importc.} + proc glFramebufferTexture1D(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glVertexAttribI2uiv(index: GLuint, v: ptr GLuint) {.importc.} + proc glBindFragDataLocation(program: GLuint, color: GLuint, name: cstring) {.importc.} + proc glClearStencil(s: GLint) {.importc.} + proc glVertexAttrib4Nubv(index: GLuint, v: ptr GLubyte) {.importc.} + proc glConvolutionFilter2DEXT(target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, image: pointer) {.importc.} + proc glGenFramebuffersEXT(n: GLsizei, framebuffers: ptr GLuint) {.importc.} + proc glFogCoordfvEXT(coord: ptr GLfloat) {.importc.} + proc glGetRenderbufferParameterivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexAttribs1fvNV(index: GLuint, count: GLsizei, v: ptr GLfloat) {.importc.} + proc glTexCoord2fColor3fVertex3fSUN(s: GLfloat, t: GLfloat, r: GLfloat, g: GLfloat, b: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glRasterPos3i(x: GLint, y: GLint, z: GLint) {.importc.} + proc glMultiTexSubImage2DEXT(texunit: GLenum, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glConvolutionParameteriEXT(target: GLenum, pname: GLenum, params: GLint) {.importc.} + proc glVertexAttribI4iEXT(index: GLuint, x: GLint, y: GLint, z: GLint, w: GLint) {.importc.} + proc glVertexAttribL2i64vNV(index: GLuint, v: ptr GLint64Ext) {.importc.} + proc glBlendColor(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat) {.importc.} + proc glGetPathColorGenivNV(color: GLenum, pname: GLenum, value: ptr GLint) {.importc.} + proc glCompressedTextureImage1DEXT(texture: GLuint, target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, border: GLint, imageSize: GLsizei, bits: pointer) {.importc.} + proc glDrawElementsInstanced(mode: GLenum, count: GLsizei, `type`: GLenum, indices: pointer, instancecount: GLsizei) {.importc.} + proc glFogCoordd(coord: GLdouble) {.importc.} + proc glTexParameterxvOES(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glWindowPos3svARB(v: ptr GLshort) {.importc.} + proc glGetVertexArrayPointervEXT(vaobj: GLuint, pname: GLenum, param: ptr pointer) {.importc.} + proc glDrawTextureNV(texture: GLuint, sampler: GLuint, x0: GLfloat, y0: GLfloat, x1: GLfloat, y1: GLfloat, z: GLfloat, s0: GLfloat, t0: GLfloat, s1: GLfloat, t1: GLfloat) {.importc.} + proc glUniformMatrix2dv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glTexImage3DOES(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glClampColorARB(target: GLenum, clamp: GLenum) {.importc.} + proc glTexParameteri(target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glWindowPos4svMESA(v: ptr GLshort) {.importc.} + proc glMultiTexCoordP4ui(texture: GLenum, `type`: GLenum, coords: GLuint) {.importc.} + proc glVertexP4uiv(`type`: GLenum, value: ptr GLuint) {.importc.} + proc glProgramUniform4iEXT(program: GLuint, location: GLint, v0: GLint, v1: GLint, v2: GLint, v3: GLint) {.importc.} + proc glTexCoord3xvOES(coords: ptr GLfixed) {.importc.} + proc glCopyTexImage2DEXT(target: GLenum, level: GLint, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, height: GLsizei, border: GLint) {.importc.} + proc glGenSamplers(count: GLsizei, samplers: ptr GLuint) {.importc.} + proc glRasterPos4iv(v: ptr GLint) {.importc.} + proc glWindowPos4sMESA(x: GLshort, y: GLshort, z: GLshort, w: GLshort) {.importc.} + proc glProgramUniform2dvEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glPrioritizeTexturesEXT(n: GLsizei, textures: ptr GLuint, priorities: ptr GLclampf) {.importc.} + proc glRects(x1: GLshort, y1: GLshort, x2: GLshort, y2: GLshort) {.importc.} + proc glMultiDrawElementsBaseVertex(mode: GLenum, count: ptr GLsizei, `type`: GLenum, indices: ptr pointer, drawcount: GLsizei, basevertex: ptr GLint) {.importc.} + proc glProgramBinaryOES(program: GLuint, binaryFormat: GLenum, binary: pointer, length: GLint) {.importc.} + proc glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN(rc: ptr GLuint, tc: ptr GLfloat, c: ptr GLfloat, n: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glGetMinmaxParameterfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glColor4fNormal3fVertex3fSUN(r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat, nx: GLfloat, ny: GLfloat, nz: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glWindowPos2d(x: GLdouble, y: GLdouble) {.importc.} + proc glGetPerfMonitorGroupStringAMD(group: GLuint, bufSize: GLsizei, length: ptr GLsizei, groupString: cstring) {.importc.} + proc glUniformHandleui64vNV(location: GLint, count: GLsizei, value: ptr GLuint64) {.importc.} + proc glBlendEquation(mode: GLenum) {.importc.} + proc glMapBufferARB(target: GLenum, access: GLenum): pointer {.importc.} + proc glGetMaterialxvOES(face: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glVertexAttribI1ivEXT(index: GLuint, v: ptr GLint) {.importc.} + proc glTexCoord4hvNV(v: ptr GLhalfNv) {.importc.} + proc glVertexArrayVertexAttribLOffsetEXT(vaobj: GLuint, buffer: GLuint, index: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glExtGetShadersQCOM(shaders: ptr GLuint, maxShaders: GLint, numShaders: ptr GLint) {.importc.} + proc glWindowPos4ivMESA(v: ptr GLint) {.importc.} + proc glVertexAttrib1sNV(index: GLuint, x: GLshort) {.importc.} + proc glNormalStream3ivATI(stream: GLenum, coords: ptr GLint) {.importc.} + proc glSecondaryColor3fEXT(red: GLfloat, green: GLfloat, blue: GLfloat) {.importc.} + proc glVertexArrayFogCoordOffsetEXT(vaobj: GLuint, buffer: GLuint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glGetTextureImageEXT(texture: GLuint, target: GLenum, level: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glVertexAttrib4hNV(index: GLuint, x: GLhalfNv, y: GLhalfNv, z: GLhalfNv, w: GLhalfNv) {.importc.} + proc glReplacementCodeusSUN(code: GLushort) {.importc.} + proc glPixelTexGenSGIX(mode: GLenum) {.importc.} + proc glMultiDrawRangeElementArrayAPPLE(mode: GLenum, start: GLuint, `end`: GLuint, first: ptr GLint, count: ptr GLsizei, primcount: GLsizei) {.importc.} + proc glDrawElements(mode: GLenum, count: GLsizei, `type`: GLenum, indices: pointer) {.importc.} + proc glTexCoord1hvNV(v: ptr GLhalfNv) {.importc.} + proc glGetPixelMapuiv(map: GLenum, values: ptr GLuint) {.importc.} + proc glRasterPos4d(x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glTexImage1D(target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glConvolutionParameterxOES(target: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glSecondaryColor3dEXT(red: GLdouble, green: GLdouble, blue: GLdouble) {.importc.} + proc glGetCombinerOutputParameterivNV(stage: GLenum, portion: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glQueryCounter(id: GLuint, target: GLenum) {.importc.} + proc glGetUniformi64vNV(program: GLuint, location: GLint, params: ptr GLint64Ext) {.importc.} + proc glTexCoord2fv(v: ptr GLfloat) {.importc.} + proc glWindowPos3d(x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glBlendFuncSeparateINGR(sfactorRgb: GLenum, dfactorRgb: GLenum, sfactorAlpha: GLenum, dfactorAlpha: GLenum) {.importc.} + proc glTextureNormalEXT(mode: GLenum) {.importc.} + proc glVertexStream2fATI(stream: GLenum, x: GLfloat, y: GLfloat) {.importc.} + proc glViewportIndexedf(index: GLuint, x: GLfloat, y: GLfloat, w: GLfloat, h: GLfloat) {.importc.} + proc glMultiTexCoord4ivARB(target: GLenum, v: ptr GLint) {.importc.} + proc glBindBufferOffsetEXT(target: GLenum, index: GLuint, buffer: GLuint, offset: GLintptr) {.importc.} + proc glTexCoord3sv(v: ptr GLshort) {.importc.} + proc glVertexArrayVertexAttribBindingEXT(vaobj: GLuint, attribindex: GLuint, bindingindex: GLuint) {.importc.} + proc glVertexAttrib2f(index: GLuint, x: GLfloat, y: GLfloat) {.importc.} + proc glMultiTexGenivEXT(texunit: GLenum, coord: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glUniformui64vNV(location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glGetInfoLogARB(obj: GLhandleArb, maxLength: GLsizei, length: ptr GLsizei, infoLog: cstring) {.importc.} + proc glGetNamedProgramLocalParameterIivEXT(program: GLuint, target: GLenum, index: GLuint, params: ptr GLint) {.importc.} + proc glVertexAttrib4s(index: GLuint, x: GLshort, y: GLshort, z: GLshort, w: GLshort) {.importc.} + proc glUniformMatrix4x2dv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glVertexAttribs3dvNV(index: GLuint, count: GLsizei, v: ptr GLdouble) {.importc.} + proc glSecondaryColor3dvEXT(v: ptr GLdouble) {.importc.} + proc glTextureRenderbufferEXT(texture: GLuint, target: GLenum, renderbuffer: GLuint) {.importc.} + proc glVertexAttribL2ui64vNV(index: GLuint, v: ptr GLuint64Ext) {.importc.} + proc glBlendFuncSeparateOES(srcRgb: GLenum, dstRgb: GLenum, srcAlpha: GLenum, dstAlpha: GLenum) {.importc.} + proc glVertexAttribDivisorARB(index: GLuint, divisor: GLuint) {.importc.} + proc glWindowPos2sv(v: ptr GLshort) {.importc.} + proc glMultiTexCoord3svARB(target: GLenum, v: ptr GLshort) {.importc.} + proc glCombinerParameterfvNV(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetImageTransformParameterfvHP(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glTexParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetArrayObjectivATI(`array`: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetTexParameterIuiv(target: GLenum, pname: GLenum, params: ptr GLuint) {.importc.} + proc glGetProgramPipelineInfoLog(pipeline: GLuint, bufSize: GLsizei, length: ptr GLsizei, infoLog: cstring) {.importc.} + proc glGetOcclusionQueryuivNV(id: GLuint, pname: GLenum, params: ptr GLuint) {.importc.} + proc glVertexAttrib4bvARB(index: GLuint, v: ptr GLbyte) {.importc.} + proc glListParameterfvSGIX(list: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glDeleteSamplers(count: GLsizei, samplers: ptr GLuint) {.importc.} + proc glNormalStream3dATI(stream: GLenum, nx: GLdouble, ny: GLdouble, nz: GLdouble) {.importc.} + proc glProgramUniform4i64vNV(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint64Ext) {.importc.} + proc glBlendFuncSeparateiARB(buf: GLuint, srcRgb: GLenum, dstRgb: GLenum, srcAlpha: GLenum, dstAlpha: GLenum) {.importc.} + proc glEndTransformFeedbackEXT() {.importc.} + proc glMultiTexCoord3i(target: GLenum, s: GLint, t: GLint, r: GLint) {.importc.} + proc glMakeBufferResidentNV(target: GLenum, access: GLenum) {.importc.} + proc glTangent3dvEXT(v: ptr GLdouble) {.importc.} + proc glMatrixPopEXT(mode: GLenum) {.importc.} + proc glVertexAttrib4NivARB(index: GLuint, v: ptr GLint) {.importc.} + proc glProgramUniform2ui64NV(program: GLuint, location: GLint, x: GLuint64Ext, y: GLuint64Ext) {.importc.} + proc glWeightPointerARB(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glCullParameterdvEXT(pname: GLenum, params: ptr GLdouble) {.importc.} + proc glFramebufferTexture2D(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glGenVertexArrays(n: GLsizei, arrays: ptr GLuint) {.importc.} + proc glUniformHandleui64NV(location: GLint, value: GLuint64) {.importc.} + proc glIndexPointer(`type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glGetProgramSubroutineParameteruivNV(target: GLenum, index: GLuint, param: ptr GLuint) {.importc.} + proc glVertexAttrib1svARB(index: GLuint, v: ptr GLshort) {.importc.} + proc glDetachObjectARB(containerObj: GLhandleArb, attachedObj: GLhandleArb) {.importc.} + proc glCompressedTexImage3D(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, imageSize: GLsizei, data: pointer) {.importc.} + proc glBlendFuncSeparate(sfactorRgb: GLenum, dfactorRgb: GLenum, sfactorAlpha: GLenum, dfactorAlpha: GLenum) {.importc.} + proc glExecuteProgramNV(target: GLenum, id: GLuint, params: ptr GLfloat) {.importc.} + proc glAttachObjectARB(containerObj: GLhandleArb, obj: GLhandleArb) {.importc.} + proc glCompressedTexSubImage1D(target: GLenum, level: GLint, xoffset: GLint, width: GLsizei, format: GLenum, imageSize: GLsizei, data: pointer) {.importc.} + proc glProgramUniform4iv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glVertexAttrib3sv(index: GLuint, v: ptr GLshort) {.importc.} + proc glTexCoord3bvOES(coords: ptr GLbyte) {.importc.} + proc glGenTexturesEXT(n: GLsizei, textures: ptr GLuint) {.importc.} + proc glColor4f(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat) {.importc.} + proc glGetFramebufferAttachmentParameterivOES(target: GLenum, attachment: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glClearColor(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat) {.importc.} + proc glNamedProgramLocalParametersI4ivEXT(program: GLuint, target: GLenum, index: GLuint, count: GLsizei, params: ptr GLint) {.importc.} + proc glMakeImageHandleNonResidentARB(handle: GLuint64) {.importc.} + proc glGenRenderbuffers(n: GLsizei, renderbuffers: ptr GLuint) {.importc.} + proc glVertexAttribL1ui64vARB(index: GLuint, v: ptr GLuint64Ext) {.importc.} + proc glBindFramebufferEXT(target: GLenum, framebuffer: GLuint) {.importc.} + proc glProgramUniform2dEXT(program: GLuint, location: GLint, x: GLdouble, y: GLdouble) {.importc.} + proc glCompressedMultiTexImage2DEXT(texunit: GLenum, target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, border: GLint, imageSize: GLsizei, bits: pointer) {.importc.} + proc glDeleteSyncAPPLE(sync: GLsync) {.importc.} + proc glDebugMessageInsertAMD(category: GLenum, severity: GLenum, id: GLuint, length: GLsizei, buf: cstring) {.importc.} + proc glSecondaryColorPointerEXT(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glTextureImage2DMultisampleNV(texture: GLuint, target: GLenum, samples: GLsizei, internalFormat: GLint, width: GLsizei, height: GLsizei, fixedSampleLocations: GLboolean) {.importc.} + proc glBeginFragmentShaderATI() {.importc.} + proc glClearDepth(depth: GLdouble) {.importc.} + proc glBindTextures(first: GLuint, count: GLsizei, textures: ptr GLuint) {.importc.} + proc glEvalCoord1d(u: GLdouble) {.importc.} + proc glSecondaryColor3b(red: GLbyte, green: GLbyte, blue: GLbyte) {.importc.} + proc glExtGetTexSubImageQCOM(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, `type`: GLenum, texels: pointer) {.importc.} + proc glClearColorIiEXT(red: GLint, green: GLint, blue: GLint, alpha: GLint) {.importc.} + proc glVertex2xOES(x: GLfixed) {.importc.} + proc glVertexAttrib2s(index: GLuint, x: GLshort, y: GLshort) {.importc.} + proc glUniformHandleui64vARB(location: GLint, count: GLsizei, value: ptr GLuint64) {.importc.} + proc glAreTexturesResidentEXT(n: GLsizei, textures: ptr GLuint, residences: ptr GLboolean): GLboolean {.importc.} + proc glDrawElementsInstancedBaseInstance(mode: GLenum, count: GLsizei, `type`: GLenum, indices: ptr pointer, instancecount: GLsizei, baseinstance: GLuint) {.importc.} + proc glGetString(name: GLenum): ptr GLubyte {.importc.} + proc glDrawTransformFeedbackStream(mode: GLenum, id: GLuint, stream: GLuint) {.importc.} + proc glSecondaryColor3uiv(v: ptr GLuint) {.importc.} + proc glNamedFramebufferParameteriEXT(framebuffer: GLuint, pname: GLenum, param: GLint) {.importc.} + proc glVertexAttrib4hvNV(index: GLuint, v: ptr GLhalfNv) {.importc.} + proc glGetnUniformuivARB(program: GLuint, location: GLint, bufSize: GLsizei, params: ptr GLuint) {.importc.} + proc glProgramUniform4ui(program: GLuint, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint, v3: GLuint) {.importc.} + proc glPointParameterxvOES(pname: GLenum, params: ptr GLfixed) {.importc.} + proc glIsEnabledi(target: GLenum, index: GLuint): GLboolean {.importc.} + proc glColorPointerEXT(size: GLint, `type`: GLenum, stride: GLsizei, count: GLsizei, `pointer`: pointer) {.importc.} + proc glFragmentLightModelfvSGIX(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glRasterPos3f(x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glDeleteObjectARB(obj: GLhandleArb) {.importc.} + proc glSetFenceNV(fence: GLuint, condition: GLenum) {.importc.} + proc glTransformFeedbackAttribsNV(count: GLuint, attribs: ptr GLint, bufferMode: GLenum) {.importc.} + proc glProgramUniformMatrix2fvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glGetPointerv(pname: GLenum, params: ptr pointer) {.importc.} + proc glWindowPos2dvMESA(v: ptr GLdouble) {.importc.} + proc glTexImage2DMultisample(target: GLenum, samples: GLsizei, internalformat: GLint, width: GLsizei, height: GLsizei, fixedsamplelocations: GLboolean) {.importc.} + proc glGenFragmentShadersATI(range: GLuint): GLuint {.importc.} + proc glTexCoord4fv(v: ptr GLfloat) {.importc.} + proc glCompressedTexImage1D(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, border: GLint, imageSize: GLsizei, data: pointer) {.importc.} + proc glGetNamedBufferSubDataEXT(buffer: GLuint, offset: GLintptr, size: GLsizeiptr, data: pointer) {.importc.} + proc glFinish() {.importc.} + proc glDeleteVertexShaderEXT(id: GLuint) {.importc.} + proc glFinishObjectAPPLE(`object`: GLenum, name: GLint) {.importc.} + proc glGetActiveAttribARB(programObj: GLhandleArb, index: GLuint, maxLength: GLsizei, length: ptr GLsizei, size: ptr GLint, `type`: ptr GLenum, name: cstring) {.importc.} + proc glPointParameterx(pname: GLenum, param: GLfixed) {.importc.} + proc glProgramUniformui64vNV(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glSecondaryColor3ubv(v: ptr GLubyte) {.importc.} + proc glGetProgramLocalParameterIivNV(target: GLenum, index: GLuint, params: ptr GLint) {.importc.} + proc glDeleteProgramPipelinesEXT(n: GLsizei, pipelines: ptr GLuint) {.importc.} + proc glVertexAttrib4fNV(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glGetColorTableParameterfvSGI(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetFloati_v(target: GLenum, index: GLuint, data: ptr GLfloat) {.importc.} + proc glGenBuffers(n: GLsizei, buffers: ptr GLuint) {.importc.} + proc glNormal3b(nx: GLbyte, ny: GLbyte, nz: GLbyte) {.importc.} + proc glDrawArraysInstancedARB(mode: GLenum, first: GLint, count: GLsizei, primcount: GLsizei) {.importc.} + proc glTexStorage2DMultisample(target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei, fixedsamplelocations: GLboolean) {.importc.} + proc glGetVariantIntegervEXT(id: GLuint, value: GLenum, data: ptr GLint) {.importc.} + proc glColor3ubv(v: ptr GLubyte) {.importc.} + proc glVertexAttribP4uiv(index: GLuint, `type`: GLenum, normalized: GLboolean, value: ptr GLuint) {.importc.} + proc glProgramUniform2ivEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glVertexStream4dATI(stream: GLenum, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glVertexAttribL2ui64NV(index: GLuint, x: GLuint64Ext, y: GLuint64Ext) {.importc.} + proc glSecondaryColor3bEXT(red: GLbyte, green: GLbyte, blue: GLbyte) {.importc.} + proc glGetBufferPointervOES(target: GLenum, pname: GLenum, params: ptr pointer) {.importc.} + proc glGetMaterialfv(face: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertexStream3sATI(stream: GLenum, x: GLshort, y: GLshort, z: GLshort) {.importc.} + proc glUniform1i(location: GLint, v0: GLint) {.importc.} + proc glVertexAttribL2d(index: GLuint, x: GLdouble, y: GLdouble) {.importc.} + proc glTestObjectAPPLE(`object`: GLenum, name: GLuint): GLboolean {.importc.} + proc glGetTransformFeedbackVarying(program: GLuint, index: GLuint, bufSize: GLsizei, length: ptr GLsizei, size: ptr GLsizei, `type`: ptr GLenum, name: cstring) {.importc.} + proc glFramebufferRenderbufferOES(target: GLenum, attachment: GLenum, renderbuffertarget: GLenum, renderbuffer: GLuint) {.importc.} + proc glVertexStream3iATI(stream: GLenum, x: GLint, y: GLint, z: GLint) {.importc.} + proc glMakeTextureHandleNonResidentNV(handle: GLuint64) {.importc.} + proc glVertexAttrib4fvNV(index: GLuint, v: ptr GLfloat) {.importc.} + proc glArrayElement(i: GLint) {.importc.} + proc glClearBufferData(target: GLenum, internalformat: GLenum, format: GLenum, `type`: GLenum, data: ptr pointer) {.importc.} + proc glSecondaryColor3usEXT(red: GLushort, green: GLushort, blue: GLushort) {.importc.} + proc glRenderbufferStorageMultisample(target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glTexCoord2xvOES(coords: ptr GLfixed) {.importc.} + proc glWindowPos3f(x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glTangent3svEXT(v: ptr GLshort) {.importc.} + proc glPointParameterf(pname: GLenum, param: GLfloat) {.importc.} + proc glVertexAttribI4uivEXT(index: GLuint, v: ptr GLuint) {.importc.} + proc glColorTableParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glMatrixMultdEXT(mode: GLenum, m: ptr GLdouble) {.importc.} + proc glUseProgramStages(pipeline: GLuint, stages: GLbitfield, program: GLuint) {.importc.} + proc glVertexStream4sATI(stream: GLenum, x: GLshort, y: GLshort, z: GLshort, w: GLshort) {.importc.} + proc glDrawElementsInstancedNV(mode: GLenum, count: GLsizei, `type`: GLenum, indices: pointer, primcount: GLsizei) {.importc.} + proc glUniform3d(location: GLint, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glDebugMessageControlARB(source: GLenum, `type`: GLenum, severity: GLenum, count: GLsizei, ids: ptr GLuint, enabled: GLboolean) {.importc.} + proc glVertexAttribs3svNV(index: GLuint, count: GLsizei, v: ptr GLshort) {.importc.} + proc glElementPointerATI(`type`: GLenum, `pointer`: pointer) {.importc.} + proc glColor4fNormal3fVertex3fvSUN(c: ptr GLfloat, n: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glGetPerfMonitorCountersAMD(group: GLuint, numCounters: ptr GLint, maxActiveCounters: ptr GLint, counterSize: GLsizei, counters: ptr GLuint) {.importc.} + proc glDispatchCompute(num_groups_x: GLuint, num_groups_y: GLuint, num_groups_z: GLuint) {.importc.} + proc glVertexAttribDivisorNV(index: GLuint, divisor: GLuint) {.importc.} + proc glProgramUniform3uiEXT(program: GLuint, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint) {.importc.} + proc glRenderbufferStorageMultisampleNV(target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glBinormalPointerEXT(`type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glRectxvOES(v1: ptr GLfixed, v2: ptr GLfixed) {.importc.} + proc glGenVertexArraysOES(n: GLsizei, arrays: ptr GLuint) {.importc.} + proc glDebugMessageControlKHR(source: GLenum, `type`: GLenum, severity: GLenum, count: GLsizei, ids: ptr GLuint, enabled: GLboolean) {.importc.} + proc glProgramUniform1uiEXT(program: GLuint, location: GLint, v0: GLuint) {.importc.} + proc glPixelTransferi(pname: GLenum, param: GLint) {.importc.} + proc glIsPointInFillPathNV(path: GLuint, mask: GLuint, x: GLfloat, y: GLfloat): GLboolean {.importc.} + proc glVertexBindingDivisor(bindingindex: GLuint, divisor: GLuint) {.importc.} + proc glGetVertexAttribLui64vARB(index: GLuint, pname: GLenum, params: ptr GLuint64Ext) {.importc.} + proc glProgramUniformMatrix3dvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glDrawBuffer(mode: GLenum) {.importc.} + proc glMultiTexCoord1sARB(target: GLenum, s: GLshort) {.importc.} + proc glSeparableFilter2DEXT(target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, row: pointer, column: pointer) {.importc.} + proc glTangent3bvEXT(v: ptr GLbyte) {.importc.} + proc glTexParameterIuiv(target: GLenum, pname: GLenum, params: ptr GLuint) {.importc.} + proc glVertexAttribL4i64NV(index: GLuint, x: GLint64Ext, y: GLint64Ext, z: GLint64Ext, w: GLint64Ext) {.importc.} + proc glDebugMessageCallbackARB(callback: GLdebugProcArb, userParam: ptr pointer) {.importc.} + proc glMultiTexCoordP1uiv(texture: GLenum, `type`: GLenum, coords: ptr GLuint) {.importc.} + proc glLabelObjectEXT(`type`: GLenum, `object`: GLuint, length: GLsizei, label: cstring) {.importc.} + proc glGetnPolygonStippleARB(bufSize: GLsizei, pattern: ptr GLubyte) {.importc.} + proc glTexCoord3xOES(s: GLfixed, t: GLfixed, r: GLfixed) {.importc.} + proc glCopyPixels(x: GLint, y: GLint, width: GLsizei, height: GLsizei, `type`: GLenum) {.importc.} + proc glGetnUniformfvEXT(program: GLuint, location: GLint, bufSize: GLsizei, params: ptr GLfloat) {.importc.} + proc glColorMaski(index: GLuint, r: GLboolean, g: GLboolean, b: GLboolean, a: GLboolean) {.importc.} + proc glRasterPos2fv(v: ptr GLfloat) {.importc.} + proc glBindBuffersBase(target: GLenum, first: GLuint, count: GLsizei, buffers: ptr GLuint) {.importc.} + proc glSpriteParameterfvSGIX(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetSyncivAPPLE(sync: GLsync, pname: GLenum, bufSize: GLsizei, length: ptr GLsizei, values: ptr GLint) {.importc.} + proc glVertexAttribI3i(index: GLuint, x: GLint, y: GLint, z: GLint) {.importc.} + proc glPixelTransformParameteriEXT(target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glMultiDrawArraysEXT(mode: GLenum, first: ptr GLint, count: ptr GLsizei, primcount: GLsizei) {.importc.} + proc glGetTextureHandleNV(texture: GLuint): GLuint64 {.importc.} + proc glTexCoordP2ui(`type`: GLenum, coords: GLuint) {.importc.} + proc glDeleteQueries(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glGetVertexAttribArrayObjectivATI(index: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexArrayVertexBindingDivisorEXT(vaobj: GLuint, bindingindex: GLuint, divisor: GLuint) {.importc.} + proc glVertex3i(x: GLint, y: GLint, z: GLint) {.importc.} + proc glBlendEquationSeparatei(buf: GLuint, modeRgb: GLenum, modeAlpha: GLenum) {.importc.} + proc glGetMapAttribParameterivNV(target: GLenum, index: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetVideoCaptureivNV(video_capture_slot: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glFragmentMaterialfvSGIX(face: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glEGLImageTargetTexture2DOES(target: GLenum, image: GLeglImageOes) {.importc.} + proc glCopyImageSubDataNV(srcName: GLuint, srcTarget: GLenum, srcLevel: GLint, srcX: GLint, srcY: GLint, srcZ: GLint, dstName: GLuint, dstTarget: GLenum, dstLevel: GLint, dstX: GLint, dstY: GLint, dstZ: GLint, width: GLsizei, height: GLsizei, depth: GLsizei) {.importc.} + proc glUniform2i(location: GLint, v0: GLint, v1: GLint) {.importc.} + proc glVertexAttrib3fvNV(index: GLuint, v: ptr GLfloat) {.importc.} + proc glNamedBufferStorageEXT(buffer: GLuint, size: GLsizeiptr, data: ptr pointer, flags: GLbitfield) {.importc.} + proc glProgramEnvParameterI4uivNV(target: GLenum, index: GLuint, params: ptr GLuint) {.importc.} + proc glGetVertexAttribdvARB(index: GLuint, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glVertexAttribL3ui64vNV(index: GLuint, v: ptr GLuint64Ext) {.importc.} + proc glUniform4fvARB(location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glWeightsvARB(size: GLint, weights: ptr GLshort) {.importc.} + proc glMakeTextureHandleNonResidentARB(handle: GLuint64) {.importc.} + proc glEvalCoord1xOES(u: GLfixed) {.importc.} + proc glVertexAttrib2sv(index: GLuint, v: ptr GLshort) {.importc.} + proc glVertexAttrib4dvNV(index: GLuint, v: ptr GLdouble) {.importc.} + proc glProgramNamedParameter4fNV(id: GLuint, len: GLsizei, name: ptr GLubyte, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glCompileShaderARB(shaderObj: GLhandleArb) {.importc.} + proc glProgramEnvParameter4fvARB(target: GLenum, index: GLuint, params: ptr GLfloat) {.importc.} + proc glGetVertexAttribiv(index: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glEvalPoint1(i: GLint) {.importc.} + proc glEvalMapsNV(target: GLenum, mode: GLenum) {.importc.} + proc glGetTexGenxvOES(coord: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glBlendEquationSeparate(modeRgb: GLenum, modeAlpha: GLenum) {.importc.} + proc glGetColorTableParameterfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glQueryCounterEXT(id: GLuint, target: GLenum) {.importc.} + proc glExtGetProgramBinarySourceQCOM(program: GLuint, shadertype: GLenum, source: cstring, length: ptr GLint) {.importc.} + proc glGetConvolutionParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glIsProgramPipeline(pipeline: GLuint): GLboolean {.importc.} + proc glVertexWeightfvEXT(weight: ptr GLfloat) {.importc.} + proc glDisableDriverControlQCOM(driverControl: GLuint) {.importc.} + proc glVertexStream1fvATI(stream: GLenum, coords: ptr GLfloat) {.importc.} + proc glMakeTextureHandleResidentNV(handle: GLuint64) {.importc.} + proc glSamplerParameteriv(sampler: GLuint, pname: GLenum, param: ptr GLint) {.importc.} + proc glTexEnvxOES(target: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glEndOcclusionQueryNV() {.importc.} + proc glFlushMappedBufferRangeAPPLE(target: GLenum, offset: GLintptr, size: GLsizeiptr) {.importc.} + proc glVertex4iv(v: ptr GLint) {.importc.} + proc glVertexArrayVertexAttribIFormatEXT(vaobj: GLuint, attribindex: GLuint, size: GLint, `type`: GLenum, relativeoffset: GLuint) {.importc.} + proc glDisableIndexedEXT(target: GLenum, index: GLuint) {.importc.} + proc glVertexAttribL1dEXT(index: GLuint, x: GLdouble) {.importc.} + proc glBeginPerfMonitorAMD(monitor: GLuint) {.importc.} + proc glConvolutionFilter1DEXT(target: GLenum, internalformat: GLenum, width: GLsizei, format: GLenum, `type`: GLenum, image: pointer) {.importc.} + proc glPrimitiveRestartIndex(index: GLuint) {.importc.} + proc glWindowPos2dv(v: ptr GLdouble) {.importc.} + proc glBindFramebufferOES(target: GLenum, framebuffer: GLuint) {.importc.} + proc glTessellationModeAMD(mode: GLenum) {.importc.} + proc glIsVariantEnabledEXT(id: GLuint, cap: GLenum): GLboolean {.importc.} + proc glColor3iv(v: ptr GLint) {.importc.} + proc glFogCoordFormatNV(`type`: GLenum, stride: GLsizei) {.importc.} + proc glClearNamedBufferDataEXT(buffer: GLuint, internalformat: GLenum, format: GLenum, `type`: GLenum, data: ptr pointer) {.importc.} + proc glTextureRangeAPPLE(target: GLenum, length: GLsizei, `pointer`: pointer) {.importc.} + proc glTexCoord4bvOES(coords: ptr GLbyte) {.importc.} + proc glRotated(angle: GLdouble, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glAccum(op: GLenum, value: GLfloat) {.importc.} + proc glVertex3d(x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glGetPathMetricRangeNV(metricQueryMask: GLbitfield, firstPathName: GLuint, numPaths: GLsizei, stride: GLsizei, metrics: ptr GLfloat) {.importc.} + proc glUniform4d(location: GLint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glTextureSubImage2DEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glMultiTexCoord1iv(target: GLenum, v: ptr GLint) {.importc.} + proc glFogFuncSGIS(n: GLsizei, points: ptr GLfloat) {.importc.} + proc glGetMaterialxOES(face: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glGlobalAlphaFactorbSUN(factor: GLbyte) {.importc.} + proc glGetProgramLocalParameterdvARB(target: GLenum, index: GLuint, params: ptr GLdouble) {.importc.} + proc glDeleteProgramsARB(n: GLsizei, programs: ptr GLuint) {.importc.} + proc glVertexStream1sATI(stream: GLenum, x: GLshort) {.importc.} + proc glMatrixTranslatedEXT(mode: GLenum, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glTexSubImage1D(target: GLenum, level: GLint, xoffset: GLint, width: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glGetBufferSubData(target: GLenum, offset: GLintptr, size: GLsizeiptr, data: pointer) {.importc.} + proc glUniform4uiEXT(location: GLint, v0: GLuint, v1: GLuint, v2: GLuint, v3: GLuint) {.importc.} + proc glGetShaderiv(shader: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetQueryIndexediv(target: GLenum, index: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glDebugMessageInsert(source: GLenum, `type`: GLenum, id: GLuint, severity: GLenum, length: GLsizei, buf: cstring) {.importc.} + proc glVertexAttribs2dvNV(index: GLuint, count: GLsizei, v: ptr GLdouble) {.importc.} + proc glGetFixedvOES(pname: GLenum, params: ptr GLfixed) {.importc.} + proc glUniform2iv(location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glTextureView(texture: GLuint, target: GLenum, origtexture: GLuint, internalformat: GLenum, minlevel: GLuint, numlevels: GLuint, minlayer: GLuint, numlayers: GLuint) {.importc.} + proc glMultiTexCoord1xvOES(texture: GLenum, coords: ptr GLfixed) {.importc.} + proc glTexBufferRange(target: GLenum, internalformat: GLenum, buffer: GLuint, offset: GLintptr, size: GLsizeiptr) {.importc.} + proc glMultiTexCoordPointerEXT(texunit: GLenum, size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glBlendColorxOES(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed) {.importc.} + proc glReadPixels(x: GLint, y: GLint, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glWindowPos3dARB(x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glPixelTexGenParameterivSGIS(pname: GLenum, params: ptr GLint) {.importc.} + proc glSecondaryColor3svEXT(v: ptr GLshort) {.importc.} + proc glPopGroupMarkerEXT() {.importc.} + proc glImportSyncEXT(external_sync_type: GLenum, external_sync: GLintptr, flags: GLbitfield): GLsync {.importc.} + proc glVertexAttribLFormatNV(index: GLuint, size: GLint, `type`: GLenum, stride: GLsizei) {.importc.} + proc glVertexAttrib2sNV(index: GLuint, x: GLshort, y: GLshort) {.importc.} + proc glGetIntegeri_v(target: GLenum, index: GLuint, data: ptr GLint) {.importc.} + proc glProgramUniform3uiv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glGetActiveUniformBlockiv(program: GLuint, uniformBlockIndex: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glCreateShaderProgramv(`type`: GLenum, count: GLsizei, strings: cstringArray): GLuint {.importc.} + proc glUniform2fARB(location: GLint, v0: GLfloat, v1: GLfloat) {.importc.} + proc glVertexStream4ivATI(stream: GLenum, coords: ptr GLint) {.importc.} + proc glNormalP3uiv(`type`: GLenum, coords: ptr GLuint) {.importc.} + proc glVertexAttribLFormat(attribindex: GLuint, size: GLint, `type`: GLenum, relativeoffset: GLuint) {.importc.} + proc glTexCoord2bvOES(coords: ptr GLbyte) {.importc.} + proc glGetActiveUniformName(program: GLuint, uniformIndex: GLuint, bufSize: GLsizei, length: ptr GLsizei, uniformName: cstring) {.importc.} + proc glTexCoord2sv(v: ptr GLshort) {.importc.} + proc glVertexAttrib2dNV(index: GLuint, x: GLdouble, y: GLdouble) {.importc.} + proc glGetFogFuncSGIS(points: ptr GLfloat) {.importc.} + proc glSetFenceAPPLE(fence: GLuint) {.importc.} + proc glRasterPos2f(x: GLfloat, y: GLfloat) {.importc.} + proc glVertexWeightPointerEXT(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glEndList() {.importc.} + proc glVDPAUFiniNV() {.importc.} + proc glTbufferMask3DFX(mask: GLuint) {.importc.} + proc glVertexP4ui(`type`: GLenum, value: GLuint) {.importc.} + proc glTexEnviv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glColor4xOES(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed) {.importc.} + proc glBlendEquationi(buf: GLuint, mode: GLenum) {.importc.} + proc glLoadMatrixxOES(m: ptr GLfixed) {.importc.} + proc glFogxOES(pname: GLenum, param: GLfixed) {.importc.} + proc glTexCoord4dv(v: ptr GLdouble) {.importc.} + proc glFogCoordPointerListIBM(`type`: GLenum, stride: GLint, `pointer`: ptr pointer, ptrstride: GLint) {.importc.} + proc glGetPerfMonitorGroupsAMD(numGroups: ptr GLint, groupsSize: GLsizei, groups: ptr GLuint) {.importc.} + proc glVertex2hNV(x: GLhalfNv, y: GLhalfNv) {.importc.} + proc glDeleteFragmentShaderATI(id: GLuint) {.importc.} + proc glGetSamplerParameterIiv(sampler: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glUniform2fvARB(location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glFogf(pname: GLenum, param: GLfloat) {.importc.} + proc glMultiTexCoord1iARB(target: GLenum, s: GLint) {.importc.} + proc glGetActiveUniformARB(programObj: GLhandleArb, index: GLuint, maxLength: GLsizei, length: ptr GLsizei, size: ptr GLint, `type`: ptr GLenum, name: cstring) {.importc.} + proc glMapGrid1xOES(n: GLint, u1: GLfixed, u2: GLfixed) {.importc.} + proc glIndexsv(c: ptr GLshort) {.importc.} + proc glFragmentMaterialfSGIX(face: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glBindTextureEXT(target: GLenum, texture: GLuint) {.importc.} + proc glRectiv(v1: ptr GLint, v2: ptr GLint) {.importc.} + proc glTangent3dEXT(tx: GLdouble, ty: GLdouble, tz: GLdouble) {.importc.} + proc glProgramUniformMatrix3x4fvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glNormal3hNV(nx: GLhalfNv, ny: GLhalfNv, nz: GLhalfNv) {.importc.} + proc glPushClientAttribDefaultEXT(mask: GLbitfield) {.importc.} + proc glUnmapBufferARB(target: GLenum): GLboolean {.importc.} + proc glVertexAttribs1dvNV(index: GLuint, count: GLsizei, v: ptr GLdouble) {.importc.} + proc glUniformMatrix2x3dv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glUniform3f(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat) {.importc.} + proc glTexEnvxv(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glMapBufferOES(target: GLenum, access: GLenum): pointer {.importc.} + proc glBufferData(target: GLenum, size: GLsizeiptr, data: pointer, usage: GLenum) {.importc.} + proc glDrawElementsInstancedANGLE(mode: GLenum, count: GLsizei, `type`: GLenum, indices: ptr pointer, primcount: GLsizei) {.importc.} + proc glGetTextureHandleARB(texture: GLuint): GLuint64 {.importc.} + proc glNormal3f(nx: GLfloat, ny: GLfloat, nz: GLfloat) {.importc.} + proc glTexCoordP3uiv(`type`: GLenum, coords: ptr GLuint) {.importc.} + proc glTexParameterx(target: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glMapBufferRange(target: GLenum, offset: GLintptr, length: GLsizeiptr, access: GLbitfield): pointer {.importc.} + proc glTexCoord2fVertex3fSUN(s: GLfloat, t: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glVariantArrayObjectATI(id: GLuint, `type`: GLenum, stride: GLsizei, buffer: GLuint, offset: GLuint) {.importc.} + proc glGetnHistogramARB(target: GLenum, reset: GLboolean, format: GLenum, `type`: GLenum, bufSize: GLsizei, values: pointer) {.importc.} + proc glWindowPos3sv(v: ptr GLshort) {.importc.} + proc glGetVariantPointervEXT(id: GLuint, value: GLenum, data: ptr pointer) {.importc.} + proc glGetLightfv(light: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetnTexImageARB(target: GLenum, level: GLint, format: GLenum, `type`: GLenum, bufSize: GLsizei, img: pointer) {.importc.} + proc glGenRenderbuffersEXT(n: GLsizei, renderbuffers: ptr GLuint) {.importc.} + proc glMultiDrawArraysIndirectBindlessNV(mode: GLenum, indirect: pointer, drawCount: GLsizei, stride: GLsizei, vertexBufferCount: GLint) {.importc.} + proc glDisableClientStateIndexedEXT(`array`: GLenum, index: GLuint) {.importc.} + proc glMapGrid1f(un: GLint, u1: GLfloat, u2: GLfloat) {.importc.} + proc glTexStorage2D(target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glShaderStorageBlockBinding(program: GLuint, storageBlockIndex: GLuint, storageBlockBinding: GLuint) {.importc.} + proc glBlendBarrierNV() {.importc.} + proc glGetVideoui64vNV(video_slot: GLuint, pname: GLenum, params: ptr GLuint64Ext) {.importc.} + proc glUniform3ui64NV(location: GLint, x: GLuint64Ext, y: GLuint64Ext, z: GLuint64Ext) {.importc.} + proc glUniform4ivARB(location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glGetQueryObjectivARB(id: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glCompressedTexSubImage3DOES(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, imageSize: GLsizei, data: pointer) {.importc.} + proc glEnableIndexedEXT(target: GLenum, index: GLuint) {.importc.} + proc glNamedRenderbufferStorageMultisampleCoverageEXT(renderbuffer: GLuint, coverageSamples: GLsizei, colorSamples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glVertexAttribI3iEXT(index: GLuint, x: GLint, y: GLint, z: GLint) {.importc.} + proc glUniform4uivEXT(location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glGetUniformLocation(program: GLuint, name: cstring): GLint {.importc.} + proc glCurrentPaletteMatrixARB(index: GLint) {.importc.} + proc glVertexAttribLPointerEXT(index: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glFogCoorddvEXT(coord: ptr GLdouble) {.importc.} + proc glInitNames() {.importc.} + proc glGetPathSpacingNV(pathListMode: GLenum, numPaths: GLsizei, pathNameType: GLenum, paths: pointer, pathBase: GLuint, advanceScale: GLfloat, kerningScale: GLfloat, transformType: GLenum, returnedSpacing: ptr GLfloat) {.importc.} + proc glNormal3fVertex3fvSUN(n: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glTexCoord2iv(v: ptr GLint) {.importc.} + proc glWindowPos3s(x: GLshort, y: GLshort, z: GLshort) {.importc.} + proc glProgramUniformMatrix3x4fv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glVertexAttribP4ui(index: GLuint, `type`: GLenum, normalized: GLboolean, value: GLuint) {.importc.} + proc glVertexAttribs4ubvNV(index: GLuint, count: GLsizei, v: ptr GLubyte) {.importc.} + proc glProgramLocalParameterI4iNV(target: GLenum, index: GLuint, x: GLint, y: GLint, z: GLint, w: GLint) {.importc.} + proc glStencilMaskSeparate(face: GLenum, mask: GLuint) {.importc.} + proc glClientWaitSync(sync: GLsync, flags: GLbitfield, timeout: GLuint64): GLenum {.importc.} + proc glPolygonOffsetx(factor: GLfixed, units: GLfixed) {.importc.} + proc glCreateProgramObjectARB(): GLhandleArb {.importc.} + proc glClearColorIuiEXT(red: GLuint, green: GLuint, blue: GLuint, alpha: GLuint) {.importc.} + proc glDeleteTransformFeedbacksNV(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glFramebufferDrawBuffersEXT(framebuffer: GLuint, n: GLsizei, bufs: ptr GLenum) {.importc.} + proc glAreTexturesResident(n: GLsizei, textures: ptr GLuint, residences: ptr GLboolean): GLboolean {.importc.} + proc glNamedBufferDataEXT(buffer: GLuint, size: GLsizeiptr, data: pointer, usage: GLenum) {.importc.} + proc glGetInvariantFloatvEXT(id: GLuint, value: GLenum, data: ptr GLfloat) {.importc.} + proc glMultiTexCoord4d(target: GLenum, s: GLdouble, t: GLdouble, r: GLdouble, q: GLdouble) {.importc.} + proc glGetPixelTransformParameterfvEXT(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetStringi(name: GLenum, index: GLuint): ptr GLubyte {.importc.} + proc glMakeBufferNonResidentNV(target: GLenum) {.importc.} + proc glVertex4bOES(x: GLbyte, y: GLbyte, z: GLbyte) {.importc.} + proc glGetObjectLabel(identifier: GLenum, name: GLuint, bufSize: GLsizei, length: ptr GLsizei, label: cstring) {.importc.} + proc glClipPlanexOES(plane: GLenum, equation: ptr GLfixed) {.importc.} + proc glElementPointerAPPLE(`type`: GLenum, `pointer`: pointer) {.importc.} + proc glIsAsyncMarkerSGIX(marker: GLuint): GLboolean {.importc.} + proc glUseShaderProgramEXT(`type`: GLenum, program: GLuint) {.importc.} + proc glReplacementCodeuiColor4ubVertex3fSUN(rc: GLuint, r: GLubyte, g: GLubyte, b: GLubyte, a: GLubyte, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glIsTransformFeedback(id: GLuint): GLboolean {.importc.} + proc glEdgeFlag(flag: GLboolean) {.importc.} + proc glGetTexGeniv(coord: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glBeginQueryEXT(target: GLenum, id: GLuint) {.importc.} + proc glUniform1uiEXT(location: GLint, v0: GLuint) {.importc.} + proc glProgramUniform3fvEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glGetVideoi64vNV(video_slot: GLuint, pname: GLenum, params: ptr GLint64Ext) {.importc.} + proc glProgramUniform3ui(program: GLuint, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint) {.importc.} + proc glSecondaryColor3uiEXT(red: GLuint, green: GLuint, blue: GLuint) {.importc.} + proc glPathStencilFuncNV(fun: GLenum, `ref`: GLint, mask: GLuint) {.importc.} + proc glVertexAttribP1ui(index: GLuint, `type`: GLenum, normalized: GLboolean, value: GLuint) {.importc.} + proc glStencilFillPathInstancedNV(numPaths: GLsizei, pathNameType: GLenum, paths: pointer, pathBase: GLuint, fillMode: GLenum, mask: GLuint, transformType: GLenum, transformValues: ptr GLfloat) {.importc.} + proc glFogCoordfEXT(coord: GLfloat) {.importc.} + proc glTextureParameterIuivEXT(texture: GLuint, target: GLenum, pname: GLenum, params: ptr GLuint) {.importc.} + proc glProgramUniform4dEXT(program: GLuint, location: GLint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glFramebufferTextureFaceARB(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint, face: GLenum) {.importc.} + proc glTexCoord3s(s: GLshort, t: GLshort, r: GLshort) {.importc.} + proc glGetFramebufferAttachmentParameteriv(target: GLenum, attachment: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glEndVideoCaptureNV(video_capture_slot: GLuint) {.importc.} + proc glProgramUniformMatrix2x4dv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glGetFloatIndexedvEXT(target: GLenum, index: GLuint, data: ptr GLfloat) {.importc.} + proc glTexCoord1xOES(s: GLfixed) {.importc.} + proc glTexCoord4f(s: GLfloat, t: GLfloat, r: GLfloat, q: GLfloat) {.importc.} + proc glShaderSource(shader: GLuint, count: GLsizei, string: cstringArray, length: ptr GLint) {.importc.} + proc glGetDetailTexFuncSGIS(target: GLenum, points: ptr GLfloat) {.importc.} + proc glResetHistogram(target: GLenum) {.importc.} + proc glVertexAttribP2ui(index: GLuint, `type`: GLenum, normalized: GLboolean, value: GLuint) {.importc.} + proc glDrawTransformFeedbackNV(mode: GLenum, id: GLuint) {.importc.} + proc glWindowPos2fMESA(x: GLfloat, y: GLfloat) {.importc.} + proc glObjectLabelKHR(identifier: GLenum, name: GLuint, length: GLsizei, label: cstring) {.importc.} + proc glMultiTexCoord2iARB(target: GLenum, s: GLint, t: GLint) {.importc.} + proc glVertexAttrib4usv(index: GLuint, v: ptr GLushort) {.importc.} + proc glGetGraphicsResetStatusARB(): GLenum {.importc.} + proc glProgramUniform3dEXT(program: GLuint, location: GLint, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glPathSubCommandsNV(path: GLuint, commandStart: GLsizei, commandsToDelete: GLsizei, numCommands: GLsizei, commands: ptr GLubyte, numCoords: GLsizei, coordType: GLenum, coords: pointer) {.importc.} + proc glEndTransformFeedbackNV() {.importc.} + proc glWindowPos2sMESA(x: GLshort, y: GLshort) {.importc.} + proc glTangent3sEXT(tx: GLshort, ty: GLshort, tz: GLshort) {.importc.} + proc glLineWidthx(width: GLfixed) {.importc.} + proc glGetUniformBufferSizeEXT(program: GLuint, location: GLint): GLint {.importc.} + proc glTexCoord2bOES(s: GLbyte, t: GLbyte) {.importc.} + proc glWindowPos3iMESA(x: GLint, y: GLint, z: GLint) {.importc.} + proc glTexGend(coord: GLenum, pname: GLenum, param: GLdouble) {.importc.} + proc glRenderbufferStorageMultisampleANGLE(target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glGetProgramiv(program: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glDrawTransformFeedbackStreamInstanced(mode: GLenum, id: GLuint, stream: GLuint, instancecount: GLsizei) {.importc.} + proc glMatrixTranslatefEXT(mode: GLenum, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glColor4iv(v: ptr GLint) {.importc.} + proc glSecondaryColor3ivEXT(v: ptr GLint) {.importc.} + proc glIsNamedStringARB(namelen: GLint, name: cstring): GLboolean {.importc.} + proc glVertexAttribL4dv(index: GLuint, v: ptr GLdouble) {.importc.} + proc glEndTransformFeedback() {.importc.} + proc glVertexStream3fvATI(stream: GLenum, coords: ptr GLfloat) {.importc.} + proc glProgramUniformMatrix4x2dv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glTextureBufferRangeEXT(texture: GLuint, target: GLenum, internalformat: GLenum, buffer: GLuint, offset: GLintptr, size: GLsizeiptr) {.importc.} + proc glTexCoord2fNormal3fVertex3fvSUN(tc: ptr GLfloat, n: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glProgramUniform2f(program: GLuint, location: GLint, v0: GLfloat, v1: GLfloat) {.importc.} + proc glMultiTexCoord2sv(target: GLenum, v: ptr GLshort) {.importc.} + proc glTexCoord3bOES(s: GLbyte, t: GLbyte, r: GLbyte) {.importc.} + proc glGenFramebuffersOES(n: GLsizei, framebuffers: ptr GLuint) {.importc.} + proc glMultiTexCoord3sv(target: GLenum, v: ptr GLshort) {.importc.} + proc glVertexAttrib4Nub(index: GLuint, x: GLubyte, y: GLubyte, z: GLubyte, w: GLubyte) {.importc.} + proc glColor3d(red: GLdouble, green: GLdouble, blue: GLdouble) {.importc.} + proc glGetActiveAttrib(program: GLuint, index: GLuint, bufSize: GLsizei, length: ptr GLsizei, size: ptr GLint, `type`: ptr GLenum, name: cstring) {.importc.} + proc glConvolutionParameterfEXT(target: GLenum, pname: GLenum, params: GLfloat) {.importc.} + proc glTexSubImage2DEXT(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glBinormal3fvEXT(v: ptr GLfloat) {.importc.} + proc glDebugMessageControl(source: GLenum, `type`: GLenum, severity: GLenum, count: GLsizei, ids: ptr GLuint, enabled: GLboolean) {.importc.} + proc glProgramUniform3uivEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glPNTrianglesiATI(pname: GLenum, param: GLint) {.importc.} + proc glGetPerfMonitorCounterInfoAMD(group: GLuint, counter: GLuint, pname: GLenum, data: pointer) {.importc.} + proc glVertexAttribL3ui64NV(index: GLuint, x: GLuint64Ext, y: GLuint64Ext, z: GLuint64Ext) {.importc.} + proc glIsRenderbufferOES(renderbuffer: GLuint): GLboolean {.importc.} + proc glColorSubTable(target: GLenum, start: GLsizei, count: GLsizei, format: GLenum, `type`: GLenum, data: pointer) {.importc.} + proc glCompressedMultiTexImage1DEXT(texunit: GLenum, target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, border: GLint, imageSize: GLsizei, bits: pointer) {.importc.} + proc glBindSampler(unit: GLuint, sampler: GLuint) {.importc.} + proc glVariantubvEXT(id: GLuint, `addr`: ptr GLubyte) {.importc.} + proc glDisablei(target: GLenum, index: GLuint) {.importc.} + proc glVertexAttribI2uiEXT(index: GLuint, x: GLuint, y: GLuint) {.importc.} + proc glDrawElementArrayATI(mode: GLenum, count: GLsizei) {.importc.} + proc glTagSampleBufferSGIX() {.importc.} + proc glVertexPointerEXT(size: GLint, `type`: GLenum, stride: GLsizei, count: GLsizei, `pointer`: pointer) {.importc.} + proc glFragmentLightiSGIX(light: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glLoadTransposeMatrixxOES(m: ptr GLfixed) {.importc.} + proc glProgramLocalParameter4fvARB(target: GLenum, index: GLuint, params: ptr GLfloat) {.importc.} + proc glGetVariantFloatvEXT(id: GLuint, value: GLenum, data: ptr GLfloat) {.importc.} + proc glProgramUniform4ui64vNV(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glFragmentLightfSGIX(light: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glIsVertexArrayAPPLE(`array`: GLuint): GLboolean {.importc.} + proc glTexCoord1bvOES(coords: ptr GLbyte) {.importc.} + proc glUniform4fv(location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glPixelDataRangeNV(target: GLenum, length: GLsizei, `pointer`: pointer) {.importc.} + proc glUniformMatrix4x2fv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glRectf(x1: GLfloat, y1: GLfloat, x2: GLfloat, y2: GLfloat) {.importc.} + proc glCoverageMaskNV(mask: GLboolean) {.importc.} + proc glPointParameterfvSGIS(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glProgramUniformMatrix4x2dvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glFragmentLightModelfSGIX(pname: GLenum, param: GLfloat) {.importc.} + proc glDisableVertexAttribAPPLE(index: GLuint, pname: GLenum) {.importc.} + proc glMultiTexCoord3dvARB(target: GLenum, v: ptr GLdouble) {.importc.} + proc glTexCoord4iv(v: ptr GLint) {.importc.} + proc glUniform1f(location: GLint, v0: GLfloat) {.importc.} + proc glVertexAttribParameteriAMD(index: GLuint, pname: GLenum, param: GLint) {.importc.} + proc glGetConvolutionParameterfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glRecti(x1: GLint, y1: GLint, x2: GLint, y2: GLint) {.importc.} + proc glTexEnvxvOES(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glGetRenderbufferParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glBlendFuncIndexedAMD(buf: GLuint, src: GLenum, dst: GLenum) {.importc.} + proc glProgramUniformMatrix3x2fv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glDrawArraysInstancedANGLE(mode: GLenum, first: GLint, count: GLsizei, primcount: GLsizei) {.importc.} + proc glTextureBarrierNV() {.importc.} + proc glDrawBuffersIndexedEXT(n: GLint, location: ptr GLenum, indices: ptr GLint) {.importc.} + proc glUniformMatrix4fvARB(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glInstrumentsBufferSGIX(size: GLsizei, buffer: ptr GLint) {.importc.} + proc glAlphaFuncQCOM(fun: GLenum, `ref`: GLclampf) {.importc.} + proc glUniformMatrix4fv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glGetMinmaxParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetInvariantBooleanvEXT(id: GLuint, value: GLenum, data: ptr GLboolean) {.importc.} + proc glVDPAUIsSurfaceNV(surface: GLvdpauSurfaceNv) {.importc.} + proc glGenProgramsARB(n: GLsizei, programs: ptr GLuint) {.importc.} + proc glDrawRangeElementArrayATI(mode: GLenum, start: GLuint, `end`: GLuint, count: GLsizei) {.importc.} + proc glFramebufferRenderbufferEXT(target: GLenum, attachment: GLenum, renderbuffertarget: GLenum, renderbuffer: GLuint) {.importc.} + proc glClearIndex(c: GLfloat) {.importc.} + proc glDepthRangeIndexed(index: GLuint, n: GLdouble, f: GLdouble) {.importc.} + proc glDrawTexivOES(coords: ptr GLint) {.importc.} + proc glTangent3iEXT(tx: GLint, ty: GLint, tz: GLint) {.importc.} + proc glStringMarkerGREMEDY(len: GLsizei, string: pointer) {.importc.} + proc glTexCoordP1ui(`type`: GLenum, coords: GLuint) {.importc.} + proc glOrthox(l: GLfixed, r: GLfixed, b: GLfixed, t: GLfixed, n: GLfixed, f: GLfixed) {.importc.} + proc glReplacementCodeuiVertex3fvSUN(rc: ptr GLuint, v: ptr GLfloat) {.importc.} + proc glMultiTexCoord1bvOES(texture: GLenum, coords: ptr GLbyte) {.importc.} + proc glDrawArraysInstancedBaseInstance(mode: GLenum, first: GLint, count: GLsizei, instancecount: GLsizei, baseinstance: GLuint) {.importc.} + proc glMultMatrixf(m: ptr GLfloat) {.importc.} + proc glProgramUniform4i(program: GLuint, location: GLint, v0: GLint, v1: GLint, v2: GLint, v3: GLint) {.importc.} + proc glScissorArrayv(first: GLuint, count: GLsizei, v: ptr GLint) {.importc.} + proc glGetnUniformivEXT(program: GLuint, location: GLint, bufSize: GLsizei, params: ptr GLint) {.importc.} + proc glGetTexEnvxvOES(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glWindowPos3ivARB(v: ptr GLint) {.importc.} + proc glProgramStringARB(target: GLenum, format: GLenum, len: GLsizei, string: pointer) {.importc.} + proc glTextureColorMaskSGIS(red: GLboolean, green: GLboolean, blue: GLboolean, alpha: GLboolean) {.importc.} + proc glMultiTexCoord4fv(target: GLenum, v: ptr GLfloat) {.importc.} + proc glUniformMatrix4x3fv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glIsPathNV(path: GLuint): GLboolean {.importc.} + proc glStartTilingQCOM(x: GLuint, y: GLuint, width: GLuint, height: GLuint, preserveMask: GLbitfield) {.importc.} + proc glVariantivEXT(id: GLuint, `addr`: ptr GLint) {.importc.} + proc glGetnMinmaxARB(target: GLenum, reset: GLboolean, format: GLenum, `type`: GLenum, bufSize: GLsizei, values: pointer) {.importc.} + proc glTransformFeedbackVaryings(program: GLuint, count: GLsizei, varyings: cstringArray, bufferMode: GLenum) {.importc.} + proc glShaderOp2EXT(op: GLenum, res: GLuint, arg1: GLuint, arg2: GLuint) {.importc.} + proc glVertexAttribPointer(index: GLuint, size: GLint, `type`: GLenum, normalized: GLboolean, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glMultiTexCoord4dvARB(target: GLenum, v: ptr GLdouble) {.importc.} + proc glProgramUniform1ui64NV(program: GLuint, location: GLint, x: GLuint64Ext) {.importc.} + proc glGetShaderSourceARB(obj: GLhandleArb, maxLength: GLsizei, length: ptr GLsizei, source: cstring) {.importc.} + proc glGetBufferSubDataARB(target: GLenum, offset: GLintPtrArb, size: GLsizeiptrArb, data: pointer) {.importc.} + proc glCopyTexSubImage2D(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glProgramEnvParameterI4iNV(target: GLenum, index: GLuint, x: GLint, y: GLint, z: GLint, w: GLint) {.importc.} + proc glGetVertexAttribivARB(index: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetFinalCombinerInputParameterivNV(variable: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glIndexFuncEXT(fun: GLenum, `ref`: GLclampf) {.importc.} + proc glProgramUniformMatrix3dv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glTexStorage1DEXT(target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei) {.importc.} + proc glUniformMatrix2fv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glConvolutionParameterf(target: GLenum, pname: GLenum, params: GLfloat) {.importc.} + proc glGlobalAlphaFactordSUN(factor: GLdouble) {.importc.} + proc glCopyTextureImage2DEXT(texture: GLuint, target: GLenum, level: GLint, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, height: GLsizei, border: GLint) {.importc.} + proc glVertex4xOES(x: GLfixed, y: GLfixed, z: GLfixed) {.importc.} + proc glClearDepthx(depth: GLfixed) {.importc.} + proc glGetColorTableParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGenProgramPipelines(n: GLsizei, pipelines: ptr GLuint) {.importc.} + proc glVertexAttribL4ui64vNV(index: GLuint, v: ptr GLuint64Ext) {.importc.} + proc glUniform1fARB(location: GLint, v0: GLfloat) {.importc.} + proc glUniformMatrix3fv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glUniform3dv(location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glVertexAttribI4iv(index: GLuint, v: ptr GLint) {.importc.} + proc glPixelZoom(xfactor: GLfloat, yfactor: GLfloat) {.importc.} + proc glShadeModel(mode: GLenum) {.importc.} + proc glFramebufferTexture3DOES(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint, zoffset: GLint) {.importc.} + proc glMultiTexCoord2i(target: GLenum, s: GLint, t: GLint) {.importc.} + proc glBlendEquationSeparateIndexedAMD(buf: GLuint, modeRgb: GLenum, modeAlpha: GLenum) {.importc.} + proc glIsEnabled(cap: GLenum): GLboolean {.importc.} + proc glTexImage2D(target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, height: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glPolygonOffsetxOES(factor: GLfixed, units: GLfixed) {.importc.} + proc glDrawBuffersEXT(n: GLsizei, bufs: ptr GLenum) {.importc.} + proc glPixelTexGenParameterfSGIS(pname: GLenum, param: GLfloat) {.importc.} + proc glExtGetRenderbuffersQCOM(renderbuffers: ptr GLuint, maxRenderbuffers: GLint, numRenderbuffers: ptr GLint) {.importc.} + proc glBindImageTextures(first: GLuint, count: GLsizei, textures: ptr GLuint) {.importc.} + proc glVertexAttribP2uiv(index: GLuint, `type`: GLenum, normalized: GLboolean, value: ptr GLuint) {.importc.} + proc glTextureImage3DMultisampleCoverageNV(texture: GLuint, target: GLenum, coverageSamples: GLsizei, colorSamples: GLsizei, internalFormat: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, fixedSampleLocations: GLboolean) {.importc.} + proc glRasterPos2s(x: GLshort, y: GLshort) {.importc.} + proc glVertexAttrib4dvARB(index: GLuint, v: ptr GLdouble) {.importc.} + proc glProgramUniformMatrix2x3fvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glProgramUniformMatrix2x4dvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glMultiTexCoord1d(target: GLenum, s: GLdouble) {.importc.} + proc glGetProgramParameterdvNV(target: GLenum, index: GLuint, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glPNTrianglesfATI(pname: GLenum, param: GLfloat) {.importc.} + proc glUniformMatrix3x4fv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glVertexAttrib3sNV(index: GLuint, x: GLshort, y: GLshort, z: GLshort) {.importc.} + proc glGetVideoCaptureStreamfvNV(video_capture_slot: GLuint, stream: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glCombinerParameterivNV(pname: GLenum, params: ptr GLint) {.importc.} + proc glGetTexGenfvOES(coord: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glCopyTexSubImage2DEXT(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glGetProgramLocalParameterfvARB(target: GLenum, index: GLuint, params: ptr GLfloat) {.importc.} + proc glTexCoord3iv(v: ptr GLint) {.importc.} + proc glVertexAttribs2hvNV(index: GLuint, n: GLsizei, v: ptr GLhalfNv) {.importc.} + proc glNormal3sv(v: ptr GLshort) {.importc.} + proc glUniform2dv(location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glSecondaryColor3hvNV(v: ptr GLhalfNv) {.importc.} + proc glDrawArraysInstancedEXT(mode: GLenum, start: GLint, count: GLsizei, primcount: GLsizei) {.importc.} + proc glBeginTransformFeedback(primitiveMode: GLenum) {.importc.} + proc glTexParameterIuivEXT(target: GLenum, pname: GLenum, params: ptr GLuint) {.importc.} + proc glProgramBufferParametersfvNV(target: GLenum, bindingIndex: GLuint, wordIndex: GLuint, count: GLsizei, params: ptr GLfloat) {.importc.} + proc glVertexArrayBindVertexBufferEXT(vaobj: GLuint, bindingindex: GLuint, buffer: GLuint, offset: GLintptr, stride: GLsizei) {.importc.} + proc glPathParameterfNV(path: GLuint, pname: GLenum, value: GLfloat) {.importc.} + proc glGetClipPlanexOES(plane: GLenum, equation: ptr GLfixed) {.importc.} + proc glSecondaryColor3ubvEXT(v: ptr GLubyte) {.importc.} + proc glGetPixelMapxv(map: GLenum, size: GLint, values: ptr GLfixed) {.importc.} + proc glVertexAttribI1uivEXT(index: GLuint, v: ptr GLuint) {.importc.} + proc glMultiTexImage3DEXT(texunit: GLenum, target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glAlphaFuncxOES(fun: GLenum, `ref`: GLfixed) {.importc.} + proc glMultiTexCoord2dv(target: GLenum, v: ptr GLdouble) {.importc.} + proc glBindRenderbufferOES(target: GLenum, renderbuffer: GLuint) {.importc.} + proc glPathStencilDepthOffsetNV(factor: GLfloat, units: GLfloat) {.importc.} + proc glPointParameterfvEXT(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glSampleCoverageARB(value: GLfloat, invert: GLboolean) {.importc.} + proc glVertexAttrib3dNV(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glNamedProgramLocalParameter4dvEXT(program: GLuint, target: GLenum, index: GLuint, params: ptr GLdouble) {.importc.} + proc glGenFramebuffers(n: GLsizei, framebuffers: ptr GLuint) {.importc.} + proc glMultiDrawElementsEXT(mode: GLenum, count: ptr GLsizei, `type`: GLenum, indices: ptr pointer, primcount: GLsizei) {.importc.} + proc glVertexAttrib2fNV(index: GLuint, x: GLfloat, y: GLfloat) {.importc.} + proc glProgramUniform4ivEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glTexGeniOES(coord: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glBindProgramPipeline(pipeline: GLuint) {.importc.} + proc glBindSamplers(first: GLuint, count: GLsizei, samplers: ptr GLuint) {.importc.} + proc glColorTableSGI(target: GLenum, internalformat: GLenum, width: GLsizei, format: GLenum, `type`: GLenum, table: pointer) {.importc.} + proc glMultiTexCoord3xOES(texture: GLenum, s: GLfixed, t: GLfixed, r: GLfixed) {.importc.} + proc glIsQueryEXT(id: GLuint): GLboolean {.importc.} + proc glGenBuffersARB(n: GLsizei, buffers: ptr GLuint) {.importc.} + proc glVertex4xvOES(coords: ptr GLfixed) {.importc.} + proc glPixelMapuiv(map: GLenum, mapsize: GLsizei, values: ptr GLuint) {.importc.} + proc glDrawTexfOES(x: GLfloat, y: GLfloat, z: GLfloat, width: GLfloat, height: GLfloat) {.importc.} + proc glPointParameterfEXT(pname: GLenum, param: GLfloat) {.importc.} + proc glPathDashArrayNV(path: GLuint, dashCount: GLsizei, dashArray: ptr GLfloat) {.importc.} + proc glClearTexImage(texture: GLuint, level: GLint, format: GLenum, `type`: GLenum, data: ptr pointer) {.importc.} + proc glIndexdv(c: ptr GLdouble) {.importc.} + proc glMultTransposeMatrixfARB(m: ptr GLfloat) {.importc.} + proc glVertexAttribL3d(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glUniform3fv(location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glGetProgramInterfaceiv(program: GLuint, programInterface: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glFogCoordfv(coord: ptr GLfloat) {.importc.} + proc glTexSubImage3DOES(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glGetPolygonStipple(mask: ptr GLubyte) {.importc.} + proc glGetQueryObjectivEXT(id: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glColor3xOES(red: GLfixed, green: GLfixed, blue: GLfixed) {.importc.} + proc glMultiTexParameterIivEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetMaterialiv(face: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertex2fv(v: ptr GLfloat) {.importc.} + proc glConvolutionParameterivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGenOcclusionQueriesNV(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glGetVertexAttribdvNV(index: GLuint, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glVertexAttribs4fvNV(index: GLuint, count: GLsizei, v: ptr GLfloat) {.importc.} + proc glVertexAttribL3dv(index: GLuint, v: ptr GLdouble) {.importc.} + proc glTexEnvi(target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glObjectPtrLabel(`ptr`: ptr pointer, length: GLsizei, label: cstring) {.importc.} + proc glGetTexGenfv(coord: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glMapVertexAttrib1dAPPLE(index: GLuint, size: GLuint, u1: GLdouble, u2: GLdouble, stride: GLint, order: GLint, points: ptr GLdouble) {.importc.} + proc glTexCoord3dv(v: ptr GLdouble) {.importc.} + proc glIsEnabledIndexedEXT(target: GLenum, index: GLuint): GLboolean {.importc.} + proc glGlobalAlphaFactoruiSUN(factor: GLuint) {.importc.} + proc glMatrixIndexPointerARB(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glUniformHandleui64ARB(location: GLint, value: GLuint64) {.importc.} + proc glUniform1fvARB(location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glGetActiveSubroutineUniformName(program: GLuint, shadertype: GLenum, index: GLuint, bufsize: GLsizei, length: ptr GLsizei, name: cstring) {.importc.} + proc glProgramUniformMatrix4x2fvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glMultiTexCoord4fARB(target: GLenum, s: GLfloat, t: GLfloat, r: GLfloat, q: GLfloat) {.importc.} + proc glGetDriverControlsQCOM(num: ptr GLint, size: GLsizei, driverControls: ptr GLuint) {.importc.} + proc glBindBufferRange(target: GLenum, index: GLuint, buffer: GLuint, offset: GLintptr, size: GLsizeiptr) {.importc.} + proc glMapGrid2f(un: GLint, u1: GLfloat, u2: GLfloat, vn: GLint, v1: GLfloat, v2: GLfloat) {.importc.} + proc glUniform2fv(location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glOrtho(left: GLdouble, right: GLdouble, bottom: GLdouble, top: GLdouble, zNear: GLdouble, zFar: GLdouble) {.importc.} + proc glGetImageHandleNV(texture: GLuint, level: GLint, layered: GLboolean, layer: GLint, format: GLenum): GLuint64 {.importc.} + proc glIsImageHandleResidentARB(handle: GLuint64): GLboolean {.importc.} + proc glGetConvolutionParameterivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glLineWidthxOES(width: GLfixed) {.importc.} + proc glPathCommandsNV(path: GLuint, numCommands: GLsizei, commands: ptr GLubyte, numCoords: GLsizei, coordType: GLenum, coords: pointer) {.importc.} + proc glMaterialxvOES(face: GLenum, pname: GLenum, param: ptr GLfixed) {.importc.} + proc glPauseTransformFeedbackNV() {.importc.} + proc glTexCoord4d(s: GLdouble, t: GLdouble, r: GLdouble, q: GLdouble) {.importc.} + proc glUniform3ui64vNV(location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glMultiTexCoord3dARB(target: GLenum, s: GLdouble, t: GLdouble, r: GLdouble) {.importc.} + proc glProgramUniform3fEXT(program: GLuint, location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat) {.importc.} + proc glTexImage3DMultisampleCoverageNV(target: GLenum, coverageSamples: GLsizei, colorSamples: GLsizei, internalFormat: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, fixedSampleLocations: GLboolean) {.importc.} + proc glNormalPointerEXT(`type`: GLenum, stride: GLsizei, count: GLsizei, `pointer`: pointer) {.importc.} + proc glPathColorGenNV(color: GLenum, genMode: GLenum, colorFormat: GLenum, coeffs: ptr GLfloat) {.importc.} + proc glGetMultiTexGendvEXT(texunit: GLenum, coord: GLenum, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glColor3i(red: GLint, green: GLint, blue: GLint) {.importc.} + proc glPointSizex(size: GLfixed) {.importc.} + proc glGetConvolutionFilterEXT(target: GLenum, format: GLenum, `type`: GLenum, image: pointer) {.importc.} + proc glBindBufferBaseNV(target: GLenum, index: GLuint, buffer: GLuint) {.importc.} + proc glInsertComponentEXT(res: GLuint, src: GLuint, num: GLuint) {.importc.} + proc glVertex2d(x: GLdouble, y: GLdouble) {.importc.} + proc glGetPathDashArrayNV(path: GLuint, dashArray: ptr GLfloat) {.importc.} + proc glVertexAttrib2sARB(index: GLuint, x: GLshort, y: GLshort) {.importc.} + proc glScissor(x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glLoadMatrixd(m: ptr GLdouble) {.importc.} + proc glVertex2bvOES(coords: ptr GLbyte) {.importc.} + proc glTexCoord2i(s: GLint, t: GLint) {.importc.} + proc glWriteMaskEXT(res: GLuint, `in`: GLuint, outX: GLenum, outY: GLenum, outZ: GLenum, outW: GLenum) {.importc.} + proc glClientWaitSyncAPPLE(sync: GLsync, flags: GLbitfield, timeout: GLuint64): GLenum {.importc.} + proc glGetObjectBufferivATI(buffer: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetNamedBufferParameterivEXT(buffer: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glTexCoord1bOES(s: GLbyte) {.importc.} + proc glVertexAttrib4dARB(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glUniform3fARB(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat) {.importc.} + proc glWindowPos2ivARB(v: ptr GLint) {.importc.} + proc glCreateShaderProgramvEXT(`type`: GLenum, count: GLsizei, strings: cstringArray): GLuint {.importc.} + proc glListParameterivSGIX(list: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetGraphicsResetStatusEXT(): GLenum {.importc.} + proc glActiveShaderProgramEXT(pipeline: GLuint, program: GLuint) {.importc.} + proc glTexCoordP1uiv(`type`: GLenum, coords: ptr GLuint) {.importc.} + proc glVideoCaptureStreamParameterdvNV(video_capture_slot: GLuint, stream: GLuint, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glGetVertexAttribPointerv(index: GLuint, pname: GLenum, `pointer`: ptr pointer) {.importc.} + proc glGetCompressedMultiTexImageEXT(texunit: GLenum, target: GLenum, lod: GLint, img: pointer) {.importc.} + proc glWindowPos4fMESA(x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glDrawElementsInstancedARB(mode: GLenum, count: GLsizei, `type`: GLenum, indices: pointer, primcount: GLsizei) {.importc.} + proc glVertexStream1dATI(stream: GLenum, x: GLdouble) {.importc.} + proc glMatrixMultfEXT(mode: GLenum, m: ptr GLfloat) {.importc.} + proc glGetPathParameterivNV(path: GLuint, pname: GLenum, value: ptr GLint) {.importc.} + proc glCombinerParameteriNV(pname: GLenum, param: GLint) {.importc.} + proc glUpdateObjectBufferATI(buffer: GLuint, offset: GLuint, size: GLsizei, `pointer`: pointer, preserve: GLenum) {.importc.} + proc glVertexAttrib4uivARB(index: GLuint, v: ptr GLuint) {.importc.} + proc glVertexAttrib4iv(index: GLuint, v: ptr GLint) {.importc.} + proc glFrustum(left: GLdouble, right: GLdouble, bottom: GLdouble, top: GLdouble, zNear: GLdouble, zFar: GLdouble) {.importc.} + proc glDrawTexxvOES(coords: ptr GLfixed) {.importc.} + proc glTexCoord2fColor4ubVertex3fSUN(s: GLfloat, t: GLfloat, r: GLubyte, g: GLubyte, b: GLubyte, a: GLubyte, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glMultiTexCoord2fARB(target: GLenum, s: GLfloat, t: GLfloat) {.importc.} + proc glGenTransformFeedbacksNV(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glMultiTexGenfEXT(texunit: GLenum, coord: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glGetMinmax(target: GLenum, reset: GLboolean, format: GLenum, `type`: GLenum, values: pointer) {.importc.} + proc glBindTransformFeedback(target: GLenum, id: GLuint) {.importc.} + proc glEnableVertexAttribArrayARB(index: GLuint) {.importc.} + proc glIsFenceAPPLE(fence: GLuint): GLboolean {.importc.} + proc glMultiTexGendvEXT(texunit: GLenum, coord: GLenum, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glRotatex(angle: GLfixed, x: GLfixed, y: GLfixed, z: GLfixed) {.importc.} + proc glGetFragmentLightfvSGIX(light: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glMultiTexCoord4dv(target: GLenum, v: ptr GLdouble) {.importc.} + proc glBlendFuncSeparateEXT(sfactorRgb: GLenum, dfactorRgb: GLenum, sfactorAlpha: GLenum, dfactorAlpha: GLenum) {.importc.} + proc glMultiTexCoord1f(target: GLenum, s: GLfloat) {.importc.} + proc glWindowPos2f(x: GLfloat, y: GLfloat) {.importc.} + proc glGetPathTexGenivNV(texCoordSet: GLenum, pname: GLenum, value: ptr GLint) {.importc.} + proc glIndexxvOES(component: ptr GLfixed) {.importc.} + proc glDisableVertexArrayAttribEXT(vaobj: GLuint, index: GLuint) {.importc.} + proc glGetProgramivARB(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glPatchParameteri(pname: GLenum, value: GLint) {.importc.} + proc glMultiTexCoord2fv(target: GLenum, v: ptr GLfloat) {.importc.} + proc glTexSubImage3DEXT(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glFramebufferTexture1DEXT(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glTangent3fEXT(tx: GLfloat, ty: GLfloat, tz: GLfloat) {.importc.} + proc glIsVertexAttribEnabledAPPLE(index: GLuint, pname: GLenum): GLboolean {.importc.} + proc glGetShaderInfoLog(shader: GLuint, bufSize: GLsizei, length: ptr GLsizei, infoLog: cstring) {.importc.} + proc glFrustumx(l: GLfixed, r: GLfixed, b: GLfixed, t: GLfixed, n: GLfixed, f: GLfixed) {.importc.} + proc glTexGenfv(coord: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glCompressedTexImage2DARB(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, border: GLint, imageSize: GLsizei, data: pointer) {.importc.} + proc glMultiTexCoord2bvOES(texture: GLenum, coords: ptr GLbyte) {.importc.} + proc glGetTexBumpParameterivATI(pname: GLenum, param: ptr GLint) {.importc.} + proc glMultiTexCoord2svARB(target: GLenum, v: ptr GLshort) {.importc.} + proc glProgramBufferParametersIivNV(target: GLenum, bindingIndex: GLuint, wordIndex: GLuint, count: GLsizei, params: ptr GLint) {.importc.} + proc glIsQueryARB(id: GLuint): GLboolean {.importc.} + proc glFramebufferTextureLayer(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint, layer: GLint) {.importc.} + proc glUniform4i(location: GLint, v0: GLint, v1: GLint, v2: GLint, v3: GLint) {.importc.} + proc glDrawArrays(mode: GLenum, first: GLint, count: GLsizei) {.importc.} + proc glWeightubvARB(size: GLint, weights: ptr GLubyte) {.importc.} + proc glGetUniformSubroutineuiv(shadertype: GLenum, location: GLint, params: ptr GLuint) {.importc.} + proc glMultTransposeMatrixdARB(m: ptr GLdouble) {.importc.} + proc glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN(rc: ptr GLuint, tc: ptr GLfloat, n: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glGetMapdv(target: GLenum, query: GLenum, v: ptr GLdouble) {.importc.} + proc glGetMultisamplefvNV(pname: GLenum, index: GLuint, val: ptr GLfloat) {.importc.} + proc glVertex2hvNV(v: ptr GLhalfNv) {.importc.} + proc glProgramUniformMatrix2x3fv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glProgramUniform3iEXT(program: GLuint, location: GLint, v0: GLint, v1: GLint, v2: GLint) {.importc.} + proc glGetnPixelMapusvARB(map: GLenum, bufSize: GLsizei, values: ptr GLushort) {.importc.} + proc glVertexWeighthvNV(weight: ptr GLhalfNv) {.importc.} + proc glDrawTransformFeedbackInstanced(mode: GLenum, id: GLuint, instancecount: GLsizei) {.importc.} + proc glFlushStaticDataIBM(target: GLenum) {.importc.} + proc glWindowPos2fvARB(v: ptr GLfloat) {.importc.} + proc glMultiTexCoord3sARB(target: GLenum, s: GLshort, t: GLshort, r: GLshort) {.importc.} + proc glWindowPos3fv(v: ptr GLfloat) {.importc.} + proc glFlushVertexArrayRangeNV() {.importc.} + proc glTangent3bEXT(tx: GLbyte, ty: GLbyte, tz: GLbyte) {.importc.} + proc glIglooInterfaceSGIX(pname: GLenum, params: pointer) {.importc.} + proc glProgramUniformMatrix4x2fv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glVertexAttribIFormatNV(index: GLuint, size: GLint, `type`: GLenum, stride: GLsizei) {.importc.} + proc glNamedRenderbufferStorageMultisampleEXT(renderbuffer: GLuint, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glCopyTexImage1DEXT(target: GLenum, level: GLint, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, border: GLint) {.importc.} + proc glBindTexGenParameterEXT(unit: GLenum, coord: GLenum, value: GLenum): GLuint {.importc.} + proc glVertex4hNV(x: GLhalfNv, y: GLhalfNv, z: GLhalfNv, w: GLhalfNv) {.importc.} + proc glGetMapfv(target: GLenum, query: GLenum, v: ptr GLfloat) {.importc.} + proc glSamplePatternEXT(pattern: GLenum) {.importc.} + proc glIndexxOES(component: GLfixed) {.importc.} + proc glVertexAttrib4ubv(index: GLuint, v: ptr GLubyte) {.importc.} + proc glGetColorTable(target: GLenum, format: GLenum, `type`: GLenum, table: pointer) {.importc.} + proc glFragmentLightModelivSGIX(pname: GLenum, params: ptr GLint) {.importc.} + proc glPixelTransformParameterfEXT(target: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glSamplerParameterfv(sampler: GLuint, pname: GLenum, param: ptr GLfloat) {.importc.} + proc glBindTextureUnitParameterEXT(unit: GLenum, value: GLenum): GLuint {.importc.} + proc glColor3ub(red: GLubyte, green: GLubyte, blue: GLubyte) {.importc.} + proc glGetMultiTexGenivEXT(texunit: GLenum, coord: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glVariantusvEXT(id: GLuint, `addr`: ptr GLushort) {.importc.} + proc glMaterialiv(face: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glPassTexCoordATI(dst: GLuint, coord: GLuint, swizzle: GLenum) {.importc.} + proc glGetIntegerui64vNV(value: GLenum, result: ptr GLuint64Ext) {.importc.} + proc glProgramParameteriEXT(program: GLuint, pname: GLenum, value: GLint) {.importc.} + proc glVertexArrayEdgeFlagOffsetEXT(vaobj: GLuint, buffer: GLuint, stride: GLsizei, offset: GLintptr) {.importc.} + proc glGetCombinerInputParameterivNV(stage: GLenum, portion: GLenum, variable: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glLogicOp(opcode: GLenum) {.importc.} + proc glConvolutionParameterfvEXT(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glIsBufferResidentNV(target: GLenum): GLboolean {.importc.} + proc glIsProgram(program: GLuint): GLboolean {.importc.} + proc glEndQueryARB(target: GLenum) {.importc.} + proc glRenderbufferStorage(target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glMaterialfv(face: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glTranslatex(x: GLfixed, y: GLfixed, z: GLfixed) {.importc.} + proc glPathParameterivNV(path: GLuint, pname: GLenum, value: ptr GLint) {.importc.} + proc glLightxOES(light: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glSampleMaskEXT(value: GLclampf, invert: GLboolean) {.importc.} + proc glReplacementCodeubvSUN(code: ptr GLubyte) {.importc.} + proc glVertexAttribArrayObjectATI(index: GLuint, size: GLint, `type`: GLenum, normalized: GLboolean, stride: GLsizei, buffer: GLuint, offset: GLuint) {.importc.} + proc glBeginTransformFeedbackNV(primitiveMode: GLenum) {.importc.} + proc glEvalCoord1fv(u: ptr GLfloat) {.importc.} + proc glProgramUniformMatrix2x3dvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glMaterialxv(face: GLenum, pname: GLenum, param: ptr GLfixed) {.importc.} + proc glGetIntegerui64i_vNV(value: GLenum, index: GLuint, result: ptr GLuint64Ext) {.importc.} + proc glUniformBlockBinding(program: GLuint, uniformBlockIndex: GLuint, uniformBlockBinding: GLuint) {.importc.} + proc glColor4ui(red: GLuint, green: GLuint, blue: GLuint, alpha: GLuint) {.importc.} + proc glColor4ubVertex2fvSUN(c: ptr GLubyte, v: ptr GLfloat) {.importc.} + proc glRectd(x1: GLdouble, y1: GLdouble, x2: GLdouble, y2: GLdouble) {.importc.} + proc glGenVertexShadersEXT(range: GLuint): GLuint {.importc.} + proc glLinkProgramARB(programObj: GLhandleArb) {.importc.} + proc glVertexAttribL4dEXT(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glBlitFramebuffer(srcX0: GLint, srcY0: GLint, srcX1: GLint, srcY1: GLint, dstX0: GLint, dstY0: GLint, dstX1: GLint, dstY1: GLint, mask: GLbitfield, filter: GLenum) {.importc.} + proc glUseProgram(program: GLuint) {.importc.} + proc glNamedProgramLocalParameterI4ivEXT(program: GLuint, target: GLenum, index: GLuint, params: ptr GLint) {.importc.} + proc glMatrixLoadTransposedEXT(mode: GLenum, m: ptr GLdouble) {.importc.} + proc glTranslatef(x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glGetBooleani_v(target: GLenum, index: GLuint, data: ptr GLboolean) {.importc.} + proc glEndFragmentShaderATI() {.importc.} + proc glVertexAttribI4ivEXT(index: GLuint, v: ptr GLint) {.importc.} + proc glMultiDrawElementsIndirectBindlessNV(mode: GLenum, `type`: GLenum, indirect: pointer, drawCount: GLsizei, stride: GLsizei, vertexBufferCount: GLint) {.importc.} + proc glTexCoord2s(s: GLshort, t: GLshort) {.importc.} + proc glProgramUniform1i64vNV(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint64Ext) {.importc.} + proc glPointSizePointerOES(`type`: GLenum, stride: GLsizei, `pointer`: ptr pointer) {.importc.} + proc glGetTexFilterFuncSGIS(target: GLenum, filter: GLenum, weights: ptr GLfloat) {.importc.} + proc glMapGrid2xOES(n: GLint, u1: GLfixed, u2: GLfixed, v1: GLfixed, v2: GLfixed) {.importc.} + proc glRasterPos4xvOES(coords: ptr GLfixed) {.importc.} + proc glGetProgramBinary(program: GLuint, bufSize: GLsizei, length: ptr GLsizei, binaryFormat: ptr GLenum, binary: pointer) {.importc.} + proc glNamedProgramLocalParameterI4uiEXT(program: GLuint, target: GLenum, index: GLuint, x: GLuint, y: GLuint, z: GLuint, w: GLuint) {.importc.} + proc glGetTexImage(target: GLenum, level: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glColor4d(red: GLdouble, green: GLdouble, blue: GLdouble, alpha: GLdouble) {.importc.} + proc glTexCoord2fColor4fNormal3fVertex3fSUN(s: GLfloat, t: GLfloat, r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat, nx: GLfloat, ny: GLfloat, nz: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glIndexi(c: GLint) {.importc.} + proc glGetSamplerParameterIuiv(sampler: GLuint, pname: GLenum, params: ptr GLuint) {.importc.} + proc glGetnUniformivARB(program: GLuint, location: GLint, bufSize: GLsizei, params: ptr GLint) {.importc.} + proc glCopyTexSubImage3DEXT(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glVertexAttribI2uivEXT(index: GLuint, v: ptr GLuint) {.importc.} + proc glVertexStream2fvATI(stream: GLenum, coords: ptr GLfloat) {.importc.} + proc glArrayElementEXT(i: GLint) {.importc.} + proc glVertexAttrib2fv(index: GLuint, v: ptr GLfloat) {.importc.} + proc glCopyMultiTexSubImage1DEXT(texunit: GLenum, target: GLenum, level: GLint, xoffset: GLint, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glTexCoord4sv(v: ptr GLshort) {.importc.} + proc glTexGenfvOES(coord: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glPointParameteriv(pname: GLenum, params: ptr GLint) {.importc.} + proc glGetNamedRenderbufferParameterivEXT(renderbuffer: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glProgramVertexLimitNV(target: GLenum, limit: GLint) {.importc.} + proc glSetMultisamplefvAMD(pname: GLenum, index: GLuint, val: ptr GLfloat) {.importc.} + proc glLoadIdentityDeformationMapSGIX(mask: GLbitfield) {.importc.} + proc glIsSyncAPPLE(sync: GLsync): GLboolean {.importc.} + proc glProgramUniform1ui64vNV(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glEdgeFlagPointerListIBM(stride: GLint, `pointer`: ptr ptr GLboolean, ptrstride: GLint) {.importc.} + proc glBeginVertexShaderEXT() {.importc.} + proc glGetIntegerv(pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexAttrib2dvARB(index: GLuint, v: ptr GLdouble) {.importc.} + proc glBeginConditionalRenderNV(id: GLuint, mode: GLenum) {.importc.} + proc glEdgeFlagv(flag: ptr GLboolean) {.importc.} + proc glReplacementCodeubSUN(code: GLubyte) {.importc.} + proc glObjectLabel(identifier: GLenum, name: GLuint, length: GLsizei, label: cstring) {.importc.} + proc glMultiTexCoord3xvOES(texture: GLenum, coords: ptr GLfixed) {.importc.} + proc glNormal3iv(v: ptr GLint) {.importc.} + proc glSamplerParameteri(sampler: GLuint, pname: GLenum, param: GLint) {.importc.} + proc glTextureStorage1DEXT(texture: GLuint, target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei) {.importc.} + proc glVertexStream4dvATI(stream: GLenum, coords: ptr GLdouble) {.importc.} + proc glWindowPos2fv(v: ptr GLfloat) {.importc.} + proc glTexCoord4i(s: GLint, t: GLint, r: GLint, q: GLint) {.importc.} + proc glVertexAttrib4NusvARB(index: GLuint, v: ptr GLushort) {.importc.} + proc glVertexAttribL4d(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glVertexAttribDivisorANGLE(index: GLuint, divisor: GLuint) {.importc.} + proc glMatrixIndexPointerOES(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glMultMatrixxOES(m: ptr GLfixed) {.importc.} + proc glMultiTexCoordP2ui(texture: GLenum, `type`: GLenum, coords: GLuint) {.importc.} + proc glDeformationMap3dSGIX(target: GLenum, u1: GLdouble, u2: GLdouble, ustride: GLint, uorder: GLint, v1: GLdouble, v2: GLdouble, vstride: GLint, vorder: GLint, w1: GLdouble, w2: GLdouble, wstride: GLint, worder: GLint, points: ptr GLdouble) {.importc.} + proc glClearDepthfOES(depth: GLclampf) {.importc.} + proc glVertexStream1ivATI(stream: GLenum, coords: ptr GLint) {.importc.} + proc glHint(target: GLenum, mode: GLenum) {.importc.} + proc glVertex3fv(v: ptr GLfloat) {.importc.} + proc glWaitSyncAPPLE(sync: GLsync, flags: GLbitfield, timeout: GLuint64) {.importc.} + proc glWindowPos3i(x: GLint, y: GLint, z: GLint) {.importc.} + proc glCompressedTexImage3DARB(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, imageSize: GLsizei, data: pointer) {.importc.} + proc glVertexAttrib1fvARB(index: GLuint, v: ptr GLfloat) {.importc.} + proc glMultiTexCoord4xOES(texture: GLenum, s: GLfixed, t: GLfixed, r: GLfixed, q: GLfixed) {.importc.} + proc glUniform4ui64NV(location: GLint, x: GLuint64Ext, y: GLuint64Ext, z: GLuint64Ext, w: GLuint64Ext) {.importc.} + proc glProgramUniform4uiEXT(program: GLuint, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint, v3: GLuint) {.importc.} + proc glUnmapNamedBufferEXT(buffer: GLuint): GLboolean {.importc.} + proc glBitmap(width: GLsizei, height: GLsizei, xorig: GLfloat, yorig: GLfloat, xmove: GLfloat, ymove: GLfloat, bitmap: ptr GLubyte) {.importc.} + proc glNamedProgramLocalParameters4fvEXT(program: GLuint, target: GLenum, index: GLuint, count: GLsizei, params: ptr GLfloat) {.importc.} + proc glGetPathCommandsNV(path: GLuint, commands: ptr GLubyte) {.importc.} + proc glVertexAttrib3fNV(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glNamedProgramStringEXT(program: GLuint, target: GLenum, format: GLenum, len: GLsizei, string: pointer) {.importc.} + proc glMatrixIndexusvARB(size: GLint, indices: ptr GLushort) {.importc.} + proc glBlitFramebufferNV(srcX0: GLint, srcY0: GLint, srcX1: GLint, srcY1: GLint, dstX0: GLint, dstY0: GLint, dstX1: GLint, dstY1: GLint, mask: GLbitfield, filter: GLenum) {.importc.} + proc glVertexAttribI1uiv(index: GLuint, v: ptr GLuint) {.importc.} + proc glEndConditionalRenderNV() {.importc.} + proc glFeedbackBuffer(size: GLsizei, `type`: GLenum, buffer: ptr GLfloat) {.importc.} + proc glMultiTexCoord3bvOES(texture: GLenum, coords: ptr GLbyte) {.importc.} + proc glCopyColorTableSGI(target: GLenum, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glActiveTexture(texture: GLenum) {.importc.} + proc glFogCoordhNV(fog: GLhalfNv) {.importc.} + proc glColorMaskIndexedEXT(index: GLuint, r: GLboolean, g: GLboolean, b: GLboolean, a: GLboolean) {.importc.} + proc glGetCompressedTexImage(target: GLenum, level: GLint, img: pointer) {.importc.} + proc glRasterPos2iv(v: ptr GLint) {.importc.} + proc glGetBufferParameterivARB(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glProgramUniform3d(program: GLuint, location: GLint, v0: GLdouble, v1: GLdouble, v2: GLdouble) {.importc.} + proc glRasterPos3xvOES(coords: ptr GLfixed) {.importc.} + proc glGetTextureParameterIuivEXT(texture: GLuint, target: GLenum, pname: GLenum, params: ptr GLuint) {.importc.} + proc glBindImageTextureEXT(index: GLuint, texture: GLuint, level: GLint, layered: GLboolean, layer: GLint, access: GLenum, format: GLint) {.importc.} + proc glWindowPos2iMESA(x: GLint, y: GLint) {.importc.} + proc glVertexPointervINTEL(size: GLint, `type`: GLenum, `pointer`: ptr pointer) {.importc.} + proc glPixelTexGenParameterfvSGIS(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glUniform1iARB(location: GLint, v0: GLint) {.importc.} + proc glTextureSubImage3DEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glStencilOpSeparate(face: GLenum, sfail: GLenum, dpfail: GLenum, dppass: GLenum) {.importc.} + proc glVertexAttrib1dARB(index: GLuint, x: GLdouble) {.importc.} + proc glGetVideoCaptureStreamivNV(video_capture_slot: GLuint, stream: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glIsFramebufferEXT(framebuffer: GLuint): GLboolean {.importc.} + proc glPointParameterxv(pname: GLenum, params: ptr GLfixed) {.importc.} + proc glProgramUniform4dv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glPassThrough(token: GLfloat) {.importc.} + proc glGetProgramPipelineiv(pipeline: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glApplyTextureEXT(mode: GLenum) {.importc.} + proc glVertexArrayNormalOffsetEXT(vaobj: GLuint, buffer: GLuint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glTexFilterFuncSGIS(target: GLenum, filter: GLenum, n: GLsizei, weights: ptr GLfloat) {.importc.} + proc glRenderbufferStorageOES(target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glBindParameterEXT(value: GLenum): GLuint {.importc.} + proc glVertex4s(x: GLshort, y: GLshort, z: GLshort, w: GLshort) {.importc.} + proc glLoadTransposeMatrixf(m: ptr GLfloat) {.importc.} + proc glDepthFunc(fun: GLenum) {.importc.} + proc glGetFramebufferAttachmentParameterivEXT(target: GLenum, attachment: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glSampleMaskSGIS(value: GLclampf, invert: GLboolean) {.importc.} + proc glGetPointerIndexedvEXT(target: GLenum, index: GLuint, data: ptr pointer) {.importc.} + proc glVertexStream4iATI(stream: GLenum, x: GLint, y: GLint, z: GLint, w: GLint) {.importc.} + proc glUnlockArraysEXT() {.importc.} + proc glReplacementCodeuivSUN(code: ptr GLuint) {.importc.} + proc glMatrixScaledEXT(mode: GLenum, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glMultiTexImage2DEXT(texunit: GLenum, target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, height: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glFeedbackBufferxOES(n: GLsizei, `type`: GLenum, buffer: ptr GLfixed) {.importc.} + proc glLightEnviSGIX(pname: GLenum, param: GLint) {.importc.} + proc glMultiTexCoord4dARB(target: GLenum, s: GLdouble, t: GLdouble, r: GLdouble, q: GLdouble) {.importc.} + proc glExtGetTexLevelParameterivQCOM(texture: GLuint, face: GLenum, level: GLint, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexAttribI4usvEXT(index: GLuint, v: ptr GLushort) {.importc.} + proc glWindowPos2dvARB(v: ptr GLdouble) {.importc.} + proc glBindFramebuffer(target: GLenum, framebuffer: GLuint) {.importc.} + proc glGetProgramPipelineivEXT(pipeline: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glProgramUniformHandleui64vNV(program: GLuint, location: GLint, count: GLsizei, values: ptr GLuint64) {.importc.} + proc glFogCoordhvNV(fog: ptr GLhalfNv) {.importc.} + proc glTextureImage1DEXT(texture: GLuint, target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glGetActiveAtomicCounterBufferiv(program: GLuint, bufferIndex: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glBeginQueryARB(target: GLenum, id: GLuint) {.importc.} + proc glGetTexParameterIuivEXT(target: GLenum, pname: GLenum, params: ptr GLuint) {.importc.} + proc glUniform4ui64vNV(location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glClearAccumxOES(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed) {.importc.} + proc glFreeObjectBufferATI(buffer: GLuint) {.importc.} + proc glGetVideouivNV(video_slot: GLuint, pname: GLenum, params: ptr GLuint) {.importc.} + proc glVertexAttribL4ui64NV(index: GLuint, x: GLuint64Ext, y: GLuint64Ext, z: GLuint64Ext, w: GLuint64Ext) {.importc.} + proc glGetUniformBlockIndex(program: GLuint, uniformBlockName: cstring): GLuint {.importc.} + proc glCopyMultiTexSubImage2DEXT(texunit: GLenum, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glVertex3bvOES(coords: ptr GLbyte) {.importc.} + proc glMultiDrawElementArrayAPPLE(mode: GLenum, first: ptr GLint, count: ptr GLsizei, primcount: GLsizei) {.importc.} + proc glPrimitiveRestartNV() {.importc.} + proc glMateriali(face: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glBegin(mode: GLenum) {.importc.} + proc glFogCoordPointerEXT(`type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glTexCoord1sv(v: ptr GLshort) {.importc.} + proc glVertexAttribI4sv(index: GLuint, v: ptr GLshort) {.importc.} + proc glTexEnvx(target: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glTexParameterIivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glLoadTransposeMatrixfARB(m: ptr GLfloat) {.importc.} + proc glGetTextureSamplerHandleARB(texture: GLuint, sampler: GLuint): GLuint64 {.importc.} + proc glVertexP3uiv(`type`: GLenum, value: ptr GLuint) {.importc.} + proc glProgramUniform2dv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glTexCoord4xvOES(coords: ptr GLfixed) {.importc.} + proc glTexStorage1D(target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei) {.importc.} + proc glTextureParameterfEXT(texture: GLuint, target: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glVertexAttrib1d(index: GLuint, x: GLdouble) {.importc.} + proc glGetnPixelMapfvARB(map: GLenum, bufSize: GLsizei, values: ptr GLfloat) {.importc.} + proc glDisableVertexAttribArray(index: GLuint) {.importc.} + proc glUniformMatrix4x3dv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glRasterPos4f(x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glProgramUniform1fEXT(program: GLuint, location: GLint, v0: GLfloat) {.importc.} + proc glPathTexGenNV(texCoordSet: GLenum, genMode: GLenum, components: GLint, coeffs: ptr GLfloat) {.importc.} + proc glUniform3ui(location: GLint, v0: GLuint, v1: GLuint, v2: GLuint) {.importc.} + proc glVDPAURegisterOutputSurfaceNV(vdpSurface: pointer, target: GLenum, numTextureNames: GLsizei, textureNames: ptr GLuint): GLvdpauSurfaceNv {.importc.} + proc glGetProgramLocalParameterIuivNV(target: GLenum, index: GLuint, params: ptr GLuint) {.importc.} + proc glIsTextureHandleResidentNV(handle: GLuint64): GLboolean {.importc.} + proc glProgramEnvParameters4fvEXT(target: GLenum, index: GLuint, count: GLsizei, params: ptr GLfloat) {.importc.} + proc glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN(rc: GLuint, s: GLfloat, t: GLfloat, nx: GLfloat, ny: GLfloat, nz: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glGetMultiTexEnvivEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetFloatv(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glInsertEventMarkerEXT(length: GLsizei, marker: cstring) {.importc.} + proc glRasterPos3d(x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glNamedFramebufferRenderbufferEXT(framebuffer: GLuint, attachment: GLenum, renderbuffertarget: GLenum, renderbuffer: GLuint) {.importc.} + proc glGetConvolutionFilter(target: GLenum, format: GLenum, `type`: GLenum, image: pointer) {.importc.} + proc glIsOcclusionQueryNV(id: GLuint): GLboolean {.importc.} + proc glGetnPixelMapuivARB(map: GLenum, bufSize: GLsizei, values: ptr GLuint) {.importc.} + proc glMapParameterfvNV(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glPushDebugGroup(source: GLenum, id: GLuint, length: GLsizei, message: cstring) {.importc.} + proc glMakeImageHandleResidentARB(handle: GLuint64, access: GLenum) {.importc.} + proc glProgramUniformMatrix2fv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glUniform3i64vNV(location: GLint, count: GLsizei, value: ptr GLint64Ext) {.importc.} + proc glImageTransformParameteriHP(target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glMultiTexCoord1s(target: GLenum, s: GLshort) {.importc.} + proc glVertexAttribL4dvEXT(index: GLuint, v: ptr GLdouble) {.importc.} + proc glGetProgramEnvParameterfvARB(target: GLenum, index: GLuint, params: ptr GLfloat) {.importc.} + proc glVertexArrayColorOffsetEXT(vaobj: GLuint, buffer: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glGetHistogramParameterivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetRenderbufferParameterivOES(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetBufferPointerv(target: GLenum, pname: GLenum, params: ptr pointer) {.importc.} + proc glSecondaryColor3ui(red: GLuint, green: GLuint, blue: GLuint) {.importc.} + proc glGetDebugMessageLog(count: GLuint, bufsize: GLsizei, sources: ptr GLenum, types: ptr GLenum, ids: ptr GLuint, severities: ptr GLenum, lengths: ptr GLsizei, messageLog: cstring): GLuint {.importc.} + proc glNormal3i(nx: GLint, ny: GLint, nz: GLint) {.importc.} + proc glTestFenceNV(fence: GLuint): GLboolean {.importc.} + proc glSecondaryColor3usv(v: ptr GLushort) {.importc.} + proc glGenPathsNV(range: GLsizei): GLuint {.importc.} + proc glDeleteBuffersARB(n: GLsizei, buffers: ptr GLuint) {.importc.} + proc glProgramUniform4fvEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glGetSharpenTexFuncSGIS(target: GLenum, points: ptr GLfloat) {.importc.} + proc glDrawMeshArraysSUN(mode: GLenum, first: GLint, count: GLsizei, width: GLsizei) {.importc.} + proc glVertexAttribs4hvNV(index: GLuint, n: GLsizei, v: ptr GLhalfNv) {.importc.} + proc glGetClipPlane(plane: GLenum, equation: ptr GLdouble) {.importc.} + proc glEvalCoord2fv(u: ptr GLfloat) {.importc.} + proc glAsyncMarkerSGIX(marker: GLuint) {.importc.} + proc glGetSynciv(sync: GLsync, pname: GLenum, bufSize: GLsizei, length: ptr GLsizei, values: ptr GLint) {.importc.} + proc glGetPathTexGenfvNV(texCoordSet: GLenum, pname: GLenum, value: ptr GLfloat) {.importc.} + proc glTexParameterf(target: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glMultiTexCoord1fvARB(target: GLenum, v: ptr GLfloat) {.importc.} + proc glNormalPointerListIBM(`type`: GLenum, stride: GLint, `pointer`: ptr pointer, ptrstride: GLint) {.importc.} + proc glFragmentLightfvSGIX(light: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glViewportArrayv(first: GLuint, count: GLsizei, v: ptr GLfloat) {.importc.} + proc glNormal3fVertex3fSUN(nx: GLfloat, ny: GLfloat, nz: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glMultiTexCoord2dvARB(target: GLenum, v: ptr GLdouble) {.importc.} + proc glCopyColorSubTable(target: GLenum, start: GLsizei, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glTexCoord2hvNV(v: ptr GLhalfNv) {.importc.} + proc glGetQueryObjectiv(id: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glColor4hNV(red: GLhalfNv, green: GLhalfNv, blue: GLhalfNv, alpha: GLhalfNv) {.importc.} + proc glProgramUniform2fv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glMultiTexCoord4hNV(target: GLenum, s: GLhalfNv, t: GLhalfNv, r: GLhalfNv, q: GLhalfNv) {.importc.} + proc glWindowPos2fvMESA(v: ptr GLfloat) {.importc.} + proc glVertexAttrib3s(index: GLuint, x: GLshort, y: GLshort, z: GLshort) {.importc.} + proc glGetIntegerIndexedvEXT(target: GLenum, index: GLuint, data: ptr GLint) {.importc.} + proc glVertexAttrib4Niv(index: GLuint, v: ptr GLint) {.importc.} + proc glProgramLocalParameter4dvARB(target: GLenum, index: GLuint, params: ptr GLdouble) {.importc.} + proc glFramebufferTextureLayerEXT(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint, layer: GLint) {.importc.} + proc glVertexAttribI1ui(index: GLuint, x: GLuint) {.importc.} + proc glFogCoorddv(coord: ptr GLdouble) {.importc.} + proc glLightModelxv(pname: GLenum, param: ptr GLfixed) {.importc.} + proc glGetCombinerOutputParameterfvNV(stage: GLenum, portion: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glFramebufferReadBufferEXT(framebuffer: GLuint, mode: GLenum) {.importc.} + proc glGetActiveUniformsiv(program: GLuint, uniformCount: GLsizei, uniformIndices: ptr GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetProgramStringNV(id: GLuint, pname: GLenum, program: ptr GLubyte) {.importc.} + proc glCopyConvolutionFilter2D(target: GLenum, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glMultiTexCoord3iARB(target: GLenum, s: GLint, t: GLint, r: GLint) {.importc.} + proc glPushName(name: GLuint) {.importc.} + proc glProgramParameter4dNV(target: GLenum, index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glVertexAttrib4svARB(index: GLuint, v: ptr GLshort) {.importc.} + proc glSecondaryColor3iv(v: ptr GLint) {.importc.} + proc glCopyColorSubTableEXT(target: GLenum, start: GLsizei, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glCallList(list: GLuint) {.importc.} + proc glGetMultiTexLevelParameterivEXT(texunit: GLenum, target: GLenum, level: GLint, pname: GLenum, params: ptr GLint) {.importc.} + proc glProgramUniformMatrix2x4fv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glTexBumpParameterivATI(pname: GLenum, param: ptr GLint) {.importc.} + proc glTexGeni(coord: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glSecondaryColor3dv(v: ptr GLdouble) {.importc.} + proc glGetnUniformdvARB(program: GLuint, location: GLint, bufSize: GLsizei, params: ptr GLdouble) {.importc.} + proc glGetNamedProgramLocalParameterdvEXT(program: GLuint, target: GLenum, index: GLuint, params: ptr GLdouble) {.importc.} + proc glGetVertexAttribPointervARB(index: GLuint, pname: GLenum, `pointer`: ptr pointer) {.importc.} + proc glCopyColorTable(target: GLenum, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glNamedFramebufferTextureLayerEXT(framebuffer: GLuint, attachment: GLenum, texture: GLuint, level: GLint, layer: GLint) {.importc.} + proc glLoadProgramNV(target: GLenum, id: GLuint, len: GLsizei, program: ptr GLubyte) {.importc.} + proc glAlphaFragmentOp2ATI(op: GLenum, dst: GLuint, dstMod: GLuint, arg1: GLuint, arg1Rep: GLuint, arg1Mod: GLuint, arg2: GLuint, arg2Rep: GLuint, arg2Mod: GLuint) {.importc.} + proc glBindLightParameterEXT(light: GLenum, value: GLenum): GLuint {.importc.} + proc glVertexAttrib1fv(index: GLuint, v: ptr GLfloat) {.importc.} + proc glLoadIdentity() {.importc.} + proc glFramebufferTexture2DMultisampleEXT(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint, samples: GLsizei) {.importc.} + proc glVertexAttrib1dvARB(index: GLuint, v: ptr GLdouble) {.importc.} + proc glDrawRangeElementsBaseVertex(mode: GLenum, start: GLuint, `end`: GLuint, count: GLsizei, `type`: GLenum, indices: pointer, basevertex: GLint) {.importc.} + proc glPixelMapfv(map: GLenum, mapsize: GLsizei, values: ptr GLfloat) {.importc.} + proc glPointParameterxOES(pname: GLenum, param: GLfixed) {.importc.} + proc glBindBufferRangeNV(target: GLenum, index: GLuint, buffer: GLuint, offset: GLintptr, size: GLsizeiptr) {.importc.} + proc glDepthBoundsEXT(zmin: GLclampd, zmax: GLclampd) {.importc.} + proc glProgramUniformMatrix2dv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glSecondaryColor3s(red: GLshort, green: GLshort, blue: GLshort) {.importc.} + proc glEdgeFlagPointerEXT(stride: GLsizei, count: GLsizei, `pointer`: ptr GLboolean) {.importc.} + proc glVertexStream1fATI(stream: GLenum, x: GLfloat) {.importc.} + proc glUniformui64NV(location: GLint, value: GLuint64Ext) {.importc.} + proc glTexCoordP4uiv(`type`: GLenum, coords: ptr GLuint) {.importc.} + proc glTexCoord3d(s: GLdouble, t: GLdouble, r: GLdouble) {.importc.} + proc glDeleteProgramPipelines(n: GLsizei, pipelines: ptr GLuint) {.importc.} + proc glVertex2iv(v: ptr GLint) {.importc.} + proc glGetMultisamplefv(pname: GLenum, index: GLuint, val: ptr GLfloat) {.importc.} + proc glStartInstrumentsSGIX() {.importc.} + proc glGetOcclusionQueryivNV(id: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glDebugMessageCallback(callback: GLdebugProc, userParam: ptr pointer) {.importc.} + proc glPixelZoomxOES(xfactor: GLfixed, yfactor: GLfixed) {.importc.} + proc glTexCoord3i(s: GLint, t: GLint, r: GLint) {.importc.} + proc glEdgeFlagFormatNV(stride: GLsizei) {.importc.} + proc glProgramUniform2i(program: GLuint, location: GLint, v0: GLint, v1: GLint) {.importc.} + proc glColor3b(red: GLbyte, green: GLbyte, blue: GLbyte) {.importc.} + proc glDepthRangefOES(n: GLclampf, f: GLclampf) {.importc.} + proc glEndVertexShaderEXT() {.importc.} + proc glBindVertexArrayAPPLE(`array`: GLuint) {.importc.} + proc glColor4bv(v: ptr GLbyte) {.importc.} + proc glNamedFramebufferTexture2DEXT(framebuffer: GLuint, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glTexCoord1f(s: GLfloat) {.importc.} + proc glUniform3fvARB(location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glGetQueryObjectuivARB(id: GLuint, pname: GLenum, params: ptr GLuint) {.importc.} + proc glVertexAttrib4bv(index: GLuint, v: ptr GLbyte) {.importc.} + proc glGetPixelTransformParameterivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexAttrib3svNV(index: GLuint, v: ptr GLshort) {.importc.} + proc glDeleteQueriesEXT(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glUniform3ivARB(location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glNormal3xvOES(coords: ptr GLfixed) {.importc.} + proc glMatrixLoadfEXT(mode: GLenum, m: ptr GLfloat) {.importc.} + proc glGetNamedFramebufferAttachmentParameterivEXT(framebuffer: GLuint, attachment: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glSeparableFilter2D(target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, row: pointer, column: pointer) {.importc.} + proc glVertexAttribI3uiv(index: GLuint, v: ptr GLuint) {.importc.} + proc glTextureStorageSparseAMD(texture: GLuint, target: GLenum, internalFormat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, layers: GLsizei, flags: GLbitfield) {.importc.} + proc glMultiDrawArraysIndirectCountARB(mode: GLenum, indirect: GLintptr, drawcount: GLintptr, maxdrawcount: GLsizei, stride: GLsizei) {.importc.} + proc glTranslated(x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glColorPointer(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glDrawElementsInstancedBaseVertex(mode: GLenum, count: GLsizei, `type`: GLenum, indices: pointer, instancecount: GLsizei, basevertex: GLint) {.importc.} + proc glBindAttribLocationARB(programObj: GLhandleArb, index: GLuint, name: cstring) {.importc.} + proc glTexGendv(coord: GLenum, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glGetPathCoordsNV(path: GLuint, coords: ptr GLfloat) {.importc.} + proc glGetMapParameterivNV(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glClientAttribDefaultEXT(mask: GLbitfield) {.importc.} + proc glProgramUniformMatrix4x3fvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glEnable(cap: GLenum) {.importc.} + proc glGetVertexAttribPointervNV(index: GLuint, pname: GLenum, `pointer`: ptr pointer) {.importc.} + proc glBindMultiTextureEXT(texunit: GLenum, target: GLenum, texture: GLuint) {.importc.} + proc glGetConvolutionParameterfvEXT(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glLightModelxvOES(pname: GLenum, param: ptr GLfixed) {.importc.} + proc glMultiTexCoord4sv(target: GLenum, v: ptr GLshort) {.importc.} + proc glGetColorTableParameterivSGI(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glFramebufferTexture2DOES(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glClearDepthxOES(depth: GLfixed) {.importc.} + proc glDisableClientStateiEXT(`array`: GLenum, index: GLuint) {.importc.} + proc glWindowPos2dARB(x: GLdouble, y: GLdouble) {.importc.} + proc glVertexAttrib1fvNV(index: GLuint, v: ptr GLfloat) {.importc.} + proc glDepthRangedNV(zNear: GLdouble, zFar: GLdouble) {.importc.} + proc glClear(mask: GLbitfield) {.importc.} + proc glUnmapTexture2DINTEL(texture: GLuint, level: GLint) {.importc.} + proc glSecondaryColor3ub(red: GLubyte, green: GLubyte, blue: GLubyte) {.importc.} + proc glVertexAttribI4bv(index: GLuint, v: ptr GLbyte) {.importc.} + proc glTexRenderbufferNV(target: GLenum, renderbuffer: GLuint) {.importc.} + proc glColor4ubVertex3fvSUN(c: ptr GLubyte, v: ptr GLfloat) {.importc.} + proc glVertexAttrib2svNV(index: GLuint, v: ptr GLshort) {.importc.} + proc glMultiTexCoord1ivARB(target: GLenum, v: ptr GLint) {.importc.} + proc glUniformMatrix3x2dv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glVertexAttribL3dvEXT(index: GLuint, v: ptr GLdouble) {.importc.} + proc glMultiTexSubImage1DEXT(texunit: GLenum, target: GLenum, level: GLint, xoffset: GLint, width: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glGetBufferPointervARB(target: GLenum, pname: GLenum, params: ptr pointer) {.importc.} + proc glGetMultiTexLevelParameterfvEXT(texunit: GLenum, target: GLenum, level: GLint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glMultiTexParameterIuivEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLuint) {.importc.} + proc glGetShaderSource(shader: GLuint, bufSize: GLsizei, length: ptr GLsizei, source: cstring) {.importc.} + proc glStencilFunc(fun: GLenum, `ref`: GLint, mask: GLuint) {.importc.} + proc glVertexAttribI4bvEXT(index: GLuint, v: ptr GLbyte) {.importc.} + proc glVertexAttrib4NuivARB(index: GLuint, v: ptr GLuint) {.importc.} + proc glIsObjectBufferATI(buffer: GLuint): GLboolean {.importc.} + proc glRasterPos2xOES(x: GLfixed, y: GLfixed) {.importc.} + proc glIsFenceNV(fence: GLuint): GLboolean {.importc.} + proc glGetFramebufferParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glClearBufferfv(buffer: GLenum, drawbuffer: GLint, value: ptr GLfloat) {.importc.} + proc glClearColorxOES(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed) {.importc.} + proc glVertexWeightfEXT(weight: GLfloat) {.importc.} + proc glExtIsProgramBinaryQCOM(program: GLuint): GLboolean {.importc.} + proc glTextureStorage2DMultisampleEXT(texture: GLuint, target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei, fixedsamplelocations: GLboolean) {.importc.} + proc glGetHistogramParameterxvOES(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glVertexAttrib4dNV(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glGetPerfMonitorCounterStringAMD(group: GLuint, counter: GLuint, bufSize: GLsizei, length: ptr GLsizei, counterString: cstring) {.importc.} + proc glMultiTexCoord2sARB(target: GLenum, s: GLshort, t: GLshort) {.importc.} + proc glSpriteParameterivSGIX(pname: GLenum, params: ptr GLint) {.importc.} + proc glCompressedTextureImage3DEXT(texture: GLuint, target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, imageSize: GLsizei, bits: pointer) {.importc.} + proc glBufferSubData(target: GLenum, offset: GLintptr, size: GLsizeiptr, data: pointer) {.importc.} + proc glBlendParameteriNV(pname: GLenum, value: GLint) {.importc.} + proc glVertexAttrib2fvNV(index: GLuint, v: ptr GLfloat) {.importc.} + proc glGetVariantBooleanvEXT(id: GLuint, value: GLenum, data: ptr GLboolean) {.importc.} + proc glProgramParameteri(program: GLuint, pname: GLenum, value: GLint) {.importc.} + proc glGetLocalConstantIntegervEXT(id: GLuint, value: GLenum, data: ptr GLint) {.importc.} + proc glFragmentMaterialiSGIX(face: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glGetNamedStringivARB(namelen: GLint, name: cstring, pname: GLenum, params: ptr GLint) {.importc.} + proc glBinormal3ivEXT(v: ptr GLint) {.importc.} + proc glCheckFramebufferStatusEXT(target: GLenum): GLenum {.importc.} + proc glVertexAttrib1fNV(index: GLuint, x: GLfloat) {.importc.} + proc glNamedRenderbufferStorageEXT(renderbuffer: GLuint, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glPresentFrameKeyedNV(video_slot: GLuint, minPresentTime: GLuint64Ext, beginPresentTimeId: GLuint, presentDurationId: GLuint, `type`: GLenum, target0: GLenum, fill0: GLuint, key0: GLuint, target1: GLenum, fill1: GLuint, key1: GLuint) {.importc.} + proc glGetObjectParameterfvARB(obj: GLhandleArb, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertex3sv(v: ptr GLshort) {.importc.} + proc glColor4s(red: GLshort, green: GLshort, blue: GLshort, alpha: GLshort) {.importc.} + proc glGetQueryObjecti64vEXT(id: GLuint, pname: GLenum, params: ptr GLint64) {.importc.} + proc glEvalMesh2(mode: GLenum, i1: GLint, i2: GLint, j1: GLint, j2: GLint) {.importc.} + proc glBeginTransformFeedbackEXT(primitiveMode: GLenum) {.importc.} + proc glBufferAddressRangeNV(pname: GLenum, index: GLuint, address: GLuint64Ext, length: GLsizeiptr) {.importc.} + proc glPointParameterfvARB(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetActiveVaryingNV(program: GLuint, index: GLuint, bufSize: GLsizei, length: ptr GLsizei, size: ptr GLsizei, `type`: ptr GLenum, name: cstring) {.importc.} + proc glIndexMask(mask: GLuint) {.importc.} + proc glVertexAttribBinding(attribindex: GLuint, bindingindex: GLuint) {.importc.} + proc glDeleteFencesNV(n: GLsizei, fences: ptr GLuint) {.importc.} + proc glVertexAttribI4ubv(index: GLuint, v: ptr GLubyte) {.importc.} + proc glPathParameterfvNV(path: GLuint, pname: GLenum, value: ptr GLfloat) {.importc.} + proc glVertexStream3fATI(stream: GLenum, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glVertexAttribs4svNV(index: GLuint, count: GLsizei, v: ptr GLshort) {.importc.} + proc glVertexAttrib4sNV(index: GLuint, x: GLshort, y: GLshort, z: GLshort, w: GLshort) {.importc.} + proc glAlphaFragmentOp3ATI(op: GLenum, dst: GLuint, dstMod: GLuint, arg1: GLuint, arg1Rep: GLuint, arg1Mod: GLuint, arg2: GLuint, arg2Rep: GLuint, arg2Mod: GLuint, arg3: GLuint, arg3Rep: GLuint, arg3Mod: GLuint) {.importc.} + proc glGetHistogramParameterfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertexAttribL1ui64NV(index: GLuint, x: GLuint64Ext) {.importc.} + proc glVertexAttribs3fvNV(index: GLuint, count: GLsizei, v: ptr GLfloat) {.importc.} + proc glMultiTexCoord3ivARB(target: GLenum, v: ptr GLint) {.importc.} + proc glClipPlanefOES(plane: GLenum, equation: ptr GLfloat) {.importc.} + proc glVertex3s(x: GLshort, y: GLshort, z: GLshort) {.importc.} + proc glVertex3dv(v: ptr GLdouble) {.importc.} + proc glWeightPointerOES(size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glBindBufferBase(target: GLenum, index: GLuint, buffer: GLuint) {.importc.} + proc glIndexs(c: GLshort) {.importc.} + proc glTessellationFactorAMD(factor: GLfloat) {.importc.} + proc glColor4ubVertex3fSUN(r: GLubyte, g: GLubyte, b: GLubyte, a: GLubyte, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glPauseTransformFeedback() {.importc.} + proc glImageTransformParameterivHP(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glColor3dv(v: ptr GLdouble) {.importc.} + proc glRasterPos4sv(v: ptr GLshort) {.importc.} + proc glInvalidateTexSubImage(texture: GLuint, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei) {.importc.} + proc glNormalStream3bvATI(stream: GLenum, coords: ptr GLbyte) {.importc.} + proc glUniformMatrix2x4fv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glMinmax(target: GLenum, internalformat: GLenum, sink: GLboolean) {.importc.} + proc glGetProgramStageiv(program: GLuint, shadertype: GLenum, pname: GLenum, values: ptr GLint) {.importc.} + proc glScalex(x: GLfixed, y: GLfixed, z: GLfixed) {.importc.} + proc glTexBufferARB(target: GLenum, internalformat: GLenum, buffer: GLuint) {.importc.} + proc glDrawArraysIndirect(mode: GLenum, indirect: pointer) {.importc.} + proc glMatrixLoadTransposefEXT(mode: GLenum, m: ptr GLfloat) {.importc.} + proc glMultiTexCoord2f(target: GLenum, s: GLfloat, t: GLfloat) {.importc.} + proc glDrawRangeElements(mode: GLenum, start: GLuint, `end`: GLuint, count: GLsizei, `type`: GLenum, indices: pointer) {.importc.} + proc glVertexAttrib4NubARB(index: GLuint, x: GLubyte, y: GLubyte, z: GLubyte, w: GLubyte) {.importc.} + proc glMultiTexCoord4xvOES(texture: GLenum, coords: ptr GLfixed) {.importc.} + proc glVertexArrayVertexAttribOffsetEXT(vaobj: GLuint, buffer: GLuint, index: GLuint, size: GLint, `type`: GLenum, normalized: GLboolean, stride: GLsizei, offset: GLintptr) {.importc.} + proc glVertexAttribL1i64vNV(index: GLuint, v: ptr GLint64Ext) {.importc.} + proc glMapBuffer(target: GLenum, access: GLenum): pointer {.importc.} + proc glUniform1ui(location: GLint, v0: GLuint) {.importc.} + proc glGetPixelMapfv(map: GLenum, values: ptr GLfloat) {.importc.} + proc glTexImage2DMultisampleCoverageNV(target: GLenum, coverageSamples: GLsizei, colorSamples: GLsizei, internalFormat: GLint, width: GLsizei, height: GLsizei, fixedSampleLocations: GLboolean) {.importc.} + proc glUniform2ivARB(location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glVertexAttribI3ui(index: GLuint, x: GLuint, y: GLuint, z: GLuint) {.importc.} + proc glGetProgramResourceiv(program: GLuint, programInterface: GLenum, index: GLuint, propCount: GLsizei, props: ptr GLenum, bufSize: GLsizei, length: ptr GLsizei, params: ptr GLint) {.importc.} + proc glUniform4iv(location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glVertexAttrib3f(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glClientActiveVertexStreamATI(stream: GLenum) {.importc.} + proc glTexCoord4fColor4fNormal3fVertex4fvSUN(tc: ptr GLfloat, c: ptr GLfloat, n: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glColor3xvOES(components: ptr GLfixed) {.importc.} + proc glVertexPointerListIBM(size: GLint, `type`: GLenum, stride: GLint, `pointer`: ptr pointer, ptrstride: GLint) {.importc.} + proc glProgramEnvParameter4dARB(target: GLenum, index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glGetLocalConstantFloatvEXT(id: GLuint, value: GLenum, data: ptr GLfloat) {.importc.} + proc glTexCoordPointerEXT(size: GLint, `type`: GLenum, stride: GLsizei, count: GLsizei, `pointer`: pointer) {.importc.} + proc glTexCoordPointervINTEL(size: GLint, `type`: GLenum, `pointer`: ptr pointer) {.importc.} + proc glSelectPerfMonitorCountersAMD(monitor: GLuint, enable: GLboolean, group: GLuint, numCounters: GLint, counterList: ptr GLuint) {.importc.} + proc glVertexStream4svATI(stream: GLenum, coords: ptr GLshort) {.importc.} + proc glColor3ui(red: GLuint, green: GLuint, blue: GLuint) {.importc.} + proc glBindTransformFeedbackNV(target: GLenum, id: GLuint) {.importc.} + proc glDeformSGIX(mask: GLbitfield) {.importc.} + proc glDeformationMap3fSGIX(target: GLenum, u1: GLfloat, u2: GLfloat, ustride: GLint, uorder: GLint, v1: GLfloat, v2: GLfloat, vstride: GLint, vorder: GLint, w1: GLfloat, w2: GLfloat, wstride: GLint, worder: GLint, points: ptr GLfloat) {.importc.} + proc glNamedBufferSubDataEXT(buffer: GLuint, offset: GLintptr, size: GLsizeiptr, data: pointer) {.importc.} + proc glGetNamedProgramStringEXT(program: GLuint, target: GLenum, pname: GLenum, string: pointer) {.importc.} + proc glCopyPathNV(resultPath: GLuint, srcPath: GLuint) {.importc.} + proc glMapControlPointsNV(target: GLenum, index: GLuint, `type`: GLenum, ustride: GLsizei, vstride: GLsizei, uorder: GLint, vorder: GLint, packed: GLboolean, points: pointer) {.importc.} + proc glGetBufferParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glUnmapObjectBufferATI(buffer: GLuint) {.importc.} + proc glGetProgramResourceLocation(program: GLuint, programInterface: GLenum, name: cstring): GLint {.importc.} + proc glUniform4i64vNV(location: GLint, count: GLsizei, value: ptr GLint64Ext) {.importc.} + proc glImageTransformParameterfHP(target: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glArrayObjectATI(`array`: GLenum, size: GLint, `type`: GLenum, stride: GLsizei, buffer: GLuint, offset: GLuint) {.importc.} + proc glBindBufferRangeEXT(target: GLenum, index: GLuint, buffer: GLuint, offset: GLintptr, size: GLsizeiptr) {.importc.} + proc glVertexArrayVertexAttribFormatEXT(vaobj: GLuint, attribindex: GLuint, size: GLint, `type`: GLenum, normalized: GLboolean, relativeoffset: GLuint) {.importc.} + proc glBindRenderbufferEXT(target: GLenum, renderbuffer: GLuint) {.importc.} + proc glListParameteriSGIX(list: GLuint, pname: GLenum, param: GLint) {.importc.} + proc glProgramUniformMatrix2dvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glProgramUniform2i64vNV(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint64Ext) {.importc.} + proc glObjectPtrLabelKHR(`ptr`: ptr pointer, length: GLsizei, label: cstring) {.importc.} + proc glVertexAttribL1i64NV(index: GLuint, x: GLint64Ext) {.importc.} + proc glMultiTexBufferEXT(texunit: GLenum, target: GLenum, internalformat: GLenum, buffer: GLuint) {.importc.} + proc glCoverFillPathInstancedNV(numPaths: GLsizei, pathNameType: GLenum, paths: pointer, pathBase: GLuint, coverMode: GLenum, transformType: GLenum, transformValues: ptr GLfloat) {.importc.} + proc glGetVertexAttribIivEXT(index: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glLightf(light: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glGetMinmaxParameterfvEXT(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glUniform1d(location: GLint, x: GLdouble) {.importc.} + proc glLightiv(light: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexAttrib2dvNV(index: GLuint, v: ptr GLdouble) {.importc.} + proc glNormalP3ui(`type`: GLenum, coords: GLuint) {.importc.} + proc glFinalCombinerInputNV(variable: GLenum, input: GLenum, mapping: GLenum, componentUsage: GLenum) {.importc.} + proc glUniform1uiv(location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glValidateProgramARB(programObj: GLhandleArb) {.importc.} + proc glNormalPointer(`type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glProgramNamedParameter4fvNV(id: GLuint, len: GLsizei, name: ptr GLubyte, v: ptr GLfloat) {.importc.} + proc glGetBooleanv(pname: GLenum, params: ptr GLboolean) {.importc.} + proc glTangent3ivEXT(v: ptr GLint) {.importc.} + proc glTexImage3DMultisample(target: GLenum, samples: GLsizei, internalformat: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, fixedsamplelocations: GLboolean) {.importc.} + proc glGetUniformIndices(program: GLuint, uniformCount: GLsizei, uniformNames: cstringArray, uniformIndices: ptr GLuint) {.importc.} + proc glVDPAUInitNV(vdpDevice: pointer, getProcAddress: pointer) {.importc.} + proc glGetMinmaxParameterivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glMultiTexCoord2fvARB(target: GLenum, v: ptr GLfloat) {.importc.} + proc glProgramEnvParametersI4ivNV(target: GLenum, index: GLuint, count: GLsizei, params: ptr GLint) {.importc.} + proc glClearTexSubImage(texture: GLuint, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, `type`: GLenum, data: ptr pointer) {.importc.} + proc glRectxOES(x1: GLfixed, y1: GLfixed, x2: GLfixed, y2: GLfixed) {.importc.} + proc glBlendEquationOES(mode: GLenum) {.importc.} + proc glFramebufferTexture(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glGetInstrumentsSGIX(): GLint {.importc.} + proc glFramebufferParameteri(target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glPathCoverDepthFuncNV(fun: GLenum) {.importc.} + proc glGetTranslatedShaderSourceANGLE(shader: GLuint, bufsize: GLsizei, length: ptr GLsizei, source: cstring) {.importc.} + proc glIndexfv(c: ptr GLfloat) {.importc.} + proc glGetActiveUniformBlockName(program: GLuint, uniformBlockIndex: GLuint, bufSize: GLsizei, length: ptr GLsizei, uniformBlockName: cstring) {.importc.} + proc glNormal3s(nx: GLshort, ny: GLshort, nz: GLshort) {.importc.} + proc glColorFragmentOp3ATI(op: GLenum, dst: GLuint, dstMask: GLuint, dstMod: GLuint, arg1: GLuint, arg1Rep: GLuint, arg1Mod: GLuint, arg2: GLuint, arg2Rep: GLuint, arg2Mod: GLuint, arg3: GLuint, arg3Rep: GLuint, arg3Mod: GLuint) {.importc.} + proc glGetProgramResourceLocationIndex(program: GLuint, programInterface: GLenum, name: cstring): GLint {.importc.} + proc glGetBooleanIndexedvEXT(target: GLenum, index: GLuint, data: ptr GLboolean) {.importc.} + proc glGenPerfMonitorsAMD(n: GLsizei, monitors: ptr GLuint) {.importc.} + proc glDrawRangeElementsEXT(mode: GLenum, start: GLuint, `end`: GLuint, count: GLsizei, `type`: GLenum, indices: pointer) {.importc.} + proc glFramebufferTexture3D(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint, zoffset: GLint) {.importc.} + proc glGetTexParameterxvOES(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glCompileShaderIncludeARB(shader: GLuint, count: GLsizei, path: cstringArray, length: ptr GLint) {.importc.} + proc glGetMultiTexParameterfvEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glEvalPoint2(i: GLint, j: GLint) {.importc.} + proc glGetProgramivNV(id: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glProgramParameter4fNV(target: GLenum, index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glMultiTexParameterfvEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertexAttrib3svARB(index: GLuint, v: ptr GLshort) {.importc.} + proc glDrawElementArrayAPPLE(mode: GLenum, first: GLint, count: GLsizei) {.importc.} + proc glMultiTexCoord4x(texture: GLenum, s: GLfixed, t: GLfixed, r: GLfixed, q: GLfixed) {.importc.} + proc glUniformMatrix3dv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glVertexAttribPointerARB(index: GLuint, size: GLint, `type`: GLenum, normalized: GLboolean, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glProgramUniformMatrix3x4dv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glGetFloati_vEXT(pname: GLenum, index: GLuint, params: ptr GLfloat) {.importc.} + proc glGetObjectParameterivAPPLE(objectType: GLenum, name: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glPushGroupMarkerEXT(length: GLsizei, marker: cstring) {.importc.} + proc glProgramUniform4uivEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glReplacementCodeuiVertex3fSUN(rc: GLuint, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glTexSubImage1DEXT(target: GLenum, level: GLint, xoffset: GLint, width: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glProgramUniform1uivEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glGetFenceivNV(fence: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetnCompressedTexImageARB(target: GLenum, lod: GLint, bufSize: GLsizei, img: pointer) {.importc.} + proc glTexGenfOES(coord: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glVertexAttrib4dv(index: GLuint, v: ptr GLdouble) {.importc.} + proc glVertexAttribL1ui64vNV(index: GLuint, v: ptr GLuint64Ext) {.importc.} + proc glVertexAttrib4fvARB(index: GLuint, v: ptr GLfloat) {.importc.} + proc glDeleteVertexArraysOES(n: GLsizei, arrays: ptr GLuint) {.importc.} + proc glSamplerParameterIiv(sampler: GLuint, pname: GLenum, param: ptr GLint) {.importc.} + proc glMapGrid1d(un: GLint, u1: GLdouble, u2: GLdouble) {.importc.} + proc glTranslatexOES(x: GLfixed, y: GLfixed, z: GLfixed) {.importc.} + proc glCullFace(mode: GLenum) {.importc.} + proc glPrioritizeTextures(n: GLsizei, textures: ptr GLuint, priorities: ptr GLfloat) {.importc.} + proc glGetSeparableFilterEXT(target: GLenum, format: GLenum, `type`: GLenum, row: pointer, column: pointer, span: pointer) {.importc.} + proc glVertexAttrib4NubvARB(index: GLuint, v: ptr GLubyte) {.importc.} + proc glGetTransformFeedbackVaryingNV(program: GLuint, index: GLuint, location: ptr GLint) {.importc.} + proc glTexCoord4xOES(s: GLfixed, t: GLfixed, r: GLfixed, q: GLfixed) {.importc.} + proc glGetProgramEnvParameterdvARB(target: GLenum, index: GLuint, params: ptr GLdouble) {.importc.} + proc glWindowPos2ivMESA(v: ptr GLint) {.importc.} + proc glGlobalAlphaFactorfSUN(factor: GLfloat) {.importc.} + proc glNormalStream3fvATI(stream: GLenum, coords: ptr GLfloat) {.importc.} + proc glRasterPos4i(x: GLint, y: GLint, z: GLint, w: GLint) {.importc.} + proc glReleaseShaderCompiler() {.importc.} + proc glProgramUniformMatrix4fvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glCopyMultiTexImage1DEXT(texunit: GLenum, target: GLenum, level: GLint, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, border: GLint) {.importc.} + proc glColorTableParameterfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glSecondaryColor3bvEXT(v: ptr GLbyte) {.importc.} + proc glMap1xOES(target: GLenum, u1: GLfixed, u2: GLfixed, stride: GLint, order: GLint, points: GLfixed) {.importc.} + proc glVertexStream1svATI(stream: GLenum, coords: ptr GLshort) {.importc.} + proc glIsRenderbuffer(renderbuffer: GLuint): GLboolean {.importc.} + proc glPatchParameterfv(pname: GLenum, values: ptr GLfloat) {.importc.} + proc glProgramUniformMatrix4dvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glVertexAttrib4ubNV(index: GLuint, x: GLubyte, y: GLubyte, z: GLubyte, w: GLubyte) {.importc.} + proc glVertex2i(x: GLint, y: GLint) {.importc.} + proc glPushClientAttrib(mask: GLbitfield) {.importc.} + proc glDrawArraysEXT(mode: GLenum, first: GLint, count: GLsizei) {.importc.} + proc glCreateProgram(): GLuint {.importc.} + proc glPolygonStipple(mask: ptr GLubyte) {.importc.} + proc glGetColorTableEXT(target: GLenum, format: GLenum, `type`: GLenum, data: pointer) {.importc.} + proc glSharpenTexFuncSGIS(target: GLenum, n: GLsizei, points: ptr GLfloat) {.importc.} + proc glNamedFramebufferTextureEXT(framebuffer: GLuint, attachment: GLenum, texture: GLuint, level: GLint) {.importc.} + proc glWindowPos3fvMESA(v: ptr GLfloat) {.importc.} + proc glBinormal3iEXT(bx: GLint, by: GLint, bz: GLint) {.importc.} + proc glEnableClientStateiEXT(`array`: GLenum, index: GLuint) {.importc.} + proc glProgramUniform3iv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glProgramUniform1dEXT(program: GLuint, location: GLint, x: GLdouble) {.importc.} + proc glPollInstrumentsSGIX(marker_p: ptr GLint): GLint {.importc.} + proc glSecondaryColor3f(red: GLfloat, green: GLfloat, blue: GLfloat) {.importc.} + proc glDeleteTransformFeedbacks(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glCoverStrokePathInstancedNV(numPaths: GLsizei, pathNameType: GLenum, paths: pointer, pathBase: GLuint, coverMode: GLenum, transformType: GLenum, transformValues: ptr GLfloat) {.importc.} + proc glIsTextureHandleResidentARB(handle: GLuint64): GLboolean {.importc.} + proc glVariantsvEXT(id: GLuint, `addr`: ptr GLshort) {.importc.} + proc glTexCoordFormatNV(size: GLint, `type`: GLenum, stride: GLsizei) {.importc.} + proc glTexStorage3DEXT(target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei) {.importc.} + proc glUniform2ui(location: GLint, v0: GLuint, v1: GLuint) {.importc.} + proc glReplacementCodePointerSUN(`type`: GLenum, stride: GLsizei, `pointer`: ptr pointer) {.importc.} + proc glFramebufferTextureLayerARB(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint, layer: GLint) {.importc.} + proc glBinormal3dvEXT(v: ptr GLdouble) {.importc.} + proc glProgramUniform2ui64vNV(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glGetnConvolutionFilterARB(target: GLenum, format: GLenum, `type`: GLenum, bufSize: GLsizei, image: pointer) {.importc.} + proc glStopInstrumentsSGIX(marker: GLint) {.importc.} + proc glVertexAttrib1svNV(index: GLuint, v: ptr GLshort) {.importc.} + proc glVertexAttribs2fvNV(index: GLuint, count: GLsizei, v: ptr GLfloat) {.importc.} + proc glGetInternalformativ(target: GLenum, internalformat: GLenum, pname: GLenum, bufSize: GLsizei, params: ptr GLint) {.importc.} + proc glIsProgramPipelineEXT(pipeline: GLuint): GLboolean {.importc.} + proc glMatrixIndexubvARB(size: GLint, indices: ptr GLubyte) {.importc.} + proc glTexCoord4bOES(s: GLbyte, t: GLbyte, r: GLbyte, q: GLbyte) {.importc.} + proc glSecondaryColor3us(red: GLushort, green: GLushort, blue: GLushort) {.importc.} + proc glGlobalAlphaFactorubSUN(factor: GLubyte) {.importc.} + proc glNamedStringARB(`type`: GLenum, namelen: GLint, name: cstring, stringlen: GLint, string: cstring) {.importc.} + proc glGetAttachedShaders(program: GLuint, maxCount: GLsizei, count: ptr GLsizei, shaders: ptr GLuint) {.importc.} + proc glMatrixRotatefEXT(mode: GLenum, angle: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glVertexStream3ivATI(stream: GLenum, coords: ptr GLint) {.importc.} + proc glMatrixIndexuivARB(size: GLint, indices: ptr GLuint) {.importc.} + proc glMatrixRotatedEXT(mode: GLenum, angle: GLdouble, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glPathFogGenNV(genMode: GLenum) {.importc.} + proc glMultiTexCoord4hvNV(target: GLenum, v: ptr GLhalfNv) {.importc.} + proc glVertexAttribIPointer(index: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glMultiTexCoord3bOES(texture: GLenum, s: GLbyte, t: GLbyte, r: GLbyte) {.importc.} + proc glResizeBuffersMESA() {.importc.} + proc glPrimitiveRestartIndexNV(index: GLuint) {.importc.} + proc glProgramUniform4f(program: GLuint, location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat) {.importc.} + proc glColor4ubVertex2fSUN(r: GLubyte, g: GLubyte, b: GLubyte, a: GLubyte, x: GLfloat, y: GLfloat) {.importc.} + proc glGetColorTableParameterivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glDepthRangef(n: GLfloat, f: GLfloat) {.importc.} + proc glVertexArrayVertexOffsetEXT(vaobj: GLuint, buffer: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glMatrixLoaddEXT(mode: GLenum, m: ptr GLdouble) {.importc.} + proc glVariantfvEXT(id: GLuint, `addr`: ptr GLfloat) {.importc.} + proc glReplacementCodeuiTexCoord2fVertex3fvSUN(rc: ptr GLuint, tc: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glSamplePatternSGIS(pattern: GLenum) {.importc.} + proc glProgramUniform3i64NV(program: GLuint, location: GLint, x: GLint64Ext, y: GLint64Ext, z: GLint64Ext) {.importc.} + proc glUniform3uivEXT(location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glGetImageTransformParameterivHP(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glPopMatrix() {.importc.} + proc glVertexAttrib3sARB(index: GLuint, x: GLshort, y: GLshort, z: GLshort) {.importc.} + proc glGenQueriesEXT(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glGetQueryObjectui64v(id: GLuint, pname: GLenum, params: ptr GLuint64) {.importc.} + proc glWeightusvARB(size: GLint, weights: ptr GLushort) {.importc.} + proc glWindowPos2sARB(x: GLshort, y: GLshort) {.importc.} + proc glGetTextureLevelParameterivEXT(texture: GLuint, target: GLenum, level: GLint, pname: GLenum, params: ptr GLint) {.importc.} + proc glBufferParameteriAPPLE(target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glMultiModeDrawArraysIBM(mode: ptr GLenum, first: ptr GLint, count: ptr GLsizei, primcount: GLsizei, modestride: GLint) {.importc.} + proc glUniformMatrix2x3fv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLfloat) {.importc.} + proc glTangentPointerEXT(`type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glResetMinmax(target: GLenum) {.importc.} + proc glVertexAttribP1uiv(index: GLuint, `type`: GLenum, normalized: GLboolean, value: ptr GLuint) {.importc.} + proc glPixelMapx(map: GLenum, size: GLint, values: ptr GLfixed) {.importc.} + proc glPixelStoref(pname: GLenum, param: GLfloat) {.importc.} + proc glBinormal3dEXT(bx: GLdouble, by: GLdouble, bz: GLdouble) {.importc.} + proc glVertexAttribs1hvNV(index: GLuint, n: GLsizei, v: ptr GLhalfNv) {.importc.} + proc glVertexAttrib4usvARB(index: GLuint, v: ptr GLushort) {.importc.} + proc glUnmapBuffer(target: GLenum): GLboolean {.importc.} + proc glFlushRasterSGIX() {.importc.} + proc glColor3uiv(v: ptr GLuint) {.importc.} + proc glInvalidateBufferSubData(buffer: GLuint, offset: GLintptr, length: GLsizeiptr) {.importc.} + proc glPassThroughxOES(token: GLfixed) {.importc.} + proc glLockArraysEXT(first: GLint, count: GLsizei) {.importc.} + proc glStencilFuncSeparateATI(frontfunc: GLenum, backfunc: GLenum, `ref`: GLint, mask: GLuint) {.importc.} + proc glProgramUniform3dvEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glGenTransformFeedbacks(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glCopyTexSubImage3DOES(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glIsNamedBufferResidentNV(buffer: GLuint): GLboolean {.importc.} + proc glSampleMaskIndexedNV(index: GLuint, mask: GLbitfield) {.importc.} + proc glVDPAUSurfaceAccessNV(surface: GLvdpauSurfaceNv, access: GLenum) {.importc.} + proc glProgramUniform3dv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLdouble) {.importc.} + proc glDeleteProgram(program: GLuint) {.importc.} + proc glConvolutionFilter1D(target: GLenum, internalformat: GLenum, width: GLsizei, format: GLenum, `type`: GLenum, image: pointer) {.importc.} + proc glVertex2f(x: GLfloat, y: GLfloat) {.importc.} + proc glWindowPos4dvMESA(v: ptr GLdouble) {.importc.} + proc glColor4us(red: GLushort, green: GLushort, blue: GLushort, alpha: GLushort) {.importc.} + proc glColorMask(red: GLboolean, green: GLboolean, blue: GLboolean, alpha: GLboolean) {.importc.} + proc glGetTexEnviv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glProgramUniform3ivEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glSecondaryColor3i(red: GLint, green: GLint, blue: GLint) {.importc.} + proc glGetSamplerParameteriv(sampler: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glDeleteFramebuffersEXT(n: GLsizei, framebuffers: ptr GLuint) {.importc.} + proc glCompressedTexSubImage3D(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, imageSize: GLsizei, data: pointer) {.importc.} + proc glVertex2s(x: GLshort, y: GLshort) {.importc.} + proc glIsQuery(id: GLuint): GLboolean {.importc.} + proc glFogxv(pname: GLenum, param: ptr GLfixed) {.importc.} + proc glAreProgramsResidentNV(n: GLsizei, programs: ptr GLuint, residences: ptr GLboolean): GLboolean {.importc.} + proc glShaderSourceARB(shaderObj: GLhandleArb, count: GLsizei, string: cstringArray, length: ptr GLint) {.importc.} + proc glPointSizexOES(size: GLfixed) {.importc.} + proc glPixelTransferf(pname: GLenum, param: GLfloat) {.importc.} + proc glExtractComponentEXT(res: GLuint, src: GLuint, num: GLuint) {.importc.} + proc glUniform1fv(location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glGetNamedStringARB(namelen: GLint, name: cstring, bufSize: GLsizei, stringlen: ptr GLint, string: cstring) {.importc.} + proc glGetProgramBinaryOES(program: GLuint, bufSize: GLsizei, length: ptr GLsizei, binaryFormat: ptr GLenum, binary: pointer) {.importc.} + proc glDeleteOcclusionQueriesNV(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glEnableClientState(`array`: GLenum) {.importc.} + proc glProgramBufferParametersIuivNV(target: GLenum, bindingIndex: GLuint, wordIndex: GLuint, count: GLsizei, params: ptr GLuint) {.importc.} + proc glProgramUniform2ui(program: GLuint, location: GLint, v0: GLuint, v1: GLuint) {.importc.} + proc glReplacementCodeuiSUN(code: GLuint) {.importc.} + proc glMultMatrixd(m: ptr GLdouble) {.importc.} + proc glInvalidateSubFramebuffer(target: GLenum, numAttachments: GLsizei, attachments: ptr GLenum, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glGenerateMultiTexMipmapEXT(texunit: GLenum, target: GLenum) {.importc.} + proc glDepthRangex(n: GLfixed, f: GLfixed) {.importc.} + proc glGetInteger64i_v(target: GLenum, index: GLuint, data: ptr GLint64) {.importc.} + proc glDrawBuffers(n: GLsizei, bufs: ptr GLenum) {.importc.} + proc glGetPointervEXT(pname: GLenum, params: ptr pointer) {.importc.} + proc glFogxvOES(pname: GLenum, param: ptr GLfixed) {.importc.} + proc glTexCoordP2uiv(`type`: GLenum, coords: ptr GLuint) {.importc.} + proc glVertexFormatNV(size: GLint, `type`: GLenum, stride: GLsizei) {.importc.} + proc glColorPointervINTEL(size: GLint, `type`: GLenum, `pointer`: ptr pointer) {.importc.} + proc glGetMultiTexParameterivEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glMultiTexCoordP4uiv(texture: GLenum, `type`: GLenum, coords: ptr GLuint) {.importc.} + proc glResetMinmaxEXT(target: GLenum) {.importc.} + proc glCopyBufferSubData(readTarget: GLenum, writeTarget: GLenum, readOffset: GLintptr, writeOffset: GLintptr, size: GLsizeiptr) {.importc.} + proc glSecondaryColor3sv(v: ptr GLshort) {.importc.} + proc glPixelStorex(pname: GLenum, param: GLfixed) {.importc.} + proc glWaitSync(sync: GLsync, flags: GLbitfield, timeout: GLuint64) {.importc.} + proc glVertexAttribI1iv(index: GLuint, v: ptr GLint) {.importc.} + proc glColorSubTableEXT(target: GLenum, start: GLsizei, count: GLsizei, format: GLenum, `type`: GLenum, data: pointer) {.importc.} + proc glGetDoublev(pname: GLenum, params: ptr GLdouble) {.importc.} + proc glMultiTexParameterivEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glMultiTexCoord4svARB(target: GLenum, v: ptr GLshort) {.importc.} + proc glColorPointerListIBM(size: GLint, `type`: GLenum, stride: GLint, `pointer`: ptr pointer, ptrstride: GLint) {.importc.} + proc glScissorIndexed(index: GLuint, left: GLint, bottom: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glStencilOpSeparateATI(face: GLenum, sfail: GLenum, dpfail: GLenum, dppass: GLenum) {.importc.} + proc glLoadName(name: GLuint) {.importc.} + proc glIsTransformFeedbackNV(id: GLuint): GLboolean {.importc.} + proc glPopDebugGroup() {.importc.} + proc glClipPlanef(p: GLenum, eqn: ptr GLfloat) {.importc.} + proc glDeleteFencesAPPLE(n: GLsizei, fences: ptr GLuint) {.importc.} + proc glGetQueryObjecti64v(id: GLuint, pname: GLenum, params: ptr GLint64) {.importc.} + proc glAlphaFunc(fun: GLenum, `ref`: GLfloat) {.importc.} + proc glIndexPointerEXT(`type`: GLenum, stride: GLsizei, count: GLsizei, `pointer`: pointer) {.importc.} + proc glVertexAttribI3ivEXT(index: GLuint, v: ptr GLint) {.importc.} + proc glIndexub(c: GLubyte) {.importc.} + proc glVertexP2uiv(`type`: GLenum, value: ptr GLuint) {.importc.} + proc glProgramUniform1uiv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glDebugMessageInsertKHR(source: GLenum, `type`: GLenum, id: GLuint, severity: GLenum, length: GLsizei, buf: cstring) {.importc.} + proc glColor4b(red: GLbyte, green: GLbyte, blue: GLbyte, alpha: GLbyte) {.importc.} + proc glRenderbufferStorageMultisampleAPPLE(target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glMinSampleShading(value: GLfloat) {.importc.} + proc glBindProgramNV(target: GLenum, id: GLuint) {.importc.} + proc glWindowPos3dMESA(x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glEdgeFlagPointer(stride: GLsizei, `pointer`: pointer) {.importc.} + proc glGetFragDataIndex(program: GLuint, name: cstring): GLint {.importc.} + proc glTexCoord3hNV(s: GLhalfNv, t: GLhalfNv, r: GLhalfNv) {.importc.} + proc glMultiDrawArraysIndirectAMD(mode: GLenum, indirect: pointer, primcount: GLsizei, stride: GLsizei) {.importc.} + proc glFragmentColorMaterialSGIX(face: GLenum, mode: GLenum) {.importc.} + proc glTexGenf(coord: GLenum, pname: GLenum, param: GLfloat) {.importc.} + proc glVertexAttrib4ubvARB(index: GLuint, v: ptr GLubyte) {.importc.} + proc glClearBufferiv(buffer: GLenum, drawbuffer: GLint, value: ptr GLint) {.importc.} + proc glGenQueriesARB(n: GLsizei, ids: ptr GLuint) {.importc.} + proc glRectdv(v1: ptr GLdouble, v2: ptr GLdouble) {.importc.} + proc glBlendEquationSeparateEXT(modeRgb: GLenum, modeAlpha: GLenum) {.importc.} + proc glTestFenceAPPLE(fence: GLuint): GLboolean {.importc.} + proc glTexGeniv(coord: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glPolygonMode(face: GLenum, mode: GLenum) {.importc.} + proc glFrameZoomSGIX(factor: GLint) {.importc.} + proc glReplacementCodeuiTexCoord2fVertex3fSUN(rc: GLuint, s: GLfloat, t: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glUniformSubroutinesuiv(shadertype: GLenum, count: GLsizei, indices: ptr GLuint) {.importc.} + proc glBeginQueryIndexed(target: GLenum, index: GLuint, id: GLuint) {.importc.} + proc glMultiTexGeniEXT(texunit: GLenum, coord: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glRasterPos3fv(v: ptr GLfloat) {.importc.} + proc glMapObjectBufferATI(buffer: GLuint): pointer {.importc.} + proc glIndexiv(c: ptr GLint) {.importc.} + proc glVertexAttribLPointer(index: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glMultiTexCoord4s(target: GLenum, s: GLshort, t: GLshort, r: GLshort, q: GLshort) {.importc.} + proc glSecondaryColorP3uiv(`type`: GLenum, color: ptr GLuint) {.importc.} + proc glNormalFormatNV(`type`: GLenum, stride: GLsizei) {.importc.} + proc glVertex4i(x: GLint, y: GLint, z: GLint, w: GLint) {.importc.} + proc glUniform1ui64NV(location: GLint, x: GLuint64Ext) {.importc.} + proc glScissorIndexedv(index: GLuint, v: ptr GLint) {.importc.} + proc glProgramUniform1i(program: GLuint, location: GLint, v0: GLint) {.importc.} + proc glCompressedMultiTexSubImage3DEXT(texunit: GLenum, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, imageSize: GLsizei, bits: pointer) {.importc.} + proc glFinishTextureSUNX() {.importc.} + proc glFramebufferTexture3DEXT(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint, zoffset: GLint) {.importc.} + proc glSetInvariantEXT(id: GLuint, `type`: GLenum, `addr`: pointer) {.importc.} + proc glGetTexParameterIivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glMultiTexCoordP3ui(texture: GLenum, `type`: GLenum, coords: GLuint) {.importc.} + proc glMultiTexCoord3f(target: GLenum, s: GLfloat, t: GLfloat, r: GLfloat) {.importc.} + proc glNormalStream3fATI(stream: GLenum, nx: GLfloat, ny: GLfloat, nz: GLfloat) {.importc.} + proc glActiveShaderProgram(pipeline: GLuint, program: GLuint) {.importc.} + proc glDisableVertexArrayEXT(vaobj: GLuint, `array`: GLenum) {.importc.} + proc glVertexAttribI3iv(index: GLuint, v: ptr GLint) {.importc.} + proc glProvokingVertex(mode: GLenum) {.importc.} + proc glTexCoord1fv(v: ptr GLfloat) {.importc.} + proc glVertexAttrib3fv(index: GLuint, v: ptr GLfloat) {.importc.} + proc glWindowPos3iv(v: ptr GLint) {.importc.} + proc glProgramUniform4ui64NV(program: GLuint, location: GLint, x: GLuint64Ext, y: GLuint64Ext, z: GLuint64Ext, w: GLuint64Ext) {.importc.} + proc glProgramUniform2d(program: GLuint, location: GLint, v0: GLdouble, v1: GLdouble) {.importc.} + proc glDebugMessageInsertARB(source: GLenum, `type`: GLenum, id: GLuint, severity: GLenum, length: GLsizei, buf: cstring) {.importc.} + proc glMultiTexSubImage3DEXT(texunit: GLenum, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glMap1d(target: GLenum, u1: GLdouble, u2: GLdouble, stride: GLint, order: GLint, points: ptr GLdouble) {.importc.} + proc glDeleteShader(shader: GLuint) {.importc.} + proc glTexturePageCommitmentEXT(texture: GLuint, target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, resident: GLboolean) {.importc.} + proc glFramebufferDrawBufferEXT(framebuffer: GLuint, mode: GLenum) {.importc.} + proc glTexCoord2fNormal3fVertex3fSUN(s: GLfloat, t: GLfloat, nx: GLfloat, ny: GLfloat, nz: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glDeleteProgramsNV(n: GLsizei, programs: ptr GLuint) {.importc.} + proc glPointAlongPathNV(path: GLuint, startSegment: GLsizei, numSegments: GLsizei, distance: GLfloat, x: ptr GLfloat, y: ptr GLfloat, tangentX: ptr GLfloat, tangentY: ptr GLfloat): GLboolean {.importc.} + proc glTexCoord1d(s: GLdouble) {.importc.} + proc glStencilStrokePathNV(path: GLuint, reference: GLint, mask: GLuint) {.importc.} + proc glQueryMatrixxOES(mantissa: ptr GLfixed, exponent: ptr GLint): GLbitfield {.importc.} + proc glGetNamedProgramLocalParameterIuivEXT(program: GLuint, target: GLenum, index: GLuint, params: ptr GLuint) {.importc.} + proc glGenerateMipmapOES(target: GLenum) {.importc.} + proc glRenderbufferStorageMultisampleIMG(target: GLenum, samples: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glVertexBlendEnviATI(pname: GLenum, param: GLint) {.importc.} + proc glPushAttrib(mask: GLbitfield) {.importc.} + proc glShaderOp3EXT(op: GLenum, res: GLuint, arg1: GLuint, arg2: GLuint, arg3: GLuint) {.importc.} + proc glEnableVertexAttribArray(index: GLuint) {.importc.} + proc glVertexAttrib4Nbv(index: GLuint, v: ptr GLbyte) {.importc.} + proc glExtGetBuffersQCOM(buffers: ptr GLuint, maxBuffers: GLint, numBuffers: ptr GLint) {.importc.} + proc glCopyTexSubImage3D(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glDeletePerfMonitorsAMD(n: GLsizei, monitors: ptr GLuint) {.importc.} + proc glGetTrackMatrixivNV(target: GLenum, address: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glEndConditionalRender() {.importc.} + proc glVertexAttribL3i64NV(index: GLuint, x: GLint64Ext, y: GLint64Ext, z: GLint64Ext) {.importc.} + proc glProgramLocalParametersI4ivNV(target: GLenum, index: GLuint, count: GLsizei, params: ptr GLint) {.importc.} + proc glFlush() {.importc.} + proc glGetNamedBufferParameterui64vNV(buffer: GLuint, pname: GLenum, params: ptr GLuint64Ext) {.importc.} + proc glGetVertexArrayIntegeri_vEXT(vaobj: GLuint, index: GLuint, pname: GLenum, param: ptr GLint) {.importc.} + proc glReadnPixelsEXT(x: GLint, y: GLint, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, bufSize: GLsizei, data: pointer) {.importc.} + proc glMultiTexImage1DEXT(texunit: GLenum, target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glGetVaryingLocationNV(program: GLuint, name: cstring): GLint {.importc.} + proc glMultiTexCoord4fvARB(target: GLenum, v: ptr GLfloat) {.importc.} + proc glMultiTexCoord3iv(target: GLenum, v: ptr GLint) {.importc.} + proc glVertexAttribL2dvEXT(index: GLuint, v: ptr GLdouble) {.importc.} + proc glTexParameterxOES(target: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glSecondaryColor3uivEXT(v: ptr GLuint) {.importc.} + proc glReadnPixelsARB(x: GLint, y: GLint, width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, bufSize: GLsizei, data: pointer) {.importc.} + proc glCopyTexSubImage1DEXT(target: GLenum, level: GLint, xoffset: GLint, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glGetDoublei_vEXT(pname: GLenum, index: GLuint, params: ptr GLdouble) {.importc.} + proc glVariantPointerEXT(id: GLuint, `type`: GLenum, stride: GLuint, `addr`: pointer) {.importc.} + proc glProgramUniform3ui64vNV(program: GLuint, location: GLint, count: GLsizei, value: ptr GLuint64Ext) {.importc.} + proc glTexCoord2fColor3fVertex3fvSUN(tc: ptr GLfloat, c: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glProgramUniform3fv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glBindFragDataLocationIndexed(program: GLuint, colorNumber: GLuint, index: GLuint, name: cstring) {.importc.} + proc glGetnSeparableFilterARB(target: GLenum, format: GLenum, `type`: GLenum, rowBufSize: GLsizei, row: pointer, columnBufSize: GLsizei, column: pointer, span: pointer) {.importc.} + proc glTextureParameteriEXT(texture: GLuint, target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glGetUniformuivEXT(program: GLuint, location: GLint, params: ptr GLuint) {.importc.} + proc glFragmentMaterialivSGIX(face: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glMultiTexCoord1svARB(target: GLenum, v: ptr GLshort) {.importc.} + proc glClientActiveTextureARB(texture: GLenum) {.importc.} + proc glVertexAttrib1fARB(index: GLuint, x: GLfloat) {.importc.} + proc glVertexAttrib4NbvARB(index: GLuint, v: ptr GLbyte) {.importc.} + proc glRasterPos2d(x: GLdouble, y: GLdouble) {.importc.} + proc glMultiTexCoord4iARB(target: GLenum, s: GLint, t: GLint, r: GLint, q: GLint) {.importc.} + proc glGetPixelTexGenParameterfvSGIS(pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertexAttribL2dv(index: GLuint, v: ptr GLdouble) {.importc.} + proc glGetProgramStringARB(target: GLenum, pname: GLenum, string: pointer) {.importc.} + proc glRasterPos2i(x: GLint, y: GLint) {.importc.} + proc glTexCoord2fColor4fNormal3fVertex3fvSUN(tc: ptr GLfloat, c: ptr GLfloat, n: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glMultiTexCoord3s(target: GLenum, s: GLshort, t: GLshort, r: GLshort) {.importc.} + proc glMultTransposeMatrixd(m: ptr GLdouble) {.importc.} + proc glActiveVaryingNV(program: GLuint, name: cstring) {.importc.} + proc glProgramUniform1f(program: GLuint, location: GLint, v0: GLfloat) {.importc.} + proc glGetActiveSubroutineName(program: GLuint, shadertype: GLenum, index: GLuint, bufsize: GLsizei, length: ptr GLsizei, name: cstring) {.importc.} + proc glClipPlanex(plane: GLenum, equation: ptr GLfixed) {.importc.} + proc glMultiTexCoord4iv(target: GLenum, v: ptr GLint) {.importc.} + proc glTransformFeedbackVaryingsEXT(program: GLuint, count: GLsizei, varyings: cstringArray, bufferMode: GLenum) {.importc.} + proc glBlendEquationSeparateiARB(buf: GLuint, modeRgb: GLenum, modeAlpha: GLenum) {.importc.} + proc glVertex2sv(v: ptr GLshort) {.importc.} + proc glAccumxOES(op: GLenum, value: GLfixed) {.importc.} + proc glProgramLocalParameter4dARB(target: GLenum, index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glIsRenderbufferEXT(renderbuffer: GLuint): GLboolean {.importc.} + proc glMultiDrawElementsIndirectAMD(mode: GLenum, `type`: GLenum, indirect: pointer, primcount: GLsizei, stride: GLsizei) {.importc.} + proc glVertexAttribI4uiEXT(index: GLuint, x: GLuint, y: GLuint, z: GLuint, w: GLuint) {.importc.} + proc glVertex4fv(v: ptr GLfloat) {.importc.} + proc glGenerateMipmapEXT(target: GLenum) {.importc.} + proc glVertexP3ui(`type`: GLenum, value: GLuint) {.importc.} + proc glTexCoord2dv(v: ptr GLdouble) {.importc.} + proc glFlushMappedBufferRange(target: GLenum, offset: GLintptr, length: GLsizeiptr) {.importc.} + proc glTrackMatrixNV(target: GLenum, address: GLuint, matrix: GLenum, transform: GLenum) {.importc.} + proc glFragmentLightModeliSGIX(pname: GLenum, param: GLint) {.importc.} + proc glVertexAttrib4Nusv(index: GLuint, v: ptr GLushort) {.importc.} + proc glScalef(x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glLightxvOES(light: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glTextureParameterivEXT(texture: GLuint, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glCompressedMultiTexImage3DEXT(texunit: GLenum, target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, imageSize: GLsizei, bits: pointer) {.importc.} + proc glVertexAttribL1d(index: GLuint, x: GLdouble) {.importc.} + proc glVertexAttrib3fARB(index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glVertexAttrib3hvNV(index: GLuint, v: ptr GLhalfNv) {.importc.} + proc glSpriteParameteriSGIX(pname: GLenum, param: GLint) {.importc.} + proc glFrustumxOES(l: GLfixed, r: GLfixed, b: GLfixed, t: GLfixed, n: GLfixed, f: GLfixed) {.importc.} + proc glGetnMapdvARB(target: GLenum, query: GLenum, bufSize: GLsizei, v: ptr GLdouble) {.importc.} + proc glGetMinmaxEXT(target: GLenum, reset: GLboolean, format: GLenum, `type`: GLenum, values: pointer) {.importc.} + proc glProgramUniformHandleui64NV(program: GLuint, location: GLint, value: GLuint64) {.importc.} + proc glWindowPos4fvMESA(v: ptr GLfloat) {.importc.} + proc glExtGetTexturesQCOM(textures: ptr GLuint, maxTextures: GLint, numTextures: ptr GLint) {.importc.} + proc glProgramSubroutineParametersuivNV(target: GLenum, count: GLsizei, params: ptr GLuint) {.importc.} + proc glSampleCoveragexOES(value: GLclampx, invert: GLboolean) {.importc.} + proc glMultiTexEnvivEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetFinalCombinerInputParameterfvNV(variable: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glLightModeliv(pname: GLenum, params: ptr GLint) {.importc.} + proc glUniform4f(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat) {.importc.} + proc glDepthRange(near: GLdouble, far: GLdouble) {.importc.} + proc glProgramUniformMatrix4x3dv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glProgramUniform4fv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glGetTexParameterIiv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexAttribs4dvNV(index: GLuint, count: GLsizei, v: ptr GLdouble) {.importc.} + proc glConvolutionParameteri(target: GLenum, pname: GLenum, params: GLint) {.importc.} + proc glVertexAttribI4uiv(index: GLuint, v: ptr GLuint) {.importc.} + proc glEvalCoord1dv(u: ptr GLdouble) {.importc.} + proc glIsFramebuffer(framebuffer: GLuint): GLboolean {.importc.} + proc glEvalCoord2d(u: GLdouble, v: GLdouble) {.importc.} + proc glClearDepthf(d: GLfloat) {.importc.} + proc glCompressedTexSubImage2D(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, width: GLsizei, height: GLsizei, format: GLenum, imageSize: GLsizei, data: pointer) {.importc.} + proc glProgramUniformMatrix3x2dvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glGetTexParameterxv(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glBinormal3fEXT(bx: GLfloat, by: GLfloat, bz: GLfloat) {.importc.} + proc glProgramParameteriARB(program: GLuint, pname: GLenum, value: GLint) {.importc.} + proc glWindowPos3ivMESA(v: ptr GLint) {.importc.} + proc glReplacementCodeuiColor4fNormal3fVertex3fvSUN(rc: ptr GLuint, c: ptr GLfloat, n: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glPresentFrameDualFillNV(video_slot: GLuint, minPresentTime: GLuint64Ext, beginPresentTimeId: GLuint, presentDurationId: GLuint, `type`: GLenum, target0: GLenum, fill0: GLuint, target1: GLenum, fill1: GLuint, target2: GLenum, fill2: GLuint, target3: GLenum, fill3: GLuint) {.importc.} + proc glIndexPointerListIBM(`type`: GLenum, stride: GLint, `pointer`: ptr pointer, ptrstride: GLint) {.importc.} + proc glVertexStream2dATI(stream: GLenum, x: GLdouble, y: GLdouble) {.importc.} + proc glUniformMatrix3x4dv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glMapNamedBufferRangeEXT(buffer: GLuint, offset: GLintptr, length: GLsizeiptr, access: GLbitfield): pointer {.importc.} + proc glColor4sv(v: ptr GLshort) {.importc.} + proc glStencilFillPathNV(path: GLuint, fillMode: GLenum, mask: GLuint) {.importc.} + proc glGetVertexAttribfvARB(index: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glWindowPos3dv(v: ptr GLdouble) {.importc.} + proc glHintPGI(target: GLenum, mode: GLint) {.importc.} + proc glVertexAttribs3hvNV(index: GLuint, n: GLsizei, v: ptr GLhalfNv) {.importc.} + proc glProgramUniform1i64NV(program: GLuint, location: GLint, x: GLint64Ext) {.importc.} + proc glReplacementCodeuiColor3fVertex3fSUN(rc: GLuint, r: GLfloat, g: GLfloat, b: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glUniform2iARB(location: GLint, v0: GLint, v1: GLint) {.importc.} + proc glViewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glBlendFuncSeparateIndexedAMD(buf: GLuint, srcRgb: GLenum, dstRgb: GLenum, srcAlpha: GLenum, dstAlpha: GLenum) {.importc.} + proc glColor3us(red: GLushort, green: GLushort, blue: GLushort) {.importc.} + proc glVertexAttrib2hvNV(index: GLuint, v: ptr GLhalfNv) {.importc.} + proc glGenerateMipmap(target: GLenum) {.importc.} + proc glGetProgramEnvParameterIuivNV(target: GLenum, index: GLuint, params: ptr GLuint) {.importc.} + proc glBlendEquationiARB(buf: GLuint, mode: GLenum) {.importc.} + proc glReadBufferNV(mode: GLenum) {.importc.} + proc glProvokingVertexEXT(mode: GLenum) {.importc.} + proc glPointParameterivNV(pname: GLenum, params: ptr GLint) {.importc.} + proc glBlitFramebufferANGLE(srcX0: GLint, srcY0: GLint, srcX1: GLint, srcY1: GLint, dstX0: GLint, dstY0: GLint, dstX1: GLint, dstY1: GLint, mask: GLbitfield, filter: GLenum) {.importc.} + proc glGetObjectParameterivARB(obj: GLhandleArb, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetSubroutineIndex(program: GLuint, shadertype: GLenum, name: cstring): GLuint {.importc.} + proc glMap2d(target: GLenum, u1: GLdouble, u2: GLdouble, ustride: GLint, uorder: GLint, v1: GLdouble, v2: GLdouble, vstride: GLint, vorder: GLint, points: ptr GLdouble) {.importc.} + proc glRectfv(v1: ptr GLfloat, v2: ptr GLfloat) {.importc.} + proc glDepthRangeArrayv(first: GLuint, count: GLsizei, v: ptr GLdouble) {.importc.} + proc glMultiTexParameteriEXT(texunit: GLenum, target: GLenum, pname: GLenum, param: GLint) {.importc.} + proc glTexStorageSparseAMD(target: GLenum, internalFormat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, layers: GLsizei, flags: GLbitfield) {.importc.} + proc glGenerateTextureMipmapEXT(texture: GLuint, target: GLenum) {.importc.} + proc glCopyConvolutionFilter1D(target: GLenum, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glVertex4d(x: GLdouble, y: GLdouble, z: GLdouble, w: GLdouble) {.importc.} + proc glGetPathParameterfvNV(path: GLuint, pname: GLenum, value: ptr GLfloat) {.importc.} + proc glDetachShader(program: GLuint, shader: GLuint) {.importc.} + proc glGetColorTableSGI(target: GLenum, format: GLenum, `type`: GLenum, table: pointer) {.importc.} + proc glPixelTransformParameterfvEXT(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glBufferSubDataARB(target: GLenum, offset: GLintPtrArb, size: GLsizeiptrArb, data: pointer) {.importc.} + proc glVertexAttrib4ubvNV(index: GLuint, v: ptr GLubyte) {.importc.} + proc glCopyTextureImage1DEXT(texture: GLuint, target: GLenum, level: GLint, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, border: GLint) {.importc.} + proc glGetQueryivARB(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glVertexAttribIPointerEXT(index: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glVertexAttribL3dEXT(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glGetQueryObjectui64vEXT(id: GLuint, pname: GLenum, params: ptr GLuint64) {.importc.} + proc glColor4x(red: GLfixed, green: GLfixed, blue: GLfixed, alpha: GLfixed) {.importc.} + proc glProgramUniformMatrix3x2dv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glVertexAttribI4i(index: GLuint, x: GLint, y: GLint, z: GLint, w: GLint) {.importc.} + proc glVertexAttrib1f(index: GLuint, x: GLfloat) {.importc.} + proc glUnmapBufferOES(target: GLenum): GLboolean {.importc.} + proc glVertexStream2ivATI(stream: GLenum, coords: ptr GLint) {.importc.} + proc glBeginOcclusionQueryNV(id: GLuint) {.importc.} + proc glVertex4sv(v: ptr GLshort) {.importc.} + proc glEnablei(target: GLenum, index: GLuint) {.importc.} + proc glUseProgramObjectARB(programObj: GLhandleArb) {.importc.} + proc glGetVertexAttribLdvEXT(index: GLuint, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glUniform2d(location: GLint, x: GLdouble, y: GLdouble) {.importc.} + proc glMinmaxEXT(target: GLenum, internalformat: GLenum, sink: GLboolean) {.importc.} + proc glTexImage3D(target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glGenSymbolsEXT(datatype: GLenum, storagetype: GLenum, range: GLenum, components: GLuint): GLuint {.importc.} + proc glVertexAttribI4svEXT(index: GLuint, v: ptr GLshort) {.importc.} + proc glProgramEnvParameter4dvARB(target: GLenum, index: GLuint, params: ptr GLdouble) {.importc.} + proc glProgramUniformMatrix4dv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glGetSamplerParameterfv(sampler: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glPopClientAttrib() {.importc.} + proc glHistogram(target: GLenum, width: GLsizei, internalformat: GLenum, sink: GLboolean) {.importc.} + proc glTexEnvfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glMultiTexCoord1dvARB(target: GLenum, v: ptr GLdouble) {.importc.} + proc glGetTexGenivOES(coord: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glUniform1ivARB(location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glTexCoord3fv(v: ptr GLfloat) {.importc.} + proc glVertex2xvOES(coords: ptr GLfixed) {.importc.} + proc glTexCoord4fVertex4fvSUN(tc: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glUniform2uiv(location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glMultiTexEnvfvEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glGetTextureParameterIivEXT(texture: GLuint, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glMemoryBarrierEXT(barriers: GLbitfield) {.importc.} + proc glGetTexParameterPointervAPPLE(target: GLenum, pname: GLenum, params: ptr pointer) {.importc.} + proc glWindowPos2svARB(v: ptr GLshort) {.importc.} + proc glEndQuery(target: GLenum) {.importc.} + proc glBlitFramebufferEXT(srcX0: GLint, srcY0: GLint, srcX1: GLint, srcY1: GLint, dstX0: GLint, dstY0: GLint, dstX1: GLint, dstY1: GLint, mask: GLbitfield, filter: GLenum) {.importc.} + proc glProgramEnvParametersI4uivNV(target: GLenum, index: GLuint, count: GLsizei, params: ptr GLuint) {.importc.} + proc glGetActiveUniform(program: GLuint, index: GLuint, bufSize: GLsizei, length: ptr GLsizei, size: ptr GLint, `type`: ptr GLenum, name: cstring) {.importc.} + proc glGenAsyncMarkersSGIX(range: GLsizei): GLuint {.importc.} + proc glClipControlARB(origin: GLenum, depth: GLenum) {.importc.} + proc glDrawElementsInstancedEXT(mode: GLenum, count: GLsizei, `type`: GLenum, indices: pointer, primcount: GLsizei) {.importc.} + proc glGetFragmentMaterialivSGIX(face: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glSwizzleEXT(res: GLuint, `in`: GLuint, outX: GLenum, outY: GLenum, outZ: GLenum, outW: GLenum) {.importc.} + proc glMultiTexCoord1bOES(texture: GLenum, s: GLbyte) {.importc.} + proc glProgramParameters4dvNV(target: GLenum, index: GLuint, count: GLsizei, v: ptr GLdouble) {.importc.} + proc glWindowPos2s(x: GLshort, y: GLshort) {.importc.} + proc glBlendFuncSeparatei(buf: GLuint, srcRgb: GLenum, dstRgb: GLenum, srcAlpha: GLenum, dstAlpha: GLenum) {.importc.} + proc glMultiModeDrawElementsIBM(mode: ptr GLenum, count: ptr GLsizei, `type`: GLenum, indices: ptr pointer, primcount: GLsizei, modestride: GLint) {.importc.} + proc glNormal3x(nx: GLfixed, ny: GLfixed, nz: GLfixed) {.importc.} + proc glProgramUniform1fvEXT(program: GLuint, location: GLint, count: GLsizei, value: ptr GLfloat) {.importc.} + proc glTexCoord2hNV(s: GLhalfNv, t: GLhalfNv) {.importc.} + proc glViewportIndexedfv(index: GLuint, v: ptr GLfloat) {.importc.} + proc glDrawTexxOES(x: GLfixed, y: GLfixed, z: GLfixed, width: GLfixed, height: GLfixed) {.importc.} + proc glProgramParameter4dvNV(target: GLenum, index: GLuint, v: ptr GLdouble) {.importc.} + proc glDeleteBuffers(n: GLsizei, buffers: ptr GLuint) {.importc.} + proc glGetVertexArrayIntegervEXT(vaobj: GLuint, pname: GLenum, param: ptr GLint) {.importc.} + proc glBindFragDataLocationEXT(program: GLuint, color: GLuint, name: cstring) {.importc.} + proc glGenProgramsNV(n: GLsizei, programs: ptr GLuint) {.importc.} + proc glMultiTexCoord1i(target: GLenum, s: GLint) {.importc.} + proc glCompressedTexImage3DOES(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, imageSize: GLsizei, data: pointer) {.importc.} + proc glGetQueryivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glExtGetBufferPointervQCOM(target: GLenum, params: ptr pointer) {.importc.} + proc glVertex3iv(v: ptr GLint) {.importc.} + proc glVertexAttribL1dvEXT(index: GLuint, v: ptr GLdouble) {.importc.} + proc glValidateProgramPipeline(pipeline: GLuint) {.importc.} + proc glBindVertexArray(`array`: GLuint) {.importc.} + proc glUniform2uiEXT(location: GLint, v0: GLuint, v1: GLuint) {.importc.} + proc glUniform3i(location: GLint, v0: GLint, v1: GLint, v2: GLint) {.importc.} + proc glGetVertexAttribIuiv(index: GLuint, pname: GLenum, params: ptr GLuint) {.importc.} + proc glVertexArrayParameteriAPPLE(pname: GLenum, param: GLint) {.importc.} + proc glVertexAttribL2i64NV(index: GLuint, x: GLint64Ext, y: GLint64Ext) {.importc.} + proc glTexGenivOES(coord: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glIsFramebufferOES(framebuffer: GLuint): GLboolean {.importc.} + proc glColor4ubv(v: ptr GLubyte) {.importc.} + proc glDeleteNamedStringARB(namelen: GLint, name: cstring) {.importc.} + proc glCopyConvolutionFilter1DEXT(target: GLenum, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei) {.importc.} + proc glBufferStorage(target: GLenum, size: GLsizeiptr, data: ptr pointer, flags: GLbitfield) {.importc.} + proc glDrawTexiOES(x: GLint, y: GLint, z: GLint, width: GLint, height: GLint) {.importc.} + proc glRasterPos3dv(v: ptr GLdouble) {.importc.} + proc glIndexMaterialEXT(face: GLenum, mode: GLenum) {.importc.} + proc glGetClipPlanex(plane: GLenum, equation: ptr GLfixed) {.importc.} + proc glIsVertexArrayOES(`array`: GLuint): GLboolean {.importc.} + proc glColorTableEXT(target: GLenum, internalFormat: GLenum, width: GLsizei, format: GLenum, `type`: GLenum, table: pointer) {.importc.} + proc glCompressedTexImage2D(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, border: GLint, imageSize: GLsizei, data: pointer) {.importc.} + proc glLightx(light: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glGetTexParameterfv(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertexAttrib4NsvARB(index: GLuint, v: ptr GLshort) {.importc.} + proc glInterleavedArrays(format: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glProgramLocalParameter4fARB(target: GLenum, index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {.importc.} + proc glPopDebugGroupKHR() {.importc.} + proc glVDPAUUnregisterSurfaceNV(surface: GLvdpauSurfaceNv) {.importc.} + proc glTexCoord1s(s: GLshort) {.importc.} + proc glFramebufferTexture2DMultisampleIMG(target: GLenum, attachment: GLenum, textarget: GLenum, texture: GLuint, level: GLint, samples: GLsizei) {.importc.} + proc glShaderBinary(count: GLsizei, shaders: ptr GLuint, binaryformat: GLenum, binary: pointer, length: GLsizei) {.importc.} + proc glVertexAttrib2dv(index: GLuint, v: ptr GLdouble) {.importc.} + proc glUniformMatrix4dv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glWeightivARB(size: GLint, weights: ptr GLint) {.importc.} + proc glGetMultiTexParameterIivEXT(texunit: GLenum, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glCopyConvolutionFilter2DEXT(target: GLenum, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {.importc.} + proc glSecondaryColor3hNV(red: GLhalfNv, green: GLhalfNv, blue: GLhalfNv) {.importc.} + proc glVertexAttrib1sv(index: GLuint, v: ptr GLshort) {.importc.} + proc glFrustumfOES(l: GLfloat, r: GLfloat, b: GLfloat, t: GLfloat, n: GLfloat, f: GLfloat) {.importc.} + proc glVertexStream2iATI(stream: GLenum, x: GLint, y: GLint) {.importc.} + proc glNormalStream3bATI(stream: GLenum, nx: GLbyte, ny: GLbyte, nz: GLbyte) {.importc.} + proc glVertexArrayTexCoordOffsetEXT(vaobj: GLuint, buffer: GLuint, size: GLint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glGetQueryiv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glTransformFeedbackStreamAttribsNV(count: GLsizei, attribs: ptr GLint, nbuffers: GLsizei, bufstreams: ptr GLint, bufferMode: GLenum) {.importc.} + proc glTextureStorage3DEXT(texture: GLuint, target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei) {.importc.} + proc glWindowPos3dvMESA(v: ptr GLdouble) {.importc.} + proc glUniform2uivEXT(location: GLint, count: GLsizei, value: ptr GLuint) {.importc.} + proc glTextureStorage2DEXT(texture: GLuint, target: GLenum, levels: GLsizei, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glVertexArrayMultiTexCoordOffsetEXT(vaobj: GLuint, buffer: GLuint, texunit: GLenum, size: GLint, `type`: GLenum, stride: GLsizei, offset: GLintptr) {.importc.} + proc glVertexStream1dvATI(stream: GLenum, coords: ptr GLdouble) {.importc.} + proc glCopyImageSubData(srcName: GLuint, srcTarget: GLenum, srcLevel: GLint, srcX: GLint, srcY: GLint, srcZ: GLint, dstName: GLuint, dstTarget: GLenum, dstLevel: GLint, dstX: GLint, dstY: GLint, dstZ: GLint, srcWidth: GLsizei, srcHeight: GLsizei, srcDepth: GLsizei) {.importc.} + proc glClearNamedBufferSubDataEXT(buffer: GLuint, internalformat: GLenum, format: GLenum, `type`: GLenum, offset: GLsizeiptr, size: GLsizeiptr, data: ptr pointer) {.importc.} + proc glBindBuffersRange(target: GLenum, first: GLuint, count: GLsizei, buffers: ptr GLuint, offsets: ptr GLintptr, sizes: ptr GLsizeiptr) {.importc.} + proc glGetVertexAttribIuivEXT(index: GLuint, pname: GLenum, params: ptr GLuint) {.importc.} + proc glLoadMatrixx(m: ptr GLfixed) {.importc.} + proc glTransformFeedbackVaryingsNV(program: GLuint, count: GLsizei, locations: ptr GLint, bufferMode: GLenum) {.importc.} + proc glUniform1i64vNV(location: GLint, count: GLsizei, value: ptr GLint64Ext) {.importc.} + proc glVertexArrayVertexAttribLFormatEXT(vaobj: GLuint, attribindex: GLuint, size: GLint, `type`: GLenum, relativeoffset: GLuint) {.importc.} + proc glClearBufferuiv(buffer: GLenum, drawbuffer: GLint, value: ptr GLuint) {.importc.} + proc glCombinerOutputNV(stage: GLenum, portion: GLenum, abOutput: GLenum, cdOutput: GLenum, sumOutput: GLenum, scale: GLenum, bias: GLenum, abDotProduct: GLboolean, cdDotProduct: GLboolean, muxSum: GLboolean) {.importc.} + proc glTexImage3DEXT(target: GLenum, level: GLint, internalformat: GLenum, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glPixelTransformParameterivEXT(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glActiveStencilFaceEXT(face: GLenum) {.importc.} + proc glCreateShaderObjectARB(shaderType: GLenum): GLhandleArb {.importc.} + proc glGetTextureParameterivEXT(texture: GLuint, target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glCopyTextureLevelsAPPLE(destinationTexture: GLuint, sourceTexture: GLuint, sourceBaseLevel: GLint, sourceLevelCount: GLsizei) {.importc.} + proc glVertexAttrib4Nuiv(index: GLuint, v: ptr GLuint) {.importc.} + proc glDrawPixels(width: GLsizei, height: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glWindowPos3dvARB(v: ptr GLdouble) {.importc.} + proc glProgramLocalParameterI4ivNV(target: GLenum, index: GLuint, params: ptr GLint) {.importc.} + proc glRasterPos4s(x: GLshort, y: GLshort, z: GLshort, w: GLshort) {.importc.} + proc glTexCoord2fVertex3fvSUN(tc: ptr GLfloat, v: ptr GLfloat) {.importc.} + proc glGetPathMetricsNV(metricQueryMask: GLbitfield, numPaths: GLsizei, pathNameType: GLenum, paths: pointer, pathBase: GLuint, stride: GLsizei, metrics: ptr GLfloat) {.importc.} + proc glMultiTexCoord4bOES(texture: GLenum, s: GLbyte, t: GLbyte, r: GLbyte, q: GLbyte) {.importc.} + proc glTextureBufferEXT(texture: GLuint, target: GLenum, internalformat: GLenum, buffer: GLuint) {.importc.} + proc glSecondaryColor3fv(v: ptr GLfloat) {.importc.} + proc glMultiTexCoord3fv(target: GLenum, v: ptr GLfloat) {.importc.} + proc glGetTexParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glMap2xOES(target: GLenum, u1: GLfixed, u2: GLfixed, ustride: GLint, uorder: GLint, v1: GLfixed, v2: GLfixed, vstride: GLint, vorder: GLint, points: GLfixed) {.importc.} + proc glFlushVertexArrayRangeAPPLE(length: GLsizei, `pointer`: pointer) {.importc.} + proc glActiveTextureARB(texture: GLenum) {.importc.} + proc glGetVertexAttribLi64vNV(index: GLuint, pname: GLenum, params: ptr GLint64Ext) {.importc.} + proc glNormal3bv(v: ptr GLbyte) {.importc.} + proc glCreateSyncFromCLeventARB(context: ptr ClContext, event: ptr ClContext, flags: GLbitfield): GLsync {.importc.} + proc glRenderbufferStorageEXT(target: GLenum, internalformat: GLenum, width: GLsizei, height: GLsizei) {.importc.} + proc glGetCompressedTextureImageEXT(texture: GLuint, target: GLenum, lod: GLint, img: pointer) {.importc.} + proc glColorFragmentOp2ATI(op: GLenum, dst: GLuint, dstMask: GLuint, dstMod: GLuint, arg1: GLuint, arg1Rep: GLuint, arg1Mod: GLuint, arg2: GLuint, arg2Rep: GLuint, arg2Mod: GLuint) {.importc.} + proc glPixelMapusv(map: GLenum, mapsize: GLsizei, values: ptr GLushort) {.importc.} + proc glGlobalAlphaFactorsSUN(factor: GLshort) {.importc.} + proc glTexParameterxv(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glEvalCoord2xOES(u: GLfixed, v: GLfixed) {.importc.} + proc glIsList(list: GLuint): GLboolean {.importc.} + proc glVertexAttrib3d(index: GLuint, x: GLdouble, y: GLdouble, z: GLdouble) {.importc.} + proc glSpriteParameterfSGIX(pname: GLenum, param: GLfloat) {.importc.} + proc glPathGlyphRangeNV(firstPathName: GLuint, fontTarget: GLenum, fontName: pointer, fontStyle: GLbitfield, firstGlyph: GLuint, numGlyphs: GLsizei, handleMissingGlyphs: GLenum, pathParameterTemplate: GLuint, emScale: GLfloat) {.importc.} + proc glUniform3iv(location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glClearBufferfi(buffer: GLenum, drawbuffer: GLint, depth: GLfloat, stencil: GLint) {.importc.} + proc glWindowPos3sMESA(x: GLshort, y: GLshort, z: GLshort) {.importc.} + proc glGetMapParameterfvNV(target: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glBindFragmentShaderATI(id: GLuint) {.importc.} + proc glTexCoord4s(s: GLshort, t: GLshort, r: GLshort, q: GLshort) {.importc.} + proc glGetMultiTexGenfvEXT(texunit: GLenum, coord: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glColorMaterial(face: GLenum, mode: GLenum) {.importc.} + proc glVertexAttribs1svNV(index: GLuint, count: GLsizei, v: ptr GLshort) {.importc.} + proc glEnableVertexAttribAPPLE(index: GLuint, pname: GLenum) {.importc.} + proc glGetDoubleIndexedvEXT(target: GLenum, index: GLuint, data: ptr GLdouble) {.importc.} + proc glOrthof(l: GLfloat, r: GLfloat, b: GLfloat, t: GLfloat, n: GLfloat, f: GLfloat) {.importc.} + proc glVertexBlendEnvfATI(pname: GLenum, param: GLfloat) {.importc.} + proc glUniformMatrix2x4dv(location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glPrioritizeTexturesxOES(n: GLsizei, textures: ptr GLuint, priorities: ptr GLfixed) {.importc.} + proc glGetTextureSamplerHandleNV(texture: GLuint, sampler: GLuint): GLuint64 {.importc.} + proc glDeleteVertexArrays(n: GLsizei, arrays: ptr GLuint) {.importc.} + proc glMultiTexCoord1xOES(texture: GLenum, s: GLfixed) {.importc.} + proc glGlobalAlphaFactorusSUN(factor: GLushort) {.importc.} + proc glGetConvolutionParameterxvOES(target: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glProgramUniform4fEXT(program: GLuint, location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat) {.importc.} + proc glProgramUniformMatrix3x4dvEXT(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + proc glBindVertexBuffer(bindingindex: GLuint, buffer: GLuint, offset: GLintptr, stride: GLsizei) {.importc.} + proc glGetHistogramParameteriv(target: GLenum, pname: GLenum, params: ptr GLint) {.importc.} + proc glGetShaderPrecisionFormat(shadertype: GLenum, precisiontype: GLenum, range: ptr GLint, precision: ptr GLint) {.importc.} + proc glTextureMaterialEXT(face: GLenum, mode: GLenum) {.importc.} + proc glEvalCoord2xvOES(coords: ptr GLfixed) {.importc.} + proc glWeightuivARB(size: GLint, weights: ptr GLuint) {.importc.} + proc glGetTextureLevelParameterfvEXT(texture: GLuint, target: GLenum, level: GLint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glVertexAttribP3uiv(index: GLuint, `type`: GLenum, normalized: GLboolean, value: ptr GLuint) {.importc.} + proc glProgramEnvParameterI4ivNV(target: GLenum, index: GLuint, params: ptr GLint) {.importc.} + proc glFogi(pname: GLenum, param: GLint) {.importc.} + proc glTexCoord1iv(v: ptr GLint) {.importc.} + proc glReplacementCodeuiColor4ubVertex3fvSUN(rc: ptr GLuint, c: ptr GLubyte, v: ptr GLfloat) {.importc.} + proc glProgramUniform1ui(program: GLuint, location: GLint, v0: GLuint) {.importc.} + proc glMultiTexCoord3d(target: GLenum, s: GLdouble, t: GLdouble, r: GLdouble) {.importc.} + proc glBeginVideoCaptureNV(video_capture_slot: GLuint) {.importc.} + proc glEvalCoord1f(u: GLfloat) {.importc.} + proc glMultiTexCoord1hvNV(target: GLenum, v: ptr GLhalfNv) {.importc.} + proc glSecondaryColor3sEXT(red: GLshort, green: GLshort, blue: GLshort) {.importc.} + proc glTextureImage3DEXT(texture: GLuint, target: GLenum, level: GLint, internalformat: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, border: GLint, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glCopyTexImage2D(target: GLenum, level: GLint, internalformat: GLenum, x: GLint, y: GLint, width: GLsizei, height: GLsizei, border: GLint) {.importc.} + proc glFinishFenceAPPLE(fence: GLuint) {.importc.} + proc glVertexArrayRangeNV(length: GLsizei, `pointer`: pointer) {.importc.} + proc glLightModelf(pname: GLenum, param: GLfloat) {.importc.} + proc glVertexAttribL1ui64ARB(index: GLuint, x: GLuint64Ext) {.importc.} + proc glPolygonOffset(factor: GLfloat, units: GLfloat) {.importc.} + proc glRasterPos4xOES(x: GLfixed, y: GLfixed, z: GLfixed, w: GLfixed) {.importc.} + proc glVertexAttrib3dvNV(index: GLuint, v: ptr GLdouble) {.importc.} + proc glBeginQuery(target: GLenum, id: GLuint) {.importc.} + proc glWeightfvARB(size: GLint, weights: ptr GLfloat) {.importc.} + proc glGetUniformuiv(program: GLuint, location: GLint, params: ptr GLuint) {.importc.} + proc glIsTextureEXT(texture: GLuint): GLboolean {.importc.} + proc glGetClipPlanef(plane: GLenum, equation: ptr GLfloat) {.importc.} + proc glTexGenxOES(coord: GLenum, pname: GLenum, param: GLfixed) {.importc.} + proc glFramebufferTextureFaceEXT(target: GLenum, attachment: GLenum, texture: GLuint, level: GLint, face: GLenum) {.importc.} + proc glDisableClientState(`array`: GLenum) {.importc.} + proc glTexPageCommitmentARB(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, resident: GLboolean) {.importc.} + proc glRasterPos4dv(v: ptr GLdouble) {.importc.} + proc glGetLightx(light: GLenum, pname: GLenum, params: ptr GLfixed) {.importc.} + proc glVertexAttrib1hvNV(index: GLuint, v: ptr GLhalfNv) {.importc.} + proc glMultiTexCoord2s(target: GLenum, s: GLshort, t: GLshort) {.importc.} + proc glProgramUniform2iv(program: GLuint, location: GLint, count: GLsizei, value: ptr GLint) {.importc.} + proc glGetListParameterivSGIX(list: GLuint, pname: GLenum, params: ptr GLint) {.importc.} + proc glColorFragmentOp1ATI(op: GLenum, dst: GLuint, dstMask: GLuint, dstMod: GLuint, arg1: GLuint, arg1Rep: GLuint, arg1Mod: GLuint) {.importc.} + proc glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN(rc: GLuint, s: GLfloat, t: GLfloat, r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat, nx: GLfloat, ny: GLfloat, nz: GLfloat, x: GLfloat, y: GLfloat, z: GLfloat) {.importc.} + proc glSampleMapATI(dst: GLuint, interp: GLuint, swizzle: GLenum) {.importc.} + proc glProgramUniform1d(program: GLuint, location: GLint, v0: GLdouble) {.importc.} + proc glBindAttribLocation(program: GLuint, index: GLuint, name: cstring) {.importc.} + proc glGetCombinerStageParameterfvNV(stage: GLenum, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glTexSubImage4DSGIS(target: GLenum, level: GLint, xoffset: GLint, yoffset: GLint, zoffset: GLint, woffset: GLint, width: GLsizei, height: GLsizei, depth: GLsizei, size4d: GLsizei, format: GLenum, `type`: GLenum, pixels: pointer) {.importc.} + proc glGetMapAttribParameterfvNV(target: GLenum, index: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glNewObjectBufferATI(size: GLsizei, `pointer`: pointer, usage: GLenum): GLuint {.importc.} + proc glWindowPos4iMESA(x: GLint, y: GLint, z: GLint, w: GLint) {.importc.} + proc glNewList(list: GLuint, mode: GLenum) {.importc.} + proc glUniform1i64NV(location: GLint, x: GLint64Ext) {.importc.} + proc glTexCoordP3ui(`type`: GLenum, coords: GLuint) {.importc.} + proc glEndQueryEXT(target: GLenum) {.importc.} + proc glGetVertexAttribLdv(index: GLuint, pname: GLenum, params: ptr GLdouble) {.importc.} + proc glStencilMask(mask: GLuint) {.importc.} + proc glVertexAttrib4sv(index: GLuint, v: ptr GLshort) {.importc.} + proc glRectsv(v1: ptr GLshort, v2: ptr GLshort) {.importc.} + proc glGetVariantArrayObjectfvATI(id: GLuint, pname: GLenum, params: ptr GLfloat) {.importc.} + proc glTexCoord3hvNV(v: ptr GLhalfNv) {.importc.} + proc glGetUniformdv(program: GLuint, location: GLint, params: ptr GLdouble) {.importc.} + proc glSecondaryColor3fvEXT(v: ptr GLfloat) {.importc.} + proc glAlphaFuncx(fun: GLenum, `ref`: GLfixed) {.importc.} + proc glVertexAttribPointerNV(index: GLuint, fsize: GLint, `type`: GLenum, stride: GLsizei, `pointer`: pointer) {.importc.} + proc glColorTable(target: GLenum, internalformat: GLenum, width: GLsizei, format: GLenum, `type`: GLenum, table: pointer) {.importc.} + proc glProgramUniformMatrix2x3dv(program: GLuint, location: GLint, count: GLsizei, transpose: GLboolean, value: ptr GLdouble) {.importc.} + + ## # GL_ARB_direct_state_access + proc glCreateTransformFeedbacks(n: GLsizei; ids: ptr GLuint) {.importc.} + proc glTransformFeedbackBufferBase(xfb: GLuint; index: GLuint; buffer: GLuint) {.importc.} + proc glTransformFeedbackBufferRange(xfb: GLuint; index: GLuint; buffer: GLuint; offset: GLintptr; size: GLsizeiptr) {.importc.} + proc glGetTransformFeedbackiv(xfb: GLuint; pname: GLenum; param: ptr GLint) {.importc.} + proc glGetTransformFeedbacki_v(xfb: GLuint; pname: GLenum; index: GLuint; param: ptr GLint) {.importc.} + proc glGetTransformFeedbacki64_v(xfb: GLuint; pname: GLenum; index: GLuint; param: ptr GLint64) {.importc.} + ## # Buffer object functions + + proc glCreateBuffers(n: GLsizei; buffers: ptr GLuint) {.importc.} + proc glNamedBufferStorage(buffer: GLuint; size: GLsizeiptr; data: pointer; flags: GLbitfield) {.importc.} + proc glNamedBufferData(buffer: GLuint; size: GLsizeiptr; data: pointer; usage: GLenum) {.importc.} + proc glNamedBufferSubData(buffer: GLuint; offset: GLintptr; size: GLsizeiptr; data: pointer) {.importc.} + proc glCopyNamedBufferSubData(readBuffer: GLuint; writeBuffer: GLuint; readOffset: GLintptr; writeOffset: GLintptr; size: GLsizeiptr) {.importc.} + proc glClearNamedBufferData(buffer: GLuint; internalformat: GLenum; format: GLenum; `type`: GLenum; data: pointer) {.importc.} + proc glClearNamedBufferSubData(buffer: GLuint; internalformat: GLenum; offset: GLintptr; size: GLsizeiptr; format: GLenum; `type`: GLenum; data: pointer) {.importc.} + proc glMapNamedBuffer(buffer: GLuint; access: GLenum): pointer {.importc.} + proc glMapNamedBufferRange(buffer: GLuint; offset: GLintptr; length: GLsizeiptr; access: GLbitfield): pointer {.importc.} + proc glUnmapNamedBuffer(buffer: GLuint): GLboolean {.importc.} + proc glFlushMappedNamedBufferRange(buffer: GLuint; offset: GLintptr; length: GLsizeiptr) {.importc.} + proc glGetNamedBufferParameteriv(buffer: GLuint; pname: GLenum; params: ptr GLint) {.importc.} + proc glGetNamedBufferParameteri64v(buffer: GLuint; pname: GLenum; params: ptr GLint64) {.importc.} + proc glGetNamedBufferPointerv(buffer: GLuint; pname: GLenum; params: ptr pointer) {.importc.} + proc glGetNamedBufferSubData(buffer: GLuint; offset: GLintptr; size: GLsizeiptr; data: pointer) {.importc.} + ## # Framebuffer object functions + + proc glCreateFramebuffers(n: GLsizei; framebuffers: ptr GLuint) {.importc.} + proc glNamedFramebufferRenderbuffer(framebuffer: GLuint; attachment: GLenum; renderbuffertarget: GLenum; renderbuffer: GLuint) {.importc.} + proc glNamedFramebufferParameteri(framebuffer: GLuint; pname: GLenum; param: GLint) {.importc.} + proc glNamedFramebufferTexture(framebuffer: GLuint; attachment: GLenum; texture: GLuint; level: GLint) {.importc.} + proc glNamedFramebufferTextureLayer(framebuffer: GLuint; attachment: GLenum; texture: GLuint; level: GLint; layer: GLint) {.importc.} + proc glNamedFramebufferDrawBuffer(framebuffer: GLuint; mode: GLenum) {.importc.} + proc glNamedFramebufferDrawBuffers(framebuffer: GLuint; n: GLsizei; bufs: ptr GLenum) {.importc.} + proc glNamedFramebufferReadBuffer(framebuffer: GLuint; mode: GLenum) {.importc.} + proc glInvalidateNamedFramebufferData(framebuffer: GLuint; numAttachments: GLsizei; attachments: ptr GLenum) {.importc.} + proc glInvalidateNamedFramebufferSubData(framebuffer: GLuint; numAttachments: GLsizei; attachments: ptr GLenum; x: GLint; y: GLint; width: GLsizei; height: GLsizei) {.importc.} + proc glClearNamedFramebufferiv(framebuffer: GLuint; buffer: GLenum; drawbuffer: GLint; value: ptr GLint) {.importc.} + proc glClearNamedFramebufferuiv(framebuffer: GLuint; buffer: GLenum; drawbuffer: GLint; value: ptr GLuint) {.importc.} + proc glClearNamedFramebufferfv(framebuffer: GLuint; buffer: GLenum; drawbuffer: GLint; value: ptr cfloat) {.importc.} + proc glClearNamedFramebufferfi(framebuffer: GLuint; buffer: GLenum; drawbuffer: GLint; depth: cfloat; stencil: GLint) {.importc.} + proc glBlitNamedFramebuffer(readFramebuffer: GLuint; drawFramebuffer: GLuint; srcX0: GLint; srcY0: GLint; srcX1: GLint; srcY1: GLint; dstX0: GLint; dstY0: GLint; dstX1: GLint; dstY1: GLint; mask: GLbitfield; filter: GLenum) {.importc.} + proc glCheckNamedFramebufferStatus(framebuffer: GLuint; target: GLenum): GLenum {.importc.} + proc glGetNamedFramebufferParameteriv(framebuffer: GLuint; pname: GLenum; param: ptr GLint) {.importc.} + proc glGetNamedFramebufferAttachmentParameteriv(framebuffer: GLuint; attachment: GLenum; pname: GLenum; params: ptr GLint) {.importc.} + ## # Renderbuffer object functions + + proc glCreateRenderbuffers(n: GLsizei; renderbuffers: ptr GLuint) {.importc.} + proc glNamedRenderbufferStorage(renderbuffer: GLuint; internalformat: GLenum; width: GLsizei; height: GLsizei) {.importc.} + proc glNamedRenderbufferStorageMultisample(renderbuffer: GLuint; samples: GLsizei; internalformat: GLenum; width: GLsizei; height: GLsizei) {.importc.} + proc glGetNamedRenderbufferParameteriv(renderbuffer: GLuint; pname: GLenum; params: ptr GLint) {.importc.} + ## # Texture object functions + + proc glCreateTextures(target: GLenum; n: GLsizei; textures: ptr GLuint) {.importc.} + proc glTextureBuffer(texture: GLuint; internalformat: GLenum; buffer: GLuint) {.importc.} + proc glTextureBufferRange(texture: GLuint; internalformat: GLenum; buffer: GLuint; offset: GLintptr; size: GLsizeiptr) {.importc.} + proc glTextureStorage1D(texture: GLuint; levels: GLsizei; internalformat: GLenum; width: GLsizei) {.importc.} + proc glTextureStorage2D(texture: GLuint; levels: GLsizei; internalformat: GLenum; width: GLsizei; height: GLsizei) {.importc.} + proc glTextureStorage3D(texture: GLuint; levels: GLsizei; internalformat: GLenum; width: GLsizei; height: GLsizei; depth: GLsizei) {.importc.} + proc glTextureStorage2DMultisample(texture: GLuint; samples: GLsizei; internalformat: GLenum; width: GLsizei; height: GLsizei; fixedsamplelocations: GLboolean) {.importc.} + proc glTextureStorage3DMultisample(texture: GLuint; samples: GLsizei; internalformat: GLenum; width: GLsizei; height: GLsizei; depth: GLsizei; fixedsamplelocations: GLboolean) {.importc.} + proc glTextureSubImage1D(texture: GLuint; level: GLint; xoffset: GLint; width: GLsizei; format: GLenum; `type`: GLenum; pixels: pointer) {.importc.} + proc glTextureSubImage2D(texture: GLuint; level: GLint; xoffset: GLint; yoffset: GLint; width: GLsizei; height: GLsizei; format: GLenum; `type`: GLenum; pixels: pointer) {.importc.} + proc glTextureSubImage3D(texture: GLuint; level: GLint; xoffset: GLint; yoffset: GLint; zoffset: GLint; width: GLsizei; height: GLsizei; depth: GLsizei; format: GLenum; `type`: GLenum; pixels: pointer) {.importc.} + proc glCompressedTextureSubImage1D(texture: GLuint; level: GLint; xoffset: GLint; width: GLsizei; format: GLenum; imageSize: GLsizei; data: pointer) {.importc.} + proc glCompressedTextureSubImage2D(texture: GLuint; level: GLint; xoffset: GLint; yoffset: GLint; width: GLsizei; height: GLsizei; format: GLenum; imageSize: GLsizei; data: pointer) {.importc.} + proc glCompressedTextureSubImage3D(texture: GLuint; level: GLint; xoffset: GLint; yoffset: GLint; zoffset: GLint; width: GLsizei; height: GLsizei; depth: GLsizei; format: GLenum; imageSize: GLsizei; data: pointer) {.importc.} + proc glCopyTextureSubImage1D(texture: GLuint; level: GLint; xoffset: GLint; x: GLint; y: GLint; width: GLsizei) {.importc.} + proc glCopyTextureSubImage2D(texture: GLuint; level: GLint; xoffset: GLint; yoffset: GLint; x: GLint; y: GLint; width: GLsizei; height: GLsizei) {.importc.} + proc glCopyTextureSubImage3D(texture: GLuint; level: GLint; xoffset: GLint; yoffset: GLint; zoffset: GLint; x: GLint; y: GLint; width: GLsizei; height: GLsizei) {.importc.} + proc glTextureParameterf(texture: GLuint; pname: GLenum; param: cfloat) {.importc.} + proc glTextureParameterfv(texture: GLuint; pname: GLenum; param: ptr cfloat) {.importc.} + proc glTextureParameteri(texture: GLuint; pname: GLenum; param: GLint) {.importc.} + proc glTextureParameterIiv(texture: GLuint; pname: GLenum; params: ptr GLint) {.importc.} + proc glTextureParameterIuiv(texture: GLuint; pname: GLenum; params: ptr GLuint) {.importc.} + proc glTextureParameteriv(texture: GLuint; pname: GLenum; param: ptr GLint) {.importc.} + proc glGenerateTextureMipmap(texture: GLuint) {.importc.} + proc glBindTextureUnit(unit: GLuint; texture: GLuint) {.importc.} + proc glGetTextureImage(texture: GLuint; level: GLint; format: GLenum; `type`: GLenum; bufSize: GLsizei; pixels: pointer) {.importc.} + + proc glGetCompressedTextureImage(texture: GLuint; level: GLint; bufSize: GLsizei; pixels: pointer) {.importc.} + proc glGetTextureLevelParameterfv(texture: GLuint; level: GLint; pname: GLenum; params: ptr cfloat) {.importc.} + proc glGetTextureLevelParameteriv(texture: GLuint; level: GLint; pname: GLenum; params: ptr GLint) {.importc.} + proc glGetTextureParameterfv(texture: GLuint; pname: GLenum; params: ptr cfloat) {.importc.} + proc glGetTextureParameterIiv(texture: GLuint; pname: GLenum; params: ptr GLint) {.importc.} + proc glGetTextureParameterIuiv(texture: GLuint; pname: GLenum; params: ptr GLuint) {.importc.} + proc glGetTextureParameteriv(texture: GLuint; pname: GLenum; params: ptr GLint) {.importc.} + ## # Vertex Array object functions + + proc glCreateVertexArrays(n: GLsizei; arrays: ptr GLuint) {.importc.} + proc glDisableVertexArrayAttrib(vaobj: GLuint; index: GLuint) {.importc.} + proc glEnableVertexArrayAttrib(vaobj: GLuint; index: GLuint) {.importc.} + proc glVertexArrayElementBuffer(vaobj: GLuint; buffer: GLuint) {.importc.} + proc glVertexArrayVertexBuffer(vaobj: GLuint; bindingindex: GLuint; buffer: GLuint; offset: GLintptr; stride: GLsizei) {.importc.} + proc glVertexArrayVertexBuffers(vaobj: GLuint; first: GLuint; count: GLsizei; buffers: ptr GLuint; offsets: ptr GLintptr; strides: ptr GLsizei) {.importc.} + proc glVertexArrayAttribFormat(vaobj: GLuint; attribindex: GLuint; size: GLint; `type`: GLenum; normalized: GLboolean; relativeoffset: GLuint) {.importc.} + proc glVertexArrayAttribIFormat(vaobj: GLuint; attribindex: GLuint; size: GLint; `type`: GLenum; relativeoffset: GLuint) {.importc.} + proc glVertexArrayAttribLFormat(vaobj: GLuint; attribindex: GLuint; size: GLint; `type`: GLenum; relativeoffset: GLuint) {.importc.} + proc glVertexArrayAttribBinding(vaobj: GLuint; attribindex: GLuint; bindingindex: GLuint) {.importc.} + proc glVertexArrayBindingDivisor(vaobj: GLuint; bindingindex: GLuint; divisor: GLuint) {.importc.} + proc glGetVertexArrayiv(vaobj: GLuint; pname: GLenum; param: ptr GLint) {.importc.} + proc glGetVertexArrayIndexediv(vaobj: GLuint; index: GLuint; pname: GLenum; param: ptr GLint) {.importc.} + proc glGetVertexArrayIndexed64iv(vaobj: GLuint; index: GLuint; pname: GLenum; param: ptr GLint64) {.importc.} + ## # Sampler object functions + + proc glCreateSamplers(n: GLsizei; samplers: ptr GLuint) {.importc.} + ## # Program Pipeline object functions + + proc glCreateProgramPipelines(n: GLsizei; pipelines: ptr GLuint) {.importc.} + ## # Query object functions + + proc glCreateQueries(target: GLenum; n: GLsizei; ids: ptr GLuint) {.importc.} + proc glGetQueryBufferObjectiv(id: GLuint; buffer: GLuint; pname: GLenum; offset: GLintptr) {.importc.} + proc glGetQueryBufferObjectuiv(id: GLuint; buffer: GLuint; pname: GLenum; offset: GLintptr) {.importc.} + proc glGetQueryBufferObjecti64v(id: GLuint; buffer: GLuint; pname: GLenum; offset: GLintptr) {.importc.} + proc glGetQueryBufferObjectui64v(id: GLuint; buffer: GLuint; pname: GLenum; offset: GLintptr) {.importc.} + +{.pop.} # stdcall, hint[XDeclaredButNotUsed]: off, warning[SmallLshouldNotBeUsed]: off. + +const + cGL_UNSIGNED_BYTE* = 0x1401.GLenum + cGL_UNSIGNED_SHORT* = 0x1403.GLenum + + GL_2X_BIT_ATI* = 0x00000001.GLbitfield + GL_MODELVIEW6_ARB* = 0x8726.GLenum + GL_CULL_FACE_MODE* = 0x0B45.GLenum + GL_TEXTURE_MAG_FILTER* = 0x2800.GLenum + GL_TRANSFORM_FEEDBACK_VARYINGS_EXT* = 0x8C83.GLenum + GL_PATH_JOIN_STYLE_NV* = 0x9079.GLenum + GL_FEEDBACK_BUFFER_SIZE* = 0x0DF1.GLenum + GL_FRAGMENT_LIGHT0_SGIX* = 0x840C.GLenum + GL_DRAW_BUFFER7_ARB* = 0x882C.GLenum + GL_POINT_SPRITE_OES* = 0x8861.GLenum + GL_INT_SAMPLER_RENDERBUFFER_NV* = 0x8E57.GLenum + GL_POST_CONVOLUTION_COLOR_TABLE_SGI* = 0x80D1.GLenum + GL_ZOOM_X* = 0x0D16.GLenum + GL_DRAW_FRAMEBUFFER_NV* = 0x8CA9.GLenum + GL_RGB_FLOAT16_ATI* = 0x881B.GLenum + GL_NUM_COMPRESSED_TEXTURE_FORMATS* = 0x86A2.GLenum + GL_LINE_STRIP* = 0x0003.GLenum + GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI* = 0x80D5.GLenum + GL_CURRENT_TIME_NV* = 0x8E28.GLenum + GL_FRAMEBUFFER_UNSUPPORTED* = 0x8CDD.GLenum + GL_PIXEL_TEX_GEN_Q_CEILING_SGIX* = 0x8184.GLenum + GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT* = 0x8C76.GLenum + GL_MAP_PERSISTENT_BIT* = 0x0040.GLbitfield + GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT* = 0x9056.GLenum + GL_CON_16_ATI* = 0x8951.GLenum + GL_DEPTH_BUFFER_BIT1_QCOM* = 0x00000200.GLbitfield + GL_TEXTURE30_ARB* = 0x84DE.GLenum + GL_SAMPLER_BUFFER* = 0x8DC2.GLenum + GL_MAX_COLOR_TEXTURE_SAMPLES* = 0x910E.GLenum + GL_DEPTH_STENCIL* = 0x84F9.GLenum + GL_C4F_N3F_V3F* = 0x2A26.GLenum + GL_ZOOM_Y* = 0x0D17.GLenum + GL_RGB10* = 0x8052.GLenum + GL_PRESERVE_ATI* = 0x8762.GLenum + GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB* = 0x8B4D.GLenum + GL_COLOR_ATTACHMENT12_NV* = 0x8CEC.GLenum + GL_GREEN_MAX_CLAMP_INGR* = 0x8565.GLenum + GL_CURRENT_VERTEX_ATTRIB* = 0x8626.GLenum + GL_TEXTURE_SHARED_SIZE* = 0x8C3F.GLenum + GL_NORMAL_ARRAY_TYPE* = 0x807E.GLenum + GL_DYNAMIC_READ* = 0x88E9.GLenum + GL_ALPHA4_EXT* = 0x803B.GLenum + GL_REPLACEMENT_CODE_ARRAY_SUN* = 0x85C0.GLenum + GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV* = 0x8852.GLenum + GL_MAX_VERTEX_ATTRIBS_ARB* = 0x8869.GLenum + GL_VIDEO_COLOR_CONVERSION_MIN_NV* = 0x902B.GLenum + GL_SOURCE3_RGB_NV* = 0x8583.GLenum + GL_ALPHA* = 0x1906.GLenum + GL_OUTPUT_TEXTURE_COORD16_EXT* = 0x87AD.GLenum + GL_BLEND_EQUATION_EXT* = 0x8009.GLenum + GL_BIAS_BIT_ATI* = 0x00000008.GLbitfield + GL_BLEND_EQUATION_RGB* = 0x8009.GLenum + GL_SHADER_BINARY_DMP* = 0x9250.GLenum + GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE* = 0x90C8.GLenum + GL_Z4Y12Z4CB12Z4CR12_444_NV* = 0x9037.GLenum + GL_READ_PIXELS_TYPE* = 0x828E.GLenum + GL_CONVOLUTION_HINT_SGIX* = 0x8316.GLenum + GL_TRANSPOSE_AFFINE_3D_NV* = 0x9098.GLenum + GL_PIXEL_MAP_B_TO_B* = 0x0C78.GLenum + GL_VERTEX_BLEND_ARB* = 0x86A7.GLenum + GL_LIGHT2* = 0x4002.GLenum + cGL_BYTE* = 0x1400.GLenum + GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS* = 0x92D3.GLenum + GL_DOMAIN* = 0x0A02.GLenum + GL_PROGRAM_NATIVE_TEMPORARIES_ARB* = 0x88A6.GLenum + GL_RELATIVE_CUBIC_CURVE_TO_NV* = 0x0D.GLenum + GL_TEXTURE_DEPTH_TYPE_ARB* = 0x8C16.GLenum + GL_STENCIL_BACK_PASS_DEPTH_PASS* = 0x8803.GLenum + GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV* = 0x8868.GLenum + GL_ATTRIB_STACK_DEPTH* = 0x0BB0.GLenum + GL_DEPTH_COMPONENT16_ARB* = 0x81A5.GLenum + GL_TESSELLATION_MODE_AMD* = 0x9004.GLenum + GL_UNSIGNED_INT8_VEC3_NV* = 0x8FEE.GLenum + GL_DOUBLE_VEC4* = 0x8FFE.GLenum + GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS* = 0x8E85.GLenum + GL_TEXTURE_GREEN_TYPE_ARB* = 0x8C11.GLenum + GL_PIXEL_PACK_BUFFER* = 0x88EB.GLenum + GL_VERTEX_WEIGHT_ARRAY_EXT* = 0x850C.GLenum + GL_HALF_FLOAT* = 0x140B.GLenum + GL_REG_0_ATI* = 0x8921.GLenum + GL_DEPTH_BUFFER_BIT4_QCOM* = 0x00001000.GLbitfield + GL_UNSIGNED_INT_5_9_9_9_REV_EXT* = 0x8C3E.GLenum + GL_DEPTH_COMPONENT16_SGIX* = 0x81A5.GLenum + GL_COMPRESSED_RGBA_ASTC_8x5_KHR* = 0x93B5.GLenum + GL_EDGE_FLAG_ARRAY_LENGTH_NV* = 0x8F30.GLenum + GL_CON_17_ATI* = 0x8952.GLenum + GL_PARAMETER_BUFFER_ARB* = 0x80EE.GLenum + GL_COLOR_ATTACHMENT6_EXT* = 0x8CE6.GLenum + GL_INDEX_ARRAY_EXT* = 0x8077.GLenum + GL_ALPHA_SCALE* = 0x0D1C.GLenum + GL_LINE_QUALITY_HINT_SGIX* = 0x835B.GLenum + GL_SLUMINANCE8* = 0x8C47.GLenum + GL_DEBUG_OUTPUT_KHR* = 0x92E0.GLenum + GL_TEXTURE_LIGHTING_MODE_HP* = 0x8167.GLenum + GL_SPOT_DIRECTION* = 0x1204.GLenum + GL_V3F* = 0x2A21.GLenum + GL_ALPHA16_EXT* = 0x803E.GLenum + GL_DRAW_BUFFER15_NV* = 0x8834.GLenum + GL_MIN_PROGRAM_TEXEL_OFFSET_EXT* = 0x8904.GLenum + GL_ACTIVE_VARYING_MAX_LENGTH_NV* = 0x8C82.GLenum + GL_COLOR_ATTACHMENT10* = 0x8CEA.GLenum + GL_COLOR_ARRAY_LIST_STRIDE_IBM* = 103082.GLenum + GL_TEXTURE_TARGET_QCOM* = 0x8BDA.GLenum + GL_DRAW_BUFFER12_ARB* = 0x8831.GLenum + GL_SAMPLE_MASK* = 0x8E51.GLenum + GL_TEXTURE_FORMAT_QCOM* = 0x8BD6.GLenum + GL_TEXTURE_COMPONENTS* = 0x1003.GLenum + GL_PROGRAM_PIPELINE_BINDING* = 0x825A.GLenum + GL_HIGH_INT* = 0x8DF5.GLenum + GL_MAP_INVALIDATE_BUFFER_BIT* = 0x0008.GLbitfield + GL_LAYOUT_LINEAR_CPU_CACHED_INTEL* = 2.GLenum + GL_TEXTURE_DS_SIZE_NV* = 0x871D.GLenum + GL_HALF_FLOAT_NV* = 0x140B.GLenum + GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE* = 0x80D5.GLenum + GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER* = 0x8A45.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR* = 0x93DB.GLenum + GL_REG_18_ATI* = 0x8933.GLenum + GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS* = 0x8266.GLenum + GL_UNPACK_FLIP_Y_WEBGL* = 0x9240.GLenum + GL_POLYGON_STIPPLE_BIT* = 0x00000010.GLbitfield + GL_MULTISAMPLE_BUFFER_BIT6_QCOM* = 0x40000000.GLbitfield + GL_ONE_MINUS_SRC_ALPHA* = 0x0303.GLenum + GL_RASTERIZER_DISCARD_EXT* = 0x8C89.GLenum + GL_BGRA_INTEGER* = 0x8D9B.GLenum + GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS* = 0x92CE.GLenum + GL_MODELVIEW1_EXT* = 0x850A.GLenum + GL_VERTEX_ELEMENT_SWIZZLE_AMD* = 0x91A4.GLenum + GL_MAP1_GRID_SEGMENTS* = 0x0DD1.GLenum + GL_PATH_ERROR_POSITION_NV* = 0x90AB.GLenum + GL_FOG_COORDINATE_ARRAY_EXT* = 0x8457.GLenum + GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI* = 0x8973.GLenum + GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB* = 0x880D.GLenum + GL_PATH_GEN_COLOR_FORMAT_NV* = 0x90B2.GLenum + GL_BUFFER_VARIABLE* = 0x92E5.GLenum + GL_PROXY_TEXTURE_CUBE_MAP_ARB* = 0x851B.GLenum + GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB* = 0x8E8D.GLenum + GL_TEXT_FRAGMENT_SHADER_ATI* = 0x8200.GLenum + GL_ALPHA_MAX_SGIX* = 0x8321.GLenum + GL_UNPACK_ALIGNMENT* = 0x0CF5.GLenum + GL_POST_COLOR_MATRIX_RED_SCALE* = 0x80B4.GLenum + GL_CIRCULAR_CW_ARC_TO_NV* = 0xFA.GLenum + GL_MAX_SAMPLES_APPLE* = 0x8D57.GLenum + GL_4PASS_3_SGIS* = 0x80A7.GLenum + GL_SAMPLER_3D_OES* = 0x8B5F.GLenum + GL_UNSIGNED_INT16_VEC2_NV* = 0x8FF1.GLenum + GL_UNSIGNED_INT_SAMPLER_1D_ARRAY* = 0x8DD6.GLenum + GL_REG_8_ATI* = 0x8929.GLenum + GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT* = 0x8366.GLenum + GL_QUERY_RESULT_AVAILABLE_EXT* = 0x8867.GLenum + GL_INTENSITY8_EXT* = 0x804B.GLenum + GL_OUTPUT_TEXTURE_COORD9_EXT* = 0x87A6.GLenum + GL_TEXTURE_BINDING_RECTANGLE_NV* = 0x84F6.GLenum + GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV* = 0x8853.GLenum + GL_IMAGE_FORMAT_COMPATIBILITY_TYPE* = 0x90C7.GLenum + GL_WRITE_ONLY* = 0x88B9.GLenum + GL_SAMPLER_1D_SHADOW* = 0x8B61.GLenum + GL_DISPATCH_INDIRECT_BUFFER_BINDING* = 0x90EF.GLenum + GL_VERTEX_PROGRAM_BINDING_NV* = 0x864A.GLenum + GL_RGB8_EXT* = 0x8051.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR* = 0x93D7.GLenum + GL_CON_5_ATI* = 0x8946.GLenum + GL_DUAL_INTENSITY8_SGIS* = 0x8119.GLenum + GL_MAX_SAMPLES_EXT* = 0x8D57.GLenum + GL_VERTEX_ARRAY_POINTER_EXT* = 0x808E.GLenum + GL_COMBINE_EXT* = 0x8570.GLenum + GL_MULTISAMPLE_BUFFER_BIT1_QCOM* = 0x02000000.GLbitfield + GL_MAGNITUDE_SCALE_NV* = 0x8712.GLenum + GL_SYNC_CONDITION_APPLE* = 0x9113.GLenum + GL_RGBA_S3TC* = 0x83A2.GLenum + GL_LINE_STIPPLE_REPEAT* = 0x0B26.GLenum + GL_TEXTURE_COMPRESSION_HINT* = 0x84EF.GLenum + GL_TEXTURE_COMPARE_MODE* = 0x884C.GLenum + GL_RGBA_FLOAT_MODE_ATI* = 0x8820.GLenum + GL_OPERAND0_RGB* = 0x8590.GLenum + GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV* = 0x870D.GLenum + GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI* = 0x80B5.GLenum + GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV* = 0x9033.GLenum + GL_UNPACK_ROW_LENGTH* = 0x0CF2.GLenum + GL_DOUBLE_MAT2_EXT* = 0x8F46.GLenum + GL_TEXTURE_GEQUAL_R_SGIX* = 0x819D.GLenum + GL_UNSIGNED_INT_8_24_REV_MESA* = 0x8752.GLenum + GL_DSDT8_NV* = 0x8709.GLenum + GL_RESAMPLE_DECIMATE_SGIX* = 0x8430.GLenum + GL_DEBUG_SOURCE_OTHER_KHR* = 0x824B.GLenum + GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB* = 0x8DA8.GLenum + GL_MAX_VERTEX_UNITS_OES* = 0x86A4.GLenum + GL_ISOLINES* = 0x8E7A.GLenum + GL_INCR_WRAP* = 0x8507.GLenum + GL_BUFFER_MAP_POINTER* = 0x88BD.GLenum + GL_INT_SAMPLER_CUBE_MAP_ARRAY* = 0x900E.GLenum + GL_UNSIGNED_INT_VEC2* = 0x8DC6.GLenum + GL_RENDERBUFFER_HEIGHT_OES* = 0x8D43.GLenum + GL_COMPRESSED_RGBA_ASTC_10x10_KHR* = 0x93BB.GLenum + GL_PIXEL_TEX_GEN_ALPHA_MS_SGIX* = 0x818A.GLenum + GL_LINEAR_SHARPEN_COLOR_SGIS* = 0x80AF.GLenum + GL_COLOR_ATTACHMENT5_EXT* = 0x8CE5.GLenum + GL_VERTEX_ATTRIB_ARRAY9_NV* = 0x8659.GLenum + GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING* = 0x889D.GLenum + GL_BLEND_DST_RGB* = 0x80C8.GLenum + GL_VERTEX_ARRAY_EXT* = 0x8074.GLenum + GL_VERTEX_ARRAY_RANGE_POINTER_NV* = 0x8521.GLenum + GL_DEBUG_SEVERITY_MEDIUM_ARB* = 0x9147.GLenum + GL_OPERAND0_ALPHA* = 0x8598.GLenum + GL_TEXTURE_BINDING_CUBE_MAP* = 0x8514.GLenum + GL_ADD_ATI* = 0x8963.GLenum + GL_AUX1* = 0x040A.GLenum + GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT* = 0x8210.GLenum + GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS* = 0x8CD9.GLenum + GL_MINUS_NV* = 0x929F.GLenum + GL_RGB4* = 0x804F.GLenum + GL_COMPRESSED_RGBA_ASTC_12x12_KHR* = 0x93BD.GLenum + GL_MAX_GEOMETRY_OUTPUT_VERTICES* = 0x8DE0.GLenum + GL_SURFACE_STATE_NV* = 0x86EB.GLenum + GL_COLOR_MATERIAL_FACE* = 0x0B55.GLenum + GL_TEXTURE18_ARB* = 0x84D2.GLenum + GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2_OES* = 0x9277.GLenum + GL_LOWER_LEFT* = 0x8CA1.GLenum + GL_DRAW_BUFFER8_ATI* = 0x882D.GLenum + GL_TEXTURE_CONSTANT_DATA_SUNX* = 0x81D6.GLenum + GL_SAMPLER_1D* = 0x8B5D.GLenum + GL_POLYGON_OFFSET_EXT* = 0x8037.GLenum + GL_EQUIV* = 0x1509.GLenum + GL_QUERY_BUFFER_BINDING* = 0x9193.GLenum + GL_COMBINE_ARB* = 0x8570.GLenum + GL_MATRIX0_NV* = 0x8630.GLenum + GL_CLAMP_TO_BORDER_SGIS* = 0x812D.GLint + GL_INTENSITY8UI_EXT* = 0x8D7F.GLenum + GL_TRACK_MATRIX_TRANSFORM_NV* = 0x8649.GLenum + GL_SURFACE_MAPPED_NV* = 0x8700.GLenum + GL_INT_VEC3_ARB* = 0x8B54.GLenum + GL_IMAGE_TRANSFORM_2D_HP* = 0x8161.GLenum + GL_PROGRAM_BINARY_RETRIEVABLE_HINT* = 0x8257.GLenum + GL_DRAW_BUFFER8_EXT* = 0x882D.GLenum + GL_DEPTH_STENCIL_EXT* = 0x84F9.GLenum + GL_CONTEXT_PROFILE_MASK* = 0x9126.GLenum + GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB* = 0x88A3.GLenum + GL_MATRIX5_ARB* = 0x88C5.GLenum + GL_FRAMEBUFFER_UNDEFINED_OES* = 0x8219.GLenum + GL_UNPACK_CMYK_HINT_EXT* = 0x800F.GLenum + GL_UNSIGNED_NORMALIZED_EXT* = 0x8C17.GLenum + GL_ONE* = 1.GLenum + GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB* = 0x889B.GLenum + GL_TRANSPOSE_PROJECTION_MATRIX* = 0x84E4.GLenum + GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV* = 0x8C28.GLenum + GL_CLIP_DISTANCE3* = 0x3003.GLenum + GL_4PASS_1_SGIS* = 0x80A5.GLenum + GL_MAX_FRAGMENT_LIGHTS_SGIX* = 0x8404.GLenum + GL_TEXTURE_3D_OES* = 0x806F.GLenum + GL_TEXTURE0* = 0x84C0.GLenum + GL_INT_IMAGE_CUBE_EXT* = 0x905B.GLenum + GL_INNOCENT_CONTEXT_RESET_ARB* = 0x8254.GLenum + GL_INDEX_ARRAY_TYPE_EXT* = 0x8085.GLenum + GL_SAMPLER_OBJECT_AMD* = 0x9155.GLenum + GL_INDEX_ARRAY_BUFFER_BINDING_ARB* = 0x8899.GLenum + GL_RENDERBUFFER_DEPTH_SIZE_OES* = 0x8D54.GLenum + GL_MAX_SAMPLE_MASK_WORDS* = 0x8E59.GLenum + GL_COMBINER2_NV* = 0x8552.GLenum + GL_COLOR_ARRAY_BUFFER_BINDING_ARB* = 0x8898.GLenum + GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB* = 0x886A.GLenum + GL_STREAM_DRAW* = 0x88E0.GLenum + GL_RGB8I* = 0x8D8F.GLenum + GL_BLEND_COLOR_EXT* = 0x8005.GLenum + GL_MAX_VARYING_VECTORS* = 0x8DFC.GLenum + GL_COPY_WRITE_BUFFER_BINDING* = 0x8F37.GLenum + GL_FIXED_ONLY_ARB* = 0x891D.GLenum + GL_INT_VEC4* = 0x8B55.GLenum + GL_PROGRAM_PIPELINE_BINDING_EXT* = 0x825A.GLenum + GL_UNSIGNED_NORMALIZED_ARB* = 0x8C17.GLenum + GL_NUM_INSTRUCTIONS_PER_PASS_ATI* = 0x8971.GLenum + GL_PIXEL_MODE_BIT* = 0x00000020.GLbitfield + GL_COMPRESSED_RED_RGTC1* = 0x8DBB.GLenum + GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT* = 0x00000020.GLbitfield + GL_VARIANT_DATATYPE_EXT* = 0x87E5.GLenum + GL_DARKEN_NV* = 0x9297.GLenum + GL_POINT_SIZE_MAX_SGIS* = 0x8127.GLenum + GL_OBJECT_ATTACHED_OBJECTS_ARB* = 0x8B85.GLenum + GL_SLUMINANCE_ALPHA_EXT* = 0x8C44.GLenum + GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY* = 0x906A.GLenum + GL_EDGE_FLAG_ARRAY* = 0x8079.GLenum + GL_LINEAR_CLIPMAP_NEAREST_SGIX* = 0x844F.GLenum + GL_LUMINANCE_ALPHA32F_EXT* = 0x8819.GLenum + GL_NORMAL_BIT_PGI* = 0x08000000.GLbitfield + GL_SECONDARY_COLOR_ARRAY* = 0x845E.GLenum + GL_CLIP_PLANE1_IMG* = 0x3001.GLenum + GL_REG_19_ATI* = 0x8934.GLenum + GL_PIXEL_PACK_BUFFER_BINDING* = 0x88ED.GLenum + GL_PIXEL_GROUP_COLOR_SGIS* = 0x8356.GLenum + GL_SELECTION_BUFFER_SIZE* = 0x0DF4.GLenum + GL_SRC_OUT_NV* = 0x928C.GLenum + GL_TEXTURE7* = 0x84C7.GLenum + GL_COMPARE_R_TO_TEXTURE* = 0x884E.GLenum + GL_DUDV_ATI* = 0x8779.GLenum + GL_TEXTURE_BASE_LEVEL* = 0x813C.GLenum + GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI* = 0x87F5.GLenum + GL_LAYOUT_LINEAR_INTEL* = 1.GLenum + GL_DEPTH_BUFFER_BIT2_QCOM* = 0x00000400.GLbitfield + GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS* = 0x8E8A.GLenum + GL_LIGHT3* = 0x4003.GLenum + GL_ALPHA_MAX_CLAMP_INGR* = 0x8567.GLenum + GL_RG_INTEGER* = 0x8228.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL* = 0x8CD2.GLenum + GL_TEXTURE_STACK_DEPTH* = 0x0BA5.GLenum + GL_ALREADY_SIGNALED* = 0x911A.GLenum + GL_TEXTURE_CUBE_MAP_OES* = 0x8513.GLenum + GL_N3F_V3F* = 0x2A25.GLenum + GL_SUBTRACT_ARB* = 0x84E7.GLenum + GL_ELEMENT_ARRAY_LENGTH_NV* = 0x8F33.GLenum + GL_NORMAL_ARRAY_EXT* = 0x8075.GLenum + GL_POLYGON_OFFSET_FACTOR_EXT* = 0x8038.GLenum + GL_EIGHTH_BIT_ATI* = 0x00000020.GLbitfield + GL_UNSIGNED_INT_SAMPLER_2D_RECT* = 0x8DD5.GLenum + GL_OBJECT_ACTIVE_ATTRIBUTES_ARB* = 0x8B89.GLenum + GL_MAX_VERTEX_VARYING_COMPONENTS_ARB* = 0x8DDE.GLenum + GL_TEXTURE_COORD_ARRAY_STRIDE_EXT* = 0x808A.GLenum + GL_4_BYTES* = 0x1409.GLenum + GL_SAMPLE_SHADING* = 0x8C36.GLenum + GL_FOG_MODE* = 0x0B65.GLenum + GL_CON_7_ATI* = 0x8948.GLenum + GL_DRAW_FRAMEBUFFER* = 0x8CA9.GLenum + GL_TEXTURE_MEMORY_LAYOUT_INTEL* = 0x83FF.GLenum + GL_RGB32I_EXT* = 0x8D83.GLenum + GL_VERTEX_ARRAY_STRIDE* = 0x807C.GLenum + GL_COLOR_ATTACHMENT3_NV* = 0x8CE3.GLenum + GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL* = 0x83F6.GLenum + GL_CONTRAST_NV* = 0x92A1.GLenum + GL_RGBA32F* = 0x8814.GLenum + GL_YCBAYCR8A_4224_NV* = 0x9032.GLenum + GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET* = 0x82D9.GLenum + GL_TEXTURE22* = 0x84D6.GLenum + GL_TEXTURE_3D* = 0x806F.GLenum + GL_STENCIL_PASS_DEPTH_FAIL* = 0x0B95.GLenum + GL_PROXY_HISTOGRAM_EXT* = 0x8025.GLenum + GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS* = 0x92C5.GLenum + GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE* = 0x92D8.GLenum + GL_FOG_COORD_ARRAY_TYPE* = 0x8454.GLenum + GL_MAP2_VERTEX_4* = 0x0DB8.GLenum + GL_PACK_COMPRESSED_SIZE_SGIX* = 0x831C.GLenum + GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX* = 0x817C.GLenum + GL_ITALIC_BIT_NV* = 0x02.GLbitfield + GL_COMPRESSED_LUMINANCE_ALPHA* = 0x84EB.GLenum + GL_COLOR_TABLE_SCALE_SGI* = 0x80D6.GLenum + GL_DOUBLE_MAT2x4_EXT* = 0x8F4A.GLenum + GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE* = 0x8215.GLenum + GL_MATRIX11_ARB* = 0x88CB.GLenum + GL_REG_5_ATI* = 0x8926.GLenum + GL_RGBA2_EXT* = 0x8055.GLenum + GL_DISCARD_NV* = 0x8530.GLenum + GL_TEXTURE7_ARB* = 0x84C7.GLenum + GL_LUMINANCE32UI_EXT* = 0x8D74.GLenum + GL_ACTIVE_UNIFORM_BLOCKS* = 0x8A36.GLenum + GL_UNSIGNED_INT16_VEC4_NV* = 0x8FF3.GLenum + GL_VERTEX_ATTRIB_ARRAY5_NV* = 0x8655.GLenum + GL_DOUBLE_MAT3x4* = 0x8F4C.GLenum + GL_BOOL* = 0x8B56.GLenum + GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB* = 0x86A2.GLenum + GL_COMPRESSED_RGB_ARB* = 0x84ED.GLenum + GL_DEBUG_TYPE_MARKER_KHR* = 0x8268.GLenum + GL_TEXTURE_DEPTH_QCOM* = 0x8BD4.GLenum + GL_VARIABLE_F_NV* = 0x8528.GLenum + GL_MAX_PIXEL_MAP_TABLE* = 0x0D34.GLenum + GL_DST_COLOR* = 0x0306.GLenum + GL_OR_INVERTED* = 0x150D.GLenum + GL_TRANSFORM_FEEDBACK_VARYINGS_NV* = 0x8C83.GLenum + GL_RGB_INTEGER* = 0x8D98.GLenum + GL_COLOR_MATERIAL* = 0x0B57.GLenum + GL_DEBUG_SEVERITY_LOW_AMD* = 0x9148.GLenum + GL_MIRROR_CLAMP_TO_BORDER_EXT* = 0x8912.GLint + GL_TEXTURE1_ARB* = 0x84C1.GLenum + GL_MIN_MAP_BUFFER_ALIGNMENT* = 0x90BC.GLenum + GL_MATRIX16_ARB* = 0x88D0.GLenum + GL_TEXTURE_ALPHA_TYPE_ARB* = 0x8C13.GLenum + GL_PROGRAM_POINT_SIZE* = 0x8642.GLenum + GL_COMBINER_AB_OUTPUT_NV* = 0x854A.GLenum + GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2_OES* = 0x9276.GLenum + GL_RGB4_S3TC* = 0x83A1.GLenum + GL_TEXTURE_EXTERNAL_OES* = 0x8D65.GLenum + GL_MAX_MAP_TESSELLATION_NV* = 0x86D6.GLenum + GL_AUX_DEPTH_STENCIL_APPLE* = 0x8A14.GLenum + GL_MAX_DEBUG_LOGGED_MESSAGES_AMD* = 0x9144.GLenum + GL_CONSTANT_BORDER* = 0x8151.GLenum + GL_RESAMPLE_ZERO_FILL_OML* = 0x8987.GLenum + GL_POST_CONVOLUTION_ALPHA_SCALE_EXT* = 0x801F.GLenum + GL_OBJECT_VALIDATE_STATUS_ARB* = 0x8B83.GLenum + GL_DST_ALPHA* = 0x0304.GLenum + GL_COMBINER5_NV* = 0x8555.GLenum + GL_VERSION_ES_CL_1_1* = 1.GLenum + GL_MOVE_TO_CONTINUES_NV* = 0x90B6.GLenum + GL_IMAGE_MAG_FILTER_HP* = 0x815C.GLenum + GL_TEXTURE_FREE_MEMORY_ATI* = 0x87FC.GLenum + GL_DEBUG_TYPE_PORTABILITY_KHR* = 0x824F.GLenum + GL_BUFFER_UPDATE_BARRIER_BIT* = 0x00000200.GLbitfield + GL_FUNC_ADD* = 0x8006.GLenum + GL_PN_TRIANGLES_POINT_MODE_ATI* = 0x87F2.GLenum + GL_DEBUG_CALLBACK_USER_PARAM_ARB* = 0x8245.GLenum + GL_CURRENT_SECONDARY_COLOR* = 0x8459.GLenum + GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV* = 0x885A.GLenum + GL_FRAGMENT_LIGHT7_SGIX* = 0x8413.GLenum + GL_MAP2_TEXTURE_COORD_4* = 0x0DB6.GLenum + GL_PACK_ALIGNMENT* = 0x0D05.GLenum + GL_VERTEX23_BIT_PGI* = 0x00000004.GLbitfield + GL_MAX_CLIPMAP_DEPTH_SGIX* = 0x8177.GLenum + GL_TEXTURE_3D_BINDING_EXT* = 0x806A.GLenum + GL_COLOR_ATTACHMENT1* = 0x8CE1.GLenum + GL_NEAREST* = 0x2600.GLint + GL_MAX_DEBUG_LOGGED_MESSAGES* = 0x9144.GLenum + GL_COMBINER6_NV* = 0x8556.GLenum + GL_COLOR_SUM_EXT* = 0x8458.GLenum + GL_CONVOLUTION_WIDTH* = 0x8018.GLenum + GL_SAMPLE_ALPHA_TO_COVERAGE_ARB* = 0x809E.GLenum + GL_DRAW_FRAMEBUFFER_EXT* = 0x8CA9.GLenum + GL_PROXY_HISTOGRAM* = 0x8025.GLenum + GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS* = 0x8355.GLenum + GL_COMPRESSED_RGBA_ASTC_10x5_KHR* = 0x93B8.GLenum + GL_SMOOTH_CUBIC_CURVE_TO_NV* = 0x10.GLenum + GL_BGR_EXT* = 0x80E0.GLenum + GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB* = 0x88B6.GLenum + GL_VIBRANCE_BIAS_NV* = 0x8719.GLenum + GL_UNPACK_COLORSPACE_CONVERSION_WEBGL* = 0x9243.GLenum + GL_SLUMINANCE8_NV* = 0x8C47.GLenum + GL_TEXTURE_MAX_LEVEL_SGIS* = 0x813D.GLenum + GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX* = 0x92DA.GLenum + GL_RGB9_E5_EXT* = 0x8C3D.GLenum + GL_CULL_VERTEX_IBM* = 103050.GLenum + GL_PROXY_COLOR_TABLE* = 0x80D3.GLenum + GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE* = 0x8216.GLenum + GL_MAX_FRAGMENT_UNIFORM_COMPONENTS* = 0x8B49.GLenum + GL_CCW* = 0x0901.GLenum + GL_COLOR_WRITEMASK* = 0x0C23.GLenum + GL_TEXTURE19_ARB* = 0x84D3.GLenum + GL_VERTEX_STREAM3_ATI* = 0x876F.GLenum + GL_ONE_EXT* = 0x87DE.GLenum + GL_MAX_SAMPLES* = 0x8D57.GLenum + GL_STENCIL_PASS_DEPTH_PASS* = 0x0B96.GLenum + GL_PERFMON_RESULT_AVAILABLE_AMD* = 0x8BC4.GLenum + GL_RETURN* = 0x0102.GLenum + GL_DETAIL_TEXTURE_LEVEL_SGIS* = 0x809A.GLenum + GL_UNSIGNED_INT_IMAGE_CUBE_EXT* = 0x9066.GLenum + GL_FOG_OFFSET_VALUE_SGIX* = 0x8199.GLenum + GL_TEXTURE_MAX_LOD_SGIS* = 0x813B.GLenum + GL_TRANSPOSE_COLOR_MATRIX_ARB* = 0x84E6.GLenum + GL_DEBUG_SOURCE_APPLICATION_ARB* = 0x824A.GLenum + GL_SIGNED_ALPHA_NV* = 0x8705.GLenum + GL_UNSIGNED_INT_IMAGE_2D_EXT* = 0x9063.GLenum + GL_SHADER_IMAGE_ACCESS_BARRIER_BIT* = 0x00000020.GLbitfield + GL_ATOMIC_COUNTER_BARRIER_BIT* = 0x00001000.GLbitfield + GL_COLOR3_BIT_PGI* = 0x00010000.GLbitfield + GL_MATERIAL_SIDE_HINT_PGI* = 0x1A22C.GLenum + GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE* = 0x85B0.GLenum + GL_LINEAR_SHARPEN_SGIS* = 0x80AD.GLenum + GL_LUMINANCE_SNORM* = 0x9011.GLenum + GL_TEXTURE_LUMINANCE_SIZE* = 0x8060.GLenum + GL_REPLACE_MIDDLE_SUN* = 0x0002.GLenum + GL_TEXTURE_DEFORMATION_SGIX* = 0x8195.GLenum + GL_MULTISAMPLE_BUFFER_BIT7_QCOM* = 0x80000000.GLbitfield + GL_FONT_HAS_KERNING_BIT_NV* = 0x10000000.GLbitfield + GL_COPY* = 0x1503.GLenum + GL_READ_BUFFER_NV* = 0x0C02.GLenum + GL_TRANSPOSE_CURRENT_MATRIX_ARB* = 0x88B7.GLenum + GL_VERTEX_ARRAY_OBJECT_AMD* = 0x9154.GLenum + GL_TIMEOUT_EXPIRED* = 0x911B.GLenum + GL_DYNAMIC_COPY* = 0x88EA.GLenum + GL_DRAW_BUFFER2_ARB* = 0x8827.GLenum + GL_OUTPUT_TEXTURE_COORD10_EXT* = 0x87A7.GLenum + GL_SIGNED_RGBA8_NV* = 0x86FC.GLenum + GL_MATRIX6_ARB* = 0x88C6.GLenum + GL_OP_SUB_EXT* = 0x8796.GLenum + GL_NO_RESET_NOTIFICATION_EXT* = 0x8261.GLenum + GL_TEXTURE_BASE_LEVEL_SGIS* = 0x813C.GLenum + GL_ALPHA_INTEGER* = 0x8D97.GLenum + GL_TEXTURE13* = 0x84CD.GLenum + GL_EYE_LINEAR* = 0x2400.GLenum + GL_INTENSITY4_EXT* = 0x804A.GLenum + GL_SOURCE1_RGB_EXT* = 0x8581.GLenum + GL_AUX_BUFFERS* = 0x0C00.GLenum + GL_SOURCE0_ALPHA* = 0x8588.GLenum + GL_RGB32I* = 0x8D83.GLenum + GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS* = 0x8C8A.GLenum + GL_VIEW_CLASS_S3TC_DXT1_RGBA* = 0x82CD.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV* = 0x8C85.GLenum + GL_SAMPLER_KHR* = 0x82E6.GLenum + GL_WRITEONLY_RENDERING_QCOM* = 0x8823.GLenum + GL_PACK_SKIP_ROWS* = 0x0D03.GLenum + GL_MAP1_VERTEX_ATTRIB0_4_NV* = 0x8660.GLenum + GL_PATH_STENCIL_VALUE_MASK_NV* = 0x90B9.GLenum + GL_REPLACE_EXT* = 0x8062.GLenum + GL_MODELVIEW3_ARB* = 0x8723.GLenum + GL_ONE_MINUS_CONSTANT_ALPHA* = 0x8004.GLenum + GL_DSDT8_MAG8_INTENSITY8_NV* = 0x870B.GLenum + GL_CURRENT_QUERY_ARB* = 0x8865.GLenum + GL_LUMINANCE8_ALPHA8_OES* = 0x8045.GLenum + GL_ARRAY_ELEMENT_LOCK_COUNT_EXT* = 0x81A9.GLenum + GL_MODELVIEW19_ARB* = 0x8733.GLenum + GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT* = 0x87C5.GLenum + GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB* = 0x8810.GLenum + GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT* = 0x906C.GLenum + GL_NORMAL_ARRAY_BUFFER_BINDING* = 0x8897.GLenum + GL_AMBIENT* = 0x1200.GLenum + GL_TEXTURE_MATERIAL_PARAMETER_EXT* = 0x8352.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR* = 0x93DA.GLenum + GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS* = 0x8E7F.GLenum + GL_COMPRESSED_LUMINANCE_ALPHA_ARB* = 0x84EB.GLenum + GL_MODELVIEW14_ARB* = 0x872E.GLenum + GL_INTERLACE_READ_OML* = 0x8981.GLenum + GL_RENDERBUFFER_FREE_MEMORY_ATI* = 0x87FD.GLenum + GL_EMBOSS_MAP_NV* = 0x855F.GLenum + GL_POINT_SIZE_RANGE* = 0x0B12.GLenum + GL_FOG_COORDINATE* = 0x8451.GLenum + GL_MAJOR_VERSION* = 0x821B.GLenum + GL_FRAME_NV* = 0x8E26.GLenum + GL_CURRENT_TEXTURE_COORDS* = 0x0B03.GLenum + GL_PACK_RESAMPLE_OML* = 0x8984.GLenum + GL_DEPTH24_STENCIL8_OES* = 0x88F0.GLenum + GL_PROGRAM_BINARY_FORMATS_OES* = 0x87FF.GLenum + GL_TRANSLATE_3D_NV* = 0x9091.GLenum + GL_TEXTURE_GEN_Q* = 0x0C63.GLenum + GL_COLOR_ATTACHMENT0_EXT* = 0x8CE0.GLenum + GL_ALPHA12* = 0x803D.GLenum + GL_INCR_WRAP_EXT* = 0x8507.GLenum + GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN* = 0x8C88.GLenum + GL_DUAL_ALPHA12_SGIS* = 0x8112.GLenum + GL_EYE_LINE_SGIS* = 0x81F6.GLenum + GL_TEXTURE_MAX_LEVEL_APPLE* = 0x813D.GLenum + GL_TRIANGLE_FAN* = 0x0006.GLenum + GL_DEBUG_GROUP_STACK_DEPTH* = 0x826D.GLenum + GL_IMAGE_CLASS_1_X_16* = 0x82BE.GLenum + GL_COMPILE* = 0x1300.GLenum + GL_LINE_SMOOTH* = 0x0B20.GLenum + GL_FEEDBACK_BUFFER_POINTER* = 0x0DF0.GLenum + GL_CURRENT_SECONDARY_COLOR_EXT* = 0x8459.GLenum + GL_DRAW_BUFFER2_ATI* = 0x8827.GLenum + GL_PN_TRIANGLES_NORMAL_MODE_ATI* = 0x87F3.GLenum + GL_MODELVIEW0_ARB* = 0x1700.GLenum + GL_SRGB8_ALPHA8* = 0x8C43.GLenum + GL_TEXTURE_BLUE_TYPE* = 0x8C12.GLenum + GL_POST_CONVOLUTION_ALPHA_BIAS* = 0x8023.GLenum + GL_PATH_STROKE_BOUNDING_BOX_NV* = 0x90A2.GLenum + GL_RGBA16UI* = 0x8D76.GLenum + GL_OFFSET_HILO_TEXTURE_2D_NV* = 0x8854.GLenum + GL_PREVIOUS_ARB* = 0x8578.GLenum + GL_BINORMAL_ARRAY_EXT* = 0x843A.GLenum + GL_UNSIGNED_INT_IMAGE_CUBE* = 0x9066.GLenum + GL_REG_30_ATI* = 0x893F.GLenum + GL_VIEWPORT_SUBPIXEL_BITS* = 0x825C.GLenum + GL_VERSION* = 0x1F02.GLenum + GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV* = 0x90FC.GLenum + GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD* = 0x914E.GLenum + GL_CONVOLUTION_FILTER_SCALE_EXT* = 0x8014.GLenum + GL_HALF_BIT_ATI* = 0x00000008.GLbitfield + GL_SPRITE_AXIS_SGIX* = 0x814A.GLenum + GL_INDEX_ARRAY_STRIDE* = 0x8086.GLenum + GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB* = 0x88B2.GLenum + GL_EVAL_VERTEX_ATTRIB0_NV* = 0x86C6.GLenum + GL_COUNTER_RANGE_AMD* = 0x8BC1.GLenum + GL_VERTEX_WEIGHTING_EXT* = 0x8509.GLenum + GL_POST_CONVOLUTION_GREEN_SCALE* = 0x801D.GLenum + GL_UNSIGNED_INT8_NV* = 0x8FEC.GLenum + GL_CURRENT_MATRIX_STACK_DEPTH_NV* = 0x8640.GLenum + GL_STENCIL_INDEX1_OES* = 0x8D46.GLenum + GL_SLUMINANCE_NV* = 0x8C46.GLenum + GL_UNSIGNED_INT_8_8_8_8_REV_EXT* = 0x8367.GLenum + GL_HISTOGRAM_FORMAT* = 0x8027.GLenum + GL_LUMINANCE12_ALPHA4_EXT* = 0x8046.GLenum + GL_FLOAT_MAT3* = 0x8B5B.GLenum + GL_MAX_PROGRAM_TEXEL_OFFSET_NV* = 0x8905.GLenum + GL_PALETTE8_RGBA4_OES* = 0x8B98.GLenum + GL_UNPACK_SKIP_IMAGES_EXT* = 0x806D.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y* = 0x8518.GLenum + GL_UNPACK_SUBSAMPLE_RATE_SGIX* = 0x85A1.GLenum + GL_NORMAL_ARRAY_LENGTH_NV* = 0x8F2C.GLenum + GL_VERTEX_ATTRIB_ARRAY4_NV* = 0x8654.GLenum + GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES* = 0x8CD9.GLenum + GL_UNSIGNED_BYTE* = 0x1401.GLenum + GL_RGB2_EXT* = 0x804E.GLenum + GL_TEXTURE_BUFFER_SIZE* = 0x919E.GLenum + GL_MAP_STENCIL* = 0x0D11.GLenum + GL_TIMEOUT_EXPIRED_APPLE* = 0x911B.GLenum + GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS* = 0x8C29.GLenum + GL_CON_14_ATI* = 0x894F.GLenum + GL_RGBA12* = 0x805A.GLenum + GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS* = 0x919A.GLenum + GL_CON_20_ATI* = 0x8955.GLenum + GL_LOCAL_CONSTANT_DATATYPE_EXT* = 0x87ED.GLenum + GL_DUP_FIRST_CUBIC_CURVE_TO_NV* = 0xF2.GLenum + GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV* = 0x8F27.GLenum + GL_TEXTURE_COORD_ARRAY* = 0x8078.GLenum + GL_LUMINANCE8I_EXT* = 0x8D92.GLenum + GL_REPLACE_OLDEST_SUN* = 0x0003.GLenum + GL_TEXTURE_SHADER_NV* = 0x86DE.GLenum + GL_UNSIGNED_INT_8_8_8_8_EXT* = 0x8035.GLenum + GL_SAMPLE_COVERAGE_INVERT* = 0x80AB.GLenum + GL_FOG_COORD_ARRAY_ADDRESS_NV* = 0x8F28.GLenum + GL_GPU_DISJOINT_EXT* = 0x8FBB.GLenum + GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI* = 0x8803.GLenum + GL_TEXTURE_GREEN_SIZE_EXT* = 0x805D.GLenum + GL_INTERLEAVED_ATTRIBS* = 0x8C8C.GLenum + GL_FOG_FUNC_SGIS* = 0x812A.GLenum + GL_TEXTURE_DEPTH_SIZE_ARB* = 0x884A.GLenum + GL_MAP_COHERENT_BIT* = 0x0080.GLbitfield + GL_COMPRESSED_SLUMINANCE_ALPHA* = 0x8C4B.GLenum + GL_RGB32UI* = 0x8D71.GLenum + GL_SEPARABLE_2D* = 0x8012.GLenum + GL_MATRIX10_ARB* = 0x88CA.GLenum + GL_FLOAT_RGBA32_NV* = 0x888B.GLenum + GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB* = 0x9199.GLenum + GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV* = 0x8E54.GLenum + GL_REG_9_ATI* = 0x892A.GLenum + GL_MAP2_VERTEX_ATTRIB14_4_NV* = 0x867E.GLenum + GL_OP_EXP_BASE_2_EXT* = 0x8791.GLenum + GL_INT_IMAGE_BUFFER_EXT* = 0x905C.GLenum + GL_TEXTURE_WRAP_R_EXT* = 0x8072.GLenum + GL_DOUBLE_VEC3* = 0x8FFD.GLenum + GL_DRAW_BUFFER5_EXT* = 0x882A.GLenum + GL_OUTPUT_TEXTURE_COORD7_EXT* = 0x87A4.GLenum + GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB* = 0x8242.GLenum + GL_MAX_TESS_GEN_LEVEL* = 0x8E7E.GLenum + GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB* = 0x8895.GLenum + GL_RGBA16I_EXT* = 0x8D88.GLenum + GL_REG_10_ATI* = 0x892B.GLenum + GL_MAT_EMISSION_BIT_PGI* = 0x00800000.GLbitfield + GL_TEXTURE_COORD_ARRAY_SIZE_EXT* = 0x8088.GLenum + GL_RED_BIAS* = 0x0D15.GLenum + GL_RGB16F_ARB* = 0x881B.GLenum + GL_ANY_SAMPLES_PASSED_CONSERVATIVE* = 0x8D6A.GLenum + GL_BLUE_MAX_CLAMP_INGR* = 0x8566.GLenum + cGL_FLOAT* = 0x1406.GLenum + GL_STENCIL_INDEX8_EXT* = 0x8D48.GLenum + GL_POINT_SIZE_ARRAY_OES* = 0x8B9C.GLenum + GL_INT16_NV* = 0x8FE4.GLenum + GL_PALETTE4_RGB8_OES* = 0x8B90.GLenum + GL_RENDERBUFFER_GREEN_SIZE_OES* = 0x8D51.GLenum + GL_SEPARATE_ATTRIBS_NV* = 0x8C8D.GLenum + GL_BOOL_VEC3_ARB* = 0x8B58.GLenum + GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES* = 0x92C6.GLenum + GL_STACK_UNDERFLOW_KHR* = 0x0504.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB* = 0x8519.GLenum + GL_COMPRESSED_INTENSITY_ARB* = 0x84EC.GLenum + GL_MAX_ASYNC_TEX_IMAGE_SGIX* = 0x835F.GLenum + GL_TEXTURE_4D_SGIS* = 0x8134.GLenum + GL_TEXCOORD3_BIT_PGI* = 0x40000000.GLbitfield + GL_PIXEL_MAP_I_TO_R_SIZE* = 0x0CB2.GLenum + GL_NORMAL_MAP_ARB* = 0x8511.GLenum + GL_MAX_CONVOLUTION_HEIGHT* = 0x801B.GLenum + GL_COMPRESSED_INTENSITY* = 0x84EC.GLenum + GL_FONT_Y_MAX_BOUNDS_BIT_NV* = 0x00080000.GLbitfield + GL_FLOAT_MAT2* = 0x8B5A.GLenum + GL_TEXTURE_SRGB_DECODE_EXT* = 0x8A48.GLenum + GL_FRAMEBUFFER_BLEND* = 0x828B.GLenum + GL_TEXTURE_COORD_ARRAY_LIST_IBM* = 103074.GLenum + GL_REG_12_ATI* = 0x892D.GLenum + GL_UNSIGNED_INT_ATOMIC_COUNTER* = 0x92DB.GLenum + GL_DETAIL_TEXTURE_2D_BINDING_SGIS* = 0x8096.GLenum + GL_OCCLUSION_TEST_HP* = 0x8165.GLenum + GL_TEXTURE11_ARB* = 0x84CB.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC* = 0x9279.GLenum + GL_BUFFER_MAPPED* = 0x88BC.GLenum + GL_VARIANT_ARRAY_STRIDE_EXT* = 0x87E6.GLenum + GL_CONVOLUTION_BORDER_COLOR_HP* = 0x8154.GLenum + GL_UNPACK_RESAMPLE_OML* = 0x8985.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_SIZE* = 0x8C85.GLenum + GL_PROXY_TEXTURE_2D_ARRAY_EXT* = 0x8C1B.GLenum + GL_RGBA4_EXT* = 0x8056.GLenum + GL_ALPHA32I_EXT* = 0x8D84.GLenum + GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE* = 0x92C4.GLenum + GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX* = 0x840A.GLenum + GL_BINORMAL_ARRAY_TYPE_EXT* = 0x8440.GLenum + GL_VIEW_CLASS_S3TC_DXT5_RGBA* = 0x82CF.GLenum + GL_TEXTURE_CLIPMAP_OFFSET_SGIX* = 0x8173.GLenum + GL_RESTART_SUN* = 0x0001.GLenum + GL_PERTURB_EXT* = 0x85AE.GLenum + GL_UNSIGNED_BYTE_3_3_2_EXT* = 0x8032.GLenum + GL_LUMINANCE16I_EXT* = 0x8D8C.GLenum + GL_TEXTURE3_ARB* = 0x84C3.GLenum + GL_POINT_SIZE_MIN_EXT* = 0x8126.GLenum + GL_OUTPUT_TEXTURE_COORD1_EXT* = 0x879E.GLenum + GL_COMPARE_REF_TO_TEXTURE* = 0x884E.GLenum + GL_KEEP* = 0x1E00.GLenum + GL_FLOAT_MAT2x4* = 0x8B66.GLenum + GL_FLOAT_VEC4_ARB* = 0x8B52.GLenum + GL_BIAS_BY_NEGATIVE_ONE_HALF_NV* = 0x8541.GLenum + GL_BGR* = 0x80E0.GLenum + GL_SHADER_BINARY_FORMATS* = 0x8DF8.GLenum + GL_CND0_ATI* = 0x896B.GLenum + GL_MIRRORED_REPEAT_IBM* = 0x8370.GLint + GL_REFLECTION_MAP_OES* = 0x8512.GLenum + GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT* = 0x8DE2.GLenum + GL_R* = 0x2002.GLenum + GL_MAX_SHADER_STORAGE_BLOCK_SIZE* = 0x90DE.GLenum + GL_ATTRIB_ARRAY_STRIDE_NV* = 0x8624.GLenum + GL_VARIABLE_E_NV* = 0x8527.GLenum + GL_HISTOGRAM_EXT* = 0x8024.GLenum + GL_TEXTURE_BINDING_BUFFER_ARB* = 0x8C2C.GLenum + GL_MAX_SPARSE_TEXTURE_SIZE_ARB* = 0x9198.GLenum + GL_TEXTURE5* = 0x84C5.GLenum + GL_NUM_ACTIVE_VARIABLES* = 0x9304.GLenum + GL_DEPTH_STENCIL_ATTACHMENT* = 0x821A.GLenum + GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB* = 0x889E.GLenum + GL_AMBIENT_AND_DIFFUSE* = 0x1602.GLenum + GL_LAYER_NV* = 0x8DAA.GLenum + GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV* = 0x08.GLbitfield + GL_TEXTURE8* = 0x84C8.GLenum + GL_MODELVIEW5_ARB* = 0x8725.GLenum + GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS* = 0x92D1.GLenum + GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS* = 0x92CD.GLenum + GL_BLUE_MIN_CLAMP_INGR* = 0x8562.GLenum + GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS* = 0x90D9.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES* = 0x8519.GLenum + GL_MAX_SAMPLES_IMG* = 0x9135.GLenum + GL_QUERY_BY_REGION_WAIT* = 0x8E15.GLenum + GL_T* = 0x2001.GLenum + GL_VIEW_CLASS_RGTC2_RG* = 0x82D1.GLenum + GL_TEXTURE_ENV_MODE* = 0x2200.GLenum + GL_COMPRESSED_SRGB8_ETC2* = 0x9275.GLenum + GL_MAP_FLUSH_EXPLICIT_BIT* = 0x0010.GLbitfield + GL_COLOR_MATERIAL_PARAMETER* = 0x0B56.GLenum + GL_HALF_FLOAT_ARB* = 0x140B.GLenum + GL_NOTEQUAL* = 0x0205.GLenum + GL_MAP_INVALIDATE_BUFFER_BIT_EXT* = 0x0008.GLbitfield + GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT* = 0x8C29.GLenum + GL_DUAL_TEXTURE_SELECT_SGIS* = 0x8124.GLenum + GL_TEXTURE31* = 0x84DF.GLenum + GL_EVAL_TRIANGULAR_2D_NV* = 0x86C1.GLenum + GL_VIDEO_COLOR_CONVERSION_OFFSET_NV* = 0x902C.GLenum + GL_COMPRESSED_R11_EAC_OES* = 0x9270.GLenum + GL_RGB8_OES* = 0x8051.GLenum + GL_CLIP_PLANE2* = 0x3002.GLenum + GL_HINT_BIT* = 0x00008000.GLbitfield + GL_TEXTURE6_ARB* = 0x84C6.GLenum + GL_FLOAT_VEC2* = 0x8B50.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT* = 0x8C85.GLenum + GL_MAX_EVAL_ORDER* = 0x0D30.GLenum + GL_DUAL_LUMINANCE8_SGIS* = 0x8115.GLenum + GL_ALPHA16I_EXT* = 0x8D8A.GLenum + GL_IDENTITY_NV* = 0x862A.GLenum + GL_VIEW_CLASS_BPTC_UNORM* = 0x82D2.GLenum + GL_PATH_DASH_CAPS_NV* = 0x907B.GLenum + GL_IGNORE_BORDER_HP* = 0x8150.GLenum + GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI* = 0x87F6.GLenum + GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT* = 0x8C8B.GLenum + GL_DRAW_BUFFER1_ATI* = 0x8826.GLenum + GL_TEXTURE_MIN_FILTER* = 0x2801.GLenum + GL_EVAL_VERTEX_ATTRIB12_NV* = 0x86D2.GLenum + GL_INT_IMAGE_2D_ARRAY* = 0x905E.GLenum + GL_SRC0_RGB* = 0x8580.GLenum + GL_MIN_EXT* = 0x8007.GLenum + GL_PROGRAM_PIPELINE_OBJECT_EXT* = 0x8A4F.GLenum + GL_STENCIL_BUFFER_BIT* = 0x00000400.GLbitfield + GL_SCREEN_COORDINATES_REND* = 0x8490.GLenum + GL_DOUBLE_VEC3_EXT* = 0x8FFD.GLenum + GL_SUBSAMPLE_DISTANCE_AMD* = 0x883F.GLenum + GL_VERTEX_SHADER_LOCALS_EXT* = 0x87D3.GLenum + GL_VERTEX_ATTRIB_ARRAY13_NV* = 0x865D.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR* = 0x93D9.GLenum + GL_UNSIGNED_NORMALIZED* = 0x8C17.GLenum + GL_DRAW_BUFFER10_NV* = 0x882F.GLenum + GL_PATH_STROKE_MASK_NV* = 0x9084.GLenum + GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB* = 0x88A7.GLenum + GL_SRGB_ALPHA_EXT* = 0x8C42.GLenum + GL_CONST_EYE_NV* = 0x86E5.GLenum + GL_MODELVIEW1_ARB* = 0x850A.GLenum + GL_FORMAT_SUBSAMPLE_244_244_OML* = 0x8983.GLenum + GL_LOGIC_OP_MODE* = 0x0BF0.GLenum + GL_CLIP_DISTANCE4* = 0x3004.GLenum + GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD* = 0x914A.GLenum + GL_SAMPLES* = 0x80A9.GLenum + GL_UNSIGNED_SHORT_5_5_5_1_EXT* = 0x8034.GLenum + GL_POINT_DISTANCE_ATTENUATION* = 0x8129.GLenum + GL_3D_COLOR* = 0x0602.GLenum + GL_BGRA* = 0x80E1.GLenum + GL_PARAMETER_BUFFER_BINDING_ARB* = 0x80EF.GLenum + GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM* = 103085.GLenum + GL_HSL_LUMINOSITY_NV* = 0x92B0.GLenum + GL_PROJECTION_STACK_DEPTH* = 0x0BA4.GLenum + GL_COMBINER_BIAS_NV* = 0x8549.GLenum + GL_AND* = 0x1501.GLenum + GL_TEXTURE27* = 0x84DB.GLenum + GL_VERTEX_PROGRAM_CALLBACK_DATA_MESA* = 0x8BB7.GLenum + GL_DRAW_BUFFER13_ATI* = 0x8832.GLenum + GL_UNSIGNED_SHORT_5_5_5_1* = 0x8034.GLenum + GL_PERFMON_GLOBAL_MODE_QCOM* = 0x8FA0.GLenum + GL_RED_EXT* = 0x1903.GLenum + GL_INNOCENT_CONTEXT_RESET_EXT* = 0x8254.GLenum + GL_UNIFORM_BUFFER_START* = 0x8A29.GLenum + GL_MAX_UNIFORM_BUFFER_BINDINGS* = 0x8A2F.GLenum + GL_SLICE_ACCUM_SUN* = 0x85CC.GLenum + GL_DRAW_BUFFER9_ATI* = 0x882E.GLenum + GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV* = 0x8DA2.GLenum + GL_READ_FRAMEBUFFER_BINDING_APPLE* = 0x8CAA.GLenum + GL_INDEX_ARRAY_LENGTH_NV* = 0x8F2E.GLenum + GL_DETAIL_TEXTURE_MODE_SGIS* = 0x809B.GLenum + GL_MATRIX13_ARB* = 0x88CD.GLenum + GL_ADD_SIGNED_ARB* = 0x8574.GLenum + GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE* = 0x910A.GLenum + GL_DEPTH_BITS* = 0x0D56.GLenum + GL_LUMINANCE_ALPHA_SNORM* = 0x9012.GLenum + GL_VIEW_CLASS_RGTC1_RED* = 0x82D0.GLenum + GL_LINE_WIDTH* = 0x0B21.GLenum + GL_DRAW_BUFFER14_ATI* = 0x8833.GLenum + GL_CON_30_ATI* = 0x895F.GLenum + GL_POST_COLOR_MATRIX_BLUE_BIAS* = 0x80BA.GLenum + GL_PIXEL_TRANSFORM_2D_EXT* = 0x8330.GLenum + GL_CONTEXT_LOST_WEBGL* = 0x9242.GLenum + GL_COLOR_TABLE_BLUE_SIZE_SGI* = 0x80DC.GLenum + GL_CONSTANT_EXT* = 0x8576.GLenum + GL_IMPLEMENTATION_COLOR_READ_TYPE* = 0x8B9A.GLenum + GL_HSL_COLOR_NV* = 0x92AF.GLenum + GL_LOAD* = 0x0101.GLenum + GL_TEXTURE_BIT* = 0x00040000.GLbitfield + GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT* = 0x8CD9.GLenum + GL_IMAGE_ROTATE_ORIGIN_X_HP* = 0x815A.GLenum + GL_DEPTH_BUFFER_BIT6_QCOM* = 0x00004000.GLbitfield + GL_QUERY* = 0x82E3.GLenum + GL_INVALID_VALUE* = 0x0501.GLenum + GL_PACK_COMPRESSED_BLOCK_HEIGHT* = 0x912C.GLenum + GL_MAX_PROGRAM_GENERIC_RESULTS_NV* = 0x8DA6.GLenum + GL_BACK_PRIMARY_COLOR_NV* = 0x8C77.GLenum + GL_ALPHA8_OES* = 0x803C.GLenum + GL_INDEX* = 0x8222.GLenum + GL_ATTRIB_ARRAY_SIZE_NV* = 0x8623.GLenum + GL_INT_IMAGE_1D_ARRAY* = 0x905D.GLenum + GL_LOCATION* = 0x930E.GLenum + GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT* = 0x8CD7.GLenum + GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE* = 0x82AF.GLenum + GL_RESAMPLE_ZERO_FILL_SGIX* = 0x842F.GLenum + GL_VERTEX_ARRAY_BINDING_OES* = 0x85B5.GLenum + GL_MATRIX4_ARB* = 0x88C4.GLenum + GL_NEXT_BUFFER_NV* = -2 + GL_ELEMENT_ARRAY_BARRIER_BIT* = 0x00000002.GLbitfield + GL_RGBA16_EXT* = 0x805B.GLenum + GL_SEPARABLE_2D_EXT* = 0x8012.GLenum + GL_R11F_G11F_B10F_EXT* = 0x8C3A.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT* = 0x8CD4.GLenum + GL_IMAGE_2D_EXT* = 0x904D.GLenum + GL_DRAW_BUFFER6_NV* = 0x882B.GLenum + GL_TEXTURE_RANGE_LENGTH_APPLE* = 0x85B7.GLenum + GL_TEXTURE_RED_TYPE_ARB* = 0x8C10.GLenum + GL_ALPHA16F_ARB* = 0x881C.GLenum + GL_DEBUG_LOGGED_MESSAGES_ARB* = 0x9145.GLenum + GL_TRANSPOSE_MODELVIEW_MATRIX_ARB* = 0x84E3.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT* = 0x8C8F.GLenum + GL_MAX_CONVOLUTION_WIDTH* = 0x801A.GLenum + GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV* = 0x8E5B.GLenum + GL_PIXEL_TILE_CACHE_SIZE_SGIX* = 0x8145.GLenum + GL_4PASS_0_SGIS* = 0x80A4.GLenum + GL_PRIMITIVE_RESTART* = 0x8F9D.GLenum + GL_RG16_SNORM* = 0x8F99.GLenum + GL_SAMPLER_2D_SHADOW_EXT* = 0x8B62.GLenum + GL_FRONT* = 0x0404.GLenum + GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY* = 0x9103.GLenum + GL_SAMPLER_BINDING* = 0x8919.GLenum + GL_TEXTURE_2D_STACK_MESAX* = 0x875A.GLenum + GL_ASYNC_HISTOGRAM_SGIX* = 0x832C.GLenum + GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES* = 0x8B9B.GLenum + GL_OP_SET_LT_EXT* = 0x878D.GLenum + GL_INTERNALFORMAT_RED_TYPE* = 0x8278.GLenum + GL_AUX2* = 0x040B.GLenum + GL_CLAMP_FRAGMENT_COLOR* = 0x891B.GLenum + GL_BROWSER_DEFAULT_WEBGL* = 0x9244.GLenum + GL_IMAGE_CLASS_11_11_10* = 0x82C2.GLenum + GL_BUMP_ENVMAP_ATI* = 0x877B.GLenum + GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV* = 0x8DAD.GLenum + GL_RG_SNORM* = 0x8F91.GLenum + GL_BUMP_ROT_MATRIX_ATI* = 0x8775.GLenum + GL_UNIFORM_TYPE* = 0x8A37.GLenum + GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX* = 0x8403.GLenum + GL_TEXTURE_BINDING_CUBE_MAP_ARRAY* = 0x900A.GLenum + GL_LUMINANCE12* = 0x8041.GLenum + GL_QUERY_NO_WAIT_NV* = 0x8E14.GLenum + GL_TEXTURE_CUBE_MAP_ARRAY_ARB* = 0x9009.GLenum + GL_QUERY_BY_REGION_NO_WAIT_NV* = 0x8E16.GLenum + GL_FOG_END* = 0x0B64.GLenum + GL_OBJECT_LINK_STATUS_ARB* = 0x8B82.GLenum + GL_TEXTURE_COORD_ARRAY_SIZE* = 0x8088.GLenum + GL_SOURCE0_ALPHA_ARB* = 0x8588.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB* = 0x8518.GLenum + GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX* = 0x840B.GLenum + GL_STATIC_COPY* = 0x88E6.GLenum + GL_LINE_WIDTH_RANGE* = 0x0B22.GLenum + GL_VERTEX_SOURCE_ATI* = 0x8774.GLenum + GL_FLOAT_MAT4x3* = 0x8B6A.GLenum + GL_HALF_APPLE* = 0x140B.GLenum + GL_TEXTURE11* = 0x84CB.GLenum + GL_DECODE_EXT* = 0x8A49.GLenum + GL_VERTEX_ARRAY_STRIDE_EXT* = 0x807C.GLenum + GL_SAMPLER_BUFFER_EXT* = 0x8DC2.GLenum + GL_TEXTURE_LOD_BIAS_EXT* = 0x8501.GLenum + GL_MODULATE_SIGNED_ADD_ATI* = 0x8745.GLenum + GL_DEPTH_CLEAR_VALUE* = 0x0B73.GLenum + GL_COMPRESSED_ALPHA* = 0x84E9.GLenum + GL_TEXTURE_1D_STACK_MESAX* = 0x8759.GLenum + GL_TEXTURE_FIXED_SAMPLE_LOCATIONS* = 0x9107.GLenum + GL_LARGE_CCW_ARC_TO_NV* = 0x16.GLenum + GL_COMBINER1_NV* = 0x8551.GLenum + GL_ARRAY_SIZE* = 0x92FB.GLenum + GL_MAX_COMPUTE_IMAGE_UNIFORMS* = 0x91BD.GLenum + GL_TEXTURE_BINDING_EXTERNAL_OES* = 0x8D67.GLenum + GL_REG_26_ATI* = 0x893B.GLenum + GL_MUL_ATI* = 0x8964.GLenum + GL_STENCIL_BUFFER_BIT6_QCOM* = 0x00400000.GLbitfield + GL_INVALID_OPERATION* = 0x0502.GLenum + GL_COLOR_SUM* = 0x8458.GLenum + GL_OP_CROSS_PRODUCT_EXT* = 0x8797.GLenum + GL_COLOR_ATTACHMENT4_NV* = 0x8CE4.GLenum + GL_MAX_RECTANGLE_TEXTURE_SIZE_NV* = 0x84F8.GLenum + GL_BOOL_ARB* = 0x8B56.GLenum + GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB* = 0x8625.GLenum + GL_MODELVIEW8_ARB* = 0x8728.GLenum + GL_STENCIL_TEST* = 0x0B90.GLenum + GL_SRC_OVER_NV* = 0x9288.GLenum + GL_COMPRESSED_LUMINANCE* = 0x84EA.GLenum + GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV* = 0x8E5A.GLenum + GL_WEIGHT_ARRAY_TYPE_ARB* = 0x86A9.GLenum + GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV* = 0x887C.GLenum + GL_COLOR_ARRAY_STRIDE_EXT* = 0x8083.GLenum + GL_BLEND_SRC_ALPHA_EXT* = 0x80CB.GLenum + GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB* = 0x88B4.GLenum + GL_SCALAR_EXT* = 0x87BE.GLenum + GL_DEBUG_SEVERITY_MEDIUM_KHR* = 0x9147.GLenum + GL_IMAGE_SCALE_X_HP* = 0x8155.GLenum + GL_LUMINANCE6_ALPHA2_EXT* = 0x8044.GLenum + GL_OUTPUT_TEXTURE_COORD22_EXT* = 0x87B3.GLenum + GL_CURRENT_PROGRAM* = 0x8B8D.GLenum + GL_FRAGMENT_PROGRAM_ARB* = 0x8804.GLenum + GL_INFO_LOG_LENGTH* = 0x8B84.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_Z* = 0x8519.GLenum + GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES* = 0x898E.GLenum + GL_PRIMITIVE_RESTART_FIXED_INDEX* = 0x8D69.GLenum + GL_ARRAY_BUFFER_ARB* = 0x8892.GLenum + GL_DEPTH_STENCIL_MESA* = 0x8750.GLenum + GL_LUMINANCE8_OES* = 0x8040.GLenum + GL_REFLECTION_MAP_EXT* = 0x8512.GLenum + GL_PRIMITIVES_GENERATED* = 0x8C87.GLenum + GL_IMAGE_PIXEL_FORMAT* = 0x82A9.GLenum + GL_VERTEX_ARRAY_LIST_STRIDE_IBM* = 103080.GLenum + GL_MAP2_COLOR_4* = 0x0DB0.GLenum + GL_MULTIPLY_NV* = 0x9294.GLenum + GL_UNIFORM_BARRIER_BIT_EXT* = 0x00000004.GLbitfield + GL_STENCIL_BUFFER_BIT3_QCOM* = 0x00080000.GLbitfield + GL_REG_7_ATI* = 0x8928.GLenum + GL_STATIC_READ_ARB* = 0x88E5.GLenum + GL_MATRIX2_ARB* = 0x88C2.GLenum + GL_STENCIL_BUFFER_BIT5_QCOM* = 0x00200000.GLbitfield + GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB* = 0x8B4C.GLenum + GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG* = 0x8C03.GLenum + GL_R1UI_T2F_N3F_V3F_SUN* = 0x85CA.GLenum + GL_TEXTURE27_ARB* = 0x84DB.GLenum + GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES* = 0x8CDA.GLenum + GL_MAX_PROGRAM_TEXEL_OFFSET* = 0x8905.GLenum + GL_INT_SAMPLER_2D_ARRAY_EXT* = 0x8DCF.GLenum + GL_DRAW_BUFFER9_EXT* = 0x882E.GLenum + GL_RGB5_A1_EXT* = 0x8057.GLenum + GL_FIELDS_NV* = 0x8E27.GLenum + GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV* = 0x862E.GLenum + GL_SHADER_COMPILER* = 0x8DFA.GLenum + GL_SRC2_ALPHA* = 0x858A.GLenum + GL_TRACE_NAME_MESA* = 0x8756.GLenum + GL_MIRROR_CLAMP_TO_EDGE* = 0x8743.GLint + GL_OPERAND0_RGB_EXT* = 0x8590.GLenum + GL_UNSIGNED_BYTE_2_3_3_REV_EXT* = 0x8362.GLenum + GL_UNSIGNED_INT_2_10_10_10_REV* = 0x8368.GLenum + GL_MAX_CLIP_DISTANCES* = 0x0D32.GLenum + GL_MAP2_TEXTURE_COORD_3* = 0x0DB5.GLenum + GL_DUAL_LUMINANCE16_SGIS* = 0x8117.GLenum + GL_TEXTURE_UPDATE_BARRIER_BIT_EXT* = 0x00000100.GLbitfield + GL_IMAGE_BUFFER_EXT* = 0x9051.GLenum + GL_REDUCE_EXT* = 0x8016.GLenum + GL_EVAL_VERTEX_ATTRIB9_NV* = 0x86CF.GLenum + GL_IMAGE_CLASS_4_X_32* = 0x82B9.GLenum + GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT* = 0x8DE3.GLenum + GL_FRAGMENTS_INSTRUMENT_MAX_SGIX* = 0x8315.GLenum + GL_REG_28_ATI* = 0x893D.GLenum + GL_VARIABLE_B_NV* = 0x8524.GLenum + GL_GET_TEXTURE_IMAGE_TYPE* = 0x8292.GLenum + GL_PERCENTAGE_AMD* = 0x8BC3.GLenum + GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB* = 0x8DE1.GLenum + GL_MAX_COMPUTE_UNIFORM_BLOCKS* = 0x91BB.GLenum + GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_APPLE* = 0x8D56.GLenum + GL_PROVOKING_VERTEX* = 0x8E4F.GLenum + GL_FRAMEZOOM_FACTOR_SGIX* = 0x818C.GLenum + GL_COLOR_TABLE_ALPHA_SIZE* = 0x80DD.GLenum + GL_PIXEL_TEXTURE_SGIS* = 0x8353.GLenum + GL_MODELVIEW26_ARB* = 0x873A.GLenum + GL_MAX_DEBUG_MESSAGE_LENGTH_KHR* = 0x9143.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT* = 0x8519.GLenum + GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT* = 0x87D2.GLenum + GL_DRAW_INDIRECT_LENGTH_NV* = 0x8F42.GLenum + GL_OPERAND2_RGB_ARB* = 0x8592.GLenum + GL_TESS_EVALUATION_SHADER* = 0x8E87.GLenum + GL_INTERLACE_SGIX* = 0x8094.GLenum + GL_HARDLIGHT_NV* = 0x929B.GLenum + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT* = 0x8CD0.GLenum + GL_OUTPUT_TEXTURE_COORD6_EXT* = 0x87A3.GLenum + GL_SIGNED_LUMINANCE_NV* = 0x8701.GLenum + GL_CON_13_ATI* = 0x894E.GLenum + GL_CURRENT_TANGENT_EXT* = 0x843B.GLenum + GL_UNSIGNED_INT_IMAGE_3D* = 0x9064.GLenum + GL_MODELVIEW24_ARB* = 0x8738.GLenum + GL_EVAL_FRACTIONAL_TESSELLATION_NV* = 0x86C5.GLenum + GL_POINT_SPRITE_NV* = 0x8861.GLenum + GL_MULTISAMPLE_EXT* = 0x809D.GLenum + GL_INT64_VEC3_NV* = 0x8FEA.GLenum + GL_ABGR_EXT* = 0x8000.GLenum + GL_MAX_GENERAL_COMBINERS_NV* = 0x854D.GLenum + GL_NUM_PROGRAM_BINARY_FORMATS* = 0x87FE.GLenum + GL_TEXTURE_LO_SIZE_NV* = 0x871C.GLenum + GL_INT_IMAGE_1D_ARRAY_EXT* = 0x905D.GLenum + GL_MULTISAMPLE_BUFFER_BIT3_QCOM* = 0x08000000.GLbitfield + GL_TEXTURE_GEN_MODE_OES* = 0x2500.GLenum + GL_SECONDARY_COLOR_ARRAY_STRIDE* = 0x845C.GLenum + GL_ELEMENT_ARRAY_TYPE_APPLE* = 0x8A0D.GLenum + GL_UNPACK_IMAGE_HEIGHT_EXT* = 0x806E.GLenum + GL_PALETTE4_R5_G6_B5_OES* = 0x8B92.GLenum + GL_TEXTURE_RED_SIZE* = 0x805C.GLenum + GL_COLOR_ATTACHMENT7_EXT* = 0x8CE7.GLenum + GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET* = 0x8E5F.GLenum + GL_DRAW_BUFFER11* = 0x8830.GLenum + GL_MODELVIEW0_MATRIX_EXT* = 0x0BA6.GLenum + GL_LAYER_PROVOKING_VERTEX* = 0x825E.GLenum + GL_TEXTURE14* = 0x84CE.GLenum + GL_ALPHA8_EXT* = 0x803C.GLenum + GL_GENERIC_ATTRIB_NV* = 0x8C7D.GLenum + GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES* = 0x8B8B.GLenum + GL_STENCIL_ATTACHMENT_OES* = 0x8D20.GLenum + GL_MAX_VARYING_FLOATS* = 0x8B4B.GLenum + GL_RGB_SNORM* = 0x8F92.GLenum + GL_SECONDARY_COLOR_ARRAY_TYPE_EXT* = 0x845B.GLenum + GL_MAX_PROGRAM_LOOP_DEPTH_NV* = 0x88F7.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER* = 0x8CD4.GLenum + GL_MAX_MODELVIEW_STACK_DEPTH* = 0x0D36.GLenum + GL_CON_23_ATI* = 0x8958.GLenum + GL_VERTEX_ARRAY_RANGE_POINTER_APPLE* = 0x8521.GLenum + GL_VERTEX_ARRAY_BUFFER_BINDING* = 0x8896.GLenum + GL_VERTEX_STREAM2_ATI* = 0x876E.GLenum + GL_STENCIL* = 0x1802.GLenum + GL_IMAGE_2D_ARRAY_EXT* = 0x9053.GLenum + GL_RGBA8* = 0x8058.GLenum + GL_TEXTURE_SPARSE_ARB* = 0x91A6.GLenum + GL_PIXEL_TEX_GEN_ALPHA_NO_REPLACE_SGIX* = 0x8188.GLenum + GL_SECONDARY_INTERPOLATOR_ATI* = 0x896D.GLenum + GL_MAX_COMBINED_DIMENSIONS* = 0x8282.GLenum + GL_DEBUG_TYPE_POP_GROUP* = 0x826A.GLenum + GL_IMAGE_CLASS_4_X_8* = 0x82BF.GLenum + GL_VERTEX_ARRAY_RANGE_VALID_NV* = 0x851F.GLenum + GL_LUMINANCE_ALPHA8UI_EXT* = 0x8D81.GLenum + GL_RGBA32F_ARB* = 0x8814.GLenum + GL_GLYPH_HEIGHT_BIT_NV* = 0x02.GLbitfield + GL_FOG_COORD_ARRAY_BUFFER_BINDING* = 0x889D.GLenum + GL_TRACE_OPERATIONS_BIT_MESA* = 0x0001.GLbitfield + GL_INT8_VEC4_NV* = 0x8FE3.GLenum + GL_VERTEX_BINDING_STRIDE* = 0x82D8.GLenum + GL_LIGHT_ENV_MODE_SGIX* = 0x8407.GLenum + GL_PROXY_TEXTURE_1D_EXT* = 0x8063.GLenum + GL_CON_31_ATI* = 0x8960.GLenum + GL_TEXTURE_BORDER_COLOR* = 0x1004.GLenum + GL_ELEMENT_ARRAY_POINTER_APPLE* = 0x8A0E.GLenum + GL_NAME_LENGTH* = 0x92F9.GLenum + GL_PIXEL_COUNT_AVAILABLE_NV* = 0x8867.GLenum + GL_IUI_V3F_EXT* = 0x81AE.GLenum + GL_OBJECT_LINE_SGIS* = 0x81F7.GLenum + GL_T2F_N3F_V3F* = 0x2A2B.GLenum + GL_TRUE* = true.GLboolean + GL_COMPARE_REF_TO_TEXTURE_EXT* = 0x884E.GLenum + GL_MAX_3D_TEXTURE_SIZE* = 0x8073.GLenum + GL_LUMINANCE16_ALPHA16_EXT* = 0x8048.GLenum + GL_DRAW_INDIRECT_ADDRESS_NV* = 0x8F41.GLenum + GL_TEXTURE_IMAGE_FORMAT* = 0x828F.GLenum + GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES* = 0x898D.GLenum + GL_TEXTURE_RECTANGLE_ARB* = 0x84F5.GLenum + GL_TEXTURE_INDEX_SIZE_EXT* = 0x80ED.GLenum + GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV* = 0x8F2A.GLenum + GL_DEBUG_CALLBACK_USER_PARAM* = 0x8245.GLenum + GL_INTENSITY8_SNORM* = 0x9017.GLenum + GL_DISTANCE_ATTENUATION_EXT* = 0x8129.GLenum + GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS* = 0x90CC.GLenum + GL_ATTRIB_ARRAY_POINTER_NV* = 0x8645.GLenum + GL_OBJECT_TYPE* = 0x9112.GLenum + GL_PROGRAM_KHR* = 0x82E2.GLenum + GL_SOURCE0_ALPHA_EXT* = 0x8588.GLenum + GL_PIXEL_MAP_I_TO_G_SIZE* = 0x0CB3.GLenum + GL_RGBA_MODE* = 0x0C31.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR* = 0x93D6.GLenum + GL_MAX_ELEMENTS_VERTICES_EXT* = 0x80E8.GLenum + GL_DEBUG_SOURCE_SHADER_COMPILER* = 0x8248.GLenum + GL_ARC_TO_NV* = 0xFE.GLenum + GL_CON_6_ATI* = 0x8947.GLenum + GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT* = 0x87CE.GLenum + GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE* = 0x8A05.GLenum + GL_R16_SNORM* = 0x8F98.GLenum + GL_DOUBLE_VEC2_EXT* = 0x8FFC.GLenum + GL_UNSIGNED_INT8_VEC4_NV* = 0x8FEF.GLenum + GL_POST_CONVOLUTION_RED_SCALE* = 0x801C.GLenum + GL_FULL_STIPPLE_HINT_PGI* = 0x1A219.GLenum + GL_ACTIVE_ATTRIBUTES* = 0x8B89.GLenum + GL_TEXTURE_MATERIAL_FACE_EXT* = 0x8351.GLenum + GL_INCR_WRAP_OES* = 0x8507.GLenum + GL_UNPACK_COMPRESSED_BLOCK_WIDTH* = 0x9127.GLenum + GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT* = 0x8C73.GLenum + GL_MAX_VERTEX_SHADER_LOCALS_EXT* = 0x87C9.GLenum + GL_NUM_VIDEO_CAPTURE_STREAMS_NV* = 0x9024.GLenum + GL_DRAW_BUFFER3_ARB* = 0x8828.GLenum + GL_COMBINER_COMPONENT_USAGE_NV* = 0x8544.GLenum + GL_ELEMENT_ARRAY_POINTER_ATI* = 0x876A.GLenum + GL_RGB8UI_EXT* = 0x8D7D.GLenum + GL_RGBA8I* = 0x8D8E.GLenum + GL_TEXTURE_WIDTH_QCOM* = 0x8BD2.GLenum + GL_DOT3_RGB* = 0x86AE.GLenum + GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV* = 0x903B.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_X* = 0x8516.GLenum + GL_UNIFORM_BUFFER_SIZE* = 0x8A2A.GLenum + GL_OPERAND1_ALPHA* = 0x8599.GLenum + GL_TEXTURE_INTENSITY_SIZE_EXT* = 0x8061.GLenum + GL_DEBUG_TYPE_OTHER* = 0x8251.GLenum + GL_MAX_TESS_PATCH_COMPONENTS* = 0x8E84.GLenum + GL_UNIFORM_BUFFER_BINDING* = 0x8A28.GLenum + GL_INTENSITY_FLOAT16_APPLE* = 0x881D.GLenum + GL_TEXTURE_BLUE_SIZE* = 0x805E.GLenum + GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT* = 0x919F.GLenum + GL_TEXTURE_SWIZZLE_G* = 0x8E43.GLenum + GL_MAX_PROGRAM_TEXEL_OFFSET_EXT* = 0x8905.GLenum + GL_COLOR_BUFFER_BIT* = 0x00004000.GLbitfield + GL_ALPHA_FLOAT32_APPLE* = 0x8816.GLenum + GL_PROXY_TEXTURE_2D_EXT* = 0x8064.GLenum + GL_STENCIL_COMPONENTS* = 0x8285.GLenum + GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV* = 0x9026.GLenum + GL_TEXTURE_COMPRESSED_ARB* = 0x86A1.GLenum + GL_OBJECT_SUBTYPE_ARB* = 0x8B4F.GLenum + GL_MAX_PROGRAM_PARAMETERS_ARB* = 0x88A9.GLenum + GL_OFFSET_TEXTURE_2D_MATRIX_NV* = 0x86E1.GLenum + GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI* = 0x87F7.GLenum + GL_PATCH_VERTICES* = 0x8E72.GLenum + GL_NEGATIVE_Y_EXT* = 0x87DA.GLenum + GL_INT_2_10_10_10_REV* = 0x8D9F.GLenum + GL_READ_FRAMEBUFFER_BINDING_NV* = 0x8CAA.GLenum + GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI* = 0x80D2.GLenum + GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS* = 0x90DA.GLenum + GL_IMAGE_COMPATIBILITY_CLASS* = 0x82A8.GLenum + GL_FLOAT_MAT4* = 0x8B5C.GLenum + GL_FIELD_LOWER_NV* = 0x9023.GLenum + GL_UNPACK_IMAGE_HEIGHT* = 0x806E.GLenum + GL_PATH_COMMAND_COUNT_NV* = 0x909D.GLenum + GL_UNSIGNED_SHORT_4_4_4_4_EXT* = 0x8033.GLenum + GL_VIEW_CLASS_S3TC_DXT3_RGBA* = 0x82CE.GLenum + GL_STENCIL_BUFFER_BIT1_QCOM* = 0x00020000.GLbitfield + GL_BLOCK_INDEX* = 0x92FD.GLenum + GL_BUMP_TARGET_ATI* = 0x877C.GLenum + GL_PATH_STROKE_COVER_MODE_NV* = 0x9083.GLenum + GL_INT_IMAGE_2D_RECT* = 0x905A.GLenum + GL_VECTOR_EXT* = 0x87BF.GLenum + GL_INDEX_ARRAY_BUFFER_BINDING* = 0x8899.GLenum + GL_SAMPLER_2D_SHADOW* = 0x8B62.GLenum + GL_OBJECT_BUFFER_SIZE_ATI* = 0x8764.GLenum + GL_NORMALIZED_RANGE_EXT* = 0x87E0.GLenum + GL_DEPTH_COMPONENT32_OES* = 0x81A7.GLenum + GL_CON_9_ATI* = 0x894A.GLenum + GL_VIRTUAL_PAGE_SIZE_X_ARB* = 0x9195.GLenum + GL_LESS* = 0x0201.GLenum + GL_FRAMEBUFFER_UNSUPPORTED_OES* = 0x8CDD.GLenum + GL_CON_19_ATI* = 0x8954.GLenum + GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB* = 0x88A2.GLenum + GL_MAX_TEXTURE_COORDS_ARB* = 0x8871.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_MODE* = 0x8C7F.GLenum + GL_TEXTURE_1D_BINDING_EXT* = 0x8068.GLenum + GL_LINE_TOKEN* = 0x0702.GLenum + GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES* = 0x8CD7.GLenum + GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV* = 0x9036.GLenum + GL_TEXTURE_SWIZZLE_R* = 0x8E42.GLenum + GL_PIXEL_UNPACK_BUFFER_ARB* = 0x88EC.GLenum + GL_UNKNOWN_CONTEXT_RESET_EXT* = 0x8255.GLenum + GL_PROGRAM_ERROR_POSITION_NV* = 0x864B.GLenum + GL_ONE_MINUS_CONSTANT_COLOR* = 0x8002.GLenum + GL_POST_COLOR_MATRIX_GREEN_SCALE* = 0x80B5.GLenum + GL_TEXTURE_CUBE_MAP_SEAMLESS* = 0x884F.GLenum + GL_DRAW_BUFFER2* = 0x8827.GLenum + GL_STENCIL_INDEX* = 0x1901.GLenum + GL_FOG_DENSITY* = 0x0B62.GLenum + GL_MATRIX27_ARB* = 0x88DB.GLenum + GL_CURRENT_NORMAL* = 0x0B02.GLenum + GL_AFFINE_3D_NV* = 0x9094.GLenum + GL_STATIC_COPY_ARB* = 0x88E6.GLenum + GL_4X_BIT_ATI* = 0x00000002.GLbitfield + GL_COLOR_BUFFER_BIT3_QCOM* = 0x00000008.GLbitfield + GL_TEXTURE_MATRIX* = 0x0BA8.GLenum + GL_UNDEFINED_APPLE* = 0x8A1C.GLenum + GL_COLOR_TABLE_LUMINANCE_SIZE_SGI* = 0x80DE.GLenum + GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY* = 0x9061.GLenum + GL_RELATIVE_ARC_TO_NV* = 0xFF.GLenum + GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL* = 0x9241.GLenum + GL_READ_FRAMEBUFFER_BINDING_EXT* = 0x8CAA.GLenum + GL_TEXTURE_WRAP_R_OES* = 0x8072.GLenum + GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT* = 0x8DDD.GLenum + GL_TEXTURE_CUBE_MAP_EXT* = 0x8513.GLenum + GL_COMMAND_BARRIER_BIT_EXT* = 0x00000040.GLbitfield + GL_DEBUG_SEVERITY_NOTIFICATION* = 0x826B.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR* = 0x93D8.GLenum + GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS* = 0x8C8B.GLenum + GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV* = 0x90D0.GLenum + GL_INT_IMAGE_2D_EXT* = 0x9058.GLenum + GL_RGB_S3TC* = 0x83A0.GLenum + GL_SUCCESS_NV* = 0x902F.GLenum + GL_MATRIX_INDEX_ARRAY_SIZE_OES* = 0x8846.GLenum + GL_VIEW_CLASS_8_BITS* = 0x82CB.GLenum + GL_DONT_CARE* = 0x1100.GLenum + GL_FOG_COORDINATE_ARRAY* = 0x8457.GLenum + GL_DRAW_BUFFER9* = 0x882E.GLenum + GL_TEXTURE28_ARB* = 0x84DC.GLenum + GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB* = 0x8E5F.GLenum + GL_TEXTURE21* = 0x84D5.GLenum + GL_TRANSLATE_Y_NV* = 0x908F.GLenum + GL_MODELVIEW17_ARB* = 0x8731.GLenum + GL_ALPHA_FLOAT16_ATI* = 0x881C.GLenum + GL_DEPTH_STENCIL_OES* = 0x84F9.GLenum + GL_QUAD_MESH_SUN* = 0x8614.GLenum + GL_PROGRAM_ADDRESS_REGISTERS_ARB* = 0x88B0.GLenum + GL_VERTEX_BINDING_OFFSET* = 0x82D7.GLenum + GL_FIRST_TO_REST_NV* = 0x90AF.GLenum + GL_SHADE_MODEL* = 0x0B54.GLenum + GL_INT_IMAGE_2D_ARRAY_EXT* = 0x905E.GLenum + GL_FRONT_FACE* = 0x0B46.GLenum + GL_PRIMITIVE_RESTART_INDEX* = 0x8F9E.GLenum + GL_LUMINANCE8* = 0x8040.GLenum + GL_COVERAGE_ALL_FRAGMENTS_NV* = 0x8ED5.GLenum + GL_FRAGMENT_ALPHA_MODULATE_IMG* = 0x8C08.GLenum + GL_CLIP_PLANE3_IMG* = 0x3003.GLenum + GL_EVAL_VERTEX_ATTRIB15_NV* = 0x86D5.GLenum + GL_SYNC_GPU_COMMANDS_COMPLETE* = 0x9117.GLenum + GL_FALSE* = false.GLboolean + GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR* = 0x826C.GLenum + GL_STENCIL_ATTACHMENT_EXT* = 0x8D20.GLenum + GL_DST_ATOP_NV* = 0x928F.GLenum + GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN* = 0x85C1.GLenum + GL_COMBINE4_NV* = 0x8503.GLenum + GL_MINMAX_SINK_EXT* = 0x8030.GLenum + GL_RG16I* = 0x8239.GLenum + GL_BGRA_IMG* = 0x80E1.GLenum + GL_REFERENCED_BY_COMPUTE_SHADER* = 0x930B.GLenum + GL_MIN_LOD_WARNING_AMD* = 0x919C.GLenum + GL_READ_BUFFER_EXT* = 0x0C02.GLenum + GL_RGBA8UI_EXT* = 0x8D7C.GLenum + GL_LINE_BIT* = 0x00000004.GLbitfield + GL_CONDITION_SATISFIED* = 0x911C.GLenum + GL_SLUMINANCE_ALPHA* = 0x8C44.GLenum + GL_FOG_COORDINATE_ARRAY_TYPE* = 0x8454.GLenum + GL_EXPAND_NORMAL_NV* = 0x8538.GLenum + GL_TEXTURE_2D_ARRAY_EXT* = 0x8C1A.GLenum + GL_SAMPLER_2D_RECT_ARB* = 0x8B63.GLenum + GL_CLAMP_TO_BORDER_NV* = 0x812D.GLint + GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB* = 0x8DE0.GLenum + GL_TEXCOORD2_BIT_PGI* = 0x20000000.GLbitfield + GL_MATRIX0_ARB* = 0x88C0.GLenum + GL_STENCIL_BUFFER_BIT2_QCOM* = 0x00040000.GLbitfield + GL_COLOR_MATRIX_SGI* = 0x80B1.GLenum + GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI* = 0x87F4.GLenum + GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT* = 0x8CDC.GLenum + GL_LEFT* = 0x0406.GLenum + GL_LO_SCALE_NV* = 0x870F.GLenum + GL_STRICT_DEPTHFUNC_HINT_PGI* = 0x1A216.GLenum + GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS* = 0x8E1E.GLenum + GL_REPEAT* = 0x2901.GLint + GL_DEBUG_TYPE_PORTABILITY_ARB* = 0x824F.GLenum + GL_MAX_FRAMEBUFFER_LAYERS* = 0x9317.GLenum + GL_TRIANGLE_STRIP* = 0x0005.GLenum + GL_RECLAIM_MEMORY_HINT_PGI* = 0x1A1FE.GLenum + GL_RELATIVE_LINE_TO_NV* = 0x05.GLenum + GL_MAX_LIGHTS* = 0x0D31.GLenum + GL_MULTISAMPLE_BIT* = 0x20000000.GLbitfield + GL_READ_PIXELS* = 0x828C.GLenum + GL_DISCRETE_AMD* = 0x9006.GLenum + GL_QUAD_TEXTURE_SELECT_SGIS* = 0x8125.GLenum + GL_CON_25_ATI* = 0x895A.GLenum + GL_BUFFER_IMMUTABLE_STORAGE* = 0x821F.GLenum + GL_FLOAT_R16_NV* = 0x8884.GLenum + GL_GREEN_INTEGER_EXT* = 0x8D95.GLenum + cGL_FIXED* = 0x140C.GLenum + GL_LIST_PRIORITY_SGIX* = 0x8182.GLenum + GL_DRAW_BUFFER6_EXT* = 0x882B.GLenum + GL_OFFSET_TEXTURE_BIAS_NV* = 0x86E3.GLenum + GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB* = 0x8645.GLenum + GL_MALI_SHADER_BINARY_ARM* = 0x8F60.GLenum + GL_RGB_422_APPLE* = 0x8A1F.GLenum + GL_R1UI_N3F_V3F_SUN* = 0x85C7.GLenum + GL_VERTEX_ARRAY_OBJECT_EXT* = 0x9154.GLenum + GL_UNSIGNED_INT_10F_11F_11F_REV* = 0x8C3B.GLenum + GL_VERSION_ES_CM_1_1* = 1.GLenum + GL_CLEAR_TEXTURE* = 0x9365.GLenum + GL_FLOAT16_VEC3_NV* = 0x8FFA.GLenum + GL_TEXTURE_LUMINANCE_TYPE* = 0x8C14.GLenum + GL_TRANSFORM_FEEDBACK* = 0x8E22.GLenum + GL_POST_CONVOLUTION_COLOR_TABLE* = 0x80D1.GLenum + GL_DEPTH_TEST* = 0x0B71.GLenum + GL_CON_1_ATI* = 0x8942.GLenum + GL_FRAGMENT_SHADER_ATI* = 0x8920.GLenum + GL_SAMPLER_1D_ARRAY_SHADOW* = 0x8DC3.GLenum + GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT* = 0x90DF.GLenum + GL_MAX_SERVER_WAIT_TIMEOUT* = 0x9111.GLenum + GL_VERTEX_SHADER_BIT_EXT* = 0x00000001.GLbitfield + GL_TEXTURE_BINDING_CUBE_MAP_OES* = 0x8514.GLenum + GL_PIXEL_MAP_S_TO_S_SIZE* = 0x0CB1.GLenum + GL_CURRENT_OCCLUSION_QUERY_ID_NV* = 0x8865.GLenum + GL_TIMEOUT_IGNORED_APPLE* = 0xFFFFFFFFFFFFFFFF.GLenum + GL_MAX_COMPUTE_UNIFORM_COMPONENTS* = 0x8263.GLenum + GL_COPY_PIXEL_TOKEN* = 0x0706.GLenum + GL_SPOT_CUTOFF* = 0x1206.GLenum + GL_FRACTIONAL_EVEN* = 0x8E7C.GLenum + GL_MAP1_VERTEX_ATTRIB6_4_NV* = 0x8666.GLenum + GL_TRIANGLE_LIST_SUN* = 0x81D7.GLenum + GL_ATOMIC_COUNTER_BUFFER_START* = 0x92C2.GLenum + GL_MAX_ELEMENTS_VERTICES* = 0x80E8.GLenum + GL_COLOR_ATTACHMENT9_EXT* = 0x8CE9.GLenum + GL_ACCUM_CLEAR_VALUE* = 0x0B80.GLenum + GL_TEXTURE_COORD_ARRAY_LENGTH_NV* = 0x8F2F.GLenum + GL_DRAW_BUFFER3_EXT* = 0x8828.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT* = 0x8517.GLenum + GL_C4UB_V3F* = 0x2A23.GLenum + GL_MAX_PROGRAM_ATTRIBS_ARB* = 0x88AD.GLenum + GL_PIXEL_TILE_CACHE_INCREMENT_SGIX* = 0x813F.GLenum + GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB* = 0x8DA9.GLenum + GL_CON_8_ATI* = 0x8949.GLenum + GL_POST_COLOR_MATRIX_ALPHA_BIAS* = 0x80BB.GLenum + GL_RENDERBUFFER_WIDTH* = 0x8D42.GLenum + GL_VERTEX_ID_NV* = 0x8C7B.GLenum + GL_STRICT_LIGHTING_HINT_PGI* = 0x1A217.GLenum + GL_COMPRESSED_RGBA8_ETC2_EAC_OES* = 0x9278.GLenum + GL_PACK_COMPRESSED_BLOCK_WIDTH* = 0x912B.GLenum + GL_ZERO_EXT* = 0x87DD.GLenum + GL_DEBUG_SOURCE_OTHER* = 0x824B.GLenum + GL_MAP_UNSYNCHRONIZED_BIT* = 0x0020.GLbitfield + GL_VERTEX_ARRAY_POINTER* = 0x808E.GLenum + GL_FLOAT_RGBA_NV* = 0x8883.GLenum + GL_WEIGHT_ARRAY_STRIDE_OES* = 0x86AA.GLenum + GL_UNPACK_ROW_BYTES_APPLE* = 0x8A16.GLenum + GL_CURRENT_COLOR* = 0x0B00.GLenum + GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT* = 0x8CD7.GLenum + GL_MAX_NAME_STACK_DEPTH* = 0x0D37.GLenum + GL_SHADER_STORAGE_BUFFER_START* = 0x90D4.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT* = 0x8C7F.GLenum + GL_PATH_GEN_COMPONENTS_NV* = 0x90B3.GLenum + GL_AUTO_GENERATE_MIPMAP* = 0x8295.GLenum + GL_UNSIGNED_INT_5_9_9_9_REV* = 0x8C3E.GLenum + GL_VIEWPORT* = 0x0BA2.GLenum + GL_MAX_VERTEX_STREAMS_ATI* = 0x876B.GLenum + GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT* = 0x87CB.GLenum + GL_STENCIL_CLEAR_VALUE* = 0x0B91.GLenum + GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT* = 0x9069.GLenum + GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX* = 0x8409.GLenum + GL_FRAGMENT_SHADER_BIT_EXT* = 0x00000002.GLbitfield + GL_COLOR_SUM_ARB* = 0x8458.GLenum + GL_RGBA4_DXT5_S3TC* = 0x83A5.GLenum + GL_INT_IMAGE_CUBE* = 0x905B.GLenum + GL_ACTIVE_ATOMIC_COUNTER_BUFFERS* = 0x92D9.GLenum + GL_INTERNALFORMAT_GREEN_SIZE* = 0x8272.GLenum + GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV* = 0x8855.GLenum + GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI* = 0x87F1.GLenum + GL_REG_24_ATI* = 0x8939.GLenum + GL_MULT* = 0x0103.GLenum + GL_RGBA2* = 0x8055.GLenum + GL_CONVOLUTION_WIDTH_EXT* = 0x8018.GLenum + GL_STENCIL_EXT* = 0x1802.GLenum + GL_PATH_STROKE_WIDTH_NV* = 0x9075.GLenum + GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB* = 0x8247.GLenum + GL_QUERY_COUNTER_BITS* = 0x8864.GLenum + GL_OUTPUT_FOG_EXT* = 0x87BD.GLenum + GL_POST_COLOR_MATRIX_RED_BIAS* = 0x80B8.GLenum + GL_UNSIGNED_INT_10_10_10_2* = 0x8036.GLenum + GL_INT_SAMPLER_1D* = 0x8DC9.GLenum + GL_INT_IMAGE_2D_MULTISAMPLE_EXT* = 0x9060.GLenum + GL_RENDERBUFFER_INTERNAL_FORMAT_OES* = 0x8D44.GLenum + GL_TRACE_PIXELS_BIT_MESA* = 0x0010.GLbitfield + GL_FAILURE_NV* = 0x9030.GLenum + GL_INT_SAMPLER_3D_EXT* = 0x8DCB.GLenum + GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV* = 0x8DA1.GLenum + GL_OBJECT_DISTANCE_TO_POINT_SGIS* = 0x81F1.GLenum + GL_BLEND_SRC_RGB_OES* = 0x80C9.GLenum + GL_LUMINANCE4_ALPHA4_OES* = 0x8043.GLenum + GL_REG_4_ATI* = 0x8925.GLenum + GL_SHADING_LANGUAGE_VERSION_ARB* = 0x8B8C.GLenum + GL_RGBA16F_ARB* = 0x881A.GLenum + GL_R32F* = 0x822E.GLenum + GL_COMPRESSED_SRGB_S3TC_DXT1_NV* = 0x8C4C.GLenum + GL_TESS_CONTROL_OUTPUT_VERTICES* = 0x8E75.GLenum + GL_ONE_MINUS_DST_COLOR* = 0x0307.GLenum + GL_MATRIX19_ARB* = 0x88D3.GLenum + GL_INT_SAMPLER_2D_RECT* = 0x8DCD.GLenum + GL_POST_CONVOLUTION_GREEN_SCALE_EXT* = 0x801D.GLenum + GL_CLIP_DISTANCE5* = 0x3005.GLenum + GL_HISTOGRAM_RED_SIZE_EXT* = 0x8028.GLenum + GL_INTENSITY_FLOAT32_APPLE* = 0x8817.GLenum + GL_MODULATE_ADD_ATI* = 0x8744.GLenum + GL_NEGATIVE_X_EXT* = 0x87D9.GLenum + GL_REG_21_ATI* = 0x8936.GLenum + GL_STENCIL_RENDERABLE* = 0x8288.GLenum + GL_FOG_COORD_ARRAY_STRIDE* = 0x8455.GLenum + GL_FACTOR_MAX_AMD* = 0x901D.GLenum + GL_LUMINANCE16_EXT* = 0x8042.GLenum + GL_VARIANT_ARRAY_POINTER_EXT* = 0x87E9.GLenum + GL_DECAL* = 0x2101.GLenum + GL_SIGNED_ALPHA8_NV* = 0x8706.GLenum + GL_ALPHA_BITS* = 0x0D55.GLenum + GL_MATRIX29_ARB* = 0x88DD.GLenum + GL_FOG* = 0x0B60.GLenum + GL_INDEX_ARRAY_LIST_STRIDE_IBM* = 103083.GLenum + GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS* = 0x90C9.GLenum + GL_RGBA4_S3TC* = 0x83A3.GLenum + GL_LUMINANCE16_ALPHA16* = 0x8048.GLenum + GL_PROXY_TEXTURE_RECTANGLE* = 0x84F7.GLenum + GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV* = 0x8DA4.GLenum + GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER* = 0x84F0.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE* = 0x8CD3.GLenum + GL_COLOR_TABLE_GREEN_SIZE_SGI* = 0x80DB.GLenum + GL_TEXTURE_PRE_SPECULAR_HP* = 0x8169.GLenum + GL_SHADOW_ATTENUATION_EXT* = 0x834E.GLenum + GL_SIGNED_RGB_NV* = 0x86FE.GLenum + GL_CLIENT_ALL_ATTRIB_BITS* = 0xFFFFFFFF.GLbitfield + GL_DEPTH_ATTACHMENT_EXT* = 0x8D00.GLenum + GL_DEBUG_SOURCE_API_KHR* = 0x8246.GLenum + GL_COLOR_INDEXES* = 0x1603.GLenum + GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH* = 0x8243.GLenum + GL_TEXTURE_BINDING_1D* = 0x8068.GLenum + GL_UNSIGNED_INT_SAMPLER_2D* = 0x8DD2.GLenum + GL_DRAW_BUFFER9_NV* = 0x882E.GLenum + GL_RED* = 0x1903.GLenum + GL_LINE_STRIP_ADJACENCY_EXT* = 0x000B.GLenum + GL_NUM_PASSES_ATI* = 0x8970.GLenum + GL_MAT_DIFFUSE_BIT_PGI* = 0x00400000.GLbitfield + GL_LUMINANCE_INTEGER_EXT* = 0x8D9C.GLenum + GL_PIXEL_MAP_I_TO_I* = 0x0C70.GLenum + GL_SLUMINANCE8_ALPHA8_NV* = 0x8C45.GLenum + GL_RGBA4_OES* = 0x8056.GLenum + GL_COMPRESSED_SIGNED_R11_EAC* = 0x9271.GLenum + GL_FRAGMENT_LIGHT4_SGIX* = 0x8410.GLenum + GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV* = 0x8C80.GLenum + GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT* = 0x8C4D.GLenum + GL_READ_FRAMEBUFFER_APPLE* = 0x8CA8.GLenum + GL_DRAW_BUFFER15_ARB* = 0x8834.GLenum + GL_INSTRUMENT_MEASUREMENTS_SGIX* = 0x8181.GLenum + GL_REG_15_ATI* = 0x8930.GLenum + GL_UNSIGNED_INT_IMAGE_1D_ARRAY* = 0x9068.GLenum + GL_COMPUTE_LOCAL_WORK_SIZE* = 0x8267.GLenum + GL_RGBA32I* = 0x8D82.GLenum + GL_VERTEX_ATTRIB_MAP2_APPLE* = 0x8A01.GLenum + GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR* = 0x824D.GLenum + GL_READ_FRAMEBUFFER_BINDING_ANGLE* = 0x8CAA.GLenum + GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR* = 0x8247.GLenum + GL_OP_FRAC_EXT* = 0x8789.GLenum + GL_RGB_FLOAT32_APPLE* = 0x8815.GLenum + GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER* = 0x8A44.GLenum + GL_NORMAL_ARRAY* = 0x8075.GLenum + GL_TEXTURE21_ARB* = 0x84D5.GLenum + GL_WRITE_ONLY_OES* = 0x88B9.GLenum + GL_TEXTURE0_ARB* = 0x84C0.GLenum + GL_SPRITE_OBJECT_ALIGNED_SGIX* = 0x814D.GLenum + GL_POSITION* = 0x1203.GLenum + GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR* = 0x824E.GLenum + GL_GEOMETRY_OUTPUT_TYPE_ARB* = 0x8DDC.GLenum + GL_IMAGE_PIXEL_TYPE* = 0x82AA.GLenum + GL_UNSIGNED_INT64_AMD* = 0x8BC2.GLenum + GL_LIST_INDEX* = 0x0B33.GLenum + GL_UNSIGNED_INT_8_8_S8_S8_REV_NV* = 0x86DB.GLenum + GL_MAP_ATTRIB_U_ORDER_NV* = 0x86C3.GLenum + GL_PROXY_TEXTURE_RECTANGLE_ARB* = 0x84F7.GLenum + GL_CLIP_NEAR_HINT_PGI* = 0x1A220.GLenum + GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX* = 0x817B.GLenum + GL_MAX_UNIFORM_BLOCK_SIZE* = 0x8A30.GLenum + GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER* = 0x8CDB.GLenum + GL_SAMPLE_MASK_INVERT_EXT* = 0x80AB.GLenum + GL_MAP1_VERTEX_ATTRIB14_4_NV* = 0x866E.GLenum + GL_SYNC_FLAGS* = 0x9115.GLenum + GL_COMPRESSED_RGBA* = 0x84EE.GLenum + GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT* = 0x82B2.GLenum + GL_INDEX_ARRAY_STRIDE_EXT* = 0x8086.GLenum + GL_CLIP_DISTANCE_NV* = 0x8C7A.GLenum + GL_UNSIGNED_INT_VEC4* = 0x8DC8.GLenum + GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB* = 0x8E8E.GLenum + GL_MIRRORED_REPEAT_OES* = 0x8370.GLint + GL_WEIGHT_ARRAY_SIZE_ARB* = 0x86AB.GLenum + GL_MIN_SAMPLE_SHADING_VALUE* = 0x8C37.GLenum + GL_SOURCE0_RGB* = 0x8580.GLenum + GL_RG32I* = 0x823B.GLenum + GL_QUERY_BUFFER_BINDING_AMD* = 0x9193.GLenum + GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV* = 0x8851.GLenum + GL_POST_CONVOLUTION_BLUE_SCALE_EXT* = 0x801E.GLenum + GL_DOUBLE_MAT3x4_EXT* = 0x8F4C.GLenum + GL_MAX_VERTEX_HINT_PGI* = 0x1A22D.GLenum + GL_ADD* = 0x0104.GLenum + GL_PATH_FORMAT_SVG_NV* = 0x9070.GLenum + GL_VIDEO_BUFFER_BINDING_NV* = 0x9021.GLenum + GL_NUM_EXTENSIONS* = 0x821D.GLenum + GL_DEPTH_RANGE* = 0x0B70.GLenum + GL_FRAGMENT_SUBROUTINE* = 0x92EC.GLenum + GL_DEPTH24_STENCIL8_EXT* = 0x88F0.GLenum + GL_COMPRESSED_RGBA_S3TC_DXT3_EXT* = 0x83F2.GLenum + GL_COLOR_TABLE_SGI* = 0x80D0.GLenum + GL_OBJECT_ACTIVE_UNIFORMS_ARB* = 0x8B86.GLenum + GL_RGBA16F* = 0x881A.GLenum + GL_COORD_REPLACE_ARB* = 0x8862.GLenum + GL_SAMPLE_POSITION_NV* = 0x8E50.GLenum + GL_SRC_ALPHA* = 0x0302.GLenum + GL_COMBINE_ALPHA* = 0x8572.GLenum + GL_CLEAR* = 0x1500.GLenum + GL_HSL_HUE_NV* = 0x92AD.GLenum + GL_SCISSOR_TEST* = 0x0C11.GLenum + GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT* = 0x8DD8.GLenum + GL_RGB16UI* = 0x8D77.GLenum + GL_MATRIX9_ARB* = 0x88C9.GLenum + GL_COLOR_ATTACHMENT13* = 0x8CED.GLenum + GL_BUMP_ROT_MATRIX_SIZE_ATI* = 0x8776.GLenum + GL_PIXEL_PACK_BUFFER_BINDING_ARB* = 0x88ED.GLenum + GL_FONT_X_MAX_BOUNDS_BIT_NV* = 0x00040000.GLbitfield + GL_MODELVIEW31_ARB* = 0x873F.GLenum + GL_DRAW_BUFFER14_ARB* = 0x8833.GLenum + GL_EDGEFLAG_BIT_PGI* = 0x00040000.GLbitfield + GL_TEXTURE_LOD_BIAS_R_SGIX* = 0x8190.GLenum + GL_FIELD_UPPER_NV* = 0x9022.GLenum + GL_CLIP_PLANE3* = 0x3003.GLenum + GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX* = 0x8408.GLenum + GL_BLUE* = 0x1905.GLenum + GL_LUMINANCE_ALPHA_FLOAT32_ATI* = 0x8819.GLenum + GL_MATRIX31_ARB* = 0x88DF.GLenum + GL_OR_REVERSE* = 0x150B.GLenum + GL_INTERPOLATE_EXT* = 0x8575.GLenum + GL_MODELVIEW13_ARB* = 0x872D.GLenum + GL_UTF16_NV* = 0x909B.GLenum + GL_READ_FRAMEBUFFER_ANGLE* = 0x8CA8.GLenum + GL_LUMINANCE16F_EXT* = 0x881E.GLenum + GL_VERTEX_ATTRIB_ARRAY7_NV* = 0x8657.GLenum + GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT* = 0x8C8A.GLenum + GL_PRIMARY_COLOR_EXT* = 0x8577.GLenum + GL_VERTEX_ATTRIB_RELATIVE_OFFSET* = 0x82D5.GLenum + GL_LARGE_CW_ARC_TO_NV* = 0x18.GLenum + GL_PROGRAM_PARAMETER_NV* = 0x8644.GLenum + GL_ASYNC_MARKER_SGIX* = 0x8329.GLenum + GL_TEXTURE24_ARB* = 0x84D8.GLenum + GL_PIXEL_SUBSAMPLE_4242_SGIX* = 0x85A4.GLenum + GL_RGB10_A2_EXT* = 0x8059.GLenum + GL_IMAGE_CLASS_2_X_32* = 0x82BA.GLenum + GL_TEXTURE_INTENSITY_TYPE* = 0x8C15.GLenum + GL_TEXTURE_LOD_BIAS_S_SGIX* = 0x818E.GLenum + GL_PROGRAM_BINARY_LENGTH* = 0x8741.GLenum + GL_CURRENT_RASTER_NORMAL_SGIX* = 0x8406.GLenum + GL_DETAIL_TEXTURE_2D_SGIS* = 0x8095.GLenum + GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV* = 0x8E5C.GLenum + GL_CONVOLUTION_FILTER_BIAS_EXT* = 0x8015.GLenum + GL_DT_BIAS_NV* = 0x8717.GLenum + GL_RESET_NOTIFICATION_STRATEGY_EXT* = 0x8256.GLenum + GL_SHADER_STORAGE_BUFFER* = 0x90D2.GLenum + GL_RESET_NOTIFICATION_STRATEGY_ARB* = 0x8256.GLenum + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT* = 0x8CD1.GLenum + GL_SRC_NV* = 0x9286.GLenum + GL_POINT_FADE_THRESHOLD_SIZE* = 0x8128.GLenum + GL_DEPENDENT_RGB_TEXTURE_3D_NV* = 0x8859.GLenum + GL_QUERY_RESULT_ARB* = 0x8866.GLenum + GL_GEOMETRY_VERTICES_OUT* = 0x8916.GLenum + GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB* = 0x90EB.GLenum + GL_MODELVIEW27_ARB* = 0x873B.GLenum + GL_DRAW_BUFFER11_NV* = 0x8830.GLenum + GL_COLOR_ATTACHMENT9_NV* = 0x8CE9.GLenum + GL_BLEND_SRC* = 0x0BE1.GLenum + GL_CONVOLUTION_2D_EXT* = 0x8011.GLenum + GL_MAX_ELEMENTS_INDICES* = 0x80E9.GLenum + GL_LUMINANCE_ALPHA_FLOAT32_APPLE* = 0x8819.GLenum + GL_INT_IMAGE_1D* = 0x9057.GLenum + GL_CONSTANT_COLOR* = 0x8001.GLenum + GL_FRAMEBUFFER_BARRIER_BIT* = 0x00000400.GLbitfield + GL_POST_CONVOLUTION_BLUE_SCALE* = 0x801E.GLenum + GL_DEBUG_SOURCE_SHADER_COMPILER_ARB* = 0x8248.GLenum + GL_RGB16I* = 0x8D89.GLenum + GL_MAX_WIDTH* = 0x827E.GLenum + GL_LIGHT_MODEL_AMBIENT* = 0x0B53.GLenum + GL_COVERAGE_ATTACHMENT_NV* = 0x8ED2.GLenum + GL_PROGRAM* = 0x82E2.GLenum + GL_IMAGE_ROTATE_ANGLE_HP* = 0x8159.GLenum + GL_SRC2_RGB* = 0x8582.GLenum + GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR* = 0x824E.GLenum + GL_PASS_THROUGH_NV* = 0x86E6.GLenum + GL_HALF_BIAS_NEGATE_NV* = 0x853B.GLenum + GL_SAMPLER_CUBE_SHADOW_EXT* = 0x8DC5.GLenum + GL_COMPRESSED_RGBA_BPTC_UNORM_ARB* = 0x8E8C.GLenum + GL_MAX_SERVER_WAIT_TIMEOUT_APPLE* = 0x9111.GLenum + GL_STORAGE_PRIVATE_APPLE* = 0x85BD.GLenum + GL_VERTEX_SHADER_BIT* = 0x00000001.GLbitfield + GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI* = 0x80B6.GLenum + GL_VERTEX_SHADER_VARIANTS_EXT* = 0x87D0.GLenum + GL_TRANSFORM_FEEDBACK_ACTIVE* = 0x8E24.GLenum + GL_ACTIVE_UNIFORMS* = 0x8B86.GLenum + GL_MULTISAMPLE_BUFFER_BIT0_QCOM* = 0x01000000.GLbitfield + GL_OFFSET_TEXTURE_SCALE_NV* = 0x86E2.GLenum + GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB* = 0x88FE.GLenum + GL_BEVEL_NV* = 0x90A6.GLenum + GL_MAX_DRAW_BUFFERS_NV* = 0x8824.GLenum + GL_MAP1_TANGENT_EXT* = 0x8444.GLenum + GL_ANY_SAMPLES_PASSED* = 0x8C2F.GLenum + GL_MAX_IMAGE_SAMPLES* = 0x906D.GLenum + GL_PIXEL_UNPACK_BUFFER_BINDING* = 0x88EF.GLenum + GL_SRGB8_ALPHA8_EXT* = 0x8C43.GLenum + GL_2PASS_1_SGIS* = 0x80A3.GLenum + GL_PROGRAM_POINT_SIZE_ARB* = 0x8642.GLenum + GL_ALLOW_DRAW_WIN_HINT_PGI* = 0x1A20F.GLenum + GL_INTERNALFORMAT_RED_SIZE* = 0x8271.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES* = 0x8CD3.GLenum + GL_4PASS_2_SGIS* = 0x80A6.GLenum + GL_PROGRAM_OBJECT_EXT* = 0x8B40.GLenum + GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST* = 0x82AD.GLenum + GL_LIGHTING_BIT* = 0x00000040.GLbitfield + GL_DRAW_BUFFER13_EXT* = 0x8832.GLenum + GL_STREAM_DRAW_ARB* = 0x88E0.GLenum + GL_INDEX_ARRAY_TYPE* = 0x8085.GLenum + GL_DEBUG_SOURCE_THIRD_PARTY* = 0x8249.GLenum + GL_DYNAMIC_COPY_ARB* = 0x88EA.GLenum + GL_COMPARE_R_TO_TEXTURE_ARB* = 0x884E.GLenum + GL_FRAGMENTS_INSTRUMENT_COUNTERS_SGIX* = 0x8314.GLenum + GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB* = 0x91A9.GLenum + GL_MAX_GEOMETRY_UNIFORM_COMPONENTS* = 0x8DDF.GLenum + GL_READ_PIXEL_DATA_RANGE_POINTER_NV* = 0x887D.GLenum + GL_BUFFER_MAPPED_OES* = 0x88BC.GLenum + GL_COLOR_ARRAY_COUNT_EXT* = 0x8084.GLenum + GL_SET_AMD* = 0x874A.GLenum + GL_BLEND_DST_RGB_OES* = 0x80C8.GLenum + GL_MAX_CONVOLUTION_HEIGHT_EXT* = 0x801B.GLenum + GL_DEBUG_SEVERITY_MEDIUM* = 0x9147.GLenum + GL_TEXTURE_INTENSITY_TYPE_ARB* = 0x8C15.GLenum + GL_IMAGE_CLASS_10_10_10_2* = 0x82C3.GLenum + GL_TEXTURE_BORDER_COLOR_NV* = 0x1004.GLenum + GL_VERTEX_ATTRIB_ARRAY12_NV* = 0x865C.GLenum + GL_MAX_GEOMETRY_SHADER_INVOCATIONS* = 0x8E5A.GLenum + GL_NEAREST_CLIPMAP_NEAREST_SGIX* = 0x844D.GLenum + GL_MAP2_VERTEX_ATTRIB12_4_NV* = 0x867C.GLenum + GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING* = 0x889A.GLenum + GL_SEPARATE_SPECULAR_COLOR_EXT* = 0x81FA.GLenum + GL_MATRIX_INDEX_ARRAY_SIZE_ARB* = 0x8846.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB* = 0x8517.GLenum + GL_DECR* = 0x1E03.GLenum + GL_DEPTH_BUFFER_BIT7_QCOM* = 0x00008000.GLbitfield + GL_LOCAL_EXT* = 0x87C4.GLenum + GL_FUNC_REVERSE_SUBTRACT_OES* = 0x800B.GLenum + GL_FLOAT_VEC3* = 0x8B51.GLenum + GL_POINT_SIZE_GRANULARITY* = 0x0B13.GLenum + GL_COLOR_ATTACHMENT9* = 0x8CE9.GLenum + GL_MAT_SPECULAR_BIT_PGI* = 0x04000000.GLbitfield + GL_VERTEX_ATTRIB_MAP1_APPLE* = 0x8A00.GLenum + GL_DEBUG_SOURCE_WINDOW_SYSTEM* = 0x8247.GLenum + GL_NEAREST_MIPMAP_NEAREST* = 0x2700.GLint + GL_MODELVIEW7_ARB* = 0x8727.GLenum + GL_OUTPUT_VERTEX_EXT* = 0x879A.GLenum + GL_FRAMEBUFFER_EXT* = 0x8D40.GLenum + GL_ATC_RGBA_EXPLICIT_ALPHA_AMD* = 0x8C93.GLenum + GL_RENDERBUFFER_WIDTH_OES* = 0x8D42.GLenum + GL_TEXTURE_VIEW_MIN_LAYER* = 0x82DD.GLenum + GL_TEXTURE25_ARB* = 0x84D9.GLenum + GL_LIGHT7* = 0x4007.GLenum + GL_TESS_EVALUATION_SHADER_BIT* = 0x00000010.GLbitfield + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT* = 0x8CD2.GLenum + GL_COLOR_ATTACHMENT15_NV* = 0x8CEF.GLenum + GL_RED_SNORM* = 0x8F90.GLenum + GL_VIVIDLIGHT_NV* = 0x92A6.GLenum + GL_OBJECT_COMPILE_STATUS_ARB* = 0x8B81.GLenum + GL_INTERNALFORMAT_PREFERRED* = 0x8270.GLenum + GL_OUT_OF_MEMORY* = 0x0505.GLenum + GL_422_REV_EXT* = 0x80CD.GLenum + GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV* = 0x86F0.GLenum + GL_PROXY_TEXTURE_1D* = 0x8063.GLenum + GL_FRAGMENT_PROGRAM_CALLBACK_FUNC_MESA* = 0x8BB2.GLenum + GL_YCBCR_422_APPLE* = 0x85B9.GLenum + GL_DRAW_BUFFER10_ATI* = 0x882F.GLenum + GL_COLOR_TABLE_ALPHA_SIZE_SGI* = 0x80DD.GLenum + GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS* = 0x8E86.GLenum + GL_MAX_PROGRAM_OUTPUT_VERTICES_NV* = 0x8C27.GLenum + GL_IMAGE_2D_MULTISAMPLE_EXT* = 0x9055.GLenum + GL_ACTIVE_TEXTURE_ARB* = 0x84E0.GLenum + GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV* = 0x02000000.GLbitfield + GL_QUERY_WAIT_NV* = 0x8E13.GLenum + GL_MAX_ELEMENT_INDEX* = 0x8D6B.GLenum + GL_OP_LOG_BASE_2_EXT* = 0x8792.GLenum + GL_ADD_SIGNED* = 0x8574.GLenum + GL_CONVOLUTION_FORMAT* = 0x8017.GLenum + GL_RENDERBUFFER_RED_SIZE_EXT* = 0x8D50.GLenum + GL_RENDERBUFFER_INTERNAL_FORMAT* = 0x8D44.GLenum + GL_COLOR_ATTACHMENT11_NV* = 0x8CEB.GLenum + GL_MATRIX14_ARB* = 0x88CE.GLenum + GL_COLOR_TABLE_RED_SIZE_SGI* = 0x80DA.GLenum + GL_CON_22_ATI* = 0x8957.GLenum + GL_TEXTURE_SWIZZLE_B_EXT* = 0x8E44.GLenum + GL_SAMPLES_SGIS* = 0x80A9.GLenum + GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV* = 0x887A.GLenum + GL_FONT_X_MIN_BOUNDS_BIT_NV* = 0x00010000.GLbitfield + GL_3_BYTES* = 0x1408.GLenum + GL_TEXTURE_MAX_CLAMP_S_SGIX* = 0x8369.GLenum + GL_PROXY_TEXTURE_CUBE_MAP_EXT* = 0x851B.GLenum + GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE* = 0x88FE.GLenum + GL_VERTEX_DATA_HINT_PGI* = 0x1A22A.GLenum + GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT* = 0x850D.GLenum + GL_MAX_INTEGER_SAMPLES* = 0x9110.GLenum + GL_TEXTURE_BUFFER_ARB* = 0x8C2A.GLenum + GL_FOG_COORD_ARRAY_POINTER* = 0x8456.GLenum + GL_UNSIGNED_SHORT_1_15_REV_MESA* = 0x8754.GLenum + GL_IMAGE_CUBIC_WEIGHT_HP* = 0x815E.GLenum + GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES* = 0x8CD6.GLenum + GL_RGBA_DXT5_S3TC* = 0x83A4.GLenum + GL_INT_IMAGE_2D_MULTISAMPLE* = 0x9060.GLenum + GL_ACTIVE_RESOURCES* = 0x92F5.GLenum + GL_TEXTURE_BINDING_2D* = 0x8069.GLenum + GL_SAMPLE_COVERAGE* = 0x80A0.GLenum + GL_SMOOTH* = 0x1D01.GLenum + GL_SAMPLER_1D_SHADOW_ARB* = 0x8B61.GLenum + GL_VIRTUAL_PAGE_SIZE_Y_AMD* = 0x9196.GLenum + GL_HORIZONTAL_LINE_TO_NV* = 0x06.GLenum + GL_HISTOGRAM_GREEN_SIZE_EXT* = 0x8029.GLenum + GL_COLOR_FLOAT_APPLE* = 0x8A0F.GLenum + GL_NUM_SHADER_BINARY_FORMATS* = 0x8DF9.GLenum + GL_TIMESTAMP* = 0x8E28.GLenum + GL_SRGB_EXT* = 0x8C40.GLenum + GL_MAX_VERTEX_UNIFORM_BLOCKS* = 0x8A2B.GLenum + GL_COLOR_ATTACHMENT2_EXT* = 0x8CE2.GLenum + GL_DEBUG_CALLBACK_FUNCTION_KHR* = 0x8244.GLenum + GL_DISPLAY_LIST* = 0x82E7.GLenum + GL_MAP1_NORMAL* = 0x0D92.GLenum + GL_COMPUTE_TEXTURE* = 0x82A0.GLenum + GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS* = 0x90DB.GLenum + GL_W_EXT* = 0x87D8.GLenum + GL_SAMPLE_SHADING_ARB* = 0x8C36.GLenum + GL_FRAGMENT_INTERPOLATION_OFFSET_BITS* = 0x8E5D.GLenum + GL_IMAGE_CLASS_4_X_16* = 0x82BC.GLenum + GL_FRAGMENT_DEPTH_EXT* = 0x8452.GLenum + GL_EVAL_BIT* = 0x00010000.GLbitfield + GL_UNSIGNED_INT_8_8_8_8* = 0x8035.GLenum + GL_MAX_TESS_CONTROL_INPUT_COMPONENTS* = 0x886C.GLenum + GL_FRAGMENT_PROGRAM_CALLBACK_DATA_MESA* = 0x8BB3.GLenum + GL_SLUMINANCE8_ALPHA8* = 0x8C45.GLenum + GL_MODULATE_COLOR_IMG* = 0x8C04.GLenum + GL_TEXTURE20* = 0x84D4.GLenum + GL_ALPHA_INTEGER_EXT* = 0x8D97.GLenum + GL_TEXTURE_BINDING_CUBE_MAP_EXT* = 0x8514.GLenum + GL_BACK_LEFT* = 0x0402.GLenum + GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT* = 0x8F39.GLenum + GL_MAX_TRANSFORM_FEEDBACK_BUFFERS* = 0x8E70.GLenum + GL_TRANSFORM_BIT* = 0x00001000.GLbitfield + GL_RGB4_EXT* = 0x804F.GLenum + GL_FRAGMENT_COLOR_EXT* = 0x834C.GLenum + GL_PIXEL_MAP_S_TO_S* = 0x0C71.GLenum + GL_COMPRESSED_RGBA_S3TC_DXT5_EXT* = 0x83F3.GLenum + GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV* = 0x90BD.GLenum + GL_SOURCE0_RGB_EXT* = 0x8580.GLenum + GL_PIXEL_COUNTER_BITS_NV* = 0x8864.GLenum + GL_ALIASED_LINE_WIDTH_RANGE* = 0x846E.GLenum + GL_DRAW_BUFFER10* = 0x882F.GLenum + GL_T4F_C4F_N3F_V4F* = 0x2A2D.GLenum + GL_BLEND_EQUATION_OES* = 0x8009.GLenum + GL_DEPTH_COMPONENT32* = 0x81A7.GLenum + GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT* = 0x87CA.GLenum + GL_DEPTH_BUFFER_BIT5_QCOM* = 0x00002000.GLbitfield + GL_RED_MIN_CLAMP_INGR* = 0x8560.GLenum + GL_RGBA_INTEGER_MODE_EXT* = 0x8D9E.GLenum + GL_DOUBLE_MAT4_EXT* = 0x8F48.GLenum + GL_OBJECT_DELETE_STATUS_ARB* = 0x8B80.GLenum + GL_FOG_COORD_ARRAY_LENGTH_NV* = 0x8F32.GLenum + GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING* = 0x889C.GLenum + GL_MAP1_VERTEX_ATTRIB7_4_NV* = 0x8667.GLenum + GL_BLEND_SRC_RGB_EXT* = 0x80C9.GLenum + GL_VERTEX_PROGRAM_POINT_SIZE_ARB* = 0x8642.GLenum + GL_STENCIL_INDEX1_EXT* = 0x8D46.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT* = 0x8516.GLenum + GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT* = 0x8A52.GLenum + GL_FOG_COORD_SRC* = 0x8450.GLenum + GL_ANY_SAMPLES_PASSED_EXT* = 0x8C2F.GLenum + GL_ALPHA4* = 0x803B.GLenum + GL_TEXTURE_GEN_MODE* = 0x2500.GLenum + GL_FLOAT_MAT3_ARB* = 0x8B5B.GLenum + GL_PIXEL_MAP_A_TO_A_SIZE* = 0x0CB9.GLenum + GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB* = 0x8B8B.GLenum + GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI* = 0x8802.GLenum + GL_COPY_READ_BUFFER_BINDING* = 0x8F36.GLenum + GL_YCRCB_444_SGIX* = 0x81BC.GLenum + GL_SLUMINANCE_EXT* = 0x8C46.GLenum + GL_EDGE_FLAG_ARRAY_EXT* = 0x8079.GLenum + GL_STENCIL_INDEX8_OES* = 0x8D48.GLenum + GL_RGBA32UI* = 0x8D70.GLenum + GL_TEXTURE_CUBE_MAP* = 0x8513.GLenum + GL_STREAM_COPY* = 0x88E2.GLenum + GL_VIEWPORT_BOUNDS_RANGE* = 0x825D.GLenum + GL_ASYNC_READ_PIXELS_SGIX* = 0x835E.GLenum + GL_VERTEX_ATTRIB_ARRAY_INTEGER* = 0x88FD.GLenum + GL_INTERNALFORMAT_STENCIL_TYPE* = 0x827D.GLenum + GL_OUTPUT_TEXTURE_COORD28_EXT* = 0x87B9.GLenum + GL_MATRIX_MODE* = 0x0BA0.GLenum + GL_MULTISAMPLE_SGIS* = 0x809D.GLenum + GL_R1UI_V3F_SUN* = 0x85C4.GLenum + GL_FLOAT_R32_NV* = 0x8885.GLenum + GL_MAX_DRAW_BUFFERS* = 0x8824.GLenum + GL_CIRCULAR_CCW_ARC_TO_NV* = 0xF8.GLenum + GL_PROGRAM_OUTPUT* = 0x92E4.GLenum + GL_MAX_CUBE_MAP_TEXTURE_SIZE* = 0x851C.GLenum + GL_TRIANGLE_STRIP_ADJACENCY_ARB* = 0x000D.GLenum + GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT* = 0x8A34.GLenum + GL_SRGB* = 0x8C40.GLenum + GL_BUFFER_ACCESS* = 0x88BB.GLenum + GL_TEXTURE_WRAP_S* = 0x2802.GLenum + GL_TRANSFORM_FEEDBACK_VARYINGS* = 0x8C83.GLenum + GL_RG16UI* = 0x823A.GLenum + GL_DUAL_LUMINANCE4_SGIS* = 0x8114.GLenum + GL_DOT_PRODUCT_DEPTH_REPLACE_NV* = 0x86ED.GLenum + GL_READ_FRAMEBUFFER_BINDING* = 0x8CAA.GLenum + GL_MAX_FOG_FUNC_POINTS_SGIS* = 0x812C.GLenum + GL_QUERY_RESULT_NO_WAIT* = 0x9194.GLenum + GL_FILE_NAME_NV* = 0x9074.GLenum + GL_DRAW_FRAMEBUFFER_BINDING* = 0x8CA6.GLenum + GL_FRAGMENT_SHADER* = 0x8B30.GLenum + GL_VIBRANCE_SCALE_NV* = 0x8713.GLenum + GL_PATH_FILL_COVER_MODE_NV* = 0x9082.GLenum + GL_LINEAR_MIPMAP_LINEAR* = 0x2703.GLint + GL_TEXTURE29* = 0x84DD.GLenum + GL_SCISSOR_BOX* = 0x0C10.GLenum + GL_PACK_SKIP_IMAGES* = 0x806B.GLenum + GL_BUFFER_MAP_OFFSET* = 0x9121.GLenum + GL_SLUMINANCE8_EXT* = 0x8C47.GLenum + GL_CONVOLUTION_1D* = 0x8010.GLenum + GL_MAX_GEOMETRY_IMAGE_UNIFORMS* = 0x90CD.GLenum + GL_MAP1_VERTEX_ATTRIB11_4_NV* = 0x866B.GLenum + GL_COLOR_LOGIC_OP* = 0x0BF2.GLenum + GL_SYNC_FLAGS_APPLE* = 0x9115.GLenum + GL_ACCUM_RED_BITS* = 0x0D58.GLenum + GL_VIEW_CLASS_128_BITS* = 0x82C4.GLenum + GL_INT_VEC3* = 0x8B54.GLenum + GL_INTENSITY12* = 0x804C.GLenum + GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER* = 0x90EC.GLenum + GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES* = 0x8D68.GLenum + GL_MAX_COLOR_MATRIX_STACK_DEPTH* = 0x80B3.GLenum + GL_GLOBAL_ALPHA_FACTOR_SUN* = 0x81DA.GLenum + GL_PACK_RESAMPLE_SGIX* = 0x842C.GLenum + GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB* = 0x91BF.GLenum + GL_DEPTH_BUFFER_FLOAT_MODE_NV* = 0x8DAF.GLenum + GL_SIGNED_LUMINANCE_ALPHA_NV* = 0x8703.GLenum + GL_OP_MIN_EXT* = 0x878B.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV* = 0x8C7F.GLenum + GL_COLOR_INDEX12_EXT* = 0x80E6.GLenum + GL_AUTO_NORMAL* = 0x0D80.GLenum + GL_ARRAY_BUFFER* = 0x8892.GLenum + GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT* = 0x8DE1.GLenum + GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV* = 0x903C.GLenum + GL_ACCUM_BLUE_BITS* = 0x0D5A.GLenum + GL_RENDERBUFFER_SAMPLES_ANGLE* = 0x8CAB.GLenum + GL_MAX_ASYNC_HISTOGRAM_SGIX* = 0x832D.GLenum + GL_GLYPH_HAS_KERNING_BIT_NV* = 0x100.GLbitfield + GL_TESS_CONTROL_SUBROUTINE_UNIFORM* = 0x92EF.GLenum + GL_DRAW_BUFFER1* = 0x8826.GLenum + GL_INT8_NV* = 0x8FE0.GLenum + GL_2PASS_0_EXT* = 0x80A2.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_INDEX* = 0x934B.GLenum + GL_NUM_VIRTUAL_PAGE_SIZES_ARB* = 0x91A8.GLenum + GL_INT_SAMPLER_3D* = 0x8DCB.GLenum + GL_RASTERIZER_DISCARD* = 0x8C89.GLenum + GL_SOURCE2_RGB_ARB* = 0x8582.GLenum + GL_LOCAL_CONSTANT_EXT* = 0x87C3.GLenum + GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT* = 0x8DA9.GLenum + GL_MODELVIEW12_ARB* = 0x872C.GLenum + GL_VERTEX_SUBROUTINE_UNIFORM* = 0x92EE.GLenum + GL_OPERAND0_ALPHA_ARB* = 0x8598.GLenum + GL_DEPTH24_STENCIL8* = 0x88F0.GLenum + GL_RENDERBUFFER_RED_SIZE* = 0x8D50.GLenum + GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING* = 0x8210.GLenum + GL_DRAW_BUFFER10_ARB* = 0x882F.GLenum + GL_UNSIGNED_INT_SAMPLER_3D* = 0x8DD3.GLenum + GL_SKIP_COMPONENTS2_NV* = -5 + GL_PROGRAM_BINARY_LENGTH_OES* = 0x8741.GLenum + GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE* = 0x8A02.GLenum + GL_QUERY_RESULT_EXT* = 0x8866.GLenum + GL_CONSTANT_COLOR0_NV* = 0x852A.GLenum + GL_MAX_ASYNC_DRAW_PIXELS_SGIX* = 0x8360.GLenum + GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV* = 0x86F1.GLenum + GL_ALPHA_TEST_REF* = 0x0BC2.GLenum + GL_MAX_4D_TEXTURE_SIZE_SGIS* = 0x8138.GLenum + GL_INT_SAMPLER_2D_MULTISAMPLE* = 0x9109.GLenum + GL_DRAW_BUFFER6_ATI* = 0x882B.GLenum + GL_INTENSITY16UI_EXT* = 0x8D79.GLenum + GL_POINT_FADE_THRESHOLD_SIZE_ARB* = 0x8128.GLenum + GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING* = 0x889F.GLenum + GL_RENDERBUFFER_WIDTH_EXT* = 0x8D42.GLenum + GL_FIXED_ONLY* = 0x891D.GLenum + GL_HISTOGRAM_BLUE_SIZE* = 0x802A.GLenum + GL_PROGRAM_TEX_INSTRUCTIONS_ARB* = 0x8806.GLenum + GL_MAX_VERTEX_SHADER_VARIANTS_EXT* = 0x87C6.GLenum + GL_UNSIGNED_INT_10_10_10_2_EXT* = 0x8036.GLenum + GL_SAMPLE_ALPHA_TO_ONE_EXT* = 0x809F.GLenum + GL_INDEX_ARRAY* = 0x8077.GLenum + GL_GEQUAL* = 0x0206.GLenum + GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS* = 0x90D8.GLenum + GL_DITHER* = 0x0BD0.GLenum + GL_ATTACHED_SHADERS* = 0x8B85.GLenum + GL_FUNC_SUBTRACT* = 0x800A.GLenum + GL_ATOMIC_COUNTER_BARRIER_BIT_EXT* = 0x00001000.GLbitfield + GL_LUMINANCE4* = 0x803F.GLenum + GL_BLEND_EQUATION_RGB_EXT* = 0x8009.GLenum + GL_TEXTURE_MULTI_BUFFER_HINT_SGIX* = 0x812E.GLenum + GL_DEBUG_SEVERITY_LOW_KHR* = 0x9148.GLenum + GL_UNPACK_COMPRESSED_BLOCK_HEIGHT* = 0x9128.GLenum + GL_CULL_VERTEX_OBJECT_POSITION_EXT* = 0x81AC.GLenum + GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI* = 0x80BB.GLenum + GL_ADD_SIGNED_EXT* = 0x8574.GLenum + GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL* = 0x83F5.GLenum + GL_CURRENT_RASTER_SECONDARY_COLOR* = 0x845F.GLenum + GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV* = 0x8E5F.GLenum + GL_CONTINUOUS_AMD* = 0x9007.GLenum + GL_R1UI_T2F_C4F_N3F_V3F_SUN* = 0x85CB.GLenum + GL_COMPUTE_SHADER* = 0x91B9.GLenum + GL_CLIP_DISTANCE6* = 0x3006.GLenum + GL_SRC_ATOP_NV* = 0x928E.GLenum + GL_DEPTH_COMPONENT16_OES* = 0x81A5.GLenum + GL_DOUBLE_MAT4* = 0x8F48.GLenum + GL_MAT_SHININESS_BIT_PGI* = 0x02000000.GLbitfield + GL_SAMPLER_BUFFER_AMD* = 0x9001.GLenum + GL_ARRAY_BUFFER_BINDING_ARB* = 0x8894.GLenum + GL_VOLATILE_APPLE* = 0x8A1A.GLenum + GL_ALPHA32UI_EXT* = 0x8D72.GLenum + GL_COLOR_BUFFER_BIT1_QCOM* = 0x00000002.GLbitfield + GL_VERTEX_PROGRAM_CALLBACK_MESA* = 0x8BB4.GLenum + GL_CULL_VERTEX_EXT* = 0x81AA.GLenum + GL_RENDERBUFFER_STENCIL_SIZE_EXT* = 0x8D55.GLenum + GL_SELECT* = 0x1C02.GLenum + GL_LUMINANCE12_ALPHA4* = 0x8046.GLenum + GL_IMAGE_BINDING_LEVEL_EXT* = 0x8F3B.GLenum + GL_MATRIX_PALETTE_ARB* = 0x8840.GLenum + GL_DUAL_ALPHA4_SGIS* = 0x8110.GLenum + GL_BACK_NORMALS_HINT_PGI* = 0x1A223.GLenum + GL_UNSIGNED_SHORT_15_1_MESA* = 0x8753.GLenum + GL_UNSIGNED_SHORT_4_4_4_4_REV* = 0x8365.GLenum + GL_BUFFER* = 0x82E0.GLenum + GL_RENDERBUFFER_INTERNAL_FORMAT_EXT* = 0x8D44.GLenum + GL_MATRIX5_NV* = 0x8635.GLenum + GL_ATOMIC_COUNTER_BUFFER* = 0x92C0.GLenum + GL_SMOOTH_QUADRATIC_CURVE_TO_NV* = 0x0E.GLenum + GL_VARIABLE_D_NV* = 0x8526.GLenum + GL_PINLIGHT_NV* = 0x92A8.GLenum + GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT* = 0x88FD.GLenum + GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS* = 0x92CF.GLenum + GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV* = 0x9034.GLenum + GL_RESAMPLE_REPLICATE_SGIX* = 0x842E.GLenum + GL_UNSIGNED_SHORT_5_6_5_REV* = 0x8364.GLenum + GL_VERTEX_ATTRIB_ARRAY2_NV* = 0x8652.GLenum + GL_3D_COLOR_TEXTURE* = 0x0603.GLenum + GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS* = 0x8B4C.GLenum + GL_DEBUG_TYPE_PERFORMANCE_KHR* = 0x8250.GLenum + GL_MATRIX_INDEX_ARRAY_OES* = 0x8844.GLenum + GL_TEXTURE_TOO_LARGE_EXT* = 0x8065.GLenum + GL_PACK_IMAGE_HEIGHT_EXT* = 0x806C.GLenum + GL_YCBYCR8_422_NV* = 0x9031.GLenum + GL_COLOR_ATTACHMENT8* = 0x8CE8.GLenum + GL_SAMPLE_COVERAGE_ARB* = 0x80A0.GLenum + GL_CURRENT_VERTEX_EXT* = 0x87E2.GLenum + GL_LINEAR* = 0x2601.GLint + GL_STENCIL_TAG_BITS_EXT* = 0x88F2.GLenum + GL_T2F_IUI_V3F_EXT* = 0x81B2.GLenum + GL_TEXTURE_3D_BINDING_OES* = 0x806A.GLenum + GL_PATH_CLIENT_LENGTH_NV* = 0x907F.GLenum + GL_MAT_AMBIENT_BIT_PGI* = 0x00100000.GLbitfield + GL_DOUBLE_MAT4x3* = 0x8F4E.GLenum + GL_QUERY_BY_REGION_WAIT_NV* = 0x8E15.GLenum + GL_LEQUAL* = 0x0203.GLenum + GL_PROGRAM_ATTRIBS_ARB* = 0x88AC.GLenum + GL_BUFFER_MAPPED_ARB* = 0x88BC.GLenum + GL_VERTEX_SHADER_ARB* = 0x8B31.GLenum + GL_SOURCE1_ALPHA_EXT* = 0x8589.GLenum + GL_UNSIGNED_INT16_VEC3_NV* = 0x8FF2.GLenum + GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB* = 0x88B1.GLenum + GL_RGB16* = 0x8054.GLenum + GL_TEXTURE15_ARB* = 0x84CF.GLenum + GL_TEXTURE_GATHER_SHADOW* = 0x82A3.GLenum + GL_FENCE_APPLE* = 0x8A0B.GLenum + GL_TRIANGLES* = 0x0004.GLenum + GL_DOT4_ATI* = 0x8967.GLenum + GL_CURRENT_FOG_COORD* = 0x8453.GLenum + GL_DEPTH_CLAMP_NEAR_AMD* = 0x901E.GLenum + GL_SYNC_FENCE* = 0x9116.GLenum + GL_UNSIGNED_INT64_VEC3_NV* = 0x8FF6.GLenum + GL_DEPTH* = 0x1801.GLenum + GL_TEXTURE_COORD_NV* = 0x8C79.GLenum + GL_COMBINE* = 0x8570.GLenum + GL_MAX_VERTEX_UNITS_ARB* = 0x86A4.GLenum + GL_COLOR_INDEX2_EXT* = 0x80E3.GLenum + GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP* = 0x8162.GLenum + GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB* = 0x900E.GLenum + GL_MIRROR_CLAMP_EXT* = 0x8742.GLint + GL_STENCIL_VALUE_MASK* = 0x0B93.GLenum + GL_UNSIGNED_INT_SAMPLER_BUFFER* = 0x8DD8.GLenum + GL_TRACK_MATRIX_NV* = 0x8648.GLenum + GL_MAP1_VERTEX_3* = 0x0D97.GLenum + GL_OP_MOV_EXT* = 0x8799.GLenum + GL_MAP_INVALIDATE_RANGE_BIT_EXT* = 0x0004.GLbitfield + GL_MAX_CONVOLUTION_WIDTH_EXT* = 0x801A.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES* = 0x8518.GLenum + GL_RGBA_SNORM* = 0x8F93.GLenum + GL_MAX_TRACK_MATRICES_NV* = 0x862F.GLenum + GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS* = 0x886D.GLenum + GL_DOUBLE_VEC4_EXT* = 0x8FFE.GLenum + GL_COLOR_TABLE_BLUE_SIZE* = 0x80DC.GLenum + GL_T2F_C3F_V3F* = 0x2A2A.GLenum + GL_INTENSITY16_SNORM* = 0x901B.GLenum + GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT* = 0x905F.GLenum + GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD* = 0x914C.GLenum + GL_NORMAL_MAP_EXT* = 0x8511.GLenum + GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV* = 0x8C8B.GLenum + GL_DRAW_BUFFER4_EXT* = 0x8829.GLenum + GL_PIXEL_MAP_G_TO_G* = 0x0C77.GLenum + GL_TESS_GEN_POINT_MODE* = 0x8E79.GLenum + GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS* = 0x92CC.GLenum + GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT* = 0x8DD5.GLenum + GL_MULTISAMPLE_BUFFER_BIT2_QCOM* = 0x04000000.GLbitfield + GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI* = 0x80B9.GLenum + GL_POST_COLOR_MATRIX_GREEN_BIAS* = 0x80B9.GLenum + GL_TEXTURE10* = 0x84CA.GLenum + GL_RGB32F* = 0x8815.GLenum + GL_DYNAMIC_READ_ARB* = 0x88E9.GLenum + GL_MODELVIEW22_ARB* = 0x8736.GLenum + GL_VERTEX_STREAM0_ATI* = 0x876C.GLenum + GL_TEXTURE_FETCH_BARRIER_BIT_EXT* = 0x00000008.GLbitfield + GL_COMBINER_INPUT_NV* = 0x8542.GLenum + GL_DRAW_BUFFER0_NV* = 0x8825.GLenum + GL_ALPHA_TEST* = 0x0BC0.GLenum + GL_PIXEL_UNPACK_BUFFER* = 0x88EC.GLenum + GL_SRC_IN_NV* = 0x928A.GLenum + GL_COMPRESSED_SIGNED_RED_RGTC1_EXT* = 0x8DBC.GLenum + GL_PACK_SUBSAMPLE_RATE_SGIX* = 0x85A0.GLenum + GL_FRAMEBUFFER_DEFAULT_SAMPLES* = 0x9313.GLenum + GL_ARRAY_OBJECT_OFFSET_ATI* = 0x8767.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES* = 0x8515.GLenum + GL_STENCIL_BITS* = 0x0D57.GLenum + GL_DEPTH_COMPONENT24_OES* = 0x81A6.GLenum + GL_FRAMEBUFFER* = 0x8D40.GLenum + GL_8X_BIT_ATI* = 0x00000004.GLbitfield + GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY* = 0x9105.GLenum + GL_BOOL_VEC2* = 0x8B57.GLenum + GL_EXP* = 0x0800.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT* = 0x851A.GLenum + GL_STENCIL_INDEX16* = 0x8D49.GLenum + GL_FRAGMENT_LIGHTING_SGIX* = 0x8400.GLenum + GL_PACK_SKIP_PIXELS* = 0x0D04.GLenum + GL_TEXTURE_MIN_LOD* = 0x813A.GLenum + GL_COMPRESSED_RGB* = 0x84ED.GLenum + GL_MAP1_VERTEX_ATTRIB2_4_NV* = 0x8662.GLenum + GL_CONJOINT_NV* = 0x9284.GLenum + GL_MAX_COMPUTE_SHARED_MEMORY_SIZE* = 0x8262.GLenum + GL_INTENSITY8* = 0x804B.GLenum + GL_SAMPLER_2D_MULTISAMPLE* = 0x9108.GLenum + GL_MAX_LIST_NESTING* = 0x0B31.GLenum + GL_DOUBLE_MAT3* = 0x8F47.GLenum + GL_TEXTURE_DEPTH* = 0x8071.GLenum + GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION* = 0x8E4C.GLenum + GL_TEXTURE12_ARB* = 0x84CC.GLenum + GL_R1UI_T2F_V3F_SUN* = 0x85C9.GLenum + GL_REPLACE* = 0x1E01.GLenum + GL_MAX_NUM_ACTIVE_VARIABLES* = 0x92F7.GLenum + GL_RGBA_INTEGER_EXT* = 0x8D99.GLenum + GL_TEXTURE_COMPRESSED_BLOCK_SIZE* = 0x82B3.GLenum + GL_INDEX_CLEAR_VALUE* = 0x0C20.GLenum + GL_PROGRAM_ERROR_POSITION_ARB* = 0x864B.GLenum + GL_LINEARBURN_NV* = 0x92A5.GLenum + GL_TEXTURE_BINDING_CUBE_MAP_ARB* = 0x8514.GLenum + GL_TESSELLATION_FACTOR_AMD* = 0x9005.GLenum + GL_SHADER_IMAGE_STORE* = 0x82A5.GLenum + GL_COMPRESSED_SLUMINANCE_ALPHA_EXT* = 0x8C4B.GLenum + GL_MAX_PALETTE_MATRICES_ARB* = 0x8842.GLenum + GL_UNPACK_CONSTANT_DATA_SUNX* = 0x81D5.GLenum + GL_FLOAT_MAT3x4* = 0x8B68.GLenum + GL_DRAW_BUFFER8_NV* = 0x882D.GLenum + GL_ATTENUATION_EXT* = 0x834D.GLenum + GL_REG_25_ATI* = 0x893A.GLenum + GL_UNSIGNED_INT_SAMPLER_1D* = 0x8DD1.GLenum + GL_TEXTURE_1D_STACK_BINDING_MESAX* = 0x875D.GLenum + GL_SYNC_STATUS_APPLE* = 0x9114.GLenum + GL_TEXTURE_CUBE_MAP_ARRAY* = 0x9009.GLenum + GL_EXP2* = 0x0801.GLenum + GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT* = 0x8C71.GLenum + GL_BUFFER_ACCESS_ARB* = 0x88BB.GLenum + GL_LO_BIAS_NV* = 0x8715.GLenum + GL_MIRROR_CLAMP_ATI* = 0x8742.GLint + GL_SAMPLE_COVERAGE_VALUE* = 0x80AA.GLenum + GL_UNSIGNED_INT_24_8_EXT* = 0x84FA.GLenum + GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT* = 0x8C88.GLenum + GL_R16UI* = 0x8234.GLenum + GL_BLEND_PREMULTIPLIED_SRC_NV* = 0x9280.GLenum + GL_COLOR_ATTACHMENT0* = 0x8CE0.GLenum + GL_GEOMETRY_VERTICES_OUT_EXT* = 0x8DDA.GLenum + GL_SAMPLE_MASK_NV* = 0x8E51.GLenum + GL_BGRA_INTEGER_EXT* = 0x8D9B.GLenum + GL_PALETTE8_RGBA8_OES* = 0x8B96.GLenum + GL_MAX_ARRAY_TEXTURE_LAYERS_EXT* = 0x88FF.GLenum + GL_TEXTURE_COLOR_TABLE_SGI* = 0x80BC.GLenum + GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT* = 0x8C80.GLenum + GL_TEXTURE10_ARB* = 0x84CA.GLenum + GL_TRIANGLES_ADJACENCY* = 0x000C.GLenum + GL_COLOR_ARRAY_EXT* = 0x8076.GLenum + GL_MAX_FRAMEBUFFER_SAMPLES* = 0x9318.GLenum + GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB* = 0x889F.GLenum + GL_IMAGE_TEXEL_SIZE* = 0x82A7.GLenum + GL_MAGNITUDE_BIAS_NV* = 0x8718.GLenum + GL_SHADOW_AMBIENT_SGIX* = 0x80BF.GLenum + GL_BUFFER_SERIALIZED_MODIFY_APPLE* = 0x8A12.GLenum + GL_TEXTURE_COORD_ARRAY_COUNT_EXT* = 0x808B.GLenum + GL_MAX_DRAW_BUFFERS_ARB* = 0x8824.GLenum + GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT* = 0x87CD.GLenum + GL_PASS_THROUGH_TOKEN* = 0x0700.GLenum + GL_BLEND_EQUATION* = 0x8009.GLenum + GL_FOG_HINT* = 0x0C54.GLenum + GL_FLOAT_RGB16_NV* = 0x8888.GLenum + GL_OUTPUT_TEXTURE_COORD18_EXT* = 0x87AF.GLenum + GL_T2F_IUI_N3F_V2F_EXT* = 0x81B3.GLenum + GL_SAMPLER_EXTERNAL_OES* = 0x8D66.GLenum + GL_MAX_SUBROUTINES* = 0x8DE7.GLenum + GL_RED_BIT_ATI* = 0x00000001.GLbitfield + GL_SOURCE2_ALPHA* = 0x858A.GLenum + GL_AUX0* = 0x0409.GLenum + GL_OPERAND1_ALPHA_ARB* = 0x8599.GLenum + GL_TEXTURE_MAX_ANISOTROPY_EXT* = 0x84FE.GLenum + GL_VERTEX_PROGRAM_POINT_SIZE_NV* = 0x8642.GLenum + GL_MULTIVIEW_EXT* = 0x90F1.GLenum + GL_FOG_OFFSET_SGIX* = 0x8198.GLenum + GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL* = 0x83F7.GLenum + GL_ELEMENT_ARRAY_ATI* = 0x8768.GLenum + GL_ALPHA16_SNORM* = 0x9018.GLenum + GL_COMPRESSED_SLUMINANCE_EXT* = 0x8C4A.GLenum + GL_TEXTURE_OBJECT_VALID_QCOM* = 0x8BDB.GLenum + GL_STENCIL_BACK_FUNC* = 0x8800.GLenum + GL_CULL_FACE* = 0x0B44.GLenum + GL_MAP1_COLOR_4* = 0x0D90.GLenum + GL_SHADER_OBJECT_ARB* = 0x8B48.GLenum + GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG* = 0x8C01.GLenum + GL_TANGENT_ARRAY_EXT* = 0x8439.GLenum + GL_NUM_FRAGMENT_CONSTANTS_ATI* = 0x896F.GLenum + GL_COLOR_RENDERABLE* = 0x8286.GLenum + GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS* = 0x8B4D.GLenum + GL_TRANSFORM_FEEDBACK_RECORD_NV* = 0x8C86.GLenum + GL_COLOR_ATTACHMENT1_NV* = 0x8CE1.GLenum + GL_ALPHA_SNORM* = 0x9010.GLenum + GL_PIXEL_TRANSFORM_2D_MATRIX_EXT* = 0x8338.GLenum + GL_SMOOTH_POINT_SIZE_GRANULARITY* = 0x0B13.GLenum + GL_R8I* = 0x8231.GLenum + GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT* = 0x8D56.GLenum + GL_POLYGON_OFFSET_BIAS_EXT* = 0x8039.GLenum + GL_DEPTH_COMPONENT24* = 0x81A6.GLenum + GL_TEXTURE_SWIZZLE_B* = 0x8E44.GLenum + GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS* = 0x8E81.GLenum + GL_MAP2_INDEX* = 0x0DB1.GLenum + GL_SAMPLER_CUBE_MAP_ARRAY* = 0x900C.GLenum + GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT* = 0x8CD6.GLenum + GL_UNSIGNED_INT_8_8_8_8_REV* = 0x8367.GLenum + GL_PATH_GEN_COEFF_NV* = 0x90B1.GLenum + GL_OPERAND3_ALPHA_NV* = 0x859B.GLenum + GL_LUMINANCE* = 0x1909.GLenum + GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS* = 0x8DE8.GLenum + GL_MAP_READ_BIT* = 0x0001.GLbitfield + GL_MAX_TEXTURE_STACK_DEPTH* = 0x0D39.GLenum + GL_ORDER* = 0x0A01.GLenum + GL_PATH_FILL_MODE_NV* = 0x9080.GLenum + GL_RENDERBUFFER_BLUE_SIZE* = 0x8D52.GLenum + GL_TEXTURE_INTENSITY_SIZE* = 0x8061.GLenum + GL_DRAW_BUFFER1_NV* = 0x8826.GLenum + GL_SCREEN_NV* = 0x9295.GLenum + GL_RGB8I_EXT* = 0x8D8F.GLenum + GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET* = 0x8E5E.GLenum + GL_DUAL_INTENSITY12_SGIS* = 0x811A.GLenum + GL_SPARE1_NV* = 0x852F.GLenum + GL_PALETTE8_R5_G6_B5_OES* = 0x8B97.GLenum + GL_COLOR_ATTACHMENT7_NV* = 0x8CE7.GLenum + GL_TEXTURE_HEIGHT* = 0x1001.GLenum + GL_RENDERBUFFER_BINDING* = 0x8CA7.GLenum + GL_DRAW_BUFFER7_EXT* = 0x882C.GLenum + GL_HISTOGRAM* = 0x8024.GLenum + GL_COLOR_ATTACHMENT0_OES* = 0x8CE0.GLenum + GL_BINORMAL_ARRAY_STRIDE_EXT* = 0x8441.GLenum + GL_DEBUG_SEVERITY_HIGH_AMD* = 0x9146.GLenum + GL_MIN_SPARSE_LEVEL_AMD* = 0x919B.GLenum + GL_MAP1_VERTEX_ATTRIB10_4_NV* = 0x866A.GLenum + GL_COEFF* = 0x0A00.GLenum + GL_COMPRESSED_RGBA_ASTC_6x5_KHR* = 0x93B3.GLenum + GL_TEXTURE_4D_BINDING_SGIS* = 0x814F.GLenum + GL_BUFFER_USAGE* = 0x8765.GLenum + GL_YCBCR_MESA* = 0x8757.GLenum + GL_CLAMP_VERTEX_COLOR* = 0x891A.GLenum + GL_RGBA8_EXT* = 0x8058.GLenum + GL_BITMAP_TOKEN* = 0x0704.GLenum + GL_IMAGE_SCALE_Y_HP* = 0x8156.GLenum + GL_OUTPUT_TEXTURE_COORD25_EXT* = 0x87B6.GLenum + GL_DEBUG_SOURCE_API* = 0x8246.GLenum + GL_STACK_UNDERFLOW* = 0x0504.GLenum + GL_COMBINER_CD_DOT_PRODUCT_NV* = 0x8546.GLenum + GL_FRAMEBUFFER_BINDING_EXT* = 0x8CA6.GLenum + GL_REG_20_ATI* = 0x8935.GLenum + GL_MAP1_TEXTURE_COORD_4* = 0x0D96.GLenum + GL_DEBUG_OUTPUT_SYNCHRONOUS* = 0x8242.GLenum + GL_ACCUM_ALPHA_BITS* = 0x0D5B.GLenum + GL_INT_10_10_10_2_OES* = 0x8DF7.GLenum + GL_FLOAT_MAT2_ARB* = 0x8B5A.GLenum + GL_FRONT_RIGHT* = 0x0401.GLenum + GL_COMBINER_AB_DOT_PRODUCT_NV* = 0x8545.GLenum + GL_LUMINANCE_ALPHA* = 0x190A.GLenum + GL_C4UB_V2F* = 0x2A22.GLenum + GL_COMBINER_MUX_SUM_NV* = 0x8547.GLenum + GL_MODELVIEW_STACK_DEPTH* = 0x0BA3.GLenum + GL_SAMPLES_ARB* = 0x80A9.GLenum + GL_ALPHA_TEST_FUNC* = 0x0BC1.GLenum + GL_DEPTH_CLAMP* = 0x864F.GLenum + GL_MAP2_VERTEX_ATTRIB8_4_NV* = 0x8678.GLenum + GL_INVALID_INDEX* = 0xFFFFFFFF.GLenum + GL_COMBINER_SCALE_NV* = 0x8548.GLenum + GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER* = 0x92CB.GLenum + GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV* = 0x864E.GLenum + GL_RELATIVE_SMALL_CW_ARC_TO_NV* = 0x15.GLenum + GL_UNSIGNED_INT_10_10_10_2_OES* = 0x8DF6.GLenum + GL_DISCARD_ATI* = 0x8763.GLenum + GL_PRIMITIVE_RESTART_INDEX_NV* = 0x8559.GLenum + GL_IMAGE_CLASS_2_X_8* = 0x82C0.GLenum + GL_MANUAL_GENERATE_MIPMAP* = 0x8294.GLenum + GL_FLOAT_R_NV* = 0x8880.GLenum + GL_SATURATE_BIT_ATI* = 0x00000040.GLbitfield + GL_BUFFER_SIZE* = 0x8764.GLenum + GL_FRAMEBUFFER_BARRIER_BIT_EXT* = 0x00000400.GLbitfield + GL_LUMINANCE8UI_EXT* = 0x8D80.GLenum + GL_T2F_IUI_V2F_EXT* = 0x81B1.GLenum + GL_OUTPUT_TEXTURE_COORD15_EXT* = 0x87AC.GLenum + GL_COVERAGE_AUTOMATIC_NV* = 0x8ED7.GLenum + GL_TEXTURE_INTERNAL_FORMAT_QCOM* = 0x8BD5.GLenum + GL_INT_IMAGE_CUBE_MAP_ARRAY* = 0x905F.GLenum + GL_BUFFER_UPDATE_BARRIER_BIT_EXT* = 0x00000200.GLbitfield + GL_GLYPH_WIDTH_BIT_NV* = 0x01.GLbitfield + GL_OP_MAX_EXT* = 0x878A.GLenum + GL_MINMAX_FORMAT_EXT* = 0x802F.GLenum + GL_R16I* = 0x8233.GLenum + GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB* = 0x8809.GLenum + GL_TEXTURE_MAX_LEVEL* = 0x813D.GLenum + GL_GEOMETRY_SHADER* = 0x8DD9.GLenum + GL_MAX_RENDERBUFFER_SIZE* = 0x84E8.GLenum + GL_RGB16_EXT* = 0x8054.GLenum + GL_DUAL_INTENSITY16_SGIS* = 0x811B.GLenum + GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT* = 0x8CD6.GLenum + GL_BLUE_SCALE* = 0x0D1A.GLenum + GL_RGBA_FLOAT16_APPLE* = 0x881A.GLenum + GL_RGBA8UI* = 0x8D7C.GLenum + GL_COLOR_ATTACHMENT5* = 0x8CE5.GLenum + GL_UNSIGNED_IDENTITY_NV* = 0x8536.GLenum + GL_COMPRESSED_RGBA_ASTC_10x8_KHR* = 0x93BA.GLenum + GL_FRAGMENT_SHADER_ARB* = 0x8B30.GLenum + GL_R8* = 0x8229.GLenum + GL_IMAGE_BINDING_LAYERED* = 0x8F3C.GLenum + GL_RGBA_FLOAT32_ATI* = 0x8814.GLenum + GL_TEXTURE_RED_SIZE_EXT* = 0x805C.GLenum + GL_INT8_VEC2_NV* = 0x8FE1.GLenum + GL_NEGATE_BIT_ATI* = 0x00000004.GLbitfield + GL_ALL_BARRIER_BITS_EXT* = 0xFFFFFFFF.GLbitfield + GL_LIGHT_MODEL_COLOR_CONTROL_EXT* = 0x81F8.GLenum + GL_LUMINANCE_ALPHA16UI_EXT* = 0x8D7B.GLenum + GL_COUNT_UP_NV* = 0x9088.GLenum + GL_QUERY_RESULT_AVAILABLE_ARB* = 0x8867.GLenum + GL_DRAW_INDIRECT_BUFFER* = 0x8F3F.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT* = 0x8CD3.GLenum + GL_OP_DOT3_EXT* = 0x8784.GLenum + GL_COLOR_ATTACHMENT10_NV* = 0x8CEA.GLenum + GL_STENCIL_INDEX4_OES* = 0x8D47.GLenum + GL_LUMINANCE_FLOAT32_ATI* = 0x8818.GLenum + GL_DRAW_BUFFER9_ARB* = 0x882E.GLenum + GL_RG8_EXT* = 0x822B.GLenum + GL_FONT_DESCENDER_BIT_NV* = 0x00400000.GLbitfield + GL_TEXTURE_ALPHA_SIZE_EXT* = 0x805F.GLenum + GL_Y_EXT* = 0x87D6.GLenum + GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT* = 0x8DE4.GLenum + GL_SAMPLER_3D_ARB* = 0x8B5F.GLenum + GL_INVERT_OVG_NV* = 0x92B4.GLenum + GL_REFERENCED_BY_TESS_EVALUATION_SHADER* = 0x9308.GLenum + GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL* = 0x83F8.GLenum + GL_LIGHT4* = 0x4004.GLenum + GL_VERTEX_STATE_PROGRAM_NV* = 0x8621.GLenum + GL_ZERO* = 0.GLenum + GL_SAMPLER_CUBE_MAP_ARRAY_ARB* = 0x900C.GLenum + GL_SAMPLE_MASK_EXT* = 0x80A0.GLenum + GL_COMBINER_CD_OUTPUT_NV* = 0x854B.GLenum + GL_SAMPLE_ALPHA_TO_MASK_SGIS* = 0x809E.GLenum + GL_RGBA16* = 0x805B.GLenum + GL_PATH_TERMINAL_DASH_CAP_NV* = 0x907D.GLenum + GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB* = 0x889C.GLenum + GL_DEBUG_SEVERITY_HIGH_KHR* = 0x9146.GLenum + GL_DRAW_BUFFER14_EXT* = 0x8833.GLenum + GL_READ_FRAMEBUFFER* = 0x8CA8.GLenum + GL_UNSIGNED_SHORT_8_8_APPLE* = 0x85BA.GLenum + GL_OR* = 0x1507.GLenum + GL_ONE_MINUS_DST_ALPHA* = 0x0305.GLenum + GL_RGB12* = 0x8053.GLenum + GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_OES* = 0x8CDB.GLenum + GL_OUTPUT_TEXTURE_COORD26_EXT* = 0x87B7.GLenum + GL_LOCAL_CONSTANT_VALUE_EXT* = 0x87EC.GLenum + GL_SURFACE_REGISTERED_NV* = 0x86FD.GLenum + GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV* = 0x8E5D.GLenum + GL_COMPRESSED_RG_RGTC2* = 0x8DBD.GLenum + GL_MAX_VERTEX_ATTRIB_STRIDE* = 0x82E5.GLenum + GL_COLOR_ARRAY_ADDRESS_NV* = 0x8F23.GLenum + GL_MATRIX_INDEX_ARRAY_POINTER_ARB* = 0x8849.GLenum + GL_DUAL_ALPHA8_SGIS* = 0x8111.GLenum + GL_TEXTURE_MAX_LOD* = 0x813B.GLenum + GL_INTERNALFORMAT_SHARED_SIZE* = 0x8277.GLenum + GL_LINEAR_DETAIL_SGIS* = 0x8097.GLenum + GL_RG16F_EXT* = 0x822F.GLenum + GL_LIST_MODE* = 0x0B30.GLenum + GL_VIEWPORT_INDEX_PROVOKING_VERTEX* = 0x825F.GLenum + GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW* = 0x900D.GLenum + GL_COLOR_TABLE_LUMINANCE_SIZE* = 0x80DE.GLenum + GL_COLOR_ARRAY_POINTER* = 0x8090.GLenum + GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT* = 0x84FF.GLenum + GL_LUMINANCE32F_EXT* = 0x8818.GLenum + GL_FRAMEBUFFER_COMPLETE_OES* = 0x8CD5.GLenum + GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB* = 0x8F9F.GLenum + GL_FEEDBACK* = 0x1C01.GLenum + GL_UNSIGNED_INT_IMAGE_2D_ARRAY* = 0x9069.GLenum + GL_VERTEX_STREAM1_ATI* = 0x876D.GLenum + GL_SLUMINANCE_ALPHA_NV* = 0x8C44.GLenum + GL_MAX_TEXTURE_UNITS_ARB* = 0x84E2.GLenum + GL_MODELVIEW11_ARB* = 0x872B.GLenum + GL_DRAW_FRAMEBUFFER_BINDING_ANGLE* = 0x8CA6.GLenum + GL_NEGATIVE_W_EXT* = 0x87DC.GLenum + GL_MODELVIEW25_ARB* = 0x8739.GLenum + GL_NORMAL_ARRAY_LIST_STRIDE_IBM* = 103081.GLenum + GL_CON_0_ATI* = 0x8941.GLenum + GL_VERTEX_SHADER_INSTRUCTIONS_EXT* = 0x87CF.GLenum + GL_TRANSPOSE_PROGRAM_MATRIX_EXT* = 0x8E2E.GLenum + GL_TEXTURE_DEPTH_TYPE* = 0x8C16.GLenum + GL_PROGRAM_TARGET_NV* = 0x8646.GLenum + GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT* = 0x87CC.GLenum + GL_NORMAL_ARRAY_STRIDE_EXT* = 0x807F.GLenum + GL_INT_SAMPLER_2D* = 0x8DCA.GLenum + GL_MAP2_VERTEX_ATTRIB10_4_NV* = 0x867A.GLenum + GL_STEREO* = 0x0C33.GLenum + GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT* = 0x9065.GLenum + GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV* = 0x8C75.GLenum + GL_TRACE_ERRORS_BIT_MESA* = 0x0020.GLbitfield + GL_MAX_GEOMETRY_UNIFORM_BLOCKS* = 0x8A2C.GLenum + GL_CONVOLUTION_2D* = 0x8011.GLenum + GL_RGB_SCALE_ARB* = 0x8573.GLenum + GL_VIDEO_COLOR_CONVERSION_MAX_NV* = 0x902A.GLenum + GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS* = 0x90DD.GLenum + GL_TABLE_TOO_LARGE_EXT* = 0x8031.GLenum + GL_TRANSFORM_FEEDBACK_BINDING_NV* = 0x8E25.GLenum + GL_TEXTURE16_ARB* = 0x84D0.GLenum + GL_FRAGMENT_SHADER_DERIVATIVE_HINT* = 0x8B8B.GLenum + GL_IUI_N3F_V2F_EXT* = 0x81AF.GLenum + GL_CLIP_PLANE2_IMG* = 0x3002.GLenum + GL_VERTEX_ATTRIB_ARRAY10_NV* = 0x865A.GLenum + GL_TEXTURE_FETCH_BARRIER_BIT* = 0x00000008.GLbitfield + GL_DOT3_RGBA_EXT* = 0x8741.GLenum + GL_RENDERBUFFER_GREEN_SIZE_EXT* = 0x8D51.GLenum + GL_MAX_CLIENT_ATTRIB_STACK_DEPTH* = 0x0D3B.GLenum + GL_UNPACK_COMPRESSED_BLOCK_SIZE* = 0x912A.GLenum + GL_SAMPLE_BUFFERS_SGIS* = 0x80A8.GLenum + GL_MAP1_VERTEX_ATTRIB1_4_NV* = 0x8661.GLenum + GL_BUFFER_OBJECT_EXT* = 0x9151.GLenum + GL_INT_SAMPLER_1D_ARRAY* = 0x8DCE.GLenum + GL_POST_TEXTURE_FILTER_SCALE_SGIX* = 0x817A.GLenum + GL_RED_MAX_CLAMP_INGR* = 0x8564.GLenum + GL_POST_COLOR_MATRIX_RED_SCALE_SGI* = 0x80B4.GLenum + GL_TEXTURE_COORD_ARRAY_TYPE* = 0x8089.GLenum + GL_COMPRESSED_SIGNED_RG11_EAC* = 0x9273.GLenum + GL_MULTISAMPLE_FILTER_HINT_NV* = 0x8534.GLenum + GL_COMPRESSED_RGBA8_ETC2_EAC* = 0x9278.GLenum + GL_FONT_UNDERLINE_THICKNESS_BIT_NV* = 0x08000000.GLbitfield + GL_READ_WRITE_ARB* = 0x88BA.GLenum + GL_RENDER_MODE* = 0x0C40.GLenum + GL_MAX_NUM_COMPATIBLE_SUBROUTINES* = 0x92F8.GLenum + GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI* = 0x87F8.GLenum + GL_MODELVIEW0_STACK_DEPTH_EXT* = 0x0BA3.GLenum + GL_CONTEXT_FLAG_DEBUG_BIT* = 0x00000002.GLbitfield + GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT* = 0x8C84.GLenum + GL_POINT_SIZE_MAX_EXT* = 0x8127.GLenum + GL_COLOR_ARRAY_LENGTH_NV* = 0x8F2D.GLenum + GL_COLOR_COMPONENTS* = 0x8283.GLenum + GL_LINEARDODGE_NV* = 0x92A4.GLenum + GL_TEXTURE20_ARB* = 0x84D4.GLenum + GL_UNSIGNED_INT64_VEC4_NV* = 0x8FF7.GLenum + GL_TEXTURE28* = 0x84DC.GLenum + GL_HISTOGRAM_FORMAT_EXT* = 0x8027.GLenum + GL_PROGRAM_MATRIX_EXT* = 0x8E2D.GLenum + GL_PIXEL_PACK_BUFFER_EXT* = 0x88EB.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT* = 0x8515.GLenum + GL_STANDARD_FONT_NAME_NV* = 0x9072.GLenum + GL_REG_13_ATI* = 0x892E.GLenum + GL_GREEN_SCALE* = 0x0D18.GLenum + GL_COLOR_BUFFER_BIT7_QCOM* = 0x00000080.GLbitfield + GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS* = 0x8264.GLenum + GL_LUMINANCE8_ALPHA8_SNORM* = 0x9016.GLenum + GL_GCCSO_SHADER_BINARY_FJ* = 0x9260.GLenum + GL_COORD_REPLACE_NV* = 0x8862.GLenum + GL_SOURCE2_RGB_EXT* = 0x8582.GLenum + GL_IR_INSTRUMENT1_SGIX* = 0x817F.GLenum + GL_CONTEXT_FLAG_DEBUG_BIT_KHR* = 0x00000002.GLbitfield + GL_SWIZZLE_STR_ATI* = 0x8976.GLenum + GL_OUTPUT_TEXTURE_COORD17_EXT* = 0x87AE.GLenum + GL_MODELVIEW2_ARB* = 0x8722.GLenum + GL_R1UI_C4F_N3F_V3F_SUN* = 0x85C8.GLenum + GL_MAX_TEXTURE_BUFFER_SIZE_ARB* = 0x8C2B.GLenum + GL_OUTPUT_TEXTURE_COORD0_EXT* = 0x879D.GLenum + GL_POINT_FADE_THRESHOLD_SIZE_EXT* = 0x8128.GLenum + GL_OUTPUT_TEXTURE_COORD30_EXT* = 0x87BB.GLenum + GL_EVAL_VERTEX_ATTRIB3_NV* = 0x86C9.GLenum + GL_SPHERE_MAP* = 0x2402.GLenum + GL_SHADER_IMAGE_ATOMIC* = 0x82A6.GLenum + GL_INDEX_BITS* = 0x0D51.GLenum + GL_INTERNALFORMAT_ALPHA_TYPE* = 0x827B.GLenum + GL_CON_15_ATI* = 0x8950.GLenum + GL_TESS_EVALUATION_TEXTURE* = 0x829D.GLenum + GL_EDGE_FLAG_ARRAY_STRIDE* = 0x808C.GLenum + GL_VERTEX_ATTRIB_ARRAY8_NV* = 0x8658.GLenum + GL_POST_COLOR_MATRIX_COLOR_TABLE* = 0x80D2.GLenum + GL_CLOSE_PATH_NV* = 0x00.GLenum + GL_SCALE_BY_TWO_NV* = 0x853E.GLenum + GL_PALETTE8_RGB8_OES* = 0x8B95.GLenum + GL_MAX_COMPUTE_ATOMIC_COUNTERS* = 0x8265.GLenum + GL_VERTEX_ATTRIB_ARRAY_NORMALIZED* = 0x886A.GLenum + GL_MAX_VERTEX_ATTRIBS* = 0x8869.GLenum + GL_PROGRAM_POINT_SIZE_EXT* = 0x8642.GLenum + GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE* = 0x93A0.GLenum + GL_SIGNED_NORMALIZED* = 0x8F9C.GLenum + GL_MAX_CUBE_MAP_TEXTURE_SIZE_OES* = 0x851C.GLenum + GL_OFFSET_TEXTURE_2D_SCALE_NV* = 0x86E2.GLenum + GL_COMPRESSED_SLUMINANCE* = 0x8C4A.GLenum + GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS* = 0x8E80.GLenum + GL_RASTER_POSITION_UNCLIPPED_IBM* = 0x19262.GLenum + GL_COMPRESSED_TEXTURE_FORMATS_ARB* = 0x86A3.GLenum + GL_TRANSPOSE_MODELVIEW_MATRIX* = 0x84E3.GLenum + GL_ALPHA_FLOAT16_APPLE* = 0x881C.GLenum + GL_PIXEL_MIN_FILTER_EXT* = 0x8332.GLenum + GL_MAX_SPARSE_TEXTURE_SIZE_AMD* = 0x9198.GLenum + GL_UNSIGNED_SHORT_5_6_5_REV_EXT* = 0x8364.GLenum + GL_DU8DV8_ATI* = 0x877A.GLenum + GL_COLOR_ARRAY_LIST_IBM* = 103072.GLenum + GL_RGBA8I_EXT* = 0x8D8E.GLenum + GL_MULTISAMPLE_BUFFER_BIT4_QCOM* = 0x10000000.GLbitfield + GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB* = 0x824D.GLenum + GL_MODELVIEW20_ARB* = 0x8734.GLenum + GL_COLOR_TABLE_RED_SIZE* = 0x80DA.GLenum + GL_UNIFORM_BARRIER_BIT* = 0x00000004.GLbitfield + GL_TEXTURE* = 0x1702.GLenum + GL_CLIP_PLANE0* = 0x3000.GLenum + GL_FOG_COORDINATE_ARRAY_POINTER* = 0x8456.GLenum + GL_CONSTANT_ALPHA_EXT* = 0x8003.GLenum + GL_NAME_STACK_DEPTH* = 0x0D70.GLenum + GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE* = 0x83F2.GLenum + GL_LINEAR_DETAIL_ALPHA_SGIS* = 0x8098.GLenum + GL_EDGE_FLAG_ARRAY_POINTER_EXT* = 0x8093.GLenum + GL_UNSIGNED_SHORT* = 0x1403.GLenum + GL_MAP2_VERTEX_ATTRIB1_4_NV* = 0x8671.GLenum + GL_DEPTH_CLAMP_FAR_AMD* = 0x901F.GLenum + GL_OPERAND3_RGB_NV* = 0x8593.GLenum + GL_TEXTURE_SWIZZLE_R_EXT* = 0x8E42.GLenum + GL_PATCHES* = 0x000E.GLenum + GL_TEXTURE12* = 0x84CC.GLenum + GL_COLOR_ATTACHMENT12_EXT* = 0x8CEC.GLenum + GL_MAP2_VERTEX_ATTRIB15_4_NV* = 0x867F.GLenum + GL_DRAW_BUFFER15_ATI* = 0x8834.GLenum + GL_GEOMETRY_INPUT_TYPE* = 0x8917.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC_OES* = 0x9279.GLenum + GL_RGBA32UI_EXT* = 0x8D70.GLenum + GL_RGBA_FLOAT32_APPLE* = 0x8814.GLenum + GL_NORMAL_MAP_OES* = 0x8511.GLenum + GL_MAP2_GRID_DOMAIN* = 0x0DD2.GLenum + GL_RELATIVE_HORIZONTAL_LINE_TO_NV* = 0x07.GLenum + GL_TANGENT_ARRAY_STRIDE_EXT* = 0x843F.GLenum + GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT* = 0x8CDB.GLenum + GL_OBJECT_POINT_SGIS* = 0x81F5.GLenum + GL_IMAGE_2D_ARRAY* = 0x9053.GLenum + GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB* = 0x8DDF.GLenum + GL_SPRITE_MODE_SGIX* = 0x8149.GLenum + GL_WEIGHT_ARRAY_OES* = 0x86AD.GLenum + GL_MAX_VERTEX_STREAMS* = 0x8E71.GLenum + GL_R16F_EXT* = 0x822D.GLenum + GL_VERSION_ES_CL_1_0* = 1.GLenum + GL_PROXY_TEXTURE_COLOR_TABLE_SGI* = 0x80BD.GLenum + GL_MAX_PROGRAM_INSTRUCTIONS_ARB* = 0x88A1.GLenum + GL_PURGEABLE_APPLE* = 0x8A1D.GLenum + GL_TEXTURE_SWIZZLE_G_EXT* = 0x8E43.GLenum + GL_FIRST_VERTEX_CONVENTION_EXT* = 0x8E4D.GLenum + GL_DEBUG_SEVERITY_LOW* = 0x9148.GLenum + GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT* = 0x00000001.GLbitfield + GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB* = 0x8B8A.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR* = 0x93D4.GLenum + GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV* = 0x86F3.GLenum + GL_RENDERBUFFER_DEPTH_SIZE* = 0x8D54.GLenum + GL_OPERAND1_RGB_ARB* = 0x8591.GLenum + GL_REFLECTION_MAP_NV* = 0x8512.GLenum + GL_MATRIX17_ARB* = 0x88D1.GLenum + GL_EYE_PLANE_ABSOLUTE_NV* = 0x855C.GLenum + GL_SRC1_ALPHA* = 0x8589.GLenum + GL_UNSIGNED_BYTE_2_3_3_REV* = 0x8362.GLenum + GL_RGB5_EXT* = 0x8050.GLenum + GL_TEXTURE_2D_ARRAY* = 0x8C1A.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB* = 0x8515.GLenum + GL_TEXTURE26* = 0x84DA.GLenum + GL_MAX_3D_TEXTURE_SIZE_OES* = 0x8073.GLenum + GL_PIXEL_TILE_WIDTH_SGIX* = 0x8140.GLenum + GL_PIXEL_UNPACK_BUFFER_BINDING_EXT* = 0x88EF.GLenum + GL_TEXTURE_ALPHA_SIZE* = 0x805F.GLenum + GL_RELATIVE_QUADRATIC_CURVE_TO_NV* = 0x0B.GLenum + GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES* = 0x8B9F.GLenum + GL_GEOMETRY_DEFORMATION_BIT_SGIX* = 0x00000002.GLbitfield + GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS* = 0x8DA8.GLenum + GL_NAMED_STRING_LENGTH_ARB* = 0x8DE9.GLenum + GL_IMAGE_1D_ARRAY* = 0x9052.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES* = 0x8CD4.GLenum + GL_MATRIX28_ARB* = 0x88DC.GLenum + GL_FRAGMENT_LIGHT1_SGIX* = 0x840D.GLenum + GL_HARDMIX_NV* = 0x92A9.GLenum + GL_DEBUG_SOURCE_THIRD_PARTY_KHR* = 0x8249.GLenum + GL_PACK_SWAP_BYTES* = 0x0D00.GLenum + GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB* = 0x8B4A.GLenum + GL_SOURCE2_ALPHA_EXT* = 0x858A.GLenum + GL_DOUBLE_MAT2x4* = 0x8F4A.GLenum + GL_MEDIUM_FLOAT* = 0x8DF1.GLenum + GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX* = 0x813E.GLenum + GL_UNPACK_SKIP_ROWS* = 0x0CF3.GLenum + GL_PACK_COMPRESSED_BLOCK_SIZE* = 0x912E.GLenum + GL_UNSIGNED_INT_IMAGE_2D* = 0x9063.GLenum + GL_COLOR_ARRAY_TYPE_EXT* = 0x8082.GLenum + GL_BUFFER_MAP_POINTER_ARB* = 0x88BD.GLenum + GL_CALLIGRAPHIC_FRAGMENT_SGIX* = 0x8183.GLenum + GL_ONE_MINUS_CONSTANT_COLOR_EXT* = 0x8002.GLenum + GL_COMPRESSED_RGBA_FXT1_3DFX* = 0x86B1.GLenum + GL_CLIP_PLANE1* = 0x3001.GLenum + GL_COVERAGE_BUFFERS_NV* = 0x8ED3.GLenum + GL_ADD_BLEND_IMG* = 0x8C09.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR* = 0x93D5.GLenum + GL_PIXEL_TILE_HEIGHT_SGIX* = 0x8141.GLenum + GL_SAMPLE_COVERAGE_INVERT_ARB* = 0x80AB.GLenum + GL_MAP1_VERTEX_ATTRIB9_4_NV* = 0x8669.GLenum + GL_COLOR_TABLE_BIAS_SGI* = 0x80D7.GLenum + GL_EDGE_FLAG_ARRAY_COUNT_EXT* = 0x808D.GLenum + GL_SAMPLE_BUFFERS_EXT* = 0x80A8.GLenum + GL_COLOR_INDEX* = 0x1900.GLenum + GL_REPLACEMENT_CODE_SUN* = 0x81D8.GLenum + GL_INT_SAMPLER_CUBE_EXT* = 0x8DCC.GLenum + GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE* = 0x8D56.GLenum + GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV* = 0x8F1E.GLenum + GL_DUAL_LUMINANCE_ALPHA8_SGIS* = 0x811D.GLenum + GL_PIXEL_TEX_GEN_ALPHA_LS_SGIX* = 0x8189.GLenum + GL_CLIP_DISTANCE7* = 0x3007.GLenum + GL_DOT3_RGB_ARB* = 0x86AE.GLenum + GL_TEXTURE_WRAP_T* = 0x2803.GLenum + GL_LUMINANCE12_EXT* = 0x8041.GLenum + GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX* = 0x8174.GLenum + GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB* = 0x86A0.GLenum + GL_EVAL_2D_NV* = 0x86C0.GLenum + GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS* = 0x9314.GLenum + GL_CURRENT_WEIGHT_ARB* = 0x86A8.GLenum + GL_DEBUG_SOURCE_API_ARB* = 0x8246.GLenum + GL_FOG_SPECULAR_TEXTURE_WIN* = 0x80EC.GLenum + GL_BOOL_VEC4* = 0x8B59.GLenum + GL_FRAGMENTS_INSTRUMENT_SGIX* = 0x8313.GLenum + GL_GEOMETRY_OUTPUT_TYPE_EXT* = 0x8DDC.GLenum + GL_TEXTURE_2D* = 0x0DE1.GLenum + GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI* = 0x00200000.GLbitfield + GL_TEXTURE_BINDING_RECTANGLE_ARB* = 0x84F6.GLenum + GL_SAMPLE_BUFFERS_3DFX* = 0x86B3.GLenum + GL_INDEX_OFFSET* = 0x0D13.GLenum + GL_MAX_COLOR_ATTACHMENTS* = 0x8CDF.GLenum + GL_PLUS_CLAMPED_NV* = 0x92B1.GLenum + GL_SIGNED_NEGATE_NV* = 0x853D.GLenum + GL_PROXY_TEXTURE_2D_STACK_MESAX* = 0x875C.GLenum + GL_MAX_VERTEX_UNIFORM_COMPONENTS* = 0x8B4A.GLenum + GL_SAMPLE_MASK_VALUE_SGIS* = 0x80AA.GLenum + GL_QUADRATIC_ATTENUATION* = 0x1209.GLenum + GL_LUMINANCE32F_ARB* = 0x8818.GLenum + GL_COVERAGE_COMPONENT4_NV* = 0x8ED1.GLenum + GL_MINMAX_FORMAT* = 0x802F.GLenum + GL_SRGB_DECODE_ARB* = 0x8299.GLenum + GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT* = 0x8CDA.GLenum + GL_UNSIGNED_INT_SAMPLER_CUBE_EXT* = 0x8DD4.GLenum + GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2* = 0x9277.GLenum + GL_DISJOINT_NV* = 0x9283.GLenum + GL_TEXTURE_ENV_BIAS_SGIX* = 0x80BE.GLenum + GL_PROXY_TEXTURE_3D_EXT* = 0x8070.GLenum + GL_SGX_BINARY_IMG* = 0x8C0A.GLenum + GL_COPY_READ_BUFFER* = 0x8F36.GLenum + GL_POINT_FADE_THRESHOLD_SIZE_SGIS* = 0x8128.GLenum + GL_UNIFORM_MATRIX_STRIDE* = 0x8A3D.GLenum + GL_UNIFORM_BLOCK_NAME_LENGTH* = 0x8A41.GLenum + GL_HISTOGRAM_LUMINANCE_SIZE* = 0x802C.GLenum + GL_UNSIGNED_SHORT_4_4_4_4* = 0x8033.GLenum + GL_MAX_DEPTH* = 0x8280.GLenum + GL_IMAGE_1D* = 0x904C.GLenum + GL_LUMINANCE8_ALPHA8_EXT* = 0x8045.GLenum + GL_MAX_TEXTURE_IMAGE_UNITS* = 0x8872.GLenum + GL_MODELVIEW16_ARB* = 0x8730.GLenum + GL_CURRENT_PALETTE_MATRIX_OES* = 0x8843.GLenum + GL_SIGNED_HILO_NV* = 0x86F9.GLenum + GL_FRAMEBUFFER_DEFAULT_HEIGHT* = 0x9311.GLenum + GL_UNPACK_SKIP_IMAGES* = 0x806D.GLenum + GL_2_BYTES* = 0x1407.GLenum + GL_ALLOW_DRAW_FRG_HINT_PGI* = 0x1A210.GLenum + GL_INTENSITY16I_EXT* = 0x8D8B.GLenum + GL_MAX_SAMPLES_NV* = 0x8D57.GLenum + GL_VERTEX_ARRAY_STORAGE_HINT_APPLE* = 0x851F.GLenum + GL_LINE_STRIP_ADJACENCY_ARB* = 0x000B.GLenum + GL_COORD_REPLACE* = 0x8862.GLenum + GL_INDEX_MATERIAL_FACE_EXT* = 0x81BA.GLenum + GL_MODELVIEW15_ARB* = 0x872F.GLenum + GL_TEXTURE19* = 0x84D3.GLenum + GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT* = 0x9068.GLenum + GL_SIGNED_INTENSITY8_NV* = 0x8708.GLenum + GL_TEXTURE_MAG_SIZE_NV* = 0x871F.GLenum + GL_DISPATCH_INDIRECT_BUFFER* = 0x90EE.GLenum + GL_MAP1_INDEX* = 0x0D91.GLenum + GL_TEXTURE_BUFFER_DATA_STORE_BINDING* = 0x8C2D.GLenum + GL_MAX_HEIGHT* = 0x827F.GLenum + GL_BLEND_DST_ALPHA* = 0x80CA.GLenum + GL_R1UI_C3F_V3F_SUN* = 0x85C6.GLenum + GL_TEXTURE_PRIORITY_EXT* = 0x8066.GLenum + GL_INT_IMAGE_2D* = 0x9058.GLenum + GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV* = 0x8E11.GLenum + GL_DRAW_BUFFER4_ATI* = 0x8829.GLenum + GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB* = 0x8DDD.GLenum + GL_DEPTH_EXT* = 0x1801.GLenum + GL_SAMPLE_POSITION* = 0x8E50.GLenum + GL_INTERNALFORMAT_DEPTH_TYPE* = 0x827C.GLenum + GL_MATRIX23_ARB* = 0x88D7.GLenum + GL_DEBUG_TYPE_PUSH_GROUP* = 0x8269.GLenum + GL_POLYGON_OFFSET_FILL* = 0x8037.GLenum + GL_FRAGMENT_PROGRAM_BINDING_NV* = 0x8873.GLenum + GL_FRAMEBUFFER_SRGB_CAPABLE_EXT* = 0x8DBA.GLenum + GL_VERTEX_ATTRIB_BINDING* = 0x82D4.GLenum + GL_UNSIGNED_INT8_VEC2_NV* = 0x8FED.GLenum + GL_POLYGON_OFFSET_FACTOR* = 0x8038.GLenum + GL_BOLD_BIT_NV* = 0x01.GLbitfield + GL_CLAMP_TO_BORDER_ARB* = 0x812D.GLint + GL_INDEX_MODE* = 0x0C30.GLenum + GL_SAMPLER_CUBE_SHADOW_NV* = 0x8DC5.GLenum + GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT* = 0x8C4F.GLenum + GL_MATRIX21_ARB* = 0x88D5.GLenum + GL_UNPACK_ROW_LENGTH_EXT* = 0x0CF2.GLenum + GL_FRAGMENT_NORMAL_EXT* = 0x834A.GLenum + GL_DOT3_ATI* = 0x8966.GLenum + GL_IMPLEMENTATION_COLOR_READ_TYPE_OES* = 0x8B9A.GLenum + GL_IMAGE_BINDING_ACCESS_EXT* = 0x8F3E.GLenum + GL_SYNC_CL_EVENT_ARB* = 0x8240.GLenum + GL_UNSIGNED_INT_24_8* = 0x84FA.GLenum + GL_2PASS_1_EXT* = 0x80A3.GLenum + GL_POST_TEXTURE_FILTER_BIAS_SGIX* = 0x8179.GLenum + GL_TEXTURE_COMPRESSED_IMAGE_SIZE* = 0x86A0.GLenum + GL_LUMINANCE_ALPHA32UI_EXT* = 0x8D75.GLenum + GL_FORCE_BLUE_TO_ONE_NV* = 0x8860.GLenum + GL_FRAMEBUFFER_DEFAULT* = 0x8218.GLenum + GL_VIRTUAL_PAGE_SIZE_Z_ARB* = 0x9197.GLenum + GL_TEXTURE_LIGHT_EXT* = 0x8350.GLenum + GL_MULTISAMPLE_BUFFER_BIT5_QCOM* = 0x20000000.GLbitfield + GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY* = 0x910D.GLenum + GL_SYNC_CONDITION* = 0x9113.GLenum + GL_PERFMON_RESULT_SIZE_AMD* = 0x8BC5.GLenum + GL_PROGRAM_OBJECT_ARB* = 0x8B40.GLenum + GL_MAX_SHININESS_NV* = 0x8504.GLenum + GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB* = 0x880A.GLenum + GL_RENDERBUFFER_COLOR_SAMPLES_NV* = 0x8E10.GLenum + GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS* = 0x8A31.GLenum + GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH* = 0x8E49.GLenum + GL_MODELVIEW29_ARB* = 0x873D.GLenum + GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB* = 0x900B.GLenum + GL_SIGNED_HILO16_NV* = 0x86FA.GLenum + GL_TRANSFORM_HINT_APPLE* = 0x85B1.GLenum + GL_STENCIL_INDEX4* = 0x8D47.GLenum + GL_EXTENSIONS* = 0x1F03.GLenum + GL_RG16F* = 0x822F.GLenum + GL_MAP_UNSYNCHRONIZED_BIT_EXT* = 0x0020.GLbitfield + GL_LUMINANCE16F_ARB* = 0x881E.GLenum + GL_UNSIGNED_INT_IMAGE_BUFFER* = 0x9067.GLenum + GL_COMPRESSED_RGBA_ASTC_8x8_KHR* = 0x93B7.GLenum + GL_AVERAGE_HP* = 0x8160.GLenum + GL_INDEX_MATERIAL_EXT* = 0x81B8.GLenum + GL_COLOR_TABLE* = 0x80D0.GLenum + GL_FOG_COORDINATE_ARRAY_LIST_IBM* = 103076.GLenum + GL_DEBUG_CATEGORY_OTHER_AMD* = 0x9150.GLenum + GL_R1UI_C4UB_V3F_SUN* = 0x85C5.GLenum + GL_SYSTEM_FONT_NAME_NV* = 0x9073.GLenum + GL_STATIC_VERTEX_ARRAY_IBM* = 103061.GLenum + GL_VERTEX_ATTRIB_ARRAY_DIVISOR_NV* = 0x88FE.GLenum + GL_SCALE_BY_ONE_HALF_NV* = 0x8540.GLenum + GL_INTENSITY_FLOAT32_ATI* = 0x8817.GLenum + GL_FRAGMENT_LIGHT6_SGIX* = 0x8412.GLenum + GL_DECR_WRAP_OES* = 0x8508.GLenum + GL_MODELVIEW23_ARB* = 0x8737.GLenum + GL_PROXY_TEXTURE_1D_ARRAY* = 0x8C19.GLenum + GL_REFERENCED_BY_VERTEX_SHADER* = 0x9306.GLenum + GL_MAX_NAME_LENGTH* = 0x92F6.GLenum + GL_AFFINE_2D_NV* = 0x9092.GLenum + GL_SYNC_OBJECT_APPLE* = 0x8A53.GLenum + GL_PLUS_DARKER_NV* = 0x9292.GLenum + GL_TESS_CONTROL_PROGRAM_NV* = 0x891E.GLenum + GL_RGB_SCALE* = 0x8573.GLenum + GL_RGBA16UI_EXT* = 0x8D76.GLenum + GL_COMPATIBLE_SUBROUTINES* = 0x8E4B.GLenum + GL_COLOR_TABLE_WIDTH* = 0x80D9.GLenum + GL_MAX_COMBINED_UNIFORM_BLOCKS* = 0x8A2E.GLenum + GL_BACK_SECONDARY_COLOR_NV* = 0x8C78.GLenum + GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB* = 0x9344.GLenum + GL_SECONDARY_COLOR_NV* = 0x852D.GLenum + GL_RGB16UI_EXT* = 0x8D77.GLenum + GL_SHADER_STORAGE_BUFFER_SIZE* = 0x90D5.GLenum + GL_VERTEX_SUBROUTINE* = 0x92E8.GLenum + GL_MAP_COLOR* = 0x0D10.GLenum + GL_OBJECT_TYPE_ARB* = 0x8B4E.GLenum + GL_LAST_VIDEO_CAPTURE_STATUS_NV* = 0x9027.GLenum + GL_RGB12_EXT* = 0x8053.GLenum + GL_UNSIGNED_INT_IMAGE_3D_EXT* = 0x9064.GLenum + GL_LUMINANCE8_ALPHA8* = 0x8045.GLenum + GL_FLOAT_RGBA_MODE_NV* = 0x888E.GLenum + GL_CURRENT_RASTER_COLOR* = 0x0B04.GLenum + GL_CURRENT_RASTER_POSITION* = 0x0B07.GLenum + GL_UNIFORM_BLOCK_DATA_SIZE* = 0x8A40.GLenum + GL_MALI_PROGRAM_BINARY_ARM* = 0x8F61.GLenum + GL_QUERY_COUNTER_BITS_ARB* = 0x8864.GLenum + GL_VARIANT_ARRAY_EXT* = 0x87E8.GLenum + GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV* = 0x903A.GLenum + GL_DEPTH_COMPONENT24_ARB* = 0x81A6.GLenum + GL_UNSIGNED_INVERT_NV* = 0x8537.GLenum + GL_TEXTURE_IMMUTABLE_LEVELS* = 0x82DF.GLenum + GL_DRAW_BUFFER12_ATI* = 0x8831.GLenum + GL_MAP_FLUSH_EXPLICIT_BIT_EXT* = 0x0010.GLbitfield + GL_INDEX_WRITEMASK* = 0x0C21.GLenum + GL_POLYGON_SMOOTH* = 0x0B41.GLenum + GL_COMPRESSED_SIGNED_R11_EAC_OES* = 0x9271.GLenum + GL_TEXTURE_SWIZZLE_A_EXT* = 0x8E45.GLenum + GL_TEXTURE_COORD_ARRAY_STRIDE* = 0x808A.GLenum + GL_PIXEL_MAP_I_TO_R* = 0x0C72.GLenum + GL_CONVOLUTION_HEIGHT* = 0x8019.GLenum + GL_SIGNALED* = 0x9119.GLenum + GL_UNSIGNED_INT_24_8_OES* = 0x84FA.GLenum + GL_DRAW_BUFFER6_ARB* = 0x882B.GLenum + GL_BUFFER_SIZE_ARB* = 0x8764.GLenum + GL_CLEAR_BUFFER* = 0x82B4.GLenum + GL_LUMINANCE16UI_EXT* = 0x8D7A.GLenum + GL_FRAMEBUFFER_ATTACHMENT_ANGLE* = 0x93A3.GLenum + GL_STENCIL_ATTACHMENT* = 0x8D20.GLenum + GL_ALL_COMPLETED_NV* = 0x84F2.GLenum + GL_MIN* = 0x8007.GLenum + GL_COLOR_ATTACHMENT11* = 0x8CEB.GLenum + GL_PATH_STENCIL_FUNC_NV* = 0x90B7.GLenum + GL_MAX_LABEL_LENGTH* = 0x82E8.GLenum + GL_WEIGHT_ARRAY_TYPE_OES* = 0x86A9.GLenum + GL_ACCUM_BUFFER_BIT* = 0x00000200.GLbitfield + GL_WEIGHT_ARRAY_POINTER_ARB* = 0x86AC.GLenum + GL_WEIGHT_SUM_UNITY_ARB* = 0x86A6.GLenum + GL_COMPRESSED_SRGB_EXT* = 0x8C48.GLenum + GL_ATTRIB_ARRAY_TYPE_NV* = 0x8625.GLenum + GL_RED_INTEGER_EXT* = 0x8D94.GLenum + GL_ALWAYS_SOFT_HINT_PGI* = 0x1A20D.GLenum + GL_COMPRESSED_SRGB8_ETC2_OES* = 0x9275.GLenum + GL_LOW_FLOAT* = 0x8DF0.GLenum + GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS* = 0x8354.GLenum + GL_TEXTURE_LEQUAL_R_SGIX* = 0x819C.GLenum + GL_CONTEXT_COMPATIBILITY_PROFILE_BIT* = 0x00000002.GLbitfield + GL_INCR* = 0x1E02.GLenum + GL_3D* = 0x0601.GLenum + GL_SHADER_KHR* = 0x82E1.GLenum + GL_SRC_COLOR* = 0x0300.GLenum + GL_DRAW_BUFFER7_NV* = 0x882C.GLenum + GL_VERTEX_ARRAY_SIZE* = 0x807A.GLenum + GL_SAMPLER_2D_RECT* = 0x8B63.GLenum + GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG* = 0x8365.GLenum + GL_READ_PIXEL_DATA_RANGE_NV* = 0x8879.GLenum + GL_EDGE_FLAG* = 0x0B43.GLenum + GL_TEXTURE_3D_EXT* = 0x806F.GLenum + GL_DOT_PRODUCT_TEXTURE_1D_NV* = 0x885C.GLenum + GL_COLOR_SUM_CLAMP_NV* = 0x854F.GLenum + GL_RGB10_A2* = 0x8059.GLenum + GL_BOOL_VEC3* = 0x8B58.GLenum + GL_REG_3_ATI* = 0x8924.GLenum + GL_LINEAR_SHARPEN_ALPHA_SGIS* = 0x80AE.GLenum + GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT* = 0x8DA8.GLenum + GL_MAP1_VERTEX_ATTRIB5_4_NV* = 0x8665.GLenum + GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS* = 0x8F39.GLenum + GL_PIXEL_MAP_I_TO_B_SIZE* = 0x0CB4.GLenum + GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT* = 0x00000800.GLbitfield + GL_COLOR_BUFFER_BIT6_QCOM* = 0x00000040.GLbitfield + GL_PROGRAM_TEMPORARIES_ARB* = 0x88A4.GLenum + GL_ELEMENT_ARRAY_BUFFER* = 0x8893.GLenum + GL_ALWAYS_FAST_HINT_PGI* = 0x1A20C.GLenum + GL_INTENSITY_FLOAT16_ATI* = 0x881D.GLenum + GL_ACTIVE_ATTRIBUTE_MAX_LENGTH* = 0x8B8A.GLenum + GL_CON_12_ATI* = 0x894D.GLenum + GL_LINEAR_MIPMAP_NEAREST* = 0x2701.GLint + GL_TEXTURE_COVERAGE_SAMPLES_NV* = 0x9045.GLenum + GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB* = 0x88AB.GLenum + GL_DEPTH_SCALE* = 0x0D1E.GLenum + GL_SOURCE3_ALPHA_NV* = 0x858B.GLenum + GL_ACTIVE_VERTEX_UNITS_ARB* = 0x86A5.GLenum + GL_SWIZZLE_STR_DR_ATI* = 0x8978.GLenum + GL_RGB16I_EXT* = 0x8D89.GLenum + GL_INT_IMAGE_2D_RECT_EXT* = 0x905A.GLenum + GL_GREEN_BIAS* = 0x0D19.GLenum + GL_FRAMEBUFFER_RENDERABLE_LAYERED* = 0x828A.GLenum + GL_COMPRESSED_RGB8_ETC2* = 0x9274.GLenum + GL_COMPRESSED_RGBA_ARB* = 0x84EE.GLenum + GL_MAX_VERTEX_ATOMIC_COUNTERS* = 0x92D2.GLenum + GL_RGBA32I_EXT* = 0x8D82.GLenum + GL_WAIT_FAILED* = 0x911D.GLenum + GL_FOG_COORDINATE_SOURCE_EXT* = 0x8450.GLenum + GL_SAMPLE_MASK_VALUE_NV* = 0x8E52.GLenum + GL_OP_MUL_EXT* = 0x8786.GLenum + GL_FRAGMENT_TEXTURE* = 0x829F.GLenum + GL_GEOMETRY_PROGRAM_NV* = 0x8C26.GLenum + GL_MATRIX20_ARB* = 0x88D4.GLenum + GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT* = 0x845C.GLenum + GL_UNSIGNED_INT_2_10_10_10_REV_EXT* = 0x8368.GLenum + GL_PHONG_HINT_WIN* = 0x80EB.GLenum + GL_EYE_DISTANCE_TO_LINE_SGIS* = 0x81F2.GLenum + GL_SAMPLES_PASSED* = 0x8914.GLenum + GL_MAX_COLOR_ATTACHMENTS_NV* = 0x8CDF.GLenum + GL_WEIGHT_ARRAY_POINTER_OES* = 0x86AC.GLenum + GL_MAX_DEBUG_GROUP_STACK_DEPTH* = 0x826C.GLenum + GL_TEXTURE_2D_STACK_BINDING_MESAX* = 0x875E.GLenum + GL_VARIANT_VALUE_EXT* = 0x87E4.GLenum + GL_TEXTURE_GEN_R* = 0x0C62.GLenum + GL_COMPRESSED_RG11_EAC* = 0x9272.GLenum + GL_IMAGE_ROTATE_ORIGIN_Y_HP* = 0x815B.GLenum + GL_BLEND_ADVANCED_COHERENT_NV* = 0x9285.GLenum + GL_DEBUG_CALLBACK_FUNCTION* = 0x8244.GLenum + GL_PROXY_TEXTURE_4D_SGIS* = 0x8135.GLenum + GL_OCCLUSION_TEST_RESULT_HP* = 0x8166.GLenum + GL_COLOR_ATTACHMENT13_EXT* = 0x8CED.GLenum + GL_LINE_STRIP_ADJACENCY* = 0x000B.GLenum + GL_DEBUG_CATEGORY_APPLICATION_AMD* = 0x914F.GLenum + GL_CIRCULAR_TANGENT_ARC_TO_NV* = 0xFC.GLenum + GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB* = 0x88B3.GLenum + GL_VERTEX_ATTRIB_ARRAY_STRIDE* = 0x8624.GLenum + GL_COMPRESSED_SRGB_ALPHA_EXT* = 0x8C49.GLenum + GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY* = 0x900F.GLenum + GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY* = 0x906C.GLenum + GL_LIGHT_MODEL_COLOR_CONTROL* = 0x81F8.GLenum + GL_INT_VEC2_ARB* = 0x8B53.GLenum + GL_PARALLEL_ARRAYS_INTEL* = 0x83F4.GLenum + GL_COLOR_ATTACHMENT11_EXT* = 0x8CEB.GLenum + GL_SAMPLE_ALPHA_TO_ONE_SGIS* = 0x809F.GLenum + GL_FUNC_ADD_OES* = 0x8006.GLenum + GL_COMBINER_MAPPING_NV* = 0x8543.GLenum + GL_INT_IMAGE_BUFFER* = 0x905C.GLenum + GL_TEXTURE_SWIZZLE_A* = 0x8E45.GLenum + GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB* = 0x8DA7.GLenum + GL_EXPAND_NEGATE_NV* = 0x8539.GLenum + GL_COVERAGE_EDGE_FRAGMENTS_NV* = 0x8ED6.GLenum + GL_PATH_OBJECT_BOUNDING_BOX_NV* = 0x908A.GLenum + GL_MAX_RECTANGLE_TEXTURE_SIZE* = 0x84F8.GLenum + GL_FONT_ASCENDER_BIT_NV* = 0x00200000.GLbitfield + GL_INDEX_SHIFT* = 0x0D12.GLenum + GL_LUMINANCE6_ALPHA2* = 0x8044.GLenum + GL_FLOAT_CLEAR_COLOR_VALUE_NV* = 0x888D.GLenum + GL_V2F* = 0x2A20.GLenum + GL_DRAW_BUFFER12_NV* = 0x8831.GLenum + GL_RIGHT* = 0x0407.GLenum + GL_CON_28_ATI* = 0x895D.GLenum + GL_SAMPLER_CUBE_ARB* = 0x8B60.GLenum + GL_OUTPUT_TEXTURE_COORD27_EXT* = 0x87B8.GLenum + GL_MAX_DEPTH_TEXTURE_SAMPLES* = 0x910F.GLenum + GL_MODULATE* = 0x2100.GLenum + GL_NUM_FILL_STREAMS_NV* = 0x8E29.GLenum + GL_DT_SCALE_NV* = 0x8711.GLenum + GL_ONE_MINUS_SRC_COLOR* = 0x0301.GLenum + GL_OPERAND2_ALPHA* = 0x859A.GLenum + GL_MATRIX15_ARB* = 0x88CF.GLenum + GL_MULTISAMPLE* = 0x809D.GLenum + GL_DEPTH32F_STENCIL8* = 0x8CAD.GLenum + GL_COMPRESSED_RGBA_ASTC_4x4_KHR* = 0x93B0.GLenum + GL_DUAL_ALPHA16_SGIS* = 0x8113.GLenum + GL_COMPRESSED_RGB_FXT1_3DFX* = 0x86B0.GLenum + GL_PROXY_TEXTURE_2D_ARRAY* = 0x8C1B.GLenum + GL_UNIFORM_NAME_LENGTH* = 0x8A39.GLenum + GL_COMPILE_AND_EXECUTE* = 0x1301.GLenum + GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG* = 0x9138.GLenum + GL_PIXEL_CUBIC_WEIGHT_EXT* = 0x8333.GLenum + GL_GREEN_MIN_CLAMP_INGR* = 0x8561.GLenum + GL_MAX_TEXTURE_LOD_BIAS* = 0x84FD.GLenum + GL_NORMAL_MAP_NV* = 0x8511.GLenum + GL_PIXEL_UNPACK_BUFFER_BINDING_ARB* = 0x88EF.GLenum + GL_LUMINANCE_ALPHA32F_ARB* = 0x8819.GLenum + GL_LUMINANCE_FLOAT16_APPLE* = 0x881E.GLenum + GL_FACTOR_MIN_AMD* = 0x901C.GLenum + GL_BUFFER_GPU_ADDRESS_NV* = 0x8F1D.GLenum + GL_DEBUG_TYPE_PERFORMANCE_ARB* = 0x8250.GLenum + GL_TEXTURE_RESIDENT* = 0x8067.GLenum + GL_TESS_CONTROL_SHADER_BIT* = 0x00000008.GLbitfield + GL_VERTEX_SHADER* = 0x8B31.GLenum + GL_COLOR_ATTACHMENT15_EXT* = 0x8CEF.GLenum + GL_DRAW_BUFFER2_NV* = 0x8827.GLenum + GL_UNSIGNED_INT* = 0x1405.GLenum + GL_TEXTURE_SHARED_SIZE_EXT* = 0x8C3F.GLenum + GL_LIGHT5* = 0x4005.GLenum + GL_VERTEX_ARRAY_SIZE_EXT* = 0x807A.GLenum + GL_YCRCB_SGIX* = 0x8318.GLenum + GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER* = 0x92C9.GLenum + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES* = 0x8CD1.GLenum + GL_QUADRATIC_CURVE_TO_NV* = 0x0A.GLenum + GL_POINTS* = 0x0000.GLenum + GL_OPERAND1_RGB* = 0x8591.GLenum + GL_POINT_DISTANCE_ATTENUATION_ARB* = 0x8129.GLenum + GL_QUERY_BUFFER_BARRIER_BIT* = 0x00008000.GLbitfield + GL_QUAD_LUMINANCE4_SGIS* = 0x8120.GLenum + GL_GENERATE_MIPMAP_SGIS* = 0x8191.GLenum + GL_FRAMEBUFFER_UNSUPPORTED_EXT* = 0x8CDD.GLenum + GL_PALETTE4_RGB5_A1_OES* = 0x8B94.GLenum + GL_TEXTURE_CROP_RECT_OES* = 0x8B9D.GLenum + GL_COMPUTE_SHADER_BIT* = 0x00000020.GLbitfield + GL_OUTPUT_TEXTURE_COORD2_EXT* = 0x879F.GLenum + GL_PALETTE4_RGBA4_OES* = 0x8B93.GLenum + GL_TEXTURE_CLIPMAP_CENTER_SGIX* = 0x8171.GLenum + GL_BLUE_BITS* = 0x0D54.GLenum + GL_RELATIVE_LARGE_CCW_ARC_TO_NV* = 0x17.GLenum + GL_UNSIGNED_SHORT_5_6_5_EXT* = 0x8363.GLenum + GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS* = 0x8DE1.GLenum + GL_UNCORRELATED_NV* = 0x9282.GLenum + GL_TESS_EVALUATION_SUBROUTINE* = 0x92EA.GLenum + GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB* = 0x8E5E.GLenum + GL_CON_11_ATI* = 0x894C.GLenum + GL_ACTIVE_TEXTURE* = 0x84E0.GLenum + GL_ASYNC_TEX_IMAGE_SGIX* = 0x835C.GLenum + GL_COLOR_CLEAR_VALUE* = 0x0C22.GLenum + GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY* = 0x910C.GLenum + GL_TESS_CONTROL_TEXTURE* = 0x829C.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES* = 0x851A.GLenum + GL_HISTOGRAM_BLUE_SIZE_EXT* = 0x802A.GLenum + GL_PATCH_DEFAULT_OUTER_LEVEL* = 0x8E74.GLenum + GL_PROGRAM_MATRIX_STACK_DEPTH_EXT* = 0x8E2F.GLenum + GL_RENDERBUFFER_BINDING_ANGLE* = 0x8CA7.GLenum + GL_CONSTANT_ATTENUATION* = 0x1207.GLenum + GL_SHADER_CONSISTENT_NV* = 0x86DD.GLenum + GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS* = 0x92D4.GLenum + GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD* = 0x9160.GLenum + GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS* = 0x809C.GLenum + GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT* = 0x9061.GLenum + GL_COUNT_DOWN_NV* = 0x9089.GLenum + GL_MATRIX12_ARB* = 0x88CC.GLenum + GL_MAX_VERTEX_SHADER_INVARIANTS_EXT* = 0x87C7.GLenum + GL_REPLICATE_BORDER_HP* = 0x8153.GLenum + GL_MODELVIEW9_ARB* = 0x8729.GLenum + GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT* = 0x8D6A.GLenum + GL_PROGRAM_PARAMETERS_ARB* = 0x88A8.GLenum + GL_LIST_BIT* = 0x00020000.GLbitfield + GL_MAX_GEOMETRY_ATOMIC_COUNTERS* = 0x92D5.GLenum + GL_CONSTANT_COLOR1_NV* = 0x852B.GLenum + GL_AVERAGE_EXT* = 0x8335.GLenum + GL_SINGLE_COLOR_EXT* = 0x81F9.GLenum + GL_VERTEX_ARRAY* = 0x8074.GLenum + GL_COLOR_INDEX1_EXT* = 0x80E2.GLenum + GL_COMPUTE_PROGRAM_NV* = 0x90FB.GLenum + GL_LINES_ADJACENCY* = 0x000A.GLenum + GL_OP_ROUND_EXT* = 0x8790.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE* = 0x934C.GLenum + GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV* = 0x90D1.GLenum + GL_REG_11_ATI* = 0x892C.GLenum + GL_SAMPLES_EXT* = 0x80A9.GLenum + GL_FUNC_REVERSE_SUBTRACT* = 0x800B.GLenum + GL_POINT_SPRITE_COORD_ORIGIN* = 0x8CA0.GLenum + GL_REG_27_ATI* = 0x893C.GLenum + GL_TEXTURE_VIEW_MIN_LEVEL* = 0x82DB.GLenum + GL_NICEST* = 0x1102.GLenum + GL_CLIP_PLANE4_IMG* = 0x3004.GLenum + GL_ARRAY_BUFFER_BINDING* = 0x8894.GLenum + GL_422_AVERAGE_EXT* = 0x80CE.GLenum + GL_RENDERER* = 0x1F01.GLenum + GL_OVERLAY_NV* = 0x9296.GLenum + GL_TEXTURE_SAMPLES_IMG* = 0x9136.GLenum + GL_DEBUG_SOURCE_SHADER_COMPILER_KHR* = 0x8248.GLenum + GL_EYE_DISTANCE_TO_POINT_SGIS* = 0x81F0.GLenum + GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV* = 0x8DA5.GLenum + GL_FILTER4_SGIS* = 0x8146.GLenum + GL_LIGHT_MODEL_LOCAL_VIEWER* = 0x0B51.GLenum + GL_TRIANGLE_MESH_SUN* = 0x8615.GLenum + GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB* = 0x900D.GLenum + GL_DEPTH_COMPONENTS* = 0x8284.GLenum + GL_NUM_GENERAL_COMBINERS_NV* = 0x854E.GLenum + GL_CLIENT_ACTIVE_TEXTURE_ARB* = 0x84E1.GLenum + GL_FRAGMENT_DEPTH* = 0x8452.GLenum + GL_SEPARATE_ATTRIBS* = 0x8C8D.GLenum + GL_HALF_FLOAT_OES* = 0x8D61.GLenum + GL_PROXY_TEXTURE_2D* = 0x8064.GLenum + GL_VARIANT_ARRAY_TYPE_EXT* = 0x87E7.GLenum + GL_DRAW_BUFFER11_ATI* = 0x8830.GLenum + GL_MATRIX_INDEX_ARRAY_POINTER_OES* = 0x8849.GLenum + GL_CURRENT_INDEX* = 0x0B01.GLenum + GL_UNSIGNED_INT_24_8_MESA* = 0x8751.GLenum + GL_PROGRAM_SEPARABLE* = 0x8258.GLenum + GL_TEXTURE8_ARB* = 0x84C8.GLenum + GL_OPERAND0_ALPHA_EXT* = 0x8598.GLenum + GL_PER_STAGE_CONSTANTS_NV* = 0x8535.GLenum + GL_LINE_LOOP* = 0x0002.GLenum + GL_DRAW_PIXEL_TOKEN* = 0x0705.GLenum + GL_DRAW_BUFFER3* = 0x8828.GLenum + GL_GEOMETRY_DEFORMATION_SGIX* = 0x8194.GLenum + GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT* = 0x851C.GLenum + GL_GLYPH_VERTICAL_BEARING_X_BIT_NV* = 0x20.GLbitfield + GL_TEXTURE30* = 0x84DE.GLenum + GL_4PASS_1_EXT* = 0x80A5.GLenum + GL_RGB16F_EXT* = 0x881B.GLenum + GL_2PASS_0_SGIS* = 0x80A2.GLenum + GL_CON_27_ATI* = 0x895C.GLenum + GL_SAMPLE_ALPHA_TO_ONE* = 0x809F.GLenum + GL_POLYGON_SMOOTH_HINT* = 0x0C53.GLenum + GL_COLOR_ATTACHMENT_EXT* = 0x90F0.GLenum + GL_PATCH_DEFAULT_INNER_LEVEL* = 0x8E73.GLenum + GL_TEXTURE_MAX_CLAMP_T_SGIX* = 0x836A.GLenum + GL_WEIGHT_ARRAY_BUFFER_BINDING_OES* = 0x889E.GLenum + GL_TEXTURE1* = 0x84C1.GLenum + GL_LINES* = 0x0001.GLenum + GL_PIXEL_TILE_GRID_DEPTH_SGIX* = 0x8144.GLenum + GL_TEXTURE2* = 0x84C2.GLenum + GL_IMAGE_CUBE_MAP_ARRAY_EXT* = 0x9054.GLenum + GL_DRAW_BUFFER4* = 0x8829.GLenum + GL_DRAW_BUFFER_EXT* = 0x0C01.GLenum + GL_STENCIL_INDEX1* = 0x8D46.GLenum + GL_DEPTH_COMPONENT32F_NV* = 0x8DAB.GLenum + GL_VERTEX_ATTRIB_ARRAY_POINTER* = 0x8645.GLenum + GL_DOUBLE_MAT4x2* = 0x8F4D.GLenum + GL_MOVE_TO_NV* = 0x02.GLenum + GL_OP_RECIP_SQRT_EXT* = 0x8795.GLenum + GL_SAMPLER_1D_ARRAY* = 0x8DC0.GLenum + GL_MIN_FRAGMENT_INTERPOLATION_OFFSET* = 0x8E5B.GLenum + GL_TEXTURE_DEPTH_EXT* = 0x8071.GLenum + GL_STENCIL_INDEX8* = 0x8D48.GLenum + GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB* = 0x880C.GLenum + GL_INTERNALFORMAT_DEPTH_SIZE* = 0x8275.GLenum + GL_STATE_RESTORE* = 0x8BDC.GLenum + GL_SMALL_CW_ARC_TO_NV* = 0x14.GLenum + GL_LUMINANCE16* = 0x8042.GLenum + GL_VERTEX_ATTRIB_ARRAY1_NV* = 0x8651.GLenum + GL_TEXTURE_MAX_CLAMP_R_SGIX* = 0x836B.GLenum + GL_LUMINANCE_FLOAT16_ATI* = 0x881E.GLenum + GL_MAX_TEXTURE_UNITS* = 0x84E2.GLenum + GL_DRAW_BUFFER4_ARB* = 0x8829.GLenum + GL_DRAW_BUFFER12* = 0x8831.GLenum + GL_R8UI* = 0x8232.GLenum + GL_STENCIL_REF* = 0x0B97.GLenum + GL_VARIANT_EXT* = 0x87C1.GLenum + GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE* = 0x8A09.GLenum + GL_QUERY_OBJECT_AMD* = 0x9153.GLenum + GL_PLUS_NV* = 0x9291.GLenum + GL_UNPACK_SWAP_BYTES* = 0x0CF0.GLenum + GL_MAX_UNIFORM_LOCATIONS* = 0x826E.GLenum + GL_GUILTY_CONTEXT_RESET_EXT* = 0x8253.GLenum + GL_DOT3_RGBA_IMG* = 0x86AF.GLenum + GL_X_EXT* = 0x87D5.GLenum + GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB* = 0x900F.GLenum + GL_TEXTURE_COMPARE_FAIL_VALUE_ARB* = 0x80BF.GLenum + GL_ETC1_RGB8_OES* = 0x8D64.GLenum + GL_LUMINANCE_ALPHA_INTEGER_EXT* = 0x8D9D.GLenum + GL_MINMAX_SINK* = 0x8030.GLenum + GL_RG32F* = 0x8230.GLenum + GL_PROXY_TEXTURE_2D_MULTISAMPLE* = 0x9101.GLenum + GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV* = 0x86D9.GLenum + GL_R16* = 0x822A.GLenum + GL_BOUNDING_BOX_NV* = 0x908D.GLenum + GL_INVALID_ENUM* = 0x0500.GLenum + GL_MOVE_TO_RESETS_NV* = 0x90B5.GLenum + GL_SYNC_GPU_COMMANDS_COMPLETE_APPLE* = 0x9117.GLenum + GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB* = 0x84F8.GLenum + GL_UNSIGNED_INT_10F_11F_11F_REV_EXT* = 0x8C3B.GLenum + GL_VERTEX_PRECLIP_HINT_SGIX* = 0x83EF.GLenum + GL_CLIENT_VERTEX_ARRAY_BIT* = 0x00000002.GLbitfield + GL_MAT_COLOR_INDEXES_BIT_PGI* = 0x01000000.GLbitfield + GL_PERFORMANCE_MONITOR_AMD* = 0x9152.GLenum + GL_QUAD_STRIP* = 0x0008.GLenum + GL_MAX_TEXTURE_COORDS_NV* = 0x8871.GLenum + GL_TESS_EVALUATION_SUBROUTINE_UNIFORM* = 0x92F0.GLenum + GL_DRAW_BUFFER1_EXT* = 0x8826.GLenum + GL_TEXTURE18* = 0x84D2.GLenum + GL_COLOR_ATTACHMENT5_NV* = 0x8CE5.GLenum + GL_MAX_COMPUTE_WORK_GROUP_SIZE* = 0x91BF.GLenum + GL_T2F_C4UB_V3F* = 0x2A29.GLenum + GL_MAP1_GRID_DOMAIN* = 0x0DD0.GLenum + GL_DEBUG_TYPE_PUSH_GROUP_KHR* = 0x8269.GLenum + GL_STATIC_READ* = 0x88E5.GLenum + GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB* = 0x880E.GLenum + GL_DOUBLE_EXT* = 0x140A.GLenum + GL_MAX_FRAGMENT_UNIFORM_VECTORS* = 0x8DFD.GLenum + GL_R32F_EXT* = 0x822E.GLenum + GL_MAX_RENDERBUFFER_SIZE_EXT* = 0x84E8.GLenum + GL_COMPRESSED_TEXTURE_FORMATS* = 0x86A3.GLenum + GL_MAX_EXT* = 0x8008.GLenum + GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB* = 0x8622.GLenum + GL_INTERPOLATE* = 0x8575.GLenum + GL_QUERY_RESULT_NO_WAIT_AMD* = 0x9194.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES* = 0x8516.GLenum + GL_LUMINANCE16_ALPHA16_SNORM* = 0x901A.GLenum + GL_SRC_ALPHA_SATURATE* = 0x0308.GLenum + GL_DRAW_INDIRECT_BUFFER_BINDING* = 0x8F43.GLenum + GL_T2F_IUI_N3F_V3F_EXT* = 0x81B4.GLenum + GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB* = 0x8B49.GLenum + GL_MAX_ASYNC_READ_PIXELS_SGIX* = 0x8361.GLenum + GL_VERTEX_ARRAY_RANGE_APPLE* = 0x851D.GLenum + GL_SAMPLER_2D_SHADOW_ARB* = 0x8B62.GLenum + GL_ETC1_SRGB8_NV* = 0x88EE.GLenum + GL_COLORBURN_NV* = 0x929A.GLenum + GL_SAMPLER_2D_ARRAY_SHADOW_EXT* = 0x8DC4.GLenum + GL_ALL_BARRIER_BITS* = 0xFFFFFFFF.GLbitfield + GL_TRIANGLE_STRIP_ADJACENCY_EXT* = 0x000D.GLenum + GL_MAX_TEXTURE_BUFFER_SIZE* = 0x8C2B.GLenum + GL_ALIASED_POINT_SIZE_RANGE* = 0x846D.GLenum + GL_STENCIL_BACK_VALUE_MASK* = 0x8CA4.GLenum + GL_CMYK_EXT* = 0x800C.GLenum + GL_OPERAND1_ALPHA_EXT* = 0x8599.GLenum + GL_TEXTURE_SHADOW* = 0x82A1.GLenum + GL_LINEAR_CLIPMAP_LINEAR_SGIX* = 0x8170.GLenum + GL_MIPMAP* = 0x8293.GLenum + GL_LINE_SMOOTH_HINT* = 0x0C52.GLenum + GL_DEPTH_STENCIL_TEXTURE_MODE* = 0x90EA.GLenum + GL_BUFFER_ACCESS_OES* = 0x88BB.GLenum + GL_PROXY_TEXTURE_1D_ARRAY_EXT* = 0x8C19.GLenum + GL_OBJECT_LINEAR* = 0x2401.GLenum + GL_MAP1_TEXTURE_COORD_3* = 0x0D95.GLenum + GL_TEXTURE_RENDERBUFFER_NV* = 0x8E55.GLenum + GL_FRAMEBUFFER_RENDERABLE* = 0x8289.GLenum + GL_DOT3_RGB_EXT* = 0x8740.GLenum + GL_QUAD_LUMINANCE8_SGIS* = 0x8121.GLenum + GL_UNIFORM_BLOCK_INDEX* = 0x8A3A.GLenum + GL_DS_SCALE_NV* = 0x8710.GLenum + GL_TYPE* = 0x92FA.GLenum + GL_MATRIX_EXT* = 0x87C0.GLenum + GL_VERTEX_STREAM4_ATI* = 0x8770.GLenum + GL_TOP_LEVEL_ARRAY_STRIDE* = 0x930D.GLenum + GL_INT_SAMPLER_2D_EXT* = 0x8DCA.GLenum + GL_PATH_FORMAT_PS_NV* = 0x9071.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR* = 0x93D2.GLenum + GL_MAX_TEXTURE_COORDS* = 0x8871.GLenum + GL_MAX_FRAGMENT_INTERPOLATION_OFFSET* = 0x8E5C.GLenum + GL_REG_17_ATI* = 0x8932.GLenum + GL_WAIT_FAILED_APPLE* = 0x911D.GLenum + GL_TEXTURE_BINDING_3D* = 0x806A.GLenum + GL_TEXTURE_VIEW* = 0x82B5.GLenum + GL_DOT3_RGBA_ARB* = 0x86AF.GLenum + GL_MAX_VARYING_FLOATS_ARB* = 0x8B4B.GLenum + GL_UNIFORM_IS_ROW_MAJOR* = 0x8A3E.GLenum + GL_FRAGMENT_SHADER_BIT* = 0x00000002.GLbitfield + GL_MATRIX_INDEX_ARRAY_ARB* = 0x8844.GLenum + GL_PIXEL_PACK_BUFFER_BINDING_EXT* = 0x88ED.GLenum + GL_MATRIX_PALETTE_OES* = 0x8840.GLenum + GL_INTENSITY_SNORM* = 0x9013.GLenum + GL_COLOR_BUFFER_BIT0_QCOM* = 0x00000001.GLbitfield + GL_BITMAP* = 0x1A00.GLenum + GL_CURRENT_MATRIX_NV* = 0x8641.GLenum + GL_QUERY_BUFFER_AMD* = 0x9192.GLenum + GL_EDGE_FLAG_ARRAY_BUFFER_BINDING* = 0x889B.GLenum + GL_4PASS_3_EXT* = 0x80A7.GLenum + GL_TEXTURE_4DSIZE_SGIS* = 0x8136.GLenum + GL_PATH_COORD_COUNT_NV* = 0x909E.GLenum + GL_SLUMINANCE* = 0x8C46.GLenum + GL_POINT_SMOOTH_HINT* = 0x0C51.GLenum + GL_ADJACENT_PAIRS_NV* = 0x90AE.GLenum + GL_BUFFER_BINDING* = 0x9302.GLenum + GL_ARRAY_OBJECT_BUFFER_ATI* = 0x8766.GLenum + GL_PATH_INITIAL_DASH_CAP_NV* = 0x907C.GLenum + GL_RGBA4* = 0x8056.GLenum + GL_PACK_LSB_FIRST* = 0x0D01.GLenum + GL_IMAGE_BINDING_NAME_EXT* = 0x8F3A.GLenum + GL_UNSIGNED_INT_SAMPLER_2D_EXT* = 0x8DD2.GLenum + GL_RGBA12_EXT* = 0x805A.GLenum + GL_COMBINER0_NV* = 0x8550.GLenum + GL_COLOR_BUFFER_BIT4_QCOM* = 0x00000010.GLbitfield + GL_TIME_ELAPSED* = 0x88BF.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_START* = 0x8C84.GLenum + GL_COMPRESSED_RGBA_ASTC_5x5_KHR* = 0x93B2.GLenum + GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD* = 0x9199.GLenum + GL_RENDERBUFFER_HEIGHT_EXT* = 0x8D43.GLenum + GL_QUARTER_BIT_ATI* = 0x00000010.GLbitfield + GL_TEXTURE_COMPRESSION_HINT_ARB* = 0x84EF.GLenum + GL_DRAW_BUFFER13* = 0x8832.GLenum + GL_CURRENT_MATRIX_STACK_DEPTH_ARB* = 0x8640.GLenum + GL_DEPENDENT_HILO_TEXTURE_2D_NV* = 0x8858.GLenum + GL_DST_NV* = 0x9287.GLenum + GL_DEBUG_OBJECT_MESA* = 0x8759.GLenum + GL_NUM_INSTRUCTIONS_TOTAL_ATI* = 0x8972.GLenum + GL_FLAT* = 0x1D00.GLenum + GL_EVAL_VERTEX_ATTRIB8_NV* = 0x86CE.GLenum + GL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA* = 0x8BB6.GLenum + GL_TEXTURE_COORD_ARRAY_EXT* = 0x8078.GLenum + GL_LOCATION_INDEX* = 0x930F.GLenum + GL_SLIM10U_SGIX* = 0x831E.GLenum + GL_PHONG_WIN* = 0x80EA.GLenum + GL_EVAL_VERTEX_ATTRIB1_NV* = 0x86C7.GLenum + GL_SMOOTH_LINE_WIDTH_RANGE* = 0x0B22.GLenum + GL_SAMPLER_RENDERBUFFER_NV* = 0x8E56.GLenum + GL_UNPACK_LSB_FIRST* = 0x0CF1.GLenum + GL_SELECTION_BUFFER_POINTER* = 0x0DF3.GLenum + GL_PIXEL_SUBSAMPLE_4444_SGIX* = 0x85A2.GLenum + GL_COMPRESSED_R11_EAC* = 0x9270.GLenum + GL_MAX_CLIP_PLANES* = 0x0D32.GLenum + GL_POST_CONVOLUTION_GREEN_BIAS* = 0x8021.GLenum + GL_COLOR_EXT* = 0x1800.GLenum + GL_VENDOR* = 0x1F00.GLenum + GL_MAP1_VERTEX_ATTRIB8_4_NV* = 0x8668.GLenum + GL_TEXTURE_ALPHA_TYPE* = 0x8C13.GLenum + GL_CURRENT_VERTEX_ATTRIB_ARB* = 0x8626.GLenum + GL_COLOR_BUFFER_BIT2_QCOM* = 0x00000004.GLbitfield + GL_VERTEX_ATTRIB_ARRAY15_NV* = 0x865F.GLenum + GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV* = 0x8850.GLenum + GL_DRAW_BUFFER5_ARB* = 0x882A.GLenum + GL_SAMPLES_PASSED_ARB* = 0x8914.GLenum + GL_PRIMITIVE_RESTART_NV* = 0x8558.GLenum + GL_FRAGMENT_LIGHT3_SGIX* = 0x840F.GLenum + GL_COLOR_INDEX16_EXT* = 0x80E7.GLenum + GL_RGBA8_OES* = 0x8058.GLenum + GL_PACK_CMYK_HINT_EXT* = 0x800E.GLenum + GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE* = 0x8214.GLenum + GL_MODELVIEW0_EXT* = 0x1700.GLenum + GL_RETAINED_APPLE* = 0x8A1B.GLenum + GL_DRAW_PIXELS_APPLE* = 0x8A0A.GLenum + GL_POINT_BIT* = 0x00000002.GLbitfield + GL_PIXEL_MAP_B_TO_B_SIZE* = 0x0CB8.GLenum + GL_RELATIVE_SMALL_CCW_ARC_TO_NV* = 0x13.GLenum + GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB* = 0x8624.GLenum + GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV* = 0x885D.GLenum + GL_CON_2_ATI* = 0x8943.GLenum + GL_SAMPLER_2D_ARRAY* = 0x8DC1.GLenum + GL_LINE_STIPPLE_PATTERN* = 0x0B25.GLenum + GL_IMPLEMENTATION_COLOR_READ_FORMAT* = 0x8B9B.GLenum + GL_TRANSPOSE_AFFINE_2D_NV* = 0x9096.GLenum + GL_COLOR_ATTACHMENT7* = 0x8CE7.GLenum + GL_COLOR_ATTACHMENT14* = 0x8CEE.GLenum + GL_SHADER* = 0x82E1.GLenum + GL_SKIP_MISSING_GLYPH_NV* = 0x90A9.GLenum + GL_VERTEX_ARRAY_TYPE* = 0x807B.GLenum + GL_OP_POWER_EXT* = 0x8793.GLenum + GL_MAX_BINDABLE_UNIFORM_SIZE_EXT* = 0x8DED.GLenum + GL_SRGB8* = 0x8C41.GLenum + GL_INTERNALFORMAT_ALPHA_SIZE* = 0x8274.GLenum + GL_IMAGE_2D_MULTISAMPLE* = 0x9055.GLenum + GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV* = 0x9039.GLenum + GL_NEVER* = 0x0200.GLenum + GL_MAP2_TEXTURE_COORD_2* = 0x0DB4.GLenum + GL_PROGRAM_RESULT_COMPONENTS_NV* = 0x8907.GLenum + GL_SHADER_STORAGE_BARRIER_BIT* = 0x00002000.GLbitfield + GL_SLIM8U_SGIX* = 0x831D.GLenum + GL_DRAW_BUFFER7_ATI* = 0x882C.GLenum + GL_CLAMP_TO_EDGE* = 0x812F.GLint + GL_LUMINANCE32I_EXT* = 0x8D86.GLenum + GL_NORMAL_ARRAY_POINTER* = 0x808F.GLenum + GL_ALPHA_TEST_REF_QCOM* = 0x0BC2.GLenum + GL_MATRIX7_NV* = 0x8637.GLenum + GL_REFERENCED_BY_FRAGMENT_SHADER* = 0x930A.GLenum + GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG* = 0x8C02.GLenum + GL_DEBUG_TYPE_MARKER* = 0x8268.GLenum + GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR* = 0x8242.GLenum + GL_CON_26_ATI* = 0x895B.GLenum + GL_COMBINER7_NV* = 0x8557.GLenum + GL_MAP2_TANGENT_EXT* = 0x8445.GLenum + GL_COMPRESSED_RGBA_ASTC_10x6_KHR* = 0x93B9.GLenum + GL_RG8* = 0x822B.GLenum + GL_INT_SAMPLER_1D_ARRAY_EXT* = 0x8DCE.GLenum + GL_POINT_SPRITE_R_MODE_NV* = 0x8863.GLenum + GL_ATOMIC_COUNTER_BUFFER_BINDING* = 0x92C1.GLenum + GL_INTENSITY16F_ARB* = 0x881D.GLenum + GL_DEFORMATIONS_MASK_SGIX* = 0x8196.GLenum + GL_PATH_TERMINAL_END_CAP_NV* = 0x9078.GLenum + GL_VERTEX_BINDING_DIVISOR* = 0x82D6.GLenum + GL_WIDE_LINE_HINT_PGI* = 0x1A222.GLenum + GL_LIGHTING* = 0x0B50.GLenum + GL_CURRENT_BIT* = 0x00000001.GLbitfield + GL_LOSE_CONTEXT_ON_RESET_ARB* = 0x8252.GLenum + GL_COLOR_ATTACHMENT15* = 0x8CEF.GLenum + GL_REGISTER_COMBINERS_NV* = 0x8522.GLenum + GL_UNSIGNED_INT64_VEC2_NV* = 0x8FF5.GLenum + GL_TEXTURE_CLIPMAP_DEPTH_SGIX* = 0x8176.GLenum + GL_HISTOGRAM_WIDTH* = 0x8026.GLenum + GL_RENDERBUFFER_ALPHA_SIZE* = 0x8D53.GLenum + GL_POST_CONVOLUTION_BLUE_BIAS_EXT* = 0x8022.GLenum + GL_SCALED_RESOLVE_FASTEST_EXT* = 0x90BA.GLenum + GL_DRAW_BUFFER15* = 0x8834.GLenum + GL_LUMINANCE4_ALPHA4* = 0x8043.GLenum + GL_SWIZZLE_STRQ_DQ_ATI* = 0x897B.GLenum + GL_OP_MADD_EXT* = 0x8788.GLenum + GL_MAX_ATTRIB_STACK_DEPTH* = 0x0D35.GLenum + GL_DEBUG_GROUP_STACK_DEPTH_KHR* = 0x826D.GLenum + GL_ACTIVE_VARYINGS_NV* = 0x8C81.GLenum + GL_DEBUG_SEVERITY_HIGH* = 0x9146.GLenum + GL_SRGB8_EXT* = 0x8C41.GLenum + GL_STENCIL_WRITEMASK* = 0x0B98.GLenum + GL_REG_14_ATI* = 0x892F.GLenum + GL_PROGRAM_BINARY_ANGLE* = 0x93A6.GLenum + GL_RENDERBUFFER_DEPTH_SIZE_EXT* = 0x8D54.GLenum + GL_ALPHA_BIAS* = 0x0D1D.GLenum + GL_STATIC_ATI* = 0x8760.GLenum + GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES* = 0x8B9E.GLenum + GL_SOFTLIGHT_NV* = 0x929C.GLenum + GL_INDEX_ARRAY_COUNT_EXT* = 0x8087.GLenum + GL_RENDERBUFFER_BLUE_SIZE_EXT* = 0x8D52.GLenum + GL_SHARED_TEXTURE_PALETTE_EXT* = 0x81FB.GLenum + GL_VERTEX_SHADER_OPTIMIZED_EXT* = 0x87D4.GLenum + GL_MAX_SAMPLE_MASK_WORDS_NV* = 0x8E59.GLenum + GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB* = 0x8841.GLenum + GL_MATRIX30_ARB* = 0x88DE.GLenum + GL_NORMAL_ARRAY_POINTER_EXT* = 0x808F.GLenum + GL_PIXEL_MAP_A_TO_A* = 0x0C79.GLenum + GL_MATRIX18_ARB* = 0x88D2.GLenum + GL_UNPACK_SKIP_ROWS_EXT* = 0x0CF3.GLenum + GL_INVARIANT_DATATYPE_EXT* = 0x87EB.GLenum + GL_INT_IMAGE_1D_EXT* = 0x9057.GLenum + GL_OUTPUT_TEXTURE_COORD24_EXT* = 0x87B5.GLenum + GL_MAP_WRITE_BIT_EXT* = 0x0002.GLbitfield + GL_MODELVIEW28_ARB* = 0x873C.GLenum + GL_MAX_VARYING_COMPONENTS_EXT* = 0x8B4B.GLenum + GL_OUTPUT_TEXTURE_COORD4_EXT* = 0x87A1.GLenum + GL_UNSIGNED_INT_VEC2_EXT* = 0x8DC6.GLenum + GL_READ_ONLY* = 0x88B8.GLenum + GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM* = 103087.GLenum + GL_UNSIGNED_INT64_NV* = 0x140F.GLenum + GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN* = 0x85C2.GLenum + GL_DEPTH_BUFFER_BIT0_QCOM* = 0x00000100.GLbitfield + GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE* = 0x8A06.GLenum + GL_POST_CONVOLUTION_ALPHA_SCALE* = 0x801F.GLenum + GL_TEXTURE_COLOR_SAMPLES_NV* = 0x9046.GLenum + GL_DEBUG_SEVERITY_HIGH_ARB* = 0x9146.GLenum + GL_MAP_WRITE_BIT* = 0x0002.GLbitfield + GL_SRC1_RGB* = 0x8581.GLenum + GL_LIGHT0* = 0x4000.GLenum + GL_READ_PIXELS_FORMAT* = 0x828D.GLenum + GL_COMBINE_RGB_EXT* = 0x8571.GLenum + GL_MATRIX2_NV* = 0x8632.GLenum + GL_INT16_VEC4_NV* = 0x8FE7.GLenum + GL_INT_SAMPLER_CUBE* = 0x8DCC.GLenum + GL_LUMINANCE_ALPHA8I_EXT* = 0x8D93.GLenum + GL_TRIANGLE_STRIP_ADJACENCY* = 0x000D.GLenum + GL_MAX_TEXTURE_BUFFER_SIZE_EXT* = 0x8C2B.GLenum + GL_COLOR_TABLE_BIAS* = 0x80D7.GLenum + GL_MAX_GEOMETRY_INPUT_COMPONENTS* = 0x9123.GLenum + GL_TEXTURE_RANGE_POINTER_APPLE* = 0x85B8.GLenum + GL_PIXEL_SUBSAMPLE_2424_SGIX* = 0x85A3.GLenum + GL_RESAMPLE_REPLICATE_OML* = 0x8986.GLenum + GL_ALL_STATIC_DATA_IBM* = 103060.GLenum + GL_DEBUG_CATEGORY_PERFORMANCE_AMD* = 0x914D.GLenum + GL_ALPHA_TEST_QCOM* = 0x0BC0.GLenum + GL_PREVIOUS_TEXTURE_INPUT_NV* = 0x86E4.GLenum + GL_SIGNED_RGBA_NV* = 0x86FB.GLenum + GL_GLOBAL_ALPHA_SUN* = 0x81D9.GLenum + GL_RGB_FLOAT16_APPLE* = 0x881B.GLenum + GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB* = 0x8808.GLenum + GL_UTF8_NV* = 0x909A.GLenum + GL_ALLOW_DRAW_OBJ_HINT_PGI* = 0x1A20E.GLenum + GL_INT_IMAGE_3D* = 0x9059.GLenum + GL_PACK_ROW_LENGTH* = 0x0D02.GLenum + GL_MAX_TEXTURE_LOD_BIAS_EXT* = 0x84FD.GLenum + GL_SCALED_RESOLVE_NICEST_EXT* = 0x90BB.GLenum + GL_422_EXT* = 0x80CC.GLenum + GL_SAMPLER_1D_ARRAY_SHADOW_EXT* = 0x8DC3.GLenum + GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT* = 0x8336.GLenum + GL_COMPRESSED_RED* = 0x8225.GLenum + GL_MAX_RATIONAL_EVAL_ORDER_NV* = 0x86D7.GLenum + GL_MAX_COMBINED_IMAGE_UNIFORMS* = 0x90CF.GLenum + GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV* = 0x10.GLbitfield + GL_TEXTURE_BINDING_1D_ARRAY* = 0x8C1C.GLenum + GL_FRAMEBUFFER_COMPLETE* = 0x8CD5.GLenum + GL_RG8I* = 0x8237.GLenum + GL_COLOR_ATTACHMENT2_NV* = 0x8CE2.GLenum + GL_INT64_VEC4_NV* = 0x8FEB.GLenum + GL_OP_SET_GE_EXT* = 0x878C.GLenum + GL_READ_WRITE* = 0x88BA.GLenum + GL_OPERAND1_RGB_EXT* = 0x8591.GLenum + GL_SHADER_STORAGE_BLOCK* = 0x92E6.GLenum + GL_TEXTURE_UPDATE_BARRIER_BIT* = 0x00000100.GLbitfield + GL_MAX_FRAGMENT_ATOMIC_COUNTERS* = 0x92D6.GLenum + GL_SHADER_INCLUDE_ARB* = 0x8DAE.GLenum + GL_UNSIGNED_SHORT_1_5_5_5_REV* = 0x8366.GLenum + GL_PROGRAM_PIPELINE* = 0x82E4.GLenum + GL_MAP1_TEXTURE_COORD_2* = 0x0D94.GLenum + GL_FOG_COORDINATE_ARRAY_STRIDE_EXT* = 0x8455.GLenum + GL_WEIGHT_ARRAY_SIZE_OES* = 0x86AB.GLenum + GL_R11F_G11F_B10F* = 0x8C3A.GLenum + GL_WRITE_PIXEL_DATA_RANGE_NV* = 0x8878.GLenum + GL_UNSIGNED_SHORT_8_8_REV_APPLE* = 0x85BB.GLenum + GL_CND_ATI* = 0x896A.GLenum + GL_IMAGE_2D_MULTISAMPLE_ARRAY* = 0x9056.GLenum + GL_MAX_TEXTURE_IMAGE_UNITS_NV* = 0x8872.GLenum + GL_COMPRESSED_SIGNED_RG11_EAC_OES* = 0x9273.GLenum + GL_DOT_PRODUCT_TEXTURE_3D_NV* = 0x86EF.GLenum + GL_IMAGE_TRANSLATE_Y_HP* = 0x8158.GLenum + GL_NORMAL_ARRAY_TYPE_EXT* = 0x807E.GLenum + GL_PIXEL_COUNT_NV* = 0x8866.GLenum + GL_INT_IMAGE_3D_EXT* = 0x9059.GLenum + GL_TEXTURE_TYPE_QCOM* = 0x8BD7.GLenum + GL_COMBINE_ALPHA_EXT* = 0x8572.GLenum + GL_POINT_TOKEN* = 0x0701.GLenum + GL_QUAD_ALPHA4_SGIS* = 0x811E.GLenum + GL_SIGNED_HILO8_NV* = 0x885F.GLenum + GL_MULTISAMPLE_ARB* = 0x809D.GLenum + GL_TEXTURE25* = 0x84D9.GLenum + GL_CURRENT_VERTEX_WEIGHT_EXT* = 0x850B.GLenum + GL_BLEND_DST_ALPHA_OES* = 0x80CA.GLenum + GL_UNSIGNED_SHORT_8_8_REV_MESA* = 0x85BB.GLenum + GL_CLAMP_TO_EDGE_SGIS* = 0x812F.GLint + GL_PATH_STENCIL_REF_NV* = 0x90B8.GLenum + GL_DEBUG_OUTPUT* = 0x92E0.GLenum + GL_OBJECT_TYPE_APPLE* = 0x9112.GLenum + GL_TEXTURE_COMPARE_MODE_ARB* = 0x884C.GLenum + GL_CONSTANT* = 0x8576.GLenum + GL_RGB5_A1_OES* = 0x8057.GLenum + GL_INT16_VEC2_NV* = 0x8FE5.GLenum + GL_CONVOLUTION_BORDER_MODE_EXT* = 0x8013.GLenum + GL_CONTEXT_FLAGS* = 0x821E.GLenum + GL_MAX_PROGRAM_SUBROUTINE_NUM_NV* = 0x8F45.GLenum + GL_SPRITE_SGIX* = 0x8148.GLenum + GL_CURRENT_QUERY* = 0x8865.GLenum + GL_STENCIL_OP_VALUE_AMD* = 0x874C.GLenum + GL_UNIFORM* = 0x92E1.GLenum + GL_TEXTURE_BINDING_RECTANGLE* = 0x84F6.GLenum + GL_TRIANGLES_ADJACENCY_EXT* = 0x000C.GLenum + GL_PROVOKING_VERTEX_EXT* = 0x8E4F.GLenum + GL_INT64_VEC2_NV* = 0x8FE9.GLenum + GL_INVERSE_NV* = 0x862B.GLenum + GL_CON_29_ATI* = 0x895E.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV* = 0x8E24.GLenum + GL_FRONT_AND_BACK* = 0x0408.GLenum + GL_MAX_LABEL_LENGTH_KHR* = 0x82E8.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_START_NV* = 0x8C84.GLenum + GL_EQUAL* = 0x0202.GLenum + GL_RGB10_EXT* = 0x8052.GLenum + GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB* = 0x8C29.GLenum + GL_OP_ADD_EXT* = 0x8787.GLenum + GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN* = 0x85C3.GLenum + GL_NORMAL_ARRAY_LIST_IBM* = 103071.GLenum + GL_RENDERBUFFER_GREEN_SIZE* = 0x8D51.GLenum + GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV* = 0x8C74.GLenum + GL_CURRENT_PALETTE_MATRIX_ARB* = 0x8843.GLenum + GL_DEBUG_TYPE_ERROR* = 0x824C.GLenum + GL_UNIFORM_BUFFER* = 0x8A11.GLenum + GL_NEAREST_CLIPMAP_LINEAR_SGIX* = 0x844E.GLenum + GL_LAST_VERTEX_CONVENTION* = 0x8E4E.GLenum + GL_COMPRESSED_RGBA_ASTC_12x10_KHR* = 0x93BC.GLenum + GL_FENCE_STATUS_NV* = 0x84F3.GLenum + GL_POST_CONVOLUTION_BLUE_BIAS* = 0x8022.GLenum + GL_BLEND_OVERLAP_NV* = 0x9281.GLenum + GL_COMBINE_RGB_ARB* = 0x8571.GLenum + GL_TESS_GEN_MODE* = 0x8E76.GLenum + GL_TEXTURE_ENV* = 0x2300.GLenum + GL_VERTEX_ATTRIB_ARRAY11_NV* = 0x865B.GLenum + GL_SHININESS* = 0x1601.GLenum + GL_DYNAMIC_STORAGE_BIT* = 0x0100.GLbitfield + GL_MODELVIEW30_ARB* = 0x873E.GLenum + GL_WRAP_BORDER_SUN* = 0x81D4.GLenum + GL_SKIP_COMPONENTS1_NV* = -6 + GL_DEPTH_CLAMP_NV* = 0x864F.GLenum + GL_PROGRAM_BINARY_FORMATS* = 0x87FF.GLenum + GL_CURRENT_RASTER_POSITION_VALID* = 0x0B08.GLenum + GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER* = 0x92C8.GLenum + GL_T2F_C4F_N3F_V3F* = 0x2A2C.GLenum + GL_R16F* = 0x822D.GLenum + GL_SECONDARY_COLOR_ARRAY_LENGTH_NV* = 0x8F31.GLenum + GL_SEPARATE_ATTRIBS_EXT* = 0x8C8D.GLenum + GL_NEGATIVE_Z_EXT* = 0x87DB.GLenum + GL_Z400_BINARY_AMD* = 0x8740.GLenum + GL_DRAW_INDIRECT_UNIFIED_NV* = 0x8F40.GLenum + GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV* = 0x8C8A.GLenum + GL_UNSIGNED_INT_S8_S8_8_8_NV* = 0x86DA.GLenum + GL_SRGB8_NV* = 0x8C41.GLenum + GL_DEBUG_SEVERITY_MEDIUM_AMD* = 0x9147.GLenum + GL_MAX_DRAW_BUFFERS_ATI* = 0x8824.GLenum + GL_TEXTURE_COORD_ARRAY_POINTER_EXT* = 0x8092.GLenum + GL_RESAMPLE_AVERAGE_OML* = 0x8988.GLenum + GL_NO_ERROR* = 0.GLenum + GL_RGB5* = 0x8050.GLenum + GL_OP_CLAMP_EXT* = 0x878E.GLenum + GL_PROGRAM_RESIDENT_NV* = 0x8647.GLenum + GL_PROGRAM_ALU_INSTRUCTIONS_ARB* = 0x8805.GLenum + GL_ELEMENT_ARRAY_UNIFIED_NV* = 0x8F1F.GLenum + GL_SECONDARY_COLOR_ARRAY_LIST_IBM* = 103077.GLenum + GL_INTENSITY12_EXT* = 0x804C.GLenum + GL_STENCIL_BUFFER_BIT7_QCOM* = 0x00800000.GLbitfield + GL_SAMPLER* = 0x82E6.GLenum + GL_MAD_ATI* = 0x8968.GLenum + GL_STENCIL_BACK_FAIL* = 0x8801.GLenum + GL_LIGHT_MODEL_TWO_SIDE* = 0x0B52.GLenum + GL_UNPACK_SKIP_PIXELS* = 0x0CF4.GLenum + GL_PIXEL_TEX_GEN_SGIX* = 0x8139.GLenum + GL_FRACTIONAL_ODD* = 0x8E7B.GLenum + GL_LOW_INT* = 0x8DF3.GLenum + GL_MODELVIEW* = 0x1700.GLenum + GL_POST_CONVOLUTION_RED_SCALE_EXT* = 0x801C.GLenum + GL_DRAW_BUFFER11_EXT* = 0x8830.GLenum + GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH* = 0x8A35.GLenum + GL_CONVOLUTION_BORDER_MODE* = 0x8013.GLenum + GL_COMPRESSED_ALPHA_ARB* = 0x84E9.GLenum + GL_DEPTH_ATTACHMENT* = 0x8D00.GLenum + GL_ALPHA8_SNORM* = 0x9014.GLenum + GL_DOUBLE_MAT4x3_EXT* = 0x8F4E.GLenum + GL_INTERNALFORMAT_STENCIL_SIZE* = 0x8276.GLenum + GL_BOOL_VEC2_ARB* = 0x8B57.GLenum + GL_FASTEST* = 0x1101.GLenum + GL_MAX_FRAGMENT_INPUT_COMPONENTS* = 0x9125.GLenum + GL_STENCIL_BACK_FUNC_ATI* = 0x8800.GLenum + GL_POLYGON* = 0x0009.GLenum + GL_SAMPLER_1D_ARRAY_EXT* = 0x8DC0.GLenum + GL_OUTPUT_COLOR1_EXT* = 0x879C.GLenum + GL_IMAGE_2D_RECT* = 0x904F.GLenum + GL_RECT_NV* = 0xF6.GLenum + GL_OUTPUT_TEXTURE_COORD21_EXT* = 0x87B2.GLenum + GL_NOR* = 0x1508.GLenum + GL_FOG_COORD_ARRAY* = 0x8457.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES* = 0x8517.GLenum + GL_TANGENT_ARRAY_POINTER_EXT* = 0x8442.GLenum + GL_DST_OUT_NV* = 0x928D.GLenum + GL_RENDERBUFFER_BINDING_OES* = 0x8CA7.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR* = 0x93D3.GLenum + GL_TEXTURE_GEN_S* = 0x0C60.GLenum + GL_SLIM12S_SGIX* = 0x831F.GLenum + GL_VERTEX_ARRAY_BINDING* = 0x85B5.GLenum + GL_TRACE_PRIMITIVES_BIT_MESA* = 0x0002.GLbitfield + GL_MAX_DEBUG_MESSAGE_LENGTH* = 0x9143.GLenum + GL_EVAL_VERTEX_ATTRIB4_NV* = 0x86CA.GLenum + GL_ACTIVE_SUBROUTINE_UNIFORMS* = 0x8DE6.GLenum + GL_ACCUM_ADJACENT_PAIRS_NV* = 0x90AD.GLenum + GL_NEGATIVE_ONE_EXT* = 0x87DF.GLenum + GL_UNPACK_RESAMPLE_SGIX* = 0x842D.GLenum + GL_ACTIVE_SUBROUTINE_MAX_LENGTH* = 0x8E48.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT* = 0x8518.GLenum + GL_DEBUG_CATEGORY_API_ERROR_AMD* = 0x9149.GLenum + GL_INTERNALFORMAT_BLUE_SIZE* = 0x8273.GLenum + GL_DRAW_BUFFER13_NV* = 0x8832.GLenum + GL_DEBUG_SOURCE_THIRD_PARTY_ARB* = 0x8249.GLenum + GL_R8_EXT* = 0x8229.GLenum + GL_GENERATE_MIPMAP* = 0x8191.GLenum + cGL_SHORT* = 0x1402.GLenum + GL_PACK_REVERSE_ROW_ORDER_ANGLE* = 0x93A4.GLenum + GL_PATH_DASH_OFFSET_RESET_NV* = 0x90B4.GLenum + GL_PACK_SKIP_VOLUMES_SGIS* = 0x8130.GLenum + GL_TEXTURE_RED_TYPE* = 0x8C10.GLenum + GL_MAX_COLOR_ATTACHMENTS_EXT* = 0x8CDF.GLenum + GL_MAP2_VERTEX_ATTRIB5_4_NV* = 0x8675.GLenum + GL_CONSTANT_ALPHA* = 0x8003.GLenum + GL_COLOR_INDEX8_EXT* = 0x80E5.GLenum + GL_DOUBLE_MAT3_EXT* = 0x8F47.GLenum + GL_ATOMIC_COUNTER_BUFFER_INDEX* = 0x9301.GLenum + GL_LINES_ADJACENCY_EXT* = 0x000A.GLenum + GL_RENDERBUFFER_SAMPLES_IMG* = 0x9133.GLenum + GL_COLOR_TABLE_FORMAT* = 0x80D8.GLenum + GL_VERTEX_ATTRIB_ARRAY_TYPE* = 0x8625.GLenum + GL_QUERY_OBJECT_EXT* = 0x9153.GLenum + GL_STREAM_READ_ARB* = 0x88E1.GLenum + GL_MIRROR_CLAMP_TO_EDGE_ATI* = 0x8743.GLint + GL_FRAGMENT_SUBROUTINE_UNIFORM* = 0x92F2.GLenum + GL_UNIFORM_BUFFER_EXT* = 0x8DEE.GLenum + GL_SOURCE2_RGB* = 0x8582.GLenum + GL_PROGRAM_NATIVE_ATTRIBS_ARB* = 0x88AE.GLenum + GL_LUMINANCE12_ALPHA12* = 0x8047.GLenum + GL_INT_SAMPLER_1D_EXT* = 0x8DC9.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT* = 0x8D6C.GLenum + GL_DEPTH_RENDERABLE* = 0x8287.GLenum + GL_INTERNALFORMAT_BLUE_TYPE* = 0x827A.GLenum + GL_SLUMINANCE8_ALPHA8_EXT* = 0x8C45.GLenum + GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB* = 0x900A.GLenum + GL_COLOR_MATRIX* = 0x80B1.GLenum + GL_RGB8_SNORM* = 0x8F96.GLenum + GL_COLOR_ARRAY_SIZE* = 0x8081.GLenum + GL_DRAW_BUFFER4_NV* = 0x8829.GLenum + GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV* = 0x902D.GLenum + GL_PRESENT_TIME_NV* = 0x8E2A.GLenum + GL_COPY_WRITE_BUFFER* = 0x8F37.GLenum + GL_UNPACK_SKIP_PIXELS_EXT* = 0x0CF4.GLenum + GL_PRIMITIVES_GENERATED_NV* = 0x8C87.GLenum + GL_INT_SAMPLER_BUFFER* = 0x8DD0.GLenum + GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV* = 0x04.GLbitfield + GL_FOG_COORDINATE_EXT* = 0x8451.GLenum + GL_VERTEX_ARRAY_ADDRESS_NV* = 0x8F21.GLenum + GL_RENDERBUFFER_RED_SIZE_OES* = 0x8D50.GLenum + GL_BGR_INTEGER_EXT* = 0x8D9A.GLenum + GL_UNSIGNED_BYTE_3_3_2* = 0x8032.GLenum + GL_VBO_FREE_MEMORY_ATI* = 0x87FB.GLenum + GL_PATH_COMPUTED_LENGTH_NV* = 0x90A0.GLenum + GL_COLOR_MATRIX_STACK_DEPTH_SGI* = 0x80B2.GLenum + GL_STACK_OVERFLOW* = 0x0503.GLenum + GL_MODELVIEW1_MATRIX_EXT* = 0x8506.GLenum + GL_CURRENT_BINORMAL_EXT* = 0x843C.GLenum + GL_OP_MULTIPLY_MATRIX_EXT* = 0x8798.GLenum + GL_CLIENT_ATTRIB_STACK_DEPTH* = 0x0BB1.GLenum + GL_VERTEX_PROGRAM_TWO_SIDE_NV* = 0x8643.GLenum + GL_HISTOGRAM_WIDTH_EXT* = 0x8026.GLenum + GL_OBJECT_INFO_LOG_LENGTH_ARB* = 0x8B84.GLenum + GL_SAMPLER_2D_ARRAY_SHADOW* = 0x8DC4.GLenum + GL_UNSIGNED_INT_IMAGE_1D* = 0x9062.GLenum + GL_MAX_IMAGE_UNITS* = 0x8F38.GLenum + GL_TEXTURE31_ARB* = 0x84DF.GLenum + GL_CUBIC_HP* = 0x815F.GLenum + GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV* = 0x8856.GLenum + GL_ARRAY_STRIDE* = 0x92FE.GLenum + GL_DEPTH_PASS_INSTRUMENT_SGIX* = 0x8310.GLenum + GL_COMMAND_BARRIER_BIT* = 0x00000040.GLbitfield + GL_STATIC_DRAW_ARB* = 0x88E4.GLenum + GL_RGB16F* = 0x881B.GLenum + GL_INDEX_MATERIAL_PARAMETER_EXT* = 0x81B9.GLenum + GL_UNPACK_SKIP_VOLUMES_SGIS* = 0x8132.GLenum + GL_TEXTURE_1D* = 0x0DE0.GLenum + GL_VERTEX_PROGRAM_NV* = 0x8620.GLenum + GL_COLOR_ATTACHMENT0_NV* = 0x8CE0.GLenum + GL_READ_PIXEL_DATA_RANGE_LENGTH_NV* = 0x887B.GLenum + GL_FLOAT_32_UNSIGNED_INT_24_8_REV* = 0x8DAD.GLenum + GL_LINE_RESET_TOKEN* = 0x0707.GLenum + GL_WEIGHT_ARRAY_ARB* = 0x86AD.GLenum + GL_TEXTURE17* = 0x84D1.GLenum + GL_DEPTH_COMPONENT32_ARB* = 0x81A7.GLenum + GL_REFERENCED_BY_TESS_CONTROL_SHADER* = 0x9307.GLenum + GL_INVERT* = 0x150A.GLenum + GL_FOG_COORDINATE_ARRAY_STRIDE* = 0x8455.GLenum + GL_COMPRESSED_SIGNED_RG_RGTC2* = 0x8DBE.GLenum + GL_UNSIGNED_SHORT_8_8_MESA* = 0x85BA.GLenum + GL_ELEMENT_ARRAY_TYPE_ATI* = 0x8769.GLenum + GL_CLAMP_VERTEX_COLOR_ARB* = 0x891A.GLenum + GL_POINT_SIZE_ARRAY_STRIDE_OES* = 0x898B.GLenum + GL_RGB8* = 0x8051.GLenum + GL_MATRIX1_ARB* = 0x88C1.GLenum + GL_TEXTURE_POST_SPECULAR_HP* = 0x8168.GLenum + GL_TEXTURE_WRAP_Q_SGIS* = 0x8137.GLenum + GL_SAMPLER_2D_MULTISAMPLE_ARRAY* = 0x910B.GLenum + GL_INVALID_FRAMEBUFFER_OPERATION_OES* = 0x0506.GLenum + GL_VERTEX_ID_SWIZZLE_AMD* = 0x91A5.GLenum + GL_USE_MISSING_GLYPH_NV* = 0x90AA.GLenum + GL_LUMINANCE8_EXT* = 0x8040.GLenum + GL_INT_VEC2* = 0x8B53.GLenum + GL_TEXTURE9* = 0x84C9.GLenum + GL_RGB32UI_EXT* = 0x8D71.GLenum + GL_FENCE_CONDITION_NV* = 0x84F4.GLenum + GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT* = 0x8E4C.GLenum + GL_HSL_SATURATION_NV* = 0x92AE.GLenum + GL_CMYKA_EXT* = 0x800D.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_NV* = 0x8C8E.GLenum + GL_BUFFER_MAP_POINTER_OES* = 0x88BD.GLenum + GL_STORAGE_CLIENT_APPLE* = 0x85B4.GLenum + GL_VERTEX_ARRAY_BUFFER_BINDING_ARB* = 0x8896.GLenum + GL_TEXTURE_INTERNAL_FORMAT* = 0x1003.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED* = 0x8E23.GLenum + GL_UNSIGNED_INT_VEC3* = 0x8DC7.GLenum + GL_TRACE_MASK_MESA* = 0x8755.GLenum + GL_MAP_READ_BIT_EXT* = 0x0001.GLbitfield + GL_READ_FRAMEBUFFER_EXT* = 0x8CA8.GLenum + GL_HISTOGRAM_GREEN_SIZE* = 0x8029.GLenum + GL_COLOR_TABLE_INTENSITY_SIZE_SGI* = 0x80DF.GLenum + GL_SMALL_CCW_ARC_TO_NV* = 0x12.GLenum + GL_RELATIVE_LARGE_CW_ARC_TO_NV* = 0x19.GLenum + GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI* = 0x80BA.GLenum + GL_SCISSOR_BIT* = 0x00080000.GLbitfield + GL_DRAW_BUFFER0_ATI* = 0x8825.GLenum + GL_GEOMETRY_SHADER_BIT* = 0x00000004.GLbitfield + GL_CLIP_FAR_HINT_PGI* = 0x1A221.GLenum + GL_TEXTURE_COMPARE_FUNC_EXT* = 0x884D.GLenum + GL_IS_ROW_MAJOR* = 0x9300.GLenum + GL_MAP1_VERTEX_4* = 0x0D98.GLenum + GL_OUTPUT_TEXTURE_COORD8_EXT* = 0x87A5.GLenum + GL_MAX_VERTEX_IMAGE_UNIFORMS* = 0x90CA.GLenum + GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE* = 0x8211.GLenum + GL_SOURCE1_ALPHA_ARB* = 0x8589.GLenum + GL_VIRTUAL_PAGE_SIZE_X_AMD* = 0x9195.GLenum + GL_CULL_FRAGMENT_NV* = 0x86E7.GLenum + GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS* = 0x92DC.GLenum + GL_QUERY_COUNTER_BITS_EXT* = 0x8864.GLenum + GL_RGB565* = 0x8D62.GLenum + GL_OFFSET_TEXTURE_RECTANGLE_NV* = 0x864C.GLenum + GL_CONVOLUTION_FORMAT_EXT* = 0x8017.GLenum + GL_EYE_POINT_SGIS* = 0x81F4.GLenum + GL_ALPHA32F_ARB* = 0x8816.GLenum + GL_TEXTURE_DEPTH_SIZE* = 0x884A.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR* = 0x93D1.GLenum + GL_PRIMARY_COLOR_NV* = 0x852C.GLenum + GL_BLEND_DST_ALPHA_EXT* = 0x80CA.GLenum + GL_NORMALIZE* = 0x0BA1.GLenum + GL_POST_CONVOLUTION_GREEN_BIAS_EXT* = 0x8021.GLenum + GL_HI_SCALE_NV* = 0x870E.GLenum + GL_TESS_EVALUATION_PROGRAM_NV* = 0x891F.GLenum + GL_MAX_DUAL_SOURCE_DRAW_BUFFERS* = 0x88FC.GLenum + GL_SWIZZLE_STRQ_ATI* = 0x897A.GLenum + GL_READ_FRAMEBUFFER_NV* = 0x8CA8.GLenum + GL_MATRIX_INDEX_ARRAY_STRIDE_OES* = 0x8848.GLenum + GL_MIN_SPARSE_LEVEL_ARB* = 0x919B.GLenum + GL_RG32UI* = 0x823C.GLenum + GL_SAMPLER_2D_ARRAY_EXT* = 0x8DC1.GLenum + GL_TEXTURE22_ARB* = 0x84D6.GLenum + GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS* = 0x8A32.GLenum + GL_CULL_VERTEX_EYE_POSITION_EXT* = 0x81AB.GLenum + GL_TEXTURE_BUFFER* = 0x8C2A.GLenum + GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB* = 0x851C.GLenum + GL_NORMAL_ARRAY_COUNT_EXT* = 0x8080.GLenum + GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_NV* = 0x8D56.GLenum + GL_ELEMENT_ARRAY_BARRIER_BIT_EXT* = 0x00000002.GLbitfield + GL_VERTEX_ARRAY_COUNT_EXT* = 0x807D.GLenum + GL_PROGRAM_ERROR_STRING_NV* = 0x8874.GLenum + GL_INVALID_FRAMEBUFFER_OPERATION* = 0x0506.GLenum + GL_RGB9_E5* = 0x8C3D.GLenum + GL_GREEN_BITS* = 0x0D53.GLenum + GL_CLIP_DISTANCE0* = 0x3000.GLenum + GL_COMBINER_SUM_OUTPUT_NV* = 0x854C.GLenum + GL_COLOR_ARRAY* = 0x8076.GLenum + GL_RGBA8_SNORM* = 0x8F97.GLenum + GL_PROGRAM_BINDING_ARB* = 0x8677.GLenum + GL_4PASS_0_EXT* = 0x80A4.GLenum + GL_STATIC_DRAW* = 0x88E4.GLenum + GL_TEXTURE_COMPRESSED_BLOCK_WIDTH* = 0x82B1.GLenum + GL_TEXTURE_STORAGE_SPARSE_BIT_AMD* = 0x00000001.GLbitfield + GL_MEDIUM_INT* = 0x8DF4.GLenum + GL_TEXTURE13_ARB* = 0x84CD.GLenum + GL_LUMINANCE_ALPHA16F_ARB* = 0x881F.GLenum + GL_CONTEXT_CORE_PROFILE_BIT* = 0x00000001.GLbitfield + GL_LOCATION_COMPONENT* = 0x934A.GLenum + GL_TEXTURE_RECTANGLE* = 0x84F5.GLenum + GL_SAMPLER_2D_ARB* = 0x8B5E.GLenum + GL_FLOAT_RG32_NV* = 0x8887.GLenum + GL_SKIP_DECODE_EXT* = 0x8A4A.GLenum + GL_LIGHT6* = 0x4006.GLenum + GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD* = 0x87EE.GLenum + GL_NOOP* = 0x1505.GLenum + GL_DEPTH_BUFFER_BIT* = 0x00000100.GLbitfield + GL_FRAMEBUFFER_BINDING_ANGLE* = 0x8CA6.GLenum + GL_DEBUG_TYPE_POP_GROUP_KHR* = 0x826A.GLenum + GL_SAMPLER_2D_RECT_SHADOW* = 0x8B64.GLenum + GL_CONSERVE_MEMORY_HINT_PGI* = 0x1A1FD.GLenum + GL_QUERY_BY_REGION_NO_WAIT* = 0x8E16.GLenum + GL_UNSIGNED_INT_SAMPLER_CUBE* = 0x8DD4.GLenum + GL_LUMINANCE4_EXT* = 0x803F.GLenum + GL_COLOR_ARRAY_STRIDE* = 0x8083.GLenum + GL_SAMPLER_2D_ARRAY_SHADOW_NV* = 0x8DC4.GLenum + GL_REFERENCED_BY_GEOMETRY_SHADER* = 0x9309.GLenum + GL_SIGNED_RGB_UNSIGNED_ALPHA_NV* = 0x870C.GLenum + GL_OBJECT_PLANE* = 0x2501.GLenum + GL_Q* = 0x2003.GLenum + GL_MAX_SPOT_EXPONENT_NV* = 0x8505.GLenum + GL_VERTEX_ATTRIB_ARRAY_LONG* = 0x874E.GLenum + GL_COLOR_ATTACHMENT3* = 0x8CE3.GLenum + GL_TEXTURE_BINDING_RENDERBUFFER_NV* = 0x8E53.GLenum + GL_EXCLUSION_NV* = 0x92A0.GLenum + GL_EDGE_FLAG_ARRAY_ADDRESS_NV* = 0x8F26.GLenum + GL_PRIMARY_COLOR_ARB* = 0x8577.GLenum + GL_LUMINANCE_ALPHA_FLOAT16_ATI* = 0x881F.GLenum + GL_TRACE_TEXTURES_BIT_MESA* = 0x0008.GLbitfield + GL_FRAMEBUFFER_OES* = 0x8D40.GLenum + GL_PIXEL_MAG_FILTER_EXT* = 0x8331.GLenum + GL_IMAGE_BINDING_LAYERED_EXT* = 0x8F3C.GLenum + GL_PATH_MITER_LIMIT_NV* = 0x907A.GLenum + GL_PROJECTION_MATRIX* = 0x0BA7.GLenum + GL_TEXTURE23_ARB* = 0x84D7.GLenum + GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE* = 0x8A07.GLenum + GL_RGB32F_ARB* = 0x8815.GLenum + GL_RED_SCALE* = 0x0D14.GLenum + GL_GEOMETRY_INPUT_TYPE_ARB* = 0x8DDB.GLenum + GL_EVAL_VERTEX_ATTRIB13_NV* = 0x86D3.GLenum + GL_INT64_NV* = 0x140E.GLenum + GL_VIEW_CLASS_24_BITS* = 0x82C9.GLenum + GL_FRAGMENT_LIGHT2_SGIX* = 0x840E.GLenum + GL_LUMINANCE12_ALPHA12_EXT* = 0x8047.GLenum + GL_MAP2_VERTEX_ATTRIB2_4_NV* = 0x8672.GLenum + GL_POINT_SIZE_MIN_SGIS* = 0x8126.GLenum + GL_DEBUG_TYPE_OTHER_ARB* = 0x8251.GLenum + GL_MAP2_VERTEX_ATTRIB0_4_NV* = 0x8670.GLenum + GL_DEBUG_PRINT_MESA* = 0x875A.GLenum + GL_TEXTURE_PRIORITY* = 0x8066.GLenum + GL_PIXEL_MAP_I_TO_G* = 0x0C73.GLenum + GL_VERTEX_ATTRIB_ARRAY_DIVISOR* = 0x88FE.GLenum + GL_TEXTURE_CUBE_MAP_ARB* = 0x8513.GLenum + GL_LUMINANCE8_SNORM* = 0x9015.GLenum + GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT* = 0x00004000.GLbitfield + GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS* = 0x8E1F.GLenum + GL_BUFFER_STORAGE_FLAGS* = 0x8220.GLenum + GL_DEPTH_COMPONENT24_SGIX* = 0x81A6.GLenum + GL_UNIFORM_OFFSET* = 0x8A3B.GLenum + GL_TEXTURE_DT_SIZE_NV* = 0x871E.GLenum + GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI* = 0x80B7.GLenum + GL_DEPTH32F_STENCIL8_NV* = 0x8DAC.GLenum + GL_STENCIL_FUNC* = 0x0B92.GLenum + GL_NEAREST_MIPMAP_LINEAR* = 0x2702.GLint + GL_COMPRESSED_LUMINANCE_LATC1_EXT* = 0x8C70.GLenum + GL_TEXTURE_BORDER* = 0x1005.GLenum + GL_COLOR_ATTACHMENT14_NV* = 0x8CEE.GLenum + GL_TEXTURE_STORAGE_HINT_APPLE* = 0x85BC.GLenum + GL_VERTEX_ARRAY_RANGE_NV* = 0x851D.GLenum + GL_COLOR_ARRAY_SIZE_EXT* = 0x8081.GLenum + GL_INTERNALFORMAT_SUPPORTED* = 0x826F.GLenum + GL_MULTISAMPLE_BIT_ARB* = 0x20000000.GLbitfield + GL_RGB* = 0x1907.GLenum + GL_TRANSFORM_FEEDBACK_PAUSED* = 0x8E23.GLenum + GL_ALPHA8* = 0x803C.GLenum + GL_STENCIL_FAIL* = 0x0B94.GLenum + GL_PACK_SKIP_IMAGES_EXT* = 0x806B.GLenum + GL_FOG_COORDINATE_ARRAY_TYPE_EXT* = 0x8454.GLenum + GL_RESCALE_NORMAL_EXT* = 0x803A.GLenum + GL_LERP_ATI* = 0x8969.GLenum + GL_MATRIX_INDEX_ARRAY_STRIDE_ARB* = 0x8848.GLenum + GL_PROGRAM_LENGTH_NV* = 0x8627.GLenum + GL_UNSIGNED_INT_SAMPLER_3D_EXT* = 0x8DD3.GLenum + GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT* = 0x8DBE.GLenum + GL_UNSIGNED_INT_24_8_NV* = 0x84FA.GLenum + GL_POINT_SIZE_MIN_ARB* = 0x8126.GLenum + GL_COMP_BIT_ATI* = 0x00000002.GLbitfield + GL_NORMAL_ARRAY_ADDRESS_NV* = 0x8F22.GLenum + GL_TEXTURE9_ARB* = 0x84C9.GLenum + GL_MAX_GEOMETRY_OUTPUT_COMPONENTS* = 0x9124.GLenum + GL_DOUBLEBUFFER* = 0x0C32.GLenum + GL_OFFSET_TEXTURE_2D_BIAS_NV* = 0x86E3.GLenum + GL_ACTIVE_PROGRAM_EXT* = 0x8B8D.GLenum + GL_PARTIAL_SUCCESS_NV* = 0x902E.GLenum + GL_SUBTRACT* = 0x84E7.GLenum + GL_DUAL_INTENSITY4_SGIS* = 0x8118.GLenum + GL_FILL* = 0x1B02.GLenum + GL_COMPRESSED_SRGB_ALPHA* = 0x8C49.GLenum + GL_RENDERBUFFER_OES* = 0x8D41.GLenum + GL_PIXEL_MAP_R_TO_R_SIZE* = 0x0CB6.GLenum + GL_TEXTURE_LUMINANCE_TYPE_ARB* = 0x8C14.GLenum + GL_TEXTURE_BUFFER_FORMAT_EXT* = 0x8C2E.GLenum + GL_OUTPUT_TEXTURE_COORD13_EXT* = 0x87AA.GLenum + GL_LINES_ADJACENCY_ARB* = 0x000A.GLenum + GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV* = 0x8F44.GLenum + GL_INTENSITY32UI_EXT* = 0x8D73.GLenum + GL_PACK_IMAGE_HEIGHT* = 0x806C.GLenum + GL_HI_BIAS_NV* = 0x8714.GLenum + GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB* = 0x824E.GLenum + GL_LINE_STIPPLE* = 0x0B24.GLenum + GL_INDEX_LOGIC_OP* = 0x0BF1.GLenum + GL_CON_18_ATI* = 0x8953.GLenum + GL_QUERY_RESULT* = 0x8866.GLenum + GL_FRAGMENT_PROGRAM_NV* = 0x8870.GLenum + GL_MATRIX1_NV* = 0x8631.GLenum + GL_FUNC_SUBTRACT_OES* = 0x800A.GLenum + GL_PIXEL_MAP_I_TO_A_SIZE* = 0x0CB5.GLenum + GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT* = 0x8365.GLenum + GL_OUTPUT_TEXTURE_COORD20_EXT* = 0x87B1.GLenum + GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT* = 0x00000001.GLbitfield + GL_TRIANGULAR_NV* = 0x90A5.GLenum + GL_TEXTURE_COMPARE_MODE_EXT* = 0x884C.GLenum + GL_SECONDARY_COLOR_ARRAY_SIZE_EXT* = 0x845A.GLenum + GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT* = 0x8DA7.GLenum + GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE* = 0x83F3.GLenum + GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB* = 0x9345.GLenum + GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB* = 0x889A.GLenum + GL_PROGRAM_FORMAT_ARB* = 0x8876.GLenum + GL_QUAD_INTENSITY4_SGIS* = 0x8122.GLenum + GL_REPLICATE_BORDER* = 0x8153.GLenum + GL_PN_TRIANGLES_ATI* = 0x87F0.GLenum + GL_DEPTH_TEXTURE_MODE* = 0x884B.GLenum + GL_VARIABLE_C_NV* = 0x8525.GLenum + GL_CLIP_PLANE0_IMG* = 0x3000.GLenum + GL_FRONT_LEFT* = 0x0400.GLenum + GL_MATRIX3_ARB* = 0x88C3.GLenum + GL_BLEND_EQUATION_ALPHA_EXT* = 0x883D.GLenum + GL_BGRA8_EXT* = 0x93A1.GLenum + GL_INTERLACE_READ_INGR* = 0x8568.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE* = 0x8E24.GLenum + GL_MAP1_VERTEX_ATTRIB13_4_NV* = 0x866D.GLenum + GL_PIXEL_TEX_GEN_Q_FLOOR_SGIX* = 0x8186.GLenum + GL_UNSIGNED_INT_SAMPLER_2D_ARRAY* = 0x8DD7.GLenum + GL_ALL_SHADER_BITS_EXT* = 0xFFFFFFFF.GLbitfield + GL_ONE_MINUS_SRC1_ALPHA* = 0x88FB.GLenum + GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE* = 0x851E.GLenum + GL_PROXY_COLOR_TABLE_SGI* = 0x80D3.GLenum + GL_MAX_RENDERBUFFER_SIZE_OES* = 0x84E8.GLenum + GL_VERTEX_ATTRIB_ARRAY_ENABLED* = 0x8622.GLenum + GL_TEXTURE_BINDING_2D_MULTISAMPLE* = 0x9104.GLenum + GL_STENCIL_BUFFER_BIT0_QCOM* = 0x00010000.GLbitfield + GL_IMAGE_BINDING_FORMAT_EXT* = 0x906E.GLenum + GL_RENDERBUFFER_SAMPLES_NV* = 0x8CAB.GLenum + GL_ACCUM_GREEN_BITS* = 0x0D59.GLenum + GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER* = 0x90ED.GLenum + GL_FRAMEBUFFER_UNDEFINED* = 0x8219.GLenum + GL_OFFSET_TEXTURE_2D_NV* = 0x86E8.GLenum + GL_POST_CONVOLUTION_RED_BIAS* = 0x8020.GLenum + GL_DRAW_BUFFER8* = 0x882D.GLenum + GL_MAP_INVALIDATE_RANGE_BIT* = 0x0004.GLbitfield + GL_ALWAYS* = 0x0207.GLenum + GL_ALPHA_MIN_SGIX* = 0x8320.GLenum + GL_SOURCE0_RGB_ARB* = 0x8580.GLenum + GL_POINT_SIZE_ARRAY_POINTER_OES* = 0x898C.GLenum + GL_CUBIC_EXT* = 0x8334.GLenum + GL_MAP2_NORMAL* = 0x0DB2.GLenum + GL_TEXTURE_RESIDENT_EXT* = 0x8067.GLenum + GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB* = 0x8C2D.GLenum + GL_BUMP_NUM_TEX_UNITS_ATI* = 0x8777.GLenum + GL_TEXTURE_LOD_BIAS_T_SGIX* = 0x818F.GLenum + GL_FONT_UNDERLINE_POSITION_BIT_NV* = 0x04000000.GLbitfield + GL_NORMAL_ARRAY_STRIDE* = 0x807F.GLenum + GL_CONDITION_SATISFIED_APPLE* = 0x911C.GLenum + GL_POINT_SIZE_MIN* = 0x8126.GLenum + GL_SPARE0_PLUS_SECONDARY_COLOR_NV* = 0x8532.GLenum + GL_LAYOUT_DEFAULT_INTEL* = 0.GLenum + GL_FRAMEBUFFER_BINDING* = 0x8CA6.GLenum + GL_HIGH_FLOAT* = 0x8DF2.GLenum + GL_NO_RESET_NOTIFICATION_ARB* = 0x8261.GLenum + GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV* = 0x864D.GLenum + GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV* = 0x8F20.GLenum + GL_VIEW_CLASS_96_BITS* = 0x82C5.GLenum + GL_BACK_RIGHT* = 0x0403.GLenum + GL_BLEND_EQUATION_ALPHA* = 0x883D.GLenum + GL_DISTANCE_ATTENUATION_SGIS* = 0x8129.GLenum + GL_PROXY_TEXTURE_CUBE_MAP_ARRAY* = 0x900B.GLenum + GL_RG16* = 0x822C.GLenum + GL_UNDEFINED_VERTEX* = 0x8260.GLenum + GL_PATH_DASH_OFFSET_NV* = 0x907E.GLenum + GL_ALL_ATTRIB_BITS* = 0xFFFFFFFF.GLbitfield + GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE* = 0x8A04.GLenum + GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI* = 0x80B3.GLenum + GL_TIME_ELAPSED_EXT* = 0x88BF.GLenum + GL_MAP2_VERTEX_3* = 0x0DB7.GLenum + GL_MAX_PROGRAM_RESULT_COMPONENTS_NV* = 0x8909.GLenum + GL_SAMPLER_2D_RECT_SHADOW_ARB* = 0x8B64.GLenum + GL_REFERENCE_PLANE_SGIX* = 0x817D.GLenum + GL_LUMINANCE4_ALPHA4_EXT* = 0x8043.GLenum + GL_PATH_FILL_MASK_NV* = 0x9081.GLenum + GL_FILTER* = 0x829A.GLenum + GL_INT_SAMPLER_2D_ARRAY* = 0x8DCF.GLenum + GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV* = 0x8908.GLenum + GL_EVAL_VERTEX_ATTRIB2_NV* = 0x86C8.GLenum + GL_NAND* = 0x150E.GLenum + GL_BLEND_SRC_RGB* = 0x80C9.GLenum + GL_OPERAND2_ALPHA_EXT* = 0x859A.GLenum + GL_IMAGE_1D_EXT* = 0x904C.GLenum + GL_CONVOLUTION_FILTER_SCALE* = 0x8014.GLenum + GL_IMAGE_CLASS_2_X_16* = 0x82BD.GLenum + GL_VIEW_CLASS_BPTC_FLOAT* = 0x82D3.GLenum + GL_PROGRAM_INPUT* = 0x92E3.GLenum + GL_1PASS_SGIS* = 0x80A1.GLenum + GL_FOG_DISTANCE_MODE_NV* = 0x855A.GLenum + GL_STENCIL_INDEX16_EXT* = 0x8D49.GLenum + GL_POST_CONVOLUTION_RED_BIAS_EXT* = 0x8020.GLenum + GL_PIXEL_MAP_R_TO_R* = 0x0C76.GLenum + GL_3DC_XY_AMD* = 0x87FA.GLenum + GL_POINT_SIZE_MAX* = 0x8127.GLenum + GL_DOUBLE_MAT3x2* = 0x8F4B.GLenum + GL_DOUBLE_MAT4x2_EXT* = 0x8F4D.GLenum + GL_TEXTURE_HI_SIZE_NV* = 0x871B.GLenum + GL_MATRIX4_NV* = 0x8634.GLenum + GL_SPRITE_TRANSLATION_SGIX* = 0x814B.GLenum + GL_TEXTURE_FILTER_CONTROL_EXT* = 0x8500.GLenum + GL_SMOOTH_LINE_WIDTH_GRANULARITY* = 0x0B23.GLenum + GL_TEXTURE_BINDING_BUFFER* = 0x8C2C.GLenum + GL_INTENSITY4* = 0x804A.GLenum + GL_MAX_IMAGE_SAMPLES_EXT* = 0x906D.GLenum + GL_COLOR_ATTACHMENT12* = 0x8CEC.GLenum + GL_CLAMP_READ_COLOR* = 0x891C.GLenum + GL_ELEMENT_ARRAY_BUFFER_ARB* = 0x8893.GLenum + GL_MAP2_VERTEX_ATTRIB6_4_NV* = 0x8676.GLenum + GL_CONVOLUTION_HEIGHT_EXT* = 0x8019.GLenum + GL_SGX_PROGRAM_BINARY_IMG* = 0x9130.GLenum + GL_MAP1_TEXTURE_COORD_1* = 0x0D93.GLenum + GL_COMPRESSED_RGBA_ASTC_6x6_KHR* = 0x93B4.GLenum + GL_TEXTURE_APPLICATION_MODE_EXT* = 0x834F.GLenum + GL_TEXTURE_GATHER* = 0x82A2.GLenum + GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS* = 0x90DC.GLenum + GL_DEBUG_LOGGED_MESSAGES_KHR* = 0x9145.GLenum + GL_TEXTURE_VIEW_NUM_LEVELS* = 0x82DC.GLenum + GL_ENABLE_BIT* = 0x00002000.GLbitfield + GL_VERTEX_PROGRAM_TWO_SIDE_ARB* = 0x8643.GLenum + GL_INDEX_TEST_EXT* = 0x81B5.GLenum + GL_TEXTURE_WRAP_R* = 0x8072.GLenum + GL_MAX* = 0x8008.GLenum + GL_UNPACK_IMAGE_DEPTH_SGIS* = 0x8133.GLenum + GL_COLOR_ATTACHMENT13_NV* = 0x8CED.GLenum + GL_FOG_BIT* = 0x00000080.GLbitfield + GL_GEOMETRY_SHADER_EXT* = 0x8DD9.GLenum + GL_ALPHA_TEST_FUNC_QCOM* = 0x0BC1.GLenum + GL_DRAW_BUFFER10_EXT* = 0x882F.GLenum + GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB* = 0x880F.GLenum + GL_STENCIL_BACK_REF* = 0x8CA3.GLenum + GL_SAMPLER_1D_ARB* = 0x8B5D.GLenum + GL_DRAW_BUFFER* = 0x0C01.GLenum + GL_CLIENT_PIXEL_STORE_BIT* = 0x00000001.GLbitfield + GL_TEXTURE_STENCIL_SIZE* = 0x88F1.GLenum + GL_ELEMENT_ARRAY_APPLE* = 0x8A0C.GLenum + GL_CON_21_ATI* = 0x8956.GLenum + GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER* = 0x92C7.GLenum + GL_PIXEL_MAP_I_TO_B* = 0x0C74.GLenum + GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE* = 0x8A03.GLenum + GL_FOG_INDEX* = 0x0B61.GLenum + GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI* = 0x80D4.GLenum + GL_OUTPUT_TEXTURE_COORD29_EXT* = 0x87BA.GLenum + GL_TESS_CONTROL_SUBROUTINE* = 0x92E9.GLenum + GL_IMAGE_CUBE_MAP_ARRAY* = 0x9054.GLenum + GL_RGB_FLOAT32_ATI* = 0x8815.GLenum + GL_OBJECT_SHADER_SOURCE_LENGTH_ARB* = 0x8B88.GLenum + GL_COLOR_INDEX4_EXT* = 0x80E4.GLenum + GL_DRAW_BUFFER14* = 0x8833.GLenum + GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV* = 0x90BE.GLenum + GL_NATIVE_GRAPHICS_HANDLE_PGI* = 0x1A202.GLenum + GL_UNSIGNED_SHORT_5_6_5* = 0x8363.GLenum + GL_GREATER* = 0x0204.GLenum + GL_DATA_BUFFER_AMD* = 0x9151.GLenum + GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV* = 0x40.GLbitfield + GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2* = 0x9276.GLenum + GL_RELATIVE_MOVE_TO_NV* = 0x03.GLenum + GL_BLUE_INTEGER* = 0x8D96.GLenum + GL_BLUE_BIAS* = 0x0D1B.GLenum + GL_SHADER_TYPE* = 0x8B4F.GLenum + GL_TRANSFORM_FEEDBACK_BINDING* = 0x8E25.GLenum + GL_TEXTURE17_ARB* = 0x84D1.GLenum + GL_GREEN* = 0x1904.GLenum + GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS* = 0x8E89.GLenum + GL_DRAW_BUFFER6* = 0x882B.GLenum + GL_VALIDATE_STATUS* = 0x8B83.GLenum + GL_TEXTURE_COORD_ARRAY_ADDRESS_NV* = 0x8F25.GLenum + GL_MVP_MATRIX_EXT* = 0x87E3.GLenum + GL_PIXEL_BUFFER_BARRIER_BIT_EXT* = 0x00000080.GLbitfield + GL_MAX_VERTEX_VARYING_COMPONENTS_EXT* = 0x8DDE.GLenum + GL_STACK_OVERFLOW_KHR* = 0x0503.GLenum + GL_MAX_PROJECTION_STACK_DEPTH* = 0x0D38.GLenum + GL_SKIP_COMPONENTS3_NV* = -4 + GL_DEBUG_ASSERT_MESA* = 0x875B.GLenum + GL_INSTRUMENT_BUFFER_POINTER_SGIX* = 0x8180.GLenum + GL_SAMPLE_ALPHA_TO_MASK_EXT* = 0x809E.GLenum + GL_REG_29_ATI* = 0x893E.GLenum + GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV* = 0x8C4E.GLenum + GL_DEBUG_CATEGORY_DEPRECATION_AMD* = 0x914B.GLenum + GL_DEPTH_STENCIL_TO_BGRA_NV* = 0x886F.GLenum + GL_UNSIGNED_INT_VEC3_EXT* = 0x8DC7.GLenum + GL_VERTEX_SHADER_EXT* = 0x8780.GLenum + GL_LIST_BASE* = 0x0B32.GLenum + GL_TEXTURE_STENCIL_SIZE_EXT* = 0x88F1.GLenum + GL_ACTIVE_PROGRAM* = 0x8259.GLenum + GL_RGBA_SIGNED_COMPONENTS_EXT* = 0x8C3C.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR* = 0x93DC.GLenum + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE* = 0x8CD0.GLenum + GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE* = 0x8217.GLenum + GL_MATRIX7_ARB* = 0x88C7.GLenum + GL_FLOAT_VEC3_ARB* = 0x8B51.GLenum + GL_PACK_ROW_BYTES_APPLE* = 0x8A15.GLenum + GL_PIXEL_TILE_GRID_HEIGHT_SGIX* = 0x8143.GLenum + GL_UNIFORM_BLOCK* = 0x92E2.GLenum + GL_VIEWPORT_BIT* = 0x00000800.GLbitfield + GL_RENDERBUFFER_COVERAGE_SAMPLES_NV* = 0x8CAB.GLenum + GL_MAP1_BINORMAL_EXT* = 0x8446.GLenum + GL_SAMPLER_3D* = 0x8B5F.GLenum + GL_RENDERBUFFER_SAMPLES_APPLE* = 0x8CAB.GLenum + GL_DEPTH_WRITEMASK* = 0x0B72.GLenum + GL_MAP2_VERTEX_ATTRIB9_4_NV* = 0x8679.GLenum + GL_TEXTURE_COMPARE_FUNC* = 0x884D.GLenum + GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB* = 0x00000004.GLbitfield + GL_READ_BUFFER* = 0x0C02.GLenum + GL_ONE_MINUS_SRC1_COLOR* = 0x88FA.GLenum + GL_PROGRAM_FORMAT_ASCII_ARB* = 0x8875.GLenum + GL_DRAW_FRAMEBUFFER_APPLE* = 0x8CA9.GLenum + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES* = 0x8CD0.GLenum + GL_BLEND_DST* = 0x0BE0.GLenum + GL_SHADER_OBJECT_EXT* = 0x8B48.GLenum + GL_UNSIGNALED* = 0x9118.GLenum + GL_VERTEX4_BIT_PGI* = 0x00000008.GLbitfield + GL_DRAW_FRAMEBUFFER_BINDING_APPLE* = 0x8CA6.GLenum + GL_IMAGE_CUBE_EXT* = 0x9050.GLenum + GL_CONTEXT_ROBUST_ACCESS_EXT* = 0x90F3.GLenum + GL_TEXTURE14_ARB* = 0x84CE.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_Y* = 0x8517.GLenum + GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV* = 0x8857.GLenum + GL_COMPRESSED_RG11_EAC_OES* = 0x9272.GLenum + GL_OP_DOT4_EXT* = 0x8785.GLenum + GL_FRAMEBUFFER_COMPLETE_EXT* = 0x8CD5.GLenum + GL_TEXTURE_COMPARE_FUNC_ARB* = 0x884D.GLenum + GL_TEXTURE_FILTER4_SIZE_SGIS* = 0x8147.GLenum + GL_ELEMENT_ARRAY_BUFFER_BINDING* = 0x8895.GLenum + GL_UNSIGNED_INT_IMAGE_BUFFER_EXT* = 0x9067.GLenum + GL_IMAGE_1D_ARRAY_EXT* = 0x9052.GLenum + GL_CLAMP_READ_COLOR_ARB* = 0x891C.GLenum + GL_COMPUTE_SUBROUTINE* = 0x92ED.GLenum + GL_R3_G3_B2* = 0x2A10.GLenum + GL_PATH_DASH_ARRAY_COUNT_NV* = 0x909F.GLenum + GL_SPOT_EXPONENT* = 0x1205.GLenum + GL_NUM_PROGRAM_BINARY_FORMATS_OES* = 0x87FE.GLenum + GL_SWIZZLE_STQ_ATI* = 0x8977.GLenum + GL_SYNC_FLUSH_COMMANDS_BIT_APPLE* = 0x00000001.GLbitfield + GL_VERTEX_STREAM6_ATI* = 0x8772.GLenum + GL_FRAGMENT_COLOR_MATERIAL_SGIX* = 0x8401.GLenum + GL_DYNAMIC_ATI* = 0x8761.GLenum + GL_SUB_ATI* = 0x8965.GLenum + GL_PREVIOUS_EXT* = 0x8578.GLenum + GL_MAP2_TEXTURE_COORD_1* = 0x0DB3.GLenum + GL_COLOR_SAMPLES_NV* = 0x8E20.GLenum + GL_HILO_NV* = 0x86F4.GLenum + GL_SHADER_STORAGE_BUFFER_BINDING* = 0x90D3.GLenum + GL_DUP_LAST_CUBIC_CURVE_TO_NV* = 0xF4.GLenum + GL_ACTIVE_SUBROUTINES* = 0x8DE5.GLenum + GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG* = 0x9134.GLenum + GL_INTENSITY16* = 0x804D.GLenum + GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB* = 0x88AF.GLenum + GL_TIMESTAMP_EXT* = 0x8E28.GLenum + GL_CLIENT_ACTIVE_TEXTURE* = 0x84E1.GLenum + GL_TEXTURE_BINDING_2D_ARRAY* = 0x8C1D.GLenum + GL_INT_SAMPLER_2D_RECT_EXT* = 0x8DCD.GLenum + GL_PREFER_DOUBLEBUFFER_HINT_PGI* = 0x1A1F8.GLenum + GL_TEXTURE_WIDTH* = 0x1000.GLenum + GL_CPU_OPTIMIZED_QCOM* = 0x8FB1.GLenum + GL_TEXTURE_IMAGE_TYPE* = 0x8290.GLenum + GL_MAX_VERTEX_UNIFORM_VECTORS* = 0x8DFB.GLenum + GL_MODULATE_SUBTRACT_ATI* = 0x8746.GLenum + GL_SYNC_STATUS* = 0x9114.GLenum + GL_IMAGE_2D_RECT_EXT* = 0x904F.GLenum + GL_MATRIX6_NV* = 0x8636.GLenum + GL_SOURCE1_RGB_ARB* = 0x8581.GLenum + GL_MAX_COMBINED_ATOMIC_COUNTERS* = 0x92D7.GLenum + GL_MAX_COMPUTE_LOCAL_INVOCATIONS* = 0x90EB.GLenum + GL_SAMPLER_CUBE* = 0x8B60.GLenum + GL_ALPHA_FLOAT32_ATI* = 0x8816.GLenum + GL_COMPRESSED_LUMINANCE_ARB* = 0x84EA.GLenum + GL_COMPRESSED_RGB8_ETC2_OES* = 0x9274.GLenum + GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR* = 0x8243.GLenum + GL_MINUS_CLAMPED_NV* = 0x92B3.GLenum + GL_REG_31_ATI* = 0x8940.GLenum + GL_ELEMENT_ARRAY_ADDRESS_NV* = 0x8F29.GLenum + GL_SRC1_COLOR* = 0x88F9.GLenum + GL_DEBUG_SEVERITY_LOW_ARB* = 0x9148.GLenum + GL_CON_3_ATI* = 0x8944.GLenum + GL_R32I* = 0x8235.GLenum + GL_BLEND_COLOR* = 0x8005.GLenum + GL_CLIP_PLANE4* = 0x3004.GLenum + GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT* = 0x00000001.GLbitfield + GL_FLOAT16_VEC4_NV* = 0x8FFB.GLenum + GL_DST_IN_NV* = 0x928B.GLenum + GL_VIRTUAL_PAGE_SIZE_Y_ARB* = 0x9196.GLenum + GL_COLOR_ATTACHMENT8_NV* = 0x8CE8.GLenum + GL_TESS_GEN_VERTEX_ORDER* = 0x8E78.GLenum + GL_LOSE_CONTEXT_ON_RESET_EXT* = 0x8252.GLenum + GL_PROGRAM_INSTRUCTIONS_ARB* = 0x88A0.GLenum + GL_TEXTURE_IMAGE_VALID_QCOM* = 0x8BD8.GLenum + GL_SAMPLE_MASK_VALUE_EXT* = 0x80AA.GLenum + GL_CURRENT_MATRIX_ARB* = 0x8641.GLenum + GL_DECR_WRAP_EXT* = 0x8508.GLenum + GL_BLUE_INTEGER_EXT* = 0x8D96.GLenum + GL_COMPRESSED_RG* = 0x8226.GLenum + GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV* = 0x88F4.GLenum + GL_MINMAX_EXT* = 0x802E.GLenum + GL_FLOAT_MAT4_ARB* = 0x8B5C.GLenum + GL_TEXTURE_CLIPMAP_FRAME_SGIX* = 0x8172.GLenum + GL_PIXEL_UNPACK_BUFFER_EXT* = 0x88EC.GLenum + GL_TEXTURE5_ARB* = 0x84C5.GLenum + GL_UNSIGNED_INT_IMAGE_2D_RECT* = 0x9065.GLenum + GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS* = 0x91BC.GLenum + GL_DEPTH_COMPONENT* = 0x1902.GLenum + GL_RG32F_EXT* = 0x8230.GLenum + GL_FACTOR_ALPHA_MODULATE_IMG* = 0x8C07.GLenum + GL_VERTEX_ARRAY_TYPE_EXT* = 0x807B.GLenum + GL_DS_BIAS_NV* = 0x8716.GLenum + GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI* = 0x1A203.GLenum + GL_ALPHA16UI_EXT* = 0x8D78.GLenum + GL_DOUBLE_VEC2* = 0x8FFC.GLenum + GL_MAP1_VERTEX_ATTRIB12_4_NV* = 0x866C.GLenum + GL_4D_COLOR_TEXTURE* = 0x0604.GLenum + GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS* = 0x90D6.GLenum + GL_SPECULAR* = 0x1202.GLenum + GL_TOP_LEVEL_ARRAY_SIZE* = 0x930C.GLenum + GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB* = 0x919A.GLenum + GL_COVERAGE_SAMPLES_NV* = 0x8ED4.GLenum + GL_SIGNALED_APPLE* = 0x9119.GLenum + GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR* = 0x824D.GLenum + GL_BUFFER_KHR* = 0x82E0.GLenum + GL_GEOMETRY_TEXTURE* = 0x829E.GLenum + GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV* = 0x8E5E.GLenum + GL_EVAL_VERTEX_ATTRIB7_NV* = 0x86CD.GLenum + GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV* = 0x80.GLbitfield + GL_BINORMAL_ARRAY_POINTER_EXT* = 0x8443.GLenum + GL_AUX3* = 0x040C.GLenum + GL_MULTISAMPLE_BIT_EXT* = 0x20000000.GLbitfield + GL_COLOR_TABLE_FORMAT_SGI* = 0x80D8.GLenum + GL_VERTEX_PROGRAM_POINT_SIZE* = 0x8642.GLenum + GL_LINE_WIDTH_GRANULARITY* = 0x0B23.GLenum + GL_MAX_VERTEX_ATTRIB_BINDINGS* = 0x82DA.GLenum + GL_TEXTURE_BINDING_2D_ARRAY_EXT* = 0x8C1D.GLenum + GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST* = 0x82AC.GLenum + GL_SCALE_BY_FOUR_NV* = 0x853F.GLenum + GL_VIRTUAL_PAGE_SIZE_Z_AMD* = 0x9197.GLenum + GL_TEXTURE16* = 0x84D0.GLenum + GL_DSDT8_MAG8_NV* = 0x870A.GLenum + GL_OP_FLOOR_EXT* = 0x878F.GLenum + GL_MAX_PROGRAM_IF_DEPTH_NV* = 0x88F6.GLenum + GL_VERTEX_ARRAY_LIST_IBM* = 103070.GLenum + GL_COMPRESSED_SIGNED_RED_RGTC1* = 0x8DBC.GLenum + GL_CUBIC_CURVE_TO_NV* = 0x0C.GLenum + GL_PROXY_POST_CONVOLUTION_COLOR_TABLE* = 0x80D4.GLenum + GL_SIGNED_IDENTITY_NV* = 0x853C.GLenum + GL_EVAL_VERTEX_ATTRIB6_NV* = 0x86CC.GLenum + GL_MODELVIEW10_ARB* = 0x872A.GLenum + GL_MULTISAMPLE_3DFX* = 0x86B2.GLenum + GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG* = 0x8C00.GLenum + GL_DSDT_MAG_VIB_NV* = 0x86F7.GLenum + GL_TEXCOORD4_BIT_PGI* = 0x80000000.GLbitfield + GL_TRANSFORM_FEEDBACK_BARRIER_BIT* = 0x00000800.GLbitfield + GL_EVAL_VERTEX_ATTRIB10_NV* = 0x86D0.GLenum + GL_DRAW_BUFFER13_ARB* = 0x8832.GLenum + GL_RENDERBUFFER_STENCIL_SIZE_OES* = 0x8D55.GLenum + GL_INTENSITY8I_EXT* = 0x8D91.GLenum + GL_STENCIL_BACK_PASS_DEPTH_FAIL* = 0x8802.GLenum + GL_INTENSITY32F_ARB* = 0x8817.GLenum + GL_CURRENT_ATTRIB_NV* = 0x8626.GLenum + GL_POLYGON_BIT* = 0x00000008.GLbitfield + GL_COMBINE_RGB* = 0x8571.GLenum + GL_MAX_FRAMEBUFFER_HEIGHT* = 0x9316.GLenum + GL_FRAMEBUFFER_BINDING_OES* = 0x8CA6.GLenum + GL_TEXTURE_GREEN_TYPE* = 0x8C11.GLenum + GL_LINE_TO_NV* = 0x04.GLenum + GL_FUNC_ADD_EXT* = 0x8006.GLenum + GL_TEXTURE_LOD_BIAS* = 0x8501.GLenum + GL_QUAD_INTENSITY8_SGIS* = 0x8123.GLenum + GL_SECONDARY_COLOR_ARRAY_EXT* = 0x845E.GLenum + GL_UNPACK_COMPRESSED_SIZE_SGIX* = 0x831A.GLenum + GL_RGBA_INTEGER* = 0x8D99.GLenum + GL_ATOMIC_COUNTER_BUFFER_SIZE* = 0x92C3.GLenum + GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE* = 0x8D56.GLenum + GL_OBJECT_DISTANCE_TO_LINE_SGIS* = 0x81F3.GLenum + GL_DEPTH_BUFFER_BIT3_QCOM* = 0x00000800.GLbitfield + GL_RGB16_SNORM* = 0x8F9A.GLenum + GL_MATRIX_INDEX_ARRAY_TYPE_ARB* = 0x8847.GLenum + GL_TRANSLATE_X_NV* = 0x908E.GLenum + GL_BUFFER_ACCESS_FLAGS* = 0x911F.GLenum + GL_IS_PER_PATCH* = 0x92E7.GLenum + GL_PATH_GEN_MODE_NV* = 0x90B0.GLenum + GL_ALPHA_MIN_CLAMP_INGR* = 0x8563.GLenum + GL_LUMINANCE_ALPHA32I_EXT* = 0x8D87.GLenum + GL_BUFFER_USAGE_ARB* = 0x8765.GLenum + GL_POINT_SIZE* = 0x0B11.GLenum + GL_INVARIANT_EXT* = 0x87C2.GLenum + GL_IMAGE_BINDING_NAME* = 0x8F3A.GLenum + GL_BLEND_SRC_ALPHA* = 0x80CB.GLenum + GL_OUTPUT_TEXTURE_COORD23_EXT* = 0x87B4.GLenum + GL_EYE_PLANE* = 0x2502.GLenum + GL_BOOL_VEC4_ARB* = 0x8B59.GLenum + GL_MITER_REVERT_NV* = 0x90A7.GLenum + GL_SYNC_X11_FENCE_EXT* = 0x90E1.GLenum + GL_GEOMETRY_SHADER_INVOCATIONS* = 0x887F.GLenum + GL_DRAW_BUFFER5_ATI* = 0x882A.GLenum + GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB* = 0x889D.GLenum + GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT* = 0x906B.GLenum + GL_PIXEL_TEX_GEN_Q_ROUND_SGIX* = 0x8185.GLenum + GL_DOUBLE_MAT3x2_EXT* = 0x8F4B.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB* = 0x8516.GLenum + GL_MOV_ATI* = 0x8961.GLenum + GL_COLOR4_BIT_PGI* = 0x00020000.GLbitfield + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR* = 0x93DD.GLenum + GL_DEPTH_BOUNDS_TEST_EXT* = 0x8890.GLenum + GL_DST_OVER_NV* = 0x9289.GLenum + GL_PIXEL_MAP_I_TO_I_SIZE* = 0x0CB0.GLenum + GL_ALPHA16F_EXT* = 0x881C.GLenum + GL_RENDERBUFFER_BINDING_EXT* = 0x8CA7.GLenum + GL_MATRIX25_ARB* = 0x88D9.GLenum + GL_OUTPUT_TEXTURE_COORD19_EXT* = 0x87B0.GLenum + GL_NORMAL_MAP* = 0x8511.GLenum + GL_GPU_ADDRESS_NV* = 0x8F34.GLenum + GL_STREAM_READ* = 0x88E1.GLenum + GL_MIRRORED_REPEAT* = 0x8370.GLint + GL_TEXTURE_SWIZZLE_RGBA* = 0x8E46.GLenum + GL_HALF_BIAS_NORMAL_NV* = 0x853A.GLenum + GL_STENCIL_BACK_OP_VALUE_AMD* = 0x874D.GLenum + GL_TEXTURE_BLUE_TYPE_ARB* = 0x8C12.GLenum + GL_MODELVIEW_PROJECTION_NV* = 0x8629.GLenum + GL_ACTIVE_UNIFORM_MAX_LENGTH* = 0x8B87.GLenum + GL_TEXTURE_SWIZZLE_RGBA_EXT* = 0x8E46.GLenum + GL_TEXTURE_GEN_T* = 0x0C61.GLenum + GL_HILO16_NV* = 0x86F8.GLenum + GL_CURRENT_QUERY_EXT* = 0x8865.GLenum + GL_FLOAT16_VEC2_NV* = 0x8FF9.GLenum + GL_RGBA_FLOAT_MODE_ARB* = 0x8820.GLenum + GL_POINT_SIZE_ARRAY_TYPE_OES* = 0x898A.GLenum + GL_GENERATE_MIPMAP_HINT* = 0x8192.GLenum + GL_1PASS_EXT* = 0x80A1.GLenum + GL_SWIZZLE_STQ_DQ_ATI* = 0x8979.GLenum + GL_VERTICAL_LINE_TO_NV* = 0x08.GLenum + GL_MINMAX* = 0x802E.GLenum + GL_RENDERBUFFER_ALPHA_SIZE_EXT* = 0x8D53.GLenum + GL_DEPTH_COMPONENT32F* = 0x8CAC.GLenum + GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV* = 0x9025.GLenum + GL_CLIP_PLANE5_IMG* = 0x3005.GLenum + GL_TEXTURE_2D_MULTISAMPLE* = 0x9100.GLenum + GL_PREVIOUS* = 0x8578.GLenum + GL_CULL_MODES_NV* = 0x86E0.GLenum + GL_TRACE_ARRAYS_BIT_MESA* = 0x0004.GLbitfield + GL_MAX_ACTIVE_LIGHTS_SGIX* = 0x8405.GLenum + GL_PRIMITIVE_ID_NV* = 0x8C7C.GLenum + GL_DEPTH_COMPONENT16* = 0x81A5.GLenum + GL_FRAMEBUFFER_ATTACHMENT_LAYERED* = 0x8DA7.GLenum + GL_MAX_FRAGMENT_UNIFORM_BLOCKS* = 0x8A2D.GLenum + GL_OUTPUT_COLOR0_EXT* = 0x879B.GLenum + GL_RGBA16F_EXT* = 0x881A.GLenum + GL_MAX_PALETTE_MATRICES_OES* = 0x8842.GLenum + GL_VIEW_CLASS_64_BITS* = 0x82C6.GLenum + GL_TRACE_ALL_BITS_MESA* = 0xFFFF.GLbitfield + GL_REPLACE_VALUE_AMD* = 0x874B.GLenum + GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP* = 0x8163.GLenum + GL_BGR_INTEGER* = 0x8D9A.GLenum + GL_MAX_DEBUG_LOGGED_MESSAGES_ARB* = 0x9144.GLenum + GL_FOG_COLOR* = 0x0B66.GLenum + GL_MAX_MULTIVIEW_BUFFERS_EXT* = 0x90F2.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER* = 0x8C8E.GLenum + GL_E_TIMES_F_NV* = 0x8531.GLenum + GL_COLOR_TABLE_WIDTH_SGI* = 0x80D9.GLenum + GL_VERTEX_ATTRIB_ARRAY_SIZE* = 0x8623.GLenum + GL_422_REV_AVERAGE_EXT* = 0x80CF.GLenum + GL_WRITE_DISCARD_NV* = 0x88BE.GLenum + GL_DRAW_BUFFER0_EXT* = 0x8825.GLenum + GL_FONT_HEIGHT_BIT_NV* = 0x00800000.GLbitfield + GL_INTERLACE_OML* = 0x8980.GLenum + GL_FUNC_REVERSE_SUBTRACT_EXT* = 0x800B.GLenum + GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT* = 0x87C8.GLenum + GL_PRIMARY_COLOR* = 0x8577.GLenum + GL_RGBA16I* = 0x8D88.GLenum + GL_TEXTURE6* = 0x84C6.GLenum + GL_PATH_FILL_BOUNDING_BOX_NV* = 0x90A1.GLenum + GL_WEIGHT_ARRAY_BUFFER_BINDING* = 0x889E.GLenum + GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI* = 0x8835.GLenum + GL_YCRCB_422_SGIX* = 0x81BB.GLenum + GL_RGB5_A1* = 0x8057.GLenum + GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT* = 0x8211.GLenum + GL_DRAW_FRAMEBUFFER_BINDING_EXT* = 0x8CA6.GLenum + GL_TEXTURE_1D_ARRAY* = 0x8C18.GLenum + GL_CLAMP_FRAGMENT_COLOR_ARB* = 0x891B.GLenum + GL_FULL_RANGE_EXT* = 0x87E1.GLenum + GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV* = 0x8DA3.GLenum + GL_CON_24_ATI* = 0x8959.GLenum + GL_2D* = 0x0600.GLenum + GL_DRAW_BUFFER5_NV* = 0x882A.GLenum + GL_PALETTE4_RGBA8_OES* = 0x8B91.GLenum + GL_READ_ONLY_ARB* = 0x88B8.GLenum + GL_NUM_SAMPLE_COUNTS* = 0x9380.GLenum + GL_MATRIX_STRIDE* = 0x92FF.GLenum + GL_HISTOGRAM_RED_SIZE* = 0x8028.GLenum + GL_COLOR_ATTACHMENT4* = 0x8CE4.GLenum + GL_PATH_INITIAL_END_CAP_NV* = 0x9077.GLenum + GL_TEXTURE_USAGE_ANGLE* = 0x93A2.GLenum + GL_DOUBLE_MAT2* = 0x8F46.GLenum + GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE* = 0x8212.GLenum + GL_SECONDARY_COLOR_ARRAY_POINTER* = 0x845D.GLenum + GL_MAX_VIEWPORTS* = 0x825B.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_EXT* = 0x8C8E.GLenum + GL_FRAMEBUFFER_SRGB_EXT* = 0x8DB9.GLenum + GL_STORAGE_SHARED_APPLE* = 0x85BF.GLenum + GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH* = 0x8C76.GLenum + GL_TRANSFORM_FEEDBACK_NV* = 0x8E22.GLenum + GL_MIRRORED_REPEAT_ARB* = 0x8370.GLint + GL_MAX_VERTEX_OUTPUT_COMPONENTS* = 0x9122.GLenum + GL_BUFFER_MAP_LENGTH* = 0x9120.GLenum + GL_BUFFER_OBJECT_APPLE* = 0x85B3.GLenum + GL_INT_VEC4_ARB* = 0x8B55.GLenum + GL_COMBINER3_NV* = 0x8553.GLenum + GL_INT16_VEC3_NV* = 0x8FE6.GLenum + GL_MAX_3D_TEXTURE_SIZE_EXT* = 0x8073.GLenum + GL_GENERATE_MIPMAP_HINT_SGIS* = 0x8192.GLenum + GL_SRC0_ALPHA* = 0x8588.GLenum + GL_IMAGE_2D* = 0x904D.GLenum + GL_VIEW_CLASS_S3TC_DXT1_RGB* = 0x82CC.GLenum + GL_DOT3_RGBA* = 0x86AF.GLenum + GL_TEXTURE_GREEN_SIZE* = 0x805D.GLenum + GL_DOUBLE_MAT2x3* = 0x8F49.GLenum + GL_COORD_REPLACE_OES* = 0x8862.GLenum + GL_MAX_DEBUG_MESSAGE_LENGTH_ARB* = 0x9143.GLenum + GL_TEXTURE_IMMUTABLE_FORMAT_EXT* = 0x912F.GLenum + GL_INDEX_ARRAY_POINTER_EXT* = 0x8091.GLenum + GL_NUM_SHADING_LANGUAGE_VERSIONS* = 0x82E9.GLenum + GL_DEBUG_CALLBACK_FUNCTION_ARB* = 0x8244.GLenum + GL_OFFSET_TEXTURE_MATRIX_NV* = 0x86E1.GLenum + GL_INTENSITY32I_EXT* = 0x8D85.GLenum + GL_BUMP_TEX_UNITS_ATI* = 0x8778.GLenum + GL_RENDERBUFFER* = 0x8D41.GLenum + GL_UPPER_LEFT* = 0x8CA2.GLenum + GL_GUILTY_CONTEXT_RESET_ARB* = 0x8253.GLenum + GL_MAP2_GRID_SEGMENTS* = 0x0DD3.GLenum + GL_REG_23_ATI* = 0x8938.GLenum + GL_UNSIGNED_INT16_NV* = 0x8FF0.GLenum + GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM* = 103084.GLenum + GL_INVARIANT_VALUE_EXT* = 0x87EA.GLenum + GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV* = 0x8C88.GLenum + GL_TEXTURE2_ARB* = 0x84C2.GLenum + GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT* = 0x8DD7.GLenum + GL_IMAGE_CUBE* = 0x9050.GLenum + GL_MAX_PROGRAM_MATRICES_ARB* = 0x862F.GLenum + GL_SIGNED_LUMINANCE8_ALPHA8_NV* = 0x8704.GLenum + GL_INDEX_ARRAY_LIST_IBM* = 103073.GLenum + GL_EVAL_VERTEX_ATTRIB5_NV* = 0x86CB.GLenum + GL_SHADER_SOURCE_LENGTH* = 0x8B88.GLenum + GL_TEXTURE4* = 0x84C4.GLenum + GL_VERTEX_ATTRIB_ARRAY6_NV* = 0x8656.GLenum + GL_PROXY_TEXTURE_1D_STACK_MESAX* = 0x875B.GLenum + GL_MAP_ATTRIB_V_ORDER_NV* = 0x86C4.GLenum + GL_DSDT_NV* = 0x86F5.GLenum + GL_DEBUG_SEVERITY_NOTIFICATION_KHR* = 0x826B.GLenum + GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM* = 103086.GLenum + GL_COMPRESSED_RGBA_ASTC_8x6_KHR* = 0x93B6.GLenum + GL_LINEAR_ATTENUATION* = 0x1208.GLenum + GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV* = 0x9035.GLenum + GL_CONVOLUTION_FILTER_BIAS* = 0x8015.GLenum + GL_IMAGE_MIN_FILTER_HP* = 0x815D.GLenum + GL_EYE_RADIAL_NV* = 0x855B.GLenum + GL_TEXTURE_MIN_LOD_SGIS* = 0x813A.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV* = 0x8C8F.GLenum + GL_TRANSLATE_2D_NV* = 0x9090.GLenum + GL_CONSTANT_ARB* = 0x8576.GLenum + GL_FLOAT_MAT2x3* = 0x8B65.GLenum + GL_MULTISAMPLE_COVERAGE_MODES_NV* = 0x8E12.GLenum + GL_TRANSPOSE_COLOR_MATRIX* = 0x84E6.GLenum + GL_PROGRAM_STRING_NV* = 0x8628.GLenum + GL_UNSIGNED_INT_SAMPLER_1D_EXT* = 0x8DD1.GLenum + GL_BLEND_SRC_ALPHA_OES* = 0x80CB.GLenum + GL_RGB32F_EXT* = 0x8815.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT* = 0x8CD4.GLenum + GL_RESTART_PATH_NV* = 0xF0.GLenum + GL_MAP2_VERTEX_ATTRIB11_4_NV* = 0x867B.GLenum + GL_VIEW_CLASS_16_BITS* = 0x82CA.GLenum + GL_BUFFER_DATA_SIZE* = 0x9303.GLenum + GL_BUFFER_FLUSHING_UNMAP_APPLE* = 0x8A13.GLenum + GL_RELATIVE_VERTICAL_LINE_TO_NV* = 0x09.GLenum + GL_SRGB_WRITE* = 0x8298.GLenum + GL_TEXTURE_LUMINANCE_SIZE_EXT* = 0x8060.GLenum + GL_VERTEX_PRECLIP_SGIX* = 0x83EE.GLenum + GL_LINEAR_DETAIL_COLOR_SGIS* = 0x8099.GLenum + GL_SOURCE2_ALPHA_ARB* = 0x858A.GLenum + GL_PATH_FOG_GEN_MODE_NV* = 0x90AC.GLenum + GL_RGB10_A2UI* = 0x906F.GLenum + GL_MULTISAMPLE_BIT_3DFX* = 0x20000000.GLbitfield + GL_PIXEL_MAP_G_TO_G_SIZE* = 0x0CB7.GLenum + GL_COVERAGE_BUFFER_BIT_NV* = 0x00008000.GLbitfield + GL_TEXTURE_COMPRESSED* = 0x86A1.GLenum + GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER* = 0x92CA.GLenum + GL_NAMED_STRING_TYPE_ARB* = 0x8DEA.GLenum + GL_RESCALE_NORMAL* = 0x803A.GLenum + GL_OUTPUT_TEXTURE_COORD3_EXT* = 0x87A0.GLenum + GL_RENDERBUFFER_EXT* = 0x8D41.GLenum + GL_QUERY_NO_WAIT* = 0x8E14.GLenum + GL_SAMPLE_ALPHA_TO_COVERAGE* = 0x809E.GLenum + GL_RG8UI* = 0x8238.GLenum + GL_MATRIX3_NV* = 0x8633.GLenum + GL_SAMPLE_BUFFERS_ARB* = 0x80A8.GLenum + GL_VERTEX_CONSISTENT_HINT_PGI* = 0x1A22B.GLenum + GL_SPRITE_AXIAL_SGIX* = 0x814C.GLenum + GL_MODELVIEW_MATRIX* = 0x0BA6.GLenum + GL_SAMPLE_PATTERN_SGIS* = 0x80AC.GLenum + GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE* = 0x906B.GLenum + GL_FLOAT_RG16_NV* = 0x8886.GLenum + GL_IMAGE_TRANSLATE_X_HP* = 0x8157.GLenum + GL_FRAMEBUFFER_SRGB* = 0x8DB9.GLenum + GL_DRAW_BUFFER7* = 0x882C.GLenum + GL_CONVOLUTION_BORDER_COLOR* = 0x8154.GLenum + GL_DRAW_BUFFER5* = 0x882A.GLenum + GL_GEOMETRY_INPUT_TYPE_EXT* = 0x8DDB.GLenum + GL_IUI_V2F_EXT* = 0x81AD.GLenum + GL_FLOAT_RG_NV* = 0x8881.GLenum + GL_VERTEX_SHADER_INVARIANTS_EXT* = 0x87D1.GLenum + GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV* = 0x8C4D.GLenum + GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB* = 0x862E.GLenum + GL_SAMPLE_PATTERN_EXT* = 0x80AC.GLenum + GL_DIFFERENCE_NV* = 0x929E.GLenum + GL_POST_CONVOLUTION_ALPHA_BIAS_EXT* = 0x8023.GLenum + GL_COLOR_ATTACHMENT1_EXT* = 0x8CE1.GLenum + GL_TEXTURE_ALPHA_MODULATE_IMG* = 0x8C06.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV* = 0x8E23.GLenum + GL_MAX_TEXTURE_IMAGE_UNITS_ARB* = 0x8872.GLenum + GL_FIXED_OES* = 0x140C.GLenum + GL_ALREADY_SIGNALED_APPLE* = 0x911A.GLenum + GL_SET* = 0x150F.GLenum + GL_PERFMON_RESULT_AMD* = 0x8BC6.GLenum + GL_VARIABLE_G_NV* = 0x8529.GLenum + GL_DRAW_FRAMEBUFFER_ANGLE* = 0x8CA9.GLenum + GL_GEOMETRY_SUBROUTINE_UNIFORM* = 0x92F1.GLenum + GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT* = 0x884E.GLenum + GL_POINT* = 0x1B00.GLenum + GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV* = 0x01000000.GLbitfield + GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS* = 0x90CB.GLenum + GL_PLUS_CLAMPED_ALPHA_NV* = 0x92B2.GLenum + GL_DRAW_BUFFER3_ATI* = 0x8828.GLenum + GL_LUMINANCE_ALPHA16I_EXT* = 0x8D8D.GLenum + GL_SUBPIXEL_BITS* = 0x0D50.GLenum + GL_POINT_SPRITE* = 0x8861.GLenum + GL_DRAW_BUFFER0* = 0x8825.GLenum + GL_DEPTH_BIAS* = 0x0D1F.GLenum + GL_COLOR_ARRAY_TYPE* = 0x8082.GLenum + GL_DEPENDENT_GB_TEXTURE_2D_NV* = 0x86EA.GLenum + GL_MAX_SAMPLES_ANGLE* = 0x8D57.GLenum + GL_ALLOW_DRAW_MEM_HINT_PGI* = 0x1A211.GLenum + GL_GEOMETRY_OUTPUT_TYPE* = 0x8918.GLenum + GL_MAX_DEBUG_LOGGED_MESSAGES_KHR* = 0x9144.GLenum + GL_VERTEX_ATTRIB_ARRAY0_NV* = 0x8650.GLenum + GL_PRIMITIVES_GENERATED_EXT* = 0x8C87.GLenum + GL_TEXTURE_FLOAT_COMPONENTS_NV* = 0x888C.GLenum + GL_CLIP_VOLUME_CLIPPING_HINT_EXT* = 0x80F0.GLenum + GL_FRAGMENT_PROGRAM_POSITION_MESA* = 0x8BB0.GLenum + GL_MAX_FRAGMENT_IMAGE_UNIFORMS* = 0x90CE.GLenum + GL_VERTEX_ARRAY_BINDING_APPLE* = 0x85B5.GLenum + GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV* = 0x00000010.GLbitfield + GL_FIRST_VERTEX_CONVENTION* = 0x8E4D.GLenum + GL_DECR_WRAP* = 0x8508.GLenum + GL_IMAGE_CLASS_1_X_32* = 0x82BB.GLenum + GL_MAX_CLIP_PLANES_IMG* = 0x0D32.GLenum + GL_MAX_VARYING_COMPONENTS* = 0x8B4B.GLenum + GL_POST_COLOR_MATRIX_RED_BIAS_SGI* = 0x80B8.GLenum + GL_DSDT_MAG_NV* = 0x86F6.GLenum + GL_DEBUG_SOURCE_APPLICATION* = 0x824A.GLenum + GL_OPERAND0_RGB_ARB* = 0x8590.GLenum + GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE* = 0x82AE.GLenum + GL_VIDEO_COLOR_CONVERSION_MATRIX_NV* = 0x9029.GLenum + GL_MAP2_VERTEX_ATTRIB13_4_NV* = 0x867D.GLenum + GL_DOT2_ADD_ATI* = 0x896C.GLenum + GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS* = 0x8A33.GLenum + GL_IMAGE_BINDING_LAYER_EXT* = 0x8F3D.GLenum + GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX* = 0x8402.GLenum + GL_PACK_IMAGE_DEPTH_SGIS* = 0x8131.GLenum + GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT* = 0x8DDF.GLenum + GL_Z_EXT* = 0x87D7.GLenum + GL_MAP1_VERTEX_ATTRIB15_4_NV* = 0x866F.GLenum + GL_RG8_SNORM* = 0x8F95.GLenum + GL_OUTPUT_TEXTURE_COORD5_EXT* = 0x87A2.GLenum + GL_TEXTURE_BINDING_1D_ARRAY_EXT* = 0x8C1C.GLenum + GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB* = 0x8B87.GLenum + GL_PATH_END_CAPS_NV* = 0x9076.GLenum + GL_COLOR_TABLE_GREEN_SIZE* = 0x80DB.GLenum + GL_MAX_ELEMENTS_INDICES_EXT* = 0x80E9.GLenum + GL_TEXTURE_IMMUTABLE_FORMAT* = 0x912F.GLenum + GL_WRITE_ONLY_ARB* = 0x88B9.GLenum + GL_COLOR_ATTACHMENT10_EXT* = 0x8CEA.GLenum + GL_INVERT_RGB_NV* = 0x92A3.GLenum + GL_CURRENT_RASTER_DISTANCE* = 0x0B09.GLenum + GL_DEPTH_STENCIL_TO_RGBA_NV* = 0x886E.GLenum + GL_INVERTED_SCREEN_W_REND* = 0x8491.GLenum + GL_TABLE_TOO_LARGE* = 0x8031.GLenum + GL_REG_16_ATI* = 0x8931.GLenum + GL_BLEND_EQUATION_ALPHA_OES* = 0x883D.GLenum + GL_DRAW_FRAMEBUFFER_BINDING_NV* = 0x8CA6.GLenum + GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS* = 0x8E47.GLenum + GL_TEXTURE_BLUE_SIZE_EXT* = 0x805E.GLenum + GL_TEXTURE_BORDER_VALUES_NV* = 0x871A.GLenum + GL_PROGRAM_LENGTH_ARB* = 0x8627.GLenum + GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV* = 0x909C.GLenum + GL_DOT_PRODUCT_NV* = 0x86EC.GLenum + GL_TRANSPOSE_PROJECTION_MATRIX_ARB* = 0x84E4.GLenum + GL_TEXTURE_2D_MULTISAMPLE_ARRAY* = 0x9102.GLenum + GL_MIN_PROGRAM_TEXEL_OFFSET_NV* = 0x8904.GLenum + GL_MAP2_BINORMAL_EXT* = 0x8447.GLenum + GL_COLOR_ARRAY_BUFFER_BINDING* = 0x8898.GLenum + GL_TEXTURE_COORD_ARRAY_POINTER* = 0x8092.GLenum + GL_TEXTURE4_ARB* = 0x84C4.GLenum + GL_VARIABLE_A_NV* = 0x8523.GLenum + GL_CURRENT_FOG_COORDINATE_EXT* = 0x8453.GLenum + GL_TEXTURE_CUBE_MAP_POSITIVE_X* = 0x8515.GLenum + GL_DEPENDENT_AR_TEXTURE_2D_NV* = 0x86E9.GLenum + GL_TEXTURE29_ARB* = 0x84DD.GLenum + GL_INVERSE_TRANSPOSE_NV* = 0x862D.GLenum + GL_TEXTURE_COLOR_WRITEMASK_SGIS* = 0x81EF.GLenum + GL_HISTOGRAM_SINK* = 0x802D.GLenum + GL_ALPHA12_EXT* = 0x803D.GLenum + GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX* = 0x8175.GLenum + GL_DSDT_MAG_INTENSITY_NV* = 0x86DC.GLenum + GL_ATC_RGB_AMD* = 0x8C92.GLenum + GL_PROGRAM_ATTRIB_COMPONENTS_NV* = 0x8906.GLenum + GL_UNIFORM_BLOCK_BINDING* = 0x8A3F.GLenum + GL_POLYGON_STIPPLE* = 0x0B42.GLenum + GL_BACK* = 0x0405.GLenum + GL_DEPTH_COMPONENT16_NONLINEAR_NV* = 0x8E2C.GLenum + GL_ALPHA32F_EXT* = 0x8816.GLenum + GL_CLAMP_TO_BORDER* = 0x812D.GLint + GL_FLOAT_RGBA16_NV* = 0x888A.GLenum + GL_VERTEX_ARRAY_RANGE_LENGTH_NV* = 0x851E.GLenum + GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV* = 0x8E58.GLenum + GL_SAMPLER_2D* = 0x8B5E.GLenum + GL_SMOOTH_POINT_SIZE_RANGE* = 0x0B12.GLenum + GL_DEPTH_PASS_INSTRUMENT_MAX_SGIX* = 0x8312.GLenum + GL_INTERPOLATE_ARB* = 0x8575.GLenum + GL_VERTEX_ARRAY_LENGTH_NV* = 0x8F2B.GLenum + GL_FUNC_SUBTRACT_EXT* = 0x800A.GLenum + GL_OUTPUT_TEXTURE_COORD14_EXT* = 0x87AB.GLenum + GL_HISTOGRAM_SINK_EXT* = 0x802D.GLenum + GL_RG_EXT* = 0x8227.GLenum + GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS* = 0x80B0.GLenum + GL_COLOR_TABLE_SCALE* = 0x80D6.GLenum + GL_CURRENT_RASTER_TEXTURE_COORDS* = 0x0B06.GLenum + GL_PIXEL_BUFFER_BARRIER_BIT* = 0x00000080.GLbitfield + GL_SHADING_LANGUAGE_VERSION* = 0x8B8C.GLenum + GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES* = 0x898F.GLenum + GL_DUAL_LUMINANCE_ALPHA4_SGIS* = 0x811C.GLenum + GL_CLAMP* = 0x2900.GLint + GL_4PASS_2_EXT* = 0x80A6.GLenum + GL_POLYGON_OFFSET_LINE* = 0x2A02.GLenum + GL_LOGIC_OP* = 0x0BF1.GLenum + GL_RENDERBUFFER_HEIGHT* = 0x8D43.GLenum + GL_COPY_INVERTED* = 0x150C.GLenum + GL_NONE* = 0.GLenum + GL_COLOR_ENCODING* = 0x8296.GLenum + GL_ONE_MINUS_CONSTANT_ALPHA_EXT* = 0x8004.GLenum + GL_DEBUG_TYPE_ERROR_KHR* = 0x824C.GLenum + GL_PIXEL_TILE_GRID_WIDTH_SGIX* = 0x8142.GLenum + GL_UNIFORM_SIZE* = 0x8A38.GLenum + GL_VERTEX_SHADER_BINDING_EXT* = 0x8781.GLenum + GL_BLEND_DST_RGB_EXT* = 0x80C8.GLenum + GL_QUADS* = 0x0007.GLenum + cGL_INT* = 0x1404.GLenum + GL_PIXEL_TEX_GEN_MODE_SGIX* = 0x832B.GLenum + GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB* = 0x8E8F.GLenum + GL_SAMPLE_ALPHA_TO_ONE_ARB* = 0x809F.GLenum + GL_RGBA32F_EXT* = 0x8814.GLenum + GL_VERTEX_PROGRAM_POSITION_MESA* = 0x8BB4.GLenum + GL_GEOMETRY_SUBROUTINE* = 0x92EB.GLenum + GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT* = 0x8DD6.GLenum + GL_IMAGE_BINDING_LAYER* = 0x8F3D.GLenum + GL_PIXEL_PACK_BUFFER_ARB* = 0x88EB.GLenum + GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER* = 0x84F1.GLenum + GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB* = 0x8623.GLenum + GL_ALPHA8UI_EXT* = 0x8D7E.GLenum + GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV* = 0x11.GLenum + GL_CAVEAT_SUPPORT* = 0x82B8.GLenum + GL_ACCUM* = 0x0100.GLenum + GL_DRAW_BUFFER3_NV* = 0x8828.GLenum + GL_DEBUG_TYPE_OTHER_KHR* = 0x8251.GLenum + GL_TESS_GEN_SPACING* = 0x8E77.GLenum + GL_FLOAT_MAT4x2* = 0x8B69.GLenum + GL_TEXTURE_GEN_STR_OES* = 0x8D60.GLenum + GL_NUM_COMPATIBLE_SUBROUTINES* = 0x8E4A.GLenum + GL_CLIP_DISTANCE1* = 0x3001.GLenum + GL_DEPTH_COMPONENT32_SGIX* = 0x81A7.GLenum + GL_FRAMEZOOM_SGIX* = 0x818B.GLenum + GL_COLOR_ATTACHMENT14_EXT* = 0x8CEE.GLenum + GL_POLYGON_TOKEN* = 0x0703.GLenum + GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE* = 0x8213.GLenum + GL_DRAW_BUFFER2_EXT* = 0x8827.GLenum + GL_MATRIX_INDEX_ARRAY_TYPE_OES* = 0x8847.GLenum + GL_HISTOGRAM_LUMINANCE_SIZE_EXT* = 0x802C.GLenum + GL_DEPTH_BOUNDS_EXT* = 0x8891.GLenum + GL_TEXTURE24* = 0x84D8.GLenum + GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES* = 0x8A43.GLenum + GL_MAX_PATCH_VERTICES* = 0x8E7D.GLenum + GL_COMPILE_STATUS* = 0x8B81.GLenum + GL_MODELVIEW4_ARB* = 0x8724.GLenum + GL_SHADER_BINARY_VIV* = 0x8FC4.GLenum + GL_CON_10_ATI* = 0x894B.GLenum + GL_FRAGMENT_LIGHT5_SGIX* = 0x8411.GLenum + GL_CONVOLUTION_1D_EXT* = 0x8010.GLenum + GL_CONSTANT_BORDER_HP* = 0x8151.GLenum + GL_SAMPLE_BUFFERS* = 0x80A8.GLenum + GL_RGB8UI* = 0x8D7D.GLenum + GL_FRAGMENT_MATERIAL_EXT* = 0x8349.GLenum + GL_OP_RECIP_EXT* = 0x8794.GLenum + GL_SHADER_OPERATION_NV* = 0x86DF.GLenum + GL_COMPUTE_SUBROUTINE_UNIFORM* = 0x92F3.GLenum + GL_VIDEO_BUFFER_PITCH_NV* = 0x9028.GLenum + GL_UNKNOWN_CONTEXT_RESET_ARB* = 0x8255.GLenum + GL_COLOR_ATTACHMENT3_EXT* = 0x8CE3.GLenum + GL_QUERY_WAIT* = 0x8E13.GLenum + GL_SOURCE1_RGB* = 0x8581.GLenum + GL_DELETE_STATUS* = 0x8B80.GLenum + GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB* = 0x8243.GLenum + GL_HILO8_NV* = 0x885E.GLenum + GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT* = 0x906A.GLenum + GL_LUMINANCE_ALPHA_FLOAT16_APPLE* = 0x881F.GLenum + GL_LUMINANCE16_SNORM* = 0x9019.GLenum + GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX* = 0x8178.GLenum + GL_RENDER* = 0x1C00.GLenum + GL_RED_INTEGER* = 0x8D94.GLenum + GL_DEBUG_TYPE_ERROR_ARB* = 0x824C.GLenum + GL_IMAGE_BINDING_ACCESS* = 0x8F3E.GLenum + GL_COVERAGE_COMPONENT_NV* = 0x8ED0.GLenum + GL_TEXTURE_BINDING_BUFFER_EXT* = 0x8C2C.GLenum + GL_MAX_PROGRAM_PATCH_ATTRIBS_NV* = 0x86D8.GLenum + GL_DUAL_LUMINANCE12_SGIS* = 0x8116.GLenum + GL_QUAD_ALPHA8_SGIS* = 0x811F.GLenum + GL_COMPRESSED_RED_GREEN_RGTC2_EXT* = 0x8DBD.GLenum + GL_PACK_INVERT_MESA* = 0x8758.GLenum + GL_OUTPUT_TEXTURE_COORD11_EXT* = 0x87A8.GLenum + GL_DYNAMIC_DRAW_ARB* = 0x88E8.GLenum + GL_RGB565_OES* = 0x8D62.GLenum + GL_LINE* = 0x1B01.GLenum + GL_T2F_V3F* = 0x2A27.GLenum + GL_DIFFUSE* = 0x1201.GLenum + GL_FOG_COORDINATE_SOURCE* = 0x8450.GLenum + GL_TEXTURE_1D_ARRAY_EXT* = 0x8C18.GLenum + GL_TEXTURE_RECTANGLE_NV* = 0x84F5.GLenum + GL_STENCIL_INDEX4_EXT* = 0x8D47.GLenum + GL_VERTEX_PROGRAM_TWO_SIDE* = 0x8643.GLenum + GL_REDUCE* = 0x8016.GLenum + GL_DEBUG_CALLBACK_USER_PARAM_KHR* = 0x8245.GLenum + GL_DEBUG_LOGGED_MESSAGES_AMD* = 0x9145.GLenum + GL_FONT_UNITS_PER_EM_BIT_NV* = 0x00100000.GLbitfield + GL_INVALID_FRAMEBUFFER_OPERATION_EXT* = 0x0506.GLenum + GL_NORMAL_ARRAY_BUFFER_BINDING_ARB* = 0x8897.GLenum + GL_SAMPLE_MASK_INVERT_SGIS* = 0x80AB.GLenum + GL_MAX_SHADER_BUFFER_ADDRESS_NV* = 0x8F35.GLenum + GL_PIXEL_MAP_I_TO_A* = 0x0C75.GLenum + GL_MINOR_VERSION* = 0x821C.GLenum + GL_TEXTURE_BUFFER_EXT* = 0x8C2A.GLenum + GL_SKIP_COMPONENTS4_NV* = -3 + GL_FLOAT16_NV* = 0x8FF8.GLenum + GL_FEEDBACK_BUFFER_TYPE* = 0x0DF2.GLenum + GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT* = 0x8C72.GLenum + GL_REG_6_ATI* = 0x8927.GLenum + GL_EDGE_FLAG_ARRAY_LIST_IBM* = 103075.GLenum + GL_MATRIX26_ARB* = 0x88DA.GLenum + GL_ALPHA16* = 0x803E.GLenum + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME* = 0x8CD1.GLenum + GL_HISTOGRAM_ALPHA_SIZE* = 0x802B.GLenum + GL_COLOR_MATRIX_STACK_DEPTH* = 0x80B2.GLenum + GL_INTERNALFORMAT_GREEN_TYPE* = 0x8279.GLenum + GL_YCRCBA_SGIX* = 0x8319.GLenum + GL_VIEW_CLASS_48_BITS* = 0x82C7.GLenum + GL_VERTEX_ATTRIB_ARRAY3_NV* = 0x8653.GLenum + GL_CLIENT_STORAGE_BIT* = 0x0200.GLbitfield + GL_MIN_SAMPLE_SHADING_VALUE_ARB* = 0x8C37.GLenum + GL_PROXY_TEXTURE_CUBE_MAP* = 0x851B.GLenum + GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES* = 0x8F39.GLenum + GL_TEXTURE15* = 0x84CF.GLenum + GL_COLOR* = 0x1800.GLenum + GL_LIGHT1* = 0x4001.GLenum + GL_LUMINANCE_ALPHA16F_EXT* = 0x881F.GLenum + GL_TEXTURE_VIEW_NUM_LAYERS* = 0x82DE.GLenum + GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS* = 0x8E82.GLenum + GL_INTERLEAVED_ATTRIBS_NV* = 0x8C8C.GLenum + GL_INT_SAMPLER_BUFFER_EXT* = 0x8DD0.GLenum + GL_EVAL_VERTEX_ATTRIB14_NV* = 0x86D4.GLenum + GL_FRAGMENT_PROGRAM_CALLBACK_MESA* = 0x8BB1.GLenum + GL_EMISSION* = 0x1600.GLenum + GL_WEIGHT_ARRAY_STRIDE_ARB* = 0x86AA.GLenum + GL_ACTIVE_VARIABLES* = 0x9305.GLenum + GL_TIMEOUT_IGNORED* = 0xFFFFFFFFFFFFFFFF.GLenum + GL_VERTEX_STREAM5_ATI* = 0x8771.GLenum + GL_INDEX_ARRAY_POINTER* = 0x8091.GLenum + GL_POST_COLOR_MATRIX_ALPHA_SCALE* = 0x80B7.GLenum + GL_TESS_CONTROL_SHADER* = 0x8E88.GLenum + GL_POLYGON_MODE* = 0x0B40.GLenum + GL_ASYNC_DRAW_PIXELS_SGIX* = 0x835D.GLenum + GL_RGBA16_SNORM* = 0x8F9B.GLenum + GL_TEXTURE_NORMAL_EXT* = 0x85AF.GLenum + GL_REG_22_ATI* = 0x8937.GLenum + GL_FRAMEBUFFER_DEFAULT_WIDTH* = 0x9310.GLenum + GL_TEXCOORD1_BIT_PGI* = 0x10000000.GLbitfield + GL_REFERENCE_PLANE_EQUATION_SGIX* = 0x817E.GLenum + GL_COLOR_ALPHA_PAIRING_ATI* = 0x8975.GLenum + GL_SINGLE_COLOR* = 0x81F9.GLenum + GL_MODELVIEW21_ARB* = 0x8735.GLenum + GL_FORMAT_SUBSAMPLE_24_24_OML* = 0x8982.GLenum + GL_SOURCE1_ALPHA* = 0x8589.GLenum + GL_LINEARLIGHT_NV* = 0x92A7.GLenum + GL_REG_2_ATI* = 0x8923.GLenum + GL_QUERY_RESULT_AVAILABLE* = 0x8867.GLenum + GL_PERSPECTIVE_CORRECTION_HINT* = 0x0C50.GLenum + GL_COMBINE_ALPHA_ARB* = 0x8572.GLenum + GL_HISTOGRAM_ALPHA_SIZE_EXT* = 0x802B.GLenum + GL_SIGNED_RGB8_NV* = 0x86FF.GLenum + GL_DEPTH_TEXTURE_MODE_ARB* = 0x884B.GLenum + GL_PRESENT_DURATION_NV* = 0x8E2B.GLenum + GL_TRIANGLES_ADJACENCY_ARB* = 0x000C.GLenum + GL_TEXTURE_BUFFER_OFFSET* = 0x919D.GLenum + GL_PROGRAM_STRING_ARB* = 0x8628.GLenum + GL_UNSIGNED_INT_IMAGE_1D_EXT* = 0x9062.GLenum + GL_COLOR_ATTACHMENT2* = 0x8CE2.GLenum + GL_DOT_PRODUCT_TEXTURE_2D_NV* = 0x86EE.GLenum + GL_QUERY_BUFFER* = 0x9192.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z* = 0x851A.GLenum + GL_PIXEL_TEX_GEN_ALPHA_REPLACE_SGIX* = 0x8187.GLenum + GL_FULL_SUPPORT* = 0x82B7.GLenum + GL_MAX_PROGRAM_ENV_PARAMETERS_ARB* = 0x88B5.GLenum + GL_MAX_COMPUTE_WORK_GROUP_COUNT* = 0x91BE.GLenum + GL_DEBUG_TYPE_PERFORMANCE* = 0x8250.GLenum + GL_DRAW_BUFFER12_EXT* = 0x8831.GLenum + GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD* = 0x9003.GLenum + GL_CURRENT_FOG_COORDINATE* = 0x8453.GLenum + GL_INTENSITY_EXT* = 0x8049.GLenum + GL_TRANSPOSE_NV* = 0x862C.GLenum + GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV* = 0x8C4F.GLenum + GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS* = 0x8C80.GLenum + GL_COLOR_ARRAY_POINTER_EXT* = 0x8090.GLenum + GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT* = 0x8C2D.GLenum + GL_GEOMETRY_VERTICES_OUT_ARB* = 0x8DDA.GLenum + GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV* = 0x0F.GLenum + GL_OP_INDEX_EXT* = 0x8782.GLenum + GL_REG_1_ATI* = 0x8922.GLenum + GL_OFFSET* = 0x92FC.GLenum + GL_PATH_COVER_DEPTH_FUNC_NV* = 0x90BF.GLenum + GL_UNPACK_COMPRESSED_BLOCK_DEPTH* = 0x9129.GLenum + GL_POLYGON_OFFSET_UNITS* = 0x2A00.GLenum + GL_INDEX_TEST_FUNC_EXT* = 0x81B6.GLenum + GL_POINT_SMOOTH* = 0x0B10.GLenum + GL_SCALEBIAS_HINT_SGIX* = 0x8322.GLenum + GL_COMPRESSED_RGBA_ASTC_5x4_KHR* = 0x93B1.GLenum + GL_SEPARATE_SPECULAR_COLOR* = 0x81FA.GLenum + GL_VERTEX_ATTRIB_ARRAY14_NV* = 0x865E.GLenum + GL_INTENSITY16_EXT* = 0x804D.GLenum + GL_R8_SNORM* = 0x8F94.GLenum + GL_DEBUG_LOGGED_MESSAGES* = 0x9145.GLenum + GL_ALPHA8I_EXT* = 0x8D90.GLenum + GL_OPERAND2_RGB* = 0x8592.GLenum + GL_EMBOSS_LIGHT_NV* = 0x855D.GLenum + GL_EDGE_FLAG_ARRAY_STRIDE_EXT* = 0x808C.GLenum + GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV* = 0x88FD.GLenum + GL_NUM_LOOPBACK_COMPONENTS_ATI* = 0x8974.GLenum + GL_DEBUG_SOURCE_APPLICATION_KHR* = 0x824A.GLenum + GL_COMPRESSED_RGB_S3TC_DXT1_EXT* = 0x83F0.GLenum + GL_DEBUG_SOURCE_OTHER_ARB* = 0x824B.GLenum + cGL_DOUBLE* = 0x140A.GLenum + GL_STENCIL_TEST_TWO_SIDE_EXT* = 0x8910.GLenum + GL_MIN_PROGRAM_TEXEL_OFFSET* = 0x8904.GLenum + GL_3DC_X_AMD* = 0x87F9.GLenum + GL_FLOAT_RGB32_NV* = 0x8889.GLenum + GL_SECONDARY_COLOR_ARRAY_POINTER_EXT* = 0x845D.GLenum + GL_OPERAND2_ALPHA_ARB* = 0x859A.GLenum + GL_IMAGE_3D* = 0x904E.GLenum + GL_SECONDARY_COLOR_ARRAY_SIZE* = 0x845A.GLenum + GL_RELEASED_APPLE* = 0x8A19.GLenum + GL_RENDER_DIRECT_TO_FRAMEBUFFER_QCOM* = 0x8FB3.GLenum + GL_FRAMEBUFFER_DEFAULT_LAYERS* = 0x9312.GLenum + GL_INTENSITY* = 0x8049.GLenum + GL_RENDERBUFFER_BLUE_SIZE_OES* = 0x8D52.GLenum + GL_FLOAT_RGB_NV* = 0x8882.GLenum + GL_ARRAY_ELEMENT_LOCK_FIRST_EXT* = 0x81A8.GLenum + GL_CON_4_ATI* = 0x8945.GLenum + GL_ROUND_NV* = 0x90A4.GLenum + GL_CLIP_DISTANCE2* = 0x3002.GLenum + GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB* = 0x880B.GLenum + GL_PROGRAM_ERROR_STRING_ARB* = 0x8874.GLenum + GL_STORAGE_CACHED_APPLE* = 0x85BE.GLenum + GL_LIGHTEN_NV* = 0x9298.GLenum + GL_TEXTURE23* = 0x84D7.GLenum + GL_SAMPLER_CUBE_SHADOW* = 0x8DC5.GLenum + GL_VERTEX_PROGRAM_ARB* = 0x8620.GLenum + GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT* = 0x8C4E.GLenum + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB* = 0x851A.GLenum + GL_RENDERBUFFER_SAMPLES* = 0x8CAB.GLenum + GL_RENDERBUFFER_STENCIL_SIZE* = 0x8D55.GLenum + GL_VIRTUAL_PAGE_SIZE_INDEX_ARB* = 0x91A7.GLenum + GL_CLIP_PLANE5* = 0x3005.GLenum + GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT* = 0x8510.GLenum + GL_COLOR_BUFFER_BIT5_QCOM* = 0x00000020.GLbitfield + GL_DOUBLE_MAT2x3_EXT* = 0x8F49.GLenum + GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS* = 0x8A42.GLenum + GL_COLOR_ATTACHMENT8_EXT* = 0x8CE8.GLenum + GL_UNIFORM_BUFFER_BINDING_EXT* = 0x8DEF.GLenum + GL_MATRIX8_ARB* = 0x88C8.GLenum + GL_COUNTER_TYPE_AMD* = 0x8BC0.GLenum + GL_INT8_VEC3_NV* = 0x8FE2.GLenum + GL_TEXTURE_BINDING_3D_OES* = 0x806A.GLenum + GL_DEPTH_PASS_INSTRUMENT_COUNTERS_SGIX* = 0x8311.GLenum + GL_IMAGE_BINDING_LEVEL* = 0x8F3B.GLenum + GL_STENCIL_BACK_FAIL_ATI* = 0x8801.GLenum + GL_TRANSFORM_FEEDBACK_ATTRIBS_NV* = 0x8C7E.GLenum + GL_COLOR_TABLE_INTENSITY_SIZE* = 0x80DF.GLenum + GL_TEXTURE_2D_BINDING_EXT* = 0x8069.GLenum + GL_CW* = 0x0900.GLenum + GL_COLOR_ATTACHMENT6* = 0x8CE6.GLenum + GL_R32UI* = 0x8236.GLenum + GL_PROXY_TEXTURE_3D* = 0x8070.GLenum + GL_FLOAT_VEC2_ARB* = 0x8B50.GLenum + GL_C3F_V3F* = 0x2A24.GLenum + GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV* = 0x8DA0.GLenum + GL_EVAL_VERTEX_ATTRIB11_NV* = 0x86D1.GLenum + GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV* = 0x8520.GLenum + GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_OES* = 0x8CDC.GLenum + GL_MAX_VIEWPORT_DIMS* = 0x0D3A.GLenum + GL_STENCIL_CLEAR_TAG_VALUE_EXT* = 0x88F3.GLenum + GL_TEXTURE_BUFFER_FORMAT_ARB* = 0x8C2E.GLenum + GL_PROGRAM_NATIVE_PARAMETERS_ARB* = 0x88AA.GLenum + GL_FLOAT_MAT3x2* = 0x8B67.GLenum + GL_BLUE_BIT_ATI* = 0x00000004.GLbitfield + GL_COLOR_ATTACHMENT6_NV* = 0x8CE6.GLenum + GL_AND_INVERTED* = 0x1504.GLenum + GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS* = 0x90D7.GLenum + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR* = 0x93D0.GLenum + GL_PACK_COMPRESSED_BLOCK_DEPTH* = 0x912D.GLenum + GL_TEXTURE_COMPARE_SGIX* = 0x819A.GLenum + GL_SYNC_CL_EVENT_COMPLETE_ARB* = 0x8241.GLenum + GL_DEBUG_TYPE_PORTABILITY* = 0x824F.GLenum + GL_IMAGE_BINDING_FORMAT* = 0x906E.GLenum + GL_RESAMPLE_DECIMATE_OML* = 0x8989.GLenum + GL_MAX_PROGRAM_TEMPORARIES_ARB* = 0x88A5.GLenum + GL_ALL_SHADER_BITS* = 0xFFFFFFFF.GLbitfield + GL_TRANSFORM_FEEDBACK_VARYING* = 0x92F4.GLenum + GL_TRANSFORM_FEEDBACK_BUFFER_BINDING* = 0x8C8F.GLenum + GL_ACTIVE_STENCIL_FACE_EXT* = 0x8911.GLenum + GL_MAP1_VERTEX_ATTRIB4_4_NV* = 0x8664.GLenum + GL_LINK_STATUS* = 0x8B82.GLenum + GL_SYNC_FLUSH_COMMANDS_BIT* = 0x00000001.GLbitfield + GL_BLEND* = 0x0BE2.GLenum + GL_OUTPUT_TEXTURE_COORD12_EXT* = 0x87A9.GLenum + GL_DRAW_BUFFER11_ARB* = 0x8830.GLenum + GL_OBJECT_BUFFER_USAGE_ATI* = 0x8765.GLenum + GL_COLORDODGE_NV* = 0x9299.GLenum + GL_SHADER_IMAGE_LOAD* = 0x82A4.GLenum + GL_EMBOSS_CONSTANT_NV* = 0x855E.GLenum + GL_MAP_TESSELLATION_NV* = 0x86C2.GLenum + GL_MAX_DRAW_BUFFERS_EXT* = 0x8824.GLenum + GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT* = 0x850E.GLenum + GL_TEXTURE_ENV_COLOR* = 0x2201.GLenum + GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER* = 0x8A46.GLenum + GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV* = 0x86F2.GLenum + GL_QUERY_KHR* = 0x82E3.GLenum + GL_RG* = 0x8227.GLenum + GL_MAX_TEXTURE_SIZE* = 0x0D33.GLenum + GL_TEXTURE_NUM_LEVELS_QCOM* = 0x8BD9.GLenum + GL_MAP2_VERTEX_ATTRIB3_4_NV* = 0x8673.GLenum + GL_LUMINANCE_FLOAT32_APPLE* = 0x8818.GLenum + GL_MAP2_VERTEX_ATTRIB7_4_NV* = 0x8677.GLenum + GL_GEOMETRY_SHADER_ARB* = 0x8DD9.GLenum + GL_SYNC_FENCE_APPLE* = 0x9116.GLenum + GL_SAMPLE_MASK_VALUE* = 0x8E52.GLenum + GL_PROXY_TEXTURE_RECTANGLE_NV* = 0x84F7.GLenum + GL_DEPTH_FUNC* = 0x0B74.GLenum + GL_S* = 0x2000.GLenum + GL_CONSTANT_COLOR_EXT* = 0x8001.GLenum + GL_MAX_PROGRAM_LOOP_COUNT_NV* = 0x88F8.GLenum + GL_VIEW_COMPATIBILITY_CLASS* = 0x82B6.GLenum + GL_INT_SAMPLER_BUFFER_AMD* = 0x9002.GLenum + GL_COMPRESSED_SRGB* = 0x8C48.GLenum + GL_PROGRAM_SEPARABLE_EXT* = 0x8258.GLenum + GL_FOG_FUNC_POINTS_SGIS* = 0x812B.GLenum + GL_MITER_TRUNCATE_NV* = 0x90A8.GLenum + GL_POLYGON_OFFSET_POINT* = 0x2A01.GLenum + GL_SRGB_READ* = 0x8297.GLenum + GL_INDEX_ARRAY_ADDRESS_NV* = 0x8F24.GLenum + GL_MAX_FRAMEBUFFER_WIDTH* = 0x9315.GLenum + GL_COMPRESSED_RED_RGTC1_EXT* = 0x8DBB.GLenum + GL_RGB_INTEGER_EXT* = 0x8D98.GLenum + GL_OP_NEGATE_EXT* = 0x8783.GLenum + GL_POINT_SIZE_MAX_ARB* = 0x8127.GLenum + GL_TEXTURE_DEFORMATION_BIT_SGIX* = 0x00000001.GLbitfield + GL_SIGNED_LUMINANCE8_NV* = 0x8702.GLenum + GL_OPERAND2_RGB_EXT* = 0x8592.GLenum + GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT* = 0x8337.GLenum + GL_RECIP_ADD_SIGNED_ALPHA_IMG* = 0x8C05.GLenum + GL_VERTEX_STREAM7_ATI* = 0x8773.GLenum + GL_MODELVIEW1_STACK_DEPTH_EXT* = 0x8502.GLenum + GL_DYNAMIC_DRAW* = 0x88E8.GLenum + GL_DRAW_BUFFER15_EXT* = 0x8834.GLenum + GL_TEXTURE_COMPARE_OPERATOR_SGIX* = 0x819B.GLenum + GL_SQUARE_NV* = 0x90A3.GLenum + GL_COMPRESSED_SRGB_S3TC_DXT1_EXT* = 0x8C4C.GLenum + GL_DRAW_BUFFER0_ARB* = 0x8825.GLenum + GL_GPU_OPTIMIZED_QCOM* = 0x8FB2.GLenum + GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT* = 0x850F.GLenum + GL_SPRITE_EYE_ALIGNED_SGIX* = 0x814E.GLenum + GL_MAP1_VERTEX_ATTRIB3_4_NV* = 0x8663.GLenum + GL_SAMPLE_MASK_SGIS* = 0x80A0.GLenum + GL_TEXTURE_SAMPLES* = 0x9106.GLenum + GL_AND_REVERSE* = 0x1502.GLenum + GL_COMBINER4_NV* = 0x8554.GLenum + GL_FONT_Y_MIN_BOUNDS_BIT_NV* = 0x00020000.GLbitfield + GL_VIEW_CLASS_32_BITS* = 0x82C8.GLenum + GL_BGRA_EXT* = 0x80E1.GLenum + GL_TANGENT_ARRAY_TYPE_EXT* = 0x843E.GLenum + GL_BLEND_EQUATION_RGB_OES* = 0x8009.GLenum + GL_TRANSPOSE_TEXTURE_MATRIX_ARB* = 0x84E5.GLenum + GL_GET_TEXTURE_IMAGE_FORMAT* = 0x8291.GLenum + GL_PACK_MAX_COMPRESSED_SIZE_SGIX* = 0x831B.GLenum + GL_UNIFORM_ARRAY_STRIDE* = 0x8A3C.GLenum + GL_REFLECTION_MAP_ARB* = 0x8512.GLenum + GL_RGBA_FLOAT16_ATI* = 0x881A.GLenum + GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS* = 0x8E83.GLenum + GL_RED_BITS* = 0x0D52.GLenum + GL_VERTEX_TEXTURE* = 0x829B.GLenum + GL_UNSIGNALED_APPLE* = 0x9118.GLenum + GL_RENDERBUFFER_ALPHA_SIZE_OES* = 0x8D53.GLenum + GL_DRAW_BUFFER14_NV* = 0x8833.GLenum + GL_STREAM_COPY_ARB* = 0x88E2.GLenum + GL_SECONDARY_COLOR_ARRAY_TYPE* = 0x845B.GLenum + GL_MATRIX22_ARB* = 0x88D6.GLenum + GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV* = 0x8533.GLenum + GL_IUI_N3F_V3F_EXT* = 0x81B0.GLenum + GL_SPARE0_NV* = 0x852E.GLenum + GL_FOG_COORD* = 0x8451.GLenum + GL_DRAW_BUFFER8_ARB* = 0x882D.GLenum + GL_MATRIX24_ARB* = 0x88D8.GLenum + GL_MAX_DEBUG_MESSAGE_LENGTH_AMD* = 0x9143.GLenum + GL_POST_COLOR_MATRIX_BLUE_SCALE* = 0x80B6.GLenum + GL_TEXTURE_HEIGHT_QCOM* = 0x8BD3.GLenum + GL_NUM_FRAGMENT_REGISTERS_ATI* = 0x896E.GLenum + GL_IMAGE_3D_EXT* = 0x904E.GLenum + GL_TEXTURE_FILTER_CONTROL* = 0x8500.GLenum + GL_VIDEO_BUFFER_NV* = 0x9020.GLenum + GL_CURRENT_MATRIX_INDEX_ARB* = 0x8845.GLenum + GL_STENCIL_BUFFER_BIT4_QCOM* = 0x00100000.GLbitfield + GL_SIGNED_INTENSITY_NV* = 0x8707.GLenum + GL_RASTERIZER_DISCARD_NV* = 0x8C89.GLenum + GL_MAX_DEFORMATION_ORDER_SGIX* = 0x8197.GLenum + GL_SAMPLES_3DFX* = 0x86B4.GLenum + GL_DOT_PRODUCT_PASS_THROUGH_NV* = 0x885B.GLenum + GL_RGB_SCALE_EXT* = 0x8573.GLenum + GL_TEXTURE_UNSIGNED_REMAP_MODE_NV* = 0x888F.GLenum + GL_MIRROR_CLAMP_TO_EDGE_EXT* = 0x8743.GLint + GL_NATIVE_GRAPHICS_END_HINT_PGI* = 0x1A204.GLenum + GL_UNPACK_CLIENT_STORAGE_APPLE* = 0x85B2.GLenum + GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER* = 0x8CDC.GLenum + GL_FOG_START* = 0x0B63.GLenum + GL_MAX_PROGRAM_CALL_DEPTH_NV* = 0x88F5.GLenum + GL_MODELVIEW18_ARB* = 0x8732.GLenum + GL_MAX_FRAMEZOOM_FACTOR_SGIX* = 0x818D.GLenum + GL_EDGE_FLAG_ARRAY_POINTER* = 0x8093.GLenum + GL_GREEN_INTEGER* = 0x8D95.GLenum + GL_IMAGE_BUFFER* = 0x9051.GLenum + GL_PROJECTION* = 0x1701.GLenum + GL_UNSIGNED_INT_VEC4_EXT* = 0x8DC8.GLenum + GL_PALETTE8_RGB5_A1_OES* = 0x8B99.GLenum + GL_RENDERBUFFER_SAMPLES_EXT* = 0x8CAB.GLenum + GL_TEXTURE3* = 0x84C3.GLenum + GL_CURRENT_RASTER_INDEX* = 0x0B05.GLenum + GL_INTERLEAVED_ATTRIBS_EXT* = 0x8C8C.GLenum + GL_STENCIL_BACK_WRITEMASK* = 0x8CA5.GLenum + GL_POINT_SPRITE_ARB* = 0x8861.GLenum + GL_TRANSPOSE_TEXTURE_MATRIX* = 0x84E5.GLenum + GL_DRAW_BUFFER1_ARB* = 0x8826.GLenum + GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS* = 0x92D0.GLenum + GL_DEPTH_ATTACHMENT_OES* = 0x8D00.GLenum + GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG* = 0x9137.GLenum + GL_SRGB_ALPHA* = 0x8C42.GLenum + GL_UNSIGNED_INT64_ARB* = 0x140F.GLenum + GL_LAST_VERTEX_CONVENTION_EXT* = 0x8E4E.GLenum + GL_IMAGE_CLASS_1_X_8* = 0x82C1.GLenum + GL_COMPRESSED_RGBA_S3TC_DXT1_EXT* = 0x83F1.GLenum + GL_REFLECTION_MAP* = 0x8512.GLenum + GL_MAX_IMAGE_UNITS_EXT* = 0x8F38.GLenum + GL_DEPTH_STENCIL_NV* = 0x84F9.GLenum + GL_PROGRAM_TEX_INDIRECTIONS_ARB* = 0x8807.GLenum + GL_BINNING_CONTROL_HINT_QCOM* = 0x8FB0.GLenum + GL_T4F_V4F* = 0x2A28.GLenum + GL_FLOAT_VEC4* = 0x8B52.GLenum + GL_CONVEX_HULL_NV* = 0x908B.GLenum + GL_TEXTURE26_ARB* = 0x84DA.GLenum + GL_INDEX_BIT_PGI* = 0x00080000.GLbitfield + GL_TEXTURE_COORD_ARRAY_TYPE_EXT* = 0x8089.GLenum + GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES* = 0x8CD2.GLenum + GL_MAX_ARRAY_TEXTURE_LAYERS* = 0x88FF.GLenum + GL_COLOR_ATTACHMENT4_EXT* = 0x8CE4.GLenum + GL_SAMPLE_COVERAGE_VALUE_ARB* = 0x80AA.GLenum + GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE* = 0x8A08.GLenum + GL_MAX_LAYERS* = 0x8281.GLenum + GL_FOG_COORDINATE_ARRAY_POINTER_EXT* = 0x8456.GLenum + GL_INDEX_TEST_REF_EXT* = 0x81B7.GLenum + GL_GREEN_BIT_ATI* = 0x00000002.GLbitfield + GL_STRICT_SCISSOR_HINT_PGI* = 0x1A218.GLenum + GL_MAP2_VERTEX_ATTRIB4_4_NV* = 0x8674.GLenum + GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT* = 0x8DE0.GLenum + GL_OUTPUT_TEXTURE_COORD31_EXT* = 0x87BC.GLenum + GL_XOR* = 0x1506.GLenum + GL_VIDEO_CAPTURE_FRAME_WIDTH_NV* = 0x9038.GLenum + GL_RGBA* = 0x1908.GLenum + GL_TEXTURE_TARGET* = 0x1006.GLenum + GL_QUERY_TARGET* = 0x82EA.GLenum + +{.deprecated: [ + cGL_TRANSFORM_FEEDBACK_VARYINGS_EXT: GL_TRANSFORM_FEEDBACK_VARYINGS_EXT, + cGL_BLEND_EQUATION_EXT: GL_BLEND_EQUATION_EXT, + cGL_VERTEX_BLEND_ARB: GL_VERTEX_BLEND_ARB, + cGL_TESSELLATION_MODE_AMD: GL_TESSELLATION_MODE_AMD, + cGL_POLYGON_OFFSET_EXT: GL_POLYGON_OFFSET_EXT, + cGL_BLEND_COLOR_EXT: GL_BLEND_COLOR_EXT, + cGL_TRANSFORM_FEEDBACK_VARYINGS_NV: GL_TRANSFORM_FEEDBACK_VARYINGS_NV, + cGL_COLOR_MATERIAL: GL_COLOR_MATERIAL, + cGL_READ_BUFFER_NV: GL_READ_BUFFER_NV, + cGL_FOG_FUNC_SGIS: GL_FOG_FUNC_SGIS, + cGL_HISTOGRAM_EXT: GL_HISTOGRAM_EXT, + cGL_LINE_WIDTH: GL_LINE_WIDTH, + cGL_PROVOKING_VERTEX: GL_PROVOKING_VERTEX, + cGL_SHADE_MODEL: GL_SHADE_MODEL, + cGL_FRONT_FACE: GL_FRONT_FACE, + cGL_PRIMITIVE_RESTART_INDEX: GL_PRIMITIVE_RESTART_INDEX, + cGL_READ_PIXELS: GL_READ_PIXELS, + cGL_VIEWPORT: GL_VIEWPORT, + cGL_DEPTH_RANGE: GL_DEPTH_RANGE, + cGL_COLOR_TABLE_SGI: GL_COLOR_TABLE_SGI, + cGL_CLEAR: GL_CLEAR, + cGL_ASYNC_MARKER_SGIX: GL_ASYNC_MARKER_SGIX, + cGL_ACTIVE_TEXTURE_ARB: GL_ACTIVE_TEXTURE_ARB, + cGL_SAMPLE_COVERAGE: GL_SAMPLE_COVERAGE, + cGL_BLEND_EQUATION_OES: GL_BLEND_EQUATION_OES, + cGL_MATRIX_MODE: GL_MATRIX_MODE, + cGL_TRANSFORM_FEEDBACK_VARYINGS: GL_TRANSFORM_FEEDBACK_VARYINGS, + cGL_SAMPLE_COVERAGE_ARB: GL_SAMPLE_COVERAGE_ARB, + cGL_TRACK_MATRIX_NV: GL_TRACK_MATRIX_NV, + cGL_COMBINER_INPUT_NV: GL_COMBINER_INPUT_NV, + cGL_TESSELLATION_FACTOR_AMD: GL_TESSELLATION_FACTOR_AMD, + cGL_BLEND_EQUATION: GL_BLEND_EQUATION, + cGL_CULL_FACE: GL_CULL_FACE, + cGL_HISTOGRAM: GL_HISTOGRAM, + cGL_PRIMITIVE_RESTART_INDEX_NV: GL_PRIMITIVE_RESTART_INDEX_NV, + cGL_SAMPLE_MASK_EXT: GL_SAMPLE_MASK_EXT, + cGL_RENDER_MODE: GL_RENDER_MODE, + cGL_CURRENT_PALETTE_MATRIX_OES: GL_CURRENT_PALETTE_MATRIX_OES, + cGL_VERTEX_ATTRIB_BINDING: GL_VERTEX_ATTRIB_BINDING, + cGL_TEXTURE_LIGHT_EXT: GL_TEXTURE_LIGHT_EXT, + cGL_INDEX_MATERIAL_EXT: GL_INDEX_MATERIAL_EXT, + cGL_COLOR_TABLE: GL_COLOR_TABLE, + cGL_PATH_STENCIL_FUNC_NV: GL_PATH_STENCIL_FUNC_NV, + cGL_EDGE_FLAG: GL_EDGE_FLAG, + cGL_ACTIVE_TEXTURE: GL_ACTIVE_TEXTURE, + cGL_CLIENT_ACTIVE_TEXTURE_ARB: GL_CLIENT_ACTIVE_TEXTURE_ARB, + cGL_VERTEX_ARRAY_RANGE_APPLE: GL_VERTEX_ARRAY_RANGE_APPLE, + cGL_TEXTURE_VIEW: GL_TEXTURE_VIEW, + cGL_BITMAP: GL_BITMAP, + cGL_PRIMITIVE_RESTART_NV: GL_PRIMITIVE_RESTART_NV, + cGL_VERTEX_BINDING_DIVISOR: GL_VERTEX_BINDING_DIVISOR, + cGL_STENCIL_OP_VALUE_AMD: GL_STENCIL_OP_VALUE_AMD, + cGL_PROVOKING_VERTEX_EXT: GL_PROVOKING_VERTEX_EXT, + cGL_CURRENT_PALETTE_MATRIX_ARB: GL_CURRENT_PALETTE_MATRIX_ARB, + cGL_PIXEL_TEX_GEN_SGIX: GL_PIXEL_TEX_GEN_SGIX, + cGL_GENERATE_MIPMAP: GL_GENERATE_MIPMAP, + cGL_UNIFORM_BUFFER_EXT: GL_UNIFORM_BUFFER_EXT, + cGL_STENCIL_FUNC: GL_STENCIL_FUNC, + cGL_VERTEX_ARRAY_RANGE_NV: GL_VERTEX_ARRAY_RANGE_NV, + cGL_ACTIVE_PROGRAM_EXT: GL_ACTIVE_PROGRAM_EXT, + cGL_LINE_STIPPLE: GL_LINE_STIPPLE, + cGL_REFERENCE_PLANE_SGIX: GL_REFERENCE_PLANE_SGIX, + cGL_DRAW_BUFFER: GL_DRAW_BUFFER, + cGL_LIST_BASE: GL_LIST_BASE, + cGL_READ_BUFFER: GL_READ_BUFFER, + cGL_FRAGMENT_COLOR_MATERIAL_SGIX: GL_FRAGMENT_COLOR_MATERIAL_SGIX, + cGL_CLIENT_ACTIVE_TEXTURE: GL_CLIENT_ACTIVE_TEXTURE, + cGL_BLEND_COLOR: GL_BLEND_COLOR, + cGL_MINMAX_EXT: GL_MINMAX_EXT, + cGL_POINT_SIZE: GL_POINT_SIZE, + cGL_MINMAX: GL_MINMAX, + cGL_SAMPLE_PATTERN_SGIS: GL_SAMPLE_PATTERN_SGIS, + cGL_SAMPLE_PATTERN_EXT: GL_SAMPLE_PATTERN_EXT, + cGL_UNIFORM_BLOCK_BINDING: GL_UNIFORM_BLOCK_BINDING, + cGL_POLYGON_STIPPLE: GL_POLYGON_STIPPLE, + cGL_LOGIC_OP: GL_LOGIC_OP, + cGL_ACCUM: GL_ACCUM, + cGL_FRAMEZOOM_SGIX: GL_FRAMEZOOM_SGIX, + cGL_DEPTH_BOUNDS_EXT: GL_DEPTH_BOUNDS_EXT, + cGL_TEXTURE_BUFFER_EXT: GL_TEXTURE_BUFFER_EXT, + cGL_POLYGON_MODE: GL_POLYGON_MODE, + cGL_TEXTURE_NORMAL_EXT: GL_TEXTURE_NORMAL_EXT, + cGL_PROGRAM_STRING_ARB: GL_PROGRAM_STRING_ARB, + cGL_PATH_COVER_DEPTH_FUNC_NV: GL_PATH_COVER_DEPTH_FUNC_NV, + cGL_TRANSFORM_FEEDBACK_ATTRIBS_NV: GL_TRANSFORM_FEEDBACK_ATTRIBS_NV, + cGL_ACTIVE_STENCIL_FACE_EXT: GL_ACTIVE_STENCIL_FACE_EXT, + cGL_DEPTH_FUNC: GL_DEPTH_FUNC, + cGL_SAMPLE_MASK_SGIS: GL_SAMPLE_MASK_SGIS +].} diff --git a/tests/deps/opengl-1.1.0/opengl.nimble b/tests/deps/opengl-1.1.0/opengl.nimble new file mode 100644 index 000000000..ac3b8aa32 --- /dev/null +++ b/tests/deps/opengl-1.1.0/opengl.nimble @@ -0,0 +1,12 @@ +# Package + +version = "1.1.0" +author = "Andreas Rumpf" +description = "an OpenGL wrapper" +license = "MIT" + +srcDir = "src" + +# Dependencies" + +requires "nim >= 0.10.3", "x11" diff --git a/tests/deps/opengl-1.1.0/wingl.nim b/tests/deps/opengl-1.1.0/wingl.nim new file mode 100644 index 000000000..9497bffb4 --- /dev/null +++ b/tests/deps/opengl-1.1.0/wingl.nim @@ -0,0 +1,369 @@ +import opengl, windows + +{.deadCodeElim: on.} + +proc wglGetExtensionsStringARB*(hdc: HDC): cstring{.dynlib: dllname, + importc: "wglGetExtensionsStringARB".} +const + WGL_FRONT_COLOR_BUFFER_BIT_ARB* = 0x00000001 + WGL_BACK_COLOR_BUFFER_BIT_ARB* = 0x00000002 + WGL_DEPTH_BUFFER_BIT_ARB* = 0x00000004 + WGL_STENCIL_BUFFER_BIT_ARB* = 0x00000008 + +proc WinChoosePixelFormat*(DC: HDC, p2: PPixelFormatDescriptor): int{. + dynlib: "gdi32", importc: "ChoosePixelFormat".} +proc wglCreateBufferRegionARB*(hDC: HDC, iLayerPlane: TGLint, uType: TGLuint): THandle{. + dynlib: dllname, importc: "wglCreateBufferRegionARB".} +proc wglDeleteBufferRegionARB*(hRegion: THandle){.dynlib: dllname, + importc: "wglDeleteBufferRegionARB".} +proc wglSaveBufferRegionARB*(hRegion: THandle, x: TGLint, y: TGLint, + width: TGLint, height: TGLint): BOOL{. + dynlib: dllname, importc: "wglSaveBufferRegionARB".} +proc wglRestoreBufferRegionARB*(hRegion: THandle, x: TGLint, y: TGLint, + width: TGLint, height: TGLint, xSrc: TGLint, + ySrc: TGLint): BOOL{.dynlib: dllname, + importc: "wglRestoreBufferRegionARB".} +proc wglAllocateMemoryNV*(size: TGLsizei, readFrequency: TGLfloat, + writeFrequency: TGLfloat, priority: TGLfloat): PGLvoid{. + dynlib: dllname, importc: "wglAllocateMemoryNV".} +proc wglFreeMemoryNV*(pointer: PGLvoid){.dynlib: dllname, + importc: "wglFreeMemoryNV".} +const + WGL_IMAGE_BUFFER_MIN_ACCESS_I3D* = 0x00000001 + WGL_IMAGE_BUFFER_LOCK_I3D* = 0x00000002 + +proc wglCreateImageBufferI3D*(hDC: HDC, dwSize: DWORD, uFlags: UINT): PGLvoid{. + dynlib: dllname, importc: "wglCreateImageBufferI3D".} +proc wglDestroyImageBufferI3D*(hDC: HDC, pAddress: PGLvoid): BOOL{. + dynlib: dllname, importc: "wglDestroyImageBufferI3D".} +proc wglAssociateImageBufferEventsI3D*(hdc: HDC, pEvent: PHandle, + pAddress: PGLvoid, pSize: PDWORD, + count: UINT): BOOL{.dynlib: dllname, + importc: "wglAssociateImageBufferEventsI3D".} +proc wglReleaseImageBufferEventsI3D*(hdc: HDC, pAddress: PGLvoid, count: UINT): BOOL{. + dynlib: dllname, importc: "wglReleaseImageBufferEventsI3D".} +proc wglEnableFrameLockI3D*(): BOOL{.dynlib: dllname, + importc: "wglEnableFrameLockI3D".} +proc wglDisableFrameLockI3D*(): BOOL{.dynlib: dllname, + importc: "wglDisableFrameLockI3D".} +proc wglIsEnabledFrameLockI3D*(pFlag: PBOOL): BOOL{.dynlib: dllname, + importc: "wglIsEnabledFrameLockI3D".} +proc wglQueryFrameLockMasterI3D*(pFlag: PBOOL): BOOL{.dynlib: dllname, + importc: "wglQueryFrameLockMasterI3D".} +proc wglGetFrameUsageI3D*(pUsage: PGLfloat): BOOL{.dynlib: dllname, + importc: "wglGetFrameUsageI3D".} +proc wglBeginFrameTrackingI3D*(): BOOL{.dynlib: dllname, + importc: "wglBeginFrameTrackingI3D".} +proc wglEndFrameTrackingI3D*(): BOOL{.dynlib: dllname, + importc: "wglEndFrameTrackingI3D".} +proc wglQueryFrameTrackingI3D*(pFrameCount: PDWORD, pMissedFrames: PDWORD, + pLastMissedUsage: PGLfloat): BOOL{. + dynlib: dllname, importc: "wglQueryFrameTrackingI3D".} +const + WGL_NUMBER_PIXEL_FORMATS_ARB* = 0x00002000 + WGL_DRAW_TO_WINDOW_ARB* = 0x00002001 + WGL_DRAW_TO_BITMAP_ARB* = 0x00002002 + WGL_ACCELERATION_ARB* = 0x00002003 + WGL_NEED_PALETTE_ARB* = 0x00002004 + WGL_NEED_SYSTEM_PALETTE_ARB* = 0x00002005 + WGL_SWAP_LAYER_BUFFERS_ARB* = 0x00002006 + WGL_SWAP_METHOD_ARB* = 0x00002007 + WGL_NUMBER_OVERLAYS_ARB* = 0x00002008 + WGL_NUMBER_UNDERLAYS_ARB* = 0x00002009 + WGL_TRANSPARENT_ARB* = 0x0000200A + WGL_TRANSPARENT_RED_VALUE_ARB* = 0x00002037 + WGL_TRANSPARENT_GREEN_VALUE_ARB* = 0x00002038 + WGL_TRANSPARENT_BLUE_VALUE_ARB* = 0x00002039 + WGL_TRANSPARENT_ALPHA_VALUE_ARB* = 0x0000203A + WGL_TRANSPARENT_INDEX_VALUE_ARB* = 0x0000203B + WGL_SHARE_DEPTH_ARB* = 0x0000200C + WGL_SHARE_STENCIL_ARB* = 0x0000200D + WGL_SHARE_ACCUM_ARB* = 0x0000200E + WGL_SUPPORT_GDI_ARB* = 0x0000200F + WGL_SUPPORT_OPENGL_ARB* = 0x00002010 + WGL_DOUBLE_BUFFER_ARB* = 0x00002011 + WGL_STEREO_ARB* = 0x00002012 + WGL_PIXEL_TYPE_ARB* = 0x00002013 + WGL_COLOR_BITS_ARB* = 0x00002014 + WGL_RED_BITS_ARB* = 0x00002015 + WGL_RED_SHIFT_ARB* = 0x00002016 + WGL_GREEN_BITS_ARB* = 0x00002017 + WGL_GREEN_SHIFT_ARB* = 0x00002018 + WGL_BLUE_BITS_ARB* = 0x00002019 + WGL_BLUE_SHIFT_ARB* = 0x0000201A + WGL_ALPHA_BITS_ARB* = 0x0000201B + WGL_ALPHA_SHIFT_ARB* = 0x0000201C + WGL_ACCUM_BITS_ARB* = 0x0000201D + WGL_ACCUM_RED_BITS_ARB* = 0x0000201E + WGL_ACCUM_GREEN_BITS_ARB* = 0x0000201F + WGL_ACCUM_BLUE_BITS_ARB* = 0x00002020 + WGL_ACCUM_ALPHA_BITS_ARB* = 0x00002021 + WGL_DEPTH_BITS_ARB* = 0x00002022 + WGL_STENCIL_BITS_ARB* = 0x00002023 + WGL_AUX_BUFFERS_ARB* = 0x00002024 + WGL_NO_ACCELERATION_ARB* = 0x00002025 + WGL_GENERIC_ACCELERATION_ARB* = 0x00002026 + WGL_FULL_ACCELERATION_ARB* = 0x00002027 + WGL_SWAP_EXCHANGE_ARB* = 0x00002028 + WGL_SWAP_COPY_ARB* = 0x00002029 + WGL_SWAP_UNDEFINED_ARB* = 0x0000202A + WGL_TYPE_RGBA_ARB* = 0x0000202B + WGL_TYPE_COLORINDEX_ARB* = 0x0000202C + +proc wglGetPixelFormatAttribivARB*(hdc: HDC, iPixelFormat: TGLint, + iLayerPlane: TGLint, nAttributes: TGLuint, + piAttributes: PGLint, piValues: PGLint): BOOL{. + dynlib: dllname, importc: "wglGetPixelFormatAttribivARB".} +proc wglGetPixelFormatAttribfvARB*(hdc: HDC, iPixelFormat: TGLint, + iLayerPlane: TGLint, nAttributes: TGLuint, + piAttributes: PGLint, pfValues: PGLfloat): BOOL{. + dynlib: dllname, importc: "wglGetPixelFormatAttribfvARB".} +proc wglChoosePixelFormatARB*(hdc: HDC, piAttribIList: PGLint, + pfAttribFList: PGLfloat, nMaxFormats: TGLuint, + piFormats: PGLint, nNumFormats: PGLuint): BOOL{. + dynlib: dllname, importc: "wglChoosePixelFormatARB".} +const + WGL_ERROR_INVALID_PIXEL_TYPE_ARB* = 0x00002043 + WGL_ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB* = 0x00002054 + +proc wglMakeContextCurrentARB*(hDrawDC: HDC, hReadDC: HDC, hglrc: HGLRC): BOOL{. + dynlib: dllname, importc: "wglMakeContextCurrentARB".} +proc wglGetCurrentReadDCARB*(): HDC{.dynlib: dllname, + importc: "wglGetCurrentReadDCARB".} +const + WGL_DRAW_TO_PBUFFER_ARB* = 0x0000202D # WGL_DRAW_TO_PBUFFER_ARB { already defined } + WGL_MAX_PBUFFER_PIXELS_ARB* = 0x0000202E + WGL_MAX_PBUFFER_WIDTH_ARB* = 0x0000202F + WGL_MAX_PBUFFER_HEIGHT_ARB* = 0x00002030 + WGL_PBUFFER_LARGEST_ARB* = 0x00002033 + WGL_PBUFFER_WIDTH_ARB* = 0x00002034 + WGL_PBUFFER_HEIGHT_ARB* = 0x00002035 + WGL_PBUFFER_LOST_ARB* = 0x00002036 + +proc wglCreatePbufferARB*(hDC: HDC, iPixelFormat: TGLint, iWidth: TGLint, + iHeight: TGLint, piAttribList: PGLint): THandle{. + dynlib: dllname, importc: "wglCreatePbufferARB".} +proc wglGetPbufferDCARB*(hPbuffer: THandle): HDC{.dynlib: dllname, + importc: "wglGetPbufferDCARB".} +proc wglReleasePbufferDCARB*(hPbuffer: THandle, hDC: HDC): TGLint{. + dynlib: dllname, importc: "wglReleasePbufferDCARB".} +proc wglDestroyPbufferARB*(hPbuffer: THandle): BOOL{.dynlib: dllname, + importc: "wglDestroyPbufferARB".} +proc wglQueryPbufferARB*(hPbuffer: THandle, iAttribute: TGLint, piValue: PGLint): BOOL{. + dynlib: dllname, importc: "wglQueryPbufferARB".} +proc wglSwapIntervalEXT*(interval: TGLint): BOOL{.dynlib: dllname, + importc: "wglSwapIntervalEXT".} +proc wglGetSwapIntervalEXT*(): TGLint{.dynlib: dllname, + importc: "wglGetSwapIntervalEXT".} +const + WGL_BIND_TO_TEXTURE_RGB_ARB* = 0x00002070 + WGL_BIND_TO_TEXTURE_RGBA_ARB* = 0x00002071 + WGL_TEXTURE_FORMAT_ARB* = 0x00002072 + WGL_TEXTURE_TARGET_ARB* = 0x00002073 + WGL_MIPMAP_TEXTURE_ARB* = 0x00002074 + WGL_TEXTURE_RGB_ARB* = 0x00002075 + WGL_TEXTURE_RGBA_ARB* = 0x00002076 + WGL_NO_TEXTURE_ARB* = 0x00002077 + WGL_TEXTURE_CUBE_MAP_ARB* = 0x00002078 + WGL_TEXTURE_1D_ARB* = 0x00002079 + WGL_TEXTURE_2D_ARB* = 0x0000207A # WGL_NO_TEXTURE_ARB { already defined } + WGL_MIPMAP_LEVEL_ARB* = 0x0000207B + WGL_CUBE_MAP_FACE_ARB* = 0x0000207C + WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB* = 0x0000207D + WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB* = 0x0000207E + WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB* = 0x0000207F + WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB* = 0x00002080 + WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB* = 0x00002081 + WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB* = 0x00002082 + WGL_FRONT_LEFT_ARB* = 0x00002083 + WGL_FRONT_RIGHT_ARB* = 0x00002084 + WGL_BACK_LEFT_ARB* = 0x00002085 + WGL_BACK_RIGHT_ARB* = 0x00002086 + WGL_AUX0_ARB* = 0x00002087 + WGL_AUX1_ARB* = 0x00002088 + WGL_AUX2_ARB* = 0x00002089 + WGL_AUX3_ARB* = 0x0000208A + WGL_AUX4_ARB* = 0x0000208B + WGL_AUX5_ARB* = 0x0000208C + WGL_AUX6_ARB* = 0x0000208D + WGL_AUX7_ARB* = 0x0000208E + WGL_AUX8_ARB* = 0x0000208F + WGL_AUX9_ARB* = 0x00002090 + +proc wglBindTexImageARB*(hPbuffer: THandle, iBuffer: TGLint): BOOL{. + dynlib: dllname, importc: "wglBindTexImageARB".} +proc wglReleaseTexImageARB*(hPbuffer: THandle, iBuffer: TGLint): BOOL{. + dynlib: dllname, importc: "wglReleaseTexImageARB".} +proc wglSetPbufferAttribARB*(hPbuffer: THandle, piAttribList: PGLint): BOOL{. + dynlib: dllname, importc: "wglSetPbufferAttribARB".} +proc wglGetExtensionsStringEXT*(): cstring{.dynlib: dllname, + importc: "wglGetExtensionsStringEXT".} +proc wglMakeContextCurrentEXT*(hDrawDC: HDC, hReadDC: HDC, hglrc: HGLRC): BOOL{. + dynlib: dllname, importc: "wglMakeContextCurrentEXT".} +proc wglGetCurrentReadDCEXT*(): HDC{.dynlib: dllname, + importc: "wglGetCurrentReadDCEXT".} +const + WGL_DRAW_TO_PBUFFER_EXT* = 0x0000202D + WGL_MAX_PBUFFER_PIXELS_EXT* = 0x0000202E + WGL_MAX_PBUFFER_WIDTH_EXT* = 0x0000202F + WGL_MAX_PBUFFER_HEIGHT_EXT* = 0x00002030 + WGL_OPTIMAL_PBUFFER_WIDTH_EXT* = 0x00002031 + WGL_OPTIMAL_PBUFFER_HEIGHT_EXT* = 0x00002032 + WGL_PBUFFER_LARGEST_EXT* = 0x00002033 + WGL_PBUFFER_WIDTH_EXT* = 0x00002034 + WGL_PBUFFER_HEIGHT_EXT* = 0x00002035 + +proc wglCreatePbufferEXT*(hDC: HDC, iPixelFormat: TGLint, iWidth: TGLint, + iHeight: TGLint, piAttribList: PGLint): THandle{. + dynlib: dllname, importc: "wglCreatePbufferEXT".} +proc wglGetPbufferDCEXT*(hPbuffer: THandle): HDC{.dynlib: dllname, + importc: "wglGetPbufferDCEXT".} +proc wglReleasePbufferDCEXT*(hPbuffer: THandle, hDC: HDC): TGLint{. + dynlib: dllname, importc: "wglReleasePbufferDCEXT".} +proc wglDestroyPbufferEXT*(hPbuffer: THandle): BOOL{.dynlib: dllname, + importc: "wglDestroyPbufferEXT".} +proc wglQueryPbufferEXT*(hPbuffer: THandle, iAttribute: TGLint, piValue: PGLint): BOOL{. + dynlib: dllname, importc: "wglQueryPbufferEXT".} +const + WGL_NUMBER_PIXEL_FORMATS_EXT* = 0x00002000 + WGL_DRAW_TO_WINDOW_EXT* = 0x00002001 + WGL_DRAW_TO_BITMAP_EXT* = 0x00002002 + WGL_ACCELERATION_EXT* = 0x00002003 + WGL_NEED_PALETTE_EXT* = 0x00002004 + WGL_NEED_SYSTEM_PALETTE_EXT* = 0x00002005 + WGL_SWAP_LAYER_BUFFERS_EXT* = 0x00002006 + WGL_SWAP_METHOD_EXT* = 0x00002007 + WGL_NUMBER_OVERLAYS_EXT* = 0x00002008 + WGL_NUMBER_UNDERLAYS_EXT* = 0x00002009 + WGL_TRANSPARENT_EXT* = 0x0000200A + WGL_TRANSPARENT_VALUE_EXT* = 0x0000200B + WGL_SHARE_DEPTH_EXT* = 0x0000200C + WGL_SHARE_STENCIL_EXT* = 0x0000200D + WGL_SHARE_ACCUM_EXT* = 0x0000200E + WGL_SUPPORT_GDI_EXT* = 0x0000200F + WGL_SUPPORT_OPENGL_EXT* = 0x00002010 + WGL_DOUBLE_BUFFER_EXT* = 0x00002011 + WGL_STEREO_EXT* = 0x00002012 + WGL_PIXEL_TYPE_EXT* = 0x00002013 + WGL_COLOR_BITS_EXT* = 0x00002014 + WGL_RED_BITS_EXT* = 0x00002015 + WGL_RED_SHIFT_EXT* = 0x00002016 + WGL_GREEN_BITS_EXT* = 0x00002017 + WGL_GREEN_SHIFT_EXT* = 0x00002018 + WGL_BLUE_BITS_EXT* = 0x00002019 + WGL_BLUE_SHIFT_EXT* = 0x0000201A + WGL_ALPHA_BITS_EXT* = 0x0000201B + WGL_ALPHA_SHIFT_EXT* = 0x0000201C + WGL_ACCUM_BITS_EXT* = 0x0000201D + WGL_ACCUM_RED_BITS_EXT* = 0x0000201E + WGL_ACCUM_GREEN_BITS_EXT* = 0x0000201F + WGL_ACCUM_BLUE_BITS_EXT* = 0x00002020 + WGL_ACCUM_ALPHA_BITS_EXT* = 0x00002021 + WGL_DEPTH_BITS_EXT* = 0x00002022 + WGL_STENCIL_BITS_EXT* = 0x00002023 + WGL_AUX_BUFFERS_EXT* = 0x00002024 + WGL_NO_ACCELERATION_EXT* = 0x00002025 + WGL_GENERIC_ACCELERATION_EXT* = 0x00002026 + WGL_FULL_ACCELERATION_EXT* = 0x00002027 + WGL_SWAP_EXCHANGE_EXT* = 0x00002028 + WGL_SWAP_COPY_EXT* = 0x00002029 + WGL_SWAP_UNDEFINED_EXT* = 0x0000202A + WGL_TYPE_RGBA_EXT* = 0x0000202B + WGL_TYPE_COLORINDEX_EXT* = 0x0000202C + +proc wglGetPixelFormatAttribivEXT*(hdc: HDC, iPixelFormat: TGLint, + iLayerPlane: TGLint, nAttributes: TGLuint, + piAttributes: PGLint, piValues: PGLint): BOOL{. + dynlib: dllname, importc: "wglGetPixelFormatAttribivEXT".} +proc wglGetPixelFormatAttribfvEXT*(hdc: HDC, iPixelFormat: TGLint, + iLayerPlane: TGLint, nAttributes: TGLuint, + piAttributes: PGLint, pfValues: PGLfloat): BOOL{. + dynlib: dllname, importc: "wglGetPixelFormatAttribfvEXT".} +proc wglChoosePixelFormatEXT*(hdc: HDC, piAttribIList: PGLint, + pfAttribFList: PGLfloat, nMaxFormats: TGLuint, + piFormats: PGLint, nNumFormats: PGLuint): BOOL{. + dynlib: dllname, importc: "wglChoosePixelFormatEXT".} +const + WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D* = 0x00002050 + WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D* = 0x00002051 + WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D* = 0x00002052 + WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D* = 0x00002053 + +proc wglGetDigitalVideoParametersI3D*(hDC: HDC, iAttribute: TGLint, + piValue: PGLint): BOOL{.dynlib: dllname, + importc: "wglGetDigitalVideoParametersI3D".} +proc wglSetDigitalVideoParametersI3D*(hDC: HDC, iAttribute: TGLint, + piValue: PGLint): BOOL{.dynlib: dllname, + importc: "wglSetDigitalVideoParametersI3D".} +const + WGL_GAMMA_TABLE_SIZE_I3D* = 0x0000204E + WGL_GAMMA_EXCLUDE_DESKTOP_I3D* = 0x0000204F + +proc wglGetGammaTableParametersI3D*(hDC: HDC, iAttribute: TGLint, + piValue: PGLint): BOOL{.dynlib: dllname, + importc: "wglGetGammaTableParametersI3D".} +proc wglSetGammaTableParametersI3D*(hDC: HDC, iAttribute: TGLint, + piValue: PGLint): BOOL{.dynlib: dllname, + importc: "wglSetGammaTableParametersI3D".} +proc wglGetGammaTableI3D*(hDC: HDC, iEntries: TGLint, puRed: PGLUSHORT, + puGreen: PGLUSHORT, puBlue: PGLUSHORT): BOOL{. + dynlib: dllname, importc: "wglGetGammaTableI3D".} +proc wglSetGammaTableI3D*(hDC: HDC, iEntries: TGLint, puRed: PGLUSHORT, + puGreen: PGLUSHORT, puBlue: PGLUSHORT): BOOL{. + dynlib: dllname, importc: "wglSetGammaTableI3D".} +const + WGL_GENLOCK_SOURCE_MULTIVIEW_I3D* = 0x00002044 + WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D* = 0x00002045 + WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D* = 0x00002046 + WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D* = 0x00002047 + WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D* = 0x00002048 + WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D* = 0x00002049 + WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D* = 0x0000204A + WGL_GENLOCK_SOURCE_EDGE_RISING_I3D* = 0x0000204B + WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D* = 0x0000204C + WGL_FLOAT_COMPONENTS_NV* = 0x000020B0 + WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV* = 0x000020B1 + WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV* = 0x000020B2 + WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV* = 0x000020B3 + WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV* = 0x000020B4 + WGL_TEXTURE_FLOAT_R_NV* = 0x000020B5 + WGL_TEXTURE_FLOAT_RG_NV* = 0x000020B6 + WGL_TEXTURE_FLOAT_RGB_NV* = 0x000020B7 + WGL_TEXTURE_FLOAT_RGBA_NV* = 0x000020B8 + +proc wglEnableGenlockI3D*(hDC: HDC): BOOL{.dynlib: dllname, + importc: "wglEnableGenlockI3D".} +proc wglDisableGenlockI3D*(hDC: HDC): BOOL{.dynlib: dllname, + importc: "wglDisableGenlockI3D".} +proc wglIsEnabledGenlockI3D*(hDC: HDC, pFlag: PBOOL): BOOL{.dynlib: dllname, + importc: "wglIsEnabledGenlockI3D".} +proc wglGenlockSourceI3D*(hDC: HDC, uSource: TGLuint): BOOL{.dynlib: dllname, + importc: "wglGenlockSourceI3D".} +proc wglGetGenlockSourceI3D*(hDC: HDC, uSource: PGLUINT): BOOL{.dynlib: dllname, + importc: "wglGetGenlockSourceI3D".} +proc wglGenlockSourceEdgeI3D*(hDC: HDC, uEdge: TGLuint): BOOL{.dynlib: dllname, + importc: "wglGenlockSourceEdgeI3D".} +proc wglGetGenlockSourceEdgeI3D*(hDC: HDC, uEdge: PGLUINT): BOOL{. + dynlib: dllname, importc: "wglGetGenlockSourceEdgeI3D".} +proc wglGenlockSampleRateI3D*(hDC: HDC, uRate: TGLuint): BOOL{.dynlib: dllname, + importc: "wglGenlockSampleRateI3D".} +proc wglGetGenlockSampleRateI3D*(hDC: HDC, uRate: PGLUINT): BOOL{. + dynlib: dllname, importc: "wglGetGenlockSampleRateI3D".} +proc wglGenlockSourceDelayI3D*(hDC: HDC, uDelay: TGLuint): BOOL{. + dynlib: dllname, importc: "wglGenlockSourceDelayI3D".} +proc wglGetGenlockSourceDelayI3D*(hDC: HDC, uDelay: PGLUINT): BOOL{. + dynlib: dllname, importc: "wglGetGenlockSourceDelayI3D".} +proc wglQueryGenlockMaxSourceDelayI3D*(hDC: HDC, uMaxLineDelay: PGLUINT, + uMaxPixelDelay: PGLUINT): BOOL{. + dynlib: dllname, importc: "wglQueryGenlockMaxSourceDelayI3D".} +const + WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV* = 0x000020A0 + WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV* = 0x000020A1 + WGL_TEXTURE_RECTANGLE_NV* = 0x000020A2 + +const + WGL_RGBA_FLOAT_MODE_ATI* = 0x00008820 + WGL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI* = 0x00008835 + WGL_TYPE_RGBA_FLOAT_ATI* = 0x000021A0 + +# implementation diff --git a/tests/deps/x11-1.0/cursorfont.nim b/tests/deps/x11-1.0/cursorfont.nim new file mode 100644 index 000000000..b262ad7c1 --- /dev/null +++ b/tests/deps/x11-1.0/cursorfont.nim @@ -0,0 +1,110 @@ +# $Xorg: cursorfont.h,v 1.4 2001/02/09 02:03:39 xorgcvs Exp $ +# +# +#Copyright 1987, 1998 The Open Group +# +#Permission to use, copy, modify, distribute, and sell this software and its +#documentation for any purpose is hereby granted without fee, provided that +#the above copyright notice appear in all copies and that both that +#copyright notice and this permission notice appear in supporting +#documentation. +# +#The above copyright notice and this permission notice shall be included +#in all copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +#OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +#MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +#IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +#OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +#ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +#OTHER DEALINGS IN THE SOFTWARE. +# +#Except as contained in this notice, the name of The Open Group shall +#not be used in advertising or otherwise to promote the sale, use or +#other dealings in this Software without prior written authorization +#from The Open Group. +# +# + +const + XC_num_glyphs* = 154 + XC_X_cursor* = 0 + XC_arrow* = 2 + XC_based_arrow_down* = 4 + XC_based_arrow_up* = 6 + XC_boat* = 8 + XC_bogosity* = 10 + XC_bottom_left_corner* = 12 + XC_bottom_right_corner* = 14 + XC_bottom_side* = 16 + XC_bottom_tee* = 18 + XC_box_spiral* = 20 + XC_center_ptr* = 22 + XC_circle* = 24 + XC_clock* = 26 + XC_coffee_mug* = 28 + XC_cross* = 30 + XC_cross_reverse* = 32 + XC_crosshair* = 34 + XC_diamond_cross* = 36 + XC_dot* = 38 + XC_dotbox* = 40 + XC_double_arrow* = 42 + XC_draft_large* = 44 + XC_draft_small* = 46 + XC_draped_box* = 48 + XC_exchange* = 50 + XC_fleur* = 52 + XC_gobbler* = 54 + XC_gumby* = 56 + XC_hand1* = 58 + XC_hand2* = 60 + XC_heart* = 62 + XC_icon* = 64 + XC_iron_cross* = 66 + XC_left_ptr* = 68 + XC_left_side* = 70 + XC_left_tee* = 72 + XC_leftbutton* = 74 + XC_ll_angle* = 76 + XC_lr_angle* = 78 + XC_man* = 80 + XC_middlebutton* = 82 + XC_mouse* = 84 + XC_pencil* = 86 + XC_pirate* = 88 + XC_plus* = 90 + XC_question_arrow* = 92 + XC_right_ptr* = 94 + XC_right_side* = 96 + XC_right_tee* = 98 + XC_rightbutton* = 100 + XC_rtl_logo* = 102 + XC_sailboat* = 104 + XC_sb_down_arrow* = 106 + XC_sb_h_double_arrow* = 108 + XC_sb_left_arrow* = 110 + XC_sb_right_arrow* = 112 + XC_sb_up_arrow* = 114 + XC_sb_v_double_arrow* = 116 + XC_shuttle* = 118 + XC_sizing* = 120 + XC_spider* = 122 + XC_spraycan* = 124 + XC_star* = 126 + XC_target* = 128 + XC_tcross* = 130 + XC_top_left_arrow* = 132 + XC_top_left_corner* = 134 + XC_top_right_corner* = 136 + XC_top_side* = 138 + XC_top_tee* = 140 + XC_trek* = 142 + XC_ul_angle* = 144 + XC_umbrella* = 146 + XC_ur_angle* = 148 + XC_watch* = 150 + XC_xterm* = 152 + +# implementation diff --git a/tests/deps/x11-1.0/keysym.nim b/tests/deps/x11-1.0/keysym.nim new file mode 100644 index 000000000..c001ab622 --- /dev/null +++ b/tests/deps/x11-1.0/keysym.nim @@ -0,0 +1,1926 @@ +# +#Converted from X11/keysym.h and X11/keysymdef.h +# +#Capital letter consts renamed from XK_... to XKc_... +# (since Pascal isn't case-sensitive) +# +#i.e. +#C Pascal +#XK_a XK_a +#XK_A XKc_A +# + +#* default keysyms * +import x + +const + XK_VoidSymbol*: TKeySym = 0x00FFFFFF # void symbol + +when defined(XK_MISCELLANY) or true: + const + #* + # * TTY Functions, cleverly chosen to map to ascii, for convenience of + # * programming, but could have been arbitrary (at the cost of lookup + # * tables in client code. + # * + XK_BackSpace*: TKeySym = 0x0000FF08 # back space, back char + XK_Tab*: TKeySym = 0x0000FF09 + XK_Linefeed*: TKeySym = 0x0000FF0A # Linefeed, LF + XK_Clear*: TKeySym = 0x0000FF0B + XK_Return*: TKeySym = 0x0000FF0D # Return, enter + XK_Pause*: TKeySym = 0x0000FF13 # Pause, hold + XK_Scroll_Lock*: TKeySym = 0x0000FF14 + XK_Sys_Req*: TKeySym = 0x0000FF15 + XK_Escape*: TKeySym = 0x0000FF1B + XK_Delete*: TKeySym = 0x0000FFFF # Delete, rubout \ + # International & multi-key character composition + XK_Multi_key*: TKeySym = 0x0000FF20 # Multi-key character compose + XK_Codeinput*: TKeySym = 0x0000FF37 + XK_SingleCandidate*: TKeySym = 0x0000FF3C + XK_MultipleCandidate*: TKeySym = 0x0000FF3D + XK_PreviousCandidate*: TKeySym = 0x0000FF3E # Japanese keyboard support + XK_Kanji*: TKeySym = 0x0000FF21 # Kanji, Kanji convert + XK_Muhenkan*: TKeySym = 0x0000FF22 # Cancel Conversion + XK_Henkan_Mode*: TKeySym = 0x0000FF23 # Start/Stop Conversion + XK_Henkan*: TKeySym = 0x0000FF23 # Alias for Henkan_Mode + XK_Romaji*: TKeySym = 0x0000FF24 # to Romaji + XK_Hiragana*: TKeySym = 0x0000FF25 # to Hiragana + XK_Katakana*: TKeySym = 0x0000FF26 # to Katakana + XK_Hiragana_Katakana*: TKeySym = 0x0000FF27 # Hiragana/Katakana toggle + XK_Zenkaku*: TKeySym = 0x0000FF28 # to Zenkaku + XK_Hankaku*: TKeySym = 0x0000FF29 # to Hankaku + XK_Zenkaku_Hankaku*: TKeySym = 0x0000FF2A # Zenkaku/Hankaku toggle + XK_Touroku*: TKeySym = 0x0000FF2B # Add to Dictionary + XK_Massyo*: TKeySym = 0x0000FF2C # Delete from Dictionary + XK_Kana_Lock*: TKeySym = 0x0000FF2D # Kana Lock + XK_Kana_Shift*: TKeySym = 0x0000FF2E # Kana Shift + XK_Eisu_Shift*: TKeySym = 0x0000FF2F # Alphanumeric Shift + XK_Eisu_toggle*: TKeySym = 0x0000FF30 # Alphanumeric toggle + XK_Kanji_Bangou*: TKeySym = 0x0000FF37 # Codeinput + XK_Zen_Koho*: TKeySym = 0x0000FF3D # Multiple/All Candidate(s) + XK_Mae_Koho*: TKeySym = 0x0000FF3E # Previous Candidate \ + # = $FF31 thru = $FF3F are under XK_KOREAN + # Cursor control & motion + XK_Home*: TKeySym = 0x0000FF50 + XK_Left*: TKeySym = 0x0000FF51 # Move left, left arrow + XK_Up*: TKeySym = 0x0000FF52 # Move up, up arrow + XK_Right*: TKeySym = 0x0000FF53 # Move right, right arrow + XK_Down*: TKeySym = 0x0000FF54 # Move down, down arrow + XK_Prior*: TKeySym = 0x0000FF55 # Prior, previous + XK_Page_Up*: TKeySym = 0x0000FF55 + XK_Next*: TKeySym = 0x0000FF56 # Next + XK_Page_Down*: TKeySym = 0x0000FF56 + XK_End*: TKeySym = 0x0000FF57 # EOL + XK_Begin*: TKeySym = 0x0000FF58 # BOL \ + # Misc Functions + XK_Select*: TKeySym = 0x0000FF60 # Select, mark + XK_Print*: TKeySym = 0x0000FF61 + XK_Execute*: TKeySym = 0x0000FF62 # Execute, run, do + XK_Insert*: TKeySym = 0x0000FF63 # Insert, insert here + XK_Undo*: TKeySym = 0x0000FF65 # Undo, oops + XK_Redo*: TKeySym = 0x0000FF66 # redo, again + XK_Menu*: TKeySym = 0x0000FF67 + XK_Find*: TKeySym = 0x0000FF68 # Find, search + XK_Cancel*: TKeySym = 0x0000FF69 # Cancel, stop, abort, exit + XK_Help*: TKeySym = 0x0000FF6A # Help + XK_Break*: TKeySym = 0x0000FF6B + XK_Mode_switch*: TKeySym = 0x0000FF7E # Character set switch + XK_script_switch*: TKeySym = 0x0000FF7E # Alias for mode_switch + XK_Num_Lock*: TKeySym = 0x0000FF7F # Keypad Functions, keypad numbers cleverly chosen to map to ascii + XK_KP_Space*: TKeySym = 0x0000FF80 # space + XK_KP_Tab*: TKeySym = 0x0000FF89 + XK_KP_Enter*: TKeySym = 0x0000FF8D # enter + XK_KP_F1*: TKeySym = 0x0000FF91 # PF1, KP_A, ... + XK_KP_F2*: TKeySym = 0x0000FF92 + XK_KP_F3*: TKeySym = 0x0000FF93 + XK_KP_F4*: TKeySym = 0x0000FF94 + XK_KP_Home*: TKeySym = 0x0000FF95 + XK_KP_Left*: TKeySym = 0x0000FF96 + XK_KP_Up*: TKeySym = 0x0000FF97 + XK_KP_Right*: TKeySym = 0x0000FF98 + XK_KP_Down*: TKeySym = 0x0000FF99 + XK_KP_Prior*: TKeySym = 0x0000FF9A + XK_KP_Page_Up*: TKeySym = 0x0000FF9A + XK_KP_Next*: TKeySym = 0x0000FF9B + XK_KP_Page_Down*: TKeySym = 0x0000FF9B + XK_KP_End*: TKeySym = 0x0000FF9C + XK_KP_Begin*: TKeySym = 0x0000FF9D + XK_KP_Insert*: TKeySym = 0x0000FF9E + XK_KP_Delete*: TKeySym = 0x0000FF9F + XK_KP_Equal*: TKeySym = 0x0000FFBD # equals + XK_KP_Multiply*: TKeySym = 0x0000FFAA + XK_KP_Add*: TKeySym = 0x0000FFAB + XK_KP_Separator*: TKeySym = 0x0000FFAC # separator, often comma + XK_KP_Subtract*: TKeySym = 0x0000FFAD + XK_KP_Decimal*: TKeySym = 0x0000FFAE + XK_KP_Divide*: TKeySym = 0x0000FFAF + XK_KP_0*: TKeySym = 0x0000FFB0 + XK_KP_1*: TKeySym = 0x0000FFB1 + XK_KP_2*: TKeySym = 0x0000FFB2 + XK_KP_3*: TKeySym = 0x0000FFB3 + XK_KP_4*: TKeySym = 0x0000FFB4 + XK_KP_5*: TKeySym = 0x0000FFB5 + XK_KP_6*: TKeySym = 0x0000FFB6 + XK_KP_7*: TKeySym = 0x0000FFB7 + XK_KP_8*: TKeySym = 0x0000FFB8 + XK_KP_9*: TKeySym = 0x0000FFB9 #*\ + # * Auxilliary Functions; note the duplicate definitions for left and right + # * function keys; Sun keyboards and a few other manufactures have such + # * function key groups on the left and/or right sides of the keyboard. + # * We've not found a keyboard with more than 35 function keys total. + # * + XK_F1*: TKeySym = 0x0000FFBE + XK_F2*: TKeySym = 0x0000FFBF + XK_F3*: TKeySym = 0x0000FFC0 + XK_F4*: TKeySym = 0x0000FFC1 + XK_F5*: TKeySym = 0x0000FFC2 + XK_F6*: TKeySym = 0x0000FFC3 + XK_F7*: TKeySym = 0x0000FFC4 + XK_F8*: TKeySym = 0x0000FFC5 + XK_F9*: TKeySym = 0x0000FFC6 + XK_F10*: TKeySym = 0x0000FFC7 + XK_F11*: TKeySym = 0x0000FFC8 + XK_L1*: TKeySym = 0x0000FFC8 + XK_F12*: TKeySym = 0x0000FFC9 + XK_L2*: TKeySym = 0x0000FFC9 + XK_F13*: TKeySym = 0x0000FFCA + XK_L3*: TKeySym = 0x0000FFCA + XK_F14*: TKeySym = 0x0000FFCB + XK_L4*: TKeySym = 0x0000FFCB + XK_F15*: TKeySym = 0x0000FFCC + XK_L5*: TKeySym = 0x0000FFCC + XK_F16*: TKeySym = 0x0000FFCD + XK_L6*: TKeySym = 0x0000FFCD + XK_F17*: TKeySym = 0x0000FFCE + XK_L7*: TKeySym = 0x0000FFCE + XK_F18*: TKeySym = 0x0000FFCF + XK_L8*: TKeySym = 0x0000FFCF + XK_F19*: TKeySym = 0x0000FFD0 + XK_L9*: TKeySym = 0x0000FFD0 + XK_F20*: TKeySym = 0x0000FFD1 + XK_L10*: TKeySym = 0x0000FFD1 + XK_F21*: TKeySym = 0x0000FFD2 + XK_R1*: TKeySym = 0x0000FFD2 + XK_F22*: TKeySym = 0x0000FFD3 + XK_R2*: TKeySym = 0x0000FFD3 + XK_F23*: TKeySym = 0x0000FFD4 + XK_R3*: TKeySym = 0x0000FFD4 + XK_F24*: TKeySym = 0x0000FFD5 + XK_R4*: TKeySym = 0x0000FFD5 + XK_F25*: TKeySym = 0x0000FFD6 + XK_R5*: TKeySym = 0x0000FFD6 + XK_F26*: TKeySym = 0x0000FFD7 + XK_R6*: TKeySym = 0x0000FFD7 + XK_F27*: TKeySym = 0x0000FFD8 + XK_R7*: TKeySym = 0x0000FFD8 + XK_F28*: TKeySym = 0x0000FFD9 + XK_R8*: TKeySym = 0x0000FFD9 + XK_F29*: TKeySym = 0x0000FFDA + XK_R9*: TKeySym = 0x0000FFDA + XK_F30*: TKeySym = 0x0000FFDB + XK_R10*: TKeySym = 0x0000FFDB + XK_F31*: TKeySym = 0x0000FFDC + XK_R11*: TKeySym = 0x0000FFDC + XK_F32*: TKeySym = 0x0000FFDD + XK_R12*: TKeySym = 0x0000FFDD + XK_F33*: TKeySym = 0x0000FFDE + XK_R13*: TKeySym = 0x0000FFDE + XK_F34*: TKeySym = 0x0000FFDF + XK_R14*: TKeySym = 0x0000FFDF + XK_F35*: TKeySym = 0x0000FFE0 + XK_R15*: TKeySym = 0x0000FFE0 # Modifiers + XK_Shift_L*: TKeySym = 0x0000FFE1 # Left shift + XK_Shift_R*: TKeySym = 0x0000FFE2 # Right shift + XK_Control_L*: TKeySym = 0x0000FFE3 # Left control + XK_Control_R*: TKeySym = 0x0000FFE4 # Right control + XK_Caps_Lock*: TKeySym = 0x0000FFE5 # Caps lock + XK_Shift_Lock*: TKeySym = 0x0000FFE6 # Shift lock + XK_Meta_L*: TKeySym = 0x0000FFE7 # Left meta + XK_Meta_R*: TKeySym = 0x0000FFE8 # Right meta + XK_Alt_L*: TKeySym = 0x0000FFE9 # Left alt + XK_Alt_R*: TKeySym = 0x0000FFEA # Right alt + XK_Super_L*: TKeySym = 0x0000FFEB # Left super + XK_Super_R*: TKeySym = 0x0000FFEC # Right super + XK_Hyper_L*: TKeySym = 0x0000FFED # Left hyper + XK_Hyper_R*: TKeySym = 0x0000FFEE # Right hyper +# XK_MISCELLANY +#* +# * ISO 9995 Function and Modifier Keys +# * Byte 3 = = $FE +# * + +when defined(XK_XKB_KEYS) or true: + const + XK_ISO_Lock*: TKeySym = 0x0000FE01 + XK_ISO_Level2_Latch*: TKeySym = 0x0000FE02 + XK_ISO_Level3_Shift*: TKeySym = 0x0000FE03 + XK_ISO_Level3_Latch*: TKeySym = 0x0000FE04 + XK_ISO_Level3_Lock*: TKeySym = 0x0000FE05 + XK_ISO_Group_Shift*: TKeySym = 0x0000FF7E # Alias for mode_switch + XK_ISO_Group_Latch*: TKeySym = 0x0000FE06 + XK_ISO_Group_Lock*: TKeySym = 0x0000FE07 + XK_ISO_Next_Group*: TKeySym = 0x0000FE08 + XK_ISO_Next_Group_Lock*: TKeySym = 0x0000FE09 + XK_ISO_Prev_Group*: TKeySym = 0x0000FE0A + XK_ISO_Prev_Group_Lock*: TKeySym = 0x0000FE0B + XK_ISO_First_Group*: TKeySym = 0x0000FE0C + XK_ISO_First_Group_Lock*: TKeySym = 0x0000FE0D + XK_ISO_Last_Group*: TKeySym = 0x0000FE0E + XK_ISO_Last_Group_Lock*: TKeySym = 0x0000FE0F + XK_ISO_Left_Tab*: TKeySym = 0x0000FE20 + XK_ISO_Move_Line_Up*: TKeySym = 0x0000FE21 + XK_ISO_Move_Line_Down*: TKeySym = 0x0000FE22 + XK_ISO_Partial_Line_Up*: TKeySym = 0x0000FE23 + XK_ISO_Partial_Line_Down*: TKeySym = 0x0000FE24 + XK_ISO_Partial_Space_Left*: TKeySym = 0x0000FE25 + XK_ISO_Partial_Space_Right*: TKeySym = 0x0000FE26 + XK_ISO_Set_Margin_Left*: TKeySym = 0x0000FE27 + XK_ISO_Set_Margin_Right*: TKeySym = 0x0000FE28 + XK_ISO_Release_Margin_Left*: TKeySym = 0x0000FE29 + XK_ISO_Release_Margin_Right*: TKeySym = 0x0000FE2A + XK_ISO_Release_Both_Margins*: TKeySym = 0x0000FE2B + XK_ISO_Fast_Cursor_Left*: TKeySym = 0x0000FE2C + XK_ISO_Fast_Cursor_Right*: TKeySym = 0x0000FE2D + XK_ISO_Fast_Cursor_Up*: TKeySym = 0x0000FE2E + XK_ISO_Fast_Cursor_Down*: TKeySym = 0x0000FE2F + XK_ISO_Continuous_Underline*: TKeySym = 0x0000FE30 + XK_ISO_Discontinuous_Underline*: TKeySym = 0x0000FE31 + XK_ISO_Emphasize*: TKeySym = 0x0000FE32 + XK_ISO_Center_Object*: TKeySym = 0x0000FE33 + XK_ISO_Enter*: TKeySym = 0x0000FE34 + XK_dead_grave*: TKeySym = 0x0000FE50 + XK_dead_acute*: TKeySym = 0x0000FE51 + XK_dead_circumflex*: TKeySym = 0x0000FE52 + XK_dead_tilde*: TKeySym = 0x0000FE53 + XK_dead_macron*: TKeySym = 0x0000FE54 + XK_dead_breve*: TKeySym = 0x0000FE55 + XK_dead_abovedot*: TKeySym = 0x0000FE56 + XK_dead_diaeresis*: TKeySym = 0x0000FE57 + XK_dead_abovering*: TKeySym = 0x0000FE58 + XK_dead_doubleacute*: TKeySym = 0x0000FE59 + XK_dead_caron*: TKeySym = 0x0000FE5A + XK_dead_cedilla*: TKeySym = 0x0000FE5B + XK_dead_ogonek*: TKeySym = 0x0000FE5C + XK_dead_iota*: TKeySym = 0x0000FE5D + XK_dead_voiced_sound*: TKeySym = 0x0000FE5E + XK_dead_semivoiced_sound*: TKeySym = 0x0000FE5F + XK_dead_belowdot*: TKeySym = 0x0000FE60 + XK_dead_hook*: TKeySym = 0x0000FE61 + XK_dead_horn*: TKeySym = 0x0000FE62 + XK_First_Virtual_Screen*: TKeySym = 0x0000FED0 + XK_Prev_Virtual_Screen*: TKeySym = 0x0000FED1 + XK_Next_Virtual_Screen*: TKeySym = 0x0000FED2 + XK_Last_Virtual_Screen*: TKeySym = 0x0000FED4 + XK_Terminate_Server*: TKeySym = 0x0000FED5 + XK_AccessX_Enable*: TKeySym = 0x0000FE70 + XK_AccessX_Feedback_Enable*: TKeySym = 0x0000FE71 + XK_RepeatKeys_Enable*: TKeySym = 0x0000FE72 + XK_SlowKeys_Enable*: TKeySym = 0x0000FE73 + XK_BounceKeys_Enable*: TKeySym = 0x0000FE74 + XK_StickyKeys_Enable*: TKeySym = 0x0000FE75 + XK_MouseKeys_Enable*: TKeySym = 0x0000FE76 + XK_MouseKeys_Accel_Enable*: TKeySym = 0x0000FE77 + XK_Overlay1_Enable*: TKeySym = 0x0000FE78 + XK_Overlay2_Enable*: TKeySym = 0x0000FE79 + XK_AudibleBell_Enable*: TKeySym = 0x0000FE7A + XK_Pointer_Left*: TKeySym = 0x0000FEE0 + XK_Pointer_Right*: TKeySym = 0x0000FEE1 + XK_Pointer_Up*: TKeySym = 0x0000FEE2 + XK_Pointer_Down*: TKeySym = 0x0000FEE3 + XK_Pointer_UpLeft*: TKeySym = 0x0000FEE4 + XK_Pointer_UpRight*: TKeySym = 0x0000FEE5 + XK_Pointer_DownLeft*: TKeySym = 0x0000FEE6 + XK_Pointer_DownRight*: TKeySym = 0x0000FEE7 + XK_Pointer_Button_Dflt*: TKeySym = 0x0000FEE8 + XK_Pointer_Button1*: TKeySym = 0x0000FEE9 + XK_Pointer_Button2*: TKeySym = 0x0000FEEA + XK_Pointer_Button3*: TKeySym = 0x0000FEEB + XK_Pointer_Button4*: TKeySym = 0x0000FEEC + XK_Pointer_Button5*: TKeySym = 0x0000FEED + XK_Pointer_DblClick_Dflt*: TKeySym = 0x0000FEEE + XK_Pointer_DblClick1*: TKeySym = 0x0000FEEF + XK_Pointer_DblClick2*: TKeySym = 0x0000FEF0 + XK_Pointer_DblClick3*: TKeySym = 0x0000FEF1 + XK_Pointer_DblClick4*: TKeySym = 0x0000FEF2 + XK_Pointer_DblClick5*: TKeySym = 0x0000FEF3 + XK_Pointer_Drag_Dflt*: TKeySym = 0x0000FEF4 + XK_Pointer_Drag1*: TKeySym = 0x0000FEF5 + XK_Pointer_Drag2*: TKeySym = 0x0000FEF6 + XK_Pointer_Drag3*: TKeySym = 0x0000FEF7 + XK_Pointer_Drag4*: TKeySym = 0x0000FEF8 + XK_Pointer_Drag5*: TKeySym = 0x0000FEFD + XK_Pointer_EnableKeys*: TKeySym = 0x0000FEF9 + XK_Pointer_Accelerate*: TKeySym = 0x0000FEFA + XK_Pointer_DfltBtnNext*: TKeySym = 0x0000FEFB + XK_Pointer_DfltBtnPrev*: TKeySym = 0x0000FEFC + #* + # * 3270 Terminal Keys + # * Byte 3 = = $FD + # * + +when defined(XK_3270) or true: + const + XK_3270_Duplicate*: TKeySym = 0x0000FD01 + XK_3270_FieldMark*: TKeySym = 0x0000FD02 + XK_3270_Right2*: TKeySym = 0x0000FD03 + XK_3270_Left2*: TKeySym = 0x0000FD04 + XK_3270_BackTab*: TKeySym = 0x0000FD05 + XK_3270_EraseEOF*: TKeySym = 0x0000FD06 + XK_3270_EraseInput*: TKeySym = 0x0000FD07 + XK_3270_Reset*: TKeySym = 0x0000FD08 + XK_3270_Quit*: TKeySym = 0x0000FD09 + XK_3270_PA1*: TKeySym = 0x0000FD0A + XK_3270_PA2*: TKeySym = 0x0000FD0B + XK_3270_PA3*: TKeySym = 0x0000FD0C + XK_3270_Test*: TKeySym = 0x0000FD0D + XK_3270_Attn*: TKeySym = 0x0000FD0E + XK_3270_CursorBlink*: TKeySym = 0x0000FD0F + XK_3270_AltCursor*: TKeySym = 0x0000FD10 + XK_3270_KeyClick*: TKeySym = 0x0000FD11 + XK_3270_Jump*: TKeySym = 0x0000FD12 + XK_3270_Ident*: TKeySym = 0x0000FD13 + XK_3270_Rule*: TKeySym = 0x0000FD14 + XK_3270_Copy*: TKeySym = 0x0000FD15 + XK_3270_Play*: TKeySym = 0x0000FD16 + XK_3270_Setup*: TKeySym = 0x0000FD17 + XK_3270_Record*: TKeySym = 0x0000FD18 + XK_3270_ChangeScreen*: TKeySym = 0x0000FD19 + XK_3270_DeleteWord*: TKeySym = 0x0000FD1A + XK_3270_ExSelect*: TKeySym = 0x0000FD1B + XK_3270_CursorSelect*: TKeySym = 0x0000FD1C + XK_3270_PrintScreen*: TKeySym = 0x0000FD1D + XK_3270_Enter*: TKeySym = 0x0000FD1E +#* +# * Latin 1 +# * Byte 3 = 0 +# * + +when defined(XK_LATIN1) or true: + const + XK_space*: TKeySym = 0x00000020 + XK_exclam*: TKeySym = 0x00000021 + XK_quotedbl*: TKeySym = 0x00000022 + XK_numbersign*: TKeySym = 0x00000023 + XK_dollar*: TKeySym = 0x00000024 + XK_percent*: TKeySym = 0x00000025 + XK_ampersand*: TKeySym = 0x00000026 + XK_apostrophe*: TKeySym = 0x00000027 + XK_quoteright*: TKeySym = 0x00000027 # deprecated + XK_parenleft*: TKeySym = 0x00000028 + XK_parenright*: TKeySym = 0x00000029 + XK_asterisk*: TKeySym = 0x0000002A + XK_plus*: TKeySym = 0x0000002B + XK_comma*: TKeySym = 0x0000002C + XK_minus*: TKeySym = 0x0000002D + XK_period*: TKeySym = 0x0000002E + XK_slash*: TKeySym = 0x0000002F + XK_0*: TKeySym = 0x00000030 + XK_1*: TKeySym = 0x00000031 + XK_2*: TKeySym = 0x00000032 + XK_3*: TKeySym = 0x00000033 + XK_4*: TKeySym = 0x00000034 + XK_5*: TKeySym = 0x00000035 + XK_6*: TKeySym = 0x00000036 + XK_7*: TKeySym = 0x00000037 + XK_8*: TKeySym = 0x00000038 + XK_9*: TKeySym = 0x00000039 + XK_colon*: TKeySym = 0x0000003A + XK_semicolon*: TKeySym = 0x0000003B + XK_less*: TKeySym = 0x0000003C + XK_equal*: TKeySym = 0x0000003D + XK_greater*: TKeySym = 0x0000003E + XK_question*: TKeySym = 0x0000003F + XK_at*: TKeySym = 0x00000040 + XKc_A*: TKeySym = 0x00000041 + XKc_B*: TKeySym = 0x00000042 + XKc_C*: TKeySym = 0x00000043 + XKc_D*: TKeySym = 0x00000044 + XKc_E*: TKeySym = 0x00000045 + XKc_F*: TKeySym = 0x00000046 + XKc_G*: TKeySym = 0x00000047 + XKc_H*: TKeySym = 0x00000048 + XKc_I*: TKeySym = 0x00000049 + XKc_J*: TKeySym = 0x0000004A + XKc_K*: TKeySym = 0x0000004B + XKc_L*: TKeySym = 0x0000004C + XKc_M*: TKeySym = 0x0000004D + XKc_N*: TKeySym = 0x0000004E + XKc_O*: TKeySym = 0x0000004F + XKc_P*: TKeySym = 0x00000050 + XKc_Q*: TKeySym = 0x00000051 + XKc_R*: TKeySym = 0x00000052 + XKc_S*: TKeySym = 0x00000053 + XKc_T*: TKeySym = 0x00000054 + XKc_U*: TKeySym = 0x00000055 + XKc_V*: TKeySym = 0x00000056 + XKc_W*: TKeySym = 0x00000057 + XKc_X*: TKeySym = 0x00000058 + XKc_Y*: TKeySym = 0x00000059 + XKc_Z*: TKeySym = 0x0000005A + XK_bracketleft*: TKeySym = 0x0000005B + XK_backslash*: TKeySym = 0x0000005C + XK_bracketright*: TKeySym = 0x0000005D + XK_asciicircum*: TKeySym = 0x0000005E + XK_underscore*: TKeySym = 0x0000005F + XK_grave*: TKeySym = 0x00000060 + XK_quoteleft*: TKeySym = 0x00000060 # deprecated + XK_a*: TKeySym = 0x00000061 + XK_b*: TKeySym = 0x00000062 + XK_c*: TKeySym = 0x00000063 + XK_d*: TKeySym = 0x00000064 + XK_e*: TKeySym = 0x00000065 + XK_f*: TKeySym = 0x00000066 + XK_g*: TKeySym = 0x00000067 + XK_h*: TKeySym = 0x00000068 + XK_i*: TKeySym = 0x00000069 + XK_j*: TKeySym = 0x0000006A + XK_k*: TKeySym = 0x0000006B + XK_l*: TKeySym = 0x0000006C + XK_m*: TKeySym = 0x0000006D + XK_n*: TKeySym = 0x0000006E + XK_o*: TKeySym = 0x0000006F + XK_p*: TKeySym = 0x00000070 + XK_q*: TKeySym = 0x00000071 + XK_r*: TKeySym = 0x00000072 + XK_s*: TKeySym = 0x00000073 + XK_t*: TKeySym = 0x00000074 + XK_u*: TKeySym = 0x00000075 + XK_v*: TKeySym = 0x00000076 + XK_w*: TKeySym = 0x00000077 + XK_x*: TKeySym = 0x00000078 + XK_y*: TKeySym = 0x00000079 + XK_z*: TKeySym = 0x0000007A + XK_braceleft*: TKeySym = 0x0000007B + XK_bar*: TKeySym = 0x0000007C + XK_braceright*: TKeySym = 0x0000007D + XK_asciitilde*: TKeySym = 0x0000007E + XK_nobreakspace*: TKeySym = 0x000000A0 + XK_exclamdown*: TKeySym = 0x000000A1 + XK_cent*: TKeySym = 0x000000A2 + XK_sterling*: TKeySym = 0x000000A3 + XK_currency*: TKeySym = 0x000000A4 + XK_yen*: TKeySym = 0x000000A5 + XK_brokenbar*: TKeySym = 0x000000A6 + XK_section*: TKeySym = 0x000000A7 + XK_diaeresis*: TKeySym = 0x000000A8 + XK_copyright*: TKeySym = 0x000000A9 + XK_ordfeminine*: TKeySym = 0x000000AA + XK_guillemotleft*: TKeySym = 0x000000AB # left angle quotation mark + XK_notsign*: TKeySym = 0x000000AC + XK_hyphen*: TKeySym = 0x000000AD + XK_registered*: TKeySym = 0x000000AE + XK_macron*: TKeySym = 0x000000AF + XK_degree*: TKeySym = 0x000000B0 + XK_plusminus*: TKeySym = 0x000000B1 + XK_twosuperior*: TKeySym = 0x000000B2 + XK_threesuperior*: TKeySym = 0x000000B3 + XK_acute*: TKeySym = 0x000000B4 + XK_mu*: TKeySym = 0x000000B5 + XK_paragraph*: TKeySym = 0x000000B6 + XK_periodcentered*: TKeySym = 0x000000B7 + XK_cedilla*: TKeySym = 0x000000B8 + XK_onesuperior*: TKeySym = 0x000000B9 + XK_masculine*: TKeySym = 0x000000BA + XK_guillemotright*: TKeySym = 0x000000BB # right angle quotation mark + XK_onequarter*: TKeySym = 0x000000BC + XK_onehalf*: TKeySym = 0x000000BD + XK_threequarters*: TKeySym = 0x000000BE + XK_questiondown*: TKeySym = 0x000000BF + XKc_Agrave*: TKeySym = 0x000000C0 + XKc_Aacute*: TKeySym = 0x000000C1 + XKc_Acircumflex*: TKeySym = 0x000000C2 + XKc_Atilde*: TKeySym = 0x000000C3 + XKc_Adiaeresis*: TKeySym = 0x000000C4 + XKc_Aring*: TKeySym = 0x000000C5 + XKc_AE*: TKeySym = 0x000000C6 + XKc_Ccedilla*: TKeySym = 0x000000C7 + XKc_Egrave*: TKeySym = 0x000000C8 + XKc_Eacute*: TKeySym = 0x000000C9 + XKc_Ecircumflex*: TKeySym = 0x000000CA + XKc_Ediaeresis*: TKeySym = 0x000000CB + XKc_Igrave*: TKeySym = 0x000000CC + XKc_Iacute*: TKeySym = 0x000000CD + XKc_Icircumflex*: TKeySym = 0x000000CE + XKc_Idiaeresis*: TKeySym = 0x000000CF + XKc_ETH*: TKeySym = 0x000000D0 + XKc_Ntilde*: TKeySym = 0x000000D1 + XKc_Ograve*: TKeySym = 0x000000D2 + XKc_Oacute*: TKeySym = 0x000000D3 + XKc_Ocircumflex*: TKeySym = 0x000000D4 + XKc_Otilde*: TKeySym = 0x000000D5 + XKc_Odiaeresis*: TKeySym = 0x000000D6 + XK_multiply*: TKeySym = 0x000000D7 + XKc_Ooblique*: TKeySym = 0x000000D8 + XKc_Oslash*: TKeySym = XKc_Ooblique + XKc_Ugrave*: TKeySym = 0x000000D9 + XKc_Uacute*: TKeySym = 0x000000DA + XKc_Ucircumflex*: TKeySym = 0x000000DB + XKc_Udiaeresis*: TKeySym = 0x000000DC + XKc_Yacute*: TKeySym = 0x000000DD + XKc_THORN*: TKeySym = 0x000000DE + XK_ssharp*: TKeySym = 0x000000DF + XK_agrave*: TKeySym = 0x000000E0 + XK_aacute*: TKeySym = 0x000000E1 + XK_acircumflex*: TKeySym = 0x000000E2 + XK_atilde*: TKeySym = 0x000000E3 + XK_adiaeresis*: TKeySym = 0x000000E4 + XK_aring*: TKeySym = 0x000000E5 + XK_ae*: TKeySym = 0x000000E6 + XK_ccedilla*: TKeySym = 0x000000E7 + XK_egrave*: TKeySym = 0x000000E8 + XK_eacute*: TKeySym = 0x000000E9 + XK_ecircumflex*: TKeySym = 0x000000EA + XK_ediaeresis*: TKeySym = 0x000000EB + XK_igrave*: TKeySym = 0x000000EC + XK_iacute*: TKeySym = 0x000000ED + XK_icircumflex*: TKeySym = 0x000000EE + XK_idiaeresis*: TKeySym = 0x000000EF + XK_eth*: TKeySym = 0x000000F0 + XK_ntilde*: TKeySym = 0x000000F1 + XK_ograve*: TKeySym = 0x000000F2 + XK_oacute*: TKeySym = 0x000000F3 + XK_ocircumflex*: TKeySym = 0x000000F4 + XK_otilde*: TKeySym = 0x000000F5 + XK_odiaeresis*: TKeySym = 0x000000F6 + XK_division*: TKeySym = 0x000000F7 + XK_oslash*: TKeySym = 0x000000F8 + XK_ooblique*: TKeySym = XK_oslash + XK_ugrave*: TKeySym = 0x000000F9 + XK_uacute*: TKeySym = 0x000000FA + XK_ucircumflex*: TKeySym = 0x000000FB + XK_udiaeresis*: TKeySym = 0x000000FC + XK_yacute*: TKeySym = 0x000000FD + XK_thorn*: TKeySym = 0x000000FE + XK_ydiaeresis*: TKeySym = 0x000000FF +# XK_LATIN1 +#* +# * Latin 2 +# * Byte 3 = 1 +# * + +when defined(XK_LATIN2) or true: + const + XKc_Aogonek*: TKeySym = 0x000001A1 + XK_breve*: TKeySym = 0x000001A2 + XKc_Lstroke*: TKeySym = 0x000001A3 + XKc_Lcaron*: TKeySym = 0x000001A5 + XKc_Sacute*: TKeySym = 0x000001A6 + XKc_Scaron*: TKeySym = 0x000001A9 + XKc_Scedilla*: TKeySym = 0x000001AA + XKc_Tcaron*: TKeySym = 0x000001AB + XKc_Zacute*: TKeySym = 0x000001AC + XKc_Zcaron*: TKeySym = 0x000001AE + XKc_Zabovedot*: TKeySym = 0x000001AF + XK_aogonek*: TKeySym = 0x000001B1 + XK_ogonek*: TKeySym = 0x000001B2 + XK_lstroke*: TKeySym = 0x000001B3 + XK_lcaron*: TKeySym = 0x000001B5 + XK_sacute*: TKeySym = 0x000001B6 + XK_caron*: TKeySym = 0x000001B7 + XK_scaron*: TKeySym = 0x000001B9 + XK_scedilla*: TKeySym = 0x000001BA + XK_tcaron*: TKeySym = 0x000001BB + XK_zacute*: TKeySym = 0x000001BC + XK_doubleacute*: TKeySym = 0x000001BD + XK_zcaron*: TKeySym = 0x000001BE + XK_zabovedot*: TKeySym = 0x000001BF + XKc_Racute*: TKeySym = 0x000001C0 + XKc_Abreve*: TKeySym = 0x000001C3 + XKc_Lacute*: TKeySym = 0x000001C5 + XKc_Cacute*: TKeySym = 0x000001C6 + XKc_Ccaron*: TKeySym = 0x000001C8 + XKc_Eogonek*: TKeySym = 0x000001CA + XKc_Ecaron*: TKeySym = 0x000001CC + XKc_Dcaron*: TKeySym = 0x000001CF + XKc_Dstroke*: TKeySym = 0x000001D0 + XKc_Nacute*: TKeySym = 0x000001D1 + XKc_Ncaron*: TKeySym = 0x000001D2 + XKc_Odoubleacute*: TKeySym = 0x000001D5 + XKc_Rcaron*: TKeySym = 0x000001D8 + XKc_Uring*: TKeySym = 0x000001D9 + XKc_Udoubleacute*: TKeySym = 0x000001DB + XKc_Tcedilla*: TKeySym = 0x000001DE + XK_racute*: TKeySym = 0x000001E0 + XK_abreve*: TKeySym = 0x000001E3 + XK_lacute*: TKeySym = 0x000001E5 + XK_cacute*: TKeySym = 0x000001E6 + XK_ccaron*: TKeySym = 0x000001E8 + XK_eogonek*: TKeySym = 0x000001EA + XK_ecaron*: TKeySym = 0x000001EC + XK_dcaron*: TKeySym = 0x000001EF + XK_dstroke*: TKeySym = 0x000001F0 + XK_nacute*: TKeySym = 0x000001F1 + XK_ncaron*: TKeySym = 0x000001F2 + XK_odoubleacute*: TKeySym = 0x000001F5 + XK_udoubleacute*: TKeySym = 0x000001FB + XK_rcaron*: TKeySym = 0x000001F8 + XK_uring*: TKeySym = 0x000001F9 + XK_tcedilla*: TKeySym = 0x000001FE + XK_abovedot*: TKeySym = 0x000001FF +# XK_LATIN2 +#* +# * Latin 3 +# * Byte 3 = 2 +# * + +when defined(XK_LATIN3) or true: + const + XKc_Hstroke*: TKeySym = 0x000002A1 + XKc_Hcircumflex*: TKeySym = 0x000002A6 + XKc_Iabovedot*: TKeySym = 0x000002A9 + XKc_Gbreve*: TKeySym = 0x000002AB + XKc_Jcircumflex*: TKeySym = 0x000002AC + XK_hstroke*: TKeySym = 0x000002B1 + XK_hcircumflex*: TKeySym = 0x000002B6 + XK_idotless*: TKeySym = 0x000002B9 + XK_gbreve*: TKeySym = 0x000002BB + XK_jcircumflex*: TKeySym = 0x000002BC + XKc_Cabovedot*: TKeySym = 0x000002C5 + XKc_Ccircumflex*: TKeySym = 0x000002C6 + XKc_Gabovedot*: TKeySym = 0x000002D5 + XKc_Gcircumflex*: TKeySym = 0x000002D8 + XKc_Ubreve*: TKeySym = 0x000002DD + XKc_Scircumflex*: TKeySym = 0x000002DE + XK_cabovedot*: TKeySym = 0x000002E5 + XK_ccircumflex*: TKeySym = 0x000002E6 + XK_gabovedot*: TKeySym = 0x000002F5 + XK_gcircumflex*: TKeySym = 0x000002F8 + XK_ubreve*: TKeySym = 0x000002FD + XK_scircumflex*: TKeySym = 0x000002FE +# XK_LATIN3 +#* +# * Latin 4 +# * Byte 3 = 3 +# * + +when defined(XK_LATIN4) or true: + const + XK_kra*: TKeySym = 0x000003A2 + XK_kappa*: TKeySym = 0x000003A2 # deprecated + XKc_Rcedilla*: TKeySym = 0x000003A3 + XKc_Itilde*: TKeySym = 0x000003A5 + XKc_Lcedilla*: TKeySym = 0x000003A6 + XKc_Emacron*: TKeySym = 0x000003AA + XKc_Gcedilla*: TKeySym = 0x000003AB + XKc_Tslash*: TKeySym = 0x000003AC + XK_rcedilla*: TKeySym = 0x000003B3 + XK_itilde*: TKeySym = 0x000003B5 + XK_lcedilla*: TKeySym = 0x000003B6 + XK_emacron*: TKeySym = 0x000003BA + XK_gcedilla*: TKeySym = 0x000003BB + XK_tslash*: TKeySym = 0x000003BC + XKc_ENG*: TKeySym = 0x000003BD + XK_eng*: TKeySym = 0x000003BF + XKc_Amacron*: TKeySym = 0x000003C0 + XKc_Iogonek*: TKeySym = 0x000003C7 + XKc_Eabovedot*: TKeySym = 0x000003CC + XKc_Imacron*: TKeySym = 0x000003CF + XKc_Ncedilla*: TKeySym = 0x000003D1 + XKc_Omacron*: TKeySym = 0x000003D2 + XKc_Kcedilla*: TKeySym = 0x000003D3 + XKc_Uogonek*: TKeySym = 0x000003D9 + XKc_Utilde*: TKeySym = 0x000003DD + XKc_Umacron*: TKeySym = 0x000003DE + XK_amacron*: TKeySym = 0x000003E0 + XK_iogonek*: TKeySym = 0x000003E7 + XK_eabovedot*: TKeySym = 0x000003EC + XK_imacron*: TKeySym = 0x000003EF + XK_ncedilla*: TKeySym = 0x000003F1 + XK_omacron*: TKeySym = 0x000003F2 + XK_kcedilla*: TKeySym = 0x000003F3 + XK_uogonek*: TKeySym = 0x000003F9 + XK_utilde*: TKeySym = 0x000003FD + XK_umacron*: TKeySym = 0x000003FE +# XK_LATIN4 +#* +# * Latin-8 +# * Byte 3 = 18 +# * + +when defined(XK_LATIN8) or true: + const + XKc_Babovedot*: TKeySym = 0x000012A1 + XK_babovedot*: TKeySym = 0x000012A2 + XKc_Dabovedot*: TKeySym = 0x000012A6 + XKc_Wgrave*: TKeySym = 0x000012A8 + XKc_Wacute*: TKeySym = 0x000012AA + XK_dabovedot*: TKeySym = 0x000012AB + XKc_Ygrave*: TKeySym = 0x000012AC + XKc_Fabovedot*: TKeySym = 0x000012B0 + XK_fabovedot*: TKeySym = 0x000012B1 + XKc_Mabovedot*: TKeySym = 0x000012B4 + XK_mabovedot*: TKeySym = 0x000012B5 + XKc_Pabovedot*: TKeySym = 0x000012B7 + XK_wgrave*: TKeySym = 0x000012B8 + XK_pabovedot*: TKeySym = 0x000012B9 + XK_wacute*: TKeySym = 0x000012BA + XKc_Sabovedot*: TKeySym = 0x000012BB + XK_ygrave*: TKeySym = 0x000012BC + XKc_Wdiaeresis*: TKeySym = 0x000012BD + XK_wdiaeresis*: TKeySym = 0x000012BE + XK_sabovedot*: TKeySym = 0x000012BF + XKc_Wcircumflex*: TKeySym = 0x000012D0 + XKc_Tabovedot*: TKeySym = 0x000012D7 + XKc_Ycircumflex*: TKeySym = 0x000012DE + XK_wcircumflex*: TKeySym = 0x000012F0 + XK_tabovedot*: TKeySym = 0x000012F7 + XK_ycircumflex*: TKeySym = 0x000012FE +# XK_LATIN8 +#* +# * Latin-9 (a.k.a. Latin-0) +# * Byte 3 = 19 +# * + +when defined(XK_LATIN9) or true: + const + XKc_OE*: TKeySym = 0x000013BC + XK_oe*: TKeySym = 0x000013BD + XKc_Ydiaeresis*: TKeySym = 0x000013BE +# XK_LATIN9 +#* +# * Katakana +# * Byte 3 = 4 +# * + +when defined(XK_KATAKANA) or true: + const + XK_overline*: TKeySym = 0x0000047E + XK_kana_fullstop*: TKeySym = 0x000004A1 + XK_kana_openingbracket*: TKeySym = 0x000004A2 + XK_kana_closingbracket*: TKeySym = 0x000004A3 + XK_kana_comma*: TKeySym = 0x000004A4 + XK_kana_conjunctive*: TKeySym = 0x000004A5 + XK_kana_middledot*: TKeySym = 0x000004A5 # deprecated + XKc_kana_WO*: TKeySym = 0x000004A6 + XK_kana_a*: TKeySym = 0x000004A7 + XK_kana_i*: TKeySym = 0x000004A8 + XK_kana_u*: TKeySym = 0x000004A9 + XK_kana_e*: TKeySym = 0x000004AA + XK_kana_o*: TKeySym = 0x000004AB + XK_kana_ya*: TKeySym = 0x000004AC + XK_kana_yu*: TKeySym = 0x000004AD + XK_kana_yo*: TKeySym = 0x000004AE + XK_kana_tsu*: TKeySym = 0x000004AF + XK_kana_tu*: TKeySym = 0x000004AF # deprecated + XK_prolongedsound*: TKeySym = 0x000004B0 + XKc_kana_A*: TKeySym = 0x000004B1 + XKc_kana_I*: TKeySym = 0x000004B2 + XKc_kana_U*: TKeySym = 0x000004B3 + XKc_kana_E*: TKeySym = 0x000004B4 + XKc_kana_O*: TKeySym = 0x000004B5 + XKc_kana_KA*: TKeySym = 0x000004B6 + XKc_kana_KI*: TKeySym = 0x000004B7 + XKc_kana_KU*: TKeySym = 0x000004B8 + XKc_kana_KE*: TKeySym = 0x000004B9 + XKc_kana_KO*: TKeySym = 0x000004BA + XKc_kana_SA*: TKeySym = 0x000004BB + XKc_kana_SHI*: TKeySym = 0x000004BC + XKc_kana_SU*: TKeySym = 0x000004BD + XKc_kana_SE*: TKeySym = 0x000004BE + XKc_kana_SO*: TKeySym = 0x000004BF + XKc_kana_TA*: TKeySym = 0x000004C0 + XKc_kana_CHI*: TKeySym = 0x000004C1 + XKc_kana_TI*: TKeySym = 0x000004C1 # deprecated + XKc_kana_TSU*: TKeySym = 0x000004C2 + XKc_kana_TU*: TKeySym = 0x000004C2 # deprecated + XKc_kana_TE*: TKeySym = 0x000004C3 + XKc_kana_TO*: TKeySym = 0x000004C4 + XKc_kana_NA*: TKeySym = 0x000004C5 + XKc_kana_NI*: TKeySym = 0x000004C6 + XKc_kana_NU*: TKeySym = 0x000004C7 + XKc_kana_NE*: TKeySym = 0x000004C8 + XKc_kana_NO*: TKeySym = 0x000004C9 + XKc_kana_HA*: TKeySym = 0x000004CA + XKc_kana_HI*: TKeySym = 0x000004CB + XKc_kana_FU*: TKeySym = 0x000004CC + XKc_kana_HU*: TKeySym = 0x000004CC # deprecated + XKc_kana_HE*: TKeySym = 0x000004CD + XKc_kana_HO*: TKeySym = 0x000004CE + XKc_kana_MA*: TKeySym = 0x000004CF + XKc_kana_MI*: TKeySym = 0x000004D0 + XKc_kana_MU*: TKeySym = 0x000004D1 + XKc_kana_ME*: TKeySym = 0x000004D2 + XKc_kana_MO*: TKeySym = 0x000004D3 + XKc_kana_YA*: TKeySym = 0x000004D4 + XKc_kana_YU*: TKeySym = 0x000004D5 + XKc_kana_YO*: TKeySym = 0x000004D6 + XKc_kana_RA*: TKeySym = 0x000004D7 + XKc_kana_RI*: TKeySym = 0x000004D8 + XKc_kana_RU*: TKeySym = 0x000004D9 + XKc_kana_RE*: TKeySym = 0x000004DA + XKc_kana_RO*: TKeySym = 0x000004DB + XKc_kana_WA*: TKeySym = 0x000004DC + XKc_kana_N*: TKeySym = 0x000004DD + XK_voicedsound*: TKeySym = 0x000004DE + XK_semivoicedsound*: TKeySym = 0x000004DF + XK_kana_switch*: TKeySym = 0x0000FF7E # Alias for mode_switch +# XK_KATAKANA +#* +# * Arabic +# * Byte 3 = 5 +# * + +when defined(XK_ARABIC) or true: + const + XK_Farsi_0*: TKeySym = 0x00000590 + XK_Farsi_1*: TKeySym = 0x00000591 + XK_Farsi_2*: TKeySym = 0x00000592 + XK_Farsi_3*: TKeySym = 0x00000593 + XK_Farsi_4*: TKeySym = 0x00000594 + XK_Farsi_5*: TKeySym = 0x00000595 + XK_Farsi_6*: TKeySym = 0x00000596 + XK_Farsi_7*: TKeySym = 0x00000597 + XK_Farsi_8*: TKeySym = 0x00000598 + XK_Farsi_9*: TKeySym = 0x00000599 + XK_Arabic_percent*: TKeySym = 0x000005A5 + XK_Arabic_superscript_alef*: TKeySym = 0x000005A6 + XK_Arabic_tteh*: TKeySym = 0x000005A7 + XK_Arabic_peh*: TKeySym = 0x000005A8 + XK_Arabic_tcheh*: TKeySym = 0x000005A9 + XK_Arabic_ddal*: TKeySym = 0x000005AA + XK_Arabic_rreh*: TKeySym = 0x000005AB + XK_Arabic_comma*: TKeySym = 0x000005AC + XK_Arabic_fullstop*: TKeySym = 0x000005AE + XK_Arabic_0*: TKeySym = 0x000005B0 + XK_Arabic_1*: TKeySym = 0x000005B1 + XK_Arabic_2*: TKeySym = 0x000005B2 + XK_Arabic_3*: TKeySym = 0x000005B3 + XK_Arabic_4*: TKeySym = 0x000005B4 + XK_Arabic_5*: TKeySym = 0x000005B5 + XK_Arabic_6*: TKeySym = 0x000005B6 + XK_Arabic_7*: TKeySym = 0x000005B7 + XK_Arabic_8*: TKeySym = 0x000005B8 + XK_Arabic_9*: TKeySym = 0x000005B9 + XK_Arabic_semicolon*: TKeySym = 0x000005BB + XK_Arabic_question_mark*: TKeySym = 0x000005BF + XK_Arabic_hamza*: TKeySym = 0x000005C1 + XK_Arabic_maddaonalef*: TKeySym = 0x000005C2 + XK_Arabic_hamzaonalef*: TKeySym = 0x000005C3 + XK_Arabic_hamzaonwaw*: TKeySym = 0x000005C4 + XK_Arabic_hamzaunderalef*: TKeySym = 0x000005C5 + XK_Arabic_hamzaonyeh*: TKeySym = 0x000005C6 + XK_Arabic_alef*: TKeySym = 0x000005C7 + XK_Arabic_beh*: TKeySym = 0x000005C8 + XK_Arabic_tehmarbuta*: TKeySym = 0x000005C9 + XK_Arabic_teh*: TKeySym = 0x000005CA + XK_Arabic_theh*: TKeySym = 0x000005CB + XK_Arabic_jeem*: TKeySym = 0x000005CC + XK_Arabic_hah*: TKeySym = 0x000005CD + XK_Arabic_khah*: TKeySym = 0x000005CE + XK_Arabic_dal*: TKeySym = 0x000005CF + XK_Arabic_thal*: TKeySym = 0x000005D0 + XK_Arabic_ra*: TKeySym = 0x000005D1 + XK_Arabic_zain*: TKeySym = 0x000005D2 + XK_Arabic_seen*: TKeySym = 0x000005D3 + XK_Arabic_sheen*: TKeySym = 0x000005D4 + XK_Arabic_sad*: TKeySym = 0x000005D5 + XK_Arabic_dad*: TKeySym = 0x000005D6 + XK_Arabic_tah*: TKeySym = 0x000005D7 + XK_Arabic_zah*: TKeySym = 0x000005D8 + XK_Arabic_ain*: TKeySym = 0x000005D9 + XK_Arabic_ghain*: TKeySym = 0x000005DA + XK_Arabic_tatweel*: TKeySym = 0x000005E0 + XK_Arabic_feh*: TKeySym = 0x000005E1 + XK_Arabic_qaf*: TKeySym = 0x000005E2 + XK_Arabic_kaf*: TKeySym = 0x000005E3 + XK_Arabic_lam*: TKeySym = 0x000005E4 + XK_Arabic_meem*: TKeySym = 0x000005E5 + XK_Arabic_noon*: TKeySym = 0x000005E6 + XK_Arabic_ha*: TKeySym = 0x000005E7 + XK_Arabic_heh*: TKeySym = 0x000005E7 # deprecated + XK_Arabic_waw*: TKeySym = 0x000005E8 + XK_Arabic_alefmaksura*: TKeySym = 0x000005E9 + XK_Arabic_yeh*: TKeySym = 0x000005EA + XK_Arabic_fathatan*: TKeySym = 0x000005EB + XK_Arabic_dammatan*: TKeySym = 0x000005EC + XK_Arabic_kasratan*: TKeySym = 0x000005ED + XK_Arabic_fatha*: TKeySym = 0x000005EE + XK_Arabic_damma*: TKeySym = 0x000005EF + XK_Arabic_kasra*: TKeySym = 0x000005F0 + XK_Arabic_shadda*: TKeySym = 0x000005F1 + XK_Arabic_sukun*: TKeySym = 0x000005F2 + XK_Arabic_madda_above*: TKeySym = 0x000005F3 + XK_Arabic_hamza_above*: TKeySym = 0x000005F4 + XK_Arabic_hamza_below*: TKeySym = 0x000005F5 + XK_Arabic_jeh*: TKeySym = 0x000005F6 + XK_Arabic_veh*: TKeySym = 0x000005F7 + XK_Arabic_keheh*: TKeySym = 0x000005F8 + XK_Arabic_gaf*: TKeySym = 0x000005F9 + XK_Arabic_noon_ghunna*: TKeySym = 0x000005FA + XK_Arabic_heh_doachashmee*: TKeySym = 0x000005FB + XK_Farsi_yeh*: TKeySym = 0x000005FC + XK_Arabic_farsi_yeh*: TKeySym = XK_Farsi_yeh + XK_Arabic_yeh_baree*: TKeySym = 0x000005FD + XK_Arabic_heh_goal*: TKeySym = 0x000005FE + XK_Arabic_switch*: TKeySym = 0x0000FF7E # Alias for mode_switch +# XK_ARABIC +#* +# * Cyrillic +# * Byte 3 = 6 +# * + +when defined(XK_CYRILLIC) or true: + const + XKc_Cyrillic_GHE_bar*: TKeySym = 0x00000680 + XK_Cyrillic_ghe_bar*: TKeySym = 0x00000690 + XKc_Cyrillic_ZHE_descender*: TKeySym = 0x00000681 + XK_Cyrillic_zhe_descender*: TKeySym = 0x00000691 + XKc_Cyrillic_KA_descender*: TKeySym = 0x00000682 + XK_Cyrillic_ka_descender*: TKeySym = 0x00000692 + XKc_Cyrillic_KA_vertstroke*: TKeySym = 0x00000683 + XK_Cyrillic_ka_vertstroke*: TKeySym = 0x00000693 + XKc_Cyrillic_EN_descender*: TKeySym = 0x00000684 + XK_Cyrillic_en_descender*: TKeySym = 0x00000694 + XKc_Cyrillic_U_straight*: TKeySym = 0x00000685 + XK_Cyrillic_u_straight*: TKeySym = 0x00000695 + XKc_Cyrillic_U_straight_bar*: TKeySym = 0x00000686 + XK_Cyrillic_u_straight_bar*: TKeySym = 0x00000696 + XKc_Cyrillic_HA_descender*: TKeySym = 0x00000687 + XK_Cyrillic_ha_descender*: TKeySym = 0x00000697 + XKc_Cyrillic_CHE_descender*: TKeySym = 0x00000688 + XK_Cyrillic_che_descender*: TKeySym = 0x00000698 + XKc_Cyrillic_CHE_vertstroke*: TKeySym = 0x00000689 + XK_Cyrillic_che_vertstroke*: TKeySym = 0x00000699 + XKc_Cyrillic_SHHA*: TKeySym = 0x0000068A + XK_Cyrillic_shha*: TKeySym = 0x0000069A + XKc_Cyrillic_SCHWA*: TKeySym = 0x0000068C + XK_Cyrillic_schwa*: TKeySym = 0x0000069C + XKc_Cyrillic_I_macron*: TKeySym = 0x0000068D + XK_Cyrillic_i_macron*: TKeySym = 0x0000069D + XKc_Cyrillic_O_bar*: TKeySym = 0x0000068E + XK_Cyrillic_o_bar*: TKeySym = 0x0000069E + XKc_Cyrillic_U_macron*: TKeySym = 0x0000068F + XK_Cyrillic_u_macron*: TKeySym = 0x0000069F + XK_Serbian_dje*: TKeySym = 0x000006A1 + XK_Macedonia_gje*: TKeySym = 0x000006A2 + XK_Cyrillic_io*: TKeySym = 0x000006A3 + XK_Ukrainian_ie*: TKeySym = 0x000006A4 + XK_Ukranian_je*: TKeySym = 0x000006A4 # deprecated + XK_Macedonia_dse*: TKeySym = 0x000006A5 + XK_Ukrainian_i*: TKeySym = 0x000006A6 + XK_Ukranian_i*: TKeySym = 0x000006A6 # deprecated + XK_Ukrainian_yi*: TKeySym = 0x000006A7 + XK_Ukranian_yi*: TKeySym = 0x000006A7 # deprecated + XK_Cyrillic_je*: TKeySym = 0x000006A8 + XK_Serbian_je*: TKeySym = 0x000006A8 # deprecated + XK_Cyrillic_lje*: TKeySym = 0x000006A9 + XK_Serbian_lje*: TKeySym = 0x000006A9 # deprecated + XK_Cyrillic_nje*: TKeySym = 0x000006AA + XK_Serbian_nje*: TKeySym = 0x000006AA # deprecated + XK_Serbian_tshe*: TKeySym = 0x000006AB + XK_Macedonia_kje*: TKeySym = 0x000006AC + XK_Ukrainian_ghe_with_upturn*: TKeySym = 0x000006AD + XK_Byelorussian_shortu*: TKeySym = 0x000006AE + XK_Cyrillic_dzhe*: TKeySym = 0x000006AF + XK_Serbian_dze*: TKeySym = 0x000006AF # deprecated + XK_numerosign*: TKeySym = 0x000006B0 + XKc_Serbian_DJE*: TKeySym = 0x000006B1 + XKc_Macedonia_GJE*: TKeySym = 0x000006B2 + XKc_Cyrillic_IO*: TKeySym = 0x000006B3 + XKc_Ukrainian_IE*: TKeySym = 0x000006B4 + XKc_Ukranian_JE*: TKeySym = 0x000006B4 # deprecated + XKc_Macedonia_DSE*: TKeySym = 0x000006B5 + XKc_Ukrainian_I*: TKeySym = 0x000006B6 + XKc_Ukranian_I*: TKeySym = 0x000006B6 # deprecated + XKc_Ukrainian_YI*: TKeySym = 0x000006B7 + XKc_Ukranian_YI*: TKeySym = 0x000006B7 # deprecated + XKc_Cyrillic_JE*: TKeySym = 0x000006B8 + XKc_Serbian_JE*: TKeySym = 0x000006B8 # deprecated + XKc_Cyrillic_LJE*: TKeySym = 0x000006B9 + XKc_Serbian_LJE*: TKeySym = 0x000006B9 # deprecated + XKc_Cyrillic_NJE*: TKeySym = 0x000006BA + XKc_Serbian_NJE*: TKeySym = 0x000006BA # deprecated + XKc_Serbian_TSHE*: TKeySym = 0x000006BB + XKc_Macedonia_KJE*: TKeySym = 0x000006BC + XKc_Ukrainian_GHE_WITH_UPTURN*: TKeySym = 0x000006BD + XKc_Byelorussian_SHORTU*: TKeySym = 0x000006BE + XKc_Cyrillic_DZHE*: TKeySym = 0x000006BF + XKc_Serbian_DZE*: TKeySym = 0x000006BF # deprecated + XK_Cyrillic_yu*: TKeySym = 0x000006C0 + XK_Cyrillic_a*: TKeySym = 0x000006C1 + XK_Cyrillic_be*: TKeySym = 0x000006C2 + XK_Cyrillic_tse*: TKeySym = 0x000006C3 + XK_Cyrillic_de*: TKeySym = 0x000006C4 + XK_Cyrillic_ie*: TKeySym = 0x000006C5 + XK_Cyrillic_ef*: TKeySym = 0x000006C6 + XK_Cyrillic_ghe*: TKeySym = 0x000006C7 + XK_Cyrillic_ha*: TKeySym = 0x000006C8 + XK_Cyrillic_i*: TKeySym = 0x000006C9 + XK_Cyrillic_shorti*: TKeySym = 0x000006CA + XK_Cyrillic_ka*: TKeySym = 0x000006CB + XK_Cyrillic_el*: TKeySym = 0x000006CC + XK_Cyrillic_em*: TKeySym = 0x000006CD + XK_Cyrillic_en*: TKeySym = 0x000006CE + XK_Cyrillic_o*: TKeySym = 0x000006CF + XK_Cyrillic_pe*: TKeySym = 0x000006D0 + XK_Cyrillic_ya*: TKeySym = 0x000006D1 + XK_Cyrillic_er*: TKeySym = 0x000006D2 + XK_Cyrillic_es*: TKeySym = 0x000006D3 + XK_Cyrillic_te*: TKeySym = 0x000006D4 + XK_Cyrillic_u*: TKeySym = 0x000006D5 + XK_Cyrillic_zhe*: TKeySym = 0x000006D6 + XK_Cyrillic_ve*: TKeySym = 0x000006D7 + XK_Cyrillic_softsign*: TKeySym = 0x000006D8 + XK_Cyrillic_yeru*: TKeySym = 0x000006D9 + XK_Cyrillic_ze*: TKeySym = 0x000006DA + XK_Cyrillic_sha*: TKeySym = 0x000006DB + XK_Cyrillic_e*: TKeySym = 0x000006DC + XK_Cyrillic_shcha*: TKeySym = 0x000006DD + XK_Cyrillic_che*: TKeySym = 0x000006DE + XK_Cyrillic_hardsign*: TKeySym = 0x000006DF + XKc_Cyrillic_YU*: TKeySym = 0x000006E0 + XKc_Cyrillic_A*: TKeySym = 0x000006E1 + XKc_Cyrillic_BE*: TKeySym = 0x000006E2 + XKc_Cyrillic_TSE*: TKeySym = 0x000006E3 + XKc_Cyrillic_DE*: TKeySym = 0x000006E4 + XKc_Cyrillic_IE*: TKeySym = 0x000006E5 + XKc_Cyrillic_EF*: TKeySym = 0x000006E6 + XKc_Cyrillic_GHE*: TKeySym = 0x000006E7 + XKc_Cyrillic_HA*: TKeySym = 0x000006E8 + XKc_Cyrillic_I*: TKeySym = 0x000006E9 + XKc_Cyrillic_SHORTI*: TKeySym = 0x000006EA + XKc_Cyrillic_KA*: TKeySym = 0x000006EB + XKc_Cyrillic_EL*: TKeySym = 0x000006EC + XKc_Cyrillic_EM*: TKeySym = 0x000006ED + XKc_Cyrillic_EN*: TKeySym = 0x000006EE + XKc_Cyrillic_O*: TKeySym = 0x000006EF + XKc_Cyrillic_PE*: TKeySym = 0x000006F0 + XKc_Cyrillic_YA*: TKeySym = 0x000006F1 + XKc_Cyrillic_ER*: TKeySym = 0x000006F2 + XKc_Cyrillic_ES*: TKeySym = 0x000006F3 + XKc_Cyrillic_TE*: TKeySym = 0x000006F4 + XKc_Cyrillic_U*: TKeySym = 0x000006F5 + XKc_Cyrillic_ZHE*: TKeySym = 0x000006F6 + XKc_Cyrillic_VE*: TKeySym = 0x000006F7 + XKc_Cyrillic_SOFTSIGN*: TKeySym = 0x000006F8 + XKc_Cyrillic_YERU*: TKeySym = 0x000006F9 + XKc_Cyrillic_ZE*: TKeySym = 0x000006FA + XKc_Cyrillic_SHA*: TKeySym = 0x000006FB + XKc_Cyrillic_E*: TKeySym = 0x000006FC + XKc_Cyrillic_SHCHA*: TKeySym = 0x000006FD + XKc_Cyrillic_CHE*: TKeySym = 0x000006FE + XKc_Cyrillic_HARDSIGN*: TKeySym = 0x000006FF +# XK_CYRILLIC +#* +# * Greek +# * Byte 3 = 7 +# * + +when defined(XK_GREEK) or true: + const + XKc_Greek_ALPHAaccent*: TKeySym = 0x000007A1 + XKc_Greek_EPSILONaccent*: TKeySym = 0x000007A2 + XKc_Greek_ETAaccent*: TKeySym = 0x000007A3 + XKc_Greek_IOTAaccent*: TKeySym = 0x000007A4 + XKc_Greek_IOTAdieresis*: TKeySym = 0x000007A5 + XKc_Greek_IOTAdiaeresis*: TKeySym = XKc_Greek_IOTAdieresis # old typo + XKc_Greek_OMICRONaccent*: TKeySym = 0x000007A7 + XKc_Greek_UPSILONaccent*: TKeySym = 0x000007A8 + XKc_Greek_UPSILONdieresis*: TKeySym = 0x000007A9 + XKc_Greek_OMEGAaccent*: TKeySym = 0x000007AB + XK_Greek_accentdieresis*: TKeySym = 0x000007AE + XK_Greek_horizbar*: TKeySym = 0x000007AF + XK_Greek_alphaaccent*: TKeySym = 0x000007B1 + XK_Greek_epsilonaccent*: TKeySym = 0x000007B2 + XK_Greek_etaaccent*: TKeySym = 0x000007B3 + XK_Greek_iotaaccent*: TKeySym = 0x000007B4 + XK_Greek_iotadieresis*: TKeySym = 0x000007B5 + XK_Greek_iotaaccentdieresis*: TKeySym = 0x000007B6 + XK_Greek_omicronaccent*: TKeySym = 0x000007B7 + XK_Greek_upsilonaccent*: TKeySym = 0x000007B8 + XK_Greek_upsilondieresis*: TKeySym = 0x000007B9 + XK_Greek_upsilonaccentdieresis*: TKeySym = 0x000007BA + XK_Greek_omegaaccent*: TKeySym = 0x000007BB + XKc_Greek_ALPHA*: TKeySym = 0x000007C1 + XKc_Greek_BETA*: TKeySym = 0x000007C2 + XKc_Greek_GAMMA*: TKeySym = 0x000007C3 + XKc_Greek_DELTA*: TKeySym = 0x000007C4 + XKc_Greek_EPSILON*: TKeySym = 0x000007C5 + XKc_Greek_ZETA*: TKeySym = 0x000007C6 + XKc_Greek_ETA*: TKeySym = 0x000007C7 + XKc_Greek_THETA*: TKeySym = 0x000007C8 + XKc_Greek_IOTA*: TKeySym = 0x000007C9 + XKc_Greek_KAPPA*: TKeySym = 0x000007CA + XKc_Greek_LAMDA*: TKeySym = 0x000007CB + XKc_Greek_LAMBDA*: TKeySym = 0x000007CB + XKc_Greek_MU*: TKeySym = 0x000007CC + XKc_Greek_NU*: TKeySym = 0x000007CD + XKc_Greek_XI*: TKeySym = 0x000007CE + XKc_Greek_OMICRON*: TKeySym = 0x000007CF + XKc_Greek_PI*: TKeySym = 0x000007D0 + XKc_Greek_RHO*: TKeySym = 0x000007D1 + XKc_Greek_SIGMA*: TKeySym = 0x000007D2 + XKc_Greek_TAU*: TKeySym = 0x000007D4 + XKc_Greek_UPSILON*: TKeySym = 0x000007D5 + XKc_Greek_PHI*: TKeySym = 0x000007D6 + XKc_Greek_CHI*: TKeySym = 0x000007D7 + XKc_Greek_PSI*: TKeySym = 0x000007D8 + XKc_Greek_OMEGA*: TKeySym = 0x000007D9 + XK_Greek_alpha*: TKeySym = 0x000007E1 + XK_Greek_beta*: TKeySym = 0x000007E2 + XK_Greek_gamma*: TKeySym = 0x000007E3 + XK_Greek_delta*: TKeySym = 0x000007E4 + XK_Greek_epsilon*: TKeySym = 0x000007E5 + XK_Greek_zeta*: TKeySym = 0x000007E6 + XK_Greek_eta*: TKeySym = 0x000007E7 + XK_Greek_theta*: TKeySym = 0x000007E8 + XK_Greek_iota*: TKeySym = 0x000007E9 + XK_Greek_kappa*: TKeySym = 0x000007EA + XK_Greek_lamda*: TKeySym = 0x000007EB + XK_Greek_lambda*: TKeySym = 0x000007EB + XK_Greek_mu*: TKeySym = 0x000007EC + XK_Greek_nu*: TKeySym = 0x000007ED + XK_Greek_xi*: TKeySym = 0x000007EE + XK_Greek_omicron*: TKeySym = 0x000007EF + XK_Greek_pi*: TKeySym = 0x000007F0 + XK_Greek_rho*: TKeySym = 0x000007F1 + XK_Greek_sigma*: TKeySym = 0x000007F2 + XK_Greek_finalsmallsigma*: TKeySym = 0x000007F3 + XK_Greek_tau*: TKeySym = 0x000007F4 + XK_Greek_upsilon*: TKeySym = 0x000007F5 + XK_Greek_phi*: TKeySym = 0x000007F6 + XK_Greek_chi*: TKeySym = 0x000007F7 + XK_Greek_psi*: TKeySym = 0x000007F8 + XK_Greek_omega*: TKeySym = 0x000007F9 + XK_Greek_switch*: TKeySym = 0x0000FF7E # Alias for mode_switch +# XK_GREEK +#* +# * Technical +# * Byte 3 = 8 +# * + +when defined(XK_TECHNICAL) or true: + const + XK_leftradical*: TKeySym = 0x000008A1 + XK_topleftradical*: TKeySym = 0x000008A2 + XK_horizconnector*: TKeySym = 0x000008A3 + XK_topintegral*: TKeySym = 0x000008A4 + XK_botintegral*: TKeySym = 0x000008A5 + XK_vertconnector*: TKeySym = 0x000008A6 + XK_topleftsqbracket*: TKeySym = 0x000008A7 + XK_botleftsqbracket*: TKeySym = 0x000008A8 + XK_toprightsqbracket*: TKeySym = 0x000008A9 + XK_botrightsqbracket*: TKeySym = 0x000008AA + XK_topleftparens*: TKeySym = 0x000008AB + XK_botleftparens*: TKeySym = 0x000008AC + XK_toprightparens*: TKeySym = 0x000008AD + XK_botrightparens*: TKeySym = 0x000008AE + XK_leftmiddlecurlybrace*: TKeySym = 0x000008AF + XK_rightmiddlecurlybrace*: TKeySym = 0x000008B0 + XK_topleftsummation*: TKeySym = 0x000008B1 + XK_botleftsummation*: TKeySym = 0x000008B2 + XK_topvertsummationconnector*: TKeySym = 0x000008B3 + XK_botvertsummationconnector*: TKeySym = 0x000008B4 + XK_toprightsummation*: TKeySym = 0x000008B5 + XK_botrightsummation*: TKeySym = 0x000008B6 + XK_rightmiddlesummation*: TKeySym = 0x000008B7 + XK_lessthanequal*: TKeySym = 0x000008BC + XK_notequal*: TKeySym = 0x000008BD + XK_greaterthanequal*: TKeySym = 0x000008BE + XK_integral*: TKeySym = 0x000008BF + XK_therefore*: TKeySym = 0x000008C0 + XK_variation*: TKeySym = 0x000008C1 + XK_infinity*: TKeySym = 0x000008C2 + XK_nabla*: TKeySym = 0x000008C5 + XK_approximate*: TKeySym = 0x000008C8 + XK_similarequal*: TKeySym = 0x000008C9 + XK_ifonlyif*: TKeySym = 0x000008CD + XK_implies*: TKeySym = 0x000008CE + XK_identical*: TKeySym = 0x000008CF + XK_radical*: TKeySym = 0x000008D6 + XK_includedin*: TKeySym = 0x000008DA + XK_includes*: TKeySym = 0x000008DB + XK_intersection*: TKeySym = 0x000008DC + XK_union*: TKeySym = 0x000008DD + XK_logicaland*: TKeySym = 0x000008DE + XK_logicalor*: TKeySym = 0x000008DF + XK_partialderivative*: TKeySym = 0x000008EF + XK_function*: TKeySym = 0x000008F6 + XK_leftarrow*: TKeySym = 0x000008FB + XK_uparrow*: TKeySym = 0x000008FC + XK_rightarrow*: TKeySym = 0x000008FD + XK_downarrow*: TKeySym = 0x000008FE +# XK_TECHNICAL +#* +# * Special +# * Byte 3 = 9 +# * + +when defined(XK_SPECIAL): + const + XK_blank*: TKeySym = 0x000009DF + XK_soliddiamond*: TKeySym = 0x000009E0 + XK_checkerboard*: TKeySym = 0x000009E1 + XK_ht*: TKeySym = 0x000009E2 + XK_ff*: TKeySym = 0x000009E3 + XK_cr*: TKeySym = 0x000009E4 + XK_lf*: TKeySym = 0x000009E5 + XK_nl*: TKeySym = 0x000009E8 + XK_vt*: TKeySym = 0x000009E9 + XK_lowrightcorner*: TKeySym = 0x000009EA + XK_uprightcorner*: TKeySym = 0x000009EB + XK_upleftcorner*: TKeySym = 0x000009EC + XK_lowleftcorner*: TKeySym = 0x000009ED + XK_crossinglines*: TKeySym = 0x000009EE + XK_horizlinescan1*: TKeySym = 0x000009EF + XK_horizlinescan3*: TKeySym = 0x000009F0 + XK_horizlinescan5*: TKeySym = 0x000009F1 + XK_horizlinescan7*: TKeySym = 0x000009F2 + XK_horizlinescan9*: TKeySym = 0x000009F3 + XK_leftt*: TKeySym = 0x000009F4 + XK_rightt*: TKeySym = 0x000009F5 + XK_bott*: TKeySym = 0x000009F6 + XK_topt*: TKeySym = 0x000009F7 + XK_vertbar*: TKeySym = 0x000009F8 +# XK_SPECIAL +#* +# * Publishing +# * Byte 3 = a +# * + +when defined(XK_PUBLISHING) or true: + const + XK_emspace*: TKeySym = 0x00000AA1 + XK_enspace*: TKeySym = 0x00000AA2 + XK_em3space*: TKeySym = 0x00000AA3 + XK_em4space*: TKeySym = 0x00000AA4 + XK_digitspace*: TKeySym = 0x00000AA5 + XK_punctspace*: TKeySym = 0x00000AA6 + XK_thinspace*: TKeySym = 0x00000AA7 + XK_hairspace*: TKeySym = 0x00000AA8 + XK_emdash*: TKeySym = 0x00000AA9 + XK_endash*: TKeySym = 0x00000AAA + XK_signifblank*: TKeySym = 0x00000AAC + XK_ellipsis*: TKeySym = 0x00000AAE + XK_doubbaselinedot*: TKeySym = 0x00000AAF + XK_onethird*: TKeySym = 0x00000AB0 + XK_twothirds*: TKeySym = 0x00000AB1 + XK_onefifth*: TKeySym = 0x00000AB2 + XK_twofifths*: TKeySym = 0x00000AB3 + XK_threefifths*: TKeySym = 0x00000AB4 + XK_fourfifths*: TKeySym = 0x00000AB5 + XK_onesixth*: TKeySym = 0x00000AB6 + XK_fivesixths*: TKeySym = 0x00000AB7 + XK_careof*: TKeySym = 0x00000AB8 + XK_figdash*: TKeySym = 0x00000ABB + XK_leftanglebracket*: TKeySym = 0x00000ABC + XK_decimalpoint*: TKeySym = 0x00000ABD + XK_rightanglebracket*: TKeySym = 0x00000ABE + XK_marker*: TKeySym = 0x00000ABF + XK_oneeighth*: TKeySym = 0x00000AC3 + XK_threeeighths*: TKeySym = 0x00000AC4 + XK_fiveeighths*: TKeySym = 0x00000AC5 + XK_seveneighths*: TKeySym = 0x00000AC6 + XK_trademark*: TKeySym = 0x00000AC9 + XK_signaturemark*: TKeySym = 0x00000ACA + XK_trademarkincircle*: TKeySym = 0x00000ACB + XK_leftopentriangle*: TKeySym = 0x00000ACC + XK_rightopentriangle*: TKeySym = 0x00000ACD + XK_emopencircle*: TKeySym = 0x00000ACE + XK_emopenrectangle*: TKeySym = 0x00000ACF + XK_leftsinglequotemark*: TKeySym = 0x00000AD0 + XK_rightsinglequotemark*: TKeySym = 0x00000AD1 + XK_leftdoublequotemark*: TKeySym = 0x00000AD2 + XK_rightdoublequotemark*: TKeySym = 0x00000AD3 + XK_prescription*: TKeySym = 0x00000AD4 + XK_minutes*: TKeySym = 0x00000AD6 + XK_seconds*: TKeySym = 0x00000AD7 + XK_latincross*: TKeySym = 0x00000AD9 + XK_hexagram*: TKeySym = 0x00000ADA + XK_filledrectbullet*: TKeySym = 0x00000ADB + XK_filledlefttribullet*: TKeySym = 0x00000ADC + XK_filledrighttribullet*: TKeySym = 0x00000ADD + XK_emfilledcircle*: TKeySym = 0x00000ADE + XK_emfilledrect*: TKeySym = 0x00000ADF + XK_enopencircbullet*: TKeySym = 0x00000AE0 + XK_enopensquarebullet*: TKeySym = 0x00000AE1 + XK_openrectbullet*: TKeySym = 0x00000AE2 + XK_opentribulletup*: TKeySym = 0x00000AE3 + XK_opentribulletdown*: TKeySym = 0x00000AE4 + XK_openstar*: TKeySym = 0x00000AE5 + XK_enfilledcircbullet*: TKeySym = 0x00000AE6 + XK_enfilledsqbullet*: TKeySym = 0x00000AE7 + XK_filledtribulletup*: TKeySym = 0x00000AE8 + XK_filledtribulletdown*: TKeySym = 0x00000AE9 + XK_leftpointer*: TKeySym = 0x00000AEA + XK_rightpointer*: TKeySym = 0x00000AEB + XK_club*: TKeySym = 0x00000AEC + XK_diamond*: TKeySym = 0x00000AED + XK_heart*: TKeySym = 0x00000AEE + XK_maltesecross*: TKeySym = 0x00000AF0 + XK_dagger*: TKeySym = 0x00000AF1 + XK_doubledagger*: TKeySym = 0x00000AF2 + XK_checkmark*: TKeySym = 0x00000AF3 + XK_ballotcross*: TKeySym = 0x00000AF4 + XK_musicalsharp*: TKeySym = 0x00000AF5 + XK_musicalflat*: TKeySym = 0x00000AF6 + XK_malesymbol*: TKeySym = 0x00000AF7 + XK_femalesymbol*: TKeySym = 0x00000AF8 + XK_telephone*: TKeySym = 0x00000AF9 + XK_telephonerecorder*: TKeySym = 0x00000AFA + XK_phonographcopyright*: TKeySym = 0x00000AFB + XK_caret*: TKeySym = 0x00000AFC + XK_singlelowquotemark*: TKeySym = 0x00000AFD + XK_doublelowquotemark*: TKeySym = 0x00000AFE + XK_cursor*: TKeySym = 0x00000AFF +# XK_PUBLISHING +#* +# * APL +# * Byte 3 = b +# * + +when defined(XK_APL) or true: + const + XK_leftcaret*: TKeySym = 0x00000BA3 + XK_rightcaret*: TKeySym = 0x00000BA6 + XK_downcaret*: TKeySym = 0x00000BA8 + XK_upcaret*: TKeySym = 0x00000BA9 + XK_overbar*: TKeySym = 0x00000BC0 + XK_downtack*: TKeySym = 0x00000BC2 + XK_upshoe*: TKeySym = 0x00000BC3 + XK_downstile*: TKeySym = 0x00000BC4 + XK_underbar*: TKeySym = 0x00000BC6 + XK_jot*: TKeySym = 0x00000BCA + XK_quad*: TKeySym = 0x00000BCC + XK_uptack*: TKeySym = 0x00000BCE + XK_circle*: TKeySym = 0x00000BCF + XK_upstile*: TKeySym = 0x00000BD3 + XK_downshoe*: TKeySym = 0x00000BD6 + XK_rightshoe*: TKeySym = 0x00000BD8 + XK_leftshoe*: TKeySym = 0x00000BDA + XK_lefttack*: TKeySym = 0x00000BDC + XK_righttack*: TKeySym = 0x00000BFC +# XK_APL +#* +# * Hebrew +# * Byte 3 = c +# * + +when defined(XK_HEBREW) or true: + const + XK_hebrew_doublelowline*: TKeySym = 0x00000CDF + XK_hebrew_aleph*: TKeySym = 0x00000CE0 + XK_hebrew_bet*: TKeySym = 0x00000CE1 + XK_hebrew_beth*: TKeySym = 0x00000CE1 # deprecated + XK_hebrew_gimel*: TKeySym = 0x00000CE2 + XK_hebrew_gimmel*: TKeySym = 0x00000CE2 # deprecated + XK_hebrew_dalet*: TKeySym = 0x00000CE3 + XK_hebrew_daleth*: TKeySym = 0x00000CE3 # deprecated + XK_hebrew_he*: TKeySym = 0x00000CE4 + XK_hebrew_waw*: TKeySym = 0x00000CE5 + XK_hebrew_zain*: TKeySym = 0x00000CE6 + XK_hebrew_zayin*: TKeySym = 0x00000CE6 # deprecated + XK_hebrew_chet*: TKeySym = 0x00000CE7 + XK_hebrew_het*: TKeySym = 0x00000CE7 # deprecated + XK_hebrew_tet*: TKeySym = 0x00000CE8 + XK_hebrew_teth*: TKeySym = 0x00000CE8 # deprecated + XK_hebrew_yod*: TKeySym = 0x00000CE9 + XK_hebrew_finalkaph*: TKeySym = 0x00000CEA + XK_hebrew_kaph*: TKeySym = 0x00000CEB + XK_hebrew_lamed*: TKeySym = 0x00000CEC + XK_hebrew_finalmem*: TKeySym = 0x00000CED + XK_hebrew_mem*: TKeySym = 0x00000CEE + XK_hebrew_finalnun*: TKeySym = 0x00000CEF + XK_hebrew_nun*: TKeySym = 0x00000CF0 + XK_hebrew_samech*: TKeySym = 0x00000CF1 + XK_hebrew_samekh*: TKeySym = 0x00000CF1 # deprecated + XK_hebrew_ayin*: TKeySym = 0x00000CF2 + XK_hebrew_finalpe*: TKeySym = 0x00000CF3 + XK_hebrew_pe*: TKeySym = 0x00000CF4 + XK_hebrew_finalzade*: TKeySym = 0x00000CF5 + XK_hebrew_finalzadi*: TKeySym = 0x00000CF5 # deprecated + XK_hebrew_zade*: TKeySym = 0x00000CF6 + XK_hebrew_zadi*: TKeySym = 0x00000CF6 # deprecated + XK_hebrew_qoph*: TKeySym = 0x00000CF7 + XK_hebrew_kuf*: TKeySym = 0x00000CF7 # deprecated + XK_hebrew_resh*: TKeySym = 0x00000CF8 + XK_hebrew_shin*: TKeySym = 0x00000CF9 + XK_hebrew_taw*: TKeySym = 0x00000CFA + XK_hebrew_taf*: TKeySym = 0x00000CFA # deprecated + XK_Hebrew_switch*: TKeySym = 0x0000FF7E # Alias for mode_switch +# XK_HEBREW +#* +# * Thai +# * Byte 3 = d +# * + +when defined(XK_THAI) or true: + const + XK_Thai_kokai*: TKeySym = 0x00000DA1 + XK_Thai_khokhai*: TKeySym = 0x00000DA2 + XK_Thai_khokhuat*: TKeySym = 0x00000DA3 + XK_Thai_khokhwai*: TKeySym = 0x00000DA4 + XK_Thai_khokhon*: TKeySym = 0x00000DA5 + XK_Thai_khorakhang*: TKeySym = 0x00000DA6 + XK_Thai_ngongu*: TKeySym = 0x00000DA7 + XK_Thai_chochan*: TKeySym = 0x00000DA8 + XK_Thai_choching*: TKeySym = 0x00000DA9 + XK_Thai_chochang*: TKeySym = 0x00000DAA + XK_Thai_soso*: TKeySym = 0x00000DAB + XK_Thai_chochoe*: TKeySym = 0x00000DAC + XK_Thai_yoying*: TKeySym = 0x00000DAD + XK_Thai_dochada*: TKeySym = 0x00000DAE + XK_Thai_topatak*: TKeySym = 0x00000DAF + XK_Thai_thothan*: TKeySym = 0x00000DB0 + XK_Thai_thonangmontho*: TKeySym = 0x00000DB1 + XK_Thai_thophuthao*: TKeySym = 0x00000DB2 + XK_Thai_nonen*: TKeySym = 0x00000DB3 + XK_Thai_dodek*: TKeySym = 0x00000DB4 + XK_Thai_totao*: TKeySym = 0x00000DB5 + XK_Thai_thothung*: TKeySym = 0x00000DB6 + XK_Thai_thothahan*: TKeySym = 0x00000DB7 + XK_Thai_thothong*: TKeySym = 0x00000DB8 + XK_Thai_nonu*: TKeySym = 0x00000DB9 + XK_Thai_bobaimai*: TKeySym = 0x00000DBA + XK_Thai_popla*: TKeySym = 0x00000DBB + XK_Thai_phophung*: TKeySym = 0x00000DBC + XK_Thai_fofa*: TKeySym = 0x00000DBD + XK_Thai_phophan*: TKeySym = 0x00000DBE + XK_Thai_fofan*: TKeySym = 0x00000DBF + XK_Thai_phosamphao*: TKeySym = 0x00000DC0 + XK_Thai_moma*: TKeySym = 0x00000DC1 + XK_Thai_yoyak*: TKeySym = 0x00000DC2 + XK_Thai_rorua*: TKeySym = 0x00000DC3 + XK_Thai_ru*: TKeySym = 0x00000DC4 + XK_Thai_loling*: TKeySym = 0x00000DC5 + XK_Thai_lu*: TKeySym = 0x00000DC6 + XK_Thai_wowaen*: TKeySym = 0x00000DC7 + XK_Thai_sosala*: TKeySym = 0x00000DC8 + XK_Thai_sorusi*: TKeySym = 0x00000DC9 + XK_Thai_sosua*: TKeySym = 0x00000DCA + XK_Thai_hohip*: TKeySym = 0x00000DCB + XK_Thai_lochula*: TKeySym = 0x00000DCC + XK_Thai_oang*: TKeySym = 0x00000DCD + XK_Thai_honokhuk*: TKeySym = 0x00000DCE + XK_Thai_paiyannoi*: TKeySym = 0x00000DCF + XK_Thai_saraa*: TKeySym = 0x00000DD0 + XK_Thai_maihanakat*: TKeySym = 0x00000DD1 + XK_Thai_saraaa*: TKeySym = 0x00000DD2 + XK_Thai_saraam*: TKeySym = 0x00000DD3 + XK_Thai_sarai*: TKeySym = 0x00000DD4 + XK_Thai_saraii*: TKeySym = 0x00000DD5 + XK_Thai_saraue*: TKeySym = 0x00000DD6 + XK_Thai_sarauee*: TKeySym = 0x00000DD7 + XK_Thai_sarau*: TKeySym = 0x00000DD8 + XK_Thai_sarauu*: TKeySym = 0x00000DD9 + XK_Thai_phinthu*: TKeySym = 0x00000DDA + XK_Thai_maihanakat_maitho*: TKeySym = 0x00000DDE + XK_Thai_baht*: TKeySym = 0x00000DDF + XK_Thai_sarae*: TKeySym = 0x00000DE0 + XK_Thai_saraae*: TKeySym = 0x00000DE1 + XK_Thai_sarao*: TKeySym = 0x00000DE2 + XK_Thai_saraaimaimuan*: TKeySym = 0x00000DE3 + XK_Thai_saraaimaimalai*: TKeySym = 0x00000DE4 + XK_Thai_lakkhangyao*: TKeySym = 0x00000DE5 + XK_Thai_maiyamok*: TKeySym = 0x00000DE6 + XK_Thai_maitaikhu*: TKeySym = 0x00000DE7 + XK_Thai_maiek*: TKeySym = 0x00000DE8 + XK_Thai_maitho*: TKeySym = 0x00000DE9 + XK_Thai_maitri*: TKeySym = 0x00000DEA + XK_Thai_maichattawa*: TKeySym = 0x00000DEB + XK_Thai_thanthakhat*: TKeySym = 0x00000DEC + XK_Thai_nikhahit*: TKeySym = 0x00000DED + XK_Thai_leksun*: TKeySym = 0x00000DF0 + XK_Thai_leknung*: TKeySym = 0x00000DF1 + XK_Thai_leksong*: TKeySym = 0x00000DF2 + XK_Thai_leksam*: TKeySym = 0x00000DF3 + XK_Thai_leksi*: TKeySym = 0x00000DF4 + XK_Thai_lekha*: TKeySym = 0x00000DF5 + XK_Thai_lekhok*: TKeySym = 0x00000DF6 + XK_Thai_lekchet*: TKeySym = 0x00000DF7 + XK_Thai_lekpaet*: TKeySym = 0x00000DF8 + XK_Thai_lekkao*: TKeySym = 0x00000DF9 +# XK_THAI +#* +# * Korean +# * Byte 3 = e +# * + +when defined(XK_KOREAN) or true: + const + XK_Hangul*: TKeySym = 0x0000FF31 # Hangul start/stop(toggle) + XK_Hangul_Start*: TKeySym = 0x0000FF32 # Hangul start + XK_Hangul_End*: TKeySym = 0x0000FF33 # Hangul end, English start + XK_Hangul_Hanja*: TKeySym = 0x0000FF34 # Start Hangul->Hanja Conversion + XK_Hangul_Jamo*: TKeySym = 0x0000FF35 # Hangul Jamo mode + XK_Hangul_Romaja*: TKeySym = 0x0000FF36 # Hangul Romaja mode + XK_Hangul_Codeinput*: TKeySym = 0x0000FF37 # Hangul code input mode + XK_Hangul_Jeonja*: TKeySym = 0x0000FF38 # Jeonja mode + XK_Hangul_Banja*: TKeySym = 0x0000FF39 # Banja mode + XK_Hangul_PreHanja*: TKeySym = 0x0000FF3A # Pre Hanja conversion + XK_Hangul_PostHanja*: TKeySym = 0x0000FF3B # Post Hanja conversion + XK_Hangul_SingleCandidate*: TKeySym = 0x0000FF3C # Single candidate + XK_Hangul_MultipleCandidate*: TKeySym = 0x0000FF3D # Multiple candidate + XK_Hangul_PreviousCandidate*: TKeySym = 0x0000FF3E # Previous candidate + XK_Hangul_Special*: TKeySym = 0x0000FF3F # Special symbols + XK_Hangul_switch*: TKeySym = 0x0000FF7E # Alias for mode_switch \ + # Hangul Consonant Characters + XK_Hangul_Kiyeog*: TKeySym = 0x00000EA1 + XK_Hangul_SsangKiyeog*: TKeySym = 0x00000EA2 + XK_Hangul_KiyeogSios*: TKeySym = 0x00000EA3 + XK_Hangul_Nieun*: TKeySym = 0x00000EA4 + XK_Hangul_NieunJieuj*: TKeySym = 0x00000EA5 + XK_Hangul_NieunHieuh*: TKeySym = 0x00000EA6 + XK_Hangul_Dikeud*: TKeySym = 0x00000EA7 + XK_Hangul_SsangDikeud*: TKeySym = 0x00000EA8 + XK_Hangul_Rieul*: TKeySym = 0x00000EA9 + XK_Hangul_RieulKiyeog*: TKeySym = 0x00000EAA + XK_Hangul_RieulMieum*: TKeySym = 0x00000EAB + XK_Hangul_RieulPieub*: TKeySym = 0x00000EAC + XK_Hangul_RieulSios*: TKeySym = 0x00000EAD + XK_Hangul_RieulTieut*: TKeySym = 0x00000EAE + XK_Hangul_RieulPhieuf*: TKeySym = 0x00000EAF + XK_Hangul_RieulHieuh*: TKeySym = 0x00000EB0 + XK_Hangul_Mieum*: TKeySym = 0x00000EB1 + XK_Hangul_Pieub*: TKeySym = 0x00000EB2 + XK_Hangul_SsangPieub*: TKeySym = 0x00000EB3 + XK_Hangul_PieubSios*: TKeySym = 0x00000EB4 + XK_Hangul_Sios*: TKeySym = 0x00000EB5 + XK_Hangul_SsangSios*: TKeySym = 0x00000EB6 + XK_Hangul_Ieung*: TKeySym = 0x00000EB7 + XK_Hangul_Jieuj*: TKeySym = 0x00000EB8 + XK_Hangul_SsangJieuj*: TKeySym = 0x00000EB9 + XK_Hangul_Cieuc*: TKeySym = 0x00000EBA + XK_Hangul_Khieuq*: TKeySym = 0x00000EBB + XK_Hangul_Tieut*: TKeySym = 0x00000EBC + XK_Hangul_Phieuf*: TKeySym = 0x00000EBD + XK_Hangul_Hieuh*: TKeySym = 0x00000EBE # Hangul Vowel Characters + XK_Hangul_A*: TKeySym = 0x00000EBF + XK_Hangul_AE*: TKeySym = 0x00000EC0 + XK_Hangul_YA*: TKeySym = 0x00000EC1 + XK_Hangul_YAE*: TKeySym = 0x00000EC2 + XK_Hangul_EO*: TKeySym = 0x00000EC3 + XK_Hangul_E*: TKeySym = 0x00000EC4 + XK_Hangul_YEO*: TKeySym = 0x00000EC5 + XK_Hangul_YE*: TKeySym = 0x00000EC6 + XK_Hangul_O*: TKeySym = 0x00000EC7 + XK_Hangul_WA*: TKeySym = 0x00000EC8 + XK_Hangul_WAE*: TKeySym = 0x00000EC9 + XK_Hangul_OE*: TKeySym = 0x00000ECA + XK_Hangul_YO*: TKeySym = 0x00000ECB + XK_Hangul_U*: TKeySym = 0x00000ECC + XK_Hangul_WEO*: TKeySym = 0x00000ECD + XK_Hangul_WE*: TKeySym = 0x00000ECE + XK_Hangul_WI*: TKeySym = 0x00000ECF + XK_Hangul_YU*: TKeySym = 0x00000ED0 + XK_Hangul_EU*: TKeySym = 0x00000ED1 + XK_Hangul_YI*: TKeySym = 0x00000ED2 + XK_Hangul_I*: TKeySym = 0x00000ED3 # Hangul syllable-final (JongSeong) Characters + XK_Hangul_J_Kiyeog*: TKeySym = 0x00000ED4 + XK_Hangul_J_SsangKiyeog*: TKeySym = 0x00000ED5 + XK_Hangul_J_KiyeogSios*: TKeySym = 0x00000ED6 + XK_Hangul_J_Nieun*: TKeySym = 0x00000ED7 + XK_Hangul_J_NieunJieuj*: TKeySym = 0x00000ED8 + XK_Hangul_J_NieunHieuh*: TKeySym = 0x00000ED9 + XK_Hangul_J_Dikeud*: TKeySym = 0x00000EDA + XK_Hangul_J_Rieul*: TKeySym = 0x00000EDB + XK_Hangul_J_RieulKiyeog*: TKeySym = 0x00000EDC + XK_Hangul_J_RieulMieum*: TKeySym = 0x00000EDD + XK_Hangul_J_RieulPieub*: TKeySym = 0x00000EDE + XK_Hangul_J_RieulSios*: TKeySym = 0x00000EDF + XK_Hangul_J_RieulTieut*: TKeySym = 0x00000EE0 + XK_Hangul_J_RieulPhieuf*: TKeySym = 0x00000EE1 + XK_Hangul_J_RieulHieuh*: TKeySym = 0x00000EE2 + XK_Hangul_J_Mieum*: TKeySym = 0x00000EE3 + XK_Hangul_J_Pieub*: TKeySym = 0x00000EE4 + XK_Hangul_J_PieubSios*: TKeySym = 0x00000EE5 + XK_Hangul_J_Sios*: TKeySym = 0x00000EE6 + XK_Hangul_J_SsangSios*: TKeySym = 0x00000EE7 + XK_Hangul_J_Ieung*: TKeySym = 0x00000EE8 + XK_Hangul_J_Jieuj*: TKeySym = 0x00000EE9 + XK_Hangul_J_Cieuc*: TKeySym = 0x00000EEA + XK_Hangul_J_Khieuq*: TKeySym = 0x00000EEB + XK_Hangul_J_Tieut*: TKeySym = 0x00000EEC + XK_Hangul_J_Phieuf*: TKeySym = 0x00000EED + XK_Hangul_J_Hieuh*: TKeySym = 0x00000EEE # Ancient Hangul Consonant Characters + XK_Hangul_RieulYeorinHieuh*: TKeySym = 0x00000EEF + XK_Hangul_SunkyeongeumMieum*: TKeySym = 0x00000EF0 + XK_Hangul_SunkyeongeumPieub*: TKeySym = 0x00000EF1 + XK_Hangul_PanSios*: TKeySym = 0x00000EF2 + XK_Hangul_KkogjiDalrinIeung*: TKeySym = 0x00000EF3 + XK_Hangul_SunkyeongeumPhieuf*: TKeySym = 0x00000EF4 + XK_Hangul_YeorinHieuh*: TKeySym = 0x00000EF5 # Ancient Hangul Vowel Characters + XK_Hangul_AraeA*: TKeySym = 0x00000EF6 + XK_Hangul_AraeAE*: TKeySym = 0x00000EF7 # Ancient Hangul syllable-final (JongSeong) Characters + XK_Hangul_J_PanSios*: TKeySym = 0x00000EF8 + XK_Hangul_J_KkogjiDalrinIeung*: TKeySym = 0x00000EF9 + XK_Hangul_J_YeorinHieuh*: TKeySym = 0x00000EFA # Korean currency symbol + XK_Korean_Won*: TKeySym = 0x00000EFF +# XK_KOREAN +#* +# * Armenian +# * Byte 3 = = $14 +# * + +when defined(XK_ARMENIAN) or true: + const + XK_Armenian_eternity*: TKeySym = 0x000014A1 + XK_Armenian_ligature_ew*: TKeySym = 0x000014A2 + XK_Armenian_full_stop*: TKeySym = 0x000014A3 + XK_Armenian_verjaket*: TKeySym = 0x000014A3 + XK_Armenian_parenright*: TKeySym = 0x000014A4 + XK_Armenian_parenleft*: TKeySym = 0x000014A5 + XK_Armenian_guillemotright*: TKeySym = 0x000014A6 + XK_Armenian_guillemotleft*: TKeySym = 0x000014A7 + XK_Armenian_em_dash*: TKeySym = 0x000014A8 + XK_Armenian_dot*: TKeySym = 0x000014A9 + XK_Armenian_mijaket*: TKeySym = 0x000014A9 + XK_Armenian_separation_mark*: TKeySym = 0x000014AA + XK_Armenian_but*: TKeySym = 0x000014AA + XK_Armenian_comma*: TKeySym = 0x000014AB + XK_Armenian_en_dash*: TKeySym = 0x000014AC + XK_Armenian_hyphen*: TKeySym = 0x000014AD + XK_Armenian_yentamna*: TKeySym = 0x000014AD + XK_Armenian_ellipsis*: TKeySym = 0x000014AE + XK_Armenian_exclam*: TKeySym = 0x000014AF + XK_Armenian_amanak*: TKeySym = 0x000014AF + XK_Armenian_accent*: TKeySym = 0x000014B0 + XK_Armenian_shesht*: TKeySym = 0x000014B0 + XK_Armenian_question*: TKeySym = 0x000014B1 + XK_Armenian_paruyk*: TKeySym = 0x000014B1 + XKc_Armenian_AYB*: TKeySym = 0x000014B2 + XK_Armenian_ayb*: TKeySym = 0x000014B3 + XKc_Armenian_BEN*: TKeySym = 0x000014B4 + XK_Armenian_ben*: TKeySym = 0x000014B5 + XKc_Armenian_GIM*: TKeySym = 0x000014B6 + XK_Armenian_gim*: TKeySym = 0x000014B7 + XKc_Armenian_DA*: TKeySym = 0x000014B8 + XK_Armenian_da*: TKeySym = 0x000014B9 + XKc_Armenian_YECH*: TKeySym = 0x000014BA + XK_Armenian_yech*: TKeySym = 0x000014BB + XKc_Armenian_ZA*: TKeySym = 0x000014BC + XK_Armenian_za*: TKeySym = 0x000014BD + XKc_Armenian_E*: TKeySym = 0x000014BE + XK_Armenian_e*: TKeySym = 0x000014BF + XKc_Armenian_AT*: TKeySym = 0x000014C0 + XK_Armenian_at*: TKeySym = 0x000014C1 + XKc_Armenian_TO*: TKeySym = 0x000014C2 + XK_Armenian_to*: TKeySym = 0x000014C3 + XKc_Armenian_ZHE*: TKeySym = 0x000014C4 + XK_Armenian_zhe*: TKeySym = 0x000014C5 + XKc_Armenian_INI*: TKeySym = 0x000014C6 + XK_Armenian_ini*: TKeySym = 0x000014C7 + XKc_Armenian_LYUN*: TKeySym = 0x000014C8 + XK_Armenian_lyun*: TKeySym = 0x000014C9 + XKc_Armenian_KHE*: TKeySym = 0x000014CA + XK_Armenian_khe*: TKeySym = 0x000014CB + XKc_Armenian_TSA*: TKeySym = 0x000014CC + XK_Armenian_tsa*: TKeySym = 0x000014CD + XKc_Armenian_KEN*: TKeySym = 0x000014CE + XK_Armenian_ken*: TKeySym = 0x000014CF + XKc_Armenian_HO*: TKeySym = 0x000014D0 + XK_Armenian_ho*: TKeySym = 0x000014D1 + XKc_Armenian_DZA*: TKeySym = 0x000014D2 + XK_Armenian_dza*: TKeySym = 0x000014D3 + XKc_Armenian_GHAT*: TKeySym = 0x000014D4 + XK_Armenian_ghat*: TKeySym = 0x000014D5 + XKc_Armenian_TCHE*: TKeySym = 0x000014D6 + XK_Armenian_tche*: TKeySym = 0x000014D7 + XKc_Armenian_MEN*: TKeySym = 0x000014D8 + XK_Armenian_men*: TKeySym = 0x000014D9 + XKc_Armenian_HI*: TKeySym = 0x000014DA + XK_Armenian_hi*: TKeySym = 0x000014DB + XKc_Armenian_NU*: TKeySym = 0x000014DC + XK_Armenian_nu*: TKeySym = 0x000014DD + XKc_Armenian_SHA*: TKeySym = 0x000014DE + XK_Armenian_sha*: TKeySym = 0x000014DF + XKc_Armenian_VO*: TKeySym = 0x000014E0 + XK_Armenian_vo*: TKeySym = 0x000014E1 + XKc_Armenian_CHA*: TKeySym = 0x000014E2 + XK_Armenian_cha*: TKeySym = 0x000014E3 + XKc_Armenian_PE*: TKeySym = 0x000014E4 + XK_Armenian_pe*: TKeySym = 0x000014E5 + XKc_Armenian_JE*: TKeySym = 0x000014E6 + XK_Armenian_je*: TKeySym = 0x000014E7 + XKc_Armenian_RA*: TKeySym = 0x000014E8 + XK_Armenian_ra*: TKeySym = 0x000014E9 + XKc_Armenian_SE*: TKeySym = 0x000014EA + XK_Armenian_se*: TKeySym = 0x000014EB + XKc_Armenian_VEV*: TKeySym = 0x000014EC + XK_Armenian_vev*: TKeySym = 0x000014ED + XKc_Armenian_TYUN*: TKeySym = 0x000014EE + XK_Armenian_tyun*: TKeySym = 0x000014EF + XKc_Armenian_RE*: TKeySym = 0x000014F0 + XK_Armenian_re*: TKeySym = 0x000014F1 + XKc_Armenian_TSO*: TKeySym = 0x000014F2 + XK_Armenian_tso*: TKeySym = 0x000014F3 + XKc_Armenian_VYUN*: TKeySym = 0x000014F4 + XK_Armenian_vyun*: TKeySym = 0x000014F5 + XKc_Armenian_PYUR*: TKeySym = 0x000014F6 + XK_Armenian_pyur*: TKeySym = 0x000014F7 + XKc_Armenian_KE*: TKeySym = 0x000014F8 + XK_Armenian_ke*: TKeySym = 0x000014F9 + XKc_Armenian_O*: TKeySym = 0x000014FA + XK_Armenian_o*: TKeySym = 0x000014FB + XKc_Armenian_FE*: TKeySym = 0x000014FC + XK_Armenian_fe*: TKeySym = 0x000014FD + XK_Armenian_apostrophe*: TKeySym = 0x000014FE + XK_Armenian_section_sign*: TKeySym = 0x000014FF +# XK_ARMENIAN +#* +# * Georgian +# * Byte 3 = = $15 +# * + +when defined(XK_GEORGIAN) or true: + const + XK_Georgian_an*: TKeySym = 0x000015D0 + XK_Georgian_ban*: TKeySym = 0x000015D1 + XK_Georgian_gan*: TKeySym = 0x000015D2 + XK_Georgian_don*: TKeySym = 0x000015D3 + XK_Georgian_en*: TKeySym = 0x000015D4 + XK_Georgian_vin*: TKeySym = 0x000015D5 + XK_Georgian_zen*: TKeySym = 0x000015D6 + XK_Georgian_tan*: TKeySym = 0x000015D7 + XK_Georgian_in*: TKeySym = 0x000015D8 + XK_Georgian_kan*: TKeySym = 0x000015D9 + XK_Georgian_las*: TKeySym = 0x000015DA + XK_Georgian_man*: TKeySym = 0x000015DB + XK_Georgian_nar*: TKeySym = 0x000015DC + XK_Georgian_on*: TKeySym = 0x000015DD + XK_Georgian_par*: TKeySym = 0x000015DE + XK_Georgian_zhar*: TKeySym = 0x000015DF + XK_Georgian_rae*: TKeySym = 0x000015E0 + XK_Georgian_san*: TKeySym = 0x000015E1 + XK_Georgian_tar*: TKeySym = 0x000015E2 + XK_Georgian_un*: TKeySym = 0x000015E3 + XK_Georgian_phar*: TKeySym = 0x000015E4 + XK_Georgian_khar*: TKeySym = 0x000015E5 + XK_Georgian_ghan*: TKeySym = 0x000015E6 + XK_Georgian_qar*: TKeySym = 0x000015E7 + XK_Georgian_shin*: TKeySym = 0x000015E8 + XK_Georgian_chin*: TKeySym = 0x000015E9 + XK_Georgian_can*: TKeySym = 0x000015EA + XK_Georgian_jil*: TKeySym = 0x000015EB + XK_Georgian_cil*: TKeySym = 0x000015EC + XK_Georgian_char*: TKeySym = 0x000015ED + XK_Georgian_xan*: TKeySym = 0x000015EE + XK_Georgian_jhan*: TKeySym = 0x000015EF + XK_Georgian_hae*: TKeySym = 0x000015F0 + XK_Georgian_he*: TKeySym = 0x000015F1 + XK_Georgian_hie*: TKeySym = 0x000015F2 + XK_Georgian_we*: TKeySym = 0x000015F3 + XK_Georgian_har*: TKeySym = 0x000015F4 + XK_Georgian_hoe*: TKeySym = 0x000015F5 + XK_Georgian_fi*: TKeySym = 0x000015F6 +# XK_GEORGIAN +#* +# * Azeri (and other Turkic or Caucasian languages of ex-USSR) +# * Byte 3 = = $16 +# * + +when defined(XK_CAUCASUS) or true: + # latin + const + XKc_Ccedillaabovedot*: TKeySym = 0x000016A2 + XKc_Xabovedot*: TKeySym = 0x000016A3 + XKc_Qabovedot*: TKeySym = 0x000016A5 + XKc_Ibreve*: TKeySym = 0x000016A6 + XKc_IE*: TKeySym = 0x000016A7 + XKc_UO*: TKeySym = 0x000016A8 + XKc_Zstroke*: TKeySym = 0x000016A9 + XKc_Gcaron*: TKeySym = 0x000016AA + XKc_Obarred*: TKeySym = 0x000016AF + XK_ccedillaabovedot*: TKeySym = 0x000016B2 + XK_xabovedot*: TKeySym = 0x000016B3 + XKc_Ocaron*: TKeySym = 0x000016B4 + XK_qabovedot*: TKeySym = 0x000016B5 + XK_ibreve*: TKeySym = 0x000016B6 + XK_ie*: TKeySym = 0x000016B7 + XK_uo*: TKeySym = 0x000016B8 + XK_zstroke*: TKeySym = 0x000016B9 + XK_gcaron*: TKeySym = 0x000016BA + XK_ocaron*: TKeySym = 0x000016BD + XK_obarred*: TKeySym = 0x000016BF + XKc_SCHWA*: TKeySym = 0x000016C6 + XK_schwa*: TKeySym = 0x000016F6 # those are not really Caucasus, but I put them here for now\ + # For Inupiak + XKc_Lbelowdot*: TKeySym = 0x000016D1 + XKc_Lstrokebelowdot*: TKeySym = 0x000016D2 + XK_lbelowdot*: TKeySym = 0x000016E1 + XK_lstrokebelowdot*: TKeySym = 0x000016E2 # For Guarani + XKc_Gtilde*: TKeySym = 0x000016D3 + XK_gtilde*: TKeySym = 0x000016E3 +# XK_CAUCASUS +#* +# * Vietnamese +# * Byte 3 = = $1e +# * + +when defined(XK_VIETNAMESE) or true: + const + XKc_Abelowdot*: TKeySym = 0x00001EA0 + XK_abelowdot*: TKeySym = 0x00001EA1 + XKc_Ahook*: TKeySym = 0x00001EA2 + XK_ahook*: TKeySym = 0x00001EA3 + XKc_Acircumflexacute*: TKeySym = 0x00001EA4 + XK_acircumflexacute*: TKeySym = 0x00001EA5 + XKc_Acircumflexgrave*: TKeySym = 0x00001EA6 + XK_acircumflexgrave*: TKeySym = 0x00001EA7 + XKc_Acircumflexhook*: TKeySym = 0x00001EA8 + XK_acircumflexhook*: TKeySym = 0x00001EA9 + XKc_Acircumflextilde*: TKeySym = 0x00001EAA + XK_acircumflextilde*: TKeySym = 0x00001EAB + XKc_Acircumflexbelowdot*: TKeySym = 0x00001EAC + XK_acircumflexbelowdot*: TKeySym = 0x00001EAD + XKc_Abreveacute*: TKeySym = 0x00001EAE + XK_abreveacute*: TKeySym = 0x00001EAF + XKc_Abrevegrave*: TKeySym = 0x00001EB0 + XK_abrevegrave*: TKeySym = 0x00001EB1 + XKc_Abrevehook*: TKeySym = 0x00001EB2 + XK_abrevehook*: TKeySym = 0x00001EB3 + XKc_Abrevetilde*: TKeySym = 0x00001EB4 + XK_abrevetilde*: TKeySym = 0x00001EB5 + XKc_Abrevebelowdot*: TKeySym = 0x00001EB6 + XK_abrevebelowdot*: TKeySym = 0x00001EB7 + XKc_Ebelowdot*: TKeySym = 0x00001EB8 + XK_ebelowdot*: TKeySym = 0x00001EB9 + XKc_Ehook*: TKeySym = 0x00001EBA + XK_ehook*: TKeySym = 0x00001EBB + XKc_Etilde*: TKeySym = 0x00001EBC + XK_etilde*: TKeySym = 0x00001EBD + XKc_Ecircumflexacute*: TKeySym = 0x00001EBE + XK_ecircumflexacute*: TKeySym = 0x00001EBF + XKc_Ecircumflexgrave*: TKeySym = 0x00001EC0 + XK_ecircumflexgrave*: TKeySym = 0x00001EC1 + XKc_Ecircumflexhook*: TKeySym = 0x00001EC2 + XK_ecircumflexhook*: TKeySym = 0x00001EC3 + XKc_Ecircumflextilde*: TKeySym = 0x00001EC4 + XK_ecircumflextilde*: TKeySym = 0x00001EC5 + XKc_Ecircumflexbelowdot*: TKeySym = 0x00001EC6 + XK_ecircumflexbelowdot*: TKeySym = 0x00001EC7 + XKc_Ihook*: TKeySym = 0x00001EC8 + XK_ihook*: TKeySym = 0x00001EC9 + XKc_Ibelowdot*: TKeySym = 0x00001ECA + XK_ibelowdot*: TKeySym = 0x00001ECB + XKc_Obelowdot*: TKeySym = 0x00001ECC + XK_obelowdot*: TKeySym = 0x00001ECD + XKc_Ohook*: TKeySym = 0x00001ECE + XK_ohook*: TKeySym = 0x00001ECF + XKc_Ocircumflexacute*: TKeySym = 0x00001ED0 + XK_ocircumflexacute*: TKeySym = 0x00001ED1 + XKc_Ocircumflexgrave*: TKeySym = 0x00001ED2 + XK_ocircumflexgrave*: TKeySym = 0x00001ED3 + XKc_Ocircumflexhook*: TKeySym = 0x00001ED4 + XK_ocircumflexhook*: TKeySym = 0x00001ED5 + XKc_Ocircumflextilde*: TKeySym = 0x00001ED6 + XK_ocircumflextilde*: TKeySym = 0x00001ED7 + XKc_Ocircumflexbelowdot*: TKeySym = 0x00001ED8 + XK_ocircumflexbelowdot*: TKeySym = 0x00001ED9 + XKc_Ohornacute*: TKeySym = 0x00001EDA + XK_ohornacute*: TKeySym = 0x00001EDB + XKc_Ohorngrave*: TKeySym = 0x00001EDC + XK_ohorngrave*: TKeySym = 0x00001EDD + XKc_Ohornhook*: TKeySym = 0x00001EDE + XK_ohornhook*: TKeySym = 0x00001EDF + XKc_Ohorntilde*: TKeySym = 0x00001EE0 + XK_ohorntilde*: TKeySym = 0x00001EE1 + XKc_Ohornbelowdot*: TKeySym = 0x00001EE2 + XK_ohornbelowdot*: TKeySym = 0x00001EE3 + XKc_Ubelowdot*: TKeySym = 0x00001EE4 + XK_ubelowdot*: TKeySym = 0x00001EE5 + XKc_Uhook*: TKeySym = 0x00001EE6 + XK_uhook*: TKeySym = 0x00001EE7 + XKc_Uhornacute*: TKeySym = 0x00001EE8 + XK_uhornacute*: TKeySym = 0x00001EE9 + XKc_Uhorngrave*: TKeySym = 0x00001EEA + XK_uhorngrave*: TKeySym = 0x00001EEB + XKc_Uhornhook*: TKeySym = 0x00001EEC + XK_uhornhook*: TKeySym = 0x00001EED + XKc_Uhorntilde*: TKeySym = 0x00001EEE + XK_uhorntilde*: TKeySym = 0x00001EEF + XKc_Uhornbelowdot*: TKeySym = 0x00001EF0 + XK_uhornbelowdot*: TKeySym = 0x00001EF1 + XKc_Ybelowdot*: TKeySym = 0x00001EF4 + XK_ybelowdot*: TKeySym = 0x00001EF5 + XKc_Yhook*: TKeySym = 0x00001EF6 + XK_yhook*: TKeySym = 0x00001EF7 + XKc_Ytilde*: TKeySym = 0x00001EF8 + XK_ytilde*: TKeySym = 0x00001EF9 + XKc_Ohorn*: TKeySym = 0x00001EFA # U+01a0 + XK_ohorn*: TKeySym = 0x00001EFB # U+01a1 + XKc_Uhorn*: TKeySym = 0x00001EFC # U+01af + XK_uhorn*: TKeySym = 0x00001EFD # U+01b0 + XK_combining_tilde*: TKeySym = 0x00001E9F # U+0303 + XK_combining_grave*: TKeySym = 0x00001EF2 # U+0300 + XK_combining_acute*: TKeySym = 0x00001EF3 # U+0301 + XK_combining_hook*: TKeySym = 0x00001EFE # U+0309 + XK_combining_belowdot*: TKeySym = 0x00001EFF # U+0323 +# XK_VIETNAMESE + +when defined(XK_CURRENCY) or true: + const + XK_EcuSign*: TKeySym = 0x000020A0 + XK_ColonSign*: TKeySym = 0x000020A1 + XK_CruzeiroSign*: TKeySym = 0x000020A2 + XK_FFrancSign*: TKeySym = 0x000020A3 + XK_LiraSign*: TKeySym = 0x000020A4 + XK_MillSign*: TKeySym = 0x000020A5 + XK_NairaSign*: TKeySym = 0x000020A6 + XK_PesetaSign*: TKeySym = 0x000020A7 + XK_RupeeSign*: TKeySym = 0x000020A8 + XK_WonSign*: TKeySym = 0x000020A9 + XK_NewSheqelSign*: TKeySym = 0x000020AA + XK_DongSign*: TKeySym = 0x000020AB + XK_EuroSign*: TKeySym = 0x000020AC +# implementation diff --git a/tests/deps/x11-1.0/x.nim b/tests/deps/x11-1.0/x.nim new file mode 100644 index 000000000..9d9df48bd --- /dev/null +++ b/tests/deps/x11-1.0/x.nim @@ -0,0 +1,400 @@ + +# +# Automatically converted by H2Pas 0.99.15 from x.h +# The following command line parameters were used: +# -p +# -T +# -S +# -d +# -c +# x.h +# +# Pointers to basic pascal types, inserted by h2pas conversion program. + +const + X_PROTOCOL* = 11 + X_PROTOCOL_REVISION* = 0 + +type + PXID* = ptr TXID + TXID* = culong + PMask* = ptr TMask + TMask* = culong + PPAtom* = ptr PAtom + PAtom* = ptr TAtom + TAtom* = culong + PVisualID* = ptr TVisualID + TVisualID* = culong + PTime* = ptr TTime + TTime* = culong + PPWindow* = ptr PWindow + PWindow* = ptr TWindow + TWindow* = TXID + PDrawable* = ptr TDrawable + TDrawable* = TXID + PFont* = ptr TFont + TFont* = TXID + PPixmap* = ptr TPixmap + TPixmap* = TXID + PCursor* = ptr TCursor + TCursor* = TXID + PColormap* = ptr TColormap + TColormap* = TXID + PGContext* = ptr TGContext + TGContext* = TXID + PKeySym* = ptr TKeySym + TKeySym* = TXID + PKeyCode* = ptr TKeyCode + TKeyCode* = cuchar + +proc `==`*(a, b: TAtom): bool = + return system.`==`(a,b) + +const + None* = 0 + ParentRelative* = 1 + CopyFromParent* = 0 + PointerWindow* = 0 + InputFocus* = 1 + PointerRoot* = 1 + AnyPropertyType* = 0 + AnyKey* = 0 + AnyButton* = 0 + AllTemporary* = 0 + CurrentTime* = 0 + NoSymbol* = 0 + NoEventMask* = 0 + KeyPressMask* = 1 shl 0 + KeyReleaseMask* = 1 shl 1 + ButtonPressMask* = 1 shl 2 + ButtonReleaseMask* = 1 shl 3 + EnterWindowMask* = 1 shl 4 + LeaveWindowMask* = 1 shl 5 + PointerMotionMask* = 1 shl 6 + PointerMotionHintMask* = 1 shl 7 + Button1MotionMask* = 1 shl 8 + Button2MotionMask* = 1 shl 9 + Button3MotionMask* = 1 shl 10 + Button4MotionMask* = 1 shl 11 + Button5MotionMask* = 1 shl 12 + ButtonMotionMask* = 1 shl 13 + KeymapStateMask* = 1 shl 14 + ExposureMask* = 1 shl 15 + VisibilityChangeMask* = 1 shl 16 + StructureNotifyMask* = 1 shl 17 + ResizeRedirectMask* = 1 shl 18 + SubstructureNotifyMask* = 1 shl 19 + SubstructureRedirectMask* = 1 shl 20 + FocusChangeMask* = 1 shl 21 + PropertyChangeMask* = 1 shl 22 + ColormapChangeMask* = 1 shl 23 + OwnerGrabButtonMask* = 1 shl 24 + KeyPress* = 2 + KeyRelease* = 3 + ButtonPress* = 4 + ButtonRelease* = 5 + MotionNotify* = 6 + EnterNotify* = 7 + LeaveNotify* = 8 + FocusIn* = 9 + FocusOut* = 10 + KeymapNotify* = 11 + Expose* = 12 + GraphicsExpose* = 13 + NoExpose* = 14 + VisibilityNotify* = 15 + CreateNotify* = 16 + DestroyNotify* = 17 + UnmapNotify* = 18 + MapNotify* = 19 + MapRequest* = 20 + ReparentNotify* = 21 + ConfigureNotify* = 22 + ConfigureRequest* = 23 + GravityNotify* = 24 + ResizeRequest* = 25 + CirculateNotify* = 26 + CirculateRequest* = 27 + PropertyNotify* = 28 + SelectionClear* = 29 + SelectionRequest* = 30 + SelectionNotify* = 31 + ColormapNotify* = 32 + ClientMessage* = 33 + MappingNotify* = 34 + LASTEvent* = 35 + ShiftMask* = 1 shl 0 + LockMask* = 1 shl 1 + ControlMask* = 1 shl 2 + Mod1Mask* = 1 shl 3 + Mod2Mask* = 1 shl 4 + Mod3Mask* = 1 shl 5 + Mod4Mask* = 1 shl 6 + Mod5Mask* = 1 shl 7 + ShiftMapIndex* = 0 + LockMapIndex* = 1 + ControlMapIndex* = 2 + Mod1MapIndex* = 3 + Mod2MapIndex* = 4 + Mod3MapIndex* = 5 + Mod4MapIndex* = 6 + Mod5MapIndex* = 7 + Button1Mask* = 1 shl 8 + Button2Mask* = 1 shl 9 + Button3Mask* = 1 shl 10 + Button4Mask* = 1 shl 11 + Button5Mask* = 1 shl 12 + AnyModifier* = 1 shl 15 + Button1* = 1 + Button2* = 2 + Button3* = 3 + Button4* = 4 + Button5* = 5 + NotifyNormal* = 0 + NotifyGrab* = 1 + NotifyUngrab* = 2 + NotifyWhileGrabbed* = 3 + NotifyHint* = 1 + NotifyAncestor* = 0 + NotifyVirtual* = 1 + NotifyInferior* = 2 + NotifyNonlinear* = 3 + NotifyNonlinearVirtual* = 4 + NotifyPointer* = 5 + NotifyPointerRoot* = 6 + NotifyDetailNone* = 7 + VisibilityUnobscured* = 0 + VisibilityPartiallyObscured* = 1 + VisibilityFullyObscured* = 2 + PlaceOnTop* = 0 + PlaceOnBottom* = 1 + FamilyInternet* = 0 + FamilyDECnet* = 1 + FamilyChaos* = 2 + FamilyInternet6* = 6 + FamilyServerInterpreted* = 5 + PropertyNewValue* = 0 + PropertyDelete* = 1 + ColormapUninstalled* = 0 + ColormapInstalled* = 1 + GrabModeSync* = 0 + GrabModeAsync* = 1 + GrabSuccess* = 0 + AlreadyGrabbed* = 1 + GrabInvalidTime* = 2 + GrabNotViewable* = 3 + GrabFrozen* = 4 + AsyncPointer* = 0 + SyncPointer* = 1 + ReplayPointer* = 2 + AsyncKeyboard* = 3 + SyncKeyboard* = 4 + ReplayKeyboard* = 5 + AsyncBoth* = 6 + SyncBoth* = 7 + RevertToNone* = None + RevertToPointerRoot* = PointerRoot + RevertToParent* = 2 + Success* = 0 + BadRequest* = 1 + BadValue* = 2 + BadWindow* = 3 + BadPixmap* = 4 + BadAtom* = 5 + BadCursor* = 6 + BadFont* = 7 + BadMatch* = 8 + BadDrawable* = 9 + BadAccess* = 10 + BadAlloc* = 11 + BadColor* = 12 + BadGC* = 13 + BadIDChoice* = 14 + BadName* = 15 + BadLength* = 16 + BadImplementation* = 17 + FirstExtensionError* = 128 + LastExtensionError* = 255 + InputOutput* = 1 + InputOnly* = 2 + CWBackPixmap* = 1 shl 0 + CWBackPixel* = 1 shl 1 + CWBorderPixmap* = 1 shl 2 + CWBorderPixel* = 1 shl 3 + CWBitGravity* = 1 shl 4 + CWWinGravity* = 1 shl 5 + CWBackingStore* = 1 shl 6 + CWBackingPlanes* = 1 shl 7 + CWBackingPixel* = 1 shl 8 + CWOverrideRedirect* = 1 shl 9 + CWSaveUnder* = 1 shl 10 + CWEventMask* = 1 shl 11 + CWDontPropagate* = 1 shl 12 + CWColormap* = 1 shl 13 + CWCursor* = 1 shl 14 + CWX* = 1 shl 0 + CWY* = 1 shl 1 + CWWidth* = 1 shl 2 + CWHeight* = 1 shl 3 + CWBorderWidth* = 1 shl 4 + CWSibling* = 1 shl 5 + CWStackMode* = 1 shl 6 + ForgetGravity* = 0 + NorthWestGravity* = 1 + NorthGravity* = 2 + NorthEastGravity* = 3 + WestGravity* = 4 + CenterGravity* = 5 + EastGravity* = 6 + SouthWestGravity* = 7 + SouthGravity* = 8 + SouthEastGravity* = 9 + StaticGravity* = 10 + UnmapGravity* = 0 + NotUseful* = 0 + WhenMapped* = 1 + Always* = 2 + IsUnmapped* = 0 + IsUnviewable* = 1 + IsViewable* = 2 + SetModeInsert* = 0 + SetModeDelete* = 1 + DestroyAll* = 0 + RetainPermanent* = 1 + RetainTemporary* = 2 + Above* = 0 + Below* = 1 + TopIf* = 2 + BottomIf* = 3 + Opposite* = 4 + RaiseLowest* = 0 + LowerHighest* = 1 + PropModeReplace* = 0 + PropModePrepend* = 1 + PropModeAppend* = 2 + GXclear* = 0x00000000 + GXand* = 0x00000001 + GXandReverse* = 0x00000002 + GXcopy* = 0x00000003 + GXandInverted* = 0x00000004 + GXnoop* = 0x00000005 + GXxor* = 0x00000006 + GXor* = 0x00000007 + GXnor* = 0x00000008 + GXequiv* = 0x00000009 + GXinvert* = 0x0000000A + GXorReverse* = 0x0000000B + GXcopyInverted* = 0x0000000C + GXorInverted* = 0x0000000D + GXnand* = 0x0000000E + GXset* = 0x0000000F + LineSolid* = 0 + LineOnOffDash* = 1 + LineDoubleDash* = 2 + CapNotLast* = 0 + CapButt* = 1 + CapRound* = 2 + CapProjecting* = 3 + JoinMiter* = 0 + JoinRound* = 1 + JoinBevel* = 2 + FillSolid* = 0 + FillTiled* = 1 + FillStippled* = 2 + FillOpaqueStippled* = 3 + EvenOddRule* = 0 + WindingRule* = 1 + ClipByChildren* = 0 + IncludeInferiors* = 1 + Unsorted* = 0 + YSorted* = 1 + YXSorted* = 2 + YXBanded* = 3 + CoordModeOrigin* = 0 + CoordModePrevious* = 1 + Complex* = 0 + Nonconvex* = 1 + Convex* = 2 + ArcChord* = 0 + ArcPieSlice* = 1 + GCFunction* = 1 shl 0 + GCPlaneMask* = 1 shl 1 + GCForeground* = 1 shl 2 + GCBackground* = 1 shl 3 + GCLineWidth* = 1 shl 4 + GCLineStyle* = 1 shl 5 + GCCapStyle* = 1 shl 6 + GCJoinStyle* = 1 shl 7 + GCFillStyle* = 1 shl 8 + GCFillRule* = 1 shl 9 + GCTile* = 1 shl 10 + GCStipple* = 1 shl 11 + GCTileStipXOrigin* = 1 shl 12 + GCTileStipYOrigin* = 1 shl 13 + GCFont* = 1 shl 14 + GCSubwindowMode* = 1 shl 15 + GCGraphicsExposures* = 1 shl 16 + GCClipXOrigin* = 1 shl 17 + GCClipYOrigin* = 1 shl 18 + GCClipMask* = 1 shl 19 + GCDashOffset* = 1 shl 20 + GCDashList* = 1 shl 21 + GCArcMode* = 1 shl 22 + GCLastBit* = 22 + FontLeftToRight* = 0 + FontRightToLeft* = 1 + FontChange* = 255 + XYBitmap* = 0 + XYPixmap* = 1 + ZPixmap* = 2 + AllocNone* = 0 + AllocAll* = 1 + DoRed* = 1 shl 0 + DoGreen* = 1 shl 1 + DoBlue* = 1 shl 2 + CursorShape* = 0 + TileShape* = 1 + StippleShape* = 2 + AutoRepeatModeOff* = 0 + AutoRepeatModeOn* = 1 + AutoRepeatModeDefault* = 2 + LedModeOff* = 0 + LedModeOn* = 1 + KBKeyClickPercent* = 1 shl 0 + KBBellPercent* = 1 shl 1 + KBBellPitch* = 1 shl 2 + KBBellDuration* = 1 shl 3 + KBLed* = 1 shl 4 + KBLedMode* = 1 shl 5 + KBKey* = 1 shl 6 + KBAutoRepeatMode* = 1 shl 7 + MappingSuccess* = 0 + MappingBusy* = 1 + MappingFailed* = 2 + MappingModifier* = 0 + MappingKeyboard* = 1 + MappingPointer* = 2 + DontPreferBlanking* = 0 + PreferBlanking* = 1 + DefaultBlanking* = 2 + DisableScreenSaver* = 0 + DisableScreenInterval* = 0 + DontAllowExposures* = 0 + AllowExposures* = 1 + DefaultExposures* = 2 + ScreenSaverReset* = 0 + ScreenSaverActive* = 1 + HostInsert* = 0 + HostDelete* = 1 + EnableAccess* = 1 + DisableAccess* = 0 + StaticGray* = 0 + GrayScale* = 1 + StaticColor* = 2 + PseudoColor* = 3 + TrueColor* = 4 + DirectColor* = 5 + LSBFirst* = 0 + MSBFirst* = 1 + +# implementation diff --git a/tests/deps/x11-1.0/x11.nimble b/tests/deps/x11-1.0/x11.nimble new file mode 100644 index 000000000..2f4385100 --- /dev/null +++ b/tests/deps/x11-1.0/x11.nimble @@ -0,0 +1,11 @@ +[Package] +name: "x11" +version: "1.0" +author: "Andreas Rumpf" +description: "Wrapper for X11" +license: "MIT" + +srcDir: "src" + +[Deps] +requires: "nimrod > 0.9.2" diff --git a/tests/deps/x11-1.0/x11pragma.nim b/tests/deps/x11-1.0/x11pragma.nim new file mode 100644 index 000000000..b4c876cf0 --- /dev/null +++ b/tests/deps/x11-1.0/x11pragma.nim @@ -0,0 +1,20 @@ +# included from xlib bindings + + +when defined(use_pkg_config) or defined(use_pkg_config_static): + {.pragma: libx11, cdecl, importc.} + {.pragma: libx11c, cdecl.} + when defined(use_pkg_config_static): + {.passl: gorge("pkg-config x11 --static --libs").} + else: + {.passl: gorge("pkg-config x11 --libs").} +else: + when defined(macosx): + const + libX11* = "libX11.dylib" + else: + const + libX11* = "libX11.so(|.6)" + + {.pragma: libx11, cdecl, dynlib: libX11, importc.} + {.pragma: libx11c, cdecl, dynlib: libX11.} diff --git a/tests/deps/x11-1.0/xatom.nim b/tests/deps/x11-1.0/xatom.nim new file mode 100644 index 000000000..b2e1dca91 --- /dev/null +++ b/tests/deps/x11-1.0/xatom.nim @@ -0,0 +1,81 @@ +# +# THIS IS A GENERATED FILE +# +# Do not change! Changing this file implies a protocol change! +# + +import + X + +const + XA_PRIMARY* = TAtom(1) + XA_SECONDARY* = TAtom(2) + XA_ARC* = TAtom(3) + XA_ATOM* = TAtom(4) + XA_BITMAP* = TAtom(5) + XA_CARDINAL* = TAtom(6) + XA_COLORMAP* = TAtom(7) + XA_CURSOR* = TAtom(8) + XA_CUT_BUFFER0* = TAtom(9) + XA_CUT_BUFFER1* = TAtom(10) + XA_CUT_BUFFER2* = TAtom(11) + XA_CUT_BUFFER3* = TAtom(12) + XA_CUT_BUFFER4* = TAtom(13) + XA_CUT_BUFFER5* = TAtom(14) + XA_CUT_BUFFER6* = TAtom(15) + XA_CUT_BUFFER7* = TAtom(16) + XA_DRAWABLE* = TAtom(17) + XA_FONT* = TAtom(18) + XA_INTEGER* = TAtom(19) + XA_PIXMAP* = TAtom(20) + XA_POINT* = TAtom(21) + XA_RECTANGLE* = TAtom(22) + XA_RESOURCE_MANAGER* = TAtom(23) + XA_RGB_COLOR_MAP* = TAtom(24) + XA_RGB_BEST_MAP* = TAtom(25) + XA_RGB_BLUE_MAP* = TAtom(26) + XA_RGB_DEFAULT_MAP* = TAtom(27) + XA_RGB_GRAY_MAP* = TAtom(28) + XA_RGB_GREEN_MAP* = TAtom(29) + XA_RGB_RED_MAP* = TAtom(30) + XA_STRING* = TAtom(31) + XA_VISUALID* = TAtom(32) + XA_WINDOW* = TAtom(33) + XA_WM_COMMAND* = TAtom(34) + XA_WM_HINTS* = TAtom(35) + XA_WM_CLIENT_MACHINE* = TAtom(36) + XA_WM_ICON_NAME* = TAtom(37) + XA_WM_ICON_SIZE* = TAtom(38) + XA_WM_NAME* = TAtom(39) + XA_WM_NORMAL_HINTS* = TAtom(40) + XA_WM_SIZE_HINTS* = TAtom(41) + XA_WM_ZOOM_HINTS* = TAtom(42) + XA_MIN_SPACE* = TAtom(43) + XA_NORM_SPACE* = TAtom(44) + XA_MAX_SPACE* = TAtom(45) + XA_END_SPACE* = TAtom(46) + XA_SUPERSCRIPT_X* = TAtom(47) + XA_SUPERSCRIPT_Y* = TAtom(48) + XA_SUBSCRIPT_X* = TAtom(49) + XA_SUBSCRIPT_Y* = TAtom(50) + XA_UNDERLINE_POSITION* = TAtom(51) + XA_UNDERLINE_THICKNESS* = TAtom(52) + XA_STRIKEOUT_ASCENT* = TAtom(53) + XA_STRIKEOUT_DESCENT* = TAtom(54) + XA_ITALIC_ANGLE* = TAtom(55) + XA_X_HEIGHT* = TAtom(56) + XA_QUAD_WIDTH* = TAtom(57) + XA_WEIGHT* = TAtom(58) + XA_POINT_SIZE* = TAtom(59) + XA_RESOLUTION* = TAtom(60) + XA_COPYRIGHT* = TAtom(61) + XA_NOTICE* = TAtom(62) + XA_FONT_NAME* = TAtom(63) + XA_FAMILY_NAME* = TAtom(64) + XA_FULL_NAME* = TAtom(65) + XA_CAP_HEIGHT* = TAtom(66) + XA_WM_CLASS* = TAtom(67) + XA_WM_TRANSIENT_FOR* = TAtom(68) + XA_LAST_PREDEFINED* = TAtom(68) + +# implementation diff --git a/tests/deps/x11-1.0/xcms.nim b/tests/deps/x11-1.0/xcms.nim new file mode 100644 index 000000000..659676c45 --- /dev/null +++ b/tests/deps/x11-1.0/xcms.nim @@ -0,0 +1,389 @@ + +import + x, xlib + +#const +# libX11* = "X11" + +# +# Automatically converted by H2Pas 0.99.15 from xcms.h +# The following command line parameters were used: +# -p +# -T +# -S +# -d +# -c +# xcms.h +# + +const + XcmsFailure* = 0 + XcmsSuccess* = 1 + XcmsSuccessWithCompression* = 2 + +type + PXcmsColorFormat* = ptr TXcmsColorFormat + TXcmsColorFormat* = int32 + +proc XcmsUndefinedFormat*(): TXcmsColorFormat +proc XcmsCIEXYZFormat*(): TXcmsColorFormat +proc XcmsCIEuvYFormat*(): TXcmsColorFormat +proc XcmsCIExyYFormat*(): TXcmsColorFormat +proc XcmsCIELabFormat*(): TXcmsColorFormat +proc XcmsCIELuvFormat*(): TXcmsColorFormat +proc XcmsTekHVCFormat*(): TXcmsColorFormat +proc XcmsRGBFormat*(): TXcmsColorFormat +proc XcmsRGBiFormat*(): TXcmsColorFormat +const + XcmsInitNone* = 0x00000000 + XcmsInitSuccess* = 0x00000001 + XcmsInitFailure* = 0x000000FF + +type + PXcmsFloat* = ptr TXcmsFloat + TXcmsFloat* = float64 + PXcmsRGB* = ptr TXcmsRGB + TXcmsRGB*{.final.} = object + red*: int16 + green*: int16 + blue*: int16 + + PXcmsRGBi* = ptr TXcmsRGBi + TXcmsRGBi*{.final.} = object + red*: TXcmsFloat + green*: TXcmsFloat + blue*: TXcmsFloat + + PXcmsCIEXYZ* = ptr TXcmsCIEXYZ + TXcmsCIEXYZ*{.final.} = object + X*: TXcmsFloat + Y*: TXcmsFloat + Z*: TXcmsFloat + + PXcmsCIEuvY* = ptr TXcmsCIEuvY + TXcmsCIEuvY*{.final.} = object + u_prime*: TXcmsFloat + v_prime*: TXcmsFloat + Y*: TXcmsFloat + + PXcmsCIExyY* = ptr TXcmsCIExyY + TXcmsCIExyY*{.final.} = object + x*: TXcmsFloat + y*: TXcmsFloat + theY*: TXcmsFloat + + PXcmsCIELab* = ptr TXcmsCIELab + TXcmsCIELab*{.final.} = object + L_star*: TXcmsFloat + a_star*: TXcmsFloat + b_star*: TXcmsFloat + + PXcmsCIELuv* = ptr TXcmsCIELuv + TXcmsCIELuv*{.final.} = object + L_star*: TXcmsFloat + u_star*: TXcmsFloat + v_star*: TXcmsFloat + + PXcmsTekHVC* = ptr TXcmsTekHVC + TXcmsTekHVC*{.final.} = object + H*: TXcmsFloat + V*: TXcmsFloat + C*: TXcmsFloat + + PXcmsPad* = ptr TXcmsPad + TXcmsPad*{.final.} = object + pad0*: TXcmsFloat + pad1*: TXcmsFloat + pad2*: TXcmsFloat + pad3*: TXcmsFloat + + PXcmsColor* = ptr TXcmsColor + TXcmsColor*{.final.} = object # spec : record + # case longint of + # 0 : ( RGB : TXcmsRGB ); + # 1 : ( RGBi : TXcmsRGBi ); + # 2 : ( CIEXYZ : TXcmsCIEXYZ ); + # 3 : ( CIEuvY : TXcmsCIEuvY ); + # 4 : ( CIExyY : TXcmsCIExyY ); + # 5 : ( CIELab : TXcmsCIELab ); + # 6 : ( CIELuv : TXcmsCIELuv ); + # 7 : ( TekHVC : TXcmsTekHVC ); + # 8 : ( Pad : TXcmsPad ); + # end; + pad*: TXcmsPad + pixel*: int32 + format*: TXcmsColorFormat + + PXcmsPerScrnInfo* = ptr TXcmsPerScrnInfo + TXcmsPerScrnInfo*{.final.} = object + screenWhitePt*: TXcmsColor + functionSet*: TXPointer + screenData*: TXPointer + state*: int8 + pad*: array[0..2, char] + + PXcmsCCC* = ptr TXcmsCCC + TXcmsCompressionProc* = proc (para1: PXcmsCCC, para2: PXcmsColor, + para3: int32, para4: int32, para5: PBool): TStatus{. + cdecl.} + TXcmsWhiteAdjustProc* = proc (para1: PXcmsCCC, para2: PXcmsColor, + para3: PXcmsColor, para4: TXcmsColorFormat, + para5: PXcmsColor, para6: int32, para7: PBool): TStatus{. + cdecl.} + TXcmsCCC*{.final.} = object + dpy*: PDisplay + screenNumber*: int32 + visual*: PVisual + clientWhitePt*: TXcmsColor + gamutCompProc*: TXcmsCompressionProc + gamutCompClientData*: TXPointer + whitePtAdjProc*: TXcmsWhiteAdjustProc + whitePtAdjClientData*: TXPointer + pPerScrnInfo*: PXcmsPerScrnInfo + + TXcmsCCCRec* = TXcmsCCC + PXcmsCCCRec* = ptr TXcmsCCCRec + TXcmsScreenInitProc* = proc (para1: PDisplay, para2: int32, + para3: PXcmsPerScrnInfo): TStatus{.cdecl.} + TXcmsScreenFreeProc* = proc (para1: TXPointer){.cdecl.} + TXcmsConversionProc* = proc (){.cdecl.} + PXcmsFuncListPtr* = ptr TXcmsFuncListPtr + TXcmsFuncListPtr* = TXcmsConversionProc + TXcmsParseStringProc* = proc (para1: cstring, para2: PXcmsColor): int32{.cdecl.} + PXcmsColorSpace* = ptr TXcmsColorSpace + TXcmsColorSpace*{.final.} = object + prefix*: cstring + id*: TXcmsColorFormat + parseString*: TXcmsParseStringProc + to_CIEXYZ*: TXcmsFuncListPtr + from_CIEXYZ*: TXcmsFuncListPtr + inverse_flag*: int32 + + PXcmsFunctionSet* = ptr TXcmsFunctionSet + TXcmsFunctionSet*{.final.} = object # error + #extern Status XcmsAddColorSpace ( + #in declaration at line 323 + DDColorSpaces*: ptr PXcmsColorSpace + screenInitProc*: TXcmsScreenInitProc + screenFreeProc*: TXcmsScreenFreeProc + + +proc XcmsAddFunctionSet*(para1: PXcmsFunctionSet): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsAllocColor*(para1: PDisplay, para2: TColormap, para3: PXcmsColor, + para4: TXcmsColorFormat): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsAllocNamedColor*(para1: PDisplay, para2: TColormap, para3: cstring, + para4: PXcmsColor, para5: PXcmsColor, + para6: TXcmsColorFormat): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsCCCOfColormap*(para1: PDisplay, para2: TColormap): TXcmsCCC{.cdecl, + dynlib: libX11, importc.} +proc XcmsCIELabClipab*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsCIELabClipL*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsCIELabClipLab*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsCIELabQueryMaxC*(para1: TXcmsCCC, para2: TXcmsFloat, para3: TXcmsFloat, + para4: PXcmsColor): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsCIELabQueryMaxL*(para1: TXcmsCCC, para2: TXcmsFloat, para3: TXcmsFloat, + para4: PXcmsColor): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsCIELabQueryMaxLC*(para1: TXcmsCCC, para2: TXcmsFloat, para3: PXcmsColor): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsCIELabQueryMinL*(para1: TXcmsCCC, para2: TXcmsFloat, para3: TXcmsFloat, + para4: PXcmsColor): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsCIELabToCIEXYZ*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsCIELabWhiteShiftColors*(para1: TXcmsCCC, para2: PXcmsColor, + para3: PXcmsColor, para4: TXcmsColorFormat, + para5: PXcmsColor, para6: int32, para7: PBool): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsCIELuvClipL*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsCIELuvClipLuv*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsCIELuvClipuv*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsCIELuvQueryMaxC*(para1: TXcmsCCC, para2: TXcmsFloat, para3: TXcmsFloat, + para4: PXcmsColor): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsCIELuvQueryMaxL*(para1: TXcmsCCC, para2: TXcmsFloat, para3: TXcmsFloat, + para4: PXcmsColor): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsCIELuvQueryMaxLC*(para1: TXcmsCCC, para2: TXcmsFloat, para3: PXcmsColor): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsCIELuvQueryMinL*(para1: TXcmsCCC, para2: TXcmsFloat, para3: TXcmsFloat, + para4: PXcmsColor): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsCIELuvToCIEuvY*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsCIELuvWhiteShiftColors*(para1: TXcmsCCC, para2: PXcmsColor, + para3: PXcmsColor, para4: TXcmsColorFormat, + para5: PXcmsColor, para6: int32, para7: PBool): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsCIEXYZToCIELab*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsCIEXYZToCIEuvY*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsCIEXYZToCIExyY*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsCIEXYZToRGBi*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: PBool): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsCIEuvYToCIELuv*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsCIEuvYToCIEXYZ*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsCIEuvYToTekHVC*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsCIExyYToCIEXYZ*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsClientWhitePointOfCCC*(para1: TXcmsCCC): PXcmsColor{.cdecl, + dynlib: libX11, importc.} +proc XcmsConvertColors*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: TXcmsColorFormat, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsCreateCCC*(para1: PDisplay, para2: int32, para3: PVisual, + para4: PXcmsColor, para5: TXcmsCompressionProc, + para6: TXPointer, para7: TXcmsWhiteAdjustProc, + para8: TXPointer): TXcmsCCC{.cdecl, dynlib: libX11, importc.} +proc XcmsDefaultCCC*(para1: PDisplay, para2: int32): TXcmsCCC{.cdecl, + dynlib: libX11, importc.} +proc XcmsDisplayOfCCC*(para1: TXcmsCCC): PDisplay{.cdecl, dynlib: libX11, + importc.} +proc XcmsFormatOfPrefix*(para1: cstring): TXcmsColorFormat{.cdecl, + dynlib: libX11, importc.} +proc XcmsFreeCCC*(para1: TXcmsCCC){.cdecl, dynlib: libX11, importc.} +proc XcmsLookupColor*(para1: PDisplay, para2: TColormap, para3: cstring, + para4: PXcmsColor, para5: PXcmsColor, + para6: TXcmsColorFormat): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsPrefixOfFormat*(para1: TXcmsColorFormat): cstring{.cdecl, + dynlib: libX11, importc.} +proc XcmsQueryBlack*(para1: TXcmsCCC, para2: TXcmsColorFormat, para3: PXcmsColor): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsQueryBlue*(para1: TXcmsCCC, para2: TXcmsColorFormat, para3: PXcmsColor): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsQueryColor*(para1: PDisplay, para2: TColormap, para3: PXcmsColor, + para4: TXcmsColorFormat): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsQueryColors*(para1: PDisplay, para2: TColormap, para3: PXcmsColor, + para4: int32, para5: TXcmsColorFormat): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsQueryGreen*(para1: TXcmsCCC, para2: TXcmsColorFormat, para3: PXcmsColor): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsQueryRed*(para1: TXcmsCCC, para2: TXcmsColorFormat, para3: PXcmsColor): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsQueryWhite*(para1: TXcmsCCC, para2: TXcmsColorFormat, para3: PXcmsColor): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsRGBiToCIEXYZ*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: PBool): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsRGBiToRGB*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: PBool): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsRGBToRGBi*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: PBool): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsScreenNumberOfCCC*(para1: TXcmsCCC): int32{.cdecl, dynlib: libX11, + importc.} +proc XcmsScreenWhitePointOfCCC*(para1: TXcmsCCC): PXcmsColor{.cdecl, + dynlib: libX11, importc.} +proc XcmsSetCCCOfColormap*(para1: PDisplay, para2: TColormap, para3: TXcmsCCC): TXcmsCCC{. + cdecl, dynlib: libX11, importc.} +proc XcmsSetCompressionProc*(para1: TXcmsCCC, para2: TXcmsCompressionProc, + para3: TXPointer): TXcmsCompressionProc{.cdecl, + dynlib: libX11, importc.} +proc XcmsSetWhiteAdjustProc*(para1: TXcmsCCC, para2: TXcmsWhiteAdjustProc, + para3: TXPointer): TXcmsWhiteAdjustProc{.cdecl, + dynlib: libX11, importc.} +proc XcmsSetWhitePoint*(para1: TXcmsCCC, para2: PXcmsColor): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsStoreColor*(para1: PDisplay, para2: TColormap, para3: PXcmsColor): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsStoreColors*(para1: PDisplay, para2: TColormap, para3: PXcmsColor, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsTekHVCClipC*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsTekHVCClipV*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsTekHVCClipVC*(para1: TXcmsCCC, para2: PXcmsColor, para3: int32, + para4: int32, para5: PBool): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XcmsTekHVCQueryMaxC*(para1: TXcmsCCC, para2: TXcmsFloat, para3: TXcmsFloat, + para4: PXcmsColor): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsTekHVCQueryMaxV*(para1: TXcmsCCC, para2: TXcmsFloat, para3: TXcmsFloat, + para4: PXcmsColor): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsTekHVCQueryMaxVC*(para1: TXcmsCCC, para2: TXcmsFloat, para3: PXcmsColor): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsTekHVCQueryMaxVSamples*(para1: TXcmsCCC, para2: TXcmsFloat, + para3: PXcmsColor, para4: int32): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsTekHVCQueryMinV*(para1: TXcmsCCC, para2: TXcmsFloat, para3: TXcmsFloat, + para4: PXcmsColor): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XcmsTekHVCToCIEuvY*(para1: TXcmsCCC, para2: PXcmsColor, para3: PXcmsColor, + para4: int32): TStatus{.cdecl, dynlib: libX11, importc.} +proc XcmsTekHVCWhiteShiftColors*(para1: TXcmsCCC, para2: PXcmsColor, + para3: PXcmsColor, para4: TXcmsColorFormat, + para5: PXcmsColor, para6: int32, para7: PBool): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XcmsVisualOfCCC*(para1: TXcmsCCC): PVisual{.cdecl, dynlib: libX11, importc.} +# implementation + +proc XcmsUndefinedFormat(): TXcmsColorFormat = + result = 0x00000000'i32 + +proc XcmsCIEXYZFormat(): TXcmsColorFormat = + result = 0x00000001'i32 + +proc XcmsCIEuvYFormat(): TXcmsColorFormat = + result = 0x00000002'i32 + +proc XcmsCIExyYFormat(): TXcmsColorFormat = + result = 0x00000003'i32 + +proc XcmsCIELabFormat(): TXcmsColorFormat = + result = 0x00000004'i32 + +proc XcmsCIELuvFormat(): TXcmsColorFormat = + result = 0x00000005'i32 + +proc XcmsTekHVCFormat(): TXcmsColorFormat = + result = 0x00000006'i32 + +proc XcmsRGBFormat(): TXcmsColorFormat = + result = 0x80000000'i32 + +proc XcmsRGBiFormat(): TXcmsColorFormat = + result = 0x80000001'i32 + +#when defined(MACROS): +proc DisplayOfCCC(ccc: TXcmsCCC): PDisplay = + result = ccc.dpy + +proc ScreenNumberOfCCC(ccc: TXcmsCCC): int32 = + result = ccc.screenNumber + +proc VisualOfCCC(ccc: TXcmsCCC): PVisual = + result = ccc.visual + +proc ClientWhitePointOfCCC(ccc: var TXcmsCCC): ptr TXcmsColor = + result = addr(ccc.clientWhitePt) + +proc ScreenWhitePointOfCCC(ccc: var TXcmsCCC): ptr TXcmsColor = + result = addr(ccc.pPerScrnInfo.screenWhitePt) + +proc FunctionSetOfCCC(ccc: TXcmsCCC): TXpointer = + result = ccc.pPerScrnInfo.functionSet diff --git a/tests/deps/x11-1.0/xf86dga.nim b/tests/deps/x11-1.0/xf86dga.nim new file mode 100644 index 000000000..65e26df22 --- /dev/null +++ b/tests/deps/x11-1.0/xf86dga.nim @@ -0,0 +1,235 @@ +# +# Copyright (c) 1999 XFree86 Inc +# +# $XFree86: xc/include/extensions/xf86dga.h,v 3.20 1999/10/13 04:20:48 dawes Exp $ + +import + x, xlib + +const + libXxf86dga* = "libXxf86dga.so" + +#type +# cfloat* = float32 + +# $XFree86: xc/include/extensions/xf86dga1.h,v 1.2 1999/04/17 07:05:41 dawes Exp $ +# +# +#Copyright (c) 1995 Jon Tombs +#Copyright (c) 1995 XFree86 Inc +# +# +#************************************************************************ +# +# THIS IS THE OLD DGA API AND IS OBSOLETE. PLEASE DO NOT USE IT ANYMORE +# +#************************************************************************ + +type + PPcchar* = ptr ptr cstring + +const + X_XF86DGAQueryVersion* = 0 + X_XF86DGAGetVideoLL* = 1 + X_XF86DGADirectVideo* = 2 + X_XF86DGAGetViewPortSize* = 3 + X_XF86DGASetViewPort* = 4 + X_XF86DGAGetVidPage* = 5 + X_XF86DGASetVidPage* = 6 + X_XF86DGAInstallColormap* = 7 + X_XF86DGAQueryDirectVideo* = 8 + X_XF86DGAViewPortChanged* = 9 + XF86DGADirectPresent* = 0x00000001 + XF86DGADirectGraphics* = 0x00000002 + XF86DGADirectMouse* = 0x00000004 + XF86DGADirectKeyb* = 0x00000008 + XF86DGAHasColormap* = 0x00000100 + XF86DGADirectColormap* = 0x00000200 + +proc XF86DGAQueryVersion*(dpy: PDisplay, majorVersion: Pcint, + minorVersion: Pcint): TBool{.cdecl, + dynlib: libXxf86dga, importc.} +proc XF86DGAQueryExtension*(dpy: PDisplay, event_base: Pcint, error_base: Pcint): TBool{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGAGetVideoLL*(dpy: PDisplay, screen: cint, base_addr: Pcint, + width: Pcint, bank_size: Pcint, ram_size: Pcint): TStatus{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGAGetVideo*(dpy: PDisplay, screen: cint, base_addr: PPcchar, + width: Pcint, bank_size: Pcint, ram_size: Pcint): TStatus{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGADirectVideo*(dpy: PDisplay, screen: cint, enable: cint): TStatus{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGADirectVideoLL*(dpy: PDisplay, screen: cint, enable: cint): TStatus{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGAGetViewPortSize*(dpy: PDisplay, screen: cint, width: Pcint, + height: Pcint): TStatus{.cdecl, + dynlib: libXxf86dga, importc.} +proc XF86DGASetViewPort*(dpy: PDisplay, screen: cint, x: cint, y: cint): TStatus{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGAGetVidPage*(dpy: PDisplay, screen: cint, vid_page: Pcint): TStatus{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGASetVidPage*(dpy: PDisplay, screen: cint, vid_page: cint): TStatus{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGAInstallColormap*(dpy: PDisplay, screen: cint, Colormap: TColormap): TStatus{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGAForkApp*(screen: cint): cint{.cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGAQueryDirectVideo*(dpy: PDisplay, screen: cint, flags: Pcint): TStatus{. + cdecl, dynlib: libXxf86dga, importc.} +proc XF86DGAViewPortChanged*(dpy: PDisplay, screen: cint, n: cint): TBool{. + cdecl, dynlib: libXxf86dga, importc.} +const + X_XDGAQueryVersion* = 0 # 1 through 9 are in xf86dga1.pp + # 10 and 11 are reserved to avoid conflicts with rogue DGA extensions + X_XDGAQueryModes* = 12 + X_XDGASetMode* = 13 + X_XDGASetViewport* = 14 + X_XDGAInstallColormap* = 15 + X_XDGASelectInput* = 16 + X_XDGAFillRectangle* = 17 + X_XDGACopyArea* = 18 + X_XDGACopyTransparentArea* = 19 + X_XDGAGetViewportStatus* = 20 + X_XDGASync* = 21 + X_XDGAOpenFramebuffer* = 22 + X_XDGACloseFramebuffer* = 23 + X_XDGASetClientVersion* = 24 + X_XDGAChangePixmapMode* = 25 + X_XDGACreateColormap* = 26 + XDGAConcurrentAccess* = 0x00000001 + XDGASolidFillRect* = 0x00000002 + XDGABlitRect* = 0x00000004 + XDGABlitTransRect* = 0x00000008 + XDGAPixmap* = 0x00000010 + XDGAInterlaced* = 0x00010000 + XDGADoublescan* = 0x00020000 + XDGAFlipImmediate* = 0x00000001 + XDGAFlipRetrace* = 0x00000002 + XDGANeedRoot* = 0x00000001 + XF86DGANumberEvents* = 7 + XDGAPixmapModeLarge* = 0 + XDGAPixmapModeSmall* = 1 + XF86DGAClientNotLocal* = 0 + XF86DGANoDirectVideoMode* = 1 + XF86DGAScreenNotActive* = 2 + XF86DGADirectNotActivated* = 3 + XF86DGAOperationNotSupported* = 4 + XF86DGANumberErrors* = (XF86DGAOperationNotSupported + 1) + +type + PXDGAMode* = ptr TXDGAMode + TXDGAMode*{.final.} = object + num*: cint # A unique identifier for the mode (num > 0) + name*: cstring # name of mode given in the XF86Config + verticalRefresh*: cfloat + flags*: cint # DGA_CONCURRENT_ACCESS, etc... + imageWidth*: cint # linear accessible portion (pixels) + imageHeight*: cint + pixmapWidth*: cint # Xlib accessible portion (pixels) + pixmapHeight*: cint # both fields ignored if no concurrent access + bytesPerScanline*: cint + byteOrder*: cint # MSBFirst, LSBFirst + depth*: cint + bitsPerPixel*: cint + redMask*: culong + greenMask*: culong + blueMask*: culong + visualClass*: cshort + viewportWidth*: cint + viewportHeight*: cint + xViewportStep*: cint # viewport position granularity + yViewportStep*: cint + maxViewportX*: cint # max viewport origin + maxViewportY*: cint + viewportFlags*: cint # types of page flipping possible + reserved1*: cint + reserved2*: cint + + PXDGADevice* = ptr TXDGADevice + TXDGADevice*{.final.} = object + mode*: TXDGAMode + data*: Pcuchar + pixmap*: TPixmap + + PXDGAButtonEvent* = ptr TXDGAButtonEvent + TXDGAButtonEvent*{.final.} = object + theType*: cint + serial*: culong + display*: PDisplay + screen*: cint + time*: TTime + state*: cuint + button*: cuint + + PXDGAKeyEvent* = ptr TXDGAKeyEvent + TXDGAKeyEvent*{.final.} = object + theType*: cint + serial*: culong + display*: PDisplay + screen*: cint + time*: TTime + state*: cuint + keycode*: cuint + + PXDGAMotionEvent* = ptr TXDGAMotionEvent + TXDGAMotionEvent*{.final.} = object + theType*: cint + serial*: culong + display*: PDisplay + screen*: cint + time*: TTime + state*: cuint + dx*: cint + dy*: cint + + PXDGAEvent* = ptr TXDGAEvent + TXDGAEvent*{.final.} = object + pad*: array[0..23, clong] # sorry you have to cast if you want access + #Case LongInt Of + # 0 : (_type : cint); + # 1 : (xbutton : TXDGAButtonEvent); + # 2 : (xkey : TXDGAKeyEvent); + # 3 : (xmotion : TXDGAMotionEvent); + # 4 : (pad : Array[0..23] Of clong); + + +proc XDGAQueryExtension*(dpy: PDisplay, eventBase: Pcint, erroBase: Pcint): TBool{. + cdecl, dynlib: libXxf86dga, importc.} +proc XDGAQueryVersion*(dpy: PDisplay, majorVersion: Pcint, minorVersion: Pcint): TBool{. + cdecl, dynlib: libXxf86dga, importc.} +proc XDGAQueryModes*(dpy: PDisplay, screen: cint, num: Pcint): PXDGAMode{.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGASetMode*(dpy: PDisplay, screen: cint, mode: cint): PXDGADevice{.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGAOpenFramebuffer*(dpy: PDisplay, screen: cint): TBool{.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGACloseFramebuffer*(dpy: PDisplay, screen: cint){.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGASetViewport*(dpy: PDisplay, screen: cint, x: cint, y: cint, flags: cint){. + cdecl, dynlib: libXxf86dga, importc.} +proc XDGAInstallColormap*(dpy: PDisplay, screen: cint, cmap: TColormap){.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGACreateColormap*(dpy: PDisplay, screen: cint, device: PXDGADevice, + alloc: cint): TColormap{.cdecl, dynlib: libXxf86dga, + importc.} +proc XDGASelectInput*(dpy: PDisplay, screen: cint, event_mask: clong){.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGAFillRectangle*(dpy: PDisplay, screen: cint, x: cint, y: cint, + width: cuint, height: cuint, color: culong){.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGACopyArea*(dpy: PDisplay, screen: cint, srcx: cint, srcy: cint, + width: cuint, height: cuint, dstx: cint, dsty: cint){.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGACopyTransparentArea*(dpy: PDisplay, screen: cint, srcx: cint, + srcy: cint, width: cuint, height: cuint, + dstx: cint, dsty: cint, key: culong){.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGAGetViewportStatus*(dpy: PDisplay, screen: cint): cint{.cdecl, + dynlib: libXxf86dga, importc.} +proc XDGASync*(dpy: PDisplay, screen: cint){.cdecl, dynlib: libXxf86dga, importc.} +proc XDGASetClientVersion*(dpy: PDisplay): TBool{.cdecl, dynlib: libXxf86dga, + importc.} +proc XDGAChangePixmapMode*(dpy: PDisplay, screen: cint, x: Pcint, y: Pcint, + mode: cint){.cdecl, dynlib: libXxf86dga, importc.} +proc XDGAKeyEventToXKeyEvent*(dk: PXDGAKeyEvent, xk: PXKeyEvent){.cdecl, + dynlib: libXxf86dga, importc.} +# implementation diff --git a/tests/deps/x11-1.0/xf86vmode.nim b/tests/deps/x11-1.0/xf86vmode.nim new file mode 100644 index 000000000..547f5cbd2 --- /dev/null +++ b/tests/deps/x11-1.0/xf86vmode.nim @@ -0,0 +1,229 @@ +# $XFree86: xc/include/extensions/xf86vmode.h,v 3.30 2001/05/07 20:09:50 mvojkovi Exp $ +# +# +#Copyright 1995 Kaleb S. KEITHLEY +# +#Permission is hereby granted, free of charge, to any person obtaining +#a copy of this software and associated documentation files (the +#"Software"), to deal in the Software without restriction, including +#without limitation the rights to use, copy, modify, merge, publish, +#distribute, sublicense, and/or sell copies of the Software, and to +#permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be +#included in all copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +#EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +#MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +#IN NO EVENT SHALL Kaleb S. KEITHLEY BE LIABLE FOR ANY CLAIM, DAMAGES +#OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +#ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +#OTHER DEALINGS IN THE SOFTWARE. +# +#Except as contained in this notice, the name of Kaleb S. KEITHLEY +#shall not be used in advertising or otherwise to promote the sale, use +#or other dealings in this Software without prior written authorization +#from Kaleb S. KEITHLEY +# +# +# $Xorg: xf86vmode.h,v 1.3 2000/08/18 04:05:46 coskrey Exp $ +# THIS IS NOT AN X CONSORTIUM STANDARD OR AN X PROJECT TEAM SPECIFICATION + +import + x, xlib + +const + libXxf86vm* = "libXxf86vm.so" + +type + PINT32* = ptr int32 + +const + X_XF86VidModeQueryVersion* = 0 + X_XF86VidModeGetModeLine* = 1 + X_XF86VidModeModModeLine* = 2 + X_XF86VidModeSwitchMode* = 3 + X_XF86VidModeGetMonitor* = 4 + X_XF86VidModeLockModeSwitch* = 5 + X_XF86VidModeGetAllModeLines* = 6 + X_XF86VidModeAddModeLine* = 7 + X_XF86VidModeDeleteModeLine* = 8 + X_XF86VidModeValidateModeLine* = 9 + X_XF86VidModeSwitchToMode* = 10 + X_XF86VidModeGetViewPort* = 11 + X_XF86VidModeSetViewPort* = 12 # new for version 2.x of this extension + X_XF86VidModeGetDotClocks* = 13 + X_XF86VidModeSetClientVersion* = 14 + X_XF86VidModeSetGamma* = 15 + X_XF86VidModeGetGamma* = 16 + X_XF86VidModeGetGammaRamp* = 17 + X_XF86VidModeSetGammaRamp* = 18 + X_XF86VidModeGetGammaRampSize* = 19 + X_XF86VidModeGetPermissions* = 20 + CLKFLAG_PROGRAMABLE* = 1 + +when defined(XF86VIDMODE_EVENTS): + const + XF86VidModeNotify* = 0 + XF86VidModeNumberEvents* = (XF86VidModeNotify + 1) + XF86VidModeNotifyMask* = 0x00000001 + XF86VidModeNonEvent* = 0 + XF86VidModeModeChange* = 1 +else: + const + XF86VidModeNumberEvents* = 0 +const + XF86VidModeBadClock* = 0 + XF86VidModeBadHTimings* = 1 + XF86VidModeBadVTimings* = 2 + XF86VidModeModeUnsuitable* = 3 + XF86VidModeExtensionDisabled* = 4 + XF86VidModeClientNotLocal* = 5 + XF86VidModeZoomLocked* = 6 + XF86VidModeNumberErrors* = (XF86VidModeZoomLocked + 1) + XF86VM_READ_PERMISSION* = 1 + XF86VM_WRITE_PERMISSION* = 2 + +type + PXF86VidModeModeLine* = ptr TXF86VidModeModeLine + TXF86VidModeModeLine*{.final.} = object + hdisplay*: cushort + hsyncstart*: cushort + hsyncend*: cushort + htotal*: cushort + hskew*: cushort + vdisplay*: cushort + vsyncstart*: cushort + vsyncend*: cushort + vtotal*: cushort + flags*: cuint + privsize*: cint + c_private*: PINT32 + + PPPXF86VidModeModeInfo* = ptr PPXF86VidModeModeInfo + PPXF86VidModeModeInfo* = ptr PXF86VidModeModeInfo + PXF86VidModeModeInfo* = ptr TXF86VidModeModeInfo + TXF86VidModeModeInfo*{.final.} = object + dotclock*: cuint + hdisplay*: cushort + hsyncstart*: cushort + hsyncend*: cushort + htotal*: cushort + hskew*: cushort + vdisplay*: cushort + vsyncstart*: cushort + vsyncend*: cushort + vtotal*: cushort + flags*: cuint + privsize*: cint + c_private*: PINT32 + + PXF86VidModeSyncRange* = ptr TXF86VidModeSyncRange + TXF86VidModeSyncRange*{.final.} = object + hi*: cfloat + lo*: cfloat + + PXF86VidModeMonitor* = ptr TXF86VidModeMonitor + TXF86VidModeMonitor*{.final.} = object + vendor*: cstring + model*: cstring + EMPTY*: cfloat + nhsync*: cuchar + hsync*: PXF86VidModeSyncRange + nvsync*: cuchar + vsync*: PXF86VidModeSyncRange + + PXF86VidModeNotifyEvent* = ptr TXF86VidModeNotifyEvent + TXF86VidModeNotifyEvent*{.final.} = object + theType*: cint # of event + serial*: culong # # of last request processed by server + send_event*: TBool # true if this came from a SendEvent req + display*: PDisplay # Display the event was read from + root*: TWindow # root window of event screen + state*: cint # What happened + kind*: cint # What happened + forced*: TBool # extents of new region + time*: TTime # event timestamp + + PXF86VidModeGamma* = ptr TXF86VidModeGamma + TXF86VidModeGamma*{.final.} = object + red*: cfloat # Red Gamma value + green*: cfloat # Green Gamma value + blue*: cfloat # Blue Gamma value + + +when defined(MACROS): + proc XF86VidModeSelectNextMode*(disp: PDisplay, scr: cint): TBool + proc XF86VidModeSelectPrevMode*(disp: PDisplay, scr: cint): TBool +proc XF86VidModeQueryVersion*(dpy: PDisplay, majorVersion: Pcint, + minorVersion: Pcint): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeQueryExtension*(dpy: PDisplay, event_base: Pcint, + error_base: Pcint): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeSetClientVersion*(dpy: PDisplay): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeGetModeLine*(dpy: PDisplay, screen: cint, dotclock: Pcint, + modeline: PXF86VidModeModeLine): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeGetAllModeLines*(dpy: PDisplay, screen: cint, modecount: Pcint, + modelinesPtr: PPPXF86VidModeModeInfo): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeAddModeLine*(dpy: PDisplay, screen: cint, + new_modeline: PXF86VidModeModeInfo, + after_modeline: PXF86VidModeModeInfo): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeDeleteModeLine*(dpy: PDisplay, screen: cint, + modeline: PXF86VidModeModeInfo): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeModModeLine*(dpy: PDisplay, screen: cint, + modeline: PXF86VidModeModeLine): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeValidateModeLine*(dpy: PDisplay, screen: cint, + modeline: PXF86VidModeModeInfo): TStatus{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeSwitchMode*(dpy: PDisplay, screen: cint, zoom: cint): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeSwitchToMode*(dpy: PDisplay, screen: cint, + modeline: PXF86VidModeModeInfo): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeLockModeSwitch*(dpy: PDisplay, screen: cint, lock: cint): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeGetMonitor*(dpy: PDisplay, screen: cint, + monitor: PXF86VidModeMonitor): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeGetViewPort*(dpy: PDisplay, screen: cint, x_return: Pcint, + y_return: Pcint): TBool{.cdecl, dynlib: libXxf86vm, + importc.} +proc XF86VidModeSetViewPort*(dpy: PDisplay, screen: cint, x: cint, y: cint): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeGetDotClocks*(dpy: PDisplay, screen: cint, flags_return: Pcint, + number_of_clocks_return: Pcint, + max_dot_clock_return: Pcint, clocks_return: PPcint): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeGetGamma*(dpy: PDisplay, screen: cint, Gamma: PXF86VidModeGamma): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeSetGamma*(dpy: PDisplay, screen: cint, Gamma: PXF86VidModeGamma): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeSetGammaRamp*(dpy: PDisplay, screen: cint, size: cint, + red_array: Pcushort, green_array: Pcushort, + blue_array: Pcushort): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeGetGammaRamp*(dpy: PDisplay, screen: cint, size: cint, + red_array: Pcushort, green_array: Pcushort, + blue_array: Pcushort): TBool{.cdecl, + dynlib: libXxf86vm, importc.} +proc XF86VidModeGetGammaRampSize*(dpy: PDisplay, screen: cint, size: Pcint): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +proc XF86VidModeGetPermissions*(dpy: PDisplay, screen: cint, permissions: Pcint): TBool{. + cdecl, dynlib: libXxf86vm, importc.} +# implementation + +#when defined(MACROS): +proc XF86VidModeSelectNextMode(disp: PDisplay, scr: cint): TBool = + XF86VidModeSwitchMode(disp, scr, 1) + +proc XF86VidModeSelectPrevMode(disp: PDisplay, scr: cint): TBool = + XF86VidModeSwitchMode(disp, scr, - 1) diff --git a/tests/deps/x11-1.0/xi.nim b/tests/deps/x11-1.0/xi.nim new file mode 100644 index 000000000..d1b9f7846 --- /dev/null +++ b/tests/deps/x11-1.0/xi.nim @@ -0,0 +1,307 @@ +# +# $Xorg: XI.h,v 1.4 2001/02/09 02:03:23 xorgcvs Exp $ +# +#************************************************************ +# +#Copyright 1989, 1998 The Open Group +# +#Permission to use, copy, modify, distribute, and sell this software and its +#documentation for any purpose is hereby granted without fee, provided that +#the above copyright notice appear in all copies and that both that +#copyright notice and this permission notice appear in supporting +#documentation. +# +#The above copyright notice and this permission notice shall be included in +#all copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +#AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +#CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +#Except as contained in this notice, the name of The Open Group shall not be +#used in advertising or otherwise to promote the sale, use or other dealings +#in this Software without prior written authorization from The Open Group. +# +#Copyright 1989 by Hewlett-Packard Company, Palo Alto, California. +# +# All Rights Reserved +# +#Permission to use, copy, modify, and distribute this software and its +#documentation for any purpose and without fee is hereby granted, +#provided that the above copyright notice appear in all copies and that +#both that copyright notice and this permission notice appear in +#supporting documentation, and that the name of Hewlett-Packard not be +#used in advertising or publicity pertaining to distribution of the +#software without specific, written prior permission. +# +#HEWLETT-PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +#ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +#HEWLETT-PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +#ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +#WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +#ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +#SOFTWARE. +# +#********************************************************/ +# $XFree86: xc/include/extensions/XI.h,v 1.5 2001/12/14 19:53:28 dawes Exp $ +# +# Definitions used by the server, library and client +# +# Pascal Convertion was made by Ido Kannner - kanerido@actcom.net.il +# +#Histroy: +# 2004/10/15 - Fixed a bug of accessing second based records by removing "paced record" and chnaged it to +# "reocrd" only. +# 2004/10/07 - Removed the "uses X;" line. The unit does not need it. +# 2004/10/03 - Conversion from C header to Pascal unit. +# + +const + sz_xGetExtensionVersionReq* = 8 + sz_xGetExtensionVersionReply* = 32 + sz_xListInputDevicesReq* = 4 + sz_xListInputDevicesReply* = 32 + sz_xOpenDeviceReq* = 8 + sz_xOpenDeviceReply* = 32 + sz_xCloseDeviceReq* = 8 + sz_xSetDeviceModeReq* = 8 + sz_xSetDeviceModeReply* = 32 + sz_xSelectExtensionEventReq* = 12 + sz_xGetSelectedExtensionEventsReq* = 8 + sz_xGetSelectedExtensionEventsReply* = 32 + sz_xChangeDeviceDontPropagateListReq* = 12 + sz_xGetDeviceDontPropagateListReq* = 8 + sz_xGetDeviceDontPropagateListReply* = 32 + sz_xGetDeviceMotionEventsReq* = 16 + sz_xGetDeviceMotionEventsReply* = 32 + sz_xChangeKeyboardDeviceReq* = 8 + sz_xChangeKeyboardDeviceReply* = 32 + sz_xChangePointerDeviceReq* = 8 + sz_xChangePointerDeviceReply* = 32 + sz_xGrabDeviceReq* = 20 + sz_xGrabDeviceReply* = 32 + sz_xUngrabDeviceReq* = 12 + sz_xGrabDeviceKeyReq* = 20 + sz_xGrabDeviceKeyReply* = 32 + sz_xUngrabDeviceKeyReq* = 16 + sz_xGrabDeviceButtonReq* = 20 + sz_xGrabDeviceButtonReply* = 32 + sz_xUngrabDeviceButtonReq* = 16 + sz_xAllowDeviceEventsReq* = 12 + sz_xGetDeviceFocusReq* = 8 + sz_xGetDeviceFocusReply* = 32 + sz_xSetDeviceFocusReq* = 16 + sz_xGetFeedbackControlReq* = 8 + sz_xGetFeedbackControlReply* = 32 + sz_xChangeFeedbackControlReq* = 12 + sz_xGetDeviceKeyMappingReq* = 8 + sz_xGetDeviceKeyMappingReply* = 32 + sz_xChangeDeviceKeyMappingReq* = 8 + sz_xGetDeviceModifierMappingReq* = 8 + sz_xSetDeviceModifierMappingReq* = 8 + sz_xSetDeviceModifierMappingReply* = 32 + sz_xGetDeviceButtonMappingReq* = 8 + sz_xGetDeviceButtonMappingReply* = 32 + sz_xSetDeviceButtonMappingReq* = 8 + sz_xSetDeviceButtonMappingReply* = 32 + sz_xQueryDeviceStateReq* = 8 + sz_xQueryDeviceStateReply* = 32 + sz_xSendExtensionEventReq* = 16 + sz_xDeviceBellReq* = 8 + sz_xSetDeviceValuatorsReq* = 8 + sz_xSetDeviceValuatorsReply* = 32 + sz_xGetDeviceControlReq* = 8 + sz_xGetDeviceControlReply* = 32 + sz_xChangeDeviceControlReq* = 8 + sz_xChangeDeviceControlReply* = 32 + +const + INAME* = "XInputExtension" + +const + XI_KEYBOARD* = "KEYBOARD" + XI_MOUSE* = "MOUSE" + XI_TABLET* = "TABLET" + XI_TOUCHSCREEN* = "TOUCHSCREEN" + XI_TOUCHPAD* = "TOUCHPAD" + XI_BARCODE* = "BARCODE" + XI_BUTTONBOX* = "BUTTONBOX" + XI_KNOB_BOX* = "KNOB_BOX" + XI_ONE_KNOB* = "ONE_KNOB" + XI_NINE_KNOB* = "NINE_KNOB" + XI_TRACKBALL* = "TRACKBALL" + XI_QUADRATURE* = "QUADRATURE" + XI_ID_MODULE* = "ID_MODULE" + XI_SPACEBALL* = "SPACEBALL" + XI_DATAGLOVE* = "DATAGLOVE" + XI_EYETRACKER* = "EYETRACKER" + XI_CURSORKEYS* = "CURSORKEYS" + XI_FOOTMOUSE* = "FOOTMOUSE" + +const + Dont_Check* = 0 + XInput_Initial_Release* = 1 + XInput_Add_XDeviceBell* = 2 + XInput_Add_XSetDeviceValuators* = 3 + XInput_Add_XChangeDeviceControl* = 4 + +const + XI_Absent* = 0 + XI_Present* = 1 + +const + XI_Initial_Release_Major* = 1 + XI_Initial_Release_Minor* = 0 + +const + XI_Add_XDeviceBell_Major* = 1 + XI_Add_XDeviceBell_Minor* = 1 + +const + XI_Add_XSetDeviceValuators_Major* = 1 + XI_Add_XSetDeviceValuators_Minor* = 2 + +const + XI_Add_XChangeDeviceControl_Major* = 1 + XI_Add_XChangeDeviceControl_Minor* = 3 + +const + DEVICE_RESOLUTION* = 1 + +const + NoSuchExtension* = 1 + +const + COUNT* = 0 + CREATE* = 1 + +const + NewPointer* = 0 + NewKeyboard* = 1 + +const + XPOINTER* = 0 + XKEYBOARD* = 1 + +const + UseXKeyboard* = 0x000000FF + +const + IsXPointer* = 0 + IsXKeyboard* = 1 + IsXExtensionDevice* = 2 + +const + AsyncThisDevice* = 0 + SyncThisDevice* = 1 + ReplayThisDevice* = 2 + AsyncOtherDevices* = 3 + AsyncAll* = 4 + SyncAll* = 5 + +const + FollowKeyboard* = 3 + RevertToFollowKeyboard* = 3 + +const + DvAccelNum* = int(1) shl 0 + DvAccelDenom* = int(1) shl 1 + DvThreshold* = int(1) shl 2 + +const + DvKeyClickPercent* = int(1) shl 0 + DvPercent* = int(1) shl 1 + DvPitch* = int(1) shl 2 + DvDuration* = int(1) shl 3 + DvLed* = int(1) shl 4 + DvLedMode* = int(1) shl 5 + DvKey* = int(1) shl 6 + DvAutoRepeatMode* = 1 shl 7 + +const + DvString* = int(1) shl 0 + +const + DvInteger* = int(1) shl 0 + +const + DeviceMode* = int(1) shl 0 + Relative* = 0 + Absolute* = 1 # Merged from Metrolink tree for XINPUT stuff + TS_Raw* = 57 + TS_Scaled* = 58 + SendCoreEvents* = 59 + DontSendCoreEvents* = 60 # End of merged section + +const + ProximityState* = int(1) shl 1 + InProximity* = int(0) shl 1 + OutOfProximity* = int(1) shl 1 + +const + AddToList* = 0 + DeleteFromList* = 1 + +const + KeyClass* = 0 + ButtonClass* = 1 + ValuatorClass* = 2 + FeedbackClass* = 3 + ProximityClass* = 4 + FocusClass* = 5 + OtherClass* = 6 + +const + KbdFeedbackClass* = 0 + PtrFeedbackClass* = 1 + StringFeedbackClass* = 2 + IntegerFeedbackClass* = 3 + LedFeedbackClass* = 4 + BellFeedbackClass* = 5 + +const + devicePointerMotionHint* = 0 + deviceButton1Motion* = 1 + deviceButton2Motion* = 2 + deviceButton3Motion* = 3 + deviceButton4Motion* = 4 + deviceButton5Motion* = 5 + deviceButtonMotion* = 6 + deviceButtonGrab* = 7 + deviceOwnerGrabButton* = 8 + noExtensionEvent* = 9 + +const + XI_BadDevice* = 0 + XI_BadEvent* = 1 + XI_BadMode* = 2 + XI_DeviceBusy* = 3 + XI_BadClass* = 4 # Make XEventClass be a CARD32 for 64 bit servers. Don't affect client + # definition of XEventClass since that would be a library interface change. + # See the top of X.h for more _XSERVER64 magic. + # + +when defined(XSERVER64): + type + XEventClass* = CARD32 +else: + type + XEventClass* = int32 +#****************************************************************** +# * +# * Extension version structure. +# * +# + +type + PXExtensionVersion* = ptr TXExtensionVersion + TXExtensionVersion*{.final.} = object + present*: int16 + major_version*: int16 + minor_version*: int16 + + +# implementation diff --git a/tests/deps/x11-1.0/xinerama.nim b/tests/deps/x11-1.0/xinerama.nim new file mode 100644 index 000000000..96f5d7da3 --- /dev/null +++ b/tests/deps/x11-1.0/xinerama.nim @@ -0,0 +1,25 @@ +# Converted from X11/Xinerama.h +import + xlib + +const + xineramaLib = "libXinerama.so" + +type + PXineramaScreenInfo* = ptr TXineramaScreenInfo + TXineramaScreenInfo*{.final.} = object + screen_number*: cint + x_org*: int16 + y_org*: int16 + width*: int16 + height*: int16 + + +proc XineramaQueryExtension*(dpy: PDisplay, event_base: Pcint, error_base: Pcint): TBool{. + cdecl, dynlib: xineramaLib, importc.} +proc XineramaQueryVersion*(dpy: PDisplay, major: Pcint, minor: Pcint): TStatus{. + cdecl, dynlib: xineramaLib, importc.} +proc XineramaIsActive*(dpy: PDisplay): TBool{.cdecl, dynlib: xineramaLib, importc.} +proc XineramaQueryScreens*(dpy: PDisplay, number: Pcint): PXineramaScreenInfo{. + cdecl, dynlib: xineramaLib, importc.} + diff --git a/tests/deps/x11-1.0/xkb.nim b/tests/deps/x11-1.0/xkb.nim new file mode 100644 index 000000000..2ecf17026 --- /dev/null +++ b/tests/deps/x11-1.0/xkb.nim @@ -0,0 +1,2387 @@ +# +# $Xorg: XKB.h,v 1.3 2000/08/18 04:05:45 coskrey Exp $ +#************************************************************ +# $Xorg: XKBstr.h,v 1.3 2000/08/18 04:05:45 coskrey Exp $ +#************************************************************ +# $Xorg: XKBgeom.h,v 1.3 2000/08/18 04:05:45 coskrey Exp $ +#************************************************************ +# +#Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc. +# +#Permission to use, copy, modify, and distribute this +#software and its documentation for any purpose and without +#fee is hereby granted, provided that the above copyright +#notice appear in all copies and that both that copyright +#notice and this permission notice appear in supporting +#documentation, and that the name of Silicon Graphics not be +#used in advertising or publicity pertaining to distribution +#of the software without specific prior written permission. +#Silicon Graphics makes no representation about the suitability +#of this software for any purpose. It is provided "as is" +#without any express or implied warranty. +# +#SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +#SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +#AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON +#GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +#DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +#DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +#OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH +#THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +#******************************************************** +# $XFree86: xc/include/extensions/XKB.h,v 1.5 2002/11/20 04:49:01 dawes Exp $ +# $XFree86: xc/include/extensions/XKBgeom.h,v 3.9 2002/09/18 17:11:40 tsi Exp $ +# +# Pascal Convertion was made by Ido Kannner - kanerido@actcom.net.il +# +#Thanks: +# I want to thanks to oliebol for putting up with all of the problems that was found +# while translating this code. ;) +# +# I want to thanks #fpc channel in freenode irc, for helping me, and to put up with my +# weird questions ;) +# +# Thanks for mmc in #xlib on freenode irc And so for the channel itself for the helping me to +# understanding some of the problems I had converting this headers and pointing me to resources +# that helped translating this headers. +# +# Ido +# +#History: +# 2004/10/15 - Fixed a bug of accessing second based records by removing "paced record" and +# chnaged it to "reocrd" only. +# 2004/10/04 - 06 - Convertion from the c header of XKBgeom.h. +# 2004/10/03 - Removed the XKBstr_UNIT compiler decleration. Afther the joined files, +# There is no need for it anymore. +# - There is a need to define (for now) XKBgeom (compiler define) in order +# to use the code of it. At this moment, I did not yet converted it to Pascal. +# +# 2004/09/17 - 10/04 - Convertion from the c header of XKBstr. +# +# 2004/10/03 - Joined xkbstr.pas into xkb.pas because of the circular calls problems. +# - Added the history of xkbstr.pas above this addition. +# +# 2004/09/17 - Fixed a wrong convertion number of XkbPerKeyBitArraySize, instead +# of float, it's now converted into integer (as it should have been). +# +# 2004/09/15 - 16 - Convertion from the c header of XKB.h. +# + +import + X, Xlib + +include "x11pragma.nim" + +proc XkbcharToInt*(v: int8): int16 +proc XkbIntTo2chars*(i: int16, h, L: var int8) +proc Xkb2charsToInt*(h, L: int8): int16 + # + # Common data structures and access macros + # +type + PWord* = ptr array[0..64_000, int16] + PByte* = ptr byte + PXkbStatePtr* = ptr TXkbStateRec + TXkbStateRec*{.final.} = object + group*: int8 + locked_group*: int8 + base_group*: int16 + latched_group*: int16 + mods*: int8 + base_mods*: int8 + latched_mods*: int8 + locked_mods*: int8 + compat_state*: int8 + grab_mods*: int8 + compat_grab_mods*: int8 + lookup_mods*: int8 + compat_lookup_mods*: int8 + ptr_buttons*: int16 + + +proc XkbModLocks*(s: PXkbStatePtr): int8 +proc XkbStateMods*(s: PXkbStatePtr): int16 +proc XkbGroupLock*(s: PXkbStatePtr): int8 +proc XkbStateGroup*(s: PXkbStatePtr): int16 +proc XkbStateFieldFromRec*(s: PXkbStatePtr): int +proc XkbGrabStateFromRec*(s: PXkbStatePtr): int +type + PXkbModsPtr* = ptr TXkbModsRec + TXkbModsRec*{.final.} = object + mask*: int8 # effective mods + real_mods*: int8 + vmods*: int16 + + +type + PXkbKTMapEntryPtr* = ptr TXkbKTMapEntryRec + TXkbKTMapEntryRec*{.final.} = object + active*: bool + level*: int8 + mods*: TXkbModsRec + + +type + PXkbKeyTypePtr* = ptr TXkbKeyTypeRec + TXkbKeyTypeRec*{.final.} = object + mods*: TXkbModsRec + num_levels*: int8 + map_count*: int8 + map*: PXkbKTMapEntryPtr + preserve*: PXkbModsPtr + name*: TAtom + level_names*: TAtom + + +proc XkbNumGroups*(g: int16): int16 +proc XkbOutOfRangeGroupInfo*(g: int16): int16 +proc XkbOutOfRangeGroupAction*(g: int16): int16 +proc XkbOutOfRangeGroupNumber*(g: int16): int16 +proc XkbSetGroupInfo*(g, w, n: int16): int16 +proc XkbSetNumGroups*(g, n: int16): int16 + # + # Structures and access macros used primarily by the server + # +type + PXkbBehavior* = ptr TXkbBehavior + TXkbBehavior*{.final.} = object + theType*: int8 + data*: int8 + + +type + PXkbModAction* = ptr TXkbModAction + TXkbModAction*{.final.} = object + theType*: int8 + flags*: int8 + mask*: int8 + real_mods*: int8 + vmods1*: int8 + vmods2*: int8 + + +proc XkbModActionVMods*(a: PXkbModAction): int16 +proc XkbSetModActionVMods*(a: PXkbModAction, v: int8) +type + PXkbGroupAction* = ptr TXkbGroupAction + TXkbGroupAction*{.final.} = object + theType*: int8 + flags*: int8 + group_XXX*: int8 + + +proc XkbSAGroup*(a: PXkbGroupAction): int8 +proc XkbSASetGroupProc*(a: PXkbGroupAction, g: int8) +type + PXkbISOAction* = ptr TXkbISOAction + TXkbISOAction*{.final.} = object + theType*: int8 + flags*: int8 + mask*: int8 + real_mods*: int8 + group_XXX*: int8 + affect*: int8 + vmods1*: int8 + vmods2*: int8 + + +type + PXkbPtrAction* = ptr TXkbPtrAction + TXkbPtrAction*{.final.} = object + theType*: int8 + flags*: int8 + high_XXX*: int8 + low_XXX*: int8 + high_YYY*: int8 + low_YYY*: int8 + + +proc XkbPtrActionX*(a: PXkbPtrAction): int16 +proc XkbPtrActionY*(a: PXkbPtrAction): int16 +proc XkbSetPtrActionX*(a: PXkbPtrAction, x: int8) +proc XkbSetPtrActionY*(a: PXkbPtrAction, y: int8) +type + PXkbPtrBtnAction* = ptr TXkbPtrBtnAction + TXkbPtrBtnAction*{.final.} = object + theType*: int8 + flags*: int8 + count*: int8 + button*: int8 + + +type + PXkbPtrDfltAction* = ptr TXkbPtrDfltAction + TXkbPtrDfltAction*{.final.} = object + theType*: int8 + flags*: int8 + affect*: int8 + valueXXX*: int8 + + +proc XkbSAPtrDfltValue*(a: PXkbPtrDfltAction): int8 +proc XkbSASetPtrDfltValue*(a: PXkbPtrDfltAction, c: pointer) +type + PXkbSwitchScreenAction* = ptr TXkbSwitchScreenAction + TXkbSwitchScreenAction*{.final.} = object + theType*: int8 + flags*: int8 + screenXXX*: int8 + + +proc XkbSAScreen*(a: PXkbSwitchScreenAction): int8 +proc XkbSASetScreen*(a: PXkbSwitchScreenAction, s: pointer) +type + PXkbCtrlsAction* = ptr TXkbCtrlsAction + TXkbCtrlsAction*{.final.} = object + theType*: int8 + flags*: int8 + ctrls3*: int8 + ctrls2*: int8 + ctrls1*: int8 + ctrls0*: int8 + + +proc XkbActionSetCtrls*(a: PXkbCtrlsAction, c: int8) +proc XkbActionCtrls*(a: PXkbCtrlsAction): int16 +type + PXkbMessageAction* = ptr TXkbMessageAction + TXkbMessageAction*{.final.} = object + theType*: int8 + flags*: int8 + message*: array[0..5, char] + + +type + PXkbRedirectKeyAction* = ptr TXkbRedirectKeyAction + TXkbRedirectKeyAction*{.final.} = object + theType*: int8 + new_key*: int8 + mods_mask*: int8 + mods*: int8 + vmods_mask0*: int8 + vmods_mask1*: int8 + vmods0*: int8 + vmods1*: int8 + + +proc XkbSARedirectVMods*(a: PXkbRedirectKeyAction): int16 +proc XkbSARedirectSetVMods*(a: PXkbRedirectKeyAction, m: int8) +proc XkbSARedirectVModsMask*(a: PXkbRedirectKeyAction): int16 +proc XkbSARedirectSetVModsMask*(a: PXkbRedirectKeyAction, m: int8) +type + PXkbDeviceBtnAction* = ptr TXkbDeviceBtnAction + TXkbDeviceBtnAction*{.final.} = object + theType*: int8 + flags*: int8 + count*: int8 + button*: int8 + device*: int8 + + +type + PXkbDeviceValuatorAction* = ptr TXkbDeviceValuatorAction + TXkbDeviceValuatorAction*{.final.} = object # + # Macros to classify key actions + # + theType*: int8 + device*: int8 + v1_what*: int8 + v1_ndx*: int8 + v1_value*: int8 + v2_what*: int8 + v2_ndx*: int8 + v2_value*: int8 + + +const + XkbAnyActionDataSize* = 7 + +type + PXkbAnyAction* = ptr TXkbAnyAction + TXkbAnyAction*{.final.} = object + theType*: int8 + data*: array[0..XkbAnyActionDataSize - 1, int8] + + +proc XkbIsModAction*(a: PXkbAnyAction): bool +proc XkbIsGroupAction*(a: PXkbAnyAction): bool +proc XkbIsPtrAction*(a: PXkbAnyAction): bool +type + PXkbAction* = ptr TXkbAction + TXkbAction*{.final.} = object # + # XKB request codes, used in: + # - xkbReqType field of all requests + # - requestMinor field of some events + # + any*: TXkbAnyAction + mods*: TXkbModAction + group*: TXkbGroupAction + iso*: TXkbISOAction + thePtr*: TXkbPtrAction + btn*: TXkbPtrBtnAction + dflt*: TXkbPtrDfltAction + screen*: TXkbSwitchScreenAction + ctrls*: TXkbCtrlsAction + msg*: TXkbMessageAction + redirect*: TXkbRedirectKeyAction + devbtn*: TXkbDeviceBtnAction + devval*: TXkbDeviceValuatorAction + theType*: int8 + + +const + X_kbUseExtension* = 0 + X_kbSelectEvents* = 1 + X_kbBell* = 3 + X_kbGetState* = 4 + X_kbLatchLockState* = 5 + X_kbGetControls* = 6 + X_kbSetControls* = 7 + X_kbGetMap* = 8 + X_kbSetMap* = 9 + X_kbGetCompatMap* = 10 + X_kbSetCompatMap* = 11 + X_kbGetIndicatorState* = 12 + X_kbGetIndicatorMap* = 13 + X_kbSetIndicatorMap* = 14 + X_kbGetNamedIndicator* = 15 + X_kbSetNamedIndicator* = 16 + X_kbGetNames* = 17 + X_kbSetNames* = 18 + X_kbGetGeometry* = 19 + X_kbSetGeometry* = 20 + X_kbPerClientFlags* = 21 + X_kbListComponents* = 22 + X_kbGetKbdByName* = 23 + X_kbGetDeviceInfo* = 24 + X_kbSetDeviceInfo* = 25 + X_kbSetDebuggingFlags* = 101 # + # In the X sense, XKB reports only one event. + # The type field of all XKB events is XkbEventCode + # + +const + XkbEventCode* = 0 + XkbNumberEvents* = XkbEventCode + 1 # + # XKB has a minor event code so it can use one X event code for + # multiple purposes. + # - reported in the xkbType field of all XKB events. + # - XkbSelectEventDetails: Indicates the event for which event details + # are being changed + # + +const + XkbNewKeyboardNotify* = 0 + XkbMapNotify* = 1 + XkbStateNotify* = 2 + XkbControlsNotify* = 3 + XkbIndicatorStateNotify* = 4 + XkbIndicatorMapNotify* = 5 + XkbNamesNotify* = 6 + XkbCompatMapNotify* = 7 + XkbBellNotify* = 8 + XkbActionMessage* = 9 + XkbAccessXNotify* = 10 + XkbExtensionDeviceNotify* = 11 # + # Event Mask: + # - XkbSelectEvents: Specifies event interest. + # + +const + XkbNewKeyboardNotifyMask* = int(1) shl 0 + XkbMapNotifyMask* = int(1) shl 1 + XkbStateNotifyMask* = int(1) shl 2 + XkbControlsNotifyMask* = int(1) shl 3 + XkbIndicatorStateNotifyMask* = int(1) shl 4 + XkbIndicatorMapNotifyMask* = int(1) shl 5 + XkbNamesNotifyMask* = int(1) shl 6 + XkbCompatMapNotifyMask* = int(1) shl 7 + XkbBellNotifyMask* = int(1) shl 8 + XkbActionMessageMask* = int(1) shl 9 + XkbAccessXNotifyMask* = int(1) shl 10 + XkbExtensionDeviceNotifyMask* = int(1) shl 11 + XkbAllEventsMask* = 0x00000FFF # + # NewKeyboardNotify event details: + # + +const + XkbNKN_KeycodesMask* = int(1) shl 0 + XkbNKN_GeometryMask* = int(1) shl 1 + XkbNKN_DeviceIDMask* = int(1) shl 2 + XkbAllNewKeyboardEventsMask* = 0x00000007 # + # AccessXNotify event types: + # - The 'what' field of AccessXNotify events reports the + # reason that the event was generated. + # + +const + XkbAXN_SKPress* = 0 + XkbAXN_SKAccept* = 1 + XkbAXN_SKReject* = 2 + XkbAXN_SKRelease* = 3 + XkbAXN_BKAccept* = 4 + XkbAXN_BKReject* = 5 + XkbAXN_AXKWarning* = 6 # + # AccessXNotify details: + # - Used as an event detail mask to limit the conditions under which + # AccessXNotify events are reported + # + +const + XkbAXN_SKPressMask* = int(1) shl 0 + XkbAXN_SKAcceptMask* = int(1) shl 1 + XkbAXN_SKRejectMask* = int(1) shl 2 + XkbAXN_SKReleaseMask* = int(1) shl 3 + XkbAXN_BKAcceptMask* = int(1) shl 4 + XkbAXN_BKRejectMask* = int(1) shl 5 + XkbAXN_AXKWarningMask* = int(1) shl 6 + XkbAllAccessXEventsMask* = 0x0000000F # + # State detail mask: + # - The 'changed' field of StateNotify events reports which of + # the keyboard state components have changed. + # - Used as an event detail mask to limit the conditions under + # which StateNotify events are reported. + # + +const + XkbModifierStateMask* = int(1) shl 0 + XkbModifierBaseMask* = int(1) shl 1 + XkbModifierLatchMask* = int(1) shl 2 + XkbModifierLockMask* = int(1) shl 3 + XkbGroupStateMask* = int(1) shl 4 + XkbGroupBaseMask* = int(1) shl 5 + XkbGroupLatchMask* = int(1) shl 6 + XkbGroupLockMask* = int(1) shl 7 + XkbCompatStateMask* = int(1) shl 8 + XkbGrabModsMask* = int(1) shl 9 + XkbCompatGrabModsMask* = int(1) shl 10 + XkbLookupModsMask* = int(1) shl 11 + XkbCompatLookupModsMask* = int(1) shl 12 + XkbPointerButtonMask* = int(1) shl 13 + XkbAllStateComponentsMask* = 0x00003FFF # + # Controls detail masks: + # The controls specified in XkbAllControlsMask: + # - The 'changed' field of ControlsNotify events reports which of + # the keyboard controls have changed. + # - The 'changeControls' field of the SetControls request specifies + # the controls for which values are to be changed. + # - Used as an event detail mask to limit the conditions under + # which ControlsNotify events are reported. + # + # The controls specified in the XkbAllBooleanCtrlsMask: + # - The 'enabledControls' field of ControlsNotify events reports the + # current status of the boolean controls. + # - The 'enabledControlsChanges' field of ControlsNotify events reports + # any boolean controls that have been turned on or off. + # - The 'affectEnabledControls' and 'enabledControls' fields of the + # kbSetControls request change the set of enabled controls. + # - The 'accessXTimeoutMask' and 'accessXTimeoutValues' fields of + # an XkbControlsRec specify the controls to be changed if the keyboard + # times out and the values to which they should be changed. + # - The 'autoCtrls' and 'autoCtrlsValues' fields of the PerClientFlags + # request specifies the specify the controls to be reset when the + # client exits and the values to which they should be reset. + # - The 'ctrls' field of an indicator map specifies the controls + # that drive the indicator. + # - Specifies the boolean controls affected by the SetControls and + # LockControls key actions. + # + +const + XkbRepeatKeysMask* = int(1) shl 0 + XkbSlowKeysMask* = int(1) shl 1 + XkbBounceKeysMask* = int(1) shl 2 + XkbStickyKeysMask* = int(1) shl 3 + XkbMouseKeysMask* = int(1) shl 4 + XkbMouseKeysAccelMask* = int(1) shl 5 + XkbAccessXKeysMask* = int(1) shl 6 + XkbAccessXTimeoutMask* = int(1) shl 7 + XkbAccessXFeedbackMask* = int(1) shl 8 + XkbAudibleBellMask* = int(1) shl 9 + XkbOverlay1Mask* = int(1) shl 10 + XkbOverlay2Mask* = int(1) shl 11 + XkbIgnoreGroupLockMask* = int(1) shl 12 + XkbGroupsWrapMask* = int(1) shl 27 + XkbInternalModsMask* = int(1) shl 28 + XkbIgnoreLockModsMask* = int(1) shl 29 + XkbPerKeyRepeatMask* = int(1) shl 30 + XkbControlsEnabledMask* = int(1) shl 31 + XkbAccessXOptionsMask* = XkbStickyKeysMask or XkbAccessXFeedbackMask + XkbAllBooleanCtrlsMask* = 0x00001FFF + XkbAllControlsMask* = 0xF8001FFF # + # Compatibility Map Compontents: + # - Specifies the components to be allocated in XkbAllocCompatMap. + # + +const + XkbSymInterpMask* = 1 shl 0 + XkbGroupCompatMask* = 1 shl 1 + XkbAllCompatMask* = 0x00000003 # + # Assorted constants and limits. + # + +const + XkbAllIndicatorsMask* = 0xFFFFFFFF # + # Map components masks: + # Those in AllMapComponentsMask: + # - Specifies the individual fields to be loaded or changed for the + # GetMap and SetMap requests. + # Those in ClientInfoMask: + # - Specifies the components to be allocated by XkbAllocClientMap. + # Those in ServerInfoMask: + # - Specifies the components to be allocated by XkbAllocServerMap. + # + +const + XkbKeyTypesMask* = 1 shl 0 + XkbKeySymsMask* = 1 shl 1 + XkbModifierMapMask* = 1 shl 2 + XkbExplicitComponentsMask* = 1 shl 3 + XkbKeyActionsMask* = 1 shl 4 + XkbKeyBehaviorsMask* = 1 shl 5 + XkbVirtualModsMask* = 1 shl 6 + XkbVirtualModMapMask* = 1 shl 7 + XkbAllClientInfoMask* = XkbKeyTypesMask or XkbKeySymsMask or + XkbModifierMapMask + XkbAllServerInfoMask* = XkbExplicitComponentsMask or XkbKeyActionsMask or + XkbKeyBehaviorsMask or XkbVirtualModsMask or XkbVirtualModMapMask + XkbAllMapComponentsMask* = XkbAllClientInfoMask or XkbAllServerInfoMask # + # Names component mask: + # - Specifies the names to be loaded or changed for the GetNames and + # SetNames requests. + # - Specifies the names that have changed in a NamesNotify event. + # - Specifies the names components to be allocated by XkbAllocNames. + # + +const + XkbKeycodesNameMask* = 1 shl 0 + XkbGeometryNameMask* = 1 shl 1 + XkbSymbolsNameMask* = 1 shl 2 + XkbPhysSymbolsNameMask* = 1 shl 3 + XkbTypesNameMask* = 1 shl 4 + XkbCompatNameMask* = 1 shl 5 + XkbKeyTypeNamesMask* = 1 shl 6 + XkbKTLevelNamesMask* = 1 shl 7 + XkbIndicatorNamesMask* = 1 shl 8 + XkbKeyNamesMask* = 1 shl 9 + XkbKeyAliasesMask* = 1 shl 10 + XkbVirtualModNamesMask* = 1 shl 11 + XkbGroupNamesMask* = 1 shl 12 + XkbRGNamesMask* = 1 shl 13 + XkbComponentNamesMask* = 0x0000003F + XkbAllNamesMask* = 0x00003FFF # + # Miscellaneous event details: + # - event detail masks for assorted events that don't reall + # have any details. + # + +const + XkbAllStateEventsMask* = XkbAllStateComponentsMask + XkbAllMapEventsMask* = XkbAllMapComponentsMask + XkbAllControlEventsMask* = XkbAllControlsMask + XkbAllIndicatorEventsMask* = XkbAllIndicatorsMask + XkbAllNameEventsMask* = XkbAllNamesMask + XkbAllCompatMapEventsMask* = XkbAllCompatMask + XkbAllBellEventsMask* = int(1) shl 0 + XkbAllActionMessagesMask* = int(1) shl 0 # + # XKB reports one error: BadKeyboard + # A further reason for the error is encoded into to most significant + # byte of the resourceID for the error: + # XkbErr_BadDevice - the device in question was not found + # XkbErr_BadClass - the device was found but it doesn't belong to + # the appropriate class. + # XkbErr_BadId - the device was found and belongs to the right + # class, but not feedback with a matching id was + # found. + # The low byte of the resourceID for this error contains the device + # id, class specifier or feedback id that failed. + # + +const + XkbKeyboard* = 0 + XkbNumberErrors* = 1 + XkbErr_BadDevice* = 0x000000FF + XkbErr_BadClass* = 0x000000FE + XkbErr_BadId* = 0x000000FD # + # Keyboard Components Mask: + # - Specifies the components that follow a GetKeyboardByNameReply + # + +const + XkbClientMapMask* = int(1) shl 0 + XkbServerMapMask* = int(1) shl 1 + XkbCompatMapMask* = int(1) shl 2 + XkbIndicatorMapMask* = int(1) shl 3 + XkbNamesMask* = int(1) shl 4 + XkbGeometryMask* = int(1) shl 5 + XkbControlsMask* = int(1) shl 6 + XkbAllComponentsMask* = 0x0000007F # + # AccessX Options Mask + # - The 'accessXOptions' field of an XkbControlsRec specifies the + # AccessX options that are currently in effect. + # - The 'accessXTimeoutOptionsMask' and 'accessXTimeoutOptionsValues' + # fields of an XkbControlsRec specify the Access X options to be + # changed if the keyboard times out and the values to which they + # should be changed. + # + +const + XkbAX_SKPressFBMask* = int(1) shl 0 + XkbAX_SKAcceptFBMask* = int(1) shl 1 + XkbAX_FeatureFBMask* = int(1) shl 2 + XkbAX_SlowWarnFBMask* = int(1) shl 3 + XkbAX_IndicatorFBMask* = int(1) shl 4 + XkbAX_StickyKeysFBMask* = int(1) shl 5 + XkbAX_TwoKeysMask* = int(1) shl 6 + XkbAX_LatchToLockMask* = int(1) shl 7 + XkbAX_SKReleaseFBMask* = int(1) shl 8 + XkbAX_SKRejectFBMask* = int(1) shl 9 + XkbAX_BKRejectFBMask* = int(1) shl 10 + XkbAX_DumbBellFBMask* = int(1) shl 11 + XkbAX_FBOptionsMask* = 0x00000F3F + XkbAX_SKOptionsMask* = 0x000000C0 + XkbAX_AllOptionsMask* = 0x00000FFF # + # XkbUseCoreKbd is used to specify the core keyboard without having + # to look up its X input extension identifier. + # XkbUseCorePtr is used to specify the core pointer without having + # to look up its X input extension identifier. + # XkbDfltXIClass is used to specify "don't care" any place that the + # XKB protocol is looking for an X Input Extension + # device class. + # XkbDfltXIId is used to specify "don't care" any place that the + # XKB protocol is looking for an X Input Extension + # feedback identifier. + # XkbAllXIClasses is used to get information about all device indicators, + # whether they're part of the indicator feedback class + # or the keyboard feedback class. + # XkbAllXIIds is used to get information about all device indicator + # feedbacks without having to list them. + # XkbXINone is used to indicate that no class or id has been specified. + # XkbLegalXILedClass(c) True if 'c' specifies a legal class with LEDs + # XkbLegalXIBellClass(c) True if 'c' specifies a legal class with bells + # XkbExplicitXIDevice(d) True if 'd' explicitly specifies a device + # XkbExplicitXIClass(c) True if 'c' explicitly specifies a device class + # XkbExplicitXIId(c) True if 'i' explicitly specifies a device id + # XkbSingleXIClass(c) True if 'c' specifies exactly one device class, + # including the default. + # XkbSingleXIId(i) True if 'i' specifies exactly one device + # identifier, including the default. + # + +const + XkbUseCoreKbd* = 0x00000100 + XkbUseCorePtr* = 0x00000200 + XkbDfltXIClass* = 0x00000300 + XkbDfltXIId* = 0x00000400 + XkbAllXIClasses* = 0x00000500 + XkbAllXIIds* = 0x00000600 + XkbXINone* = 0x0000FF00 + +proc XkbLegalXILedClass*(c: int): bool +proc XkbLegalXIBellClass*(c: int): bool +proc XkbExplicitXIDevice*(c: int): bool +proc XkbExplicitXIClass*(c: int): bool +proc XkbExplicitXIId*(c: int): bool +proc XkbSingleXIClass*(c: int): bool +proc XkbSingleXIId*(c: int): bool +const + XkbNoModifier* = 0x000000FF + XkbNoShiftLevel* = 0x000000FF + XkbNoShape* = 0x000000FF + XkbNoIndicator* = 0x000000FF + XkbNoModifierMask* = 0 + XkbAllModifiersMask* = 0x000000FF + XkbAllVirtualModsMask* = 0x0000FFFF + XkbNumKbdGroups* = 4 + XkbMaxKbdGroup* = XkbNumKbdGroups - 1 + XkbMaxMouseKeysBtn* = 4 # + # Group Index and Mask: + # - Indices into the kt_index array of a key type. + # - Mask specifies types to be changed for XkbChangeTypesOfKey + # + +const + XkbGroup1Index* = 0 + XkbGroup2Index* = 1 + XkbGroup3Index* = 2 + XkbGroup4Index* = 3 + XkbAnyGroup* = 254 + XkbAllGroups* = 255 + XkbGroup1Mask* = 1 shl 0 + XkbGroup2Mask* = 1 shl 1 + XkbGroup3Mask* = 1 shl 2 + XkbGroup4Mask* = 1 shl 3 + XkbAnyGroupMask* = 1 shl 7 + XkbAllGroupsMask* = 0x0000000F # + # BuildCoreState: Given a keyboard group and a modifier state, + # construct the value to be reported an event. + # GroupForCoreState: Given the state reported in an event, + # determine the keyboard group. + # IsLegalGroup: Returns TRUE if 'g' is a valid group index. + # + +proc XkbBuildCoreState*(m, g: int): int +proc XkbGroupForCoreState*(s: int): int +proc XkbIsLegalGroup*(g: int): bool + # + # GroupsWrap values: + # - The 'groupsWrap' field of an XkbControlsRec specifies the + # treatment of out of range groups. + # - Bits 6 and 7 of the group info field of a key symbol map + # specify the interpretation of out of range groups for the + # corresponding key. + # +const + XkbWrapIntoRange* = 0x00000000 + XkbClampIntoRange* = 0x00000040 + XkbRedirectIntoRange* = 0x00000080 # + # Action flags: Reported in the 'flags' field of most key actions. + # Interpretation depends on the type of the action; not all actions + # accept all flags. + # + # Option Used for Actions + # ------ ---------------- + # ClearLocks SetMods, LatchMods, SetGroup, LatchGroup + # LatchToLock SetMods, LatchMods, SetGroup, LatchGroup + # LockNoLock LockMods, ISOLock, LockPtrBtn, LockDeviceBtn + # LockNoUnlock LockMods, ISOLock, LockPtrBtn, LockDeviceBtn + # UseModMapMods SetMods, LatchMods, LockMods, ISOLock + # GroupAbsolute SetGroup, LatchGroup, LockGroup, ISOLock + # UseDfltButton PtrBtn, LockPtrBtn + # NoAcceleration MovePtr + # MoveAbsoluteX MovePtr + # MoveAbsoluteY MovePtr + # ISODfltIsGroup ISOLock + # ISONoAffectMods ISOLock + # ISONoAffectGroup ISOLock + # ISONoAffectPtr ISOLock + # ISONoAffectCtrls ISOLock + # MessageOnPress ActionMessage + # MessageOnRelease ActionMessage + # MessageGenKeyEvent ActionMessage + # AffectDfltBtn SetPtrDflt + # DfltBtnAbsolute SetPtrDflt + # SwitchApplication SwitchScreen + # SwitchAbsolute SwitchScreen + # + +const + XkbSA_ClearLocks* = int(1) shl 0 + XkbSA_LatchToLock* = int(1) shl 1 + XkbSA_LockNoLock* = int(1) shl 0 + XkbSA_LockNoUnlock* = int(1) shl 1 + XkbSA_UseModMapMods* = int(1) shl 2 + XkbSA_GroupAbsolute* = int(1) shl 2 + XkbSA_UseDfltButton* = 0 + XkbSA_NoAcceleration* = int(1) shl 0 + XkbSA_MoveAbsoluteX* = int(1) shl 1 + XkbSA_MoveAbsoluteY* = int(1) shl 2 + XkbSA_ISODfltIsGroup* = int(1) shl 7 + XkbSA_ISONoAffectMods* = int(1) shl 6 + XkbSA_ISONoAffectGroup* = int(1) shl 5 + XkbSA_ISONoAffectPtr* = int(1) shl 4 + XkbSA_ISONoAffectCtrls* = int(1) shl 3 + XkbSA_ISOAffectMask* = 0x00000078 + XkbSA_MessageOnPress* = int(1) shl 0 + XkbSA_MessageOnRelease* = int(1) shl 1 + XkbSA_MessageGenKeyEvent* = int(1) shl 2 + XkbSA_AffectDfltBtn* = 1 + XkbSA_DfltBtnAbsolute* = int(1) shl 2 + XkbSA_SwitchApplication* = int(1) shl 0 + XkbSA_SwitchAbsolute* = int(1) shl 2 # + # The following values apply to the SA_DeviceValuator + # action only. Valuator operations specify the action + # to be taken. Values specified in the action are + # multiplied by 2^scale before they are applied. + # + +const + XkbSA_IgnoreVal* = 0x00000000 + XkbSA_SetValMin* = 0x00000010 + XkbSA_SetValCenter* = 0x00000020 + XkbSA_SetValMax* = 0x00000030 + XkbSA_SetValRelative* = 0x00000040 + XkbSA_SetValAbsolute* = 0x00000050 + XkbSA_ValOpMask* = 0x00000070 + XkbSA_ValScaleMask* = 0x00000007 + +proc XkbSA_ValOp*(a: int): int +proc XkbSA_ValScale*(a: int): int + # + # Action types: specifies the type of a key action. Reported in the + # type field of all key actions. + # +const + XkbSA_NoAction* = 0x00000000 + XkbSA_SetMods* = 0x00000001 + XkbSA_LatchMods* = 0x00000002 + XkbSA_LockMods* = 0x00000003 + XkbSA_SetGroup* = 0x00000004 + XkbSA_LatchGroup* = 0x00000005 + XkbSA_LockGroup* = 0x00000006 + XkbSA_MovePtr* = 0x00000007 + XkbSA_PtrBtn* = 0x00000008 + XkbSA_LockPtrBtn* = 0x00000009 + XkbSA_SetPtrDflt* = 0x0000000A + XkbSA_ISOLock* = 0x0000000B + XkbSA_Terminate* = 0x0000000C + XkbSA_SwitchScreen* = 0x0000000D + XkbSA_SetControls* = 0x0000000E + XkbSA_LockControls* = 0x0000000F + XkbSA_ActionMessage* = 0x00000010 + XkbSA_RedirectKey* = 0x00000011 + XkbSA_DeviceBtn* = 0x00000012 + XkbSA_LockDeviceBtn* = 0x00000013 + XkbSA_DeviceValuator* = 0x00000014 + XkbSA_LastAction* = XkbSA_DeviceValuator + XkbSA_NumActions* = XkbSA_LastAction + 1 + +const + XkbSA_XFree86Private* = 0x00000086 +# +# Specifies the key actions that clear latched groups or modifiers. +# + +const ##define XkbSA_BreakLatch \ + # ((1<>13)&0x3) + result = (s shr 13) and 0x00000003 + +proc XkbIsLegalGroup(g: int): bool = + ##define XkbIsLegalGroup(g) (((g)>=0)&&((g)= 0) and (g < XkbNumKbdGroups) + +proc XkbSA_ValOp(a: int): int = + ##define XkbSA_ValOp(a) ((a)&XkbSA_ValOpMask) + result = a and XkbSA_ValOpMask + +proc XkbSA_ValScale(a: int): int = + ##define XkbSA_ValScale(a) ((a)&XkbSA_ValScaleMask) + result = a and XkbSA_ValScaleMask + +proc XkbIsModAction(a: PXkbAnyAction): bool = + ##define XkbIsModAction(a) (((a)->type>=Xkb_SASetMods)&&((a)->type<=XkbSA_LockMods)) + result = (ze(a.theType) >= XkbSA_SetMods) and (ze(a.theType) <= XkbSA_LockMods) + +proc XkbIsGroupAction(a: PXkbAnyAction): bool = + ##define XkbIsGroupAction(a) (((a)->type>=XkbSA_SetGroup)&&((a)->type<=XkbSA_LockGroup)) + result = (ze(a.theType) >= XkbSA_SetGroup) or (ze(a.theType) <= XkbSA_LockGroup) + +proc XkbIsPtrAction(a: PXkbAnyAction): bool = + ##define XkbIsPtrAction(a) (((a)->type>=XkbSA_MovePtr)&&((a)->type<=XkbSA_SetPtrDflt)) + result = (ze(a.theType) >= XkbSA_MovePtr) and + (ze(a.theType) <= XkbSA_SetPtrDflt) + +proc XkbIsLegalKeycode(k: int): bool = + ##define XkbIsLegalKeycode(k) (((k)>=XkbMinLegalKeyCode)&&((k)<=XkbMaxLegalKeyCode)) + result = (k >= XkbMinLegalKeyCode) and (k <= XkbMaxLegalKeyCode) + +proc XkbShiftLevel(n: int8): int8 = + ##define XkbShiftLevel(n) ((n)-1) + result = n - 1'i8 + +proc XkbShiftLevelMask(n: int8): int8 = + ##define XkbShiftLevelMask(n) (1<<((n)-1)) + result = 1'i8 shl (n - 1'i8) + +proc XkbcharToInt(v: int8): int16 = + ##define XkbcharToInt(v) ((v)&0x80?(int)((v)|(~0xff)):(int)((v)&0x7f)) + if ((v and 0x80'i8) != 0'i8): result = v or (not 0xFF'i16) + else: result = int16(v and 0x7F'i8) + +proc XkbIntTo2chars(i: int16, h, L: var int8) = + ##define XkbIntTo2chars(i,h,l) (((h)=((i>>8)&0xff)),((l)=((i)&0xff))) + h = toU8((i shr 8'i16) and 0x00FF'i16) + L = toU8(i and 0xFF'i16) + +proc Xkb2charsToInt(h, L: int8): int16 = + when defined(cpu64): + ##define Xkb2charsToInt(h,l) ((h)&0x80?(int)(((h)<<8)|(l)|(~0xffff)): (int)(((h)<<8)|(l)&0x7fff)) + if (h and 0x80'i8) != 0'i8: + result = toU16((ze(h) shl 8) or ze(L) or not 0x0000FFFF) + else: + result = toU16((ze(h) shl 8) or ze(L) and 0x00007FFF) + else: + ##define Xkb2charsToInt(h,l) ((short)(((h)<<8)|(l))) + result = toU16(ze(h) shl 8 or ze(L)) + +proc XkbModLocks(s: PXkbStatePtr): int8 = + ##define XkbModLocks(s) ((s)->locked_mods) + result = s.locked_mods + +proc XkbStateMods(s: PXkbStatePtr): int16 = + ##define XkbStateMods(s) ((s)->base_mods|(s)->latched_mods|XkbModLocks(s)) + result = s.base_mods or s.latched_mods or XkbModLocks(s) + +proc XkbGroupLock(s: PXkbStatePtr): int8 = + ##define XkbGroupLock(s) ((s)->locked_group) + result = s.locked_group + +proc XkbStateGroup(s: PXkbStatePtr): int16 = + ##define XkbStateGroup(s) ((s)->base_group+(s)->latched_group+XkbGroupLock(s)) + result = s.base_group + (s.latched_group) + XkbGroupLock(s) + +proc XkbStateFieldFromRec(s: PXkbStatePtr): int = + ##define XkbStateFieldFromRec(s) XkbBuildCoreState((s)->lookup_mods,(s)->group) + result = XkbBuildCoreState(s.lookup_mods, s.group) + +proc XkbGrabStateFromRec(s: PXkbStatePtr): int = + ##define XkbGrabStateFromRec(s) XkbBuildCoreState((s)->grab_mods,(s)->group) + result = XkbBuildCoreState(s.grab_mods, s.group) + +proc XkbNumGroups(g: int16): int16 = + ##define XkbNumGroups(g) ((g)&0x0f) + result = g and 0x0000000F'i16 + +proc XkbOutOfRangeGroupInfo(g: int16): int16 = + ##define XkbOutOfRangeGroupInfo(g) ((g)&0xf0) + result = g and 0x000000F0'i16 + +proc XkbOutOfRangeGroupAction(g: int16): int16 = + ##define XkbOutOfRangeGroupAction(g) ((g)&0xc0) + result = g and 0x000000C0'i16 + +proc XkbOutOfRangeGroupNumber(g: int16): int16 = + ##define XkbOutOfRangeGroupNumber(g) (((g)&0x30)>>4) + result = (g and 0x00000030'i16) shr 4'i16 + +proc XkbSetGroupInfo(g, w, n: int16): int16 = + ##define XkbSetGroupInfo(g,w,n) (((w)&0xc0)|(((n)&3)<<4)|((g)&0x0f)) + result = (w and 0x000000C0'i16) or + ((n and 3'i16) shl 4'i16) or (g and 0x0000000F'i16) + +proc XkbSetNumGroups(g, n: int16): int16 = + ##define XkbSetNumGroups(g,n) (((g)&0xf0)|((n)&0x0f)) + result = (g and 0x000000F0'i16) or (n and 0x0000000F'i16) + +proc XkbModActionVMods(a: PXkbModAction): int16 = + ##define XkbModActionVMods(a) ((short)(((a)->vmods1<<8)|((a)->vmods2))) + result = toU16((ze(a.vmods1) shl 8) or ze(a.vmods2)) + +proc XkbSetModActionVMods(a: PXkbModAction, v: int8) = + ##define XkbSetModActionVMods(a,v) (((a)->vmods1=(((v)>>8)&0xff)),(a)->vmods2=((v)&0xff)) + a.vmods1 = toU8((ze(v) shr 8) and 0x000000FF) + a.vmods2 = toU8(ze(v) and 0x000000FF) + +proc XkbSAGroup(a: PXkbGroupAction): int8 = + ##define XkbSAGroup(a) (XkbcharToInt((a)->group_XXX)) + result = int8(XkbcharToInt(a.group_XXX)) + +proc XkbSASetGroupProc(a: PXkbGroupAction, g: int8) = + ##define XkbSASetGroup(a,g) ((a)->group_XXX=(g)) + a.group_XXX = g + +proc XkbPtrActionX(a: PXkbPtrAction): int16 = + ##define XkbPtrActionX(a) (Xkb2charsToInt((a)->high_XXX,(a)->low_XXX)) + result = int16(Xkb2charsToInt(a.high_XXX, a.low_XXX)) + +proc XkbPtrActionY(a: PXkbPtrAction): int16 = + ##define XkbPtrActionY(a) (Xkb2charsToInt((a)->high_YYY,(a)->low_YYY)) + result = int16(Xkb2charsToInt(a.high_YYY, a.low_YYY)) + +proc XkbSetPtrActionX(a: PXkbPtrAction, x: int8) = + ##define XkbSetPtrActionX(a,x) (XkbIntTo2chars(x,(a)->high_XXX,(a)->low_XXX)) + XkbIntTo2chars(x, a.high_XXX, a.low_XXX) + +proc XkbSetPtrActionY(a: PXkbPtrAction, y: int8) = + ##define XkbSetPtrActionY(a,y) (XkbIntTo2chars(y,(a)->high_YYY,(a)->low_YYY)) + XkbIntTo2chars(y, a.high_YYY, a.low_YYY) + +proc XkbSAPtrDfltValue(a: PXkbPtrDfltAction): int8 = + ##define XkbSAPtrDfltValue(a) (XkbcharToInt((a)->valueXXX)) + result = int8(XkbcharToInt(a.valueXXX)) + +proc XkbSASetPtrDfltValue(a: PXkbPtrDfltAction, c: pointer) = + ##define XkbSASetPtrDfltValue(a,c) ((a)->valueXXX= ((c)&0xff)) + a.valueXXX = toU8(cast[int](c)) + +proc XkbSAScreen(a: PXkbSwitchScreenAction): int8 = + ##define XkbSAScreen(a) (XkbcharToInt((a)->screenXXX)) + result = toU8(XkbcharToInt(a.screenXXX)) + +proc XkbSASetScreen(a: PXkbSwitchScreenAction, s: pointer) = + ##define XkbSASetScreen(a,s) ((a)->screenXXX= ((s)&0xff)) + a.screenXXX = toU8(cast[int](s)) + +proc XkbActionSetCtrls(a: PXkbCtrlsAction, c: int8) = + ##define XkbActionSetCtrls(a,c) (((a)->ctrls3=(((c)>>24)&0xff)),((a)->ctrls2=(((c)>>16)&0xff)), + # ((a)->ctrls1=(((c)>>8)&0xff)),((a)->ctrls0=((c)&0xff))) + a.ctrls3 = toU8((ze(c) shr 24) and 0x000000FF) + a.ctrls2 = toU8((ze(c) shr 16) and 0x000000FF) + a.ctrls1 = toU8((ze(c) shr 8) and 0x000000FF) + a.ctrls0 = toU8(ze(c) and 0x000000FF) + +proc XkbActionCtrls(a: PXkbCtrlsAction): int16 = + ##define XkbActionCtrls(a) ((((unsigned int)(a)->ctrls3)<<24)|(((unsigned int)(a)->ctrls2)<<16)| + # (((unsigned int)(a)->ctrls1)<<8)|((unsigned int)((a)->ctrls0))) + result = toU16((ze(a.ctrls3) shl 24) or (ze(a.ctrls2) shl 16) or + (ze(a.ctrls1) shl 8) or ze(a.ctrls0)) + +proc XkbSARedirectVMods(a: PXkbRedirectKeyAction): int16 = + ##define XkbSARedirectVMods(a) ((((unsigned int)(a)->vmods1)<<8)|((unsigned int)(a)->vmods0)) + result = toU16((ze(a.vmods1) shl 8) or ze(a.vmods0)) + +proc XkbSARedirectSetVMods(a: PXkbRedirectKeyAction, m: int8) = + ##define XkbSARedirectSetVMods(a,m) (((a)->vmods_mask1=(((m)>>8)&0xff)),((a)->vmods_mask0=((m)&0xff))) + a.vmods_mask1 = toU8((ze(m) shr 8) and 0x000000FF) + a.vmods_mask0 = toU8(ze(m) or 0x000000FF) + +proc XkbSARedirectVModsMask(a: PXkbRedirectKeyAction): int16 = + ##define XkbSARedirectVModsMask(a) ((((unsigned int)(a)->vmods_mask1)<<8)| + # ((unsigned int)(a)->vmods_mask0)) + result = toU16((ze(a.vmods_mask1) shl 8) or ze(a.vmods_mask0)) + +proc XkbSARedirectSetVModsMask(a: PXkbRedirectKeyAction, m: int8) = + ##define XkbSARedirectSetVModsMask(a,m) (((a)->vmods_mask1=(((m)>>8)&0xff)),((a)->vmods_mask0=((m)&0xff))) + a.vmods_mask1 = toU8(ze(m) shr 8 and 0x000000FF) + a.vmods_mask0 = toU8(ze(m) and 0x000000FF) + +proc XkbAX_AnyFeedback(c: PXkbControlsPtr): int16 = + ##define XkbAX_AnyFeedback(c) ((c)->enabled_ctrls&XkbAccessXFeedbackMask) + result = toU16(ze(c.enabled_ctrls) and XkbAccessXFeedbackMask) + +proc XkbAX_NeedOption(c: PXkbControlsPtr, w: int16): int16 = + ##define XkbAX_NeedOption(c,w) ((c)->ax_options&(w)) + result = toU16(ze(c.ax_options) and ze(w)) + +proc XkbAX_NeedFeedback(c: PXkbControlsPtr, w: int16): bool = + ##define XkbAX_NeedFeedback(c,w) (XkbAX_AnyFeedback(c)&&XkbAX_NeedOption(c,w)) + result = (XkbAX_AnyFeedback(c) > 0'i16) and (XkbAX_NeedOption(c, w) > 0'i16) + +proc XkbSMKeyActionsPtr(m: PXkbServerMapPtr, k: int16): PXkbAction = + ##define XkbSMKeyActionsPtr(m,k) (&(m)->acts[(m)->key_acts[k]]) + result = addr(m.acts[ze(m.key_acts[ze(k)])]) + +proc XkbCMKeyGroupInfo(m: PXkbClientMapPtr, k: int16): int8 = + ##define XkbCMKeyGroupInfo(m,k) ((m)->key_sym_map[k].group_info) + result = m.key_sym_map[ze(k)].group_info + +proc XkbCMKeyNumGroups(m: PXkbClientMapPtr, k: int16): int8 = + ##define XkbCMKeyNumGroups(m,k) (XkbNumGroups((m)->key_sym_map[k].group_info)) + result = toU8(XkbNumGroups(m.key_sym_map[ze(k)].group_info)) + +proc XkbCMKeyGroupWidth(m: PXkbClientMapPtr, k: int16, g: int8): int8 = + ##define XkbCMKeyGroupWidth(m,k,g) (XkbCMKeyType(m,k,g)->num_levels) + result = XkbCMKeyType(m, k, g).num_levels + +proc XkbCMKeyGroupsWidth(m: PXkbClientMapPtr, k: int16): int8 = + ##define XkbCMKeyGroupsWidth(m,k) ((m)->key_sym_map[k].width) + result = m.key_sym_map[ze(k)].width + +proc XkbCMKeyTypeIndex(m: PXkbClientMapPtr, k: int16, g: int8): int8 = + ##define XkbCMKeyTypeIndex(m,k,g) ((m)->key_sym_map[k].kt_index[g&0x3]) + result = m.key_sym_map[ze(k)].kt_index[ze(g) and 0x00000003] + +proc XkbCMKeyType(m: PXkbClientMapPtr, k: int16, g: int8): PXkbKeyTypePtr = + ##define XkbCMKeyType(m,k,g) (&(m)->types[XkbCMKeyTypeIndex(m,k,g)]) + result = addr(m.types[ze(XkbCMKeyTypeIndex(m, k, g))]) + +proc XkbCMKeyNumSyms(m: PXkbClientMapPtr, k: int16): int16 = + ##define XkbCMKeyNumSyms(m,k) (XkbCMKeyGroupsWidth(m,k)*XkbCMKeyNumGroups(m,k)) + result = toU16(ze(XkbCMKeyGroupsWidth(m, k)) or ze(XkbCMKeyNumGroups(m, k))) + +proc XkbCMKeySymsOffset(m: PXkbClientMapPtr, k: int16): int8 = + ##define XkbCMKeySymsOffset(m,k) ((m)->key_sym_map[k].offset) + result = m.key_sym_map[ze(k)].offset + +proc XkbCMKeySymsPtr*(m: PXkbClientMapPtr, k: int16): PKeySym = + ##define XkbCMKeySymsPtr(m,k) (&(m)->syms[XkbCMKeySymsOffset(m,k)]) + result = addr(m.syms[ze(XkbCMKeySymsOffset(m, k))]) + +proc XkbIM_IsAuto(i: PXkbIndicatorMapPtr): bool = + ##define XkbIM_IsAuto(i) ((((i)->flags&XkbIM_NoAutomatic)==0)&&(((i)->which_groups&&(i)->groups)|| + # ((i)->which_mods&&(i)->mods.mask)|| ((i)->ctrls))) + result = ((ze(i.flags) and XkbIM_NoAutomatic) == 0) and + (((i.which_groups > 0'i8) and (i.groups > 0'i8)) or + ((i.which_mods > 0'i8) and (i.mods.mask > 0'i8)) or (i.ctrls > 0'i8)) + +proc XkbIM_InUse(i: PXkbIndicatorMapPtr): bool = + ##define XkbIM_InUse(i) (((i)->flags)||((i)->which_groups)||((i)->which_mods)||((i)->ctrls)) + result = (i.flags > 0'i8) or (i.which_groups > 0'i8) or (i.which_mods > 0'i8) or + (i.ctrls > 0'i8) + +proc XkbKeyKeyTypeIndex(d: PXkbDescPtr, k: int16, g: int8): int8 = + ##define XkbKeyKeyTypeIndex(d,k,g) (XkbCMKeyTypeIndex((d)->map,k,g)) + result = XkbCMKeyTypeIndex(d.map, k, g) + +proc XkbKeyKeyType(d: PXkbDescPtr, k: int16, g: int8): PXkbKeyTypePtr = + ##define XkbKeyKeyType(d,k,g) (XkbCMKeyType((d)->map,k,g)) + result = XkbCMKeyType(d.map, k, g) + +proc XkbKeyGroupWidth(d: PXkbDescPtr, k: int16, g: int8): int8 = + ##define XkbKeyGroupWidth(d,k,g) (XkbCMKeyGroupWidth((d)->map,k,g)) + result = XkbCMKeyGroupWidth(d.map, k, g) + +proc XkbKeyGroupsWidth(d: PXkbDescPtr, k: int16): int8 = + ##define XkbKeyGroupsWidth(d,k) (XkbCMKeyGroupsWidth((d)->map,k)) + result = XkbCMKeyGroupsWidth(d.map, k) + +proc XkbKeyGroupInfo(d: PXkbDescPtr, k: int16): int8 = + ##define XkbKeyGroupInfo(d,k) (XkbCMKeyGroupInfo((d)->map,(k))) + result = XkbCMKeyGroupInfo(d.map, k) + +proc XkbKeyNumGroups(d: PXkbDescPtr, k: int16): int8 = + ##define XkbKeyNumGroups(d,k) (XkbCMKeyNumGroups((d)->map,(k))) + result = XkbCMKeyNumGroups(d.map, k) + +proc XkbKeyNumSyms(d: PXkbDescPtr, k: int16): int16 = + ##define XkbKeyNumSyms(d,k) (XkbCMKeyNumSyms((d)->map,(k))) + result = XkbCMKeyNumSyms(d.map, k) + +proc XkbKeySymsPtr*(d: PXkbDescPtr, k: int16): PKeySym = + ##define XkbKeySymsPtr(d,k) (XkbCMKeySymsPtr((d)->map,(k))) + result = XkbCMKeySymsPtr(d.map, k) + +proc XkbKeySym(d: PXkbDescPtr, k: int16, n: int16): TKeySym = + ##define XkbKeySym(d,k,n) (XkbKeySymsPtr(d,k)[n]) + result = cast[ptr array[0..0xffff, TKeySym]](XkbKeySymsPtr(d, k))[ze(n)] # XXX: this seems strange! + +proc XkbKeySymEntry(d: PXkbDescPtr, k: int16, sl: int16, g: int8): TKeySym = + ##define XkbKeySymEntry(d,k,sl,g) (XkbKeySym(d,k,((XkbKeyGroupsWidth(d,k)*(g))+(sl)))) + result = XkbKeySym(d, k, toU16(ze(XkbKeyGroupsWidth(d, k)) * ze(g) + ze(sl))) + +proc XkbKeyAction(d: PXkbDescPtr, k: int16, n: int16): PXkbAction = + ##define XkbKeyAction(d,k,n) (XkbKeyHasActions(d,k)?&XkbKeyActionsPtr(d,k)[n]:NULL) + #if (XkbKeyHasActions(d, k)): + # result = XkbKeyActionsPtr(d, k)[ze(n)] #Buggy !!! + assert(false) + result = nil + +proc XkbKeyActionEntry(d: PXkbDescPtr, k: int16, sl: int16, g: int8): int8 = + ##define XkbKeyActionEntry(d,k,sl,g) (XkbKeyHasActions(d,k) ? + # XkbKeyAction(d, k, ((XkbKeyGroupsWidth(d, k) * (g))+(sl))):NULL) + if XkbKeyHasActions(d, k): + result = XkbKeyGroupsWidth(d, k) *% g +% toU8(sl) + else: + result = 0'i8 + +proc XkbKeyHasActions(d: PXkbDescPtr, k: int16): bool = + ##define XkbKeyHasActions(d,k) ((d)->server->key_acts[k]!=0) + result = d.server.key_acts[ze(k)] != 0'i16 + +proc XkbKeyNumActions(d: PXkbDescPtr, k: int16): int16 = + ##define XkbKeyNumActions(d,k) (XkbKeyHasActions(d,k)?XkbKeyNumSyms(d,k):1) + if (XkbKeyHasActions(d, k)): result = XkbKeyNumSyms(d, k) + else: result = 1'i16 + +proc XkbKeyActionsPtr(d: PXkbDescPtr, k: int16): PXkbAction = + ##define XkbKeyActionsPtr(d,k) (XkbSMKeyActionsPtr((d)->server,k)) + result = XkbSMKeyActionsPtr(d.server, k) + +proc XkbKeycodeInRange(d: PXkbDescPtr, k: int16): bool = + ##define XkbKeycodeInRange(d,k) (((k)>=(d)->min_key_code)&& ((k)<=(d)->max_key_code)) + result = (char(toU8(k)) >= d.min_key_code) and (char(toU8(k)) <= d.max_key_code) + +proc XkbNumKeys(d: PXkbDescPtr): int8 = + ##define XkbNumKeys(d) ((d)->max_key_code-(d)->min_key_code+1) + result = toU8(ord(d.max_key_code) - ord(d.min_key_code) + 1) + +proc XkbXI_DevHasBtnActs(d: PXkbDeviceInfoPtr): bool = + ##define XkbXI_DevHasBtnActs(d) (((d)->num_btns>0)&&((d)->btn_acts!=NULL)) + result = (d.num_btns > 0'i16) and (not (d.btn_acts == nil)) + +proc XkbXI_LegalDevBtn(d: PXkbDeviceInfoPtr, b: int16): bool = + ##define XkbXI_LegalDevBtn(d,b) (XkbXI_DevHasBtnActs(d)&&((b)<(d)->num_btns)) + result = XkbXI_DevHasBtnActs(d) and (b <% d.num_btns) + +proc XkbXI_DevHasLeds(d: PXkbDeviceInfoPtr): bool = + ##define XkbXI_DevHasLeds(d) (((d)->num_leds>0)&&((d)->leds!=NULL)) + result = (d.num_leds > 0'i16) and (not (d.leds == nil)) + +proc XkbBoundsWidth(b: PXkbBoundsPtr): int16 = + ##define XkbBoundsWidth(b) (((b)->x2)-((b)->x1)) + result = (b.x2) - b.x1 + +proc XkbBoundsHeight(b: PXkbBoundsPtr): int16 = + ##define XkbBoundsHeight(b) (((b)->y2)-((b)->y1)) + result = (b.y2) - b.y1 + +proc XkbOutlineIndex(s: PXkbShapePtr, o: PXkbOutlinePtr): int32 = + ##define XkbOutlineIndex(s,o) ((int)((o)-&(s)->outlines[0])) + result = int32((cast[TAddress](o) - cast[TAddress](addr(s.outlines[0]))) div sizeof(PXkbOutlinePtr)) + +proc XkbShapeDoodadColor(g: PXkbGeometryPtr, d: PXkbShapeDoodadPtr): PXkbColorPtr = + ##define XkbShapeDoodadColor(g,d) (&(g)->colors[(d)->color_ndx]) + result = addr((g.colors[ze(d.color_ndx)])) + +proc XkbShapeDoodadShape(g: PXkbGeometryPtr, d: PXkbShapeDoodadPtr): PXkbShapePtr = + ##define XkbShapeDoodadShape(g,d) (&(g)->shapes[(d)->shape_ndx]) + result = addr(g.shapes[ze(d.shape_ndx)]) + +proc XkbSetShapeDoodadColor(g: PXkbGeometryPtr, d: PXkbShapeDoodadPtr, + c: PXkbColorPtr) = + ##define XkbSetShapeDoodadColor(g,d,c) ((d)->color_ndx= (c)-&(g)->colors[0]) + d.color_ndx = toU16((cast[TAddress](c) - cast[TAddress](addr(g.colors[0]))) div sizeof(TXkbColorRec)) + +proc XkbSetShapeDoodadShape(g: PXkbGeometryPtr, d: PXkbShapeDoodadPtr, + s: PXkbShapePtr) = + ##define XkbSetShapeDoodadShape(g,d,s) ((d)->shape_ndx= (s)-&(g)->shapes[0]) + d.shape_ndx = toU16((cast[TAddress](s) - cast[TAddress](addr(g.shapes[0]))) div sizeof(TXkbShapeRec)) + +proc XkbTextDoodadColor(g: PXkbGeometryPtr, d: PXkbTextDoodadPtr): PXkbColorPtr = + ##define XkbTextDoodadColor(g,d) (&(g)->colors[(d)->color_ndx]) + result = addr(g.colors[ze(d.color_ndx)]) + +proc XkbSetTextDoodadColor(g: PXkbGeometryPtr, d: PXkbTextDoodadPtr, + c: PXkbColorPtr) = + ##define XkbSetTextDoodadColor(g,d,c) ((d)->color_ndx= (c)-&(g)->colors[0]) + d.color_ndx = toU16((cast[TAddress](c) - cast[TAddress](addr(g.colors[0]))) div sizeof(TXkbColorRec)) + +proc XkbIndicatorDoodadShape(g: PXkbGeometryPtr, d: PXkbIndicatorDoodadPtr): PXkbShapeDoodadPtr = + ##define XkbIndicatorDoodadShape(g,d) (&(g)->shapes[(d)->shape_ndx]) + result = cast[PXkbShapeDoodadPtr](addr(g.shapes[ze(d.shape_ndx)])) + +proc XkbIndicatorDoodadOnColor(g: PXkbGeometryPtr, d: PXkbIndicatorDoodadPtr): PXkbColorPtr = + ##define XkbIndicatorDoodadOnColor(g,d) (&(g)->colors[(d)->on_color_ndx]) + result = addr(g.colors[ze(d.on_color_ndx)]) + +proc XkbIndicatorDoodadOffColor(g: PXkbGeometryPtr, d: PXkbIndicatorDoodadPtr): PXkbColorPtr = + ##define XkbIndicatorDoodadOffColor(g,d) (&(g)->colors[(d)->off_color_ndx]) + result = addr(g.colors[ze(d.off_color_ndx)]) + +proc XkbSetIndicatorDoodadOnColor(g: PXkbGeometryPtr, d: PXkbIndicatorDoodadPtr, + c: PXkbColorPtr) = + ##define XkbSetIndicatorDoodadOnColor(g,d,c) ((d)->on_color_ndx= (c)-&(g)->colors[0]) + d.on_color_ndx = toU16((cast[TAddress](c) - cast[TAddress](addr(g.colors[0]))) div sizeof(TXkbColorRec)) + +proc XkbSetIndicatorDoodadOffColor(g: PXkbGeometryPtr, + d: PXkbIndicatorDoodadPtr, c: PXkbColorPtr) = + ##define XkbSetIndicatorDoodadOffColor(g,d,c) ((d)->off_color_ndx= (c)-&(g)->colors[0]) + d.off_color_ndx = toU16((cast[TAddress](c) - cast[TAddress](addr(g.colors[0]))) div sizeof(TxkbColorRec)) + +proc XkbSetIndicatorDoodadShape(g: PXkbGeometryPtr, d: PXkbIndicatorDoodadPtr, + s: PXkbShapeDoodadPtr) = + ##define XkbSetIndicatorDoodadShape(g,d,s) ((d)->shape_ndx= (s)-&(g)->shapes[0]) + d.shape_ndx = toU16((cast[TAddress](s) - (cast[TAddress](addr(g.shapes[0])))) div sizeof(TXkbShapeRec)) + +proc XkbLogoDoodadColor(g: PXkbGeometryPtr, d: PXkbLogoDoodadPtr): PXkbColorPtr = + ##define XkbLogoDoodadColor(g,d) (&(g)->colors[(d)->color_ndx]) + result = addr(g.colors[ze(d.color_ndx)]) + +proc XkbLogoDoodadShape(g: PXkbGeometryPtr, d: PXkbLogoDoodadPtr): PXkbShapeDoodadPtr = + ##define XkbLogoDoodadShape(g,d) (&(g)->shapes[(d)->shape_ndx]) + result = cast[PXkbShapeDoodadPtr](addr(g.shapes[ze(d.shape_ndx)])) + +proc XkbSetLogoDoodadColor(g: PXkbGeometryPtr, d: PXkbLogoDoodadPtr, + c: PXkbColorPtr) = + ##define XkbSetLogoDoodadColor(g,d,c) ((d)->color_ndx= (c)-&(g)->colors[0]) + d.color_ndx = toU16((cast[TAddress](c) - cast[TAddress](addr(g.colors[0]))) div sizeof(TXkbColorRec)) + +proc XkbSetLogoDoodadShape(g: PXkbGeometryPtr, d: PXkbLogoDoodadPtr, + s: PXkbShapeDoodadPtr) = + ##define XkbSetLogoDoodadShape(g,d,s) ((d)->shape_ndx= (s)-&(g)->shapes[0]) + d.shape_ndx = toU16((cast[TAddress](s) - cast[TAddress](addr(g.shapes[0]))) div sizeof(TXkbShapeRec)) + +proc XkbKeyShape(g: PXkbGeometryPtr, k: PXkbKeyPtr): PXkbShapeDoodadPtr = + ##define XkbKeyShape(g,k) (&(g)->shapes[(k)->shape_ndx]) + result = cast[PXkbShapeDoodadPtr](addr(g.shapes[ze(k.shape_ndx)])) + +proc XkbKeyColor(g: PXkbGeometryPtr, k: PXkbKeyPtr): PXkbColorPtr = + ##define XkbKeyColor(g,k) (&(g)->colors[(k)->color_ndx]) + result = addr(g.colors[ze(k.color_ndx)]) + +proc XkbSetKeyShape(g: PXkbGeometryPtr, k: PXkbKeyPtr, s: PXkbShapeDoodadPtr) = + ##define XkbSetKeyShape(g,k,s) ((k)->shape_ndx= (s)-&(g)->shapes[0]) + k.shape_ndx = toU8((cast[TAddress](s) - cast[TAddress](addr(g.shapes[0]))) div sizeof(TXkbShapeRec)) + +proc XkbSetKeyColor(g: PXkbGeometryPtr, k: PXkbKeyPtr, c: PXkbColorPtr) = + ##define XkbSetKeyColor(g,k,c) ((k)->color_ndx= (c)-&(g)->colors[0]) + k.color_ndx = toU8((cast[TAddress](c) - cast[TAddress](addr(g.colors[0]))) div sizeof(TxkbColorRec)) + +proc XkbGeomColorIndex(g: PXkbGeometryPtr, c: PXkbColorPtr): int32 = + ##define XkbGeomColorIndex(g,c) ((int)((c)-&(g)->colors[0])) + result = toU16((cast[TAddress](c) - (cast[TAddress](addr(g.colors[0])))) div sizeof(TxkbColorRec)) diff --git a/tests/deps/x11-1.0/xkblib.nim b/tests/deps/x11-1.0/xkblib.nim new file mode 100644 index 000000000..2366b0971 --- /dev/null +++ b/tests/deps/x11-1.0/xkblib.nim @@ -0,0 +1,661 @@ +# $Xorg: XKBlib.h,v 1.6 2000/08/17 19:45:03 cpqbld Exp $ +#************************************************************ +#Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc. +# +#Permission to use, copy, modify, and distribute this +#software and its documentation for any purpose and without +#fee is hereby granted, provided that the above copyright +#notice appear in all copies and that both that copyright +#notice and this permission notice appear in supporting +#documentation, and that the name of Silicon Graphics not be +#used in advertising or publicity pertaining to distribution +#of the software without specific prior written permission. +#Silicon Graphics makes no representation about the suitability +#of this software for any purpose. It is provided "as is" +#without any express or implied warranty. +# +#SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +#SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +#AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON +#GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +#DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING `from` LOSS OF USE, +#DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +#OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH +#THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +#********************************************************/ +# $XFree86: xc/lib/X11/XKBlib.h,v 3.3 2001/08/01 00:44:38 tsi Exp $ +# +# Pascal Convertion was made by Ido Kannner - kanerido@actcom.net.il +# +#Thanks: +# I want to thanks to oliebol for putting up with all of the problems that was found +# while translating this code. ;) +# +# I want to thanks #fpc channel in freenode irc, for helping me, and to put up with my +# weird questions ;) +# +# Thanks for mmc in #xlib on freenode irc And so for the channel itself for the helping me to +# understanding some of the problems I had converting this headers and pointing me to resources +# that helped translating this headers. +# +# Ido +# +#History: +# 2004/10/15 - Fixed a bug of accessing second based records by removing "paced record" and +# chnaged it to "reocrd" only. +# 2004/10/10 - Added to TXkbGetAtomNameFunc and TXkbInternAtomFunc the cdecl call. +# 2004/10/06 - 09 - Convertion `from` the c header of XKBlib.h +# +# + +import + X, Xlib, XKB + + +include "x11pragma.nim" + + +type + PXkbAnyEvent* = ptr TXkbAnyEvent + TXkbAnyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds; + xkb_type*: int16 # XKB event minor code + device*: int16 # device ID + + +type + PXkbNewKeyboardNotifyEvent* = ptr TXkbNewKeyboardNotifyEvent + TXkbNewKeyboardNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbNewKeyboardNotify + device*: int16 # device ID + old_device*: int16 # device ID of previous keyboard + min_key_code*: int16 # minimum key code + max_key_code*: int16 # maximum key code + old_min_key_code*: int16 # min key code of previous kbd + old_max_key_code*: int16 # max key code of previous kbd + changed*: int16 # changed aspects of the keyboard + req_major*: int8 # major and minor opcode of req + req_minor*: int8 # that caused change, if applicable + + +type + PXkbMapNotifyEvent* = ptr TXkbMapNotifyEvent + TXkbMapNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbMapNotify + device*: int16 # device ID + changed*: int16 # fields which have been changed + flags*: int16 # reserved + first_type*: int16 # first changed key type + num_types*: int16 # number of changed key types + min_key_code*: TKeyCode + max_key_code*: TKeyCode + first_key_sym*: TKeyCode + first_key_act*: TKeyCode + first_key_behavior*: TKeyCode + first_key_explicit*: TKeyCode + first_modmap_key*: TKeyCode + first_vmodmap_key*: TKeyCode + num_key_syms*: int16 + num_key_acts*: int16 + num_key_behaviors*: int16 + num_key_explicit*: int16 + num_modmap_keys*: int16 + num_vmodmap_keys*: int16 + vmods*: int16 # mask of changed virtual mods + + +type + PXkbStateNotifyEvent* = ptr TXkbStateNotifyEvent + TXkbStateNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbStateNotify + device*: int16 # device ID + changed*: int16 # mask of changed state components + group*: int16 # keyboard group + base_group*: int16 # base keyboard group + latched_group*: int16 # latched keyboard group + locked_group*: int16 # locked keyboard group + mods*: int16 # modifier state + base_mods*: int16 # base modifier state + latched_mods*: int16 # latched modifiers + locked_mods*: int16 # locked modifiers + compat_state*: int16 # compatibility state + grab_mods*: int8 # mods used for grabs + compat_grab_mods*: int8 # grab mods for non-XKB clients + lookup_mods*: int8 # mods sent to clients + compat_lookup_mods*: int8 # mods sent to non-XKB clients + ptr_buttons*: int16 # pointer button state + keycode*: TKeyCode # keycode that caused the change + event_type*: int8 # KeyPress or KeyRelease + req_major*: int8 # Major opcode of request + req_minor*: int8 # Minor opcode of request + + +type + PXkbControlsNotifyEvent* = ptr TXkbControlsNotifyEvent + TXkbControlsNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbControlsNotify + device*: int16 # device ID + changed_ctrls*: int16 # controls with changed sub-values + enabled_ctrls*: int16 # controls currently enabled + enabled_ctrl_changes*: int16 # controls just {en,dis}abled + num_groups*: int16 # total groups on keyboard + keycode*: TKeyCode # key that caused change or 0 + event_type*: int8 # type of event that caused change + req_major*: int8 # if keycode==0, major and minor + req_minor*: int8 # opcode of req that caused change + + +type + PXkbIndicatorNotifyEvent* = ptr TXkbIndicatorNotifyEvent + TXkbIndicatorNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbIndicatorNotify + device*: int16 # device + changed*: int16 # indicators with new state or map + state*: int16 # current state of all indicators + + +type + PXkbNamesNotifyEvent* = ptr TXkbNamesNotifyEvent + TXkbNamesNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbNamesNotify + device*: int16 # device ID + changed*: int32 # names that have changed + first_type*: int16 # first key type with new name + num_types*: int16 # number of key types with new names + first_lvl*: int16 # first key type new new level names + num_lvls*: int16 # # of key types w/new level names + num_aliases*: int16 # total number of key aliases + num_radio_groups*: int16 # total number of radio groups + changed_vmods*: int16 # virtual modifiers with new names + changed_groups*: int16 # groups with new names + changed_indicators*: int16 # indicators with new names + first_key*: int16 # first key with new name + num_keys*: int16 # number of keys with new names + + +type + PXkbCompatMapNotifyEvent* = ptr TXkbCompatMapNotifyEvent + TXkbCompatMapNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbCompatMapNotify + device*: int16 # device ID + changed_groups*: int16 # groups with new compat maps + first_si*: int16 # first new symbol interp + num_si*: int16 # number of new symbol interps + num_total_si*: int16 # total # of symbol interps + + +type + PXkbBellNotifyEvent* = ptr TXkbBellNotifyEvent + TXkbBellNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbBellNotify + device*: int16 # device ID + percent*: int16 # requested volume as a % of maximum + pitch*: int16 # requested pitch in Hz + duration*: int16 # requested duration in useconds + bell_class*: int16 # (input extension) feedback class + bell_id*: int16 # (input extension) ID of feedback + name*: TAtom # "name" of requested bell + window*: TWindow # window associated with event + event_only*: bool # "event only" requested + + +type + PXkbActionMessageEvent* = ptr TXkbActionMessageEvent + TXkbActionMessageEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbActionMessage + device*: int16 # device ID + keycode*: TKeyCode # key that generated the event + press*: bool # true if act caused by key press + key_event_follows*: bool # true if key event also generated + group*: int16 # effective group + mods*: int16 # effective mods + message*: array[0..XkbActionMessageLength, char] # message -- leave space for NUL + + +type + PXkbAccessXNotifyEvent* = ptr TXkbAccessXNotifyEvent + TXkbAccessXNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbAccessXNotify + device*: int16 # device ID + detail*: int16 # XkbAXN_* + keycode*: int16 # key of event + sk_delay*: int16 # current slow keys delay + debounce_delay*: int16 # current debounce delay + + +type + PXkbExtensionDeviceNotifyEvent* = ptr TXkbExtensionDeviceNotifyEvent + TXkbExtensionDeviceNotifyEvent*{.final.} = object + theType*: int16 # XkbAnyEvent + serial*: int32 # of last req processed by server + send_event*: bool # is this `from` a SendEvent request? + display*: PDisplay # Display the event was read `from` + time*: TTime # milliseconds + xkb_type*: int16 # XkbExtensionDeviceNotify + device*: int16 # device ID + reason*: int16 # reason for the event + supported*: int16 # mask of supported features + unsupported*: int16 # mask of unsupported features + # that some app tried to use + first_btn*: int16 # first button that changed + num_btns*: int16 # range of buttons changed + leds_defined*: int16 # indicators with names or maps + led_state*: int16 # current state of the indicators + led_class*: int16 # feedback class for led changes + led_id*: int16 # feedback id for led changes + + +type + PXkbEvent* = ptr TXkbEvent + TXkbEvent*{.final.} = object + theType*: int16 + any*: TXkbAnyEvent + new_kbd*: TXkbNewKeyboardNotifyEvent + map*: TXkbMapNotifyEvent + state*: TXkbStateNotifyEvent + ctrls*: TXkbControlsNotifyEvent + indicators*: TXkbIndicatorNotifyEvent + names*: TXkbNamesNotifyEvent + compat*: TXkbCompatMapNotifyEvent + bell*: TXkbBellNotifyEvent + message*: TXkbActionMessageEvent + accessx*: TXkbAccessXNotifyEvent + device*: TXkbExtensionDeviceNotifyEvent + core*: TXEvent + + +type + PXkbKbdDpyStatePtr* = ptr TXkbKbdDpyStateRec + TXkbKbdDpyStateRec*{.final.} = object # XkbOpenDisplay error codes + +const + XkbOD_Success* = 0 + XkbOD_BadLibraryVersion* = 1 + XkbOD_ConnectionRefused* = 2 + XkbOD_NonXkbServer* = 3 + XkbOD_BadServerVersion* = 4 # Values for XlibFlags + +const + XkbLC_ForceLatin1Lookup* = 1 shl 0 + XkbLC_ConsumeLookupMods* = 1 shl 1 + XkbLC_AlwaysConsumeShiftAndLock* = 1 shl 2 + XkbLC_IgnoreNewKeyboards* = 1 shl 3 + XkbLC_ControlFallback* = 1 shl 4 + XkbLC_ConsumeKeysOnComposeFail* = 1 shl 29 + XkbLC_ComposeLED* = 1 shl 30 + XkbLC_BeepOnComposeFail* = 1 shl 31 + XkbLC_AllComposeControls* = 0xC0000000 + XkbLC_AllControls* = 0xC000001F + +proc XkbIgnoreExtension*(ignore: bool): bool{.libx11c, + importc: "XkbIgnoreExtension".} +proc XkbOpenDisplay*(name: cstring, ev_rtrn, err_rtrn, major_rtrn, minor_rtrn, + reason: ptr int16): PDisplay{.libx11c, importc: "XkbOpenDisplay".} +proc XkbQueryExtension*(dpy: PDisplay, opcodeReturn, eventBaseReturn, + errorBaseReturn, majorRtrn, minorRtrn: ptr int16): bool{. + libx11c, importc: "XkbQueryExtension".} +proc XkbUseExtension*(dpy: PDisplay, major_rtrn, minor_rtrn: ptr int16): bool{. + libx11c, importc: "XkbUseExtension".} +proc XkbLibraryVersion*(libMajorRtrn, libMinorRtrn: ptr int16): bool{.libx11c, importc: "XkbLibraryVersion".} +proc XkbSetXlibControls*(dpy: PDisplay, affect, values: int16): int16{.libx11c, importc: "XkbSetXlibControls".} +proc XkbGetXlibControls*(dpy: PDisplay): int16{.libx11c, + importc: "XkbGetXlibControls".} +type + TXkbInternAtomFunc* = proc (dpy: PDisplay, name: cstring, only_if_exists: bool): TAtom{. + cdecl.} + +type + TXkbGetAtomNameFunc* = proc (dpy: PDisplay, atom: TAtom): cstring{.cdecl.} + +proc XkbSetAtomFuncs*(getAtom: TXkbInternAtomFunc, getName: TXkbGetAtomNameFunc){. + libx11c, importc: "XkbSetAtomFuncs".} +proc XkbKeycodeToKeysym*(dpy: PDisplay, kc: TKeyCode, group, level: int16): TKeySym{. + libx11c, importc: "XkbKeycodeToKeysym".} +proc XkbKeysymToModifiers*(dpy: PDisplay, ks: TKeySym): int16{.libx11c, importc: "XkbKeysymToModifiers".} +proc XkbLookupKeySym*(dpy: PDisplay, keycode: TKeyCode, + modifiers, modifiers_return: int16, keysym_return: PKeySym): bool{. + libx11c, importc: "XkbLookupKeySym".} +proc XkbLookupKeyBinding*(dpy: PDisplay, sym_rtrn: TKeySym, mods: int16, + buffer: cstring, nbytes: int16, extra_rtrn: ptr int16): int16{. + libx11c, importc: "XkbLookupKeyBinding".} +proc XkbTranslateKeyCode*(xkb: PXkbDescPtr, keycode: TKeyCode, + modifiers, modifiers_return: int16, + keysym_return: PKeySym): bool{.libx11c, + importc: "XkbTranslateKeyCode".} +proc XkbTranslateKeySym*(dpy: PDisplay, sym_return: TKeySym, modifiers: int16, + buffer: cstring, nbytes: int16, extra_rtrn: ptr int16): int16{. + libx11c, importc: "XkbTranslateKeySym".} +proc XkbSetAutoRepeatRate*(dpy: PDisplay, deviceSpec, delay, interval: int16): bool{. + libx11c, importc: "XkbSetAutoRepeatRate".} +proc XkbGetAutoRepeatRate*(dpy: PDisplay, deviceSpec: int16, + delayRtrn, intervalRtrn: PWord): bool{.libx11c, importc: "XkbGetAutoRepeatRate".} +proc XkbChangeEnabledControls*(dpy: PDisplay, deviceSpec, affect, values: int16): bool{. + libx11c, importc: "XkbChangeEnabledControls".} +proc XkbDeviceBell*(dpy: PDisplay, win: TWindow, + deviceSpec, bellClass, bellID, percent: int16, name: TAtom): bool{. + libx11c, importc: "XkbDeviceBell".} +proc XkbForceDeviceBell*(dpy: PDisplay, + deviceSpec, bellClass, bellID, percent: int16): bool{. + libx11c, importc: "XkbForceDeviceBell".} +proc XkbDeviceBellEvent*(dpy: PDisplay, win: TWindow, + deviceSpec, bellClass, bellID, percent: int16, + name: TAtom): bool{.libx11c, + importc: "XkbDeviceBellEvent".} +proc XkbBell*(dpy: PDisplay, win: TWindow, percent: int16, name: TAtom): bool{. + libx11c, importc: "XkbBell".} +proc XkbForceBell*(dpy: PDisplay, percent: int16): bool{.libx11c, + importc: "XkbForceBell".} +proc XkbBellEvent*(dpy: PDisplay, win: TWindow, percent: int16, name: TAtom): bool{. + libx11c, importc: "XkbBellEvent".} +proc XkbSelectEvents*(dpy: PDisplay, deviceID, affect, values: int16): bool{. + libx11c, importc: "XkbSelectEvents".} +proc XkbSelectEventDetails*(dpy: PDisplay, deviceID, eventType: int16, + affect, details: int32): bool{.libx11c, importc: "XkbSelectEventDetails".} +proc XkbNoteMapChanges*(old: PXkbMapChangesPtr, new: PXkbMapNotifyEvent, + wanted: int16){.libx11c, + importc: "XkbNoteMapChanges".} +proc XkbNoteNameChanges*(old: PXkbNameChangesPtr, new: PXkbNamesNotifyEvent, + wanted: int16){.libx11c, + importc: "XkbNoteNameChanges".} +proc XkbGetIndicatorState*(dpy: PDisplay, deviceSpec: int16, pStateRtrn: PWord): TStatus{. + libx11c, importc: "XkbGetIndicatorState".} +proc XkbGetDeviceIndicatorState*(dpy: PDisplay, + deviceSpec, ledClass, ledID: int16, + pStateRtrn: PWord): TStatus{.libx11c, importc: "XkbGetDeviceIndicatorState".} +proc XkbGetIndicatorMap*(dpy: PDisplay, which: int32, desc: PXkbDescPtr): TStatus{. + libx11c, importc: "XkbGetIndicatorMap".} +proc XkbSetIndicatorMap*(dpy: PDisplay, which: int32, desc: PXkbDescPtr): bool{. + libx11c, importc: "XkbSetIndicatorMap".} +proc XkbNoteIndicatorMapChanges*(o, n: PXkbIndicatorChangesPtr, w: int16) +proc XkbNoteIndicatorStateChanges*(o, n: PXkbIndicatorChangesPtr, w: int16) +proc XkbGetIndicatorMapChanges*(d: PDisplay, x: PXkbDescPtr, + c: PXkbIndicatorChangesPtr): TStatus +proc XkbChangeIndicatorMaps*(d: PDisplay, x: PXkbDescPtr, + c: PXkbIndicatorChangesPtr): bool +proc XkbGetNamedIndicator*(dpy: PDisplay, name: TAtom, pNdxRtrn: ptr int16, + pStateRtrn: ptr bool, pMapRtrn: PXkbIndicatorMapPtr, + pRealRtrn: ptr bool): bool{.libx11c, + importc: "XkbGetNamedIndicator".} +proc XkbGetNamedDeviceIndicator*(dpy: PDisplay, + deviceSpec, ledClass, ledID: int16, + name: TAtom, pNdxRtrn: ptr int16, + pStateRtrn: ptr bool, + pMapRtrn: PXkbIndicatorMapPtr, + pRealRtrn: ptr bool): bool{.libx11c, importc: "XkbGetNamedDeviceIndicator".} +proc XkbSetNamedIndicator*(dpy: PDisplay, name: TAtom, + changeState, state, createNewMap: bool, + pMap: PXkbIndicatorMapPtr): bool{.libx11c, importc: "XkbSetNamedIndicator".} +proc XkbSetNamedDeviceIndicator*(dpy: PDisplay, + deviceSpec, ledClass, ledID: int16, + name: TAtom, + changeState, state, createNewMap: bool, + pMap: PXkbIndicatorMapPtr): bool{.libx11c, importc: "XkbSetNamedDeviceIndicator".} +proc XkbLockModifiers*(dpy: PDisplay, deviceSpec, affect, values: int16): bool{. + libx11c, importc: "XkbLockModifiers".} +proc XkbLatchModifiers*(dpy: PDisplay, deviceSpec, affect, values: int16): bool{. + libx11c, importc: "XkbLatchModifiers".} +proc XkbLockGroup*(dpy: PDisplay, deviceSpec, group: int16): bool{.libx11c, importc: "XkbLockGroup".} +proc XkbLatchGroup*(dpy: PDisplay, deviceSpec, group: int16): bool{.libx11c, importc: "XkbLatchGroup".} +proc XkbSetServerInternalMods*(dpy: PDisplay, deviceSpec, affectReal, + realValues, affectVirtual, virtualValues: int16): bool{.libx11c, importc: "XkbSetServerInternalMods".} +proc XkbSetIgnoreLockMods*(dpy: PDisplay, deviceSpec, affectReal, realValues, + affectVirtual, virtualValues: int16): bool{.libx11c, + importc: "XkbSetIgnoreLockMods".} +proc XkbVirtualModsToReal*(dpy: PDisplay, virtual_mask: int16, mask_rtrn: PWord): bool{. + libx11c, importc: "XkbVirtualModsToReal".} +proc XkbComputeEffectiveMap*(xkb: PXkbDescPtr, theType: PXkbKeyTypePtr, + map_rtrn: PByte): bool{.libx11c, + importc: "XkbComputeEffectiveMap".} +proc XkbInitCanonicalKeyTypes*(xkb: PXkbDescPtr, which: int16, keypadVMod: int16): TStatus{. + libx11c, importc: "XkbInitCanonicalKeyTypes".} +proc XkbAllocKeyboard*(): PXkbDescPtr{.libx11c, + importc: "XkbAllocKeyboard".} +proc XkbFreeKeyboard*(xkb: PXkbDescPtr, which: int16, freeDesc: bool){.libx11c, importc: "XkbFreeKeyboard".} +proc XkbAllocClientMap*(xkb: PXkbDescPtr, which, nTypes: int16): TStatus{.libx11c, importc: "XkbAllocClientMap".} +proc XkbAllocServerMap*(xkb: PXkbDescPtr, which, nActions: int16): TStatus{. + libx11c, importc: "XkbAllocServerMap".} +proc XkbFreeClientMap*(xkb: PXkbDescPtr, what: int16, freeMap: bool){.libx11c, importc: "XkbFreeClientMap".} +proc XkbFreeServerMap*(xkb: PXkbDescPtr, what: int16, freeMap: bool){.libx11c, importc: "XkbFreeServerMap".} +proc XkbAddKeyType*(xkb: PXkbDescPtr, name: TAtom, map_count: int16, + want_preserve: bool, num_lvls: int16): PXkbKeyTypePtr{. + libx11c, importc: "XkbAddKeyType".} +proc XkbAllocIndicatorMaps*(xkb: PXkbDescPtr): TStatus{.libx11c, + importc: "XkbAllocIndicatorMaps".} +proc XkbFreeIndicatorMaps*(xkb: PXkbDescPtr){.libx11c, + importc: "XkbFreeIndicatorMaps".} +proc XkbGetMap*(dpy: PDisplay, which, deviceSpec: int16): PXkbDescPtr{.libx11c, importc: "XkbGetMap".} +proc XkbGetUpdatedMap*(dpy: PDisplay, which: int16, desc: PXkbDescPtr): TStatus{. + libx11c, importc: "XkbGetUpdatedMap".} +proc XkbGetMapChanges*(dpy: PDisplay, xkb: PXkbDescPtr, + changes: PXkbMapChangesPtr): TStatus{.libx11c, importc: "XkbGetMapChanges".} +proc XkbRefreshKeyboardMapping*(event: PXkbMapNotifyEvent): TStatus{.libx11c, importc: "XkbRefreshKeyboardMapping".} +proc XkbGetKeyTypes*(dpy: PDisplay, first, num: int16, xkb: PXkbDescPtr): TStatus{. + libx11c, importc: "XkbGetKeyTypes".} +proc XkbGetKeySyms*(dpy: PDisplay, first, num: int16, xkb: PXkbDescPtr): TStatus{. + libx11c, importc: "XkbGetKeySyms".} +proc XkbGetKeyActions*(dpy: PDisplay, first, num: int16, xkb: PXkbDescPtr): TStatus{. + libx11c, importc: "XkbGetKeyActions".} +proc XkbGetKeyBehaviors*(dpy: PDisplay, firstKey, nKeys: int16, + desc: PXkbDescPtr): TStatus{.libx11c, + importc: "XkbGetKeyBehaviors".} +proc XkbGetVirtualMods*(dpy: PDisplay, which: int16, desc: PXkbDescPtr): TStatus{. + libx11c, importc: "XkbGetVirtualMods".} +proc XkbGetKeyExplicitComponents*(dpy: PDisplay, firstKey, nKeys: int16, + desc: PXkbDescPtr): TStatus{.libx11c, importc: "XkbGetKeyExplicitComponents".} +proc XkbGetKeyModifierMap*(dpy: PDisplay, firstKey, nKeys: int16, + desc: PXkbDescPtr): TStatus{.libx11c, + importc: "XkbGetKeyModifierMap".} +proc XkbAllocControls*(xkb: PXkbDescPtr, which: int16): TStatus{.libx11c, importc: "XkbAllocControls".} +proc XkbFreeControls*(xkb: PXkbDescPtr, which: int16, freeMap: bool){.libx11c, importc: "XkbFreeControls".} +proc XkbGetControls*(dpy: PDisplay, which: int32, desc: PXkbDescPtr): TStatus{. + libx11c, importc: "XkbGetControls".} +proc XkbSetControls*(dpy: PDisplay, which: int32, desc: PXkbDescPtr): bool{. + libx11c, importc: "XkbSetControls".} +proc XkbNoteControlsChanges*(old: PXkbControlsChangesPtr, + new: PXkbControlsNotifyEvent, wanted: int16){. + libx11c, importc: "XkbNoteControlsChanges".} +proc XkbGetControlsChanges*(d: PDisplay, x: PXkbDescPtr, + c: PXkbControlsChangesPtr): TStatus +proc XkbChangeControls*(d: PDisplay, x: PXkbDescPtr, c: PXkbControlsChangesPtr): bool +proc XkbAllocCompatMap*(xkb: PXkbDescPtr, which, nInterpret: int16): TStatus{. + libx11c, importc: "XkbAllocCompatMap".} +proc XkbFreeCompatMap*(xkib: PXkbDescPtr, which: int16, freeMap: bool){.libx11c, importc: "XkbFreeCompatMap".} +proc XkbGetCompatMap*(dpy: PDisplay, which: int16, xkb: PXkbDescPtr): TStatus{. + libx11c, importc: "XkbGetCompatMap".} +proc XkbSetCompatMap*(dpy: PDisplay, which: int16, xkb: PXkbDescPtr, + updateActions: bool): bool{.libx11c, + importc: "XkbSetCompatMap".} +proc XkbAddSymInterpret*(xkb: PXkbDescPtr, si: PXkbSymInterpretPtr, + updateMap: bool, changes: PXkbChangesPtr): PXkbSymInterpretPtr{. + libx11c, importc: "XkbAddSymInterpret".} +proc XkbAllocNames*(xkb: PXkbDescPtr, which: int16, + nTotalRG, nTotalAliases: int16): TStatus{.libx11c, importc: "XkbAllocNames".} +proc XkbGetNames*(dpy: PDisplay, which: int16, desc: PXkbDescPtr): TStatus{. + libx11c, importc: "XkbGetNames".} +proc XkbSetNames*(dpy: PDisplay, which, firstType, nTypes: int16, + desc: PXkbDescPtr): bool{.libx11c, + importc: "XkbSetNames".} +proc XkbChangeNames*(dpy: PDisplay, xkb: PXkbDescPtr, + changes: PXkbNameChangesPtr): bool{.libx11c, + importc: "XkbChangeNames".} +proc XkbFreeNames*(xkb: PXkbDescPtr, which: int16, freeMap: bool){.libx11c, importc: "XkbFreeNames".} +proc XkbGetState*(dpy: PDisplay, deviceSpec: int16, rtrnState: PXkbStatePtr): TStatus{. + libx11c, importc: "XkbGetState".} +proc XkbSetMap*(dpy: PDisplay, which: int16, desc: PXkbDescPtr): bool{.libx11c, importc: "XkbSetMap".} +proc XkbChangeMap*(dpy: PDisplay, desc: PXkbDescPtr, changes: PXkbMapChangesPtr): bool{. + libx11c, importc: "XkbChangeMap".} +proc XkbSetDetectableAutoRepeat*(dpy: PDisplay, detectable: bool, + supported: ptr bool): bool{.libx11c, importc: "XkbSetDetectableAutoRepeat".} +proc XkbGetDetectableAutoRepeat*(dpy: PDisplay, supported: ptr bool): bool{. + libx11c, importc: "XkbGetDetectableAutoRepeat".} +proc XkbSetAutoResetControls*(dpy: PDisplay, changes: int16, + auto_ctrls, auto_values: PWord): bool{.libx11c, importc: "XkbSetAutoResetControls".} +proc XkbGetAutoResetControls*(dpy: PDisplay, auto_ctrls, auto_ctrl_values: PWord): bool{. + libx11c, importc: "XkbGetAutoResetControls".} +proc XkbSetPerClientControls*(dpy: PDisplay, change: int16, values: PWord): bool{. + libx11c, importc: "XkbSetPerClientControls".} +proc XkbGetPerClientControls*(dpy: PDisplay, ctrls: PWord): bool{.libx11c, importc: "XkbGetPerClientControls".} +proc XkbCopyKeyType*(`from`, into: PXkbKeyTypePtr): TStatus{.libx11c, importc: "XkbCopyKeyType".} +proc XkbCopyKeyTypes*(`from`, into: PXkbKeyTypePtr, num_types: int16): TStatus{. + libx11c, importc: "XkbCopyKeyTypes".} +proc XkbResizeKeyType*(xkb: PXkbDescPtr, type_ndx, map_count: int16, + want_preserve: bool, new_num_lvls: int16): TStatus{. + libx11c, importc: "XkbResizeKeyType".} +proc XkbResizeKeySyms*(desc: PXkbDescPtr, forKey, symsNeeded: int16): PKeySym{. + libx11c, importc: "XkbResizeKeySyms".} +proc XkbResizeKeyActions*(desc: PXkbDescPtr, forKey, actsNeeded: int16): PXkbAction{. + libx11c, importc: "XkbResizeKeyActions".} +proc XkbChangeTypesOfKey*(xkb: PXkbDescPtr, key, num_groups: int16, + groups: int16, newTypes: ptr int16, + pChanges: PXkbMapChangesPtr): TStatus{.libx11c, importc: "XkbChangeTypesOfKey".} + +proc XkbListComponents*(dpy: PDisplay, deviceSpec: int16, + ptrns: PXkbComponentNamesPtr, max_inout: ptr int16): PXkbComponentListPtr{. + libx11c, importc: "XkbListComponents".} +proc XkbFreeComponentList*(list: PXkbComponentListPtr){.libx11c, + importc: "XkbFreeComponentList".} +proc XkbGetKeyboard*(dpy: PDisplay, which, deviceSpec: int16): PXkbDescPtr{. + libx11c, importc: "XkbGetKeyboard".} +proc XkbGetKeyboardByName*(dpy: PDisplay, deviceSpec: int16, + names: PXkbComponentNamesPtr, want, need: int16, + load: bool): PXkbDescPtr{.libx11c, + importc: "XkbGetKeyboardByName".} + +proc XkbKeyTypesForCoreSymbols*(xkb: PXkbDescPtr, + map_width: int16, # keyboard device + core_syms: PKeySym, # always mapWidth symbols + protected: int16, # explicit key types + types_inout: ptr int16, # always four type indices + xkb_syms_rtrn: PKeySym): int16{.libx11c, importc: "XkbKeyTypesForCoreSymbols".} + # must have enough space +proc XkbApplyCompatMapToKey*(xkb: PXkbDescPtr, + key: TKeyCode, # key to be updated + changes: PXkbChangesPtr): bool{.libx11c, importc: "XkbApplyCompatMapToKey".} + # resulting changes to map +proc XkbUpdateMapFromCore*(xkb: PXkbDescPtr, + first_key: TKeyCode, # first changed key + num_keys, + map_width: int16, + core_keysyms: PKeySym, # symbols `from` core keymap + changes: PXkbChangesPtr): bool{.libx11c, importc: "XkbUpdateMapFromCore".} + +proc XkbAddDeviceLedInfo*(devi: PXkbDeviceInfoPtr, ledClass, ledId: int16): PXkbDeviceLedInfoPtr{. + libx11c, importc: "XkbAddDeviceLedInfo".} +proc XkbResizeDeviceButtonActions*(devi: PXkbDeviceInfoPtr, newTotal: int16): TStatus{. + libx11c, importc: "XkbResizeDeviceButtonActions".} +proc XkbAllocDeviceInfo*(deviceSpec, nButtons, szLeds: int16): PXkbDeviceInfoPtr{. + libx11c, importc: "XkbAllocDeviceInfo".} +proc XkbFreeDeviceInfo*(devi: PXkbDeviceInfoPtr, which: int16, freeDevI: bool){. + libx11c, importc: "XkbFreeDeviceInfo".} +proc XkbNoteDeviceChanges*(old: PXkbDeviceChangesPtr, + new: PXkbExtensionDeviceNotifyEvent, wanted: int16){. + libx11c, importc: "XkbNoteDeviceChanges".} +proc XkbGetDeviceInfo*(dpy: PDisplay, which, deviceSpec, ledClass, ledID: int16): PXkbDeviceInfoPtr{. + libx11c, importc: "XkbGetDeviceInfo".} +proc XkbGetDeviceInfoChanges*(dpy: PDisplay, devi: PXkbDeviceInfoPtr, + changes: PXkbDeviceChangesPtr): TStatus{.libx11c, importc: "XkbGetDeviceInfoChanges".} +proc XkbGetDeviceButtonActions*(dpy: PDisplay, devi: PXkbDeviceInfoPtr, + all: bool, first, nBtns: int16): TStatus{.libx11c, importc: "XkbGetDeviceButtonActions".} +proc XkbGetDeviceLedInfo*(dpy: PDisplay, devi: PXkbDeviceInfoPtr, + ledClass, ledId, which: int16): TStatus{.libx11c, importc: "XkbGetDeviceLedInfo".} +proc XkbSetDeviceInfo*(dpy: PDisplay, which: int16, devi: PXkbDeviceInfoPtr): bool{. + libx11c, importc: "XkbSetDeviceInfo".} +proc XkbChangeDeviceInfo*(dpy: PDisplay, desc: PXkbDeviceInfoPtr, + changes: PXkbDeviceChangesPtr): bool{.libx11c, importc: "XkbChangeDeviceInfo".} +proc XkbSetDeviceLedInfo*(dpy: PDisplay, devi: PXkbDeviceInfoPtr, + ledClass, ledID, which: int16): bool{.libx11c, importc: "XkbSetDeviceLedInfo".} +proc XkbSetDeviceButtonActions*(dpy: PDisplay, devi: PXkbDeviceInfoPtr, + first, nBtns: int16): bool{.libx11c, importc: "XkbSetDeviceButtonActions".} + +proc XkbToControl*(c: int8): int8{.libx11c, + importc: "XkbToControl".} + +proc XkbSetDebuggingFlags*(dpy: PDisplay, mask, flags: int16, msg: cstring, + ctrls_mask, ctrls, rtrn_flags, rtrn_ctrls: int16): bool{. + libx11c, importc: "XkbSetDebuggingFlags".} +proc XkbApplyVirtualModChanges*(xkb: PXkbDescPtr, changed: int16, + changes: PXkbChangesPtr): bool{.libx11c, importc: "XkbApplyVirtualModChanges".} + +# implementation + +proc XkbNoteIndicatorMapChanges(o, n: PXkbIndicatorChangesPtr, w: int16) = + ##define XkbNoteIndicatorMapChanges(o,n,w) ((o)->map_changes|=((n)->map_changes&(w))) + o.map_changes = o.map_changes or (n.map_changes and w) + +proc XkbNoteIndicatorStateChanges(o, n: PXkbIndicatorChangesPtr, w: int16) = + ##define XkbNoteIndicatorStateChanges(o,n,w) ((o)->state_changes|=((n)->state_changes&(w))) + o.state_changes = o.state_changes or (n.state_changes and (w)) + +proc XkbGetIndicatorMapChanges(d: PDisplay, x: PXkbDescPtr, + c: PXkbIndicatorChangesPtr): TStatus = + ##define XkbGetIndicatorMapChanges(d,x,c) (XkbGetIndicatorMap((d),(c)->map_changes,x) + result = XkbGetIndicatorMap(d, c.map_changes, x) + +proc XkbChangeIndicatorMaps(d: PDisplay, x: PXkbDescPtr, + c: PXkbIndicatorChangesPtr): bool = + ##define XkbChangeIndicatorMaps(d,x,c) (XkbSetIndicatorMap((d),(c)->map_changes,x)) + result = XkbSetIndicatorMap(d, c.map_changes, x) + +proc XkbGetControlsChanges(d: PDisplay, x: PXkbDescPtr, + c: PXkbControlsChangesPtr): TStatus = + ##define XkbGetControlsChanges(d,x,c) XkbGetControls(d,(c)->changed_ctrls,x) + result = XkbGetControls(d, c.changed_ctrls, x) + +proc XkbChangeControls(d: PDisplay, x: PXkbDescPtr, c: PXkbControlsChangesPtr): bool = + ##define XkbChangeControls(d,x,c) XkbSetControls(d,(c)->changed_ctrls,x) + result = XkbSetControls(d, c.changed_ctrls, x) diff --git a/tests/deps/x11-1.0/xlib.nim b/tests/deps/x11-1.0/xlib.nim new file mode 100644 index 000000000..e9f211936 --- /dev/null +++ b/tests/deps/x11-1.0/xlib.nim @@ -0,0 +1,2026 @@ + +import + x + +include "x11pragma.nim" + +type + cunsigned* = cint + Pcint* = ptr cint + PPcint* = ptr Pcint + PPcuchar* = ptr ptr cuchar + PWideChar* = ptr int16 + PPChar* = ptr cstring + PPPChar* = ptr ptr cstring + Pculong* = ptr culong + Pcuchar* = cstring + Pcuint* = ptr cuint + Pcushort* = ptr uint16 +# Automatically converted by H2Pas 0.99.15 from xlib.h +# The following command line parameters were used: +# -p +# -T +# -S +# -d +# -c +# xlib.h + +const + XlibSpecificationRelease* = 6 + +type + PXPointer* = ptr TXPointer + TXPointer* = ptr char + PBool* = ptr TBool + TBool* = int #cint? + PStatus* = ptr TStatus + TStatus* = cint + +const + QueuedAlready* = 0 + QueuedAfterReading* = 1 + QueuedAfterFlush* = 2 + +type + PPXExtData* = ptr PXExtData + PXExtData* = ptr TXExtData + TXExtData*{.final.} = object + number*: cint + next*: PXExtData + free_private*: proc (extension: PXExtData): cint{.cdecl.} + private_data*: TXPointer + + PXExtCodes* = ptr TXExtCodes + TXExtCodes*{.final.} = object + extension*: cint + major_opcode*: cint + first_event*: cint + first_error*: cint + + PXPixmapFormatValues* = ptr TXPixmapFormatValues + TXPixmapFormatValues*{.final.} = object + depth*: cint + bits_per_pixel*: cint + scanline_pad*: cint + + PXGCValues* = ptr TXGCValues + TXGCValues*{.final.} = object + function*: cint + plane_mask*: culong + foreground*: culong + background*: culong + line_width*: cint + line_style*: cint + cap_style*: cint + join_style*: cint + fill_style*: cint + fill_rule*: cint + arc_mode*: cint + tile*: TPixmap + stipple*: TPixmap + ts_x_origin*: cint + ts_y_origin*: cint + font*: TFont + subwindow_mode*: cint + graphics_exposures*: TBool + clip_x_origin*: cint + clip_y_origin*: cint + clip_mask*: TPixmap + dash_offset*: cint + dashes*: cchar + + PXGC* = ptr TXGC + TXGC*{.final.} = object + TGC* = PXGC + PGC* = ptr TGC + PVisual* = ptr TVisual + TVisual*{.final.} = object + ext_data*: PXExtData + visualid*: TVisualID + c_class*: cint + red_mask*, green_mask*, blue_mask*: culong + bits_per_rgb*: cint + map_entries*: cint + + PDepth* = ptr TDepth + TDepth*{.final.} = object + depth*: cint + nvisuals*: cint + visuals*: PVisual + + PXDisplay* = ptr TXDisplay + TXDisplay*{.final.} = object + PScreen* = ptr TScreen + TScreen*{.final.} = object + ext_data*: PXExtData + display*: PXDisplay + root*: TWindow + width*, height*: cint + mwidth*, mheight*: cint + ndepths*: cint + depths*: PDepth + root_depth*: cint + root_visual*: PVisual + default_gc*: TGC + cmap*: TColormap + white_pixel*: culong + black_pixel*: culong + max_maps*, min_maps*: cint + backing_store*: cint + save_unders*: TBool + root_input_mask*: clong + + PScreenFormat* = ptr TScreenFormat + TScreenFormat*{.final.} = object + ext_data*: PXExtData + depth*: cint + bits_per_pixel*: cint + scanline_pad*: cint + + PXSetWindowAttributes* = ptr TXSetWindowAttributes + TXSetWindowAttributes*{.final.} = object + background_pixmap*: TPixmap + background_pixel*: culong + border_pixmap*: TPixmap + border_pixel*: culong + bit_gravity*: cint + win_gravity*: cint + backing_store*: cint + backing_planes*: culong + backing_pixel*: culong + save_under*: TBool + event_mask*: clong + do_not_propagate_mask*: clong + override_redirect*: TBool + colormap*: TColormap + cursor*: TCursor + + PXWindowAttributes* = ptr TXWindowAttributes + TXWindowAttributes*{.final.} = object + x*, y*: cint + width*, height*: cint + border_width*: cint + depth*: cint + visual*: PVisual + root*: TWindow + c_class*: cint + bit_gravity*: cint + win_gravity*: cint + backing_store*: cint + backing_planes*: culong + backing_pixel*: culong + save_under*: TBool + colormap*: TColormap + map_installed*: TBool + map_state*: cint + all_event_masks*: clong + your_event_mask*: clong + do_not_propagate_mask*: clong + override_redirect*: TBool + screen*: PScreen + + PXHostAddress* = ptr TXHostAddress + TXHostAddress*{.final.} = object + family*: cint + len*: cint + address*: cstring + + PXServerInterpretedAddress* = ptr TXServerInterpretedAddress + TXServerInterpretedAddress*{.final.} = object + typelength*: cint + valuelength*: cint + theType*: cstring + value*: cstring + + PXImage* = ptr TXImage + TF*{.final.} = object + create_image*: proc (para1: PXDisplay, para2: PVisual, para3: cuint, + para4: cint, para5: cint, para6: cstring, para7: cuint, + para8: cuint, para9: cint, para10: cint): PXImage{. + cdecl.} + destroy_image*: proc (para1: PXImage): cint{.cdecl.} + get_pixel*: proc (para1: PXImage, para2: cint, para3: cint): culong{.cdecl.} + put_pixel*: proc (para1: PXImage, para2: cint, para3: cint, para4: culong): cint{. + cdecl.} + sub_image*: proc (para1: PXImage, para2: cint, para3: cint, para4: cuint, + para5: cuint): PXImage{.cdecl.} + add_pixel*: proc (para1: PXImage, para2: clong): cint{.cdecl.} + + TXImage*{.final.} = object + width*, height*: cint + xoffset*: cint + format*: cint + data*: cstring + byte_order*: cint + bitmap_unit*: cint + bitmap_bit_order*: cint + bitmap_pad*: cint + depth*: cint + bytes_per_line*: cint + bits_per_pixel*: cint + red_mask*: culong + green_mask*: culong + blue_mask*: culong + obdata*: TXPointer + f*: TF + + PXWindowChanges* = ptr TXWindowChanges + TXWindowChanges*{.final.} = object + x*, y*: cint + width*, height*: cint + border_width*: cint + sibling*: TWindow + stack_mode*: cint + + PXColor* = ptr TXColor + TXColor*{.final.} = object + pixel*: culong + red*, green*, blue*: cushort + flags*: cchar + pad*: cchar + + PXSegment* = ptr TXSegment + TXSegment*{.final.} = object + x1*, y1*, x2*, y2*: cshort + + PXPoint* = ptr TXPoint + TXPoint*{.final.} = object + x*, y*: cshort + + PXRectangle* = ptr TXRectangle + TXRectangle*{.final.} = object + x*, y*: cshort + width*, height*: cushort + + PXArc* = ptr TXArc + TXArc*{.final.} = object + x*, y*: cshort + width*, height*: cushort + angle1*, angle2*: cshort + + PXKeyboardControl* = ptr TXKeyboardControl + TXKeyboardControl*{.final.} = object + key_click_percent*: cint + bell_percent*: cint + bell_pitch*: cint + bell_duration*: cint + led*: cint + led_mode*: cint + key*: cint + auto_repeat_mode*: cint + + PXKeyboardState* = ptr TXKeyboardState + TXKeyboardState*{.final.} = object + key_click_percent*: cint + bell_percent*: cint + bell_pitch*, bell_duration*: cuint + led_mask*: culong + global_auto_repeat*: cint + auto_repeats*: array[0..31, cchar] + + PXTimeCoord* = ptr TXTimeCoord + TXTimeCoord*{.final.} = object + time*: TTime + x*, y*: cshort + + PXModifierKeymap* = ptr TXModifierKeymap + TXModifierKeymap*{.final.} = object + max_keypermod*: cint + modifiermap*: PKeyCode + + PDisplay* = ptr TDisplay + TDisplay* = TXDisplay + PXPrivate* = ptr TXPrivate + TXPrivate*{.final.} = object + PXrmHashBucketRec* = ptr TXrmHashBucketRec + TXrmHashBucketRec*{.final.} = object + PXPrivDisplay* = ptr TXPrivDisplay + TXPrivDisplay*{.final.} = object + ext_data*: PXExtData + private1*: PXPrivate + fd*: cint + private2*: cint + proto_major_version*: cint + proto_minor_version*: cint + vendor*: cstring + private3*: TXID + private4*: TXID + private5*: TXID + private6*: cint + resource_alloc*: proc (para1: PXDisplay): TXID{.cdecl.} + byte_order*: cint + bitmap_unit*: cint + bitmap_pad*: cint + bitmap_bit_order*: cint + nformats*: cint + pixmap_format*: PScreenFormat + private8*: cint + release*: cint + private9*, private10*: PXPrivate + qlen*: cint + last_request_read*: culong + request*: culong + private11*: TXPointer + private12*: TXPointer + private13*: TXPointer + private14*: TXPointer + max_request_size*: cunsigned + db*: PXrmHashBucketRec + private15*: proc (para1: PXDisplay): cint{.cdecl.} + display_name*: cstring + default_screen*: cint + nscreens*: cint + screens*: PScreen + motion_buffer*: culong + private16*: culong + min_keycode*: cint + max_keycode*: cint + private17*: TXPointer + private18*: TXPointer + private19*: cint + xdefaults*: cstring + + PXKeyEvent* = ptr TXKeyEvent + TXKeyEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + root*: TWindow + subwindow*: TWindow + time*: TTime + x*, y*: cint + x_root*, y_root*: cint + state*: cuint + keycode*: cuint + same_screen*: TBool + + PXKeyPressedEvent* = ptr TXKeyPressedEvent + TXKeyPressedEvent* = TXKeyEvent + PXKeyReleasedEvent* = ptr TXKeyReleasedEvent + TXKeyReleasedEvent* = TXKeyEvent + PXButtonEvent* = ptr TXButtonEvent + TXButtonEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + root*: TWindow + subwindow*: TWindow + time*: TTime + x*, y*: cint + x_root*, y_root*: cint + state*: cuint + button*: cuint + same_screen*: TBool + + PXButtonPressedEvent* = ptr TXButtonPressedEvent + TXButtonPressedEvent* = TXButtonEvent + PXButtonReleasedEvent* = ptr TXButtonReleasedEvent + TXButtonReleasedEvent* = TXButtonEvent + PXMotionEvent* = ptr TXMotionEvent + TXMotionEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + root*: TWindow + subwindow*: TWindow + time*: TTime + x*, y*: cint + x_root*, y_root*: cint + state*: cuint + is_hint*: cchar + same_screen*: TBool + + PXPointerMovedEvent* = ptr TXPointerMovedEvent + TXPointerMovedEvent* = TXMotionEvent + PXCrossingEvent* = ptr TXCrossingEvent + TXCrossingEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + root*: TWindow + subwindow*: TWindow + time*: TTime + x*, y*: cint + x_root*, y_root*: cint + mode*: cint + detail*: cint + same_screen*: TBool + focus*: TBool + state*: cuint + + PXEnterWindowEvent* = ptr TXEnterWindowEvent + TXEnterWindowEvent* = TXCrossingEvent + PXLeaveWindowEvent* = ptr TXLeaveWindowEvent + TXLeaveWindowEvent* = TXCrossingEvent + PXFocusChangeEvent* = ptr TXFocusChangeEvent + TXFocusChangeEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + mode*: cint + detail*: cint + + PXFocusInEvent* = ptr TXFocusInEvent + TXFocusInEvent* = TXFocusChangeEvent + PXFocusOutEvent* = ptr TXFocusOutEvent + TXFocusOutEvent* = TXFocusChangeEvent + PXKeymapEvent* = ptr TXKeymapEvent + TXKeymapEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + key_vector*: array[0..31, cchar] + + PXExposeEvent* = ptr TXExposeEvent + TXExposeEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + x*, y*: cint + width*, height*: cint + count*: cint + + PXGraphicsExposeEvent* = ptr TXGraphicsExposeEvent + TXGraphicsExposeEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + drawable*: TDrawable + x*, y*: cint + width*, height*: cint + count*: cint + major_code*: cint + minor_code*: cint + + PXNoExposeEvent* = ptr TXNoExposeEvent + TXNoExposeEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + drawable*: TDrawable + major_code*: cint + minor_code*: cint + + PXVisibilityEvent* = ptr TXVisibilityEvent + TXVisibilityEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + state*: cint + + PXCreateWindowEvent* = ptr TXCreateWindowEvent + TXCreateWindowEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + parent*: TWindow + window*: TWindow + x*, y*: cint + width*, height*: cint + border_width*: cint + override_redirect*: TBool + + PXDestroyWindowEvent* = ptr TXDestroyWindowEvent + TXDestroyWindowEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + event*: TWindow + window*: TWindow + + PXUnmapEvent* = ptr TXUnmapEvent + TXUnmapEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + event*: TWindow + window*: TWindow + from_configure*: TBool + + PXMapEvent* = ptr TXMapEvent + TXMapEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + event*: TWindow + window*: TWindow + override_redirect*: TBool + + PXMapRequestEvent* = ptr TXMapRequestEvent + TXMapRequestEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + parent*: TWindow + window*: TWindow + + PXReparentEvent* = ptr TXReparentEvent + TXReparentEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + event*: TWindow + window*: TWindow + parent*: TWindow + x*, y*: cint + override_redirect*: TBool + + PXConfigureEvent* = ptr TXConfigureEvent + TXConfigureEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + event*: TWindow + window*: TWindow + x*, y*: cint + width*, height*: cint + border_width*: cint + above*: TWindow + override_redirect*: TBool + + PXGravityEvent* = ptr TXGravityEvent + TXGravityEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + event*: TWindow + window*: TWindow + x*, y*: cint + + PXResizeRequestEvent* = ptr TXResizeRequestEvent + TXResizeRequestEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + width*, height*: cint + + PXConfigureRequestEvent* = ptr TXConfigureRequestEvent + TXConfigureRequestEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + parent*: TWindow + window*: TWindow + x*, y*: cint + width*, height*: cint + border_width*: cint + above*: TWindow + detail*: cint + value_mask*: culong + + PXCirculateEvent* = ptr TXCirculateEvent + TXCirculateEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + event*: TWindow + window*: TWindow + place*: cint + + PXCirculateRequestEvent* = ptr TXCirculateRequestEvent + TXCirculateRequestEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + parent*: TWindow + window*: TWindow + place*: cint + + PXPropertyEvent* = ptr TXPropertyEvent + TXPropertyEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + atom*: TAtom + time*: TTime + state*: cint + + PXSelectionClearEvent* = ptr TXSelectionClearEvent + TXSelectionClearEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + selection*: TAtom + time*: TTime + + PXSelectionRequestEvent* = ptr TXSelectionRequestEvent + TXSelectionRequestEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + owner*: TWindow + requestor*: TWindow + selection*: TAtom + target*: TAtom + property*: TAtom + time*: TTime + + PXSelectionEvent* = ptr TXSelectionEvent + TXSelectionEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + requestor*: TWindow + selection*: TAtom + target*: TAtom + property*: TAtom + time*: TTime + + PXColormapEvent* = ptr TXColormapEvent + TXColormapEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + colormap*: TColormap + c_new*: TBool + state*: cint + + PXClientMessageEvent* = ptr TXClientMessageEvent + TXClientMessageEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + message_type*: TAtom + format*: cint + data*: array[0..4, clong] # using clong here to be 32/64-bit dependent + # as the original C union + + PXMappingEvent* = ptr TXMappingEvent + TXMappingEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + request*: cint + first_keycode*: cint + count*: cint + + PXErrorEvent* = ptr TXErrorEvent + TXErrorEvent*{.final.} = object + theType*: cint + display*: PDisplay + resourceid*: TXID + serial*: culong + error_code*: cuchar + request_code*: cuchar + minor_code*: cuchar + + PXAnyEvent* = ptr TXAnyEvent + TXAnyEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + window*: TWindow + + PXEvent* = ptr TXEvent + TXEvent*{.final.} = object + theType*: cint + pad*: array[0..22, clong] # + # case longint of + # 0 : ( theType : cint ); + # 1 : ( xany : TXAnyEvent ); + # 2 : ( xkey : TXKeyEvent ); + # 3 : ( xbutton : TXButtonEvent ); + # 4 : ( xmotion : TXMotionEvent ); + # 5 : ( xcrossing : TXCrossingEvent ); + # 6 : ( xfocus : TXFocusChangeEvent ); + # 7 : ( xexpose : TXExposeEvent ); + # 8 : ( xgraphicsexpose : TXGraphicsExposeEvent ); + # 9 : ( xnoexpose : TXNoExposeEvent ); + # 10 : ( xvisibility : TXVisibilityEvent ); + # 11 : ( xcreatewindow : TXCreateWindowEvent ); + # 12 : ( xdestroywindow : TXDestroyWindowEvent ); + # 13 : ( xunmap : TXUnmapEvent ); + # 14 : ( xmap : TXMapEvent ); + # 15 : ( xmaprequest : TXMapRequestEvent ); + # 16 : ( xreparent : TXReparentEvent ); + # 17 : ( xconfigure : TXConfigureEvent ); + # 18 : ( xgravity : TXGravityEvent ); + # 19 : ( xresizerequest : TXResizeRequestEvent ); + # 20 : ( xconfigurerequest : TXConfigureRequestEvent ); + # 21 : ( xcirculate : TXCirculateEvent ); + # 22 : ( xcirculaterequest : TXCirculateRequestEvent ); + # 23 : ( xproperty : TXPropertyEvent ); + # 24 : ( xselectionclear : TXSelectionClearEvent ); + # 25 : ( xselectionrequest : TXSelectionRequestEvent ); + # 26 : ( xselection : TXSelectionEvent ); + # 27 : ( xcolormap : TXColormapEvent ); + # 28 : ( xclient : TXClientMessageEvent ); + # 29 : ( xmapping : TXMappingEvent ); + # 30 : ( xerror : TXErrorEvent ); + # 31 : ( xkeymap : TXKeymapEvent ); + # 32 : ( pad : array[0..23] of clong ); + # + + +proc xclient*(e: PXEvent): PXClientMessageEvent = + ## Treats XEvent as XClientMessageEvent + return cast[PXClientMessageEvent](e) + +proc xclient*(e: var TXEvent): PXClientMessageEvent = + return xclient(PXEvent(e.addr)) + +type + PXCharStruct* = ptr TXCharStruct + TXCharStruct*{.final.} = object + lbearing*: cshort + rbearing*: cshort + width*: cshort + ascent*: cshort + descent*: cshort + attributes*: cushort + + PXFontProp* = ptr TXFontProp + TXFontProp*{.final.} = object + name*: TAtom + card32*: culong + + PPPXFontStruct* = ptr PPXFontStruct + PPXFontStruct* = ptr PXFontStruct + PXFontStruct* = ptr TXFontStruct + TXFontStruct*{.final.} = object + ext_data*: PXExtData + fid*: TFont + direction*: cunsigned + min_char_or_byte2*: cunsigned + max_char_or_byte2*: cunsigned + min_byte1*: cunsigned + max_byte1*: cunsigned + all_chars_exist*: TBool + default_char*: cunsigned + n_properties*: cint + properties*: PXFontProp + min_bounds*: TXCharStruct + max_bounds*: TXCharStruct + per_char*: PXCharStruct + ascent*: cint + descent*: cint + + PXTextItem* = ptr TXTextItem + TXTextItem*{.final.} = object + chars*: cstring + nchars*: cint + delta*: cint + font*: TFont + + PXChar2b* = ptr TXChar2b + TXChar2b*{.final.} = object + byte1*: cuchar + byte2*: cuchar + + PXTextItem16* = ptr TXTextItem16 + TXTextItem16*{.final.} = object + chars*: PXChar2b + nchars*: cint + delta*: cint + font*: TFont + + PXEDataObject* = ptr TXEDataObject + TXEDataObject*{.final.} = object + display*: PDisplay #case longint of + # 0 : ( display : PDisplay ); + # 1 : ( gc : TGC ); + # 2 : ( visual : PVisual ); + # 3 : ( screen : PScreen ); + # 4 : ( pixmap_format : PScreenFormat ); + # 5 : ( font : PXFontStruct ); + + PXFontSetExtents* = ptr TXFontSetExtents + TXFontSetExtents*{.final.} = object + max_ink_extent*: TXRectangle + max_logical_extent*: TXRectangle + + PXOM* = ptr TXOM + TXOM*{.final.} = object + PXOC* = ptr TXOC + TXOC*{.final.} = object + TXFontSet* = PXOC + PXFontSet* = ptr TXFontSet + PXmbTextItem* = ptr TXmbTextItem + TXmbTextItem*{.final.} = object + chars*: cstring + nchars*: cint + delta*: cint + font_set*: TXFontSet + + PXwcTextItem* = ptr TXwcTextItem + TXwcTextItem*{.final.} = object + chars*: PWideChar #wchar_t* + nchars*: cint + delta*: cint + font_set*: TXFontSet + + +const + XNRequiredCharSet* = "requiredCharSet" + XNQueryOrientation* = "queryOrientation" + XNBaseFontName* = "baseFontName" + XNOMAutomatic* = "omAutomatic" + XNMissingCharSet* = "missingCharSet" + XNDefaultString* = "defaultString" + XNOrientation* = "orientation" + XNDirectionalDependentDrawing* = "directionalDependentDrawing" + XNContextualDrawing* = "contextualDrawing" + XNFontInfo* = "fontInfo" + +type + PXOMCharSetList* = ptr TXOMCharSetList + TXOMCharSetList*{.final.} = object + charset_count*: cint + charset_list*: PPChar + + PXOrientation* = ptr TXOrientation + TXOrientation* = enum + XOMOrientation_LTR_TTB, XOMOrientation_RTL_TTB, XOMOrientation_TTB_LTR, + XOMOrientation_TTB_RTL, XOMOrientation_Context + PXOMOrientation* = ptr TXOMOrientation + TXOMOrientation*{.final.} = object + num_orientation*: cint + orientation*: PXOrientation + + PXOMFontInfo* = ptr TXOMFontInfo + TXOMFontInfo*{.final.} = object + num_font*: cint + font_struct_list*: ptr PXFontStruct + font_name_list*: PPChar + + PXIM* = ptr TXIM + TXIM*{.final.} = object + PXIC* = ptr TXIC + TXIC*{.final.} = object + TXIMProc* = proc (para1: TXIM, para2: TXPointer, para3: TXPointer){.cdecl.} + TXICProc* = proc (para1: TXIC, para2: TXPointer, para3: TXPointer): TBool{. + cdecl.} + TXIDProc* = proc (para1: PDisplay, para2: TXPointer, para3: TXPointer){.cdecl.} + PXIMStyle* = ptr TXIMStyle + TXIMStyle* = culong + PXIMStyles* = ptr TXIMStyles + TXIMStyles*{.final.} = object + count_styles*: cushort + supported_styles*: PXIMStyle + + +const + XIMPreeditArea* = 0x00000001 + XIMPreeditCallbacks* = 0x00000002 + XIMPreeditPosition* = 0x00000004 + XIMPreeditNothing* = 0x00000008 + XIMPreeditNone* = 0x00000010 + XIMStatusArea* = 0x00000100 + XIMStatusCallbacks* = 0x00000200 + XIMStatusNothing* = 0x00000400 + XIMStatusNone* = 0x00000800 + XNVaNestedList* = "XNVaNestedList" + XNQueryInputStyle* = "queryInputStyle" + XNClientWindow* = "clientWindow" + XNInputStyle* = "inputStyle" + XNFocusWindow* = "focusWindow" + XNResourceName* = "resourceName" + XNResourceClass* = "resourceClass" + XNGeometryCallback* = "geometryCallback" + XNDestroyCallback* = "destroyCallback" + XNFilterEvents* = "filterEvents" + XNPreeditStartCallback* = "preeditStartCallback" + XNPreeditDoneCallback* = "preeditDoneCallback" + XNPreeditDrawCallback* = "preeditDrawCallback" + XNPreeditCaretCallback* = "preeditCaretCallback" + XNPreeditStateNotifyCallback* = "preeditStateNotifyCallback" + XNPreeditAttributes* = "preeditAttributes" + XNStatusStartCallback* = "statusStartCallback" + XNStatusDoneCallback* = "statusDoneCallback" + XNStatusDrawCallback* = "statusDrawCallback" + XNStatusAttributes* = "statusAttributes" + XNArea* = "area" + XNAreaNeeded* = "areaNeeded" + XNSpotLocation* = "spotLocation" + XNColormap* = "colorMap" + XNStdColormap* = "stdColorMap" + XNForeground* = "foreground" + XNBackground* = "background" + XNBackgroundPixmap* = "backgroundPixmap" + XNFontSet* = "fontSet" + XNLineSpace* = "lineSpace" + XNCursor* = "cursor" + XNQueryIMValuesList* = "queryIMValuesList" + XNQueryICValuesList* = "queryICValuesList" + XNVisiblePosition* = "visiblePosition" + XNR6PreeditCallback* = "r6PreeditCallback" + XNStringConversionCallback* = "stringConversionCallback" + XNStringConversion* = "stringConversion" + XNResetState* = "resetState" + XNHotKey* = "hotKey" + XNHotKeyState* = "hotKeyState" + XNPreeditState* = "preeditState" + XNSeparatorofNestedList* = "separatorofNestedList" + XBufferOverflow* = - (1) + XLookupNone* = 1 + XLookupChars* = 2 + XLookupKeySymVal* = 3 + XLookupBoth* = 4 + +type + PXVaNestedList* = ptr TXVaNestedList + TXVaNestedList* = pointer + PXIMCallback* = ptr TXIMCallback + TXIMCallback*{.final.} = object + client_data*: TXPointer + callback*: TXIMProc + + PXICCallback* = ptr TXICCallback + TXICCallback*{.final.} = object + client_data*: TXPointer + callback*: TXICProc + + PXIMFeedback* = ptr TXIMFeedback + TXIMFeedback* = culong + +const + XIMReverse* = 1 + XIMUnderline* = 1 shl 1 + XIMHighlight* = 1 shl 2 + XIMPrimary* = 1 shl 5 + XIMSecondary* = 1 shl 6 + XIMTertiary* = 1 shl 7 + XIMVisibleToForward* = 1 shl 8 + XIMVisibleToBackword* = 1 shl 9 + XIMVisibleToCenter* = 1 shl 10 + +type + PXIMText* = ptr TXIMText + TXIMText*{.final.} = object + len*: cushort + feedback*: PXIMFeedback + encoding_is_wchar*: TBool + multi_byte*: cstring + + PXIMPreeditState* = ptr TXIMPreeditState + TXIMPreeditState* = culong + +const + XIMPreeditUnKnown* = 0 + XIMPreeditEnable* = 1 + XIMPreeditDisable* = 1 shl 1 + +type + PXIMPreeditStateNotifyCallbackStruct* = ptr TXIMPreeditStateNotifyCallbackStruct + TXIMPreeditStateNotifyCallbackStruct*{.final.} = object + state*: TXIMPreeditState + + PXIMResetState* = ptr TXIMResetState + TXIMResetState* = culong + +const + XIMInitialState* = 1 + XIMPreserveState* = 1 shl 1 + +type + PXIMStringConversionFeedback* = ptr TXIMStringConversionFeedback + TXIMStringConversionFeedback* = culong + +const + XIMStringConversionLeftEdge* = 0x00000001 + XIMStringConversionRightEdge* = 0x00000002 + XIMStringConversionTopEdge* = 0x00000004 + XIMStringConversionBottomEdge* = 0x00000008 + XIMStringConversionConcealed* = 0x00000010 + XIMStringConversionWrapped* = 0x00000020 + +type + PXIMStringConversionText* = ptr TXIMStringConversionText + TXIMStringConversionText*{.final.} = object + len*: cushort + feedback*: PXIMStringConversionFeedback + encoding_is_wchar*: TBool + mbs*: cstring + + PXIMStringConversionPosition* = ptr TXIMStringConversionPosition + TXIMStringConversionPosition* = cushort + PXIMStringConversionType* = ptr TXIMStringConversionType + TXIMStringConversionType* = cushort + +const + XIMStringConversionBuffer* = 0x00000001 + XIMStringConversionLine* = 0x00000002 + XIMStringConversionWord* = 0x00000003 + XIMStringConversionChar* = 0x00000004 + +type + PXIMStringConversionOperation* = ptr TXIMStringConversionOperation + TXIMStringConversionOperation* = cushort + +const + XIMStringConversionSubstitution* = 0x00000001 + XIMStringConversionRetrieval* = 0x00000002 + +type + PXIMCaretDirection* = ptr TXIMCaretDirection + TXIMCaretDirection* = enum + XIMForwardChar, XIMBackwardChar, XIMForwardWord, XIMBackwardWord, + XIMCaretUp, XIMCaretDown, XIMNextLine, XIMPreviousLine, XIMLineStart, + XIMLineEnd, XIMAbsolutePosition, XIMDontChange + PXIMStringConversionCallbackStruct* = ptr TXIMStringConversionCallbackStruct + TXIMStringConversionCallbackStruct*{.final.} = object + position*: TXIMStringConversionPosition + direction*: TXIMCaretDirection + operation*: TXIMStringConversionOperation + factor*: cushort + text*: PXIMStringConversionText + + PXIMPreeditDrawCallbackStruct* = ptr TXIMPreeditDrawCallbackStruct + TXIMPreeditDrawCallbackStruct*{.final.} = object + caret*: cint + chg_first*: cint + chg_length*: cint + text*: PXIMText + + PXIMCaretStyle* = ptr TXIMCaretStyle + TXIMCaretStyle* = enum + XIMIsInvisible, XIMIsPrimary, XIMIsSecondary + PXIMPreeditCaretCallbackStruct* = ptr TXIMPreeditCaretCallbackStruct + TXIMPreeditCaretCallbackStruct*{.final.} = object + position*: cint + direction*: TXIMCaretDirection + style*: TXIMCaretStyle + + PXIMStatusDataType* = ptr TXIMStatusDataType + TXIMStatusDataType* = enum + XIMTextType, XIMBitmapType + PXIMStatusDrawCallbackStruct* = ptr TXIMStatusDrawCallbackStruct + TXIMStatusDrawCallbackStruct*{.final.} = object + theType*: TXIMStatusDataType + bitmap*: TPixmap + + PXIMHotKeyTrigger* = ptr TXIMHotKeyTrigger + TXIMHotKeyTrigger*{.final.} = object + keysym*: TKeySym + modifier*: cint + modifier_mask*: cint + + PXIMHotKeyTriggers* = ptr TXIMHotKeyTriggers + TXIMHotKeyTriggers*{.final.} = object + num_hot_key*: cint + key*: PXIMHotKeyTrigger + + PXIMHotKeyState* = ptr TXIMHotKeyState + TXIMHotKeyState* = culong + +const + XIMHotKeyStateON* = 0x00000001 + XIMHotKeyStateOFF* = 0x00000002 + +type + PXIMValuesList* = ptr TXIMValuesList + TXIMValuesList*{.final.} = object + count_values*: cushort + supported_values*: PPChar + + +type + funcdisp* = proc (display: PDisplay): cint{.cdecl.} + funcifevent* = proc (display: PDisplay, event: PXEvent, p: TXPointer): TBool{. + cdecl.} + chararr32* = array[0..31, char] + +const + AllPlanes*: culong = culong(not 0) + +proc XLoadQueryFont*(para1: PDisplay, para2: cstring): PXFontStruct{.libx11.} +proc XQueryFont*(para1: PDisplay, para2: TXID): PXFontStruct{.libx11.} +proc XGetMotionEvents*(para1: PDisplay, para2: TWindow, para3: TTime, + para4: TTime, para5: Pcint): PXTimeCoord{.libx11.} +proc XDeleteModifiermapEntry*(para1: PXModifierKeymap, para2: TKeyCode, + para3: cint): PXModifierKeymap{.libx11.} +proc XGetModifierMapping*(para1: PDisplay): PXModifierKeymap{.libx11.} +proc XInsertModifiermapEntry*(para1: PXModifierKeymap, para2: TKeyCode, + para3: cint): PXModifierKeymap{.libx11.} +proc XNewModifiermap*(para1: cint): PXModifierKeymap{.libx11.} +proc XCreateImage*(para1: PDisplay, para2: PVisual, para3: cuint, para4: cint, + para5: cint, para6: cstring, para7: cuint, para8: cuint, + para9: cint, para10: cint): PXImage{.libx11.} +proc XInitImage*(para1: PXImage): TStatus{.libx11.} +proc XGetImage*(para1: PDisplay, para2: TDrawable, para3: cint, para4: cint, + para5: cuint, para6: cuint, para7: culong, para8: cint): PXImage{. + libx11.} +proc XGetSubImage*(para1: PDisplay, para2: TDrawable, para3: cint, para4: cint, + para5: cuint, para6: cuint, para7: culong, para8: cint, + para9: PXImage, para10: cint, para11: cint): PXImage{.libx11.} +proc XOpenDisplay*(para1: cstring): PDisplay{.libx11.} +proc XrmInitialize*(){.libx11.} +proc XFetchBytes*(para1: PDisplay, para2: Pcint): cstring{.libx11.} +proc XFetchBuffer*(para1: PDisplay, para2: Pcint, para3: cint): cstring{.libx11.} +proc XGetAtomName*(para1: PDisplay, para2: TAtom): cstring{.libx11.} +proc XGetAtomNames*(para1: PDisplay, para2: PAtom, para3: cint, para4: PPchar): TStatus{. + libx11.} +proc XGetDefault*(para1: PDisplay, para2: cstring, para3: cstring): cstring{. + libx11.} +proc XDisplayName*(para1: cstring): cstring{.libx11.} +proc XKeysymToString*(para1: TKeySym): cstring{.libx11.} +proc XSynchronize*(para1: PDisplay, para2: TBool): funcdisp{.libx11.} +proc XSetAfterFunction*(para1: PDisplay, para2: funcdisp): funcdisp{.libx11.} +proc XInternAtom*(para1: PDisplay, para2: cstring, para3: TBool): TAtom{.libx11.} +proc XInternAtoms*(para1: PDisplay, para2: PPchar, para3: cint, para4: TBool, + para5: PAtom): TStatus{.libx11.} +proc XCopyColormapAndFree*(para1: PDisplay, para2: TColormap): TColormap{.libx11.} +proc XCreateColormap*(para1: PDisplay, para2: TWindow, para3: PVisual, + para4: cint): TColormap{.libx11.} +proc XCreatePixmapCursor*(para1: PDisplay, para2: TPixmap, para3: TPixmap, + para4: PXColor, para5: PXColor, para6: cuint, + para7: cuint): TCursor{.libx11.} +proc XCreateGlyphCursor*(para1: PDisplay, para2: TFont, para3: TFont, + para4: cuint, para5: cuint, para6: PXColor, + para7: PXColor): TCursor{.libx11.} +proc XCreateFontCursor*(para1: PDisplay, para2: cuint): TCursor{.libx11.} +proc XLoadFont*(para1: PDisplay, para2: cstring): TFont{.libx11.} +proc XCreateGC*(para1: PDisplay, para2: TDrawable, para3: culong, + para4: PXGCValues): TGC{.libx11.} +proc XGContextFromGC*(para1: TGC): TGContext{.libx11.} +proc XFlushGC*(para1: PDisplay, para2: TGC){.libx11.} +proc XCreatePixmap*(para1: PDisplay, para2: TDrawable, para3: cuint, + para4: cuint, para5: cuint): TPixmap{.libx11.} +proc XCreateBitmapFromData*(para1: PDisplay, para2: TDrawable, para3: cstring, + para4: cuint, para5: cuint): TPixmap{.libx11.} +proc XCreatePixmapFromBitmapData*(para1: PDisplay, para2: TDrawable, + para3: cstring, para4: cuint, para5: cuint, + para6: culong, para7: culong, para8: cuint): TPixmap{. + libx11.} +proc XCreateSimpleWindow*(para1: PDisplay, para2: TWindow, para3: cint, + para4: cint, para5: cuint, para6: cuint, para7: cuint, + para8: culong, para9: culong): TWindow{.libx11.} +proc XGetSelectionOwner*(para1: PDisplay, para2: TAtom): TWindow{.libx11.} +proc XCreateWindow*(para1: PDisplay, para2: TWindow, para3: cint, para4: cint, + para5: cuint, para6: cuint, para7: cuint, para8: cint, + para9: cuint, para10: PVisual, para11: culong, + para12: PXSetWindowAttributes): TWindow{.libx11.} +proc XListInstalledColormaps*(para1: PDisplay, para2: TWindow, para3: Pcint): PColormap{. + libx11.} +proc XListFonts*(para1: PDisplay, para2: cstring, para3: cint, para4: Pcint): PPChar{. + libx11.} +proc XListFontsWithInfo*(para1: PDisplay, para2: cstring, para3: cint, + para4: Pcint, para5: PPXFontStruct): PPChar{.libx11.} +proc XGetFontPath*(para1: PDisplay, para2: Pcint): PPChar{.libx11.} +proc XListExtensions*(para1: PDisplay, para2: Pcint): PPChar{.libx11.} +proc XListProperties*(para1: PDisplay, para2: TWindow, para3: Pcint): PAtom{. + libx11.} +proc XListHosts*(para1: PDisplay, para2: Pcint, para3: PBool): PXHostAddress{. + libx11.} +proc XKeycodeToKeysym*(para1: PDisplay, para2: TKeyCode, para3: cint): TKeySym{. + libx11.} +proc XLookupKeysym*(para1: PXKeyEvent, para2: cint): TKeySym{.libx11.} +proc XGetKeyboardMapping*(para1: PDisplay, para2: TKeyCode, para3: cint, + para4: Pcint): PKeySym{.libx11.} +proc XStringToKeysym*(para1: cstring): TKeySym{.libx11.} +proc XMaxRequestSize*(para1: PDisplay): clong{.libx11.} +proc XExtendedMaxRequestSize*(para1: PDisplay): clong{.libx11.} +proc XResourceManagerString*(para1: PDisplay): cstring{.libx11.} +proc XScreenResourceString*(para1: PScreen): cstring{.libx11.} +proc XDisplayMotionBufferSize*(para1: PDisplay): culong{.libx11.} +proc XVisualIDFromVisual*(para1: PVisual): TVisualID{.libx11.} +proc XInitThreads*(): TStatus{.libx11.} +proc XLockDisplay*(para1: PDisplay){.libx11.} +proc XUnlockDisplay*(para1: PDisplay){.libx11.} +proc XInitExtension*(para1: PDisplay, para2: cstring): PXExtCodes{.libx11.} +proc XAddExtension*(para1: PDisplay): PXExtCodes{.libx11.} +proc XFindOnExtensionList*(para1: PPXExtData, para2: cint): PXExtData{.libx11.} +proc XEHeadOfExtensionList*(para1: TXEDataObject): PPXExtData{.libx11.} +proc XRootWindow*(para1: PDisplay, para2: cint): TWindow{.libx11.} +proc XDefaultRootWindow*(para1: PDisplay): TWindow{.libx11.} +proc XRootWindowOfScreen*(para1: PScreen): TWindow{.libx11.} +proc XDefaultVisual*(para1: PDisplay, para2: cint): PVisual{.libx11.} +proc XDefaultVisualOfScreen*(para1: PScreen): PVisual{.libx11.} +proc XDefaultGC*(para1: PDisplay, para2: cint): TGC{.libx11.} +proc XDefaultGCOfScreen*(para1: PScreen): TGC{.libx11.} +proc XBlackPixel*(para1: PDisplay, para2: cint): culong{.libx11.} +proc XWhitePixel*(para1: PDisplay, para2: cint): culong{.libx11.} +proc XAllPlanes*(): culong{.libx11.} +proc XBlackPixelOfScreen*(para1: PScreen): culong{.libx11.} +proc XWhitePixelOfScreen*(para1: PScreen): culong{.libx11.} +proc XNextRequest*(para1: PDisplay): culong{.libx11.} +proc XLastKnownRequestProcessed*(para1: PDisplay): culong{.libx11.} +proc XServerVendor*(para1: PDisplay): cstring{.libx11.} +proc XDisplayString*(para1: PDisplay): cstring{.libx11.} +proc XDefaultColormap*(para1: PDisplay, para2: cint): TColormap{.libx11.} +proc XDefaultColormapOfScreen*(para1: PScreen): TColormap{.libx11.} +proc XDisplayOfScreen*(para1: PScreen): PDisplay{.libx11.} +proc XScreenOfDisplay*(para1: PDisplay, para2: cint): PScreen{.libx11.} +proc XDefaultScreenOfDisplay*(para1: PDisplay): PScreen{.libx11.} +proc XEventMaskOfScreen*(para1: PScreen): clong{.libx11.} +proc XScreenNumberOfScreen*(para1: PScreen): cint{.libx11.} +type + TXErrorHandler* = proc (para1: PDisplay, para2: PXErrorEvent): cint{.cdecl.} + +proc XSetErrorHandler*(para1: TXErrorHandler): TXErrorHandler{.libx11.} +type + TXIOErrorHandler* = proc (para1: PDisplay): cint{.cdecl.} + +proc XSetIOErrorHandler*(para1: TXIOErrorHandler): TXIOErrorHandler{.libx11.} +proc XListPixmapFormats*(para1: PDisplay, para2: Pcint): PXPixmapFormatValues{. + libx11.} +proc XListDepths*(para1: PDisplay, para2: cint, para3: Pcint): Pcint{.libx11.} +proc XReconfigureWMWindow*(para1: PDisplay, para2: TWindow, para3: cint, + para4: cuint, para5: PXWindowChanges): TStatus{. + libx11.} +proc XGetWMProtocols*(para1: PDisplay, para2: TWindow, para3: PPAtom, + para4: Pcint): TStatus{.libx11.} +proc XSetWMProtocols*(para1: PDisplay, para2: TWindow, para3: PAtom, para4: cint): TStatus{. + libx11.} +proc XIconifyWindow*(para1: PDisplay, para2: TWindow, para3: cint): TStatus{. + libx11.} +proc XWithdrawWindow*(para1: PDisplay, para2: TWindow, para3: cint): TStatus{. + libx11.} +proc XGetCommand*(para1: PDisplay, para2: TWindow, para3: PPPchar, para4: Pcint): TStatus{. + libx11.} +proc XGetWMColormapWindows*(para1: PDisplay, para2: TWindow, para3: PPWindow, + para4: Pcint): TStatus{.libx11.} +proc XSetWMColormapWindows*(para1: PDisplay, para2: TWindow, para3: PWindow, + para4: cint): TStatus{.libx11.} +proc XFreeStringList*(para1: PPchar){.libx11.} +proc XSetTransientForHint*(para1: PDisplay, para2: TWindow, para3: TWindow): cint{. + libx11.} +proc XActivateScreenSaver*(para1: PDisplay): cint{.libx11.} +proc XAddHost*(para1: PDisplay, para2: PXHostAddress): cint{.libx11.} +proc XAddHosts*(para1: PDisplay, para2: PXHostAddress, para3: cint): cint{. + libx11.} +proc XAddToExtensionList*(para1: PPXExtData, para2: PXExtData): cint{.libx11.} +proc XAddToSaveSet*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XAllocColor*(para1: PDisplay, para2: TColormap, para3: PXColor): TStatus{. + libx11.} +proc XAllocColorCells*(para1: PDisplay, para2: TColormap, para3: TBool, + para4: Pculong, para5: cuint, para6: Pculong, + para7: cuint): TStatus{.libx11.} +proc XAllocColorPlanes*(para1: PDisplay, para2: TColormap, para3: TBool, + para4: Pculong, para5: cint, para6: cint, para7: cint, + para8: cint, para9: Pculong, para10: Pculong, + para11: Pculong): TStatus{.libx11.} +proc XAllocNamedColor*(para1: PDisplay, para2: TColormap, para3: cstring, + para4: PXColor, para5: PXColor): TStatus{.libx11.} +proc XAllowEvents*(para1: PDisplay, para2: cint, para3: TTime): cint{.libx11.} +proc XAutoRepeatOff*(para1: PDisplay): cint{.libx11.} +proc XAutoRepeatOn*(para1: PDisplay): cint{.libx11.} +proc XBell*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XBitmapBitOrder*(para1: PDisplay): cint{.libx11.} +proc XBitmapPad*(para1: PDisplay): cint{.libx11.} +proc XBitmapUnit*(para1: PDisplay): cint{.libx11.} +proc XCellsOfScreen*(para1: PScreen): cint{.libx11.} +proc XChangeActivePointerGrab*(para1: PDisplay, para2: cuint, para3: TCursor, + para4: TTime): cint{.libx11.} +proc XChangeGC*(para1: PDisplay, para2: TGC, para3: culong, para4: PXGCValues): cint{. + libx11.} +proc XChangeKeyboardControl*(para1: PDisplay, para2: culong, + para3: PXKeyboardControl): cint{.libx11.} +proc XChangeKeyboardMapping*(para1: PDisplay, para2: cint, para3: cint, + para4: PKeySym, para5: cint): cint{.libx11.} +proc XChangePointerControl*(para1: PDisplay, para2: TBool, para3: TBool, + para4: cint, para5: cint, para6: cint): cint{.libx11.} +proc XChangeProperty*(para1: PDisplay, para2: TWindow, para3: TAtom, + para4: TAtom, para5: cint, para6: cint, para7: Pcuchar, + para8: cint): cint{.libx11.} +proc XChangeSaveSet*(para1: PDisplay, para2: TWindow, para3: cint): cint{.libx11.} +proc XChangeWindowAttributes*(para1: PDisplay, para2: TWindow, para3: culong, + para4: PXSetWindowAttributes): cint{.libx11.} +proc XCheckIfEvent*(para1: PDisplay, para2: PXEvent, para3: funcifevent, + para4: TXPointer): TBool{.libx11.} +proc XCheckMaskEvent*(para1: PDisplay, para2: clong, para3: PXEvent): TBool{. + libx11.} +proc XCheckTypedEvent*(para1: PDisplay, para2: cint, para3: PXEvent): TBool{. + libx11.} +proc XCheckTypedWindowEvent*(para1: PDisplay, para2: TWindow, para3: cint, + para4: PXEvent): TBool{.libx11.} +proc XCheckWindowEvent*(para1: PDisplay, para2: TWindow, para3: clong, + para4: PXEvent): TBool{.libx11.} +proc XCirculateSubwindows*(para1: PDisplay, para2: TWindow, para3: cint): cint{. + libx11.} +proc XCirculateSubwindowsDown*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XCirculateSubwindowsUp*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XClearArea*(para1: PDisplay, para2: TWindow, para3: cint, para4: cint, + para5: cuint, para6: cuint, para7: TBool): cint{.libx11.} +proc XClearWindow*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XCloseDisplay*(para1: PDisplay): cint{.libx11.} +proc XConfigureWindow*(para1: PDisplay, para2: TWindow, para3: cuint, + para4: PXWindowChanges): cint{.libx11.} +proc XConnectionNumber*(para1: PDisplay): cint{.libx11.} +proc XConvertSelection*(para1: PDisplay, para2: TAtom, para3: TAtom, + para4: TAtom, para5: TWindow, para6: TTime): cint{. + libx11.} +proc XCopyArea*(para1: PDisplay, para2: TDrawable, para3: TDrawable, para4: TGC, + para5: cint, para6: cint, para7: cuint, para8: cuint, + para9: cint, para10: cint): cint{.libx11.} +proc XCopyGC*(para1: PDisplay, para2: TGC, para3: culong, para4: TGC): cint{. + libx11.} +proc XCopyPlane*(para1: PDisplay, para2: TDrawable, para3: TDrawable, + para4: TGC, para5: cint, para6: cint, para7: cuint, + para8: cuint, para9: cint, para10: cint, para11: culong): cint{. + libx11.} +proc XDefaultDepth*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XDefaultDepthOfScreen*(para1: PScreen): cint{.libx11.} +proc XDefaultScreen*(para1: PDisplay): cint{.libx11.} +proc XDefineCursor*(para1: PDisplay, para2: TWindow, para3: TCursor): cint{. + libx11.} +proc XDeleteProperty*(para1: PDisplay, para2: TWindow, para3: TAtom): cint{. + libx11.} +proc XDestroyWindow*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XDestroySubwindows*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XDoesBackingStore*(para1: PScreen): cint{.libx11.} +proc XDoesSaveUnders*(para1: PScreen): TBool{.libx11.} +proc XDisableAccessControl*(para1: PDisplay): cint{.libx11.} +proc XDisplayCells*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XDisplayHeight*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XDisplayHeightMM*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XDisplayKeycodes*(para1: PDisplay, para2: Pcint, para3: Pcint): cint{. + libx11.} +proc XDisplayPlanes*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XDisplayWidth*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XDisplayWidthMM*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XDrawArc*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: cuint, para7: cuint, para8: cint, para9: cint): cint{. + libx11.} +proc XDrawArcs*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: PXArc, + para5: cint): cint{.libx11.} +proc XDrawImageString*(para1: PDisplay, para2: TDrawable, para3: TGC, + para4: cint, para5: cint, para6: cstring, para7: cint): cint{. + libx11.} +proc XDrawImageString16*(para1: PDisplay, para2: TDrawable, para3: TGC, + para4: cint, para5: cint, para6: PXChar2b, para7: cint): cint{. + libx11.} +proc XDrawLine*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: cint, para7: cint): cint{.libx11.} +proc XDrawLines*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: PXPoint, + para5: cint, para6: cint): cint{.libx11.} +proc XDrawPoint*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint): cint{.libx11.} +proc XDrawPoints*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: PXPoint, + para5: cint, para6: cint): cint{.libx11.} +proc XDrawRectangle*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: cuint, para7: cuint): cint{.libx11.} +proc XDrawRectangles*(para1: PDisplay, para2: TDrawable, para3: TGC, + para4: PXRectangle, para5: cint): cint{.libx11.} +proc XDrawSegments*(para1: PDisplay, para2: TDrawable, para3: TGC, + para4: PXSegment, para5: cint): cint{.libx11.} +proc XDrawString*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: cstring, para7: cint): cint{.libx11.} +proc XDrawString16*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: PXChar2b, para7: cint): cint{.libx11.} +proc XDrawText*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: PXTextItem, para7: cint): cint{.libx11.} +proc XDrawText16*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: PXTextItem16, para7: cint): cint{.libx11.} +proc XEnableAccessControl*(para1: PDisplay): cint{.libx11.} +proc XEventsQueued*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XFetchName*(para1: PDisplay, para2: TWindow, para3: PPchar): TStatus{. + libx11.} +proc XFillArc*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: cuint, para7: cuint, para8: cint, para9: cint): cint{. + libx11.} +proc XFillArcs*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: PXArc, + para5: cint): cint{.libx11.} +proc XFillPolygon*(para1: PDisplay, para2: TDrawable, para3: TGC, + para4: PXPoint, para5: cint, para6: cint, para7: cint): cint{. + libx11.} +proc XFillRectangle*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: cuint, para7: cuint): cint{.libx11.} +proc XFillRectangles*(para1: PDisplay, para2: TDrawable, para3: TGC, + para4: PXRectangle, para5: cint): cint{.libx11.} +proc XFlush*(para1: PDisplay): cint{.libx11.} +proc XForceScreenSaver*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XFree*(para1: pointer): cint{.libx11.} +proc XFreeColormap*(para1: PDisplay, para2: TColormap): cint{.libx11.} +proc XFreeColors*(para1: PDisplay, para2: TColormap, para3: Pculong, + para4: cint, para5: culong): cint{.libx11.} +proc XFreeCursor*(para1: PDisplay, para2: TCursor): cint{.libx11.} +proc XFreeExtensionList*(para1: PPchar): cint{.libx11.} +proc XFreeFont*(para1: PDisplay, para2: PXFontStruct): cint{.libx11.} +proc XFreeFontInfo*(para1: PPchar, para2: PXFontStruct, para3: cint): cint{. + libx11.} +proc XFreeFontNames*(para1: PPchar): cint{.libx11.} +proc XFreeFontPath*(para1: PPchar): cint{.libx11.} +proc XFreeGC*(para1: PDisplay, para2: TGC): cint{.libx11.} +proc XFreeModifiermap*(para1: PXModifierKeymap): cint{.libx11.} +proc XFreePixmap*(para1: PDisplay, para2: TPixmap): cint{.libx11.} +proc XGeometry*(para1: PDisplay, para2: cint, para3: cstring, para4: cstring, + para5: cuint, para6: cuint, para7: cuint, para8: cint, + para9: cint, para10: Pcint, para11: Pcint, para12: Pcint, + para13: Pcint): cint{.libx11.} +proc XGetErrorDatabaseText*(para1: PDisplay, para2: cstring, para3: cstring, + para4: cstring, para5: cstring, para6: cint): cint{. + libx11.} +proc XGetErrorText*(para1: PDisplay, para2: cint, para3: cstring, para4: cint): cint{. + libx11.} +proc XGetFontProperty*(para1: PXFontStruct, para2: TAtom, para3: Pculong): TBool{. + libx11.} +proc XGetGCValues*(para1: PDisplay, para2: TGC, para3: culong, para4: PXGCValues): TStatus{. + libx11.} +proc XGetGeometry*(para1: PDisplay, para2: TDrawable, para3: PWindow, + para4: Pcint, para5: Pcint, para6: Pcuint, para7: Pcuint, + para8: Pcuint, para9: Pcuint): TStatus{.libx11.} +proc XGetIconName*(para1: PDisplay, para2: TWindow, para3: PPchar): TStatus{. + libx11.} +proc XGetInputFocus*(para1: PDisplay, para2: PWindow, para3: Pcint): cint{. + libx11.} +proc XGetKeyboardControl*(para1: PDisplay, para2: PXKeyboardState): cint{.libx11.} +proc XGetPointerControl*(para1: PDisplay, para2: Pcint, para3: Pcint, + para4: Pcint): cint{.libx11.} +proc XGetPointerMapping*(para1: PDisplay, para2: Pcuchar, para3: cint): cint{. + libx11.} +proc XGetScreenSaver*(para1: PDisplay, para2: Pcint, para3: Pcint, para4: Pcint, + para5: Pcint): cint{.libx11.} +proc XGetTransientForHint*(para1: PDisplay, para2: TWindow, para3: PWindow): TStatus{. + libx11.} +proc XGetWindowProperty*(para1: PDisplay, para2: TWindow, para3: TAtom, + para4: clong, para5: clong, para6: TBool, para7: TAtom, + para8: PAtom, para9: Pcint, para10: Pculong, + para11: Pculong, para12: PPcuchar): cint{.libx11.} +proc XGetWindowAttributes*(para1: PDisplay, para2: TWindow, + para3: PXWindowAttributes): TStatus{.libx11.} +proc XGrabButton*(para1: PDisplay, para2: cuint, para3: cuint, para4: TWindow, + para5: TBool, para6: cuint, para7: cint, para8: cint, + para9: TWindow, para10: TCursor): cint{.libx11.} +proc XGrabKey*(para1: PDisplay, para2: cint, para3: cuint, para4: TWindow, + para5: TBool, para6: cint, para7: cint): cint{.libx11.} +proc XGrabKeyboard*(para1: PDisplay, para2: TWindow, para3: TBool, para4: cint, + para5: cint, para6: TTime): cint{.libx11.} +proc XGrabPointer*(para1: PDisplay, para2: TWindow, para3: TBool, para4: cuint, + para5: cint, para6: cint, para7: TWindow, para8: TCursor, + para9: TTime): cint{.libx11.} +proc XGrabServer*(para1: PDisplay): cint{.libx11.} +proc XHeightMMOfScreen*(para1: PScreen): cint{.libx11.} +proc XHeightOfScreen*(para1: PScreen): cint{.libx11.} +proc XIfEvent*(para1: PDisplay, para2: PXEvent, para3: funcifevent, + para4: TXPointer): cint{.libx11.} +proc XImageByteOrder*(para1: PDisplay): cint{.libx11.} +proc XInstallColormap*(para1: PDisplay, para2: TColormap): cint{.libx11.} +proc XKeysymToKeycode*(para1: PDisplay, para2: TKeySym): TKeyCode{.libx11.} +proc XKillClient*(para1: PDisplay, para2: TXID): cint{.libx11.} +proc XLookupColor*(para1: PDisplay, para2: TColormap, para3: cstring, + para4: PXColor, para5: PXColor): TStatus{.libx11.} +proc XLowerWindow*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XMapRaised*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XMapSubwindows*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XMapWindow*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XMaskEvent*(para1: PDisplay, para2: clong, para3: PXEvent): cint{.libx11.} +proc XMaxCmapsOfScreen*(para1: PScreen): cint{.libx11.} +proc XMinCmapsOfScreen*(para1: PScreen): cint{.libx11.} +proc XMoveResizeWindow*(para1: PDisplay, para2: TWindow, para3: cint, + para4: cint, para5: cuint, para6: cuint): cint{.libx11.} +proc XMoveWindow*(para1: PDisplay, para2: TWindow, para3: cint, para4: cint): cint{. + libx11.} +proc XNextEvent*(para1: PDisplay, para2: PXEvent): cint{.libx11.} +proc XNoOp*(para1: PDisplay): cint{.libx11.} +proc XParseColor*(para1: PDisplay, para2: TColormap, para3: cstring, + para4: PXColor): TStatus{.libx11.} +proc XParseGeometry*(para1: cstring, para2: Pcint, para3: Pcint, para4: Pcuint, + para5: Pcuint): cint{.libx11.} +proc XPeekEvent*(para1: PDisplay, para2: PXEvent): cint{.libx11.} +proc XPeekIfEvent*(para1: PDisplay, para2: PXEvent, para3: funcifevent, + para4: TXPointer): cint{.libx11.} +proc XPending*(para1: PDisplay): cint{.libx11.} +proc XPlanesOfScreen*(para1: PScreen): cint{.libx11.} +proc XProtocolRevision*(para1: PDisplay): cint{.libx11.} +proc XProtocolVersion*(para1: PDisplay): cint{.libx11.} +proc XPutBackEvent*(para1: PDisplay, para2: PXEvent): cint{.libx11.} +proc XPutImage*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: PXImage, + para5: cint, para6: cint, para7: cint, para8: cint, + para9: cuint, para10: cuint): cint{.libx11.} +proc XQLength*(para1: PDisplay): cint{.libx11.} +proc XQueryBestCursor*(para1: PDisplay, para2: TDrawable, para3: cuint, + para4: cuint, para5: Pcuint, para6: Pcuint): TStatus{. + libx11.} +proc XQueryBestSize*(para1: PDisplay, para2: cint, para3: TDrawable, + para4: cuint, para5: cuint, para6: Pcuint, para7: Pcuint): TStatus{. + libx11.} +proc XQueryBestStipple*(para1: PDisplay, para2: TDrawable, para3: cuint, + para4: cuint, para5: Pcuint, para6: Pcuint): TStatus{. + libx11.} +proc XQueryBestTile*(para1: PDisplay, para2: TDrawable, para3: cuint, + para4: cuint, para5: Pcuint, para6: Pcuint): TStatus{. + libx11.} +proc XQueryColor*(para1: PDisplay, para2: TColormap, para3: PXColor): cint{. + libx11.} +proc XQueryColors*(para1: PDisplay, para2: TColormap, para3: PXColor, + para4: cint): cint{.libx11.} +proc XQueryExtension*(para1: PDisplay, para2: cstring, para3: Pcint, + para4: Pcint, para5: Pcint): TBool{.libx11.} + #? +proc XQueryKeymap*(para1: PDisplay, para2: chararr32): cint{.libx11.} +proc XQueryPointer*(para1: PDisplay, para2: TWindow, para3: PWindow, + para4: PWindow, para5: Pcint, para6: Pcint, para7: Pcint, + para8: Pcint, para9: Pcuint): TBool{.libx11.} +proc XQueryTextExtents*(para1: PDisplay, para2: TXID, para3: cstring, + para4: cint, para5: Pcint, para6: Pcint, para7: Pcint, + para8: PXCharStruct): cint{.libx11.} +proc XQueryTextExtents16*(para1: PDisplay, para2: TXID, para3: PXChar2b, + para4: cint, para5: Pcint, para6: Pcint, para7: Pcint, + para8: PXCharStruct): cint{.libx11.} +proc XQueryTree*(para1: PDisplay, para2: TWindow, para3: PWindow, + para4: PWindow, para5: PPWindow, para6: Pcuint): TStatus{. + libx11.} +proc XRaiseWindow*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XReadBitmapFile*(para1: PDisplay, para2: TDrawable, para3: cstring, + para4: Pcuint, para5: Pcuint, para6: PPixmap, + para7: Pcint, para8: Pcint): cint{.libx11.} +proc XReadBitmapFileData*(para1: cstring, para2: Pcuint, para3: Pcuint, + para4: PPcuchar, para5: Pcint, para6: Pcint): cint{. + libx11.} +proc XRebindKeysym*(para1: PDisplay, para2: TKeySym, para3: PKeySym, + para4: cint, para5: Pcuchar, para6: cint): cint{.libx11.} +proc XRecolorCursor*(para1: PDisplay, para2: TCursor, para3: PXColor, + para4: PXColor): cint{.libx11.} +proc XRefreshKeyboardMapping*(para1: PXMappingEvent): cint{.libx11.} +proc XRemoveFromSaveSet*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XRemoveHost*(para1: PDisplay, para2: PXHostAddress): cint{.libx11.} +proc XRemoveHosts*(para1: PDisplay, para2: PXHostAddress, para3: cint): cint{. + libx11.} +proc XReparentWindow*(para1: PDisplay, para2: TWindow, para3: TWindow, + para4: cint, para5: cint): cint{.libx11.} +proc XResetScreenSaver*(para1: PDisplay): cint{.libx11.} +proc XResizeWindow*(para1: PDisplay, para2: TWindow, para3: cuint, para4: cuint): cint{. + libx11.} +proc XRestackWindows*(para1: PDisplay, para2: PWindow, para3: cint): cint{. + libx11.} +proc XRotateBuffers*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XRotateWindowProperties*(para1: PDisplay, para2: TWindow, para3: PAtom, + para4: cint, para5: cint): cint{.libx11.} +proc XScreenCount*(para1: PDisplay): cint{.libx11.} +proc XSelectInput*(para1: PDisplay, para2: TWindow, para3: clong): cint{.libx11.} +proc XSendEvent*(para1: PDisplay, para2: TWindow, para3: TBool, para4: clong, + para5: PXEvent): TStatus{.libx11.} +proc XSetAccessControl*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XSetArcMode*(para1: PDisplay, para2: TGC, para3: cint): cint{.libx11.} +proc XSetBackground*(para1: PDisplay, para2: TGC, para3: culong): cint{.libx11.} +proc XSetClipMask*(para1: PDisplay, para2: TGC, para3: TPixmap): cint{.libx11.} +proc XSetClipOrigin*(para1: PDisplay, para2: TGC, para3: cint, para4: cint): cint{. + libx11.} +proc XSetClipRectangles*(para1: PDisplay, para2: TGC, para3: cint, para4: cint, + para5: PXRectangle, para6: cint, para7: cint): cint{. + libx11.} +proc XSetCloseDownMode*(para1: PDisplay, para2: cint): cint{.libx11.} +proc XSetCommand*(para1: PDisplay, para2: TWindow, para3: PPchar, para4: cint): cint{. + libx11.} +proc XSetDashes*(para1: PDisplay, para2: TGC, para3: cint, para4: cstring, + para5: cint): cint{.libx11.} +proc XSetFillRule*(para1: PDisplay, para2: TGC, para3: cint): cint{.libx11.} +proc XSetFillStyle*(para1: PDisplay, para2: TGC, para3: cint): cint{.libx11.} +proc XSetFont*(para1: PDisplay, para2: TGC, para3: TFont): cint{.libx11.} +proc XSetFontPath*(para1: PDisplay, para2: PPchar, para3: cint): cint{.libx11.} +proc XSetForeground*(para1: PDisplay, para2: TGC, para3: culong): cint{.libx11.} +proc XSetFunction*(para1: PDisplay, para2: TGC, para3: cint): cint{.libx11.} +proc XSetGraphicsExposures*(para1: PDisplay, para2: TGC, para3: TBool): cint{. + libx11.} +proc XSetIconName*(para1: PDisplay, para2: TWindow, para3: cstring): cint{. + libx11.} +proc XSetInputFocus*(para1: PDisplay, para2: TWindow, para3: cint, para4: TTime): cint{. + libx11.} +proc XSetLineAttributes*(para1: PDisplay, para2: TGC, para3: cuint, para4: cint, + para5: cint, para6: cint): cint{.libx11.} +proc XSetModifierMapping*(para1: PDisplay, para2: PXModifierKeymap): cint{. + libx11.} +proc XSetPlaneMask*(para1: PDisplay, para2: TGC, para3: culong): cint{.libx11.} +proc XSetPointerMapping*(para1: PDisplay, para2: Pcuchar, para3: cint): cint{. + libx11.} +proc XSetScreenSaver*(para1: PDisplay, para2: cint, para3: cint, para4: cint, + para5: cint): cint{.libx11.} +proc XSetSelectionOwner*(para1: PDisplay, para2: TAtom, para3: TWindow, + para4: TTime): cint{.libx11.} +proc XSetState*(para1: PDisplay, para2: TGC, para3: culong, para4: culong, + para5: cint, para6: culong): cint{.libx11.} +proc XSetStipple*(para1: PDisplay, para2: TGC, para3: TPixmap): cint{.libx11.} +proc XSetSubwindowMode*(para1: PDisplay, para2: TGC, para3: cint): cint{.libx11.} +proc XSetTSOrigin*(para1: PDisplay, para2: TGC, para3: cint, para4: cint): cint{. + libx11.} +proc XSetTile*(para1: PDisplay, para2: TGC, para3: TPixmap): cint{.libx11.} +proc XSetWindowBackground*(para1: PDisplay, para2: TWindow, para3: culong): cint{. + libx11.} +proc XSetWindowBackgroundPixmap*(para1: PDisplay, para2: TWindow, para3: TPixmap): cint{. + libx11.} +proc XSetWindowBorder*(para1: PDisplay, para2: TWindow, para3: culong): cint{. + libx11.} +proc XSetWindowBorderPixmap*(para1: PDisplay, para2: TWindow, para3: TPixmap): cint{. + libx11.} +proc XSetWindowBorderWidth*(para1: PDisplay, para2: TWindow, para3: cuint): cint{. + libx11.} +proc XSetWindowColormap*(para1: PDisplay, para2: TWindow, para3: TColormap): cint{. + libx11.} +proc XStoreBuffer*(para1: PDisplay, para2: cstring, para3: cint, para4: cint): cint{. + libx11.} +proc XStoreBytes*(para1: PDisplay, para2: cstring, para3: cint): cint{.libx11.} +proc XStoreColor*(para1: PDisplay, para2: TColormap, para3: PXColor): cint{. + libx11.} +proc XStoreColors*(para1: PDisplay, para2: TColormap, para3: PXColor, + para4: cint): cint{.libx11.} +proc XStoreName*(para1: PDisplay, para2: TWindow, para3: cstring): cint{.libx11.} +proc XStoreNamedColor*(para1: PDisplay, para2: TColormap, para3: cstring, + para4: culong, para5: cint): cint{.libx11.} +proc XSync*(para1: PDisplay, para2: TBool): cint{.libx11.} +proc XTextExtents*(para1: PXFontStruct, para2: cstring, para3: cint, + para4: Pcint, para5: Pcint, para6: Pcint, para7: PXCharStruct): cint{. + libx11.} +proc XTextExtents16*(para1: PXFontStruct, para2: PXChar2b, para3: cint, + para4: Pcint, para5: Pcint, para6: Pcint, + para7: PXCharStruct): cint{.libx11.} +proc XTextWidth*(para1: PXFontStruct, para2: cstring, para3: cint): cint{.libx11.} +proc XTextWidth16*(para1: PXFontStruct, para2: PXChar2b, para3: cint): cint{. + libx11.} +proc XTranslateCoordinates*(para1: PDisplay, para2: TWindow, para3: TWindow, + para4: cint, para5: cint, para6: Pcint, + para7: Pcint, para8: PWindow): TBool{.libx11.} +proc XUndefineCursor*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XUngrabButton*(para1: PDisplay, para2: cuint, para3: cuint, para4: TWindow): cint{. + libx11.} +proc XUngrabKey*(para1: PDisplay, para2: cint, para3: cuint, para4: TWindow): cint{. + libx11.} +proc XUngrabKeyboard*(para1: PDisplay, para2: TTime): cint{.libx11.} +proc XUngrabPointer*(para1: PDisplay, para2: TTime): cint{.libx11.} +proc XUngrabServer*(para1: PDisplay): cint{.libx11.} +proc XUninstallColormap*(para1: PDisplay, para2: TColormap): cint{.libx11.} +proc XUnloadFont*(para1: PDisplay, para2: TFont): cint{.libx11.} +proc XUnmapSubwindows*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XUnmapWindow*(para1: PDisplay, para2: TWindow): cint{.libx11.} +proc XVendorRelease*(para1: PDisplay): cint{.libx11.} +proc XWarpPointer*(para1: PDisplay, para2: TWindow, para3: TWindow, para4: cint, + para5: cint, para6: cuint, para7: cuint, para8: cint, + para9: cint): cint{.libx11.} +proc XWidthMMOfScreen*(para1: PScreen): cint{.libx11.} +proc XWidthOfScreen*(para1: PScreen): cint{.libx11.} +proc XWindowEvent*(para1: PDisplay, para2: TWindow, para3: clong, para4: PXEvent): cint{. + libx11.} +proc XWriteBitmapFile*(para1: PDisplay, para2: cstring, para3: TPixmap, + para4: cuint, para5: cuint, para6: cint, para7: cint): cint{. + libx11.} +proc XSupportsLocale*(): TBool{.libx11.} +proc XSetLocaleModifiers*(para1: cstring): cstring{.libx11.} +proc XOpenOM*(para1: PDisplay, para2: PXrmHashBucketRec, para3: cstring, + para4: cstring): TXOM{.libx11.} +proc XCloseOM*(para1: TXOM): TStatus{.libx11.} +proc XSetOMValues*(para1: TXOM): cstring{.varargs, libx11.} +proc XGetOMValues*(para1: TXOM): cstring{.varargs, libx11.} +proc XDisplayOfOM*(para1: TXOM): PDisplay{.libx11.} +proc XLocaleOfOM*(para1: TXOM): cstring{.libx11.} +proc XCreateOC*(para1: TXOM): TXOC{.varargs, libx11.} +proc XDestroyOC*(para1: TXOC){.libx11.} +proc XOMOfOC*(para1: TXOC): TXOM{.libx11.} +proc XSetOCValues*(para1: TXOC): cstring{.varargs, libx11.} +proc XGetOCValues*(para1: TXOC): cstring{.varargs, libx11.} +proc XCreateFontSet*(para1: PDisplay, para2: cstring, para3: PPPchar, + para4: Pcint, para5: PPchar): TXFontSet{.libx11.} +proc XFreeFontSet*(para1: PDisplay, para2: TXFontSet){.libx11.} +proc XFontsOfFontSet*(para1: TXFontSet, para2: PPPXFontStruct, para3: PPPchar): cint{. + libx11.} +proc XBaseFontNameListOfFontSet*(para1: TXFontSet): cstring{.libx11.} +proc XLocaleOfFontSet*(para1: TXFontSet): cstring{.libx11.} +proc XContextDependentDrawing*(para1: TXFontSet): TBool{.libx11.} +proc XDirectionalDependentDrawing*(para1: TXFontSet): TBool{.libx11.} +proc XContextualDrawing*(para1: TXFontSet): TBool{.libx11.} +proc XExtentsOfFontSet*(para1: TXFontSet): PXFontSetExtents{.libx11.} +proc XmbTextEscapement*(para1: TXFontSet, para2: cstring, para3: cint): cint{. + libx11.} +proc XwcTextEscapement*(para1: TXFontSet, para2: PWideChar, para3: cint): cint{. + libx11.} +proc Xutf8TextEscapement*(para1: TXFontSet, para2: cstring, para3: cint): cint{. + libx11.} +proc XmbTextExtents*(para1: TXFontSet, para2: cstring, para3: cint, + para4: PXRectangle, para5: PXRectangle): cint{.libx11.} +proc XwcTextExtents*(para1: TXFontSet, para2: PWideChar, para3: cint, + para4: PXRectangle, para5: PXRectangle): cint{.libx11.} +proc Xutf8TextExtents*(para1: TXFontSet, para2: cstring, para3: cint, + para4: PXRectangle, para5: PXRectangle): cint{.libx11.} +proc XmbTextPerCharExtents*(para1: TXFontSet, para2: cstring, para3: cint, + para4: PXRectangle, para5: PXRectangle, para6: cint, + para7: Pcint, para8: PXRectangle, para9: PXRectangle): TStatus{. + libx11.} +proc XwcTextPerCharExtents*(para1: TXFontSet, para2: PWideChar, para3: cint, + para4: PXRectangle, para5: PXRectangle, para6: cint, + para7: Pcint, para8: PXRectangle, para9: PXRectangle): TStatus{. + libx11.} +proc Xutf8TextPerCharExtents*(para1: TXFontSet, para2: cstring, para3: cint, + para4: PXRectangle, para5: PXRectangle, + para6: cint, para7: Pcint, para8: PXRectangle, + para9: PXRectangle): TStatus{.libx11.} +proc XmbDrawText*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: PXmbTextItem, para7: cint){.libx11.} +proc XwcDrawText*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: PXwcTextItem, para7: cint){.libx11.} +proc Xutf8DrawText*(para1: PDisplay, para2: TDrawable, para3: TGC, para4: cint, + para5: cint, para6: PXmbTextItem, para7: cint){.libx11.} +proc XmbDrawString*(para1: PDisplay, para2: TDrawable, para3: TXFontSet, + para4: TGC, para5: cint, para6: cint, para7: cstring, + para8: cint){.libx11.} +proc XwcDrawString*(para1: PDisplay, para2: TDrawable, para3: TXFontSet, + para4: TGC, para5: cint, para6: cint, para7: PWideChar, + para8: cint){.libx11.} +proc Xutf8DrawString*(para1: PDisplay, para2: TDrawable, para3: TXFontSet, + para4: TGC, para5: cint, para6: cint, para7: cstring, + para8: cint){.libx11.} +proc XmbDrawImageString*(para1: PDisplay, para2: TDrawable, para3: TXFontSet, + para4: TGC, para5: cint, para6: cint, para7: cstring, + para8: cint){.libx11.} +proc XwcDrawImageString*(para1: PDisplay, para2: TDrawable, para3: TXFontSet, + para4: TGC, para5: cint, para6: cint, para7: PWideChar, + para8: cint){.libx11.} +proc Xutf8DrawImageString*(para1: PDisplay, para2: TDrawable, para3: TXFontSet, + para4: TGC, para5: cint, para6: cint, para7: cstring, + para8: cint){.libx11.} +proc XOpenIM*(para1: PDisplay, para2: PXrmHashBucketRec, para3: cstring, + para4: cstring): TXIM{.libx11.} +proc XCloseIM*(para1: TXIM): TStatus{.libx11.} +proc XGetIMValues*(para1: TXIM): cstring{.varargs, libx11.} +proc XSetIMValues*(para1: TXIM): cstring{.varargs, libx11.} +proc XDisplayOfIM*(para1: TXIM): PDisplay{.libx11.} +proc XLocaleOfIM*(para1: TXIM): cstring{.libx11.} +proc XCreateIC*(para1: TXIM): TXIC{.varargs, libx11.} +proc XDestroyIC*(para1: TXIC){.libx11.} +proc XSetICFocus*(para1: TXIC){.libx11.} +proc XUnsetICFocus*(para1: TXIC){.libx11.} +proc XwcResetIC*(para1: TXIC): PWideChar{.libx11.} +proc XmbResetIC*(para1: TXIC): cstring{.libx11.} +proc Xutf8ResetIC*(para1: TXIC): cstring{.libx11.} +proc XSetICValues*(para1: TXIC): cstring{.varargs, libx11.} +proc XGetICValues*(para1: TXIC): cstring{.varargs, libx11.} +proc XIMOfIC*(para1: TXIC): TXIM{.libx11.} +proc XFilterEvent*(para1: PXEvent, para2: TWindow): TBool{.libx11.} +proc XmbLookupString*(para1: TXIC, para2: PXKeyPressedEvent, para3: cstring, + para4: cint, para5: PKeySym, para6: PStatus): cint{.libx11.} +proc XwcLookupString*(para1: TXIC, para2: PXKeyPressedEvent, para3: PWideChar, + para4: cint, para5: PKeySym, para6: PStatus): cint{.libx11.} +proc Xutf8LookupString*(para1: TXIC, para2: PXKeyPressedEvent, para3: cstring, + para4: cint, para5: PKeySym, para6: PStatus): cint{. + libx11.} +proc XVaCreateNestedList*(unused: cint): TXVaNestedList{.varargs, libx11.} +proc XRegisterIMInstantiateCallback*(para1: PDisplay, para2: PXrmHashBucketRec, + para3: cstring, para4: cstring, + para5: TXIDProc, para6: TXPointer): TBool{. + libx11.} +proc XUnregisterIMInstantiateCallback*(para1: PDisplay, + para2: PXrmHashBucketRec, para3: cstring, + para4: cstring, para5: TXIDProc, + para6: TXPointer): TBool{.libx11.} +type + TXConnectionWatchProc* = proc (para1: PDisplay, para2: TXPointer, para3: cint, + para4: TBool, para5: PXPointer){.cdecl.} + +proc XInternalConnectionNumbers*(para1: PDisplay, para2: PPcint, para3: Pcint): TStatus{. + libx11.} +proc XProcessInternalConnection*(para1: PDisplay, para2: cint){.libx11.} +proc XAddConnectionWatch*(para1: PDisplay, para2: TXConnectionWatchProc, + para3: TXPointer): TStatus{.libx11.} +proc XRemoveConnectionWatch*(para1: PDisplay, para2: TXConnectionWatchProc, + para3: TXPointer){.libx11.} +proc XSetAuthorization*(para1: cstring, para2: cint, para3: cstring, para4: cint){. + libx11.} + # + # _Xmbtowc? + # _Xwctomb? + # +#when defined(MACROS): +proc ConnectionNumber*(dpy: PDisplay): cint +proc RootWindow*(dpy: PDisplay, scr: cint): TWindow +proc DefaultScreen*(dpy: PDisplay): cint +proc DefaultRootWindow*(dpy: PDisplay): TWindow +proc DefaultVisual*(dpy: PDisplay, scr: cint): PVisual +proc DefaultGC*(dpy: PDisplay, scr: cint): TGC +proc BlackPixel*(dpy: PDisplay, scr: cint): culong +proc WhitePixel*(dpy: PDisplay, scr: cint): culong +proc QLength*(dpy: PDisplay): cint +proc DisplayWidth*(dpy: PDisplay, scr: cint): cint +proc DisplayHeight*(dpy: PDisplay, scr: cint): cint +proc DisplayWidthMM*(dpy: PDisplay, scr: cint): cint +proc DisplayHeightMM*(dpy: PDisplay, scr: cint): cint +proc DisplayPlanes*(dpy: PDisplay, scr: cint): cint +proc DisplayCells*(dpy: PDisplay, scr: cint): cint +proc ScreenCount*(dpy: PDisplay): cint +proc ServerVendor*(dpy: PDisplay): cstring +proc ProtocolVersion*(dpy: PDisplay): cint +proc ProtocolRevision*(dpy: PDisplay): cint +proc VendorRelease*(dpy: PDisplay): cint +proc DisplayString*(dpy: PDisplay): cstring +proc DefaultDepth*(dpy: PDisplay, scr: cint): cint +proc DefaultColormap*(dpy: PDisplay, scr: cint): TColormap +proc BitmapUnit*(dpy: PDisplay): cint +proc BitmapBitOrder*(dpy: PDisplay): cint +proc BitmapPad*(dpy: PDisplay): cint +proc ImageByteOrder*(dpy: PDisplay): cint +proc NextRequest*(dpy: PDisplay): culong +proc LastKnownRequestProcessed*(dpy: PDisplay): culong +proc ScreenOfDisplay*(dpy: PDisplay, scr: cint): PScreen +proc DefaultScreenOfDisplay*(dpy: PDisplay): PScreen +proc DisplayOfScreen*(s: PScreen): PDisplay +proc RootWindowOfScreen*(s: PScreen): TWindow +proc BlackPixelOfScreen*(s: PScreen): culong +proc WhitePixelOfScreen*(s: PScreen): culong +proc DefaultColormapOfScreen*(s: PScreen): TColormap +proc DefaultDepthOfScreen*(s: PScreen): cint +proc DefaultGCOfScreen*(s: PScreen): TGC +proc DefaultVisualOfScreen*(s: PScreen): PVisual +proc WidthOfScreen*(s: PScreen): cint +proc HeightOfScreen*(s: PScreen): cint +proc WidthMMOfScreen*(s: PScreen): cint +proc HeightMMOfScreen*(s: PScreen): cint +proc PlanesOfScreen*(s: PScreen): cint +proc CellsOfScreen*(s: PScreen): cint +proc MinCmapsOfScreen*(s: PScreen): cint +proc MaxCmapsOfScreen*(s: PScreen): cint +proc DoesSaveUnders*(s: PScreen): TBool +proc DoesBackingStore*(s: PScreen): cint +proc EventMaskOfScreen*(s: PScreen): clong +proc XAllocID*(dpy: PDisplay): TXID +# implementation + +#when defined(MACROS): +template privDisp : expr = cast[PXPrivDisplay](dpy) + +proc ConnectionNumber(dpy: PDisplay): cint = + privDisp.fd + +proc RootWindow(dpy: PDisplay, scr: cint): TWindow = + ScreenOfDisplay(dpy, scr).root + +proc DefaultScreen(dpy: PDisplay): cint = + privDisp.default_screen + +proc DefaultRootWindow(dpy: PDisplay): TWindow = + ScreenOfDisplay(dpy, DefaultScreen(dpy)).root + +proc DefaultVisual(dpy: PDisplay, scr: cint): PVisual = + ScreenOfDisplay(dpy, scr).root_visual + +proc DefaultGC(dpy: PDisplay, scr: cint): TGC = + ScreenOfDisplay(dpy, scr).default_gc + +proc BlackPixel(dpy: PDisplay, scr: cint): culong = + ScreenOfDisplay(dpy, scr).black_pixel + +proc WhitePixel(dpy: PDisplay, scr: cint): culong = + ScreenOfDisplay(dpy, scr).white_pixel + +proc QLength(dpy: PDisplay): cint = + privDisp.qlen + +proc DisplayWidth(dpy: PDisplay, scr: cint): cint = + ScreenOfDisplay(dpy, scr).width + +proc DisplayHeight(dpy: PDisplay, scr: cint): cint = + ScreenOfDisplay(dpy, scr).height + +proc DisplayWidthMM(dpy: PDisplay, scr: cint): cint = + ScreenOfDisplay(dpy, scr).mwidth + +proc DisplayHeightMM(dpy: PDisplay, scr: cint): cint = + ScreenOfDisplay(dpy, scr).mheight + +proc DisplayPlanes(dpy: PDisplay, scr: cint): cint = + ScreenOfDisplay(dpy, scr).root_depth + +proc DisplayCells(dpy: PDisplay, scr: cint): cint = + DefaultVisual(dpy, scr).map_entries + +proc ScreenCount(dpy: PDisplay): cint = + privDisp.nscreens + +proc ServerVendor(dpy: PDisplay): cstring = + privDisp.vendor + +proc ProtocolVersion(dpy: PDisplay): cint = + privDisp.proto_major_version + +proc ProtocolRevision(dpy: PDisplay): cint = + privDisp.proto_minor_version + +proc VendorRelease(dpy: PDisplay): cint = + privDisp.release + +proc DisplayString(dpy: PDisplay): cstring = + privDisp.display_name + +proc DefaultDepth(dpy: PDisplay, scr: cint): cint = + ScreenOfDisplay(dpy, scr).root_depth + +proc DefaultColormap(dpy: PDisplay, scr: cint): TColormap = + ScreenOfDisplay(dpy, scr).cmap + +proc BitmapUnit(dpy: PDisplay): cint = + privDisp.bitmap_unit + +proc BitmapBitOrder(dpy: PDisplay): cint = + privDisp.bitmap_bit_order + +proc BitmapPad(dpy: PDisplay): cint = + privDisp.bitmap_pad + +proc ImageByteOrder(dpy: PDisplay): cint = + privDisp.byte_order + +proc NextRequest(dpy: PDisplay): culong = + privDisp.request + 1.culong + +proc LastKnownRequestProcessed(dpy: PDisplay): culong = + privDisp.last_request_read + +# from fowltek/pointer_arithm, required for ScreenOfDisplay() +proc offset[A] (some: ptr A; b: int): ptr A = + cast[ptr A](cast[int](some) + (b * sizeof(A))) +proc ScreenOfDisplay(dpy: PDisplay, scr: cint): PScreen = + #addr(((privDisp.screens)[scr])) + privDisp.screens.offset(scr.int) + +proc DefaultScreenOfDisplay(dpy: PDisplay): PScreen = + ScreenOfDisplay(dpy, DefaultScreen(dpy)) + +proc DisplayOfScreen(s: PScreen): PDisplay = + s.display + +proc RootWindowOfScreen(s: PScreen): TWindow = + s.root + +proc BlackPixelOfScreen(s: PScreen): culong = + s.black_pixel + +proc WhitePixelOfScreen(s: PScreen): culong = + s.white_pixel + +proc DefaultColormapOfScreen(s: PScreen): TColormap = + s.cmap + +proc DefaultDepthOfScreen(s: PScreen): cint = + s.root_depth + +proc DefaultGCOfScreen(s: PScreen): TGC = + s.default_gc + +proc DefaultVisualOfScreen(s: PScreen): PVisual = + s.root_visual + +proc WidthOfScreen(s: PScreen): cint = + s.width + +proc HeightOfScreen(s: PScreen): cint = + s.height + +proc WidthMMOfScreen(s: PScreen): cint = + s.mwidth + +proc HeightMMOfScreen(s: PScreen): cint = + s.mheight + +proc PlanesOfScreen(s: PScreen): cint = + s.root_depth + +proc CellsOfScreen(s: PScreen): cint = + DefaultVisualOfScreen(s).map_entries + +proc MinCmapsOfScreen(s: PScreen): cint = + s.min_maps + +proc MaxCmapsOfScreen(s: PScreen): cint = + s.max_maps + +proc DoesSaveUnders(s: PScreen): TBool = + s.save_unders + +proc DoesBackingStore(s: PScreen): cint = + s.backing_store + +proc EventMaskOfScreen(s: PScreen): clong = + s.root_input_mask + +proc XAllocID(dpy: PDisplay): TXID = + privDisp.resource_alloc(dpy) diff --git a/tests/deps/x11-1.0/xrandr.nim b/tests/deps/x11-1.0/xrandr.nim new file mode 100644 index 000000000..ee6f1705b --- /dev/null +++ b/tests/deps/x11-1.0/xrandr.nim @@ -0,0 +1,194 @@ +# +# $XFree86: xc/lib/Xrandr/Xrandr.h,v 1.9 2002/09/29 23:39:44 keithp Exp $ +# +# Copyright (C) 2000 Compaq Computer Corporation, Inc. +# Copyright (C) 2002 Hewlett-Packard Company, Inc. +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of Compaq not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. HP makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# HP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL COMPAQ +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Author: Jim Gettys, HP Labs, HP. +# + +import + x, xlib + +const + libXrandr* = "libXrandr.so" + +# * $XFree86: xc/include/extensions/randr.h,v 1.4 2001/11/24 07:24:58 keithp Exp $ +# * +# * Copyright (C) 2000, Compaq Computer Corporation, +# * Copyright (C) 2002, Hewlett Packard, Inc. +# * +# * Permission to use, copy, modify, distribute, and sell this software and its +# * documentation for any purpose is hereby granted without fee, provided that +# * the above copyright notice appear in all copies and that both that +# * copyright notice and this permission notice appear in supporting +# * documentation, and that the name of Compaq or HP not be used in advertising +# * or publicity pertaining to distribution of the software without specific, +# * written prior permission. HP makes no representations about the +# * suitability of this software for any purpose. It is provided "as is" +# * without express or implied warranty. +# * +# * HP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL +# * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL HP +# * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# * +# * Author: Jim Gettys, HP Labs, Hewlett-Packard, Inc. +# * + +type + PRotation* = ptr TRotation + TRotation* = cushort + PSizeID* = ptr TSizeID + TSizeID* = cushort + PSubpixelOrder* = ptr TSubpixelOrder + TSubpixelOrder* = cushort + +const + RANDR_NAME* = "RANDR" + RANDR_MAJOR* = 1 + RANDR_MINOR* = 1 + RRNumberErrors* = 0 + RRNumberEvents* = 1 + constX_RRQueryVersion* = 0 # we skip 1 to make old clients fail pretty immediately + X_RROldGetScreenInfo* = 1 + X_RR1_0SetScreenConfig* = 2 # V1.0 apps share the same set screen config request id + constX_RRSetScreenConfig* = 2 + X_RROldScreenChangeSelectInput* = 3 # 3 used to be ScreenChangeSelectInput; deprecated + constX_RRSelectInput* = 4 + constX_RRGetScreenInfo* = 5 # used in XRRSelectInput + RRScreenChangeNotifyMask* = 1 shl 0 + RRScreenChangeNotify* = 0 # used in the rotation field; rotation and reflection in 0.1 proto. + RR_Rotate_0* = 1 + RR_Rotate_90* = 2 + RR_Rotate_180* = 4 + RR_Rotate_270* = 8 # new in 1.0 protocol, to allow reflection of screen + RR_Reflect_X* = 16 + RR_Reflect_Y* = 32 + RRSetConfigSuccess* = 0 + RRSetConfigInvalidConfigTime* = 1 + RRSetConfigInvalidTime* = 2 + RRSetConfigFailed* = 3 + +type + PXRRScreenSize* = ptr TXRRScreenSize + TXRRScreenSize*{.final.} = object # + # Events. + # + width*, height*: cint + mwidth*, mheight*: cint + + TXRRScreenChangeNotifyEvent*{.final.} = object # internal representation is private to the library + typ*: cint # event base + serial*: culong # # of last request processed by server + send_event*: TBool # true if this came from a SendEvent request + display*: PDisplay # Display the event was read from + window*: TWindow # window which selected for this event + root*: TWindow # Root window for changed screen + timestamp*: TTime # when the screen change occurred + config_timestamp*: TTime # when the last configuration change + size_index*: TSizeID + subpixel_order*: TSubpixelOrder + rotation*: TRotation + width*: cint + height*: cint + mwidth*: cint + mheight*: cint + + PXRRScreenConfiguration* = ptr TXRRScreenConfiguration + TXRRScreenConfiguration*{.final.} = object + +proc XRRQueryExtension*(dpy: PDisplay, event_basep, error_basep: Pcint): TBool{. + cdecl, dynlib: libXrandr, importc.} +proc XRRQueryVersion*(dpy: PDisplay, major_versionp: Pcint, + minor_versionp: Pcint): TStatus{.cdecl, dynlib: libXrandr, + importc.} +proc XRRGetScreenInfo*(dpy: PDisplay, draw: TDrawable): PXRRScreenConfiguration{. + cdecl, dynlib: libXrandr, importc.} +proc XRRFreeScreenConfigInfo*(config: PXRRScreenConfiguration){.cdecl, + dynlib: libXrandr, importc.} + # + # Note that screen configuration changes are only permitted if the client can + # prove it has up to date configuration information. We are trying to + # insist that it become possible for screens to change dynamically, so + # we want to ensure the client knows what it is talking about when requesting + # changes. + # +proc XRRSetScreenConfig*(dpy: PDisplay, config: PXRRScreenConfiguration, + draw: TDrawable, size_index: cint, rotation: TRotation, + timestamp: TTime): TStatus{.cdecl, dynlib: libXrandr, + importc.} + # added in v1.1, sorry for the lame name +proc XRRSetScreenConfigAndRate*(dpy: PDisplay, config: PXRRScreenConfiguration, + draw: TDrawable, size_index: cint, + rotation: TRotation, rate: cshort, + timestamp: TTime): TStatus{.cdecl, + dynlib: libXrandr, importc.} +proc XRRConfigRotations*(config: PXRRScreenConfiguration, + current_rotation: PRotation): TRotation{.cdecl, + dynlib: libXrandr, importc.} +proc XRRConfigTimes*(config: PXRRScreenConfiguration, config_timestamp: PTime): TTime{. + cdecl, dynlib: libXrandr, importc.} +proc XRRConfigSizes*(config: PXRRScreenConfiguration, nsizes: Pcint): PXRRScreenSize{. + cdecl, dynlib: libXrandr, importc.} +proc XRRConfigRates*(config: PXRRScreenConfiguration, sizeID: cint, + nrates: Pcint): ptr int16{.cdecl, dynlib: libXrandr, importc.} +proc XRRConfigCurrentConfiguration*(config: PXRRScreenConfiguration, + rotation: PRotation): TSizeID{.cdecl, + dynlib: libXrandr, importc.} +proc XRRConfigCurrentRate*(config: PXRRScreenConfiguration): cshort{.cdecl, + dynlib: libXrandr, importc.} +proc XRRRootToScreen*(dpy: PDisplay, root: TWindow): cint{.cdecl, + dynlib: libXrandr, importc.} + # + # returns the screen configuration for the specified screen; does a lazy + # evalution to delay getting the information, and caches the result. + # These routines should be used in preference to XRRGetScreenInfo + # to avoid unneeded round trips to the X server. These are new + # in protocol version 0.1. + # +proc XRRScreenConfig*(dpy: PDisplay, screen: cint): PXRRScreenConfiguration{. + cdecl, dynlib: libXrandr, importc.} +proc XRRConfig*(screen: PScreen): PXRRScreenConfiguration{.cdecl, + dynlib: libXrandr, importc.} +proc XRRSelectInput*(dpy: PDisplay, window: TWindow, mask: cint){.cdecl, + dynlib: libXrandr, importc.} + # + # the following are always safe to call, even if RandR is not implemented + # on a screen + # +proc XRRRotations*(dpy: PDisplay, screen: cint, current_rotation: PRotation): TRotation{. + cdecl, dynlib: libXrandr, importc.} +proc XRRSizes*(dpy: PDisplay, screen: cint, nsizes: Pcint): PXRRScreenSize{. + cdecl, dynlib: libXrandr, importc.} +proc XRRRates*(dpy: PDisplay, screen: cint, sizeID: cint, nrates: Pcint): ptr int16{. + cdecl, dynlib: libXrandr, importc.} +proc XRRTimes*(dpy: PDisplay, screen: cint, config_timestamp: PTime): TTime{. + cdecl, dynlib: libXrandr, importc.} + # + # intended to take RRScreenChangeNotify, or + # ConfigureNotify (on the root window) + # returns 1 if it is an event type it understands, 0 if not + # +proc XRRUpdateConfiguration*(event: PXEvent): cint{.cdecl, dynlib: libXrandr, + importc.} +# implementation diff --git a/tests/deps/x11-1.0/xrender.nim b/tests/deps/x11-1.0/xrender.nim new file mode 100644 index 000000000..c4b2b364d --- /dev/null +++ b/tests/deps/x11-1.0/xrender.nim @@ -0,0 +1,241 @@ + +import + x, xlib + +when defined(use_pkg_config) or defined(use_pkg_config_static): + {.pragma: libxrender, cdecl, importc.} + when defined(use_pkg_config): + {.passl: gorge("pkg-config xrender --libs").} + else: + {.passl: gorge("pkg-config xrender --static --libs").} +else: + when defined(macosx): + const + libXrender* = "libXrender.dylib" + else: + const + libXrender* = "libXrender.so" + + + {.pragma: libxrender, dynlib: libXrender, cdecl, importc.} +#const +# libXrender* = "libXrender.so" + +# +# Automatically converted by H2Pas 0.99.15 from xrender.h +# The following command line parameters were used: +# -p +# -T +# -S +# -d +# -c +# xrender.h +# + +type + PGlyph* = ptr TGlyph + TGlyph* = int32 + PGlyphSet* = ptr TGlyphSet + TGlyphSet* = int32 + PPicture* = ptr TPicture + TPicture* = int32 + PPictFormat* = ptr TPictFormat + TPictFormat* = int32 + +const + RENDER_NAME* = "RENDER" + RENDER_MAJOR* = 0 + RENDER_MINOR* = 0 + constX_RenderQueryVersion* = 0 + X_RenderQueryPictFormats* = 1 + X_RenderQueryPictIndexValues* = 2 + X_RenderQueryDithers* = 3 + constX_RenderCreatePicture* = 4 + constX_RenderChangePicture* = 5 + X_RenderSetPictureClipRectangles* = 6 + constX_RenderFreePicture* = 7 + constX_RenderComposite* = 8 + X_RenderScale* = 9 + X_RenderTrapezoids* = 10 + X_RenderTriangles* = 11 + X_RenderTriStrip* = 12 + X_RenderTriFan* = 13 + X_RenderColorTrapezoids* = 14 + X_RenderColorTriangles* = 15 + X_RenderTransform* = 16 + constX_RenderCreateGlyphSet* = 17 + constX_RenderReferenceGlyphSet* = 18 + constX_RenderFreeGlyphSet* = 19 + constX_RenderAddGlyphs* = 20 + constX_RenderAddGlyphsFromPicture* = 21 + constX_RenderFreeGlyphs* = 22 + constX_RenderCompositeGlyphs8* = 23 + constX_RenderCompositeGlyphs16* = 24 + constX_RenderCompositeGlyphs32* = 25 + BadPictFormat* = 0 + BadPicture* = 1 + BadPictOp* = 2 + BadGlyphSet* = 3 + BadGlyph* = 4 + RenderNumberErrors* = BadGlyph + 1 + PictTypeIndexed* = 0 + PictTypeDirect* = 1 + PictOpClear* = 0 + PictOpSrc* = 1 + PictOpDst* = 2 + PictOpOver* = 3 + PictOpOverReverse* = 4 + PictOpIn* = 5 + PictOpInReverse* = 6 + PictOpOut* = 7 + PictOpOutReverse* = 8 + PictOpAtop* = 9 + PictOpAtopReverse* = 10 + PictOpXor* = 11 + PictOpAdd* = 12 + PictOpSaturate* = 13 + PictOpMaximum* = 13 + PolyEdgeSharp* = 0 + PolyEdgeSmooth* = 1 + PolyModePrecise* = 0 + PolyModeImprecise* = 1 + CPRepeat* = 1 shl 0 + CPAlphaMap* = 1 shl 1 + CPAlphaXOrigin* = 1 shl 2 + CPAlphaYOrigin* = 1 shl 3 + CPClipXOrigin* = 1 shl 4 + CPClipYOrigin* = 1 shl 5 + CPClipMask* = 1 shl 6 + CPGraphicsExposure* = 1 shl 7 + CPSubwindowMode* = 1 shl 8 + CPPolyEdge* = 1 shl 9 + CPPolyMode* = 1 shl 10 + CPDither* = 1 shl 11 + CPLastBit* = 11 + +type + PXRenderDirectFormat* = ptr TXRenderDirectFormat + TXRenderDirectFormat*{.final.} = object + red*: int16 + redMask*: int16 + green*: int16 + greenMask*: int16 + blue*: int16 + blueMask*: int16 + alpha*: int16 + alphaMask*: int16 + + PXRenderPictFormat* = ptr TXRenderPictFormat + TXRenderPictFormat*{.final.} = object + id*: TPictFormat + thetype*: int32 + depth*: int32 + direct*: TXRenderDirectFormat + colormap*: TColormap + + +const + PictFormatID* = 1 shl 0 + PictFormatType* = 1 shl 1 + PictFormatDepth* = 1 shl 2 + PictFormatRed* = 1 shl 3 + PictFormatRedMask* = 1 shl 4 + PictFormatGreen* = 1 shl 5 + PictFormatGreenMask* = 1 shl 6 + PictFormatBlue* = 1 shl 7 + PictFormatBlueMask* = 1 shl 8 + PictFormatAlpha* = 1 shl 9 + PictFormatAlphaMask* = 1 shl 10 + PictFormatColormap* = 1 shl 11 + +type + PXRenderVisual* = ptr TXRenderVisual + TXRenderVisual*{.final.} = object + visual*: PVisual + format*: PXRenderPictFormat + + PXRenderDepth* = ptr TXRenderDepth + TXRenderDepth*{.final.} = object + depth*: int32 + nvisuals*: int32 + visuals*: PXRenderVisual + + PXRenderScreen* = ptr TXRenderScreen + TXRenderScreen*{.final.} = object + depths*: PXRenderDepth + ndepths*: int32 + fallback*: PXRenderPictFormat + + PXRenderInfo* = ptr TXRenderInfo + TXRenderInfo*{.final.} = object + format*: PXRenderPictFormat + nformat*: int32 + screen*: PXRenderScreen + nscreen*: int32 + depth*: PXRenderDepth + ndepth*: int32 + visual*: PXRenderVisual + nvisual*: int32 + + PXRenderPictureAttributes* = ptr TXRenderPictureAttributes + TXRenderPictureAttributes*{.final.} = object + repeat*: TBool + alpha_map*: TPicture + alpha_x_origin*: int32 + alpha_y_origin*: int32 + clip_x_origin*: int32 + clip_y_origin*: int32 + clip_mask*: TPixmap + graphics_exposures*: TBool + subwindow_mode*: int32 + poly_edge*: int32 + poly_mode*: int32 + dither*: TAtom + + PXGlyphInfo* = ptr TXGlyphInfo + TXGlyphInfo*{.final.} = object + width*: int16 + height*: int16 + x*: int16 + y*: int16 + xOff*: int16 + yOff*: int16 + + +proc XRenderQueryExtension*(dpy: PDisplay, event_basep: ptr int32, + error_basep: ptr int32): TBool{.libxrender.} +proc XRenderQueryVersion*(dpy: PDisplay, major_versionp: ptr int32, + minor_versionp: ptr int32): TStatus{.libxrender.} +proc XRenderQueryFormats*(dpy: PDisplay): TStatus{.libxrender.} +proc XRenderFindVisualFormat*(dpy: PDisplay, visual: PVisual): PXRenderPictFormat{. + libxrender.} +proc XRenderFindFormat*(dpy: PDisplay, mask: int32, + `template`: PXRenderPictFormat, count: int32): PXRenderPictFormat{. + libxrender.} +proc XRenderCreatePicture*(dpy: PDisplay, drawable: TDrawable, + format: PXRenderPictFormat, valuemask: int32, + attributes: PXRenderPictureAttributes): TPicture{. + libxrender.} +proc XRenderChangePicture*(dpy: PDisplay, picture: TPicture, valuemask: int32, + attributes: PXRenderPictureAttributes){.libxrender.} +proc XRenderFreePicture*(dpy: PDisplay, picture: TPicture){.libxrender.} +proc XRenderComposite*(dpy: PDisplay, op: int32, src: TPicture, mask: TPicture, + dst: TPicture, src_x: int32, src_y: int32, mask_x: int32, + mask_y: int32, dst_x: int32, dst_y: int32, width: int32, + height: int32){.libxrender.} +proc XRenderCreateGlyphSet*(dpy: PDisplay, format: PXRenderPictFormat): TGlyphSet{. + libxrender.} +proc XRenderReferenceGlyphSet*(dpy: PDisplay, existing: TGlyphSet): TGlyphSet{. + libxrender.} +proc XRenderFreeGlyphSet*(dpy: PDisplay, glyphset: TGlyphSet){.libxrender.} +proc XRenderAddGlyphs*(dpy: PDisplay, glyphset: TGlyphSet, gids: PGlyph, + glyphs: PXGlyphInfo, nglyphs: int32, images: cstring, + nbyte_images: int32){.libxrender.} +proc XRenderFreeGlyphs*(dpy: PDisplay, glyphset: TGlyphSet, gids: PGlyph, + nglyphs: int32){.libxrender.} +proc XRenderCompositeString8*(dpy: PDisplay, op: int32, src: TPicture, + dst: TPicture, maskFormat: PXRenderPictFormat, + glyphset: TGlyphSet, xSrc: int32, ySrc: int32, + xDst: int32, yDst: int32, str: cstring, + nchar: int32){.libxrender.} +# implementation diff --git a/tests/deps/x11-1.0/xresource.nim b/tests/deps/x11-1.0/xresource.nim new file mode 100644 index 000000000..c96fbbb52 --- /dev/null +++ b/tests/deps/x11-1.0/xresource.nim @@ -0,0 +1,201 @@ + +import + x, xlib + +#const +# libX11* = "libX11.so" + +# +# Automatically converted by H2Pas 0.99.15 from xresource.h +# The following command line parameters were used: +# -p +# -T +# -S +# -d +# -c +# xresource.h +# + +proc Xpermalloc*(para1: int32): cstring{.cdecl, dynlib: libX11, importc.} +type + PXrmQuark* = ptr TXrmQuark + TXrmQuark* = int32 + TXrmQuarkList* = PXrmQuark + PXrmQuarkList* = ptr TXrmQuarkList + +proc NULLQUARK*(): TXrmQuark +type + PXrmString* = ptr TXrmString + TXrmString* = ptr char + +proc NULLSTRING*(): TXrmString +proc XrmStringToQuark*(para1: cstring): TXrmQuark{.cdecl, dynlib: libX11, + importc.} +proc XrmPermStringToQuark*(para1: cstring): TXrmQuark{.cdecl, dynlib: libX11, + importc.} +proc XrmQuarkToString*(para1: TXrmQuark): TXrmString{.cdecl, dynlib: libX11, + importc.} +proc XrmUniqueQuark*(): TXrmQuark{.cdecl, dynlib: libX11, importc.} +#when defined(MACROS): +proc XrmStringsEqual*(a1, a2: cstring): bool +type + PXrmBinding* = ptr TXrmBinding + TXrmBinding* = enum + XrmBindTightly, XrmBindLoosely + TXrmBindingList* = PXrmBinding + PXrmBindingList* = ptr TXrmBindingList + +proc XrmStringToQuarkList*(para1: cstring, para2: TXrmQuarkList){.cdecl, + dynlib: libX11, importc.} +proc XrmStringToBindingQuarkList*(para1: cstring, para2: TXrmBindingList, + para3: TXrmQuarkList){.cdecl, dynlib: libX11, + importc.} +type + PXrmName* = ptr TXrmName + TXrmName* = TXrmQuark + PXrmNameList* = ptr TXrmNameList + TXrmNameList* = TXrmQuarkList + +#when defined(MACROS): +proc XrmNameToString*(name: int32): TXrmString +proc XrmStringToName*(str: cstring): int32 +proc XrmStringToNameList*(str: cstring, name: PXrmQuark) +type + PXrmClass* = ptr TXrmClass + TXrmClass* = TXrmQuark + PXrmClassList* = ptr TXrmClassList + TXrmClassList* = TXrmQuarkList + +#when defined(MACROS): +proc XrmClassToString*(c_class: int32): TXrmString +proc XrmStringToClass*(c_class: cstring): int32 +proc XrmStringToClassList*(str: cstring, c_class: PXrmQuark) +type + PXrmRepresentation* = ptr TXrmRepresentation + TXrmRepresentation* = TXrmQuark + +#when defined(MACROS): +proc XrmStringToRepresentation*(str: cstring): int32 +proc XrmRepresentationToString*(thetype: int32): TXrmString +type + PXrmValue* = ptr TXrmValue + TXrmValue*{.final.} = object + size*: int32 + address*: TXPointer + + TXrmValuePtr* = PXrmValue + PXrmValuePtr* = ptr TXrmValuePtr + PXrmHashBucketRec* = ptr TXrmHashBucketRec + TXrmHashBucketRec*{.final.} = object + TXrmHashBucket* = PXrmHashBucketRec + PXrmHashBucket* = ptr TXrmHashBucket + PXrmHashTable* = ptr TXrmHashTable + TXrmHashTable* = ptr TXrmHashBucket + TXrmDatabase* = PXrmHashBucketRec + PXrmDatabase* = ptr TXrmDatabase + +proc XrmDestroyDatabase*(para1: TXrmDatabase){.cdecl, dynlib: libX11, importc.} +proc XrmQPutResource*(para1: PXrmDatabase, para2: TXrmBindingList, + para3: TXrmQuarkList, para4: TXrmRepresentation, + para5: PXrmValue){.cdecl, dynlib: libX11, importc.} +proc XrmPutResource*(para1: PXrmDatabase, para2: cstring, para3: cstring, + para4: PXrmValue){.cdecl, dynlib: libX11, importc.} +proc XrmQPutStringResource*(para1: PXrmDatabase, para2: TXrmBindingList, + para3: TXrmQuarkList, para4: cstring){.cdecl, + dynlib: libX11, importc.} +proc XrmPutStringResource*(para1: PXrmDatabase, para2: cstring, para3: cstring){. + cdecl, dynlib: libX11, importc.} +proc XrmPutLineResource*(para1: PXrmDatabase, para2: cstring){.cdecl, + dynlib: libX11, importc.} +proc XrmQGetResource*(para1: TXrmDatabase, para2: TXrmNameList, + para3: TXrmClassList, para4: PXrmRepresentation, + para5: PXrmValue): TBool{.cdecl, dynlib: libX11, importc.} +proc XrmGetResource*(para1: TXrmDatabase, para2: cstring, para3: cstring, + para4: PPchar, para5: PXrmValue): TBool{.cdecl, + dynlib: libX11, importc.} + # There is no definition of TXrmSearchList + #function XrmQGetSearchList(para1:TXrmDatabase; para2:TXrmNameList; para3:TXrmClassList; para4:TXrmSearchList; para5:longint):TBool;cdecl;external libX11; + #function XrmQGetSearchResource(para1:TXrmSearchList; para2:TXrmName; para3:TXrmClass; para4:PXrmRepresentation; para5:PXrmValue):TBool;cdecl;external libX11; +proc XrmSetDatabase*(para1: PDisplay, para2: TXrmDatabase){.cdecl, + dynlib: libX11, importc.} +proc XrmGetDatabase*(para1: PDisplay): TXrmDatabase{.cdecl, dynlib: libX11, + importc.} +proc XrmGetFileDatabase*(para1: cstring): TXrmDatabase{.cdecl, dynlib: libX11, + importc.} +proc XrmCombineFileDatabase*(para1: cstring, para2: PXrmDatabase, para3: TBool): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XrmGetStringDatabase*(para1: cstring): TXrmDatabase{.cdecl, dynlib: libX11, + importc.} +proc XrmPutFileDatabase*(para1: TXrmDatabase, para2: cstring){.cdecl, + dynlib: libX11, importc.} +proc XrmMergeDatabases*(para1: TXrmDatabase, para2: PXrmDatabase){.cdecl, + dynlib: libX11, importc.} +proc XrmCombineDatabase*(para1: TXrmDatabase, para2: PXrmDatabase, para3: TBool){. + cdecl, dynlib: libX11, importc.} +const + XrmEnumAllLevels* = 0 + XrmEnumOneLevel* = 1 + +type + funcbool* = proc (): TBool {.cdecl.} + +proc XrmEnumerateDatabase*(para1: TXrmDatabase, para2: TXrmNameList, + para3: TXrmClassList, para4: int32, para5: funcbool, + para6: TXPointer): TBool{.cdecl, dynlib: libX11, + importc.} +proc XrmLocaleOfDatabase*(para1: TXrmDatabase): cstring{.cdecl, dynlib: libX11, + importc.} +type + PXrmOptionKind* = ptr TXrmOptionKind + TXrmOptionKind* = enum + XrmoptionNoArg, XrmoptionIsArg, XrmoptionStickyArg, XrmoptionSepArg, + XrmoptionResArg, XrmoptionSkipArg, XrmoptionSkipLine, XrmoptionSkipNArgs + PXrmOptionDescRec* = ptr TXrmOptionDescRec + TXrmOptionDescRec*{.final.} = object + option*: cstring + specifier*: cstring + argKind*: TXrmOptionKind + value*: TXPointer + + TXrmOptionDescList* = PXrmOptionDescRec + PXrmOptionDescList* = ptr TXrmOptionDescList + +proc XrmParseCommand*(para1: PXrmDatabase, para2: TXrmOptionDescList, + para3: int32, para4: cstring, para5: ptr int32, + para6: PPchar){.cdecl, dynlib: libX11, importc.} +# implementation + +proc NULLQUARK(): TXrmQuark = + result = TXrmQuark(0) + +proc NULLSTRING(): TXrmString = + result = nil + +#when defined(MACROS): +proc XrmStringsEqual(a1, a2: cstring): bool = + #result = (strcomp(a1, a2)) == 0 + $a1 == $a2 + +proc XrmNameToString(name: int32): TXrmString = + result = XrmQuarkToString(name) + +proc XrmStringToName(str: cstring): int32 = + result = XrmStringToQuark(str) + +proc XrmStringToNameList(str: cstring, name: PXrmQuark) = + XrmStringToQuarkList(str, name) + +proc XrmClassToString(c_class: int32): TXrmString = + result = XrmQuarkToString(c_class) + +proc XrmStringToClass(c_class: cstring): int32 = + result = XrmStringToQuark(c_class) + +proc XrmStringToClassList(str: cstring, c_class: PXrmQuark) = + XrmStringToQuarkList(str, c_class) + +proc XrmStringToRepresentation(str: cstring): int32 = + result = XrmStringToQuark(str) + +proc XrmRepresentationToString(thetype: int32): TXrmString = + result = XrmQuarkToString(thetype) diff --git a/tests/deps/x11-1.0/xshm.nim b/tests/deps/x11-1.0/xshm.nim new file mode 100644 index 000000000..e56bd87b1 --- /dev/null +++ b/tests/deps/x11-1.0/xshm.nim @@ -0,0 +1,77 @@ + +import + x, xlib + +#const +# libX11* = "libX11.so" + +# +# Automatically converted by H2Pas 0.99.15 from xshm.h +# The following command line parameters were used: +# -p +# -T +# -S +# -d +# -c +# xshm.h +# + +const + constX_ShmQueryVersion* = 0 + constX_ShmAttach* = 1 + constX_ShmDetach* = 2 + constX_ShmPutImage* = 3 + constX_ShmGetImage* = 4 + constX_ShmCreatePixmap* = 5 + ShmCompletion* = 0 + ShmNumberEvents* = ShmCompletion + 1 + BadShmSeg* = 0 + ShmNumberErrors* = BadShmSeg + 1 + +type + PShmSeg* = ptr TShmSeg + TShmSeg* = culong + PXShmCompletionEvent* = ptr TXShmCompletionEvent + TXShmCompletionEvent*{.final.} = object + theType*: cint + serial*: culong + send_event*: TBool + display*: PDisplay + drawable*: TDrawable + major_code*: cint + minor_code*: cint + shmseg*: TShmSeg + offset*: culong + + PXShmSegmentInfo* = ptr TXShmSegmentInfo + TXShmSegmentInfo*{.final.} = object + shmseg*: TShmSeg + shmid*: cint + shmaddr*: cstring + readOnly*: TBool + + +proc XShmQueryExtension*(para1: PDisplay): TBool{.cdecl, dynlib: libX11, importc.} +proc XShmGetEventBase*(para1: PDisplay): cint{.cdecl, dynlib: libX11, importc.} +proc XShmQueryVersion*(para1: PDisplay, para2: Pcint, para3: Pcint, para4: PBool): TBool{. + cdecl, dynlib: libX11, importc.} +proc XShmPixmapFormat*(para1: PDisplay): cint{.cdecl, dynlib: libX11, importc.} +proc XShmAttach*(para1: PDisplay, para2: PXShmSegmentInfo): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XShmDetach*(para1: PDisplay, para2: PXShmSegmentInfo): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XShmPutImage*(para1: PDisplay, para2: TDrawable, para3: TGC, + para4: PXImage, para5: cint, para6: cint, para7: cint, + para8: cint, para9: cuint, para10: cuint, para11: TBool): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XShmGetImage*(para1: PDisplay, para2: TDrawable, para3: PXImage, + para4: cint, para5: cint, para6: culong): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XShmCreateImage*(para1: PDisplay, para2: PVisual, para3: cuint, + para4: cint, para5: cstring, para6: PXShmSegmentInfo, + para7: cuint, para8: cuint): PXImage{.cdecl, + dynlib: libX11, importc.} +proc XShmCreatePixmap*(para1: PDisplay, para2: TDrawable, para3: cstring, + para4: PXShmSegmentInfo, para5: cuint, para6: cuint, + para7: cuint): TPixmap{.cdecl, dynlib: libX11, importc.} +# implementation diff --git a/tests/deps/x11-1.0/xutil.nim b/tests/deps/x11-1.0/xutil.nim new file mode 100644 index 000000000..b7de0ae3e --- /dev/null +++ b/tests/deps/x11-1.0/xutil.nim @@ -0,0 +1,412 @@ + +import + x, xlib, keysym + +#const +# libX11* = "libX11.so" + +# +# Automatically converted by H2Pas 0.99.15 from xutil.h +# The following command line parameters were used: +# -p +# -T +# -S +# -d +# -c +# xutil.h +# + +const + NoValue* = 0x00000000 + XValue* = 0x00000001 + YValue* = 0x00000002 + WidthValue* = 0x00000004 + HeightValue* = 0x00000008 + AllValues* = 0x0000000F + XNegative* = 0x00000010 + YNegative* = 0x00000020 + +type + TCPoint*{.final.} = object + x*: cint + y*: cint + + PXSizeHints* = ptr TXSizeHints + TXSizeHints*{.final.} = object + flags*: clong + x*, y*: cint + width*, height*: cint + min_width*, min_height*: cint + max_width*, max_height*: cint + width_inc*, height_inc*: cint + min_aspect*, max_aspect*: TCPoint + base_width*, base_height*: cint + win_gravity*: cint + + +const + USPosition* = 1 shl 0 + USSize* = 1 shl 1 + PPosition* = 1 shl 2 + PSize* = 1 shl 3 + PMinSize* = 1 shl 4 + PMaxSize* = 1 shl 5 + PResizeInc* = 1 shl 6 + PAspect* = 1 shl 7 + PBaseSize* = 1 shl 8 + PWinGravity* = 1 shl 9 + PAllHints* = PPosition or PSize or PMinSize or PMaxSize or PResizeInc or + PAspect + +type + PXWMHints* = ptr TXWMHints + TXWMHints*{.final.} = object + flags*: clong + input*: TBool + initial_state*: cint + icon_pixmap*: TPixmap + icon_window*: TWindow + icon_x*, icon_y*: cint + icon_mask*: TPixmap + window_group*: TXID + + +const + InputHint* = 1 shl 0 + StateHint* = 1 shl 1 + IconPixmapHint* = 1 shl 2 + IconWindowHint* = 1 shl 3 + IconPositionHint* = 1 shl 4 + IconMaskHint* = 1 shl 5 + WindowGroupHint* = 1 shl 6 + AllHints* = InputHint or StateHint or IconPixmapHint or IconWindowHint or + IconPositionHint or IconMaskHint or WindowGroupHint + XUrgencyHint* = 1 shl 8 + WithdrawnState* = 0 + NormalState* = 1 + IconicState* = 3 + DontCareState* = 0 + ZoomState* = 2 + InactiveState* = 4 + +type + PXTextProperty* = ptr TXTextProperty + TXTextProperty*{.final.} = object + value*: Pcuchar + encoding*: TAtom + format*: cint + nitems*: culong + + +const + XNoMemory* = - 1 + XLocaleNotSupported* = - 2 + XConverterNotFound* = - 3 + +type + PXICCEncodingStyle* = ptr TXICCEncodingStyle + TXICCEncodingStyle* = enum + XStringStyle, XCompoundTextStyle, XTextStyle, XStdICCTextStyle, + XUTF8StringStyle + PPXIconSize* = ptr PXIconSize + PXIconSize* = ptr TXIconSize + TXIconSize*{.final.} = object + min_width*, min_height*: cint + max_width*, max_height*: cint + width_inc*, height_inc*: cint + + PXClassHint* = ptr TXClassHint + TXClassHint*{.final.} = object + res_name*: cstring + res_class*: cstring + + +type + PXComposeStatus* = ptr TXComposeStatus + TXComposeStatus*{.final.} = object + compose_ptr*: TXPointer + chars_matched*: cint + + +type + PXRegion* = ptr TXRegion + TXRegion*{.final.} = object + TRegion* = PXRegion + PRegion* = ptr TRegion + +const + RectangleOut* = 0 + RectangleIn* = 1 + RectanglePart* = 2 + +type + PXVisualInfo* = ptr TXVisualInfo + TXVisualInfo*{.final.} = object + visual*: PVisual + visualid*: TVisualID + screen*: cint + depth*: cint + class*: cint + red_mask*: culong + green_mask*: culong + blue_mask*: culong + colormap_size*: cint + bits_per_rgb*: cint + + +const + VisualNoMask* = 0x00000000 + VisualIDMask* = 0x00000001 + VisualScreenMask* = 0x00000002 + VisualDepthMask* = 0x00000004 + VisualClassMask* = 0x00000008 + VisualRedMaskMask* = 0x00000010 + VisualGreenMaskMask* = 0x00000020 + VisualBlueMaskMask* = 0x00000040 + VisualColormapSizeMask* = 0x00000080 + VisualBitsPerRGBMask* = 0x00000100 + VisualAllMask* = 0x000001FF + +type + PPXStandardColormap* = ptr PXStandardColormap + PXStandardColormap* = ptr TXStandardColormap + TXStandardColormap*{.final.} = object + colormap*: TColormap + red_max*: culong + red_mult*: culong + green_max*: culong + green_mult*: culong + blue_max*: culong + blue_mult*: culong + base_pixel*: culong + visualid*: TVisualID + killid*: TXID + + +const + BitmapSuccess* = 0 + BitmapOpenFailed* = 1 + BitmapFileInvalid* = 2 + BitmapNoMemory* = 3 + XCSUCCESS* = 0 + XCNOMEM* = 1 + XCNOENT* = 2 + ReleaseByFreeingColormap*: TXID = TXID(1) + +type + PXContext* = ptr TXContext + TXContext* = cint + +proc XAllocClassHint*(): PXClassHint{.cdecl, dynlib: libX11, importc.} +proc XAllocIconSize*(): PXIconSize{.cdecl, dynlib: libX11, importc.} +proc XAllocSizeHints*(): PXSizeHints{.cdecl, dynlib: libX11, importc.} +proc XAllocStandardColormap*(): PXStandardColormap{.cdecl, dynlib: libX11, + importc.} +proc XAllocWMHints*(): PXWMHints{.cdecl, dynlib: libX11, importc.} +proc XClipBox*(para1: TRegion, para2: PXRectangle): cint{.cdecl, dynlib: libX11, + importc.} +proc XCreateRegion*(): TRegion{.cdecl, dynlib: libX11, importc.} +proc XDefaultString*(): cstring{.cdecl, dynlib: libX11, importc.} +proc XDeleteContext*(para1: PDisplay, para2: TXID, para3: TXContext): cint{. + cdecl, dynlib: libX11, importc.} +proc XDestroyRegion*(para1: TRegion): cint{.cdecl, dynlib: libX11, importc.} +proc XEmptyRegion*(para1: TRegion): cint{.cdecl, dynlib: libX11, importc.} +proc XEqualRegion*(para1: TRegion, para2: TRegion): cint{.cdecl, dynlib: libX11, + importc.} +proc XFindContext*(para1: PDisplay, para2: TXID, para3: TXContext, + para4: PXPointer): cint{.cdecl, dynlib: libX11, importc.} +proc XGetClassHint*(para1: PDisplay, para2: TWindow, para3: PXClassHint): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XGetIconSizes*(para1: PDisplay, para2: TWindow, para3: PPXIconSize, + para4: Pcint): TStatus{.cdecl, dynlib: libX11, importc.} +proc XGetNormalHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XGetRGBColormaps*(para1: PDisplay, para2: TWindow, + para3: PPXStandardColormap, para4: Pcint, para5: TAtom): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XGetSizeHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints, + para4: TAtom): TStatus{.cdecl, dynlib: libX11, importc.} +proc XGetStandardColormap*(para1: PDisplay, para2: TWindow, + para3: PXStandardColormap, para4: TAtom): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XGetTextProperty*(para1: PDisplay, para2: TWindow, para3: PXTextProperty, + para4: TAtom): TStatus{.cdecl, dynlib: libX11, importc.} +proc XGetVisualInfo*(para1: PDisplay, para2: clong, para3: PXVisualInfo, + para4: Pcint): PXVisualInfo{.cdecl, dynlib: libX11, importc.} +proc XGetWMClientMachine*(para1: PDisplay, para2: TWindow, para3: PXTextProperty): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XGetWMHints*(para1: PDisplay, para2: TWindow): PXWMHints{.cdecl, + dynlib: libX11, importc.} +proc XGetWMIconName*(para1: PDisplay, para2: TWindow, para3: PXTextProperty): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XGetWMName*(para1: PDisplay, para2: TWindow, para3: PXTextProperty): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XGetWMNormalHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints, + para4: ptr int): TStatus{.cdecl, dynlib: libX11, importc.} +proc XGetWMSizeHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints, + para4: ptr int, para5: TAtom): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XGetZoomHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints): TStatus{. + cdecl, dynlib: libX11, importc.} +proc XIntersectRegion*(para1: TRegion, para2: TRegion, para3: TRegion): cint{. + cdecl, dynlib: libX11, importc.} +proc XConvertCase*(para1: TKeySym, para2: PKeySym, para3: PKeySym){.cdecl, + dynlib: libX11, importc.} +proc XLookupString*(para1: PXKeyEvent, para2: cstring, para3: cint, + para4: PKeySym, para5: PXComposeStatus): cint{.cdecl, + dynlib: libX11, importc.} +proc XMatchVisualInfo*(para1: PDisplay, para2: cint, para3: cint, para4: cint, + para5: PXVisualInfo): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XOffsetRegion*(para1: TRegion, para2: cint, para3: cint): cint{.cdecl, + dynlib: libX11, importc.} +proc XPointInRegion*(para1: TRegion, para2: cint, para3: cint): TBool{.cdecl, + dynlib: libX11, importc.} +proc XPolygonRegion*(para1: PXPoint, para2: cint, para3: cint): TRegion{.cdecl, + dynlib: libX11, importc.} +proc XRectInRegion*(para1: TRegion, para2: cint, para3: cint, para4: cuint, + para5: cuint): cint{.cdecl, dynlib: libX11, importc.} +proc XSaveContext*(para1: PDisplay, para2: TXID, para3: TXContext, + para4: cstring): cint{.cdecl, dynlib: libX11, importc.} +proc XSetClassHint*(para1: PDisplay, para2: TWindow, para3: PXClassHint): cint{. + cdecl, dynlib: libX11, importc.} +proc XSetIconSizes*(para1: PDisplay, para2: TWindow, para3: PXIconSize, + para4: cint): cint{.cdecl, dynlib: libX11, importc.} +proc XSetNormalHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints): cint{. + cdecl, dynlib: libX11, importc.} +proc XSetRGBColormaps*(para1: PDisplay, para2: TWindow, + para3: PXStandardColormap, para4: cint, para5: TAtom){. + cdecl, dynlib: libX11, importc.} +proc XSetSizeHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints, + para4: TAtom): cint{.cdecl, dynlib: libX11, importc.} +proc XSetStandardProperties*(para1: PDisplay, para2: TWindow, para3: cstring, + para4: cstring, para5: TPixmap, para6: PPchar, + para7: cint, para8: PXSizeHints): cint{.cdecl, + dynlib: libX11, importc.} +proc XSetTextProperty*(para1: PDisplay, para2: TWindow, para3: PXTextProperty, + para4: TAtom){.cdecl, dynlib: libX11, importc.} +proc XSetWMClientMachine*(para1: PDisplay, para2: TWindow, para3: PXTextProperty){. + cdecl, dynlib: libX11, importc.} +proc XSetWMHints*(para1: PDisplay, para2: TWindow, para3: PXWMHints): cint{. + cdecl, dynlib: libX11, importc.} +proc XSetWMIconName*(para1: PDisplay, para2: TWindow, para3: PXTextProperty){. + cdecl, dynlib: libX11, importc.} +proc XSetWMName*(para1: PDisplay, para2: TWindow, para3: PXTextProperty){.cdecl, + dynlib: libX11, importc.} +proc XSetWMNormalHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints){. + cdecl, dynlib: libX11, importc.} +proc XSetWMProperties*(para1: PDisplay, para2: TWindow, para3: PXTextProperty, + para4: PXTextProperty, para5: PPchar, para6: cint, + para7: PXSizeHints, para8: PXWMHints, para9: PXClassHint){. + cdecl, dynlib: libX11, importc.} +proc XmbSetWMProperties*(para1: PDisplay, para2: TWindow, para3: cstring, + para4: cstring, para5: PPchar, para6: cint, + para7: PXSizeHints, para8: PXWMHints, + para9: PXClassHint){.cdecl, dynlib: libX11, importc.} +proc Xutf8SetWMProperties*(para1: PDisplay, para2: TWindow, para3: cstring, + para4: cstring, para5: PPchar, para6: cint, + para7: PXSizeHints, para8: PXWMHints, + para9: PXClassHint){.cdecl, dynlib: libX11, importc.} +proc XSetWMSizeHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints, + para4: TAtom){.cdecl, dynlib: libX11, importc.} +proc XSetRegion*(para1: PDisplay, para2: TGC, para3: TRegion): cint{.cdecl, + dynlib: libX11, importc.} +proc XSetStandardColormap*(para1: PDisplay, para2: TWindow, + para3: PXStandardColormap, para4: TAtom){.cdecl, + dynlib: libX11, importc.} +proc XSetZoomHints*(para1: PDisplay, para2: TWindow, para3: PXSizeHints): cint{. + cdecl, dynlib: libX11, importc.} +proc XShrinkRegion*(para1: TRegion, para2: cint, para3: cint): cint{.cdecl, + dynlib: libX11, importc.} +proc XStringListToTextProperty*(para1: PPchar, para2: cint, + para3: PXTextProperty): TStatus{.cdecl, + dynlib: libX11, importc.} +proc XSubtractRegion*(para1: TRegion, para2: TRegion, para3: TRegion): cint{. + cdecl, dynlib: libX11, importc.} +proc XmbTextListToTextProperty*(para1: PDisplay, para2: PPchar, para3: cint, + para4: TXICCEncodingStyle, para5: PXTextProperty): cint{. + cdecl, dynlib: libX11, importc.} +proc XwcTextListToTextProperty*(para1: PDisplay, para2: ptr ptr int16, para3: cint, + para4: TXICCEncodingStyle, para5: PXTextProperty): cint{. + cdecl, dynlib: libX11, importc.} +proc Xutf8TextListToTextProperty*(para1: PDisplay, para2: PPchar, para3: cint, + para4: TXICCEncodingStyle, + para5: PXTextProperty): cint{.cdecl, + dynlib: libX11, importc.} +proc XwcFreeStringList*(para1: ptr ptr int16){.cdecl, dynlib: libX11, importc.} +proc XTextPropertyToStringList*(para1: PXTextProperty, para2: PPPchar, + para3: Pcint): TStatus{.cdecl, dynlib: libX11, + importc.} +proc XmbTextPropertyToTextList*(para1: PDisplay, para2: PXTextProperty, + para3: PPPchar, para4: Pcint): cint{.cdecl, + dynlib: libX11, importc.} +proc XwcTextPropertyToTextList*(para1: PDisplay, para2: PXTextProperty, + para3: ptr ptr ptr int16, para4: Pcint): cint{.cdecl, + dynlib: libX11, importc.} +proc Xutf8TextPropertyToTextList*(para1: PDisplay, para2: PXTextProperty, + para3: PPPchar, para4: Pcint): cint{.cdecl, + dynlib: libX11, importc.} +proc XUnionRectWithRegion*(para1: PXRectangle, para2: TRegion, para3: TRegion): cint{. + cdecl, dynlib: libX11, importc.} +proc XUnionRegion*(para1: TRegion, para2: TRegion, para3: TRegion): cint{.cdecl, + dynlib: libX11, importc.} +proc XWMGeometry*(para1: PDisplay, para2: cint, para3: cstring, para4: cstring, + para5: cuint, para6: PXSizeHints, para7: Pcint, para8: Pcint, + para9: Pcint, para10: Pcint, para11: Pcint): cint{.cdecl, + dynlib: libX11, importc.} +proc XXorRegion*(para1: TRegion, para2: TRegion, para3: TRegion): cint{.cdecl, + dynlib: libX11, importc.} +#when defined(MACROS): +proc XDestroyImage*(ximage: PXImage): cint +proc XGetPixel*(ximage: PXImage, x, y: cint): culong +proc XPutPixel*(ximage: PXImage, x, y: cint, pixel: culong): cint +proc XSubImage*(ximage: PXImage, x, y: cint, width, height: cuint): PXImage +proc XAddPixel*(ximage: PXImage, value: clong): cint +proc IsKeypadKey*(keysym: TKeySym): bool +proc IsPrivateKeypadKey*(keysym: TKeySym): bool +proc IsCursorKey*(keysym: TKeySym): bool +proc IsPFKey*(keysym: TKeySym): bool +proc IsFunctionKey*(keysym: TKeySym): bool +proc IsMiscFunctionKey*(keysym: TKeySym): bool +proc IsModifierKey*(keysym: TKeySym): bool + #function XUniqueContext : TXContext; + #function XStringToContext(_string : Pchar) : TXContext; +# implementation + +#when defined(MACROS): +proc XDestroyImage(ximage: PXImage): cint = + ximage.f.destroy_image(ximage) + +proc XGetPixel(ximage: PXImage, x, y: cint): culong = + ximage.f.get_pixel(ximage, x, y) + +proc XPutPixel(ximage: PXImage, x, y: cint, pixel: culong): cint = + ximage.f.put_pixel(ximage, x, y, pixel) + +proc XSubImage(ximage: PXImage, x, y: cint, width, height: cuint): PXImage = + ximage.f.sub_image(ximage, x, y, width, height) + +proc XAddPixel(ximage: PXImage, value: clong): cint = + ximage.f.add_pixel(ximage, value) + +proc IsKeypadKey(keysym: TKeySym): bool = + (keysym >= XK_KP_Space) and (keysym <= XK_KP_Equal) + +proc IsPrivateKeypadKey(keysym: TKeySym): bool = + (keysym >= 0x11000000.TKeySym) and (keysym <= 0x1100FFFF.TKeySym) + +proc IsCursorKey(keysym: TKeySym): bool = + (keysym >= XK_Home) and (keysym < XK_Select) + +proc IsPFKey(keysym: TKeySym): bool = + (keysym >= XK_KP_F1) and (keysym <= XK_KP_F4) + +proc IsFunctionKey(keysym: TKeySym): bool = + (keysym >= XK_F1) and (keysym <= XK_F35) + +proc IsMiscFunctionKey(keysym: TKeySym): bool = + (keysym >= XK_Select) and (keysym <= XK_Break) + +proc IsModifierKey(keysym: TKeySym): bool = + ((keysym >= XK_Shift_L) and (keysym <= XK_Hyper_R)) or + (keysym == XK_Mode_switch) or (keysym == XK_Num_Lock) diff --git a/tests/deps/x11-1.0/xv.nim b/tests/deps/x11-1.0/xv.nim new file mode 100644 index 000000000..45ab61418 --- /dev/null +++ b/tests/deps/x11-1.0/xv.nim @@ -0,0 +1,84 @@ +#*********************************************************** +#Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts, +#and the Massachusetts Institute of Technology, Cambridge, Massachusetts. +# +# All Rights Reserved +# +#Permission to use, copy, modify, and distribute this software and its +#documentation for any purpose and without fee is hereby granted, +#provided that the above copyright notice appear in all copies and that +#both that copyright notice and this permission notice appear in +#supporting documentation, and that the names of Digital or MIT not be +#used in advertising or publicity pertaining to distribution of the +#software without specific, written prior permission. +# +#DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +#ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +#DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +#ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +#WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +#ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +#SOFTWARE. +# +#****************************************************************** +# $XFree86: xc/include/extensions/Xv.h,v 1.3 1999/05/23 06:33:22 dawes Exp $ + +import + x + +const + XvName* = "libXVideo.so" + XvVersion* = 2 + XvRevision* = 2 # Symbols + +type + TXvPortID* = TXID + TXvEncodingID* = TXID + +const + XvNone* = 0 + XvInput* = 0 + XvOutput* = 1 + XvInputMask* = 1 shl XvInput + XvOutputMask* = 1 shl XvOutput + XvVideoMask* = 0x00000004 + XvStillMask* = 0x00000008 + XvImageMask* = 0x00000010 # These two are not client viewable + XvPixmapMask* = 0x00010000 + XvWindowMask* = 0x00020000 + XvGettable* = 0x00000001 + XvSettable* = 0x00000002 + XvRGB* = 0 + XvYUV* = 1 + XvPacked* = 0 + XvPlanar* = 1 + XvTopToBottom* = 0 + XvBottomToTop* = 1 # Events + XvVideoNotify* = 0 + XvPortNotify* = 1 + XvNumEvents* = 2 # Video Notify Reasons + XvStarted* = 0 + XvStopped* = 1 + XvBusy* = 2 + XvPreempted* = 3 + XvHardError* = 4 + XvLastReason* = 4 + XvNumReasons* = XvLastReason + 1 + XvStartedMask* = 1 shl XvStarted + XvStoppedMask* = 1 shl XvStopped + XvBusyMask* = 1 shl XvBusy + XvPreemptedMask* = 1 shl XvPreempted + XvHardErrorMask* = 1 shl XvHardError + XvAnyReasonMask* = (1 shl XvNumReasons) - 1 + XvNoReasonMask* = 0 # Errors + XvBadPort* = 0 + XvBadEncoding* = 1 + XvBadControl* = 2 + XvNumErrors* = 3 # Status + XvBadExtension* = 1 + XvAlreadyGrabbed* = 2 + XvInvalidTime* = 3 + XvBadReply* = 4 + XvBadAlloc* = 5 + +# implementation diff --git a/tests/deps/x11-1.0/xvlib.nim b/tests/deps/x11-1.0/xvlib.nim new file mode 100644 index 000000000..354a4b93b --- /dev/null +++ b/tests/deps/x11-1.0/xvlib.nim @@ -0,0 +1,234 @@ +#*********************************************************** +#Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts, +#and the Massachusetts Institute of Technology, Cambridge, Massachusetts. +# +# All Rights Reserved +# +#Permission to use, copy, modify, and distribute this software and its +#documentation for any purpose and without fee is hereby granted, +#provided that the above copyright notice appear in all copies and that +#both that copyright notice and this permission notice appear in +#supporting documentation, and that the names of Digital or MIT not be +#used in advertising or publicity pertaining to distribution of the +#software without specific, written prior permission. +# +#DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +#ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +#DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +#ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +#WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +#ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +#SOFTWARE. +# +#****************************************************************** +# $XFree86: xc/include/extensions/Xvlib.h,v 1.3 1999/12/11 19:28:48 mvojkovi Exp $ +#* +#** File: +#** +#** Xvlib.h --- Xv library public header file +#** +#** Author: +#** +#** David Carver (Digital Workstation Engineering/Project Athena) +#** +#** Revisions: +#** +#** 26.06.91 Carver +#** - changed XvFreeAdaptors to XvFreeAdaptorInfo +#** - changed XvFreeEncodings to XvFreeEncodingInfo +#** +#** 11.06.91 Carver +#** - changed SetPortControl to SetPortAttribute +#** - changed GetPortControl to GetPortAttribute +#** - changed QueryBestSize +#** +#** 05.15.91 Carver +#** - version 2.0 upgrade +#** +#** 01.24.91 Carver +#** - version 1.4 upgrade +#** +#* + +import + x, xlib, xshm, xv + +const + libXv* = "libXv.so" + +type + PXvRational* = ptr TXvRational + TXvRational*{.final.} = object + numerator*: cint + denominator*: cint + + PXvAttribute* = ptr TXvAttribute + TXvAttribute*{.final.} = object + flags*: cint # XvGettable, XvSettable + min_value*: cint + max_value*: cint + name*: cstring + + PPXvEncodingInfo* = ptr PXvEncodingInfo + PXvEncodingInfo* = ptr TXvEncodingInfo + TXvEncodingInfo*{.final.} = object + encoding_id*: TXvEncodingID + name*: cstring + width*: culong + height*: culong + rate*: TXvRational + num_encodings*: culong + + PXvFormat* = ptr TXvFormat + TXvFormat*{.final.} = object + depth*: cchar + visual_id*: culong + + PPXvAdaptorInfo* = ptr PXvAdaptorInfo + PXvAdaptorInfo* = ptr TXvAdaptorInfo + TXvAdaptorInfo*{.final.} = object + base_id*: TXvPortID + num_ports*: culong + thetype*: cchar + name*: cstring + num_formats*: culong + formats*: PXvFormat + num_adaptors*: culong + + PXvVideoNotifyEvent* = ptr TXvVideoNotifyEvent + TXvVideoNotifyEvent*{.final.} = object + theType*: cint + serial*: culong # # of last request processed by server + send_event*: TBool # true if this came from a SendEvent request + display*: PDisplay # Display the event was read from + drawable*: TDrawable # drawable + reason*: culong # what generated this event + port_id*: TXvPortID # what port + time*: TTime # milliseconds + + PXvPortNotifyEvent* = ptr TXvPortNotifyEvent + TXvPortNotifyEvent*{.final.} = object + theType*: cint + serial*: culong # # of last request processed by server + send_event*: TBool # true if this came from a SendEvent request + display*: PDisplay # Display the event was read from + port_id*: TXvPortID # what port + time*: TTime # milliseconds + attribute*: TAtom # atom that identifies attribute + value*: clong # value of attribute + + PXvEvent* = ptr TXvEvent + TXvEvent*{.final.} = object + pad*: array[0..23, clong] #case longint of + # 0 : ( + # theType : cint; + # ); + # 1 : ( + # xvvideo : TXvVideoNotifyEvent; + # ); + # 2 : ( + # xvport : TXvPortNotifyEvent; + # ); + # 3 : ( + # + # ); + + PXvImageFormatValues* = ptr TXvImageFormatValues + TXvImageFormatValues*{.final.} = object + id*: cint # Unique descriptor for the format + theType*: cint # XvRGB, XvYUV + byte_order*: cint # LSBFirst, MSBFirst + guid*: array[0..15, cchar] # Globally Unique IDentifier + bits_per_pixel*: cint + format*: cint # XvPacked, XvPlanar + num_planes*: cint # for RGB formats only + depth*: cint + red_mask*: cuint + green_mask*: cuint + blue_mask*: cuint # for YUV formats only + y_sample_bits*: cuint + u_sample_bits*: cuint + v_sample_bits*: cuint + horz_y_period*: cuint + horz_u_period*: cuint + horz_v_period*: cuint + vert_y_period*: cuint + vert_u_period*: cuint + vert_v_period*: cuint + component_order*: array[0..31, char] # e.g. UYVY + scanline_order*: cint # XvTopToBottom, XvBottomToTop + + PXvImage* = ptr TXvImage + TXvImage*{.final.} = object + id*: cint + width*, height*: cint + data_size*: cint # bytes + num_planes*: cint + pitches*: cint # bytes + offsets*: cint # bytes + data*: pointer + obdata*: TXPointer + + +proc XvQueryExtension*(display: PDisplay, p_version, p_revision, p_requestBase, + p_eventBase, p_errorBase: cuint): cint{.cdecl, dynlib: libXv, importc.} +proc XvQueryAdaptors*(display: PDisplay, window: TWindow, p_nAdaptors: cuint, + p_pAdaptors: PPXvAdaptorInfo): cint{.cdecl, dynlib: libXv, + importc.} +proc XvQueryEncodings*(display: PDisplay, port: TXvPortID, p_nEncoding: cuint, + p_pEncoding: PPXvEncodingInfo): cint{.cdecl, + dynlib: libXv, importc.} +proc XvPutVideo*(display: PDisplay, port: TXvPortID, d: TDrawable, gc: TGC, + vx, vy: cint, vw, vh: cuint, dx, dy: cint, dw, dh: cuint): cint{. + cdecl, dynlib: libXv, importc.} +proc XvPutStill*(display: PDisplay, port: TXvPortID, d: TDrawable, gc: TGC, + vx, vy: cint, vw, vh: cuint, dx, dy: cint, dw, dh: cuint): cint{. + cdecl, dynlib: libXv, importc.} +proc XvGetVideo*(display: PDisplay, port: TXvPortID, d: TDrawable, gc: TGC, + vx, vy: cint, vw, vh: cuint, dx, dy: cint, dw, dh: cuint): cint{. + cdecl, dynlib: libXv, importc.} +proc XvGetStill*(display: PDisplay, port: TXvPortID, d: TDrawable, gc: TGC, + vx, vy: cint, vw, vh: cuint, dx, dy: cint, dw, dh: cuint): cint{. + cdecl, dynlib: libXv, importc.} +proc XvStopVideo*(display: PDisplay, port: TXvPortID, drawable: TDrawable): cint{. + cdecl, dynlib: libXv, importc.} +proc XvGrabPort*(display: PDisplay, port: TXvPortID, time: TTime): cint{.cdecl, + dynlib: libXv, importc.} +proc XvUngrabPort*(display: PDisplay, port: TXvPortID, time: TTime): cint{. + cdecl, dynlib: libXv, importc.} +proc XvSelectVideoNotify*(display: PDisplay, drawable: TDrawable, onoff: TBool): cint{. + cdecl, dynlib: libXv, importc.} +proc XvSelectPortNotify*(display: PDisplay, port: TXvPortID, onoff: TBool): cint{. + cdecl, dynlib: libXv, importc.} +proc XvSetPortAttribute*(display: PDisplay, port: TXvPortID, attribute: TAtom, + value: cint): cint{.cdecl, dynlib: libXv, importc.} +proc XvGetPortAttribute*(display: PDisplay, port: TXvPortID, attribute: TAtom, + p_value: cint): cint{.cdecl, dynlib: libXv, importc.} +proc XvQueryBestSize*(display: PDisplay, port: TXvPortID, motion: TBool, + vid_w, vid_h, drw_w, drw_h: cuint, + p_actual_width, p_actual_height: cuint): cint{.cdecl, + dynlib: libXv, importc.} +proc XvQueryPortAttributes*(display: PDisplay, port: TXvPortID, number: cint): PXvAttribute{. + cdecl, dynlib: libXv, importc.} +proc XvFreeAdaptorInfo*(adaptors: PXvAdaptorInfo){.cdecl, dynlib: libXv, importc.} +proc XvFreeEncodingInfo*(encodings: PXvEncodingInfo){.cdecl, dynlib: libXv, + importc.} +proc XvListImageFormats*(display: PDisplay, port_id: TXvPortID, + count_return: cint): PXvImageFormatValues{.cdecl, + dynlib: libXv, importc.} +proc XvCreateImage*(display: PDisplay, port: TXvPortID, id: cint, data: pointer, + width, height: cint): PXvImage{.cdecl, dynlib: libXv, + importc.} +proc XvPutImage*(display: PDisplay, id: TXvPortID, d: TDrawable, gc: TGC, + image: PXvImage, src_x, src_y: cint, src_w, src_h: cuint, + dest_x, dest_y: cint, dest_w, dest_h: cuint): cint{.cdecl, + dynlib: libXv, importc.} +proc XvShmPutImage*(display: PDisplay, id: TXvPortID, d: TDrawable, gc: TGC, + image: PXvImage, src_x, src_y: cint, src_w, src_h: cuint, + dest_x, dest_y: cint, dest_w, dest_h: cuint, + send_event: TBool): cint{.cdecl, dynlib: libXv, importc.} +proc XvShmCreateImage*(display: PDisplay, port: TXvPortID, id: cint, + data: pointer, width, height: cint, + shminfo: PXShmSegmentInfo): PXvImage{.cdecl, + dynlib: libXv, importc.} +# implementation diff --git a/tests/deps/zip-0.2.1/zip.nimble b/tests/deps/zip-0.2.1/zip.nimble new file mode 100644 index 000000000..561f1b9c9 --- /dev/null +++ b/tests/deps/zip-0.2.1/zip.nimble @@ -0,0 +1,18 @@ +# Package + +version = "0.2.1" +author = "Anonymous" +description = "Wrapper for the zip library" +license = "MIT" + +skipDirs = @["tests"] + +# Dependencies + +requires "nim >= 0.10.0" + +task tests, "Run lib tests": + withDir "tests": + exec "nim c -r ziptests" + exec "nim c -r zlibtests" + exec "nim c -r gziptests" diff --git a/tests/deps/zip-0.2.1/zip/gzipfiles.nim b/tests/deps/zip-0.2.1/zip/gzipfiles.nim new file mode 100644 index 000000000..82c412bc3 --- /dev/null +++ b/tests/deps/zip-0.2.1/zip/gzipfiles.nim @@ -0,0 +1,97 @@ +import os +import zlib +import streams +export streams + +## This module implements a gzipfile stream for reading, writing, appending. + +type + GzFileStream* = ref object of Stream + mode: FileMode + f: GzFile + +const SEEK_SET = 0.int32 # Seek from beginning of file. + +proc fsClose(s: Stream) = + if not GzFileStream(s).f.isNil: + discard gzclose(GzFileStream(s).f) + GzFileStream(s).f = nil + +proc fsFlush(s: Stream) = + # compiler flushFile also discard c_fflush + discard gzflush(GzFileStream(s).f, Z_FINISH) + +proc fsAtEnd(s: Stream): bool = + result = gzeof(GzFileStream(s).f) == 1 + +proc fsSetPosition(s: Stream, pos: int) = + if gzseek(GzFileStream(s).f, pos.ZOffT, SEEK_SET) == -1: + if GzFileStream(s).mode in {fmWrite, fmAppend}: + raise newException(IOError, "error in gzip stream while seeking! (file is in write/append mode!") + else: + raise newException(IOError, "error in gzip stream while seeking!") + +proc fsGetPosition(s: Stream): int = + result = gztell(GzFileStream(s).f).int + +proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = + result = gzread(GzFileStream(s).f, buffer, bufLen).int + if result == -1: + if GzFileStream(s).mode in {fmWrite, fmAppend}: + raise newException(IOError, "cannot read data from write-only gzip stream!") + else: + raise newException(IOError, "cannot read from stream!") + +proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int = + let gz = GzFileStream(s) + if gz.mode in {fmWrite, fmAppend}: + raise newException(IOError, "cannot peek data from write-only gzip stream!") + let pos = int(gztell(gz.f)) + result = fsReadData(s, buffer, bufLen) + fsSetPosition(s, pos) + +proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) = + if gzwrite(GzFileStream(s).f, buffer, bufLen).int != bufLen: + if GzFileStream(s).mode in {fmWrite, fmAppend}: + raise newException(IOError, "cannot write data to gzip stream!") + else: + raise newException(IOError, "cannot write data to read-only gzip stream!") + + +proc newGzFileStream*(filename: string; mode=fmRead; level=Z_DEFAULT_COMPRESSION): GzFileStream = + ## Opens a Gzipfile as a file stream. `mode` can be + ## ``fmRead``, ``fmWrite`` or ``fmAppend``. + ## + ## Compression level can be set with ``level`` argument. Currently + ## ``Z_DEFAULT_COMPRESSION`` is 6. + ## + ## Note: ``level`` is ignored if ``mode`` is `fmRead` + ## + ## Note: There is only partial support for file seeking + ## - in fmRead mode, seeking randomly inside the gzip + ## file will lead to poor performance. + ## - in fmWrite, fmAppend mode, only forward seeking + ## is supported. + new(result) + case mode + of fmRead: result.f = gzopen(filename, "rb") + of fmWrite: result.f = gzopen(filename, "wb") + of fmAppend: result.f = gzopen(filename, "ab") + else: raise newException(IOError, "unsupported file mode '" & $mode & + "' for GzFileStream!") + if result.f.isNil: + let err = osLastError() + if err != OSErrorCode(0'i32): + raiseOSError(err) + if mode in {fmWrite, fmAppend}: + discard gzsetparams(result.f, level.int32, Z_DEFAULT_STRATEGY.int32) + + result.mode = mode + result.closeImpl = fsClose + result.atEndImpl = fsAtEnd + result.setPositionImpl = fsSetPosition + result.getPositionImpl = fsGetPosition + result.readDataImpl = fsReadData + result.peekDataImpl = fsPeekData + result.writeDataImpl = fsWriteData + result.flushImpl = fsFlush diff --git a/tests/deps/zip-0.2.1/zip/libzip.nim b/tests/deps/zip-0.2.1/zip/libzip.nim new file mode 100644 index 000000000..a2904cd2c --- /dev/null +++ b/tests/deps/zip-0.2.1/zip/libzip.nim @@ -0,0 +1,252 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2013 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Interface to the `libzip `_ library by +## Dieter Baron and Thomas Klausner. This version links +## against ``libzip2.so.2`` unless you define the symbol ``useLibzipSrc``; then +## it is compiled against some old ``libizp_all.c`` file. + +# +# zip.h -- exported declarations. +# Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner +# +# This file is part of libzip, a library to manipulate ZIP archives. +# The authors can be contacted at +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. The names of the authors may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +import times + +when defined(unix) and not defined(useLibzipSrc): + when defined(macosx): + {.pragma: mydll, dynlib: "libzip(|2|4).dylib".} + else: + {.pragma: mydll, dynlib: "libzip(|2).so(|.4|.2|.1|.0)".} +else: + when defined(unix): + {.passl: "-lz".} + {.compile: "zip/private/libzip_all.c".} + {.pragma: mydll.} + +type + ZipSourceCmd* = int32 + + ZipSourceCallback* = proc (state: pointer, data: pointer, length: int, + cmd: ZipSourceCmd): int {.cdecl.} + PZipStat* = ptr ZipStat + ZipStat* = object ## the 'zip_stat' struct + name*: cstring ## name of the file + index*: int32 ## index within archive + crc*: int32 ## crc of file data + mtime*: Time ## modification time + size*: int ## size of file (uncompressed) + compSize*: int ## size of file (compressed) + compMethod*: int16 ## compression method used + encryptionMethod*: int16 ## encryption method used + + Zip = object + ZipSource = object + ZipFile = object + + PZip* = ptr Zip ## represents a zip archive + PZipFile* = ptr ZipFile ## represents a file within an archive + PZipSource* = ptr ZipSource ## represents a source for an archive +{.deprecated: [TZipSourceCmd: ZipSourceCmd, TZipStat: ZipStat, TZip: Zip, + TZipSourceCallback: ZipSourceCallback, TZipSource: ZipSource, + TZipFile: ZipFile].} + +# flags for zip_name_locate, zip_fopen, zip_stat, ... +const + ZIP_CREATE* = 1'i32 + ZIP_EXCL* = 2'i32 + ZIP_CHECKCONS* = 4'i32 + ZIP_FL_NOCASE* = 1'i32 ## ignore case on name lookup + ZIP_FL_NODIR* = 2'i32 ## ignore directory component + ZIP_FL_COMPRESSED* = 4'i32 ## read compressed data + ZIP_FL_UNCHANGED* = 8'i32 ## use original data, ignoring changes + ZIP_FL_RECOMPRESS* = 16'i32 ## force recompression of data + +const # archive global flags flags + ZIP_AFL_TORRENT* = 1'i32 ## torrent zipped + +const # libzip error codes + ZIP_ER_OK* = 0'i32 ## N No error + ZIP_ER_MULTIDISK* = 1'i32 ## N Multi-disk zip archives not supported + ZIP_ER_RENAME* = 2'i32 ## S Renaming temporary file failed + ZIP_ER_CLOSE* = 3'i32 ## S Closing zip archive failed + ZIP_ER_SEEK* = 4'i32 ## S Seek error + ZIP_ER_READ* = 5'i32 ## S Read error + ZIP_ER_WRITE* = 6'i32 ## S Write error + ZIP_ER_CRC* = 7'i32 ## N CRC error + ZIP_ER_ZIPCLOSED* = 8'i32 ## N Containing zip archive was closed + ZIP_ER_NOENT* = 9'i32 ## N No such file + ZIP_ER_EXISTS* = 10'i32 ## N File already exists + ZIP_ER_OPEN* = 11'i32 ## S Can't open file + ZIP_ER_TMPOPEN* = 12'i32 ## S Failure to create temporary file + ZIP_ER_ZLIB* = 13'i32 ## Z Zlib error + ZIP_ER_MEMORY* = 14'i32 ## N Malloc failure + ZIP_ER_CHANGED* = 15'i32 ## N Entry has been changed + ZIP_ER_COMPNOTSUPP* = 16'i32 ## N Compression method not supported + ZIP_ER_EOF* = 17'i32 ## N Premature EOF + ZIP_ER_INVAL* = 18'i32 ## N Invalid argument + ZIP_ER_NOZIP* = 19'i32 ## N Not a zip archive + ZIP_ER_INTERNAL* = 20'i32 ## N Internal error + ZIP_ER_INCONS* = 21'i32 ## N Zip archive inconsistent + ZIP_ER_REMOVE* = 22'i32 ## S Can't remove file + ZIP_ER_DELETED* = 23'i32 ## N Entry has been deleted + +const # type of system error value + ZIP_ET_NONE* = 0'i32 ## sys_err unused + ZIP_ET_SYS* = 1'i32 ## sys_err is errno + ZIP_ET_ZLIB* = 2'i32 ## sys_err is zlib error code + +const # compression methods + ZIP_CM_DEFAULT* = -1'i32 ## better of deflate or store + ZIP_CM_STORE* = 0'i32 ## stored (uncompressed) + ZIP_CM_SHRINK* = 1'i32 ## shrunk + ZIP_CM_REDUCE_1* = 2'i32 ## reduced with factor 1 + ZIP_CM_REDUCE_2* = 3'i32 ## reduced with factor 2 + ZIP_CM_REDUCE_3* = 4'i32 ## reduced with factor 3 + ZIP_CM_REDUCE_4* = 5'i32 ## reduced with factor 4 + ZIP_CM_IMPLODE* = 6'i32 ## imploded + ## 7 - Reserved for Tokenizing compression algorithm + ZIP_CM_DEFLATE* = 8'i32 ## deflated + ZIP_CM_DEFLATE64* = 9'i32 ## deflate64 + ZIP_CM_PKWARE_IMPLODE* = 10'i32 ## PKWARE imploding + ## 11 - Reserved by PKWARE + ZIP_CM_BZIP2* = 12'i32 ## compressed using BZIP2 algorithm + ## 13 - Reserved by PKWARE + ZIP_CM_LZMA* = 14'i32 ## LZMA (EFS) + ## 15-17 - Reserved by PKWARE + ZIP_CM_TERSE* = 18'i32 ## compressed using IBM TERSE (new) + ZIP_CM_LZ77* = 19'i32 ## IBM LZ77 z Architecture (PFS) + ZIP_CM_WAVPACK* = 97'i32 ## WavPack compressed data + ZIP_CM_PPMD* = 98'i32 ## PPMd version I, Rev 1 + +const # encryption methods + ZIP_EM_NONE* = 0'i32 ## not encrypted + ZIP_EM_TRAD_PKWARE* = 1'i32 ## traditional PKWARE encryption + +const + ZIP_EM_UNKNOWN* = 0x0000FFFF'i32 ## unknown algorithm + +const + ZIP_SOURCE_OPEN* = 0'i32 ## prepare for reading + ZIP_SOURCE_READ* = 1'i32 ## read data + ZIP_SOURCE_CLOSE* = 2'i32 ## reading is done + ZIP_SOURCE_STAT* = 3'i32 ## get meta information + ZIP_SOURCE_ERROR* = 4'i32 ## get error information + constZIP_SOURCE_FREE* = 5'i32 ## cleanup and free resources + ZIP_SOURCE_SUPPORTS* = 14'i32 ## check supported commands + +proc zip_add*(para1: PZip, para2: cstring, para3: PZipSource): int32 {.cdecl, + importc: "zip_add", mydll.} +proc zip_add_dir*(para1: PZip, para2: cstring): int32 {.cdecl, + importc: "zip_add_dir", mydll.} +proc zip_close*(para1: PZip) {.cdecl, importc: "zip_close", mydll.} +proc zip_delete*(para1: PZip, para2: int32): int32 {.cdecl, mydll, + importc: "zip_delete".} +proc zip_error_clear*(para1: PZip) {.cdecl, importc: "zip_error_clear", mydll.} +proc zip_error_get*(para1: PZip, para2: ptr int32, para3: ptr int32) {.cdecl, + importc: "zip_error_get", mydll.} +proc zip_error_get_sys_type*(para1: int32): int32 {.cdecl, mydll, + importc: "zip_error_get_sys_type".} +proc zip_error_to_str*(para1: cstring, para2: int, para3: int32, + para4: int32): int32 {.cdecl, mydll, + importc: "zip_error_to_str".} +proc zip_fclose*(para1: PZipFile) {.cdecl, mydll, + importc: "zip_fclose".} +proc zip_file_error_clear*(para1: PZipFile) {.cdecl, mydll, + importc: "zip_file_error_clear".} +proc zip_file_error_get*(para1: PZipFile, para2: ptr int32, para3: ptr int32) {. + cdecl, mydll, importc: "zip_file_error_get".} +proc zip_file_strerror*(para1: PZipFile): cstring {.cdecl, mydll, + importc: "zip_file_strerror".} +proc zip_fopen*(para1: PZip, para2: cstring, para3: int32): PZipFile {.cdecl, + mydll, importc: "zip_fopen".} +proc zip_fopen_index*(para1: PZip, para2: int32, para3: int32): PZipFile {. + cdecl, mydll, importc: "zip_fopen_index".} +proc zip_fread*(para1: PZipFile, para2: pointer, para3: int): int {. + cdecl, mydll, importc: "zip_fread".} +proc zip_get_archive_comment*(para1: PZip, para2: ptr int32, para3: int32): cstring {. + cdecl, mydll, importc: "zip_get_archive_comment".} +proc zip_get_archive_flag*(para1: PZip, para2: int32, para3: int32): int32 {. + cdecl, mydll, importc: "zip_get_archive_flag".} +proc zip_get_file_comment*(para1: PZip, para2: int32, para3: ptr int32, + para4: int32): cstring {.cdecl, mydll, + importc: "zip_get_file_comment".} +proc zip_get_name*(para1: PZip, para2: int32, para3: int32): cstring {.cdecl, + mydll, importc: "zip_get_name".} +proc zip_get_num_files*(para1: PZip): int32 {.cdecl, + mydll, importc: "zip_get_num_files".} +proc zip_name_locate*(para1: PZip, para2: cstring, para3: int32): int32 {.cdecl, + mydll, importc: "zip_name_locate".} +proc zip_open*(para1: cstring, para2: int32, para3: ptr int32): PZip {.cdecl, + mydll, importc: "zip_open".} +proc zip_rename*(para1: PZip, para2: int32, para3: cstring): int32 {.cdecl, + mydll, importc: "zip_rename".} +proc zip_replace*(para1: PZip, para2: int32, para3: PZipSource): int32 {.cdecl, + mydll, importc: "zip_replace".} +proc zip_set_archive_comment*(para1: PZip, para2: cstring, para3: int32): int32 {. + cdecl, mydll, importc: "zip_set_archive_comment".} +proc zip_set_archive_flag*(para1: PZip, para2: int32, para3: int32): int32 {. + cdecl, mydll, importc: "zip_set_archive_flag".} +proc zip_set_file_comment*(para1: PZip, para2: int32, para3: cstring, + para4: int32): int32 {.cdecl, mydll, + importc: "zip_set_file_comment".} +proc zip_source_buffer*(para1: PZip, para2: pointer, para3: int, para4: int32): PZipSource {. + cdecl, mydll, importc: "zip_source_buffer".} +proc zip_source_file*(para1: PZip, para2: cstring, para3: int, para4: int): PZipSource {. + cdecl, mydll, importc: "zip_source_file".} +proc zip_source_filep*(para1: PZip, para2: File, para3: int, para4: int): PZipSource {. + cdecl, mydll, importc: "zip_source_filep".} +proc zip_source_free*(para1: PZipSource) {.cdecl, mydll, + importc: "zip_source_free".} +proc zip_source_function*(para1: PZip, para2: ZipSourceCallback, + para3: pointer): PZipSource {.cdecl, mydll, + importc: "zip_source_function".} +proc zip_source_zip*(para1: PZip, para2: PZip, para3: int32, para4: int32, + para5: int, para6: int): PZipSource {.cdecl, mydll, + importc: "zip_source_zip".} +proc zip_stat*(para1: PZip, para2: cstring, para3: int32, para4: PZipStat): int32 {. + cdecl, mydll, importc: "zip_stat".} +proc zip_stat_index*(para1: PZip, para2: int32, para3: int32, para4: PZipStat): int32 {. + cdecl, mydll, importc: "zip_stat_index".} +proc zip_stat_init*(para1: PZipStat) {.cdecl, mydll, importc: "zip_stat_init".} +proc zip_strerror*(para1: PZip): cstring {.cdecl, mydll, importc: "zip_strerror".} +proc zip_unchange*(para1: PZip, para2: int32): int32 {.cdecl, mydll, + importc: "zip_unchange".} +proc zip_unchange_all*(para1: PZip): int32 {.cdecl, mydll, + importc: "zip_unchange_all".} +proc zip_unchange_archive*(para1: PZip): int32 {.cdecl, mydll, + importc: "zip_unchange_archive".} diff --git a/tests/deps/zip-0.2.1/zip/private/libzip_all.c b/tests/deps/zip-0.2.1/zip/private/libzip_all.c new file mode 100644 index 000000000..e0627a7eb --- /dev/null +++ b/tests/deps/zip-0.2.1/zip/private/libzip_all.c @@ -0,0 +1,4193 @@ +/* + zipint.h -- internal declarations. + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +/* +#ifdef _MSC_VER +#define ZIP_EXTERN __declspec(dllimport) +#endif +*/ + +/* + zip.h -- exported declarations. + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef ZIP_EXTERN +#define ZIP_EXTERN +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* flags for zip_open */ + +#define ZIP_CREATE 1 +#define ZIP_EXCL 2 +#define ZIP_CHECKCONS 4 + + +/* flags for zip_name_locate, zip_fopen, zip_stat, ... */ + +#define ZIP_FL_NOCASE 1 /* ignore case on name lookup */ +#define ZIP_FL_NODIR 2 /* ignore directory component */ +#define ZIP_FL_COMPRESSED 4 /* read compressed data */ +#define ZIP_FL_UNCHANGED 8 /* use original data, ignoring changes */ +#define ZIP_FL_RECOMPRESS 16 /* force recompression of data */ + +/* archive global flags flags */ + +#define ZIP_AFL_TORRENT 1 /* torrent zipped */ + +/* libzip error codes */ + +#define ZIP_ER_OK 0 /* N No error */ +#define ZIP_ER_MULTIDISK 1 /* N Multi-disk zip archives not supported */ +#define ZIP_ER_RENAME 2 /* S Renaming temporary file failed */ +#define ZIP_ER_CLOSE 3 /* S Closing zip archive failed */ +#define ZIP_ER_SEEK 4 /* S Seek error */ +#define ZIP_ER_READ 5 /* S Read error */ +#define ZIP_ER_WRITE 6 /* S Write error */ +#define ZIP_ER_CRC 7 /* N CRC error */ +#define ZIP_ER_ZIPCLOSED 8 /* N Containing zip archive was closed */ +#define ZIP_ER_NOENT 9 /* N No such file */ +#define ZIP_ER_EXISTS 10 /* N File already exists */ +#define ZIP_ER_OPEN 11 /* S Can't open file */ +#define ZIP_ER_TMPOPEN 12 /* S Failure to create temporary file */ +#define ZIP_ER_ZLIB 13 /* Z Zlib error */ +#define ZIP_ER_MEMORY 14 /* N Malloc failure */ +#define ZIP_ER_CHANGED 15 /* N Entry has been changed */ +#define ZIP_ER_COMPNOTSUPP 16 /* N Compression method not supported */ +#define ZIP_ER_EOF 17 /* N Premature EOF */ +#define ZIP_ER_INVAL 18 /* N Invalid argument */ +#define ZIP_ER_NOZIP 19 /* N Not a zip archive */ +#define ZIP_ER_INTERNAL 20 /* N Internal error */ +#define ZIP_ER_INCONS 21 /* N Zip archive inconsistent */ +#define ZIP_ER_REMOVE 22 /* S Can't remove file */ +#define ZIP_ER_DELETED 23 /* N Entry has been deleted */ + + +/* type of system error value */ + +#define ZIP_ET_NONE 0 /* sys_err unused */ +#define ZIP_ET_SYS 1 /* sys_err is errno */ +#define ZIP_ET_ZLIB 2 /* sys_err is zlib error code */ + +/* compression methods */ + +#define ZIP_CM_DEFAULT -1 /* better of deflate or store */ +#define ZIP_CM_STORE 0 /* stored (uncompressed) */ +#define ZIP_CM_SHRINK 1 /* shrunk */ +#define ZIP_CM_REDUCE_1 2 /* reduced with factor 1 */ +#define ZIP_CM_REDUCE_2 3 /* reduced with factor 2 */ +#define ZIP_CM_REDUCE_3 4 /* reduced with factor 3 */ +#define ZIP_CM_REDUCE_4 5 /* reduced with factor 4 */ +#define ZIP_CM_IMPLODE 6 /* imploded */ +/* 7 - Reserved for Tokenizing compression algorithm */ +#define ZIP_CM_DEFLATE 8 /* deflated */ +#define ZIP_CM_DEFLATE64 9 /* deflate64 */ +#define ZIP_CM_PKWARE_IMPLODE 10 /* PKWARE imploding */ +/* 11 - Reserved by PKWARE */ +#define ZIP_CM_BZIP2 12 /* compressed using BZIP2 algorithm */ +/* 13 - Reserved by PKWARE */ +#define ZIP_CM_LZMA 14 /* LZMA (EFS) */ +/* 15-17 - Reserved by PKWARE */ +#define ZIP_CM_TERSE 18 /* compressed using IBM TERSE (new) */ +#define ZIP_CM_LZ77 19 /* IBM LZ77 z Architecture (PFS) */ +#define ZIP_CM_WAVPACK 97 /* WavPack compressed data */ +#define ZIP_CM_PPMD 98 /* PPMd version I, Rev 1 */ + +/* encryption methods */ + +#define ZIP_EM_NONE 0 /* not encrypted */ +#define ZIP_EM_TRAD_PKWARE 1 /* traditional PKWARE encryption */ +#if 0 /* Strong Encryption Header not parsed yet */ +#define ZIP_EM_DES 0x6601 /* strong encryption: DES */ +#define ZIP_EM_RC2_OLD 0x6602 /* strong encryption: RC2, version < 5.2 */ +#define ZIP_EM_3DES_168 0x6603 +#define ZIP_EM_3DES_112 0x6609 +#define ZIP_EM_AES_128 0x660e +#define ZIP_EM_AES_192 0x660f +#define ZIP_EM_AES_256 0x6610 +#define ZIP_EM_RC2 0x6702 /* strong encryption: RC2, version >= 5.2 */ +#define ZIP_EM_RC4 0x6801 +#endif +#define ZIP_EM_UNKNOWN 0xffff /* unknown algorithm */ + +typedef long myoff_t; /* XXX: 64 bit support */ + +enum zip_source_cmd { + ZIP_SOURCE_OPEN, /* prepare for reading */ + ZIP_SOURCE_READ, /* read data */ + ZIP_SOURCE_CLOSE, /* reading is done */ + ZIP_SOURCE_STAT, /* get meta information */ + ZIP_SOURCE_ERROR, /* get error information */ + ZIP_SOURCE_FREE /* cleanup and free resources */ +}; + +typedef ssize_t (*zip_source_callback)(void *state, void *data, + size_t len, enum zip_source_cmd cmd); + +struct zip_stat { + const char *name; /* name of the file */ + int index; /* index within archive */ + unsigned int crc; /* crc of file data */ + time_t mtime; /* modification time */ + myoff_t size; /* size of file (uncompressed) */ + myoff_t comp_size; /* size of file (compressed) */ + unsigned short comp_method; /* compression method used */ + unsigned short encryption_method; /* encryption method used */ +}; + +struct zip; +struct zip_file; +struct zip_source; + + +ZIP_EXTERN int zip_add(struct zip *, const char *, struct zip_source *); +ZIP_EXTERN int zip_add_dir(struct zip *, const char *); +ZIP_EXTERN int zip_close(struct zip *); +ZIP_EXTERN int zip_delete(struct zip *, int); +ZIP_EXTERN void zip_error_clear(struct zip *); +ZIP_EXTERN void zip_error_get(struct zip *, int *, int *); +ZIP_EXTERN int zip_error_get_sys_type(int); +ZIP_EXTERN int zip_error_to_str(char *, size_t, int, int); +ZIP_EXTERN int zip_fclose(struct zip_file *); +ZIP_EXTERN void zip_file_error_clear(struct zip_file *); +ZIP_EXTERN void zip_file_error_get(struct zip_file *, int *, int *); +ZIP_EXTERN const char *zip_file_strerror(struct zip_file *); +ZIP_EXTERN struct zip_file *zip_fopen(struct zip *, const char *, int); +ZIP_EXTERN struct zip_file *zip_fopen_index(struct zip *, int, int); +ZIP_EXTERN ssize_t zip_fread(struct zip_file *, void *, size_t); +ZIP_EXTERN const char *zip_get_archive_comment(struct zip *, int *, int); +ZIP_EXTERN int zip_get_archive_flag(struct zip *, int, int); +ZIP_EXTERN const char *zip_get_file_comment(struct zip *, int, int *, int); +ZIP_EXTERN const char *zip_get_name(struct zip *, int, int); +ZIP_EXTERN int zip_get_num_files(struct zip *); +ZIP_EXTERN int zip_name_locate(struct zip *, const char *, int); +ZIP_EXTERN struct zip *zip_open(const char *, int, int *); +ZIP_EXTERN int zip_rename(struct zip *, int, const char *); +ZIP_EXTERN int zip_replace(struct zip *, int, struct zip_source *); +ZIP_EXTERN int zip_set_archive_comment(struct zip *, const char *, int); +ZIP_EXTERN int zip_set_archive_flag(struct zip *, int, int); +ZIP_EXTERN int zip_set_file_comment(struct zip *, int, const char *, int); +ZIP_EXTERN struct zip_source *zip_source_buffer(struct zip *, const void *, + myoff_t, int); +ZIP_EXTERN struct zip_source *zip_source_file(struct zip *, const char *, + myoff_t, myoff_t); +ZIP_EXTERN struct zip_source *zip_source_filep(struct zip *, FILE *, + myoff_t, myoff_t); +ZIP_EXTERN void zip_source_free(struct zip_source *); +ZIP_EXTERN struct zip_source *zip_source_function(struct zip *, + zip_source_callback, void *); +ZIP_EXTERN struct zip_source *zip_source_zip(struct zip *, struct zip *, + int, int, myoff_t, myoff_t); +ZIP_EXTERN int zip_stat(struct zip *, const char *, int, struct zip_stat *); +ZIP_EXTERN int zip_stat_index(struct zip *, int, int, struct zip_stat *); +ZIP_EXTERN void zip_stat_init(struct zip_stat *); +ZIP_EXTERN const char *zip_strerror(struct zip *); +ZIP_EXTERN int zip_unchange(struct zip *, int); +ZIP_EXTERN int zip_unchange_all(struct zip *); +ZIP_EXTERN int zip_unchange_archive(struct zip *); + +#ifdef __cplusplus +} +#endif + + +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the declaration of `tzname', and to 0 if you don't. + */ +/* #undef HAVE_DECL_TZNAME */ + +#define HAVE_CONFIG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `fseeko' function. */ +#define HAVE_FSEEKO 1 + +/* Define to 1 if you have the `ftello' function. */ +#define HAVE_FTELLO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mkstemp' function. */ +#define HAVE_MKSTEMP 1 + +/* Define to 1 if you have the `MoveFileExA' function. */ +/* #undef HAVE_MOVEFILEEXA */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if `tm_zone' is member of `struct tm'. */ +#ifdef WIN32 +#undef HAVE_STRUCT_TM_TM_ZONE +#else +#define HAVE_STRUCT_TM_TM_ZONE 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +#define HAVE_TM_ZONE 1 + +/* Define to 1 if you don't have `tm_zone' but do have the external array + `tzname'. */ +/* #undef HAVE_TZNAME */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Name of package */ +#define PACKAGE "libzip" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "libzip@nih.at" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libzip" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libzip 0.9" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libzip" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.9" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Version number of package */ +#define VERSION "0.9" + + +#ifndef HAVE_MKSTEMP +int _zip_mkstemp(char *); +#define mkstemp _zip_mkstemp +#endif + +#ifdef HAVE_MOVEFILEEXA +#include +#define _zip_rename(s, t) \ + (!MoveFileExA((s), (t), \ + MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING)) +#else +#define _zip_rename rename +#endif + +#ifndef HAVE_FSEEKO +#define fseeko(s, o, w) (fseek((s), (long int)(o), (w))) +#endif +#ifndef HAVE_FTELLO +#define ftello(s) ((long)ftell((s))) +#endif + + +#define CENTRAL_MAGIC "PK\1\2" +#define LOCAL_MAGIC "PK\3\4" +#define EOCD_MAGIC "PK\5\6" +#define DATADES_MAGIC "PK\7\8" +#define TORRENT_SIG "TORRENTZIPPED-" +#define TORRENT_SIG_LEN 14 +#define TORRENT_CRC_LEN 8 +#define TORRENT_MEM_LEVEL 8 +#define CDENTRYSIZE 46u +#define LENTRYSIZE 30 +#define MAXCOMLEN 65536 +#define EOCDLEN 22 +#define CDBUFSIZE (MAXCOMLEN+EOCDLEN) +#define BUFSIZE 8192 + + +/* state of change of a file in zip archive */ + +enum zip_state { ZIP_ST_UNCHANGED, ZIP_ST_DELETED, ZIP_ST_REPLACED, + ZIP_ST_ADDED, ZIP_ST_RENAMED }; + +/* constants for struct zip_file's member flags */ + +#define ZIP_ZF_EOF 1 /* EOF reached */ +#define ZIP_ZF_DECOMP 2 /* decompress data */ +#define ZIP_ZF_CRC 4 /* compute and compare CRC */ + +/* directory entry: general purpose bit flags */ + +#define ZIP_GPBF_ENCRYPTED 0x0001 /* is encrypted */ +#define ZIP_GPBF_DATA_DESCRIPTOR 0x0008 /* crc/size after file data */ +#define ZIP_GPBF_STRONG_ENCRYPTION 0x0040 /* uses strong encryption */ + +/* error information */ + +struct zip_error { + int zip_err; /* libzip error code (ZIP_ER_*) */ + int sys_err; /* copy of errno (E*) or zlib error code */ + char *str; /* string representation or NULL */ +}; + +/* zip archive, part of API */ + +struct zip { + char *zn; /* file name */ + FILE *zp; /* file */ + struct zip_error error; /* error information */ + + unsigned int flags; /* archive global flags */ + unsigned int ch_flags; /* changed archive global flags */ + + struct zip_cdir *cdir; /* central directory */ + char *ch_comment; /* changed archive comment */ + int ch_comment_len; /* length of changed zip archive + * comment, -1 if unchanged */ + int nentry; /* number of entries */ + int nentry_alloc; /* number of entries allocated */ + struct zip_entry *entry; /* entries */ + int nfile; /* number of opened files within archive */ + int nfile_alloc; /* number of files allocated */ + struct zip_file **file; /* opened files within archive */ +}; + +/* file in zip archive, part of API */ + +struct zip_file { + struct zip *za; /* zip archive containing this file */ + struct zip_error error; /* error information */ + int flags; /* -1: eof, >0: error */ + + int method; /* compression method */ + myoff_t fpos; /* position within zip file (fread/fwrite) */ + unsigned long bytes_left; /* number of bytes left to read */ + unsigned long cbytes_left; /* number of bytes of compressed data left */ + + unsigned long crc; /* CRC so far */ + unsigned long crc_orig; /* CRC recorded in archive */ + + char *buffer; + z_stream *zstr; +}; + +/* zip archive directory entry (central or local) */ + +struct zip_dirent { + unsigned short version_madeby; /* (c) version of creator */ + unsigned short version_needed; /* (cl) version needed to extract */ + unsigned short bitflags; /* (cl) general purpose bit flag */ + unsigned short comp_method; /* (cl) compression method used */ + time_t last_mod; /* (cl) time of last modification */ + unsigned int crc; /* (cl) CRC-32 of uncompressed data */ + unsigned int comp_size; /* (cl) size of commpressed data */ + unsigned int uncomp_size; /* (cl) size of uncommpressed data */ + char *filename; /* (cl) file name (NUL-terminated) */ + unsigned short filename_len; /* (cl) length of filename (w/o NUL) */ + char *extrafield; /* (cl) extra field */ + unsigned short extrafield_len; /* (cl) length of extra field */ + char *comment; /* (c) file comment */ + unsigned short comment_len; /* (c) length of file comment */ + unsigned short disk_number; /* (c) disk number start */ + unsigned short int_attrib; /* (c) internal file attributes */ + unsigned int ext_attrib; /* (c) external file attributes */ + unsigned int offset; /* (c) offset of local header */ +}; + +/* zip archive central directory */ + +struct zip_cdir { + struct zip_dirent *entry; /* directory entries */ + int nentry; /* number of entries */ + + unsigned int size; /* size of central direcotry */ + unsigned int offset; /* offset of central directory in file */ + char *comment; /* zip archive comment */ + unsigned short comment_len; /* length of zip archive comment */ +}; + + + +struct zip_source { + zip_source_callback f; + void *ud; +}; + +/* entry in zip archive directory */ + +struct zip_entry { + enum zip_state state; + struct zip_source *source; + char *ch_filename; + char *ch_comment; + int ch_comment_len; +}; + + + +extern const char * const _zip_err_str[]; +extern const int _zip_nerr_str; +extern const int _zip_err_type[]; + + + +#define ZIP_ENTRY_DATA_CHANGED(x) \ + ((x)->state == ZIP_ST_REPLACED \ + || (x)->state == ZIP_ST_ADDED) + + + +int _zip_cdir_compute_crc(struct zip *, uLong *); +void _zip_cdir_free(struct zip_cdir *); +struct zip_cdir *_zip_cdir_new(int, struct zip_error *); +int _zip_cdir_write(struct zip_cdir *, FILE *, struct zip_error *); + +void _zip_dirent_finalize(struct zip_dirent *); +void _zip_dirent_init(struct zip_dirent *); +int _zip_dirent_read(struct zip_dirent *, FILE *, + unsigned char **, unsigned int, int, struct zip_error *); +void _zip_dirent_torrent_normalize(struct zip_dirent *); +int _zip_dirent_write(struct zip_dirent *, FILE *, int, struct zip_error *); + +void _zip_entry_free(struct zip_entry *); +void _zip_entry_init(struct zip *, int); +struct zip_entry *_zip_entry_new(struct zip *); + +void _zip_error_clear(struct zip_error *); +void _zip_error_copy(struct zip_error *, struct zip_error *); +void _zip_error_fini(struct zip_error *); +void _zip_error_get(struct zip_error *, int *, int *); +void _zip_error_init(struct zip_error *); +void _zip_error_set(struct zip_error *, int, int); +const char *_zip_error_strerror(struct zip_error *); + +int _zip_file_fillbuf(void *, size_t, struct zip_file *); +unsigned int _zip_file_get_offset(struct zip *, int); + +int _zip_filerange_crc(FILE *, myoff_t, myoff_t, uLong *, struct zip_error *); + +struct zip_source *_zip_source_file_or_p(struct zip *, const char *, FILE *, + myoff_t, myoff_t); + +void _zip_free(struct zip *); +const char *_zip_get_name(struct zip *, int, int, struct zip_error *); +int _zip_local_header_read(struct zip *, int); +void *_zip_memdup(const void *, size_t, struct zip_error *); +int _zip_name_locate(struct zip *, const char *, int, struct zip_error *); +struct zip *_zip_new(struct zip_error *); +unsigned short _zip_read2(unsigned char **); +unsigned int _zip_read4(unsigned char **); +int _zip_replace(struct zip *, int, const char *, struct zip_source *); +int _zip_set_name(struct zip *, int, const char *); +int _zip_unchange(struct zip *, int, int); +void _zip_unchange_data(struct zip_entry *); + + +#include +#include +#include +#include + +const char * +_zip_error_strerror(struct zip_error *err) +{ + const char *zs, *ss; + char buf[128], *s; + + _zip_error_fini(err); + + if (err->zip_err < 0 || err->zip_err >= _zip_nerr_str) { + sprintf(buf, "Unknown error %d", err->zip_err); + zs = NULL; + ss = buf; + } + else { + zs = _zip_err_str[err->zip_err]; + + switch (_zip_err_type[err->zip_err]) { + case ZIP_ET_SYS: + ss = strerror(err->sys_err); + break; + + case ZIP_ET_ZLIB: + ss = zError(err->sys_err); + break; + + default: + ss = NULL; + } + } + + if (ss == NULL) + return zs; + else { + if ((s=(char *)malloc(strlen(ss) + + (zs ? strlen(zs)+2 : 0) + 1)) == NULL) + return _zip_err_str[ZIP_ER_MEMORY]; + + sprintf(s, "%s%s%s", + (zs ? zs : ""), + (zs ? ": " : ""), + ss); + err->str = s; + + return s; + } +} + +#include + + + +void +_zip_error_clear(struct zip_error *err) +{ + err->zip_err = ZIP_ER_OK; + err->sys_err = 0; +} + + + +void +_zip_error_copy(struct zip_error *dst, struct zip_error *src) +{ + dst->zip_err = src->zip_err; + dst->sys_err = src->sys_err; +} + + + +void +_zip_error_fini(struct zip_error *err) +{ + free(err->str); + err->str = NULL; +} + + + +void +_zip_error_get(struct zip_error *err, int *zep, int *sep) +{ + if (zep) + *zep = err->zip_err; + if (sep) { + if (zip_error_get_sys_type(err->zip_err) != ZIP_ET_NONE) + *sep = err->sys_err; + else + *sep = 0; + } +} + + + +void +_zip_error_init(struct zip_error *err) +{ + err->zip_err = ZIP_ER_OK; + err->sys_err = 0; + err->str = NULL; +} + + + +void +_zip_error_set(struct zip_error *err, int ze, int se) +{ + if (err) { + err->zip_err = ze; + err->sys_err = se; + } +} + + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + + + +int +_zip_mkstemp(char *path) +{ + int fd; + char *start, *trv; + struct stat sbuf; + pid_t pid; + + /* To guarantee multiple calls generate unique names even if + the file is not created. 676 different possibilities with 7 + or more X's, 26 with 6 or less. */ + static char xtra[2] = "aa"; + int xcnt = 0; + + pid = getpid(); + + /* Move to end of path and count trailing X's. */ + for (trv = path; *trv; ++trv) + if (*trv == 'X') + xcnt++; + else + xcnt = 0; + + /* Use at least one from xtra. Use 2 if more than 6 X's. */ + if (*(trv - 1) == 'X') + *--trv = xtra[0]; + if (xcnt > 6 && *(trv - 1) == 'X') + *--trv = xtra[1]; + + /* Set remaining X's to pid digits with 0's to the left. */ + while (*--trv == 'X') { + *trv = (pid % 10) + '0'; + pid /= 10; + } + + /* update xtra for next call. */ + if (xtra[0] != 'z') + xtra[0]++; + else { + xtra[0] = 'a'; + if (xtra[1] != 'z') + xtra[1]++; + else + xtra[1] = 'a'; + } + + /* + * check the target directory; if you have six X's and it + * doesn't exist this runs for a *very* long time. + */ + for (start = trv + 1;; --trv) { + if (trv <= path) + break; + if (*trv == '/') { + *trv = '\0'; + if (stat(path, &sbuf)) + return (0); + if (!S_ISDIR(sbuf.st_mode)) { + errno = ENOTDIR; + return (0); + } + *trv = '/'; + break; + } + } + + for (;;) { + if ((fd=open(path, O_CREAT|O_EXCL|O_RDWR|O_BINARY, 0600)) >= 0) + return (fd); + if (errno != EEXIST) + return (0); + + /* tricky little algorithm for backward compatibility */ + for (trv = start;;) { + if (!*trv) + return (0); + if (*trv == 'z') + *trv++ = 'a'; + else { + if (isdigit((unsigned char)*trv)) + *trv = 'a'; + else + ++*trv; + break; + } + } + } + /*NOTREACHED*/ +} + + +#include +#include +#include +#include +#include +#include + +static time_t _zip_d2u_time(int, int); +static char *_zip_readfpstr(FILE *, unsigned int, int, struct zip_error *); +static char *_zip_readstr(unsigned char **, int, int, struct zip_error *); +static void _zip_u2d_time(time_t, unsigned short *, unsigned short *); +static void _zip_write2(unsigned short, FILE *); +static void _zip_write4(unsigned int, FILE *); + + + +void +_zip_cdir_free(struct zip_cdir *cd) +{ + int i; + + if (!cd) + return; + + for (i=0; inentry; i++) + _zip_dirent_finalize(cd->entry+i); + free(cd->comment); + free(cd->entry); + free(cd); +} + + + +struct zip_cdir * +_zip_cdir_new(int nentry, struct zip_error *error) +{ + struct zip_cdir *cd; + + if ((cd=(struct zip_cdir *)malloc(sizeof(*cd))) == NULL) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if ((cd->entry=(struct zip_dirent *)malloc(sizeof(*(cd->entry))*nentry)) + == NULL) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + free(cd); + return NULL; + } + + /* entries must be initialized by caller */ + + cd->nentry = nentry; + cd->size = cd->offset = 0; + cd->comment = NULL; + cd->comment_len = 0; + + return cd; +} + + + +int +_zip_cdir_write(struct zip_cdir *cd, FILE *fp, struct zip_error *error) +{ + int i; + + cd->offset = ftello(fp); + + for (i=0; inentry; i++) { + if (_zip_dirent_write(cd->entry+i, fp, 0, error) != 0) + return -1; + } + + cd->size = ftello(fp) - cd->offset; + + /* clearerr(fp); */ + fwrite(EOCD_MAGIC, 1, 4, fp); + _zip_write4(0, fp); + _zip_write2((unsigned short)cd->nentry, fp); + _zip_write2((unsigned short)cd->nentry, fp); + _zip_write4(cd->size, fp); + _zip_write4(cd->offset, fp); + _zip_write2(cd->comment_len, fp); + fwrite(cd->comment, 1, cd->comment_len, fp); + + if (ferror(fp)) { + _zip_error_set(error, ZIP_ER_WRITE, errno); + return -1; + } + + return 0; +} + + + +void +_zip_dirent_finalize(struct zip_dirent *zde) +{ + free(zde->filename); + zde->filename = NULL; + free(zde->extrafield); + zde->extrafield = NULL; + free(zde->comment); + zde->comment = NULL; +} + + + +void +_zip_dirent_init(struct zip_dirent *de) +{ + de->version_madeby = 0; + de->version_needed = 20; /* 2.0 */ + de->bitflags = 0; + de->comp_method = 0; + de->last_mod = 0; + de->crc = 0; + de->comp_size = 0; + de->uncomp_size = 0; + de->filename = NULL; + de->filename_len = 0; + de->extrafield = NULL; + de->extrafield_len = 0; + de->comment = NULL; + de->comment_len = 0; + de->disk_number = 0; + de->int_attrib = 0; + de->ext_attrib = 0; + de->offset = 0; +} + + + +/* _zip_dirent_read(zde, fp, bufp, left, localp, error): + Fills the zip directory entry zde. + + If bufp is non-NULL, data is taken from there and bufp is advanced + by the amount of data used; no more than left bytes are used. + Otherwise data is read from fp as needed. + + If localp != 0, it reads a local header instead of a central + directory entry. + + Returns 0 if successful. On error, error is filled in and -1 is + returned. +*/ + +int +_zip_dirent_read(struct zip_dirent *zde, FILE *fp, + unsigned char **bufp, unsigned int left, int localp, + struct zip_error *error) +{ + unsigned char buf[CDENTRYSIZE]; + unsigned char *cur; + unsigned short dostime, dosdate; + unsigned int size; + + if (localp) + size = LENTRYSIZE; + else + size = CDENTRYSIZE; + + if (bufp) { + /* use data from buffer */ + cur = *bufp; + if (left < size) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return -1; + } + } + else { + /* read entry from disk */ + if ((fread(buf, 1, size, fp)version_madeby = _zip_read2(&cur); + else + zde->version_madeby = 0; + zde->version_needed = _zip_read2(&cur); + zde->bitflags = _zip_read2(&cur); + zde->comp_method = _zip_read2(&cur); + + /* convert to time_t */ + dostime = _zip_read2(&cur); + dosdate = _zip_read2(&cur); + zde->last_mod = _zip_d2u_time(dostime, dosdate); + + zde->crc = _zip_read4(&cur); + zde->comp_size = _zip_read4(&cur); + zde->uncomp_size = _zip_read4(&cur); + + zde->filename_len = _zip_read2(&cur); + zde->extrafield_len = _zip_read2(&cur); + + if (localp) { + zde->comment_len = 0; + zde->disk_number = 0; + zde->int_attrib = 0; + zde->ext_attrib = 0; + zde->offset = 0; + } else { + zde->comment_len = _zip_read2(&cur); + zde->disk_number = _zip_read2(&cur); + zde->int_attrib = _zip_read2(&cur); + zde->ext_attrib = _zip_read4(&cur); + zde->offset = _zip_read4(&cur); + } + + zde->filename = NULL; + zde->extrafield = NULL; + zde->comment = NULL; + + if (bufp) { + if (left < CDENTRYSIZE + (zde->filename_len+zde->extrafield_len + +zde->comment_len)) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return -1; + } + + if (zde->filename_len) { + zde->filename = _zip_readstr(&cur, zde->filename_len, 1, error); + if (!zde->filename) + return -1; + } + + if (zde->extrafield_len) { + zde->extrafield = _zip_readstr(&cur, zde->extrafield_len, 0, + error); + if (!zde->extrafield) + return -1; + } + + if (zde->comment_len) { + zde->comment = _zip_readstr(&cur, zde->comment_len, 0, error); + if (!zde->comment) + return -1; + } + } + else { + if (zde->filename_len) { + zde->filename = _zip_readfpstr(fp, zde->filename_len, 1, error); + if (!zde->filename) + return -1; + } + + if (zde->extrafield_len) { + zde->extrafield = _zip_readfpstr(fp, zde->extrafield_len, 0, + error); + if (!zde->extrafield) + return -1; + } + + if (zde->comment_len) { + zde->comment = _zip_readfpstr(fp, zde->comment_len, 0, error); + if (!zde->comment) + return -1; + } + } + + if (bufp) + *bufp = cur; + + return 0; +} + + + +/* _zip_dirent_torrent_normalize(de); + Set values suitable for torrentzip. +*/ + +void +_zip_dirent_torrent_normalize(struct zip_dirent *de) +{ + static struct tm torrenttime; + static time_t last_mod = 0; + + if (last_mod == 0) { +#ifdef HAVE_STRUCT_TM_TM_ZONE + time_t now; + struct tm *l; +#endif + + torrenttime.tm_sec = 0; + torrenttime.tm_min = 32; + torrenttime.tm_hour = 23; + torrenttime.tm_mday = 24; + torrenttime.tm_mon = 11; + torrenttime.tm_year = 96; + torrenttime.tm_wday = 0; + torrenttime.tm_yday = 0; + torrenttime.tm_isdst = 0; + +#ifdef HAVE_STRUCT_TM_TM_ZONE + time(&now); + l = localtime(&now); + torrenttime.tm_gmtoff = l->tm_gmtoff; + torrenttime.tm_zone = l->tm_zone; +#endif + + last_mod = mktime(&torrenttime); + } + + de->version_madeby = 0; + de->version_needed = 20; /* 2.0 */ + de->bitflags = 2; /* maximum compression */ + de->comp_method = ZIP_CM_DEFLATE; + de->last_mod = last_mod; + + de->disk_number = 0; + de->int_attrib = 0; + de->ext_attrib = 0; + de->offset = 0; + + free(de->extrafield); + de->extrafield = NULL; + de->extrafield_len = 0; + free(de->comment); + de->comment = NULL; + de->comment_len = 0; +} + + + +/* _zip_dirent_write(zde, fp, localp, error): + Writes zip directory entry zde to file fp. + + If localp != 0, it writes a local header instead of a central + directory entry. + + Returns 0 if successful. On error, error is filled in and -1 is + returned. +*/ + +int +_zip_dirent_write(struct zip_dirent *zde, FILE *fp, int localp, + struct zip_error *error) +{ + unsigned short dostime, dosdate; + + fwrite(localp ? LOCAL_MAGIC : CENTRAL_MAGIC, 1, 4, fp); + + if (!localp) + _zip_write2(zde->version_madeby, fp); + _zip_write2(zde->version_needed, fp); + _zip_write2(zde->bitflags, fp); + _zip_write2(zde->comp_method, fp); + + _zip_u2d_time(zde->last_mod, &dostime, &dosdate); + _zip_write2(dostime, fp); + _zip_write2(dosdate, fp); + + _zip_write4(zde->crc, fp); + _zip_write4(zde->comp_size, fp); + _zip_write4(zde->uncomp_size, fp); + + _zip_write2(zde->filename_len, fp); + _zip_write2(zde->extrafield_len, fp); + + if (!localp) { + _zip_write2(zde->comment_len, fp); + _zip_write2(zde->disk_number, fp); + _zip_write2(zde->int_attrib, fp); + _zip_write4(zde->ext_attrib, fp); + _zip_write4(zde->offset, fp); + } + + if (zde->filename_len) + fwrite(zde->filename, 1, zde->filename_len, fp); + + if (zde->extrafield_len) + fwrite(zde->extrafield, 1, zde->extrafield_len, fp); + + if (!localp) { + if (zde->comment_len) + fwrite(zde->comment, 1, zde->comment_len, fp); + } + + if (ferror(fp)) { + _zip_error_set(error, ZIP_ER_WRITE, errno); + return -1; + } + + return 0; +} + + + +static time_t +_zip_d2u_time(int dtime, int ddate) +{ + struct tm *tm; + time_t now; + + now = time(NULL); + tm = localtime(&now); + /* let mktime decide if DST is in effect */ + tm->tm_isdst = -1; + + tm->tm_year = ((ddate>>9)&127) + 1980 - 1900; + tm->tm_mon = ((ddate>>5)&15) - 1; + tm->tm_mday = ddate&31; + + tm->tm_hour = (dtime>>11)&31; + tm->tm_min = (dtime>>5)&63; + tm->tm_sec = (dtime<<1)&62; + + return mktime(tm); +} + + + +unsigned short +_zip_read2(unsigned char **a) +{ + unsigned short ret; + + ret = (*a)[0]+((*a)[1]<<8); + *a += 2; + + return ret; +} + + + +unsigned int +_zip_read4(unsigned char **a) +{ + unsigned int ret; + + ret = ((((((*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0]; + *a += 4; + + return ret; +} + + + +static char * +_zip_readfpstr(FILE *fp, unsigned int len, int nulp, struct zip_error *error) +{ + char *r, *o; + + r = (char *)malloc(nulp ? len+1 : len); + if (!r) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if (fread(r, 1, len, fp)>8)&0xff, fp); + + return; +} + + + +static void +_zip_write4(unsigned int i, FILE *fp) +{ + putc(i&0xff, fp); + putc((i>>8)&0xff, fp); + putc((i>>16)&0xff, fp); + putc((i>>24)&0xff, fp); + + return; +} + + + +static void +_zip_u2d_time(time_t time, unsigned short *dtime, unsigned short *ddate) +{ + struct tm *tm; + + tm = localtime(&time); + *ddate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + + tm->tm_mday; + *dtime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + + ((tm->tm_sec)>>1); + + return; +} + + + +ZIP_EXTERN int +zip_delete(struct zip *za, int idx) +{ + if (idx < 0 || idx >= za->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + /* allow duplicate file names, because the file will + * be removed directly afterwards */ + if (_zip_unchange(za, idx, 1) != 0) + return -1; + + za->entry[idx].state = ZIP_ST_DELETED; + + return 0; +} + + + +ZIP_EXTERN void +zip_error_clear(struct zip *za) +{ + _zip_error_clear(&za->error); +} + + +ZIP_EXTERN int +zip_add(struct zip *za, const char *name, struct zip_source *source) +{ + if (name == NULL || source == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + return _zip_replace(za, -1, name, source); +} + + +ZIP_EXTERN int +zip_error_get_sys_type(int ze) +{ + if (ze < 0 || ze >= _zip_nerr_str) + return 0; + + return _zip_err_type[ze]; +} + + +ZIP_EXTERN void +zip_error_get(struct zip *za, int *zep, int *sep) +{ + _zip_error_get(&za->error, zep, sep); +} + + +const char * const _zip_err_str[] = { + "No error", + "Multi-disk zip archives not supported", + "Renaming temporary file failed", + "Closing zip archive failed", + "Seek error", + "Read error", + "Write error", + "CRC error", + "Containing zip archive was closed", + "No such file", + "File already exists", + "Can't open file", + "Failure to create temporary file", + "Zlib error", + "Malloc failure", + "Entry has been changed", + "Compression method not supported", + "Premature EOF", + "Invalid argument", + "Not a zip archive", + "Internal error", + "Zip archive inconsistent", + "Can't remove file", + "Entry has been deleted", +}; + +const int _zip_nerr_str = sizeof(_zip_err_str)/sizeof(_zip_err_str[0]); + +#define N ZIP_ET_NONE +#define S ZIP_ET_SYS +#define Z ZIP_ET_ZLIB + +const int _zip_err_type[] = { + N, + N, + S, + S, + S, + S, + S, + N, + N, + N, + N, + S, + S, + Z, + N, + N, + N, + N, + N, + N, + N, + N, + S, + N, +}; + + +struct zip_entry * +_zip_entry_new(struct zip *za) +{ + struct zip_entry *ze; + if (!za) { + ze = (struct zip_entry *)malloc(sizeof(struct zip_entry)); + if (!ze) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + } + else { + if (za->nentry >= za->nentry_alloc-1) { + za->nentry_alloc += 16; + za->entry = (struct zip_entry *)realloc(za->entry, + sizeof(struct zip_entry) + * za->nentry_alloc); + if (!za->entry) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + } + ze = za->entry+za->nentry; + } + + ze->state = ZIP_ST_UNCHANGED; + + ze->ch_filename = NULL; + ze->ch_comment = NULL; + ze->ch_comment_len = -1; + ze->source = NULL; + + if (za) + za->nentry++; + + return ze; +} + + +void +_zip_entry_free(struct zip_entry *ze) +{ + free(ze->ch_filename); + ze->ch_filename = NULL; + free(ze->ch_comment); + ze->ch_comment = NULL; + ze->ch_comment_len = -1; + + _zip_unchange_data(ze); +} + + +static int add_data(struct zip *, struct zip_source *, struct zip_dirent *, + FILE *); +static int add_data_comp(zip_source_callback, void *, struct zip_stat *, + FILE *, struct zip_error *); +static int add_data_uncomp(struct zip *, zip_source_callback, void *, + struct zip_stat *, FILE *); +static void ch_set_error(struct zip_error *, zip_source_callback, void *); +static int copy_data(FILE *, myoff_t, FILE *, struct zip_error *); +static int write_cdir(struct zip *, struct zip_cdir *, FILE *); +static int _zip_cdir_set_comment(struct zip_cdir *, struct zip *); +static int _zip_changed(struct zip *, int *); +static char *_zip_create_temp_output(struct zip *, FILE **); +static int _zip_torrentzip_cmp(const void *, const void *); + + + +struct filelist { + int idx; + const char *name; +}; + + + +ZIP_EXTERN int +zip_close(struct zip *za) +{ + int survivors; + int i, j, error; + char *temp; + FILE *out; + mode_t mask; + struct zip_cdir *cd; + struct zip_dirent de; + struct filelist *filelist; + int reopen_on_error; + int new_torrentzip; + + reopen_on_error = 0; + + if (za == NULL) + return -1; + + if (!_zip_changed(za, &survivors)) { + _zip_free(za); + return 0; + } + + /* don't create zip files with no entries */ + if (survivors == 0) { + if (za->zn && za->zp) { + if (remove(za->zn) != 0) { + _zip_error_set(&za->error, ZIP_ER_REMOVE, errno); + return -1; + } + } + _zip_free(za); + return 0; + } + + if ((filelist=(struct filelist *)malloc(sizeof(filelist[0])*survivors)) + == NULL) + return -1; + + if ((cd=_zip_cdir_new(survivors, &za->error)) == NULL) { + free(filelist); + return -1; + } + + for (i=0; ientry[i]); + + /* archive comment is special for torrentzip */ + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) { + cd->comment = _zip_memdup(TORRENT_SIG "XXXXXXXX", + TORRENT_SIG_LEN + TORRENT_CRC_LEN, + &za->error); + if (cd->comment == NULL) { + _zip_cdir_free(cd); + free(filelist); + return -1; + } + cd->comment_len = TORRENT_SIG_LEN + TORRENT_CRC_LEN; + } + else if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, ZIP_FL_UNCHANGED) == 0) { + if (_zip_cdir_set_comment(cd, za) == -1) { + _zip_cdir_free(cd); + free(filelist); + return -1; + } + } + + if ((temp=_zip_create_temp_output(za, &out)) == NULL) { + _zip_cdir_free(cd); + return -1; + } + + + /* create list of files with index into original archive */ + for (i=j=0; inentry; i++) { + if (za->entry[i].state == ZIP_ST_DELETED) + continue; + + filelist[j].idx = i; + filelist[j].name = zip_get_name(za, i, 0); + j++; + } + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) + qsort(filelist, survivors, sizeof(filelist[0]), + _zip_torrentzip_cmp); + + new_torrentzip = (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 1 + && zip_get_archive_flag(za, ZIP_AFL_TORRENT, + ZIP_FL_UNCHANGED) == 0); + error = 0; + for (j=0; jentry+i) || new_torrentzip) { + _zip_dirent_init(&de); + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) + _zip_dirent_torrent_normalize(&de); + + /* use it as central directory entry */ + memcpy(cd->entry+j, &de, sizeof(cd->entry[j])); + + /* set/update file name */ + if (za->entry[i].ch_filename == NULL) { + if (za->entry[i].state == ZIP_ST_ADDED) { + de.filename = strdup("-"); + de.filename_len = 1; + cd->entry[j].filename = "-"; + } + else { + de.filename = strdup(za->cdir->entry[i].filename); + de.filename_len = strlen(de.filename); + cd->entry[j].filename = za->cdir->entry[i].filename; + cd->entry[j].filename_len = de.filename_len; + } + } + } + else { + /* copy existing directory entries */ + if (fseeko(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + error = 1; + break; + } + if (_zip_dirent_read(&de, za->zp, NULL, 0, 1, &za->error) != 0) { + error = 1; + break; + } + if (de.bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { + de.crc = za->cdir->entry[i].crc; + de.comp_size = za->cdir->entry[i].comp_size; + de.uncomp_size = za->cdir->entry[i].uncomp_size; + de.bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; + } + memcpy(cd->entry+j, za->cdir->entry+i, sizeof(cd->entry[j])); + } + + if (za->entry[i].ch_filename) { + free(de.filename); + if ((de.filename=strdup(za->entry[i].ch_filename)) == NULL) { + error = 1; + break; + } + de.filename_len = strlen(de.filename); + cd->entry[j].filename = za->entry[i].ch_filename; + cd->entry[j].filename_len = de.filename_len; + } + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0 + && za->entry[i].ch_comment_len != -1) { + /* as the rest of cd entries, its malloc/free is done by za */ + cd->entry[j].comment = za->entry[i].ch_comment; + cd->entry[j].comment_len = za->entry[i].ch_comment_len; + } + + cd->entry[j].offset = ftello(out); + + if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) { + struct zip_source *zs; + + zs = NULL; + if (!ZIP_ENTRY_DATA_CHANGED(za->entry+i)) { + if ((zs=zip_source_zip(za, za, i, ZIP_FL_RECOMPRESS, 0, -1)) + == NULL) { + error = 1; + break; + } + } + + if (add_data(za, zs ? zs : za->entry[i].source, &de, out) < 0) { + error = 1; + break; + } + cd->entry[j].last_mod = de.last_mod; + cd->entry[j].comp_method = de.comp_method; + cd->entry[j].comp_size = de.comp_size; + cd->entry[j].uncomp_size = de.uncomp_size; + cd->entry[j].crc = de.crc; + } + else { + if (_zip_dirent_write(&de, out, 1, &za->error) < 0) { + error = 1; + break; + } + /* we just read the local dirent, file is at correct position */ + if (copy_data(za->zp, cd->entry[j].comp_size, out, + &za->error) < 0) { + error = 1; + break; + } + } + + _zip_dirent_finalize(&de); + } + + if (!error) { + if (write_cdir(za, cd, out) < 0) + error = 1; + } + + /* pointers in cd entries are owned by za */ + cd->nentry = 0; + _zip_cdir_free(cd); + + if (error) { + _zip_dirent_finalize(&de); + fclose(out); + remove(temp); + free(temp); + return -1; + } + + if (fclose(out) != 0) { + _zip_error_set(&za->error, ZIP_ER_CLOSE, errno); + remove(temp); + free(temp); + return -1; + } + + if (za->zp) { + fclose(za->zp); + za->zp = NULL; + reopen_on_error = 1; + } + if (_zip_rename(temp, za->zn) != 0) { + _zip_error_set(&za->error, ZIP_ER_RENAME, errno); + remove(temp); + free(temp); + if (reopen_on_error) { + /* ignore errors, since we're already in an error case */ + za->zp = fopen(za->zn, "rb"); + } + return -1; + } + mask = umask(0); + umask(mask); + chmod(za->zn, 0666&~mask); + + _zip_free(za); + free(temp); + + return 0; +} + + + +static int +add_data(struct zip *za, struct zip_source *zs, struct zip_dirent *de, FILE *ft) +{ + myoff_t offstart, offend; + zip_source_callback cb; + void *ud; + struct zip_stat st; + + cb = zs->f; + ud = zs->ud; + + if (cb(ud, &st, sizeof(st), ZIP_SOURCE_STAT) < (ssize_t)sizeof(st)) { + ch_set_error(&za->error, cb, ud); + return -1; + } + + if (cb(ud, NULL, 0, ZIP_SOURCE_OPEN) < 0) { + ch_set_error(&za->error, cb, ud); + return -1; + } + + offstart = ftello(ft); + + if (_zip_dirent_write(de, ft, 1, &za->error) < 0) + return -1; + + if (st.comp_method != ZIP_CM_STORE) { + if (add_data_comp(cb, ud, &st, ft, &za->error) < 0) + return -1; + } + else { + if (add_data_uncomp(za, cb, ud, &st, ft) < 0) + return -1; + } + + if (cb(ud, NULL, 0, ZIP_SOURCE_CLOSE) < 0) { + ch_set_error(&za->error, cb, ud); + return -1; + } + + offend = ftello(ft); + + if (fseeko(ft, offstart, SEEK_SET) < 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + return -1; + } + + + de->last_mod = st.mtime; + de->comp_method = st.comp_method; + de->crc = st.crc; + de->uncomp_size = st.size; + de->comp_size = st.comp_size; + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) + _zip_dirent_torrent_normalize(de); + + if (_zip_dirent_write(de, ft, 1, &za->error) < 0) + return -1; + + if (fseeko(ft, offend, SEEK_SET) < 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + return -1; + } + + return 0; +} + + + +static int +add_data_comp(zip_source_callback cb, void *ud, struct zip_stat *st,FILE *ft, + struct zip_error *error) +{ + char buf[BUFSIZE]; + ssize_t n; + + st->comp_size = 0; + while ((n=cb(ud, buf, sizeof(buf), ZIP_SOURCE_READ)) > 0) { + if (fwrite(buf, 1, n, ft) != (size_t)n) { + _zip_error_set(error, ZIP_ER_WRITE, errno); + return -1; + } + + st->comp_size += n; + } + if (n < 0) { + ch_set_error(error, cb, ud); + return -1; + } + + return 0; +} + + + +static int +add_data_uncomp(struct zip *za, zip_source_callback cb, void *ud, + struct zip_stat *st, FILE *ft) +{ + char b1[BUFSIZE], b2[BUFSIZE]; + int end, flush, ret; + ssize_t n; + size_t n2; + z_stream zstr; + int mem_level; + + st->comp_method = ZIP_CM_DEFLATE; + st->comp_size = st->size = 0; + st->crc = crc32(0, NULL, 0); + + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = NULL; + zstr.avail_in = 0; + zstr.avail_out = 0; + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) + mem_level = TORRENT_MEM_LEVEL; + else + mem_level = MAX_MEM_LEVEL; + + /* -MAX_WBITS: undocumented feature of zlib to _not_ write a zlib header */ + deflateInit2(&zstr, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, mem_level, + Z_DEFAULT_STRATEGY); + + zstr.next_out = (Bytef *)b2; + zstr.avail_out = sizeof(b2); + zstr.avail_in = 0; + + flush = 0; + end = 0; + while (!end) { + if (zstr.avail_in == 0 && !flush) { + if ((n=cb(ud, b1, sizeof(b1), ZIP_SOURCE_READ)) < 0) { + ch_set_error(&za->error, cb, ud); + deflateEnd(&zstr); + return -1; + } + if (n > 0) { + zstr.avail_in = n; + zstr.next_in = (Bytef *)b1; + st->size += n; + st->crc = crc32(st->crc, (Bytef *)b1, n); + } + else + flush = Z_FINISH; + } + + ret = deflate(&zstr, flush); + if (ret != Z_OK && ret != Z_STREAM_END) { + _zip_error_set(&za->error, ZIP_ER_ZLIB, ret); + return -1; + } + + if (zstr.avail_out != sizeof(b2)) { + n2 = sizeof(b2) - zstr.avail_out; + + if (fwrite(b2, 1, n2, ft) != n2) { + _zip_error_set(&za->error, ZIP_ER_WRITE, errno); + return -1; + } + + zstr.next_out = (Bytef *)b2; + zstr.avail_out = sizeof(b2); + st->comp_size += n2; + } + + if (ret == Z_STREAM_END) { + deflateEnd(&zstr); + end = 1; + } + } + + return 0; +} + + + +static void +ch_set_error(struct zip_error *error, zip_source_callback cb, void *ud) +{ + int e[2]; + + if ((cb(ud, e, sizeof(e), ZIP_SOURCE_ERROR)) < (ssize_t)sizeof(e)) { + error->zip_err = ZIP_ER_INTERNAL; + error->sys_err = 0; + } + else { + error->zip_err = e[0]; + error->sys_err = e[1]; + } +} + + + +static int +copy_data(FILE *fs, myoff_t len, FILE *ft, struct zip_error *error) +{ + char buf[BUFSIZE]; + int n, nn; + + if (len == 0) + return 0; + + while (len > 0) { + nn = len > sizeof(buf) ? sizeof(buf) : len; + if ((n=fread(buf, 1, nn, fs)) < 0) { + _zip_error_set(error, ZIP_ER_READ, errno); + return -1; + } + else if (n == 0) { + _zip_error_set(error, ZIP_ER_EOF, 0); + return -1; + } + + if (fwrite(buf, 1, n, ft) != (size_t)n) { + _zip_error_set(error, ZIP_ER_WRITE, errno); + return -1; + } + + len -= n; + } + + return 0; +} + + + +static int +write_cdir(struct zip *za, struct zip_cdir *cd, FILE *out) +{ + myoff_t offset; + uLong crc; + char buf[TORRENT_CRC_LEN+1]; + + if (_zip_cdir_write(cd, out, &za->error) < 0) + return -1; + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0) + return 0; + + + /* fix up torrentzip comment */ + + offset = ftello(out); + + if (_zip_filerange_crc(out, cd->offset, cd->size, &crc, &za->error) < 0) + return -1; + + snprintf(buf, sizeof(buf), "%08lX", (long)crc); + + if (fseeko(out, offset-TORRENT_CRC_LEN, SEEK_SET) < 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + return -1; + } + + if (fwrite(buf, TORRENT_CRC_LEN, 1, out) != 1) { + _zip_error_set(&za->error, ZIP_ER_WRITE, errno); + return -1; + } + + return 0; +} + + + +static int +_zip_cdir_set_comment(struct zip_cdir *dest, struct zip *src) +{ + if (src->ch_comment_len != -1) { + dest->comment = _zip_memdup(src->ch_comment, + src->ch_comment_len, &src->error); + if (dest->comment == NULL) + return -1; + dest->comment_len = src->ch_comment_len; + } else { + if (src->cdir && src->cdir->comment) { + dest->comment = _zip_memdup(src->cdir->comment, + src->cdir->comment_len, &src->error); + if (dest->comment == NULL) + return -1; + dest->comment_len = src->cdir->comment_len; + } + } + + return 0; +} + + + +static int +_zip_changed(struct zip *za, int *survivorsp) +{ + int changed, i, survivors; + + changed = survivors = 0; + + if (za->ch_comment_len != -1 + || za->ch_flags != za->flags) + changed = 1; + + for (i=0; inentry; i++) { + if ((za->entry[i].state != ZIP_ST_UNCHANGED) + || (za->entry[i].ch_comment_len != -1)) + changed = 1; + if (za->entry[i].state != ZIP_ST_DELETED) + survivors++; + } + + *survivorsp = survivors; + + return changed; +} + + + +static char * +_zip_create_temp_output(struct zip *za, FILE **outp) +{ + char *temp; + int tfd; + FILE *tfp; + + if ((temp=(char *)malloc(strlen(za->zn)+8)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + sprintf(temp, "%s.XXXXXX", za->zn); + + if ((tfd=mkstemp(temp)) == -1) { + _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return NULL; + } + + if ((tfp=fdopen(tfd, "r+b")) == NULL) { + _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno); + close(tfd); + remove(temp); + free(temp); + return NULL; + } + + *outp = tfp; + return temp; +} + + + +static int +_zip_torrentzip_cmp(const void *a, const void *b) +{ + return strcasecmp(((const struct filelist *)a)->name, + ((const struct filelist *)b)->name); +} + + + +ZIP_EXTERN int +zip_add_dir(struct zip *za, const char *name) +{ + int len, ret; + char *s; + struct zip_source *source; + + if (name == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + s = NULL; + len = strlen(name); + + if (name[len-1] != '/') { + if ((s=(char *)malloc(len+2)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + strcpy(s, name); + s[len] = '/'; + s[len+1] = '\0'; + } + + if ((source=zip_source_buffer(za, NULL, 0, 0)) == NULL) { + free(s); + return -1; + } + + ret = _zip_replace(za, -1, s ? s : name, source); + + free(s); + if (ret < 0) + zip_source_free(source); + + return ret; +} + + +ZIP_EXTERN int +zip_error_to_str(char *buf, size_t len, int ze, int se) +{ + const char *zs, *ss; + + if (ze < 0 || ze >= _zip_nerr_str) + return snprintf(buf, len, "Unknown error %d", ze); + + zs = _zip_err_str[ze]; + + switch (_zip_err_type[ze]) { + case ZIP_ET_SYS: + ss = strerror(se); + break; + + case ZIP_ET_ZLIB: + ss = zError(se); + break; + + default: + ss = NULL; + } + + return snprintf(buf, len, "%s%s%s", + zs, (ss ? ": " : ""), (ss ? ss : "")); +} + + +ZIP_EXTERN void +zip_file_error_clear(struct zip_file *zf) +{ + _zip_error_clear(&zf->error); +} + + +ZIP_EXTERN int +zip_fclose(struct zip_file *zf) +{ + int i, ret; + + if (zf->zstr) + inflateEnd(zf->zstr); + free(zf->buffer); + free(zf->zstr); + + for (i=0; iza->nfile; i++) { + if (zf->za->file[i] == zf) { + zf->za->file[i] = zf->za->file[zf->za->nfile-1]; + zf->za->nfile--; + break; + } + } + + ret = 0; + if (zf->error.zip_err) + ret = zf->error.zip_err; + else if ((zf->flags & ZIP_ZF_CRC) && (zf->flags & ZIP_ZF_EOF)) { + /* if EOF, compare CRC */ + if (zf->crc_orig != zf->crc) + ret = ZIP_ER_CRC; + } + + free(zf); + return ret; +} + + +int +_zip_filerange_crc(FILE *fp, myoff_t start, myoff_t len, uLong *crcp, + struct zip_error *errp) +{ + Bytef buf[BUFSIZE]; + size_t n; + + *crcp = crc32(0L, Z_NULL, 0); + + if (fseeko(fp, start, SEEK_SET) != 0) { + _zip_error_set(errp, ZIP_ER_SEEK, errno); + return -1; + } + + while (len > 0) { + n = len > BUFSIZE ? BUFSIZE : len; + if ((n=fread(buf, 1, n, fp)) <= 0) { + _zip_error_set(errp, ZIP_ER_READ, errno); + return -1; + } + + *crcp = crc32(*crcp, buf, n); + + len-= n; + } + + return 0; +} + + +ZIP_EXTERN const char * +zip_file_strerror(struct zip_file *zf) +{ + return _zip_error_strerror(&zf->error); +} + + +/* _zip_file_get_offset(za, ze): + Returns the offset of the file data for entry ze. + + On error, fills in za->error and returns 0. +*/ + +unsigned int +_zip_file_get_offset(struct zip *za, int idx) +{ + struct zip_dirent de; + unsigned int offset; + + offset = za->cdir->entry[idx].offset; + + if (fseeko(za->zp, offset, SEEK_SET) != 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + return 0; + } + + if (_zip_dirent_read(&de, za->zp, NULL, 0, 1, &za->error) != 0) + return 0; + + offset += LENTRYSIZE + de.filename_len + de.extrafield_len; + + _zip_dirent_finalize(&de); + + return offset; +} + + +ZIP_EXTERN void +zip_file_error_get(struct zip_file *zf, int *zep, int *sep) +{ + _zip_error_get(&zf->error, zep, sep); +} + + +static struct zip_file *_zip_file_new(struct zip *za); + + + +ZIP_EXTERN struct zip_file * +zip_fopen_index(struct zip *za, int fileno, int flags) +{ + int len, ret; + int zfflags; + struct zip_file *zf; + + if ((fileno < 0) || (fileno >= za->nentry)) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) == 0 + && ZIP_ENTRY_DATA_CHANGED(za->entry+fileno)) { + _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); + return NULL; + } + + if (fileno >= za->cdir->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + zfflags = 0; + switch (za->cdir->entry[fileno].comp_method) { + case ZIP_CM_STORE: + zfflags |= ZIP_ZF_CRC; + break; + + case ZIP_CM_DEFLATE: + if ((flags & ZIP_FL_COMPRESSED) == 0) + zfflags |= ZIP_ZF_CRC | ZIP_ZF_DECOMP; + break; + default: + if ((flags & ZIP_FL_COMPRESSED) == 0) { + _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); + return NULL; + } + break; + } + + zf = _zip_file_new(za); + + zf->flags = zfflags; + /* zf->name = za->cdir->entry[fileno].filename; */ + zf->method = za->cdir->entry[fileno].comp_method; + zf->bytes_left = za->cdir->entry[fileno].uncomp_size; + zf->cbytes_left = za->cdir->entry[fileno].comp_size; + zf->crc_orig = za->cdir->entry[fileno].crc; + + if ((zf->fpos=_zip_file_get_offset(za, fileno)) == 0) { + zip_fclose(zf); + return NULL; + } + + if ((zf->flags & ZIP_ZF_DECOMP) == 0) + zf->bytes_left = zf->cbytes_left; + else { + if ((zf->buffer=(char *)malloc(BUFSIZE)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + zip_fclose(zf); + return NULL; + } + + len = _zip_file_fillbuf(zf->buffer, BUFSIZE, zf); + if (len <= 0) { + _zip_error_copy(&za->error, &zf->error); + zip_fclose(zf); + return NULL; + } + + if ((zf->zstr = (z_stream *)malloc(sizeof(z_stream))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + zip_fclose(zf); + return NULL; + } + zf->zstr->zalloc = Z_NULL; + zf->zstr->zfree = Z_NULL; + zf->zstr->opaque = NULL; + zf->zstr->next_in = (Bytef *)zf->buffer; + zf->zstr->avail_in = len; + + /* negative value to tell zlib that there is no header */ + if ((ret=inflateInit2(zf->zstr, -MAX_WBITS)) != Z_OK) { + _zip_error_set(&za->error, ZIP_ER_ZLIB, ret); + zip_fclose(zf); + return NULL; + } + } + + return zf; +} + + + +int +_zip_file_fillbuf(void *buf, size_t buflen, struct zip_file *zf) +{ + int i, j; + + if (zf->error.zip_err != ZIP_ER_OK) + return -1; + + if ((zf->flags & ZIP_ZF_EOF) || zf->cbytes_left <= 0 || buflen <= 0) + return 0; + + if (fseeko(zf->za->zp, zf->fpos, SEEK_SET) < 0) { + _zip_error_set(&zf->error, ZIP_ER_SEEK, errno); + return -1; + } + if (buflen < zf->cbytes_left) + i = buflen; + else + i = zf->cbytes_left; + + j = fread(buf, 1, i, zf->za->zp); + if (j == 0) { + _zip_error_set(&zf->error, ZIP_ER_EOF, 0); + j = -1; + } + else if (j < 0) + _zip_error_set(&zf->error, ZIP_ER_READ, errno); + else { + zf->fpos += j; + zf->cbytes_left -= j; + } + + return j; +} + + + +static struct zip_file * +_zip_file_new(struct zip *za) +{ + struct zip_file *zf, **file; + int n; + + if ((zf=(struct zip_file *)malloc(sizeof(struct zip_file))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if (za->nfile >= za->nfile_alloc-1) { + n = za->nfile_alloc + 10; + file = (struct zip_file **)realloc(za->file, + n*sizeof(struct zip_file *)); + if (file == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + free(zf); + return NULL; + } + za->nfile_alloc = n; + za->file = file; + } + + za->file[za->nfile++] = zf; + + zf->za = za; + _zip_error_init(&zf->error); + zf->flags = 0; + zf->crc = crc32(0L, Z_NULL, 0); + zf->crc_orig = 0; + zf->method = -1; + zf->bytes_left = zf->cbytes_left = 0; + zf->fpos = 0; + zf->buffer = NULL; + zf->zstr = NULL; + + return zf; +} + + +ZIP_EXTERN struct zip_file * +zip_fopen(struct zip *za, const char *fname, int flags) +{ + int idx; + + if ((idx=zip_name_locate(za, fname, flags)) < 0) + return NULL; + + return zip_fopen_index(za, idx, flags); +} + + +ZIP_EXTERN int +zip_set_file_comment(struct zip *za, int idx, const char *comment, int len) +{ + char *tmpcom; + + if (idx < 0 || idx >= za->nentry + || len < 0 || len > MAXCOMLEN + || (len > 0 && comment == NULL)) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (len > 0) { + if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL) + return -1; + } + else + tmpcom = NULL; + + free(za->entry[idx].ch_comment); + za->entry[idx].ch_comment = tmpcom; + za->entry[idx].ch_comment_len = len; + + return 0; +} + + +ZIP_EXTERN struct zip_source * +zip_source_file(struct zip *za, const char *fname, myoff_t start, myoff_t len) +{ + if (za == NULL) + return NULL; + + if (fname == NULL || start < 0 || len < -1) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + return _zip_source_file_or_p(za, fname, NULL, start, len); +} + + +struct read_data { + const char *buf, *data, *end; + time_t mtime; + int freep; +}; + +static ssize_t read_data(void *state, void *data, size_t len, + enum zip_source_cmd cmd); + + + +ZIP_EXTERN struct zip_source * +zip_source_buffer(struct zip *za, const void *data, myoff_t len, int freep) +{ + struct read_data *f; + struct zip_source *zs; + + if (za == NULL) + return NULL; + + if (len < 0 || (data == NULL && len > 0)) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((f=(struct read_data *)malloc(sizeof(*f))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + f->data = (const char *)data; + f->end = ((const char *)data)+len; + f->freep = freep; + f->mtime = time(NULL); + + if ((zs=zip_source_function(za, read_data, f)) == NULL) { + free(f); + return NULL; + } + + return zs; +} + + + +static ssize_t +read_data(void *state, void *data, size_t len, enum zip_source_cmd cmd) +{ + struct read_data *z; + char *buf; + size_t n; + + z = (struct read_data *)state; + buf = (char *)data; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + z->buf = z->data; + return 0; + + case ZIP_SOURCE_READ: + n = z->end - z->buf; + if (n > len) + n = len; + + if (n) { + memcpy(buf, z->buf, n); + z->buf += n; + } + + return n; + + case ZIP_SOURCE_CLOSE: + return 0; + + case ZIP_SOURCE_STAT: + { + struct zip_stat *st; + + if (len < sizeof(*st)) + return -1; + + st = (struct zip_stat *)data; + + zip_stat_init(st); + st->mtime = z->mtime; + st->size = z->end - z->data; + + return sizeof(*st); + } + + case ZIP_SOURCE_ERROR: + { + int *e; + + if (len < sizeof(int)*2) + return -1; + + e = (int *)data; + e[0] = e[1] = 0; + } + return sizeof(int)*2; + + case ZIP_SOURCE_FREE: + if (z->freep) { + free((void *)z->data); + z->data = NULL; + } + free(z); + return 0; + + default: + ; + } + + return -1; +} + + +int +_zip_set_name(struct zip *za, int idx, const char *name) +{ + char *s; + int i; + + if (idx < 0 || idx >= za->nentry || name == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((i=_zip_name_locate(za, name, 0, NULL)) != -1 && i != idx) { + _zip_error_set(&za->error, ZIP_ER_EXISTS, 0); + return -1; + } + + /* no effective name change */ + if (i == idx) + return 0; + + if ((s=strdup(name)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + + if (za->entry[idx].state == ZIP_ST_UNCHANGED) + za->entry[idx].state = ZIP_ST_RENAMED; + + free(za->entry[idx].ch_filename); + za->entry[idx].ch_filename = s; + + return 0; +} + + +ZIP_EXTERN int +zip_set_archive_flag(struct zip *za, int flag, int value) +{ + if (value) + za->ch_flags |= flag; + else + za->ch_flags &= ~flag; + + return 0; +} + + +void +_zip_unchange_data(struct zip_entry *ze) +{ + if (ze->source) { + (void)ze->source->f(ze->source->ud, NULL, 0, ZIP_SOURCE_FREE); + free(ze->source); + ze->source = NULL; + } + + ze->state = ze->ch_filename ? ZIP_ST_RENAMED : ZIP_ST_UNCHANGED; +} + + +ZIP_EXTERN int +zip_unchange_archive(struct zip *za) +{ + free(za->ch_comment); + za->ch_comment = NULL; + za->ch_comment_len = -1; + + za->ch_flags = za->flags; + + return 0; +} + +ZIP_EXTERN int +zip_unchange(struct zip *za, int idx) +{ + return _zip_unchange(za, idx, 0); +} + + + +int +_zip_unchange(struct zip *za, int idx, int allow_duplicates) +{ + int i; + + if (idx < 0 || idx >= za->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (za->entry[idx].ch_filename) { + if (!allow_duplicates) { + i = _zip_name_locate(za, + _zip_get_name(za, idx, ZIP_FL_UNCHANGED, NULL), + 0, NULL); + if (i != -1 && i != idx) { + _zip_error_set(&za->error, ZIP_ER_EXISTS, 0); + return -1; + } + } + + free(za->entry[idx].ch_filename); + za->entry[idx].ch_filename = NULL; + } + + free(za->entry[idx].ch_comment); + za->entry[idx].ch_comment = NULL; + za->entry[idx].ch_comment_len = -1; + + _zip_unchange_data(za->entry+idx); + + return 0; +} + +ZIP_EXTERN int +zip_unchange_all(struct zip *za) +{ + int ret, i; + + ret = 0; + for (i=0; inentry; i++) + ret |= _zip_unchange(za, i, 1); + + ret |= zip_unchange_archive(za); + + return ret; +} + + +ZIP_EXTERN int +zip_set_archive_comment(struct zip *za, const char *comment, int len) +{ + char *tmpcom; + + if (len < 0 || len > MAXCOMLEN + || (len > 0 && comment == NULL)) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (len > 0) { + if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL) + return -1; + } + else + tmpcom = NULL; + + free(za->ch_comment); + za->ch_comment = tmpcom; + za->ch_comment_len = len; + + return 0; +} + + +ZIP_EXTERN int +zip_replace(struct zip *za, int idx, struct zip_source *source) +{ + if (idx < 0 || idx >= za->nentry || source == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (_zip_replace(za, idx, NULL, source) == -1) + return -1; + + return 0; +} + + + + +int +_zip_replace(struct zip *za, int idx, const char *name, + struct zip_source *source) +{ + if (idx == -1) { + if (_zip_entry_new(za) == NULL) + return -1; + + idx = za->nentry - 1; + } + + _zip_unchange_data(za->entry+idx); + + if (name && _zip_set_name(za, idx, name) != 0) + return -1; + + za->entry[idx].state = ((za->cdir == NULL || idx >= za->cdir->nentry) + ? ZIP_ST_ADDED : ZIP_ST_REPLACED); + za->entry[idx].source = source; + + return idx; +} + + +ZIP_EXTERN int +zip_rename(struct zip *za, int idx, const char *name) +{ + const char *old_name; + int old_is_dir, new_is_dir; + + if (idx >= za->nentry || idx < 0 || name[0] == '\0') { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((old_name=zip_get_name(za, idx, 0)) == NULL) + return -1; + + new_is_dir = (name[strlen(name)-1] == '/'); + old_is_dir = (old_name[strlen(old_name)-1] == '/'); + + if (new_is_dir != old_is_dir) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + return _zip_set_name(za, idx, name); +} + +#include +#include +#include +#include +#include +#include + +static void set_error(int *, struct zip_error *, int); +static struct zip *_zip_allocate_new(const char *, int *); +static int _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *); +static void _zip_check_torrentzip(struct zip *); +static struct zip_cdir *_zip_find_central_dir(FILE *, int, int *, myoff_t); +static int _zip_file_exists(const char *, int, int *); +static int _zip_headercomp(struct zip_dirent *, int, + struct zip_dirent *, int); +static unsigned char *_zip_memmem(const unsigned char *, int, + const unsigned char *, int); +static struct zip_cdir *_zip_readcdir(FILE *, unsigned char *, unsigned char *, + int, int, struct zip_error *); + + + +ZIP_EXTERN struct zip * +zip_open(const char *fn, int flags, int *zep) +{ + FILE *fp; + struct zip *za; + struct zip_cdir *cdir; + int i; + myoff_t len; + + switch (_zip_file_exists(fn, flags, zep)) { + case -1: + return NULL; + case 0: + return _zip_allocate_new(fn, zep); + default: + break; + } + + if ((fp=fopen(fn, "rb")) == NULL) { + set_error(zep, NULL, ZIP_ER_OPEN); + return NULL; + } + + fseeko(fp, 0, SEEK_END); + len = ftello(fp); + + /* treat empty files as empty archives */ + if (len == 0) { + if ((za=_zip_allocate_new(fn, zep)) == NULL) + fclose(fp); + else + za->zp = fp; + return za; + } + + cdir = _zip_find_central_dir(fp, flags, zep, len); + if (cdir == NULL) { + fclose(fp); + return NULL; + } + + if ((za=_zip_allocate_new(fn, zep)) == NULL) { + _zip_cdir_free(cdir); + fclose(fp); + return NULL; + } + + za->cdir = cdir; + za->zp = fp; + + if ((za->entry=(struct zip_entry *)malloc(sizeof(*(za->entry)) + * cdir->nentry)) == NULL) { + set_error(zep, NULL, ZIP_ER_MEMORY); + _zip_free(za); + return NULL; + } + for (i=0; inentry; i++) + _zip_entry_new(za); + + _zip_check_torrentzip(za); + za->ch_flags = za->flags; + + return za; +} + + + +static void +set_error(int *zep, struct zip_error *err, int ze) +{ + int se; + + if (err) { + _zip_error_get(err, &ze, &se); + if (zip_error_get_sys_type(ze) == ZIP_ET_SYS) + errno = se; + } + + if (zep) + *zep = ze; +} + + + +/* _zip_readcdir: + tries to find a valid end-of-central-directory at the beginning of + buf, and then the corresponding central directory entries. + Returns a struct zip_cdir which contains the central directory + entries, or NULL if unsuccessful. */ + +static struct zip_cdir * +_zip_readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen, + int flags, struct zip_error *error) +{ + struct zip_cdir *cd; + unsigned char *cdp, **bufp; + int i, comlen, nentry; + + comlen = buf + buflen - eocd - EOCDLEN; + if (comlen < 0) { + /* not enough bytes left for comment */ + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return NULL; + } + + /* check for end-of-central-dir magic */ + if (memcmp(eocd, EOCD_MAGIC, 4) != 0) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return NULL; + } + + if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) { + _zip_error_set(error, ZIP_ER_MULTIDISK, 0); + return NULL; + } + + cdp = eocd + 8; + /* number of cdir-entries on this disk */ + i = _zip_read2(&cdp); + /* number of cdir-entries */ + nentry = _zip_read2(&cdp); + + if ((cd=_zip_cdir_new(nentry, error)) == NULL) + return NULL; + + cd->size = _zip_read4(&cdp); + cd->offset = _zip_read4(&cdp); + cd->comment = NULL; + cd->comment_len = _zip_read2(&cdp); + + if ((comlen < cd->comment_len) || (cd->nentry != i)) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + free(cd); + return NULL; + } + if ((flags & ZIP_CHECKCONS) && comlen != cd->comment_len) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + free(cd); + return NULL; + } + + if (cd->comment_len) { + if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN, + cd->comment_len, error)) + == NULL) { + free(cd); + return NULL; + } + } + + cdp = eocd; + if (cd->size < (unsigned int)(eocd-buf)) { + /* if buffer already read in, use it */ + cdp = eocd - cd->size; + bufp = &cdp; + } + else { + /* go to start of cdir and read it entry by entry */ + bufp = NULL; + clearerr(fp); + fseeko(fp, cd->offset, SEEK_SET); + /* possible consistency check: cd->offset = + len-(cd->size+cd->comment_len+EOCDLEN) ? */ + if (ferror(fp) || ((unsigned long)ftello(fp) != cd->offset)) { + /* seek error or offset of cdir wrong */ + if (ferror(fp)) + _zip_error_set(error, ZIP_ER_SEEK, errno); + else + _zip_error_set(error, ZIP_ER_NOZIP, 0); + free(cd); + return NULL; + } + } + + for (i=0; inentry; i++) { + if ((_zip_dirent_read(cd->entry+i, fp, bufp, eocd-cdp, 0, + error)) < 0) { + cd->nentry = i; + _zip_cdir_free(cd); + return NULL; + } + } + + return cd; +} + + + +/* _zip_checkcons: + Checks the consistency of the central directory by comparing central + directory entries with local headers and checking for plausible + file and header offsets. Returns -1 if not plausible, else the + difference between the lowest and the highest fileposition reached */ + +static int +_zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) +{ + int i; + unsigned int min, max, j; + struct zip_dirent temp; + + if (cd->nentry) { + max = cd->entry[0].offset; + min = cd->entry[0].offset; + } + else + min = max = 0; + + for (i=0; inentry; i++) { + if (cd->entry[i].offset < min) + min = cd->entry[i].offset; + if (min > cd->offset) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return -1; + } + + j = cd->entry[i].offset + cd->entry[i].comp_size + + cd->entry[i].filename_len + LENTRYSIZE; + if (j > max) + max = j; + if (max > cd->offset) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return -1; + } + + if (fseeko(fp, cd->entry[i].offset, SEEK_SET) != 0) { + _zip_error_set(error, ZIP_ER_SEEK, 0); + return -1; + } + + if (_zip_dirent_read(&temp, fp, NULL, 0, 1, error) == -1) + return -1; + + if (_zip_headercomp(cd->entry+i, 0, &temp, 1) != 0) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_dirent_finalize(&temp); + return -1; + } + _zip_dirent_finalize(&temp); + } + + return max - min; +} + + + +/* _zip_check_torrentzip: + check wether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */ + +static void +_zip_check_torrentzip(struct zip *za) +{ + uLong crc_got, crc_should; + char buf[8+1]; + char *end; + + if (za->zp == NULL || za->cdir == NULL) + return; + + if (za->cdir->comment_len != TORRENT_SIG_LEN+8 + || strncmp(za->cdir->comment, TORRENT_SIG, TORRENT_SIG_LEN) != 0) + return; + + memcpy(buf, za->cdir->comment+TORRENT_SIG_LEN, 8); + buf[8] = '\0'; + errno = 0; + crc_should = strtoul(buf, &end, 16); + if ((crc_should == UINT_MAX && errno != 0) || (end && *end)) + return; + + if (_zip_filerange_crc(za->zp, za->cdir->offset, za->cdir->size, + &crc_got, NULL) < 0) + return; + + if (crc_got == crc_should) + za->flags |= ZIP_AFL_TORRENT; +} + + + + +/* _zip_headercomp: + compares two headers h1 and h2; if they are local headers, set + local1p or local2p respectively to 1, else 0. Return 0 if they + are identical, -1 if not. */ + +static int +_zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2, + int local2p) +{ + if ((h1->version_needed != h2->version_needed) +#if 0 + /* some zip-files have different values in local + and global headers for the bitflags */ + || (h1->bitflags != h2->bitflags) +#endif + || (h1->comp_method != h2->comp_method) + || (h1->last_mod != h2->last_mod) + || (h1->filename_len != h2->filename_len) + || !h1->filename || !h2->filename + || strcmp(h1->filename, h2->filename)) + return -1; + + /* check that CRC and sizes are zero if data descriptor is used */ + if ((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local1p + && (h1->crc != 0 + || h1->comp_size != 0 + || h1->uncomp_size != 0)) + return -1; + if ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local2p + && (h2->crc != 0 + || h2->comp_size != 0 + || h2->uncomp_size != 0)) + return -1; + + /* check that CRC and sizes are equal if no data descriptor is used */ + if (((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local1p == 0) + && ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local2p == 0)) { + if ((h1->crc != h2->crc) + || (h1->comp_size != h2->comp_size) + || (h1->uncomp_size != h2->uncomp_size)) + return -1; + } + + if ((local1p == local2p) + && ((h1->extrafield_len != h2->extrafield_len) + || (h1->extrafield_len && h2->extrafield + && memcmp(h1->extrafield, h2->extrafield, + h1->extrafield_len)))) + return -1; + + /* if either is local, nothing more to check */ + if (local1p || local2p) + return 0; + + if ((h1->version_madeby != h2->version_madeby) + || (h1->disk_number != h2->disk_number) + || (h1->int_attrib != h2->int_attrib) + || (h1->ext_attrib != h2->ext_attrib) + || (h1->offset != h2->offset) + || (h1->comment_len != h2->comment_len) + || (h1->comment_len && h2->comment + && memcmp(h1->comment, h2->comment, h1->comment_len))) + return -1; + + return 0; +} + + + +static struct zip * +_zip_allocate_new(const char *fn, int *zep) +{ + struct zip *za; + struct zip_error error; + + if ((za=_zip_new(&error)) == NULL) { + set_error(zep, &error, 0); + return NULL; + } + + za->zn = strdup(fn); + if (!za->zn) { + _zip_free(za); + set_error(zep, NULL, ZIP_ER_MEMORY); + return NULL; + } + return za; +} + + + +static int +_zip_file_exists(const char *fn, int flags, int *zep) +{ + struct stat st; + + if (fn == NULL) { + set_error(zep, NULL, ZIP_ER_INVAL); + return -1; + } + + if (stat(fn, &st) != 0) { + if (flags & ZIP_CREATE) + return 0; + else { + set_error(zep, NULL, ZIP_ER_OPEN); + return -1; + } + } + else if ((flags & ZIP_EXCL)) { + set_error(zep, NULL, ZIP_ER_EXISTS); + return -1; + } + /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, + just like open() */ + + return 1; +} + + + +static struct zip_cdir * +_zip_find_central_dir(FILE *fp, int flags, int *zep, myoff_t len) +{ + struct zip_cdir *cdir, *cdirnew; + unsigned char *buf, *match; + int a, best, buflen, i; + struct zip_error zerr; + + i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END); + if (i == -1 && errno != EFBIG) { + /* seek before start of file on my machine */ + set_error(zep, NULL, ZIP_ER_SEEK); + return NULL; + } + + /* 64k is too much for stack */ + if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) { + set_error(zep, NULL, ZIP_ER_MEMORY); + return NULL; + } + + clearerr(fp); + buflen = fread(buf, 1, CDBUFSIZE, fp); + + if (ferror(fp)) { + set_error(zep, NULL, ZIP_ER_READ); + free(buf); + return NULL; + } + + best = -1; + cdir = NULL; + match = buf; + _zip_error_set(&zerr, ZIP_ER_NOZIP, 0); + + while ((match=_zip_memmem(match, buflen-(match-buf)-18, + (const unsigned char *)EOCD_MAGIC, 4))!=NULL) { + /* found match -- check, if good */ + /* to avoid finding the same match all over again */ + match++; + if ((cdirnew=_zip_readcdir(fp, buf, match-1, buflen, flags, + &zerr)) == NULL) + continue; + + if (cdir) { + if (best <= 0) + best = _zip_checkcons(fp, cdir, &zerr); + a = _zip_checkcons(fp, cdirnew, &zerr); + if (best < a) { + _zip_cdir_free(cdir); + cdir = cdirnew; + best = a; + } + else + _zip_cdir_free(cdirnew); + } + else { + cdir = cdirnew; + if (flags & ZIP_CHECKCONS) + best = _zip_checkcons(fp, cdir, &zerr); + else + best = 0; + } + cdirnew = NULL; + } + + free(buf); + + if (best < 0) { + set_error(zep, &zerr, 0); + _zip_cdir_free(cdir); + return NULL; + } + + return cdir; +} + + + +static unsigned char * +_zip_memmem(const unsigned char *big, int biglen, const unsigned char *little, + int littlelen) +{ + const unsigned char *p; + + if ((biglen < littlelen) || (littlelen == 0)) + return NULL; + p = big-1; + while ((p=(const unsigned char *) + memchr(p+1, little[0], (size_t)(big-(p+1)+biglen-littlelen+1))) + != NULL) { + if (memcmp(p+1, little+1, littlelen-1)==0) + return (unsigned char *)p; + } + + return NULL; +} + + +/* _zip_new: + creates a new zipfile struct, and sets the contents to zero; returns + the new struct. */ + +struct zip * +_zip_new(struct zip_error *error) +{ + struct zip *za; + + za = (struct zip *)malloc(sizeof(struct zip)); + if (!za) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + za->zn = NULL; + za->zp = NULL; + _zip_error_init(&za->error); + za->cdir = NULL; + za->ch_comment = NULL; + za->ch_comment_len = -1; + za->nentry = za->nentry_alloc = 0; + za->entry = NULL; + za->nfile = za->nfile_alloc = 0; + za->file = NULL; + za->flags = za->ch_flags = 0; + + return za; +} + + +void * +_zip_memdup(const void *mem, size_t len, struct zip_error *error) +{ + void *ret; + + ret = malloc(len); + if (!ret) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + memcpy(ret, mem, len); + + return ret; +} + + +ZIP_EXTERN int +zip_get_num_files(struct zip *za) +{ + if (za == NULL) + return -1; + + return za->nentry; +} + +ZIP_EXTERN const char * +zip_get_name(struct zip *za, int idx, int flags) +{ + return _zip_get_name(za, idx, flags, &za->error); +} + + + +const char * +_zip_get_name(struct zip *za, int idx, int flags, struct zip_error *error) +{ + if (idx < 0 || idx >= za->nentry) { + _zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) == 0) { + if (za->entry[idx].state == ZIP_ST_DELETED) { + _zip_error_set(error, ZIP_ER_DELETED, 0); + return NULL; + } + if (za->entry[idx].ch_filename) + return za->entry[idx].ch_filename; + } + + if (za->cdir == NULL || idx >= za->cdir->nentry) { + _zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return za->cdir->entry[idx].filename; +} + + +ZIP_EXTERN const char * +zip_get_file_comment(struct zip *za, int idx, int *lenp, int flags) +{ + if (idx < 0 || idx >= za->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) + || (za->entry[idx].ch_comment_len == -1)) { + if (lenp != NULL) + *lenp = za->cdir->entry[idx].comment_len; + return za->cdir->entry[idx].comment; + } + + if (lenp != NULL) + *lenp = za->entry[idx].ch_comment_len; + return za->entry[idx].ch_comment; +} + + +ZIP_EXTERN int +zip_get_archive_flag(struct zip *za, int flag, int flags) +{ + int fl; + + fl = (flags & ZIP_FL_UNCHANGED) ? za->flags : za->ch_flags; + + return (fl & flag) ? 1 : 0; +} + + +ZIP_EXTERN const char * +zip_get_archive_comment(struct zip *za, int *lenp, int flags) +{ + if ((flags & ZIP_FL_UNCHANGED) + || (za->ch_comment_len == -1)) { + if (za->cdir) { + if (lenp != NULL) + *lenp = za->cdir->comment_len; + return za->cdir->comment; + } + else { + if (lenp != NULL) + *lenp = -1; + return NULL; + } + } + + if (lenp != NULL) + *lenp = za->ch_comment_len; + return za->ch_comment; +} + + +/* _zip_free: + frees the space allocated to a zipfile struct, and closes the + corresponding file. */ + +void +_zip_free(struct zip *za) +{ + int i; + + if (za == NULL) + return; + + if (za->zn) + free(za->zn); + + if (za->zp) + fclose(za->zp); + + _zip_cdir_free(za->cdir); + + if (za->entry) { + for (i=0; inentry; i++) { + _zip_entry_free(za->entry+i); + } + free(za->entry); + } + + for (i=0; infile; i++) { + if (za->file[i]->error.zip_err == ZIP_ER_OK) { + _zip_error_set(&za->file[i]->error, ZIP_ER_ZIPCLOSED, 0); + za->file[i]->za = NULL; + } + } + + free(za->file); + + free(za); + + return; +} + + +ZIP_EXTERN ssize_t +zip_fread(struct zip_file *zf, void *outbuf, size_t toread) +{ + int ret; + size_t out_before, len; + int i; + + if (!zf) + return -1; + + if (zf->error.zip_err != 0) + return -1; + + if ((zf->flags & ZIP_ZF_EOF) || (toread == 0)) + return 0; + + if (zf->bytes_left == 0) { + zf->flags |= ZIP_ZF_EOF; + if (zf->flags & ZIP_ZF_CRC) { + if (zf->crc != zf->crc_orig) { + _zip_error_set(&zf->error, ZIP_ER_CRC, 0); + return -1; + } + } + return 0; + } + + if ((zf->flags & ZIP_ZF_DECOMP) == 0) { + ret = _zip_file_fillbuf(outbuf, toread, zf); + if (ret > 0) { + if (zf->flags & ZIP_ZF_CRC) + zf->crc = crc32(zf->crc, (Bytef *)outbuf, ret); + zf->bytes_left -= ret; + } + return ret; + } + + zf->zstr->next_out = (Bytef *)outbuf; + zf->zstr->avail_out = toread; + out_before = zf->zstr->total_out; + + /* endless loop until something has been accomplished */ + for (;;) { + ret = inflate(zf->zstr, Z_SYNC_FLUSH); + + switch (ret) { + case Z_OK: + case Z_STREAM_END: + /* all ok */ + /* Z_STREAM_END probably won't happen, since we didn't + have a header */ + len = zf->zstr->total_out - out_before; + if (len >= zf->bytes_left || len >= toread) { + if (zf->flags & ZIP_ZF_CRC) + zf->crc = crc32(zf->crc, (Bytef *)outbuf, len); + zf->bytes_left -= len; + return len; + } + break; + + case Z_BUF_ERROR: + if (zf->zstr->avail_in == 0) { + i = _zip_file_fillbuf(zf->buffer, BUFSIZE, zf); + if (i == 0) { + _zip_error_set(&zf->error, ZIP_ER_INCONS, 0); + return -1; + } + else if (i < 0) + return -1; + zf->zstr->next_in = (Bytef *)zf->buffer; + zf->zstr->avail_in = i; + continue; + } + /* fallthrough */ + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_STREAM_ERROR: + case Z_MEM_ERROR: + _zip_error_set(&zf->error, ZIP_ER_ZLIB, ret); + return -1; + } + } +} + + +ZIP_EXTERN const char * +zip_strerror(struct zip *za) +{ + return _zip_error_strerror(&za->error); +} + + +ZIP_EXTERN void +zip_stat_init(struct zip_stat *st) +{ + st->name = NULL; + st->index = -1; + st->crc = 0; + st->mtime = (time_t)-1; + st->size = -1; + st->comp_size = -1; + st->comp_method = ZIP_CM_STORE; + st->encryption_method = ZIP_EM_NONE; +} + + +ZIP_EXTERN int +zip_stat_index(struct zip *za, int index, int flags, struct zip_stat *st) +{ + const char *name; + + if (index < 0 || index >= za->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((name=zip_get_name(za, index, flags)) == NULL) + return -1; + + + if ((flags & ZIP_FL_UNCHANGED) == 0 + && ZIP_ENTRY_DATA_CHANGED(za->entry+index)) { + if (za->entry[index].source->f(za->entry[index].source->ud, + st, sizeof(*st), ZIP_SOURCE_STAT) < 0) { + _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); + return -1; + } + } + else { + if (za->cdir == NULL || index >= za->cdir->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + st->crc = za->cdir->entry[index].crc; + st->size = za->cdir->entry[index].uncomp_size; + st->mtime = za->cdir->entry[index].last_mod; + st->comp_size = za->cdir->entry[index].comp_size; + st->comp_method = za->cdir->entry[index].comp_method; + if (za->cdir->entry[index].bitflags & ZIP_GPBF_ENCRYPTED) { + if (za->cdir->entry[index].bitflags & ZIP_GPBF_STRONG_ENCRYPTION) { + /* XXX */ + st->encryption_method = ZIP_EM_UNKNOWN; + } + else + st->encryption_method = ZIP_EM_TRAD_PKWARE; + } + else + st->encryption_method = ZIP_EM_NONE; + /* st->bitflags = za->cdir->entry[index].bitflags; */ + } + + st->index = index; + st->name = name; + + return 0; +} + + +ZIP_EXTERN int +zip_stat(struct zip *za, const char *fname, int flags, struct zip_stat *st) +{ + int idx; + + if ((idx=zip_name_locate(za, fname, flags)) < 0) + return -1; + + return zip_stat_index(za, idx, flags, st); +} + + +struct read_zip { + struct zip_file *zf; + struct zip_stat st; + myoff_t off, len; +}; + +static ssize_t read_zip(void *st, void *data, size_t len, + enum zip_source_cmd cmd); + + + +ZIP_EXTERN struct zip_source * +zip_source_zip(struct zip *za, struct zip *srcza, int srcidx, int flags, + myoff_t start, myoff_t len) +{ + struct zip_error error; + struct zip_source *zs; + struct read_zip *p; + + /* XXX: ZIP_FL_RECOMPRESS */ + + if (za == NULL) + return NULL; + + if (srcza == NULL || start < 0 || len < -1 || srcidx < 0 || srcidx >= srcza->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) == 0 + && ZIP_ENTRY_DATA_CHANGED(srcza->entry+srcidx)) { + _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); + return NULL; + } + + if (len == 0) + len = -1; + + if (start == 0 && len == -1 && (flags & ZIP_FL_RECOMPRESS) == 0) + flags |= ZIP_FL_COMPRESSED; + else + flags &= ~ZIP_FL_COMPRESSED; + + if ((p=(struct read_zip *)malloc(sizeof(*p))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + _zip_error_copy(&error, &srcza->error); + + if (zip_stat_index(srcza, srcidx, flags, &p->st) < 0 + || (p->zf=zip_fopen_index(srcza, srcidx, flags)) == NULL) { + free(p); + _zip_error_copy(&za->error, &srcza->error); + _zip_error_copy(&srcza->error, &error); + + return NULL; + } + p->off = start; + p->len = len; + + if ((flags & ZIP_FL_COMPRESSED) == 0) { + p->st.size = p->st.comp_size = len; + p->st.comp_method = ZIP_CM_STORE; + p->st.crc = 0; + } + + if ((zs=zip_source_function(za, read_zip, p)) == NULL) { + free(p); + return NULL; + } + + return zs; +} + + + +static ssize_t +read_zip(void *state, void *data, size_t len, enum zip_source_cmd cmd) +{ + struct read_zip *z; + char b[8192], *buf; + int i, n; + + z = (struct read_zip *)state; + buf = (char *)data; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + for (n=0; noff; n+= i) { + i = (z->off-n > sizeof(b) ? sizeof(b) : z->off-n); + if ((i=zip_fread(z->zf, b, i)) < 0) { + zip_fclose(z->zf); + z->zf = NULL; + return -1; + } + } + return 0; + + case ZIP_SOURCE_READ: + if (z->len != -1) + n = len > z->len ? z->len : len; + else + n = len; + + + if ((i=zip_fread(z->zf, buf, n)) < 0) + return -1; + + if (z->len != -1) + z->len -= i; + + return i; + + case ZIP_SOURCE_CLOSE: + return 0; + + case ZIP_SOURCE_STAT: + if (len < sizeof(z->st)) + return -1; + len = sizeof(z->st); + + memcpy(data, &z->st, len); + return len; + + case ZIP_SOURCE_ERROR: + { + int *e; + + if (len < sizeof(int)*2) + return -1; + + e = (int *)data; + zip_file_error_get(z->zf, e, e+1); + } + return sizeof(int)*2; + + case ZIP_SOURCE_FREE: + zip_fclose(z->zf); + free(z); + return 0; + + default: + ; + } + + return -1; +} + + +ZIP_EXTERN struct zip_source * +zip_source_function(struct zip *za, zip_source_callback zcb, void *ud) +{ + struct zip_source *zs; + + if (za == NULL) + return NULL; + + if ((zs=(struct zip_source *)malloc(sizeof(*zs))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + zs->f = zcb; + zs->ud = ud; + + return zs; +} + + +ZIP_EXTERN void +zip_source_free(struct zip_source *source) +{ + if (source == NULL) + return; + + (void)source->f(source->ud, NULL, 0, ZIP_SOURCE_FREE); + + free(source); +} + + +struct read_file { + char *fname; /* name of file to copy from */ + FILE *f; /* file to copy from */ + myoff_t off; /* start offset of */ + myoff_t len; /* lengt of data to copy */ + myoff_t remain; /* bytes remaining to be copied */ + int e[2]; /* error codes */ +}; + +static ssize_t read_file(void *state, void *data, size_t len, + enum zip_source_cmd cmd); + + + +ZIP_EXTERN struct zip_source * +zip_source_filep(struct zip *za, FILE *file, myoff_t start, myoff_t len) +{ + if (za == NULL) + return NULL; + + if (file == NULL || start < 0 || len < -1) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + return _zip_source_file_or_p(za, NULL, file, start, len); +} + + + +struct zip_source * +_zip_source_file_or_p(struct zip *za, const char *fname, FILE *file, + myoff_t start, myoff_t len) +{ + struct read_file *f; + struct zip_source *zs; + + if (file == NULL && fname == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((f=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + f->fname = NULL; + if (fname) { + if ((f->fname=strdup(fname)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + free(f); + return NULL; + } + } + f->f = file; + f->off = start; + f->len = (len ? len : -1); + + if ((zs=zip_source_function(za, read_file, f)) == NULL) { + free(f); + return NULL; + } + + return zs; +} + + + +static ssize_t +read_file(void *state, void *data, size_t len, enum zip_source_cmd cmd) +{ + struct read_file *z; + char *buf; + int i, n; + + z = (struct read_file *)state; + buf = (char *)data; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + if (z->fname) { + if ((z->f=fopen(z->fname, "rb")) == NULL) { + z->e[0] = ZIP_ER_OPEN; + z->e[1] = errno; + return -1; + } + } + + if (fseeko(z->f, z->off, SEEK_SET) < 0) { + z->e[0] = ZIP_ER_SEEK; + z->e[1] = errno; + return -1; + } + z->remain = z->len; + return 0; + + case ZIP_SOURCE_READ: + if (z->remain != -1) + n = len > z->remain ? z->remain : len; + else + n = len; + + if ((i=fread(buf, 1, n, z->f)) < 0) { + z->e[0] = ZIP_ER_READ; + z->e[1] = errno; + return -1; + } + + if (z->remain != -1) + z->remain -= i; + + return i; + + case ZIP_SOURCE_CLOSE: + if (z->fname) { + fclose(z->f); + z->f = NULL; + } + return 0; + + case ZIP_SOURCE_STAT: + { + struct zip_stat *st; + struct stat fst; + int err; + + if (len < sizeof(*st)) + return -1; + + if (z->f) + err = fstat(fileno(z->f), &fst); + else + err = stat(z->fname, &fst); + + if (err != 0) { + z->e[0] = ZIP_ER_READ; /* best match */ + z->e[1] = errno; + return -1; + } + + st = (struct zip_stat *)data; + + zip_stat_init(st); + st->mtime = fst.st_mtime; + if (z->len != -1) + st->size = z->len; + else if ((fst.st_mode&S_IFMT) == S_IFREG) + st->size = fst.st_size; + + return sizeof(*st); + } + + case ZIP_SOURCE_ERROR: + if (len < sizeof(int)*2) + return -1; + + memcpy(data, z->e, sizeof(int)*2); + return sizeof(int)*2; + + case ZIP_SOURCE_FREE: + free(z->fname); + if (z->f) + fclose(z->f); + free(z); + return 0; + + default: + ; + } + + return -1; +} + + +ZIP_EXTERN int +zip_name_locate(struct zip *za, const char *fname, int flags) +{ + return _zip_name_locate(za, fname, flags, &za->error); +} + + + +int +_zip_name_locate(struct zip *za, const char *fname, int flags, + struct zip_error *error) +{ + int (*cmp)(const char *, const char *); + const char *fn, *p; + int i, n; + + if (fname == NULL) { + _zip_error_set(error, ZIP_ER_INVAL, 0); + return -1; + } + + cmp = (flags & ZIP_FL_NOCASE) ? strcasecmp : strcmp; + + n = (flags & ZIP_FL_UNCHANGED) ? za->cdir->nentry : za->nentry; + for (i=0; icdir->entry[i].filename; + else + fn = _zip_get_name(za, i, flags, error); + + /* newly added (partially filled) entry */ + if (fn == NULL) + continue; + + if (flags & ZIP_FL_NODIR) { + p = strrchr(fn, '/'); + if (p) + fn = p+1; + } + + if (cmp(fname, fn) == 0) + return i; + } + + _zip_error_set(error, ZIP_ER_NOENT, 0); + return -1; +} + diff --git a/tests/deps/zip-0.2.1/zip/zipfiles.nim b/tests/deps/zip-0.2.1/zip/zipfiles.nim new file mode 100644 index 000000000..274587df9 --- /dev/null +++ b/tests/deps/zip-0.2.1/zip/zipfiles.nim @@ -0,0 +1,193 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements a zip archive creator/reader/modifier. + +import + streams, libzip, times, os, strutils + +const BufSize = 8 * 1024 + +type + ZipArchive* = object of RootObj ## represents a zip archive + mode: FileMode + w: PZip +{.deprecated: [TZipArchive: ZipArchive].} + +proc zipError(z: var ZipArchive) = + var e: ref IOError + new(e) + e.msg = $zip_strerror(z.w) + raise e + +proc open*(z: var ZipArchive, filename: string, mode: FileMode = fmRead): bool = + ## Opens a zip file for reading, writing or appending. All file modes are + ## supported. Returns true iff successful, false otherwise. + var err, flags: int32 + case mode + of fmRead, fmReadWriteExisting, fmAppend: flags = 0 + of fmWrite: + if existsFile(filename): removeFile(filename) + flags = ZIP_CREATE or ZIP_EXCL + of fmReadWrite: flags = ZIP_CREATE + z.w = zip_open(filename, flags, addr(err)) + z.mode = mode + result = z.w != nil + +proc close*(z: var ZipArchive) = + ## Closes a zip file. + zip_close(z.w) + +proc createDir*(z: var ZipArchive, dir: string) = + ## Creates a directory within the `z` archive. This does not fail if the + ## directory already exists. Note that for adding a file like + ## ``"path1/path2/filename"`` it is not necessary + ## to create the ``"path/path2"`` subdirectories - it will be done + ## automatically by ``addFile``. + assert(z.mode != fmRead) + discard zip_add_dir(z.w, dir) + zip_error_clear(z.w) + +proc addFile*(z: var ZipArchive, dest, src: string) = + ## Adds the file `src` to the archive `z` with the name `dest`. `dest` + ## may contain a path that will be created. + assert(z.mode != fmRead) + if not fileExists(src): + raise newException(IOError, "File '" & src & "' does not exist") + var zipsrc = zip_source_file(z.w, src, 0, -1) + if zipsrc == nil: + #echo("Dest: " & dest) + #echo("Src: " & src) + zipError(z) + if zip_add(z.w, dest, zipsrc) < 0'i32: + zip_source_free(zipsrc) + zipError(z) + +proc addFile*(z: var ZipArchive, file: string) = + ## A shortcut for ``addFile(z, file, file)``, i.e. the name of the source is + ## the name of the destination. + addFile(z, file, file) + +proc mySourceCallback(state, data: pointer, len: int, + cmd: ZipSourceCmd): int {.cdecl.} = + var src = cast[Stream](state) + case cmd + of ZIP_SOURCE_OPEN: + if src.setPositionImpl != nil: setPosition(src, 0) # reset + of ZIP_SOURCE_READ: + result = readData(src, data, len) + of ZIP_SOURCE_CLOSE: close(src) + of ZIP_SOURCE_STAT: + var stat = cast[PZipStat](data) + zip_stat_init(stat) + stat.size = high(int32)-1 # we don't know the size + stat.mtime = getTime() + result = sizeof(ZipStat) + of ZIP_SOURCE_ERROR: + var err = cast[ptr array[0..1, cint]](data) + err[0] = ZIP_ER_INTERNAL + err[1] = 0 + result = 2*sizeof(cint) + of constZIP_SOURCE_FREE: GC_unref(src) + of ZIP_SOURCE_SUPPORTS: + # By default a read-only source is supported, which suits us. + result = -1 + else: + # An unknown command, failing + result = -1 + +proc addFile*(z: var ZipArchive, dest: string, src: Stream) = + ## Adds a file named with `dest` to the archive `z`. `dest` + ## may contain a path. The file's content is read from the `src` stream. + assert(z.mode != fmRead) + GC_ref(src) + var zipsrc = zip_source_function(z.w, mySourceCallback, cast[pointer](src)) + if zipsrc == nil: zipError(z) + if zip_add(z.w, dest, zipsrc) < 0'i32: + zip_source_free(zipsrc) + zipError(z) + +# -------------- zip file stream --------------------------------------------- + +type + TZipFileStream = object of StreamObj + f: PZipFile + atEnd: bool + + PZipFileStream* = + ref TZipFileStream ## a reader stream of a file within a zip archive + +proc fsClose(s: Stream) = zip_fclose(PZipFileStream(s).f) +proc fsAtEnd(s: Stream): bool = PZipFileStream(s).atEnd +proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = + result = zip_fread(PZipFileStream(s).f, buffer, bufLen) + if result == 0: + PZipFileStream(s).atEnd = true + +proc newZipFileStream(f: PZipFile): PZipFileStream = + new(result) + result.f = f + result.atEnd = false + result.closeImpl = fsClose + result.readDataImpl = fsReadData + result.atEndImpl = fsAtEnd + # other methods are nil! + +# ---------------------------------------------------------------------------- + +proc getStream*(z: var ZipArchive, filename: string): PZipFileStream = + ## returns a stream that can be used to read the file named `filename` + ## from the archive `z`. Returns nil in case of an error. + ## The returned stream does not support the `setPosition`, `getPosition`, + ## `writeData` or `atEnd` methods. + var x = zip_fopen(z.w, filename, 0'i32) + if x != nil: result = newZipFileStream(x) + +iterator walkFiles*(z: var ZipArchive): string = + ## walks over all files in the archive `z` and returns the filename + ## (including the path). + var i = 0'i32 + var num = zip_get_num_files(z.w) + while i < num: + yield $zip_get_name(z.w, i, 0'i32) + inc(i) + +proc extractFile*(z: var ZipArchive, srcFile: string, dest: Stream) = + ## extracts a file from the zip archive `z` to the destination stream. + var buf: array[BufSize, byte] + var strm = getStream(z, srcFile) + while true: + let bytesRead = strm.readData(addr(buf[0]), buf.len) + if bytesRead <= 0: break + dest.writeData(addr(buf[0]), bytesRead) + + dest.flush() + strm.close() + +proc extractFile*(z: var ZipArchive, srcFile: string, dest: string) = + ## extracts a file from the zip archive `z` to the destination filename. + var file = newFileStream(dest, fmWrite) + if file.isNil: + raise newException(IOError, "Failed to create output file: " & dest) + extractFile(z, srcFile, file) + file.close() + +proc extractAll*(z: var ZipArchive, dest: string) = + ## extracts all files from archive `z` to the destination directory. + createDir(dest) + for file in walkFiles(z): + if file.contains("/"): + createDir(dest / file[0..file.rfind("/")]) + extractFile(z, file, dest / file) + +when not defined(testing) and true: + var zip: ZipArchive + if not zip.open("nim-0.11.0.zip"): + raise newException(IOError, "opening zip failed") + zip.extractAll("test") diff --git a/tests/deps/zip-0.2.1/zip/zlib.nim b/tests/deps/zip-0.2.1/zip/zlib.nim new file mode 100644 index 000000000..f41ed5cfe --- /dev/null +++ b/tests/deps/zip-0.2.1/zip/zlib.nim @@ -0,0 +1,425 @@ +# Converted from Pascal + +## Interface to the zlib http://www.zlib.net/ compression library. + +when defined(windows): + const libz = "zlib1.dll" +elif defined(macosx): + const libz = "libz.dylib" +else: + const libz = "libz.so.1" + +type + Uint* = int32 + Ulong* = int + Ulongf* = int + Pulongf* = ptr Ulongf + ZOffT* = int32 + Pbyte* = cstring + Pbytef* = cstring + Allocfunc* = proc (p: pointer, items: Uint, size: Uint): pointer{.cdecl.} + FreeFunc* = proc (p: pointer, address: pointer){.cdecl.} + InternalState*{.final, pure.} = object + PInternalState* = ptr InternalState + ZStream*{.final, pure.} = object + nextIn*: Pbytef + availIn*: Uint + totalIn*: Ulong + nextOut*: Pbytef + availOut*: Uint + totalOut*: Ulong + msg*: Pbytef + state*: PInternalState + zalloc*: Allocfunc + zfree*: FreeFunc + opaque*: pointer + dataType*: int32 + adler*: Ulong + reserved*: Ulong + + ZStreamRec* = ZStream + PZstream* = ptr ZStream + GzFile* = pointer + ZStreamHeader* = enum + DETECT_STREAM, + RAW_DEFLATE, + ZLIB_STREAM, + GZIP_STREAM + + ZlibStreamError* = object of Exception + +{.deprecated: [TInternalState: InternalState, TAllocfunc: Allocfunc, + TFreeFunc: FreeFunc, TZStream: ZStream, TZStreamRec: ZStreamRec].} + +const + Z_NO_FLUSH* = 0 + Z_PARTIAL_FLUSH* = 1 + Z_SYNC_FLUSH* = 2 + Z_FULL_FLUSH* = 3 + Z_FINISH* = 4 + Z_OK* = 0 + Z_STREAM_END* = 1 + Z_NEED_DICT* = 2 + Z_ERRNO* = -1 + Z_STREAM_ERROR* = -2 + Z_DATA_ERROR* = -3 + Z_MEM_ERROR* = -4 + Z_BUF_ERROR* = -5 + Z_VERSION_ERROR* = -6 + Z_NO_COMPRESSION* = 0 + Z_BEST_SPEED* = 1 + Z_BEST_COMPRESSION* = 9 + Z_DEFAULT_COMPRESSION* = -1 + Z_FILTERED* = 1 + Z_HUFFMAN_ONLY* = 2 + Z_DEFAULT_STRATEGY* = 0 + Z_BINARY* = 0 + Z_ASCII* = 1 + Z_UNKNOWN* = 2 + Z_DEFLATED* = 8 + Z_NULL* = 0 + Z_MEM_LEVEL* = 8 + MAX_WBITS* = 15 + +proc zlibVersion*(): cstring{.cdecl, dynlib: libz, importc: "zlibVersion".} +proc deflate*(strm: var ZStream, flush: int32): int32{.cdecl, dynlib: libz, + importc: "deflate".} +proc deflateEnd*(strm: var ZStream): int32{.cdecl, dynlib: libz, + importc: "deflateEnd".} +proc inflate*(strm: var ZStream, flush: int32): int32{.cdecl, dynlib: libz, + importc: "inflate".} +proc inflateEnd*(strm: var ZStream): int32{.cdecl, dynlib: libz, + importc: "inflateEnd".} +proc deflateSetDictionary*(strm: var ZStream, dictionary: Pbytef, + dictLength: Uint): int32{.cdecl, dynlib: libz, + importc: "deflateSetDictionary".} +proc deflateCopy*(dest, source: var ZStream): int32{.cdecl, dynlib: libz, + importc: "deflateCopy".} +proc deflateReset*(strm: var ZStream): int32{.cdecl, dynlib: libz, + importc: "deflateReset".} +proc deflateParams*(strm: var ZStream, level: int32, strategy: int32): int32{. + cdecl, dynlib: libz, importc: "deflateParams".} +proc inflateSetDictionary*(strm: var ZStream, dictionary: Pbytef, + dictLength: Uint): int32{.cdecl, dynlib: libz, + importc: "inflateSetDictionary".} +proc inflateSync*(strm: var ZStream): int32{.cdecl, dynlib: libz, + importc: "inflateSync".} +proc inflateReset*(strm: var ZStream): int32{.cdecl, dynlib: libz, + importc: "inflateReset".} +proc compress*(dest: Pbytef, destLen: Pulongf, source: Pbytef, sourceLen: Ulong): cint{. + cdecl, dynlib: libz, importc: "compress".} +proc compress2*(dest: Pbytef, destLen: Pulongf, source: Pbytef, + sourceLen: Ulong, level: cint): cint{.cdecl, dynlib: libz, + importc: "compress2".} +proc uncompress*(dest: Pbytef, destLen: Pulongf, source: Pbytef, + sourceLen: Ulong): cint{.cdecl, dynlib: libz, + importc: "uncompress".} +proc compressBound*(sourceLen: Ulong): Ulong {.cdecl, dynlib: libz, importc.} +proc gzopen*(path: cstring, mode: cstring): GzFile{.cdecl, dynlib: libz, + importc: "gzopen".} +proc gzdopen*(fd: int32, mode: cstring): GzFile{.cdecl, dynlib: libz, + importc: "gzdopen".} +proc gzsetparams*(thefile: GzFile, level: int32, strategy: int32): int32{.cdecl, + dynlib: libz, importc: "gzsetparams".} +proc gzread*(thefile: GzFile, buf: pointer, length: int): int32{.cdecl, + dynlib: libz, importc: "gzread".} +proc gzwrite*(thefile: GzFile, buf: pointer, length: int): int32{.cdecl, + dynlib: libz, importc: "gzwrite".} +proc gzprintf*(thefile: GzFile, format: Pbytef): int32{.varargs, cdecl, + dynlib: libz, importc: "gzprintf".} +proc gzputs*(thefile: GzFile, s: Pbytef): int32{.cdecl, dynlib: libz, + importc: "gzputs".} +proc gzgets*(thefile: GzFile, buf: Pbytef, length: int32): Pbytef{.cdecl, + dynlib: libz, importc: "gzgets".} +proc gzputc*(thefile: GzFile, c: char): char{.cdecl, dynlib: libz, + importc: "gzputc".} +proc gzgetc*(thefile: GzFile): char{.cdecl, dynlib: libz, importc: "gzgetc".} +proc gzflush*(thefile: GzFile, flush: int32): int32{.cdecl, dynlib: libz, + importc: "gzflush".} +proc gzseek*(thefile: GzFile, offset: ZOffT, whence: int32): ZOffT{.cdecl, + dynlib: libz, importc: "gzseek".} +proc gzrewind*(thefile: GzFile): int32{.cdecl, dynlib: libz, importc: "gzrewind".} +proc gztell*(thefile: GzFile): ZOffT{.cdecl, dynlib: libz, importc: "gztell".} +proc gzeof*(thefile: GzFile): int {.cdecl, dynlib: libz, importc: "gzeof".} +proc gzclose*(thefile: GzFile): int32{.cdecl, dynlib: libz, importc: "gzclose".} +proc gzerror*(thefile: GzFile, errnum: var int32): Pbytef{.cdecl, dynlib: libz, + importc: "gzerror".} +proc adler32*(adler: Ulong, buf: Pbytef, length: Uint): Ulong{.cdecl, + dynlib: libz, importc: "adler32".} + ## **Warning**: Adler-32 requires at least a few hundred bytes to get rolling. +proc crc32*(crc: Ulong, buf: Pbytef, length: Uint): Ulong{.cdecl, dynlib: libz, + importc: "crc32".} +proc deflateInitu*(strm: var ZStream, level: int32, version: cstring, + streamSize: int32): int32{.cdecl, dynlib: libz, + importc: "deflateInit_".} +proc inflateInitu*(strm: var ZStream, version: cstring, + streamSize: int32): int32 {. + cdecl, dynlib: libz, importc: "inflateInit_".} +proc deflateInit*(strm: var ZStream, level: int32): int32 +proc inflateInit*(strm: var ZStream): int32 +proc deflateInit2u*(strm: var ZStream, level: int32, `method`: int32, + windowBits: int32, memLevel: int32, strategy: int32, + version: cstring, streamSize: int32): int32 {.cdecl, + dynlib: libz, importc: "deflateInit2_".} +proc inflateInit2u*(strm: var ZStream, windowBits: int32, version: cstring, + streamSize: int32): int32{.cdecl, dynlib: libz, + importc: "inflateInit2_".} +proc deflateInit2*(strm: var ZStream, + level, `method`, windowBits, memLevel, + strategy: int32): int32 +proc inflateInit2*(strm: var ZStream, windowBits: int32): int32 +proc zError*(err: int32): cstring{.cdecl, dynlib: libz, importc: "zError".} +proc inflateSyncPoint*(z: PZstream): int32{.cdecl, dynlib: libz, + importc: "inflateSyncPoint".} +proc getCrcTable*(): pointer{.cdecl, dynlib: libz, importc: "get_crc_table".} + +proc deflateBound*(strm: var ZStream, sourceLen: ULong): ULong {.cdecl, + dynlib: libz, importc: "deflateBound".} + +proc deflateInit(strm: var ZStream, level: int32): int32 = + result = deflateInitu(strm, level, zlibVersion(), sizeof(ZStream).cint) + +proc inflateInit(strm: var ZStream): int32 = + result = inflateInitu(strm, zlibVersion(), sizeof(ZStream).cint) + +proc deflateInit2(strm: var ZStream, + level, `method`, windowBits, memLevel, + strategy: int32): int32 = + result = deflateInit2u(strm, level, `method`, windowBits, memLevel, + strategy, zlibVersion(), sizeof(ZStream).cint) + +proc inflateInit2(strm: var ZStream, windowBits: int32): int32 = + result = inflateInit2u(strm, windowBits, zlibVersion(), + sizeof(ZStream).cint) + +proc zlibAllocMem*(appData: pointer, items, size: int): pointer {.cdecl.} = + result = alloc(items * size) + +proc zlibFreeMem*(appData, `block`: pointer) {.cdecl.} = + dealloc(`block`) + + +proc compress*(sourceBuf: cstring; sourceLen: int; level=Z_DEFAULT_COMPRESSION; stream=GZIP_STREAM): string = + ## Given a cstring, returns its deflated version with an optional header. + ## + ## Valid argument for ``stream`` are + ## - ``ZLIB_STREAM`` - add a zlib header and footer. + ## - ``GZIP_STREAM`` - add a basic gzip header and footer. + ## - ``RAW_DEFLATE`` - no header is generated. + ## + ## Passing a nil cstring will crash this proc in release mode and assert in + ## debug mode. + ## + ## Compression level can be set with ``level`` argument. Currently + ## ``Z_DEFAULT_COMPRESSION`` is 6. + ## + ## Returns "" on failure. + assert(not sourceBuf.isNil) + assert(sourceLen >= 0) + + var z: ZStream + var windowBits = MAX_WBITS + case (stream) + of RAW_DEFLATE: windowBits = -MAX_WBITS + of GZIP_STREAM: windowBits = MAX_WBITS + 16 + of ZLIB_STREAM, DETECT_STREAM: + discard # DETECT_STREAM defaults to ZLIB_STREAM + + var status = deflateInit2(z, level.int32, Z_DEFLATED.int32, + windowBits.int32, Z_MEM_LEVEL.int32, + Z_DEFAULT_STRATEGY.int32) + case status + of Z_OK: discard + of Z_MEM_ERROR: raise newException(OutOfMemError, "") + of Z_STREAM_ERROR: raise newException(ZlibStreamError, "invalid zlib stream parameter!") + of Z_VERSION_ERROR: raise newException(ZlibStreamError, "zlib version mismatch!") + else: raise newException(ZlibStreamError, "Unkown error(" & $status & ") : " & $z.msg) + + let space = deflateBound(z, sourceLen) + var compressed = newStringOfCap(space) + z.next_in = sourceBuf + z.avail_in = sourceLen.Uint + z.next_out = addr(compressed[0]) + z.avail_out = space.Uint + + status = deflate(z, Z_FINISH) + if status != Z_STREAM_END: + discard deflateEnd(z) # cleanup allocated ressources + raise newException(ZlibStreamError, "Invalid stream state(" & $status & ") : " & $z.msg) + + status = deflateEnd(z) + if status != Z_OK: # cleanup allocated ressources + raise newException(ZlibStreamError, "Invalid stream state(" & $status & ") : " & $z.msg) + + compressed.setLen(z.total_out) + swap(result, compressed) + +proc compress*(input: string; level=Z_DEFAULT_COMPRESSION; stream=GZIP_STREAM): string = + ## Given a string, returns its deflated version with an optional header. + ## + ## Valid arguments for ``stream`` are + ## - ``ZLIB_STREAM`` - add a zlib header and footer. + ## - ``GZIP_STREAM`` - add a basic gzip header and footer. + ## - ``RAW_DEFLATE`` - no header is generated. + ## + ## Compression level can be set with ``level`` argument. Currently + ## ``Z_DEFAULT_COMPRESSION`` is 6. + ## + ## Returns "" on failure. + result = compress(input, input.len, level, stream) + +proc uncompress*(sourceBuf: cstring, sourceLen: Natural; stream=DETECT_STREAM): string = + ## Given a deflated buffer returns its inflated content as a string. + ## + ## Valid arguments for ``stream`` are + ## - ``DETECT_STREAM`` - detect if zlib or gzip header is present + ## and decompress stream. Fail on raw deflate stream. + ## - ``ZLIB_STREAM`` - decompress a zlib stream. + ## - ``GZIP_STREAM`` - decompress a gzip stream. + ## - ``RAW_DEFLATE`` - decompress a raw deflate stream. + ## + ## Passing a nil cstring will crash this proc in release mode and assert in + ## debug mode. + ## + ## Returns "" on problems. Failure is a very loose concept, it could be you + ## passing a non deflated string, or it could mean not having enough memory + ## for the inflated version. + ## + ## The uncompression algorithm is based on http://zlib.net/zpipe.c. + assert(not sourceBuf.isNil) + assert(sourceLen >= 0) + var z: ZStream + var decompressed: string = "" + var sbytes = 0 + var wbytes = 0 + ## allocate inflate state + + z.availIn = 0 + var wbits = case (stream) + of RAW_DEFLATE: -MAX_WBITS + of ZLIB_STREAM: MAX_WBITS + of GZIP_STREAM: MAX_WBITS + 16 + of DETECT_STREAM: MAX_WBITS + 32 + + var status = inflateInit2(z, wbits.int32) + + case status + of Z_OK: discard + of Z_MEM_ERROR: raise newException(OutOfMemError, "") + of Z_STREAM_ERROR: raise newException(ZlibStreamError, "invalid zlib stream parameter!") + of Z_VERSION_ERROR: raise newException(ZlibStreamError, "zlib version mismatch!") + else: raise newException(ZlibStreamError, "Unkown error(" & $status & ") : " & $z.msg) + + # run loop until all input is consumed. + # handle concatenated deflated stream with header. + while true: + z.availIn = (sourceLen - sbytes).int32 + + # no more input available + if (sourceLen - sbytes) <= 0: break + z.nextIn = sourceBuf[sbytes].unsafeaddr + + # run inflate() on available input until output buffer is full + while true: + # if written bytes >= output size : resize output + if wbytes >= decompressed.len: + let cur_outlen = decompressed.len + let new_outlen = if decompressed.len == 0: sourceLen*2 else: decompressed.len*2 + if new_outlen < cur_outlen: # unsigned integer overflow, buffer too large + discard inflateEnd(z); + raise newException(OverflowError, "zlib stream decompressed size is too large! (size > " & $int.high & ")") + + decompressed.setLen(new_outlen) + + # available space for decompression + let space = decompressed.len - wbytes + z.availOut = space.Uint + z.nextOut = decompressed[wbytes].addr + + status = inflate(z, Z_NO_FLUSH) + if status.int8 notin {Z_OK.int8, Z_STREAM_END.int8, Z_BUF_ERROR.int8}: + discard inflateEnd(z) + case status + of Z_MEM_ERROR: raise newException(OutOfMemError, "") + of Z_DATA_ERROR: raise newException(ZlibStreamError, "invalid zlib stream parameter!") + else: raise newException(ZlibStreamError, "Unkown error(" & $status & ") : " & $z.msg) + + # add written bytes, if any. + wbytes += space - z.availOut.int + + # may need more input + if not (z.availOut == 0): break + + # inflate() says stream is done + if (status == Z_STREAM_END): + # may have another stream concatenated + if z.availIn != 0: + sbytes = sourceLen - z.availIn # add consumed bytes + if inflateReset(z) != Z_OK: # reset zlib struct and try again + raise newException(ZlibStreamError, "Invalid stream state(" & $status & ") : " & $z.msg) + else: + break # end of decompression + + # clean up and don't care about any error + discard inflateEnd(z) + + if status != Z_STREAM_END: + raise newException(ZlibStreamError, "Invalid stream state(" & $status & ") : " & $z.msg) + + decompressed.setLen(wbytes) + swap(result, decompressed) + + +proc uncompress*(sourceBuf: string; stream=DETECT_STREAM): string = + ## Given a GZIP-ed string return its inflated content. + ## + ## Valid arguments for ``stream`` are + ## - ``DETECT_STREAM`` - detect if zlib or gzip header is present + ## and decompress stream. Fail on raw deflate stream. + ## - ``ZLIB_STREAM`` - decompress a zlib stream. + ## - ``GZIP_STREAM`` - decompress a gzip stream. + ## - ``RAW_DEFLATE`` - decompress a raw deflate stream. + ## + ## Returns "" on failure. + result = uncompress(sourceBuf, sourceBuf.len, stream) + + + +proc deflate*(buffer: var string; level=Z_DEFAULT_COMPRESSION; stream=GZIP_STREAM): bool {.discardable.} = + ## Convenience proc which deflates a string and insert an optional header/footer. + ## + ## Valid arguments for ``stream`` are + ## - ``ZLIB_STREAM`` - add a zlib header and footer. + ## - ``GZIP_STREAM`` - add a basic gzip header and footer. + ## - ``RAW_DEFLATE`` - no header is generated. + ## + ## Compression level can be set with ``level`` argument. Currently + ## ``Z_DEFAULT_COMPRESSION`` is 6. + ## + ## Returns true if `buffer` was successfully deflated otherwise the buffer is untouched. + + var temp = compress(addr(buffer[0]), buffer.len, level, stream) + if temp.len != 0: + swap(buffer, temp) + result = true + +proc inflate*(buffer: var string; stream=DETECT_STREAM): bool {.discardable.} = + ## Convenience proc which inflates a string containing compressed data + ## with an optional header. + ## + ## Valid argument for ``stream`` are: + ## - ``DETECT_STREAM`` - detect if zlib or gzip header is present + ## and decompress stream. Fail on raw deflate stream. + ## - ``ZLIB_STREAM`` - decompress a zlib stream. + ## - ``GZIP_STREAM`` - decompress a gzip stream. + ## - ``RAW_DEFLATE`` - decompress a raw deflate stream. + ## + ## It is ok to pass a buffer which doesn't contain deflated data, + ## in this case the proc won't modify the buffer. + ## + ## Returns true if `buffer` was successfully inflated. + + var temp = uncompress(addr(buffer[0]), buffer.len, stream) + if temp.len != 0: + swap(buffer, temp) + result = true diff --git a/tests/deps/zip-0.2.1/zip/zzip.nim b/tests/deps/zip-0.2.1/zip/zzip.nim new file mode 100644 index 000000000..553970e0c --- /dev/null +++ b/tests/deps/zip-0.2.1/zip/zzip.nim @@ -0,0 +1,176 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2008 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module is an interface to the zzip library. + +# Author: +# Guido Draheim +# Tomi Ollila +# Copyright (c) 1999,2000,2001,2002,2003,2004 Guido Draheim +# All rights reserved, +# usage allowed under the restrictions of the +# Lesser GNU General Public License +# or alternatively the restrictions +# of the Mozilla Public License 1.1 + +when defined(windows): + const + dllname = "zzip.dll" +else: + const + dllname = "libzzip.so" + +type + TZZipError* = int32 # Name conflict if we drop the `T` + +const + ZZIP_ERROR* = -4096'i32 + ZZIP_NO_ERROR* = 0'i32 # no error, may be used if user sets it. + ZZIP_OUTOFMEM* = ZZIP_ERROR - 20'i32 # out of memory + ZZIP_DIR_OPEN* = ZZIP_ERROR - 21'i32 # failed to open zipfile, see errno for details + ZZIP_DIR_STAT* = ZZIP_ERROR - 22'i32 # failed to fstat zipfile, see errno for details + ZZIP_DIR_SEEK* = ZZIP_ERROR - 23'i32 # failed to lseek zipfile, see errno for details + ZZIP_DIR_READ* = ZZIP_ERROR - 24'i32 # failed to read zipfile, see errno for details + ZZIP_DIR_TOO_SHORT* = ZZIP_ERROR - 25'i32 + ZZIP_DIR_EDH_MISSING* = ZZIP_ERROR - 26'i32 + ZZIP_DIRSIZE* = ZZIP_ERROR - 27'i32 + ZZIP_ENOENT* = ZZIP_ERROR - 28'i32 + ZZIP_UNSUPP_COMPR* = ZZIP_ERROR - 29'i32 + ZZIP_CORRUPTED* = ZZIP_ERROR - 31'i32 + ZZIP_UNDEF* = ZZIP_ERROR - 32'i32 + ZZIP_DIR_LARGEFILE* = ZZIP_ERROR - 33'i32 + + ZZIP_CASELESS* = 1'i32 shl 12'i32 + ZZIP_NOPATHS* = 1'i32 shl 13'i32 + ZZIP_PREFERZIP* = 1'i32 shl 14'i32 + ZZIP_ONLYZIP* = 1'i32 shl 16'i32 + ZZIP_FACTORY* = 1'i32 shl 17'i32 + ZZIP_ALLOWREAL* = 1'i32 shl 18'i32 + ZZIP_THREADED* = 1'i32 shl 19'i32 + +type + ZZipDir* {.final, pure.} = object + ZZipFile* {.final, pure.} = object + ZZipPluginIO* {.final, pure.} = object + + ZZipDirent* {.final, pure.} = object + d_compr*: int32 ## compression method + d_csize*: int32 ## compressed size + st_size*: int32 ## file size / decompressed size + d_name*: cstring ## file name / strdupped name + + ZZipStat* = ZZipDirent +{.deprecated: [TZZipDir: ZzipDir, TZZipFile: ZzipFile, + TZZipPluginIO: ZzipPluginIO, TZZipDirent: ZzipDirent, + TZZipStat: ZZipStat].} + +proc zzip_strerror*(errcode: int32): cstring {.cdecl, dynlib: dllname, + importc: "zzip_strerror".} +proc zzip_strerror_of*(dir: ptr ZZipDir): cstring {.cdecl, dynlib: dllname, + importc: "zzip_strerror_of".} +proc zzip_errno*(errcode: int32): int32 {.cdecl, dynlib: dllname, + importc: "zzip_errno".} + +proc zzip_geterror*(dir: ptr ZZipDir): int32 {.cdecl, dynlib: dllname, + importc: "zzip_error".} +proc zzip_seterror*(dir: ptr ZZipDir, errcode: int32) {.cdecl, dynlib: dllname, + importc: "zzip_seterror".} +proc zzip_compr_str*(compr: int32): cstring {.cdecl, dynlib: dllname, + importc: "zzip_compr_str".} +proc zzip_dirhandle*(fp: ptr ZZipFile): ptr ZZipDir {.cdecl, dynlib: dllname, + importc: "zzip_dirhandle".} +proc zzip_dirfd*(dir: ptr ZZipDir): int32 {.cdecl, dynlib: dllname, + importc: "zzip_dirfd".} +proc zzip_dir_real*(dir: ptr ZZipDir): int32 {.cdecl, dynlib: dllname, + importc: "zzip_dir_real".} +proc zzip_file_real*(fp: ptr ZZipFile): int32 {.cdecl, dynlib: dllname, + importc: "zzip_file_real".} +proc zzip_realdir*(dir: ptr ZZipDir): pointer {.cdecl, dynlib: dllname, + importc: "zzip_realdir".} +proc zzip_realfd*(fp: ptr ZZipFile): int32 {.cdecl, dynlib: dllname, + importc: "zzip_realfd".} + +proc zzip_dir_alloc*(fileext: cstringArray): ptr ZZipDir {.cdecl, + dynlib: dllname, importc: "zzip_dir_alloc".} +proc zzip_dir_free*(para1: ptr ZZipDir): int32 {.cdecl, dynlib: dllname, + importc: "zzip_dir_free".} + +proc zzip_dir_fdopen*(fd: int32, errcode_p: ptr TZZipError): ptr ZZipDir {.cdecl, + dynlib: dllname, importc: "zzip_dir_fdopen".} +proc zzip_dir_open*(filename: cstring, errcode_p: ptr TZZipError): ptr ZZipDir {. + cdecl, dynlib: dllname, importc: "zzip_dir_open".} +proc zzip_dir_close*(dir: ptr ZZipDir) {.cdecl, dynlib: dllname, + importc: "zzip_dir_close".} +proc zzip_dir_read*(dir: ptr ZZipDir, dirent: ptr ZZipDirent): int32 {.cdecl, + dynlib: dllname, importc: "zzip_dir_read".} + +proc zzip_opendir*(filename: cstring): ptr ZZipDir {.cdecl, dynlib: dllname, + importc: "zzip_opendir".} +proc zzip_closedir*(dir: ptr ZZipDir) {.cdecl, dynlib: dllname, + importc: "zzip_closedir".} +proc zzip_readdir*(dir: ptr ZZipDir): ptr ZZipDirent {.cdecl, dynlib: dllname, + importc: "zzip_readdir".} +proc zzip_rewinddir*(dir: ptr ZZipDir) {.cdecl, dynlib: dllname, + importc: "zzip_rewinddir".} +proc zzip_telldir*(dir: ptr ZZipDir): int {.cdecl, dynlib: dllname, + importc: "zzip_telldir".} +proc zzip_seekdir*(dir: ptr ZZipDir, offset: int) {.cdecl, dynlib: dllname, + importc: "zzip_seekdir".} + +proc zzip_file_open*(dir: ptr ZZipDir, name: cstring, flags: int32): ptr ZZipFile {. + cdecl, dynlib: dllname, importc: "zzip_file_open".} +proc zzip_file_close*(fp: ptr ZZipFile) {.cdecl, dynlib: dllname, + importc: "zzip_file_close".} +proc zzip_file_read*(fp: ptr ZZipFile, buf: pointer, length: int): int {. + cdecl, dynlib: dllname, importc: "zzip_file_read".} +proc zzip_open*(name: cstring, flags: int32): ptr ZZipFile {.cdecl, + dynlib: dllname, importc: "zzip_open".} +proc zzip_close*(fp: ptr ZZipFile) {.cdecl, dynlib: dllname, + importc: "zzip_close".} +proc zzip_read*(fp: ptr ZZipFile, buf: pointer, length: int): int {. + cdecl, dynlib: dllname, importc: "zzip_read".} + +proc zzip_freopen*(name: cstring, mode: cstring, para3: ptr ZZipFile): ptr ZZipFile {. + cdecl, dynlib: dllname, importc: "zzip_freopen".} +proc zzip_fopen*(name: cstring, mode: cstring): ptr ZZipFile {.cdecl, + dynlib: dllname, importc: "zzip_fopen".} +proc zzip_fread*(p: pointer, size: int, nmemb: int, + file: ptr ZZipFile): int {.cdecl, dynlib: dllname, + importc: "zzip_fread".} +proc zzip_fclose*(fp: ptr ZZipFile) {.cdecl, dynlib: dllname, + importc: "zzip_fclose".} + +proc zzip_rewind*(fp: ptr ZZipFile): int32 {.cdecl, dynlib: dllname, + importc: "zzip_rewind".} +proc zzip_seek*(fp: ptr ZZipFile, offset: int, whence: int32): int {. + cdecl, dynlib: dllname, importc: "zzip_seek".} +proc zzip_tell*(fp: ptr ZZipFile): int {.cdecl, dynlib: dllname, + importc: "zzip_tell".} + +proc zzip_dir_stat*(dir: ptr ZZipDir, name: cstring, zs: ptr ZZipStat, + flags: int32): int32 {.cdecl, dynlib: dllname, + importc: "zzip_dir_stat".} +proc zzip_file_stat*(fp: ptr ZZipFile, zs: ptr ZZipStat): int32 {.cdecl, + dynlib: dllname, importc: "zzip_file_stat".} +proc zzip_fstat*(fp: ptr ZZipFile, zs: ptr ZZipStat): int32 {.cdecl, dynlib: dllname, + importc: "zzip_fstat".} + +proc zzip_open_shared_io*(stream: ptr ZZipFile, name: cstring, + o_flags: int32, o_modes: int32, ext: cstringArray, + io: ptr ZZipPluginIO): ptr ZZipFile {.cdecl, + dynlib: dllname, importc: "zzip_open_shared_io".} +proc zzip_open_ext_io*(name: cstring, o_flags: int32, o_modes: int32, + ext: cstringArray, io: ptr ZZipPluginIO): ptr ZZipFile {. + cdecl, dynlib: dllname, importc: "zzip_open_ext_io".} +proc zzip_opendir_ext_io*(name: cstring, o_modes: int32, + ext: cstringArray, io: ptr ZZipPluginIO): ptr ZZipDir {. + cdecl, dynlib: dllname, importc: "zzip_opendir_ext_io".} +proc zzip_dir_open_ext_io*(filename: cstring, errcode_p: ptr TZZipError, + ext: cstringArray, io: ptr ZZipPluginIO): ptr ZZipDir {. + cdecl, dynlib: dllname, importc: "zzip_dir_open_ext_io".} diff --git a/tests/destructor/t12037.nim b/tests/destructor/t12037.nim index 30266690f..1a7d536cc 100644 --- a/tests/destructor/t12037.nim +++ b/tests/destructor/t12037.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c --gc:arc $file''' + cmd: '''nim c --newruntime $file''' output: ''' showing original type, length, and contents seq[int] 1 @[42] copy length and contents 1 @[42] @@ -9,11 +9,11 @@ copy length and contents 1 @[42] proc test() = var sq1 = @[42] echo "showing original type, length, and contents ", sq1.typeof, " ", sq1.len, " ", sq1 - doAssert cast[int](sq1[0].addr) != 0 + doAssert cast[int](sq1[0].unsafeAddr) != 0 var sq2 = sq1 # copy of original echo "copy length and contents ", sq2.len, " ", sq2 - doAssert cast[int](sq2[0].addr) != 0 - doAssert cast[int](sq1[0].addr) != 0 + doAssert cast[int](sq2[0].unsafeAddr) != 0 + doAssert cast[int](sq1[0].unsafeAddr) != 0 test() diff --git a/tests/destructor/t16607.nim b/tests/destructor/t16607.nim deleted file mode 100644 index f98a6d517..000000000 --- a/tests/destructor/t16607.nim +++ /dev/null @@ -1,23 +0,0 @@ -discard """ - matrix: "--gc:refc; --gc:arc" -""" - -# bug #16607 - -type - O {.requiresInit.} = object - initialized: bool - -proc `=destroy`(o: var O) = - doAssert o.initialized, "O was destroyed before initialization!" - -proc initO(): O = - O(initialized: true) - -proc pair(): tuple[a, b: O] = - result = (a: initO(), b: initO()) - -proc main() = - discard pair() - -main() diff --git a/tests/destructor/t17198.nim b/tests/destructor/t17198.nim deleted file mode 100644 index 098db8245..000000000 --- a/tests/destructor/t17198.nim +++ /dev/null @@ -1,32 +0,0 @@ -discard """ - cmd: '''nim c --gc:arc $file''' - output: ''' -other -''' -""" - -import std/macros - -macro bigCaseStmt(arg: untyped): untyped = - result = nnkCaseStmt.newTree(arg) - - # try to change 2000 to a bigger value if it doesn't crash - for x in 0 ..< 2000: - result.add nnkOfBranch.newTree(newStrLitNode($x), newStrLitNode($x)) - - result.add nnkElse.newTree(newStrLitNode("other")) - -macro bigIfElseExpr(): untyped = - result = nnkIfExpr.newTree() - - for x in 0 ..< 1000: - result.add nnkElifExpr.newTree(newLit(false), newStrLitNode($x)) - - result.add nnkElseExpr.newTree(newStrLitNode("other")) - -proc test(arg: string): string = - echo bigIfElseExpr() - - result = bigCaseStmt(arg) - -discard test("test") diff --git a/tests/destructor/t5342.nim b/tests/destructor/t5342.nim deleted file mode 100644 index 0acd5ef9d..000000000 --- a/tests/destructor/t5342.nim +++ /dev/null @@ -1,24 +0,0 @@ -discard """ - matrix: "--mm:refc; --mm:arc" - targets: "c js" - output: ''' -1 -2 -here -2 -1 -''' -""" - - -type - A = object - id: int - B = object - a: A -proc `=destroy`(a: var A) = echo a.id -var x = A(id: 1) -var y = B(a: A(id: 2)) -`=destroy`(x) -`=destroy`(y) -echo "here" \ No newline at end of file diff --git a/tests/destructor/t7346.nim b/tests/destructor/t7346.nim index 3834d39ff..9e5292a61 100644 --- a/tests/destructor/t7346.nim +++ b/tests/destructor/t7346.nim @@ -2,7 +2,8 @@ discard """ joinable: false """ -# This bug could only be reproduced with --newruntime +when not defined(nimNewRuntime): + {.error: "This bug could only be reproduced with --newruntime".} type Obj = object diff --git a/tests/destructor/t9440.nim b/tests/destructor/t9440.nim deleted file mode 100644 index 153bb303d..000000000 --- a/tests/destructor/t9440.nim +++ /dev/null @@ -1,52 +0,0 @@ -discard """ - matrix: "--gc:refc; --gc:orc; --gc:arc" - output: ''' -() -Destroyed -() -Destroyed -() -Destroyed -end -------------------------- -() -Destroyed -end -''' - -""" - -# bug #9440 -block: - type - X = object - - proc `=destroy`(x: var X) = - echo "Destroyed" - - proc main() = - for x in 0 .. 2: - var obj = X() - echo obj - # The destructor call is invoked after "end" is printed - echo "end" - - main() - -echo "-------------------------" - -block: - type - X = object - - proc `=destroy`(x: var X) = - echo "Destroyed" - - proc main() = - block: - var obj = X() - echo obj - # The destructor is not called when obj goes out of scope - echo "end" - - main() diff --git a/tests/destructor/tarray_indexing.nim b/tests/destructor/tarray_indexing.nim index a9dfdf4ed..7efd5a00c 100644 --- a/tests/destructor/tarray_indexing.nim +++ b/tests/destructor/tarray_indexing.nim @@ -1,7 +1,7 @@ discard """ output: '''allocating 1048576 65536 filling page from 1048576 len 65536''' - cmd: '''nim c --gc:arc $file''' + cmd: '''nim c --newruntime $file''' """ # bug #12669 @@ -63,7 +63,7 @@ proc fillPages*(mem: UserProcessMemory, start: uint32, data: seq[byte]) = #echo cast[uint64](addr mem.pageAccess[i]) let page = mem.pageAccess[i] assert page != nil - #copyMem(page, addr data[i * 0x1000 - start], 0x1000) + #copyMem(page, unsafeAddr data[i * 0x1000 - start], 0x1000) const base = 0x00100000 diff --git a/tests/destructor/tatomicptrs.nim b/tests/destructor/tatomicptrs.nim index 88f84d67c..7313afbf5 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]](allocShared0(sizeof(x))) + result = cast[SharedPtr[T]](allocShared(sizeof(x))) result.x[] = x echo "allocating" @@ -72,7 +72,6 @@ template `.=`*[T](s: SharedPtr[T]; field, value: untyped) = from macros import unpackVarargs template `.()`*[T](s: SharedPtr[T]; field: untyped, args: varargs[untyped]): untyped = - # xxx this isn't used, the test should be improved unpackVarargs(s.x.field, args) diff --git a/tests/destructor/tbintree2.nim b/tests/destructor/tbintree2.nim index d56c2850b..6fdda6e54 100644 --- a/tests/destructor/tbintree2.nim +++ b/tests/destructor/tbintree2.nim @@ -21,21 +21,21 @@ proc merge(lower, greater: owned Node): owned Node = elif greater.isNil: result = lower elif lower.y < greater.y: - lower.right = merge(move lower.right, greater) + lower.right = merge(lower.right, greater) result = lower else: - greater.left = merge(lower, move greater.left) + greater.left = merge(lower, greater.left) result = greater proc splitBinary(orig: owned Node, value: int32): (owned Node, owned Node) = if orig.isNil: result = (nil, nil) elif orig.x < value: - let splitPair = splitBinary(move orig.right, value) + let splitPair = splitBinary(orig.right, value) orig.right = splitPair[0] result = (orig, splitPair[1]) else: - let splitPair = splitBinary(move orig.left, value) + let splitPair = splitBinary(orig.left, value) orig.left = splitPair[1] result = (splitPair[0], orig) @@ -57,7 +57,7 @@ proc `=destroy`(t: var Tree) {.nodestroy.} = let x = s.pop if x.left != nil: s.add(x.left) if x.right != nil: s.add(x.right) - `=dispose`(x) + dispose(x) `=destroy`(s) proc hasValue(self: var Tree, x: int32): bool = diff --git a/tests/destructor/tcaseobj_transitions.nim b/tests/destructor/tcaseobj_transitions.nim index 61464101f..4e203f4ef 100644 --- a/tests/destructor/tcaseobj_transitions.nim +++ b/tests/destructor/tcaseobj_transitions.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c --gc:arc $file''' + cmd: '''nim c --newruntime $file''' output: '''no crash''' """ diff --git a/tests/destructor/tcustomseqs.nim b/tests/destructor/tcustomseqs.nim index 17a19f871..4087dc4a8 100644 --- a/tests/destructor/tcustomseqs.nim +++ b/tests/destructor/tcustomseqs.nim @@ -121,7 +121,7 @@ proc createSeq*[T](elems: varargs[T]): myseq[T] = result.data = cast[type(result.data)](alloc(result.cap * sizeof(T))) inc allocCount when supportsCopyMem(T): - copyMem(result.data, addr(elems[0]), result.cap * sizeof(T)) + copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T)) else: for i in 0..= self.cap: resize(self) @@ -58,22 +60,22 @@ proc add*(self: var mystring; c: char) = inc self.len proc ensure(self: var mystring; newLen: int) = - 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)) + 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)) proc add*(self: var mystring; y: mystring) = - let newLen = self.len + y.len + let newLen = len + y.len ensure(self, newLen) - copyMem(addr self.data[self.len], y.data, y.data.len + 1) - self.len = newLen + copyMem(addr data[len], y.data, y.data.len + 1) + len = newLen proc create*(lit: string): mystring = let newLen = lit.len ensure(result, newLen) - copyMem(addr result.data[result.len], addr lit[0], newLen + 1) + copyMem(addr result.data[result.len], unsafeAddr lit[0], newLen + 1) result.len = newLen proc `&`*(a, b: mystring): mystring = diff --git a/tests/destructor/tdont_return_unowned_from_owned.nim b/tests/destructor/tdont_return_unowned_from_owned.nim index ffe87cd76..a726960c6 100644 --- a/tests/destructor/tdont_return_unowned_from_owned.nim +++ b/tests/destructor/tdont_return_unowned_from_owned.nim @@ -1,12 +1,8 @@ discard """ cmd: "nim check --newruntime --hints:off $file" - nimout: ''' -tdont_return_unowned_from_owned.nim(26, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref -tdont_return_unowned_from_owned.nim(27, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref -tdont_return_unowned_from_owned.nim(31, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type -tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type -tdont_return_unowned_from_owned.nim(46, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type -tdont_return_unowned_from_owned.nim(49, 6) Error: type mismatch: got + nimout: '''tdont_return_unowned_from_owned.nim(36, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type +tdont_return_unowned_from_owned.nim(39, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type +tdont_return_unowned_from_owned.nim(42, 6) Error: type mismatch: got but expected one of: proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.}) first type mismatch at position: 2 @@ -14,21 +10,18 @@ proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.}) 2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them expression: new(result) -tdont_return_unowned_from_owned.nim(49, 6) Error: illformed AST: +tdont_return_unowned_from_owned.nim(42, 6) Error: illformed AST: +tdont_return_unowned_from_owned.nim(50, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref +tdont_return_unowned_from_owned.nim(51, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref +tdont_return_unowned_from_owned.nim(55, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type ''' - errormsg: "illformed AST:" + errormsg: "cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type" + line: 55 """ -proc testA(result: var (RootRef, RootRef)) = - let r: owned RootRef = RootRef() - result[0] = r - result[1] = RootRef() -proc testB(): RootRef = - let r: owned RootRef = RootRef() - result = r @@ -46,11 +39,17 @@ proc newObjB(): Obj = result = Obj() proc newObjC(): Obj = - new(result) # illFormedAst raises GlobalError, - # without pipeline parsing, it needs to placed at the end - # in case that it disturbs other errors + new(result) let a = newObjA() let b = newObjB() let c = newObjC() +proc testA(result: var (RootRef, RootRef)) = + let r: owned RootRef = RootRef() + result[0] = r + result[1] = RootRef() + +proc testB(): RootRef = + let r: owned RootRef = RootRef() + result = r diff --git a/tests/destructor/tgcdestructors.nim b/tests/destructor/tgcdestructors.nim index 07a3731a0..4731bf694 100644 --- a/tests/destructor/tgcdestructors.nim +++ b/tests/destructor/tgcdestructors.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c -d:nimAllocStats --gc:arc $file''' + cmd: '''nim c -d:nimAllocStats --newruntime $file''' output: '''hi ho ha diff --git a/tests/destructor/tglobaldestructor.nim b/tests/destructor/tglobaldestructor.nim index 4d002a092..403f670a0 100644 --- a/tests/destructor/tglobaldestructor.nim +++ b/tests/destructor/tglobaldestructor.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c --gc:arc $file''' + cmd: '''nim c --newruntime $file''' output: '''(v: 42) igotdestroyed''' """ diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index a583a1704..5faaabb8b 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -50,7 +50,7 @@ proc `=destroy`(o: var Pony) = echo "Pony is dying!" proc getPony: Pony = - result = Pony(name: "Sparkles") + result.name = "Sparkles" iterator items(p: Pony): int = for i in 1..4: @@ -178,7 +178,7 @@ proc myfuncLoop(x: int): MySeqNonCopyable = discard myfuncLoop(3) #------------------------------------------------------------ -# Move into table via openArray +# Move into table via openarray #------------------------------------------------------------ type diff --git a/tests/destructor/tnewruntime_misc.nim b/tests/destructor/tnewruntime_misc.nim index 21c70557d..1a5b0e3b0 100644 --- a/tests/destructor/tnewruntime_misc.nim +++ b/tests/destructor/tnewruntime_misc.nim @@ -7,7 +7,7 @@ axc ... destroying GenericObj[T] GenericObj[system.int] test -(allocCount: 12, deallocCount: 10) +(allocCount: 13, deallocCount: 11) 3''' """ @@ -25,13 +25,8 @@ putEnv("HEAPTRASHING", "Indeed") let s1 = getAllocStats() - -proc newTableOwned[A, B](initialSize = defaultInitialSize): owned(TableRef[A, B]) = - new(result) - result[] = initTable[A, B](initialSize) - proc main = - var w = newTableOwned[string, owned Node]() + var w = newTable[string, owned Node]() w["key"] = Node(field: "value") echo w["key"][] echo getEnv("HEAPTRASHING") @@ -137,16 +132,7 @@ proc xx(xml: string): MyObject = discard xx("test") - -# 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 +echo getAllocStats() - s1 # bug #13457 var s = "abcde" diff --git a/tests/destructor/tnewruntime_strutils.nim b/tests/destructor/tnewruntime_strutils.nim index 9c8d41973..0725718d7 100644 --- a/tests/destructor/tnewruntime_strutils.nim +++ b/tests/destructor/tnewruntime_strutils.nim @@ -1,12 +1,11 @@ discard """ valgrind: true - cmd: '''nim c -d:nimAllocStats --gc:arc -d:useMalloc $file''' + cmd: '''nim c -d:nimAllocStats --newruntime -d:useMalloc $file''' output: ''' @[(input: @["KXSC", "BGMC"]), (input: @["PXFX"]), (input: @["WXRQ", "ZSCZD"])] 14 First tasks completed. -Second tasks completed. -test1''' +Second tasks completed.''' """ import strutils, os, std / wordwrap @@ -242,13 +241,3 @@ when true: test_string_b.setLen new_len_b echo "Second tasks completed." - -# bug #17450 -proc main = - var i = 1 - echo: - block: - "test" & $i - -main() - diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim index 8cf757e8b..fa5495689 100644 --- a/tests/destructor/topttree.nim +++ b/tests/destructor/topttree.nim @@ -1,5 +1,4 @@ discard """ - disabled: i386 output: '''10.0 60.0 90.0 diff --git a/tests/destructor/towned_binary_tree.nim b/tests/destructor/towned_binary_tree.nim index fb635e7c6..320e33759 100644 --- a/tests/destructor/towned_binary_tree.nim +++ b/tests/destructor/towned_binary_tree.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c -d:nimAllocStats --gc:arc $file''' + cmd: '''nim c -d:nimAllocStats --newruntime $file''' output: '''31665 (allocCount: 33334, deallocCount: 33334)''' """ diff --git a/tests/destructor/tprevent_assign2.nim b/tests/destructor/tprevent_assign2.nim index fd1a711db..ef20672d5 100644 --- a/tests/destructor/tprevent_assign2.nim +++ b/tests/destructor/tprevent_assign2.nim @@ -18,7 +18,7 @@ proc createTree(x: int): Foo = proc take2(a, b: sink Foo) = echo a.x, " ", b.x -when false: +proc allowThis() = var otherTree: Foo try: for i in 0..3: @@ -51,5 +51,5 @@ proc preventThis() = else: discard -#allowThis() +allowThis() preventThis() diff --git a/tests/destructor/tprevent_assign3.nim b/tests/destructor/tprevent_assign3.nim index b9bd40fa9..0577aa5ff 100644 --- a/tests/destructor/tprevent_assign3.nim +++ b/tests/destructor/tprevent_assign3.nim @@ -18,7 +18,7 @@ proc createTree(x: int): Foo = proc take2(a, b: sink Foo) = echo a.x, " ", b.x -when false: +proc allowThis() = var otherTree: Foo try: for i in 0..3: @@ -47,7 +47,7 @@ proc preventThis2() = finally: echo otherTree -#allowThis() +allowThis() preventThis2() diff --git a/tests/destructor/trecursive.nim b/tests/destructor/trecursive.nim index e7afa6ba9..17a40e5a9 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 = createShared(int) + result.p = create(int) result.p[] = i proc test: seq[MyObject] = diff --git a/tests/destructor/tsimpleclosure.nim b/tests/destructor/tsimpleclosure.nim index 9626dd6f8..35c57a634 100644 --- a/tests/destructor/tsimpleclosure.nim +++ b/tests/destructor/tsimpleclosure.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c -d:nimAllocStats --gc:arc $file''' + cmd: '''nim c -d:nimAllocStats --newruntime $file''' output: '''a b 70 hello diff --git a/tests/destructor/tuse_ownedref_after_move.nim b/tests/destructor/tuse_ownedref_after_move.nim index 69348d530..ce96b741e 100644 --- a/tests/destructor/tuse_ownedref_after_move.nim +++ b/tests/destructor/tuse_ownedref_after_move.nim @@ -1,6 +1,6 @@ discard """ cmd: '''nim c --newruntime $file''' - errormsg: "'=copy' is not available for type ; requires a copy because it's not the last read of ':envAlt.b1'; routine: main" + errormsg: "'=copy' is not available for type ; requires a copy because it's not the last read of ':envAlt.b1'; another read is done here: tuse_ownedref_after_move.nim(52, 4)" line: 48 """ diff --git a/tests/destructor/tuse_result_prevents_sinks.nim b/tests/destructor/tuse_result_prevents_sinks.nim index e74c16da3..d2777bd97 100644 --- a/tests/destructor/tuse_result_prevents_sinks.nim +++ b/tests/destructor/tuse_result_prevents_sinks.nim @@ -18,7 +18,7 @@ proc `=sink`(self: var Foo; other: Foo) = proc `=destroy`(self: var Foo) = discard template preventCursorInference(x) = - let p = addr(x) + let p = unsafeAddr(x) proc test(): Foo = result = Foo() diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim index 6fa419996..9c05b2ae1 100644 --- a/tests/destructor/tv2_cast.nim +++ b/tests/destructor/tv2_cast.nim @@ -1,113 +1,10 @@ discard """ + cmd: '''nim c --newruntime $file''' output: '''@[1] @[116, 101, 115, 116] -@[1953719668, 875770417] -destroying O1''' - cmd: '''nim c --gc:arc --expandArc:main --expandArc:main1 --expandArc:main2 --expandArc:main3 --hints:off --assertions:off $file''' - nimout: ''' ---expandArc: main - -var - data - :tmpD - :tmpD_1 - :tmpD_2 -data = - `=wasMoved`(:tmpD) - `=copy`(:tmpD, cast[string]( - :tmpD_2 = encode(cast[seq[byte]]( - :tmpD_1 = newString(100) - :tmpD_1)) - :tmpD_2)) - :tmpD -`=destroy`(:tmpD_2) -`=destroy_1`(:tmpD_1) -`=destroy_1`(data) --- end of expandArc ------------------------ ---expandArc: main1 - -var - s - data - :tmpD - :tmpD_1 -s = newString(100) -data = - `=wasMoved`(:tmpD) - `=copy`(:tmpD, cast[string]( - :tmpD_1 = encode(toOpenArrayByte(s, 0, len(s) - 1)) - :tmpD_1)) - :tmpD -`=destroy`(:tmpD_1) -`=destroy_1`(data) -`=destroy_1`(s) --- end of expandArc ------------------------ ---expandArc: main2 - -var - s - data - :tmpD - :tmpD_1 -s = newSeq(100) -data = - `=wasMoved`(:tmpD) - `=copy`(:tmpD, cast[string]( - :tmpD_1 = encode(s) - :tmpD_1)) - :tmpD -`=destroy`(:tmpD_1) -`=destroy_1`(data) -`=destroy`(s) --- end of expandArc ------------------------ ---expandArc: main3 - -var - data - :tmpD - :tmpD_1 - :tmpD_2 -data = - `=wasMoved`(:tmpD) - `=copy`(:tmpD, cast[string]( - :tmpD_2 = encode do: - :tmpD_1 = newSeq(100) - :tmpD_1 - :tmpD_2)) - :tmpD -`=destroy`(:tmpD_2) -`=destroy`(:tmpD_1) -`=destroy_1`(data) --- end of expandArc ------------------------ -''' +@[1953719668, 875770417]''' """ -func encode*(src: openArray[byte]): seq[byte] = - result = newSeq[byte](src.len) - -template compress*(src: string): string = - cast[string](encode(cast[seq[byte]](src))) - -proc main = - let data = compress(newString(100)) -main() - -proc main1 = - var - s = newString(100) - let data = cast[string](encode(s.toOpenArrayByte(0, s.len-1))) -main1() - -proc main2 = - var - s = newSeq[byte](100) - let data = cast[string](encode(s)) -main2() - -proc main3 = - let data = cast[string](encode(newSeq[byte](100))) -main3() - # bug #11018 discard cast[seq[uint8]](@[1]) discard cast[seq[uint8]]("test") @@ -123,26 +20,4 @@ echo a #issue 11204 var ac {.compileTime.} = @["a", "b"] -const bc = ac.len - - -type - O = object of RootRef - i: int - - O1 = object of O - O2 = object of O - -proc `=destroy`(o: var O) = - echo "destroying O" - -proc `=destroy`(o: var O1) = - echo "destroying O1" - -proc `=destroy`(o: var O2) = - echo "destroying O2" - -proc test = - let o3 = cast[ref O2]((ref O1)()) - -test() +const bc = ac.len \ No newline at end of file diff --git a/tests/destructor/twasmoved_error.nim b/tests/destructor/twasmoved_error.nim deleted file mode 100644 index 1cd57e3df..000000000 --- a/tests/destructor/twasmoved_error.nim +++ /dev/null @@ -1,37 +0,0 @@ -discard """ - cmd: '''nim c --mm:arc $file''' - errormsg: "'=wasMoved' is not available for type ; routine: main" -""" - -# bug #19291 - -const - screenWidth = 800 - screenHeight = 450 - -var - ready = false -type - Game = object - -proc `=destroy`(x: var Game) = - assert ready, "Window is already opened" - ready = false - -proc `=sink`(x: var Game; y: Game) {.error.} -proc `=copy`(x: var Game; y: Game) {.error.} -proc `=wasMoved`(x: var Game) {.error.} - -proc initGame(width, height: int32, title: string): Game = - assert not ready, "Window is already closed" - ready = true - -proc update(x: Game) = discard - -proc main = - var g = initGame(screenWidth, screenHeight, "Tetris raylib") - g.update() - var g2 = g - echo "hello" - -main() diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim index 69cb9f6a1..032050139 100644 --- a/tests/discard/tdiscardable.nim +++ b/tests/discard/tdiscardable.nim @@ -65,48 +65,3 @@ proc main2() = main1() main2() - -block: # bug #13583 - block: - proc hello(): int {.discardable.} = 12 - - iterator test(): int {.closure.} = - while true: - hello() - - let t = test - - block: - proc hello(): int {.discardable.} = 12 - - iterator test(): int {.closure.} = - while true: - block: - yield 12 - hello() - - let t = test - doAssert t() == 12 - - block: - proc hello(): int {.discardable.} = 12 - - iterator test(): int {.closure.} = - while true: - yield 12 - hello() - - let t = test - doAssert t() == 12 - -block: - proc bar(): string {.discardable.} = - "15" - - proc foo(): int = - while true: - raise newException(ValueError, "check") - 12 - - doAssertRaises(ValueError): - doAssert foo() == 12 diff --git a/tests/discard/tillegaldiscardtypes.nim b/tests/discard/tillegaldiscardtypes.nim deleted file mode 100644 index b7877bcd2..000000000 --- a/tests/discard/tillegaldiscardtypes.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - cmd: "nim check $file" - errormsg: "statement returns no value that can be discarded" - nimout: ''' -tillegaldiscardtypes.nim(11, 3) Error: statement returns no value that can be discarded -tillegaldiscardtypes.nim(12, 3) Error: statement returns no value that can be discarded -''' -""" - -proc b(v: int) = # bug #21360 - discard @[] - discard [] - -b(0) \ No newline at end of file diff --git a/tests/distinct/tdistinct.nim b/tests/distinct/tdistinct.nim index 8ec083020..d64f33443 100644 --- a/tests/distinct/tdistinct.nim +++ b/tests/distinct/tdistinct.nim @@ -1,5 +1,4 @@ discard """ - targets: "c js" output: ''' tdistinct 25 @@ -8,7 +7,6 @@ false false false Foo -foo ''' """ @@ -135,69 +133,8 @@ block tRequiresInit: reject: var s: DistinctString s = "test" - doAssert string(s) == "test" + doAssert s == "test" accept: - let s = DistinctString("test") - doAssert string(s) == "test" - -block: #17322 - type - A[T] = distinct string - - proc foo(a: var A) = - a.string.add "foo" - - type - B = distinct A[int] - - var b: B - foo(A[int](b)) - echo A[int](b).string - b.string.add "bar" - assert b.string == "foobar" - -type Foo = distinct string - -template main() = - # xxx put everything here to test under RT + VM - block: # bug #12282 - block: - proc test() = - var s: Foo - s.string.add('c') - doAssert s.string == "c" # was failing - test() - - block: - proc add(a: var Foo, b: char) {.borrow.} - proc test() = - var s: Foo - s.add('c') - doAssert s.string == "c" # was ok - test() - - block: - proc add(a: var Foo, b: char) {.borrow.} - proc test() = - var s: string - s.Foo.add('c') - doAssert s.string == "c" # was failing - test() - block: #18061 - type - A = distinct (0..100) - B = A(0) .. A(10) - proc test(b: B) = discard - let - a = A(10) - b = B(a) - test(b) - - proc test(a: A) = discard - discard cast[B](A(1)) - var c: B - - -static: main() -main() + let s = "test" + doAssert s == "test" diff --git a/tests/distinct/tdistinct_issues.nim b/tests/distinct/tdistinct_issues.nim index 747cf0b8d..ce71344d0 100644 --- a/tests/distinct/tdistinct_issues.nim +++ b/tests/distinct/tdistinct_issues.nim @@ -65,17 +65,3 @@ block t9322: proc mystr(s: string) = echo s mystr($Fix("apr")) - - -block: # bug #13517 - type MyUint64 = distinct uint64 - - proc `==`(a: MyUint64, b: uint64): bool = uint64(a) == b - - block: - doAssert MyUint64.high is MyUint64 - doAssert MyUint64.high == 18446744073709551615'u64 - - static: - doAssert MyUint64.high is MyUint64 - doAssert MyUint64.high == 18446744073709551615'u64 diff --git a/tests/distinct/tnil.nim b/tests/distinct/tnil.nim index b54c07432..5bdb97f37 100644 --- a/tests/distinct/tnil.nim +++ b/tests/distinct/tnil.nim @@ -1,21 +1,29 @@ +discard """ +output: ''' +1 +0 +0 +''' +""" {.experimental: "notnil".} type MyPointer = distinct pointer MyString = distinct string MyInt = distinct int -proc foo(a: MyPointer): int = +proc foo(a: MyPointer) = # workaround a Windows 'repr' difference: - cast[int](a) + echo cast[int](a) -doAssert foo(cast[MyPointer](1)) == 1 -doAssert foo(cast[MyPointer](nil)) == 0 -doAssert foo(MyPointer(nil)) == 0 +foo(cast[MyPointer](1)) +foo(cast[MyPointer](nil)) +foo(nil) 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/dll/client.nim b/tests/dll/client.nim index 62697569f..150af3a17 100644 --- a/tests/dll/client.nim +++ b/tests/dll/client.nim @@ -1,4 +1,5 @@ discard """ + output: "Done" cmd: "nim $target --debuginfo --hints:on --define:useNimRtl $options $file" """ @@ -36,8 +37,5 @@ proc eval(n: PNode): int = for i in 0..100_000: discard eval(buildTree(2)) -# bug https://forum.nim-lang.org/t/8176; Error: ambiguous identifier: 'nimrtl' -import std/strutils -doAssert join(@[1, 2]) == "12" -doAssert join(@[1.5, 2.5]) == "1.52.5" -doAssert join(@["a", "bc"]) == "abc" +echo "Done" + diff --git a/tests/dll/nimhcr_integration.nim b/tests/dll/nimhcr_integration.nim index ac34f1f85..58851b5c4 100644 --- a/tests/dll/nimhcr_integration.nim +++ b/tests/dll/nimhcr_integration.nim @@ -1,5 +1,5 @@ discard """ - disabled: "true" + disabled: "openbsd" output: ''' main: HELLO! main: hasAnyModuleChanged? true diff --git a/tests/effects/tdiagnostic_messages.nim b/tests/effects/tdiagnostic_messages.nim deleted file mode 100644 index b1acf8c5c..000000000 --- a/tests/effects/tdiagnostic_messages.nim +++ /dev/null @@ -1,37 +0,0 @@ -discard """ - nimoutFull: true - action: "reject" - cmd: "nim r --hint:Conf:off $file" - nimout: ''' -tdiagnostic_messages.nim(36, 6) Error: 'a' can have side effects -> tdiagnostic_messages.nim(37, 30) Hint: 'a' calls `.sideEffect` 'callWithSideEffects' ->> tdiagnostic_messages.nim(29, 6) Hint: 'callWithSideEffects' called by 'a' ->>> tdiagnostic_messages.nim(31, 34) Hint: 'callWithSideEffects' calls `.sideEffect` 'indirectCallViaVarParam' ->>>> tdiagnostic_messages.nim(25, 6) Hint: 'indirectCallViaVarParam' called by 'callWithSideEffects' ->>>>> tdiagnostic_messages.nim(26, 7) Hint: 'indirectCallViaVarParam' calls routine via hidden pointer indirection ->>> tdiagnostic_messages.nim(32, 33) Hint: 'callWithSideEffects' calls `.sideEffect` 'indirectCallViaPointer' ->>>> tdiagnostic_messages.nim(27, 6) Hint: 'indirectCallViaPointer' called by 'callWithSideEffects' ->>>>> tdiagnostic_messages.nim(28, 32) Hint: 'indirectCallViaPointer' calls routine via pointer indirection ->>> tdiagnostic_messages.nim(33, 3) Hint: 'callWithSideEffects' calls `.sideEffect` 'myEcho' ->>>> tdiagnostic_messages.nim(24, 6) Hint: 'myEcho' called by 'callWithSideEffects' ->>> tdiagnostic_messages.nim(34, 3) Hint: 'callWithSideEffects' accesses global state 'globalVar' ->>>> tdiagnostic_messages.nim(23, 5) Hint: 'globalVar' accessed by 'callWithSideEffects' - -''' -""" - -var globalVar = 0 -proc myEcho(a: string) {.sideEffect.} = discard -proc indirectCallViaVarParam(call: var proc(): int {.nimcall.}): int = - call() -proc indirectCallViaPointer(call: pointer): int = - cast[ptr proc(): int](call)[]() -proc callWithSideEffects(): int = - var p = proc (): int {.nimcall.} = 0 - discard indirectCallViaVarParam(p) - discard indirectCallViaPointer(addr p) - myEcho "" - globalVar - -func a: int = - discard callWithSideEffects() diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim index caa8907c3..66d6a3518 100644 --- a/tests/effects/teffects1.nim +++ b/tests/effects/teffects1.nim @@ -1,10 +1,12 @@ discard """ - cmd: "nim check --hint:Conf:off --hint:XDeclaredButNotUsed:off $file" - nimout: ''' -teffects1.nim(17, 28) template/generic instantiation from here -''' + 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''' """ -{.push warningAsError[Effect]: on.} + type TObj {.pure, inheritable.} = object TObjB = object of TObj @@ -14,10 +16,11 @@ 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 - ^ writeLine stdout, ["arg"] can raise an unlisted exception: ref IOError ]# + ^ can raise an unlisted exception: ref IOError + ]# proc forw: int = raise newException(IOError, "arg") @@ -27,16 +30,14 @@ proc forw: int = type MyProcType* = proc(x: int): string #{.raises: [ValueError, Defect].} -proc foo(x: int): string {.nimcall, raises: [ValueError].} = +proc foo(x: int): string {.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.}' - Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'. -.raise effects differ +type mismatch: got but expected 'MyProcType = proc (x: int): string{.closure.}' + ]# {.pop.} -{.pop.} diff --git a/tests/effects/teffects11.nim b/tests/effects/teffects11.nim deleted file mode 100644 index 20b7026ab..000000000 --- a/tests/effects/teffects11.nim +++ /dev/null @@ -1,21 +0,0 @@ -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 deleted file mode 100644 index 5f5499c38..000000000 --- a/tests/effects/teffects12.nim +++ /dev/null @@ -1,52 +0,0 @@ -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 deleted file mode 100644 index 73082f997..000000000 --- a/tests/effects/teffects13.nim +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index 6291d9569..000000000 --- a/tests/effects/teffects14.nim +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index c3079cdbc..000000000 --- a/tests/effects/teffects15.nim +++ /dev/null @@ -1,18 +0,0 @@ -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 deleted file mode 100644 index ee8f782a3..000000000 --- a/tests/effects/teffects16.nim +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index 5e6b83896..000000000 --- a/tests/effects/teffects17.nim +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index 576e76635..000000000 --- a/tests/effects/teffects18.nim +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index 6b4ab0819..000000000 --- a/tests/effects/teffects19.nim +++ /dev/null @@ -1,23 +0,0 @@ -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 777a4cebc..e4b50aba5 100644 --- a/tests/effects/teffects2.nim +++ b/tests/effects/teffects2.nim @@ -2,9 +2,9 @@ discard """ errormsg: "can raise an unlisted exception: ref IOError" line: 19 """ -{.push warningAsError[Effect]: on.} + type - TObj {.pure, inheritable.} = object + TObj = object {.pure, inheritable.} TObjB = object of TObj a, b, c: string @@ -17,4 +17,3 @@ proc lier(): int {.raises: [IOError].} = proc forw: int = raise newException(IOError, "arg") -{.pop.} diff --git a/tests/effects/teffects3.nim b/tests/effects/teffects3.nim index 4c050510a..ee5470c47 100644 --- a/tests/effects/teffects3.nim +++ b/tests/effects/teffects3.nim @@ -4,7 +4,7 @@ discard """ """ type - TObj {.pure, inheritable.} = object + TObj = object {.pure, inheritable.} 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 b875754b6..88cc0efa9 100644 --- a/tests/effects/teffects4.nim +++ b/tests/effects/teffects4.nim @@ -4,7 +4,7 @@ discard """ """ type - TObj {.pure, inheritable.} = object + TObj = object {.pure, inheritable.} TObjB = object of TObj a, b, c: string fn: proc (): int {.tags: [ReadIOEffect].} diff --git a/tests/effects/teffects6.nim b/tests/effects/teffects6.nim index d3af22434..6a4eea155 100644 --- a/tests/effects/teffects6.nim +++ b/tests/effects/teffects6.nim @@ -21,7 +21,7 @@ createMenuItem(s, "Go to definition...", ) -proc noRaise(x: proc()) {.raises: [], effectsOf: x.} = +proc noRaise(x: proc()) {.raises: [].} = # unknown call that might raise anything, but valid: x() @@ -34,23 +34,3 @@ proc use*() = use() - -# bug #12642 -import os - -proc raises() {.raises: Exception.} = discard -proc harmless() {.raises: [].} = discard - -let x = if paramStr(1) == "true": harmless else: raises - -let - choice = 0 - -proc withoutSideEffects(): int = 0 -proc withSideEffects(): int = echo "foo" # the echo causes the side effect - -let procPtr = case choice - of 0: withoutSideEffects - else: withSideEffects - -echo procPtr.repr diff --git a/tests/effects/teffects7.nim b/tests/effects/teffects7.nim index 9b7fbf5f0..73865b18d 100644 --- a/tests/effects/teffects7.nim +++ b/tests/effects/teffects7.nim @@ -2,7 +2,7 @@ discard """ errormsg: "can raise an unlisted exception: ref ValueError" line: 10 """ -{.push warningAsError[Effect]: on.} + proc foo() {.raises: [].} = try: discard @@ -12,5 +12,3 @@ proc foo() {.raises: [].} = discard foo() - -{.pop.} diff --git a/tests/effects/teffects8.nim b/tests/effects/teffects8.nim index 359b3a1df..fb3c088d6 100644 --- a/tests/effects/teffects8.nim +++ b/tests/effects/teffects8.nim @@ -2,7 +2,7 @@ discard """ errormsg: "can raise an unlisted exception: Exception" line: 10 """ -{.push warningAsError[Effect]: on.} + proc foo() {.raises: [].} = try: discard @@ -10,4 +10,3 @@ proc foo() {.raises: [].} = raise foo() -{.pop.} diff --git a/tests/effects/teffectsmisc.nim b/tests/effects/teffectsmisc.nim deleted file mode 100644 index 8fb95b275..000000000 --- a/tests/effects/teffectsmisc.nim +++ /dev/null @@ -1,39 +0,0 @@ -discard """ - output: ''' -printing from adder -''' -""" - -import std/sugar - -block: - proc makeAdder(a: int): (int) -> void = - proc discard_adder(x: int) {.closure.} = - discard a + x - - proc echo_adder(x: int) {.closure.} = - echo("printing from adder") - - if a > 0: - discard_adder - else: - echo_adder - - let newAdder = makeAdder(0) - newAdder(5) - -block: - proc makeAdder(a: int): (int) -> void = - proc discard_adder(x: int) {.closure.} = - discard a + x - - proc echo_adder(x: int) {.closure.} = - echo("printing from adder") - - if a > 0: - echo_adder - else: - discard_adder - - let newAdder = makeAdder(0) - newAdder(5) diff --git a/tests/effects/tfuncs_cannot_mutate.nim b/tests/effects/tfuncs_cannot_mutate.nim index 9934d27a7..4768af2b3 100644 --- a/tests/effects/tfuncs_cannot_mutate.nim +++ b/tests/effects/tfuncs_cannot_mutate.nim @@ -1,6 +1,9 @@ discard """ - errormsg: "cannot mutate location select(x, z).data within a strict func" - line: 35 + errormsg: "'mutate' can have side effects" + nimout: '''an object reachable from 'n' is potentially mutated +tfuncs_cannot_mutate.nim(39, 15) the mutation is here +tfuncs_cannot_mutate.nim(37, 7) is the statement that connected the mutation to the parameter''' + line: 33 """ {.experimental: "strictFuncs".} @@ -22,7 +25,8 @@ func len(n: Node): int = it = it.ri func doNotDistract(n: Node) = - var m = Node(data: "abc") + var m = Node() + m.data = "abc" func select(a, b: Node): Node = b diff --git a/tests/effects/tfuncs_cannot_mutate2.nim b/tests/effects/tfuncs_cannot_mutate2.nim deleted file mode 100644 index 86f811017..000000000 --- a/tests/effects/tfuncs_cannot_mutate2.nim +++ /dev/null @@ -1,24 +0,0 @@ -discard """ - errormsg: "cannot mutate location x[0].a within a strict func" - line: 12 -""" - -{.experimental: "strictFuncs".} - -func copy[T](x: var openArray[T]; y: openArray[T]) = - for i in 0..high(x): - x[i] = y[i] - - x[0].a = nil - -type - R = ref object - a, b: R - data: string - -proc main = - var a, b: array[3, R] - b = [R(data: "a"), R(data: "b"), R(data: "c")] - copy a, b - -main() diff --git a/tests/effects/tfuncs_cannot_mutate3.nim b/tests/effects/tfuncs_cannot_mutate3.nim deleted file mode 100644 index 029152029..000000000 --- a/tests/effects/tfuncs_cannot_mutate3.nim +++ /dev/null @@ -1,35 +0,0 @@ -discard """ - errormsg: "cannot mutate location kid.parent within a strict func" - line: 16 -""" - -{.experimental: "strictFuncs".} - -type - Node = ref object - name: string - kids: seq[Node] - parent: Node - -func initParents(tree: Node) = - for kid in tree.kids: - kid.parent = tree - initParents(kid) - -proc process(intro: Node): Node = - var tree = Node(name: "root", kids: @[ - intro, - Node(name: "one", kids: @[ - Node(name: "two"), - Node(name: "three"), - ]), - Node(name: "four"), - ]) - initParents(tree) - -proc main() = - var intro = Node(name: "intro") - var tree = process(intro) - echo intro.parent.name - -main() diff --git a/tests/effects/tfuncs_cannot_mutate_simple.nim b/tests/effects/tfuncs_cannot_mutate_simple.nim index 0ae4a0db9..9de20d1ec 100644 --- a/tests/effects/tfuncs_cannot_mutate_simple.nim +++ b/tests/effects/tfuncs_cannot_mutate_simple.nim @@ -1,6 +1,8 @@ discard """ - errormsg: '''cannot mutate location x.data within a strict func''' - line: 15 + errormsg: "'edit' can have side effects" + nimout: '''an object reachable from 'x' is potentially mutated +tfuncs_cannot_mutate_simple.nim(17, 4) the mutation is here''' + line: 16 """ {.experimental: "strictFuncs".} diff --git a/tests/effects/tgcsafe.nim b/tests/effects/tgcsafe.nim index cfac3ddd8..363624f19 100644 --- a/tests/effects/tgcsafe.nim +++ b/tests/effects/tgcsafe.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'mainUnsafe' is not GC-safe as it performs an indirect call here" + errormsg: "'mainUnsafe' is not GC-safe" line: 26 cmd: "nim $target --hints:on --threads:on $options $file" """ @@ -13,7 +13,7 @@ proc myproc(i: int) {.gcsafe.} = if isNil(global_proc): return -proc mymap(x: proc ()) {.effectsOf: x.} = +proc mymap(x: proc ()) = x() var diff --git a/tests/effects/tgcsafe2.nim b/tests/effects/tgcsafe2.nim index 6268592a9..07da4e3f8 100644 --- a/tests/effects/tgcsafe2.nim +++ b/tests/effects/tgcsafe2.nim @@ -1,5 +1,5 @@ discard """ - errormsg: '''type mismatch: got ''' + errormsg: '''type mismatch: got ''' line: 11 """ #5620 diff --git a/tests/effects/tgcsafe3.nim b/tests/effects/tgcsafe3.nim index 36ea5112c..5137efe4c 100644 --- a/tests/effects/tgcsafe3.nim +++ b/tests/effects/tgcsafe3.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'myproc' is not GC-safe as it calls 'global_proc'" + errormsg: "'myproc' is not GC-safe as it accesses 'global_proc' which is a global using GC'ed memory" line: 12 cmd: "nim $target --hints:on --threads:on $options $file" """ diff --git a/tests/effects/tlaxeffects.nim b/tests/effects/tlaxeffects.nim deleted file mode 100644 index 7eedc372a..000000000 --- a/tests/effects/tlaxeffects.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - cmd: "nim $target $options --legacy:laxEffects $file" -""" - - -type - Foo = object - bar: seq[Foo] - -proc `==`(a, b: Foo): bool = - a.bar == b.bar diff --git a/tests/effects/tnestedprocs.nim b/tests/effects/tnestedprocs.nim deleted file mode 100644 index 125896d44..000000000 --- a/tests/effects/tnestedprocs.nim +++ /dev/null @@ -1,63 +0,0 @@ -discard """ - cmd: "nim check --hints:off $file" - nimout: '''tnestedprocs.nim(27, 8) Error: 'inner' can have side effects -> tnestedprocs.nim(29, 13) Hint: 'inner' calls `.sideEffect` 'outer2' ->> tnestedprocs.nim(26, 6) Hint: 'outer2' called by 'inner' - -tnestedprocs.nim(45, 8) Error: 'inner' can have side effects -> tnestedprocs.nim(47, 13) Hint: 'inner' calls `.sideEffect` 'outer6' ->> tnestedprocs.nim(44, 6) Hint: 'outer6' called by 'inner' - -tnestedprocs.nim(58, 41) Error: type mismatch: got but expected 'proc (){.closure, noSideEffect.}' - Pragma mismatch: got '{..}', but expected '{.noSideEffect.}'. -''' - errormsg: "type mismatch: got but expected 'proc (){.closure, noSideEffect.}'" -""" -{.experimental: "strictEffects".} -proc outer {.noSideEffect.} = - proc inner(p: int) = - if p == 0: - outer() - - inner(4) - -outer() - -proc outer2 = - proc inner(p: int) {.noSideEffect.} = - if p == 0: - outer2() - - inner(4) - -outer2() - -proc outer3(p: int) {.noSideEffect.} = - proc inner(p: int) {.noSideEffect.} = - if p == 0: - p.outer3() - - inner(4) - -outer3(5) - -proc outer6 = - proc inner(p: int) {.noSideEffect.} = - if p == 0: - outer6() - - inner(4) - echo "bad" - -outer6() - - -proc outer4 = - proc inner(p: int) {.noSideEffect.} = - if p == 0: - let x: proc () {.noSideEffect.} = outer4 - x() - - inner(4) - -outer4() diff --git a/tests/effects/tnosideeffect.nim b/tests/effects/tnosideeffect.nim deleted file mode 100644 index 9fc2f74d4..000000000 --- a/tests/effects/tnosideeffect.nim +++ /dev/null @@ -1,24 +0,0 @@ -block: # `.noSideEffect` - func foo(bar: proc(): int): int {.effectsOf: bar.} = bar() - var count = 0 - proc fn1(): int = 1 - proc fn2(): int = (count.inc; count) - - template accept(body) = - doAssert compiles(block: - body) - - template reject(body) = - doAssert not compiles(block: - body) - - accept: - func fun1() = discard foo(fn1) - reject: - func fun1() = discard foo(fn2) - - var foo2: type(foo) = foo - accept: - func main() = discard foo(fn1) - reject: - func main() = discard foo2(fn1) diff --git a/tests/effects/toutparam.nim b/tests/effects/toutparam.nim index 1126aa77e..00572aa6b 100644 --- a/tests/effects/toutparam.nim +++ b/tests/effects/toutparam.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c --warningAsError:Uninit:on --skipCfg --skipParentCfg $file''' + cmd: '''nim c --warningAsError[Uninit]:on --skipCfg --skipParentCfg $file''' errormsg: "use explicit initialization of 'x' for clarity [Uninit]" line: 24 disabled: "true" diff --git a/tests/effects/tstrict_caseobjects.nim b/tests/effects/tstrict_caseobjects.nim deleted file mode 100644 index 20bc810e0..000000000 --- a/tests/effects/tstrict_caseobjects.nim +++ /dev/null @@ -1,47 +0,0 @@ -discard """ - errormsg: "field access outside of valid case branch: x.x" - line: 45 -""" - -{.experimental: "strictCaseObjects".} - -type - NodeKind = enum - nkParent, - nkChild - - Node {.acyclic.} = ref object - case kind: NodeKind - of nkParent: - children: seq[Node] - of nkChild: - name: string - -let list = @[Node(kind: nkParent, children: @[]), Node(kind: nkChild, name: "hello")] -for node in list: - case node.kind - of nkChild: - echo $node.name # here this time there is a warning - else: discard - - -type - Foo = object - case b: bool - of false: - s: string - of true: - x: int - -var x = Foo(b: true, x: 4) -case x.b -of true: - echo x.x -of false: - echo "no" - -case x.b -of false: - echo x.x -of true: - echo "no" diff --git a/tests/effects/tstrict_effects.nim b/tests/effects/tstrict_effects.nim deleted file mode 100644 index eee8fb71a..000000000 --- a/tests/effects/tstrict_effects.nim +++ /dev/null @@ -1,27 +0,0 @@ -discard """ - errormsg: "s1 can raise an unlisted exception: CatchableError" - line: 27 -""" - -{.push warningAsError[Effect]: on.} -{.experimental: "strictEffects".} - -# bug #18376 - -{.push raises: [Defect].} -type Call = proc (x: int): int {.gcsafe, raises: [Defect, CatchableError].} - -type Bar* = object - foo*: Call - -proc passOn*(x: Call) = discard - -proc barCal(b: var Bar, s: string, s1: Call) = - #compiler complains that his line can throw CatchableError - passOn s1 - - -proc passOnB*(x: Call) {.effectsOf: x.} = discard - -proc barCal2(b: var Bar, s: string, s1: Call) = - passOnB s1 diff --git a/tests/effects/tstrict_effects2.nim b/tests/effects/tstrict_effects2.nim deleted file mode 100644 index acc0a0540..000000000 --- a/tests/effects/tstrict_effects2.nim +++ /dev/null @@ -1,28 +0,0 @@ -discard """ - errormsg: "can raise an unlisted exception: Exception" - line: 23 -""" - -{.push warningAsError[Effect]: on.} -{.experimental: "strictEffects".} - -# bug #13905 - -proc atoi(v: cstring): cint {.importc: "atoi", cdecl, raises: [].} - -type Conv = proc(v: cstring): cint {.cdecl, raises: [].} - -var x: Conv = atoi - -# bug #17475 - -type - Callback = proc() - -proc f(callback: Callback) {.raises: [].} = - callback() - -proc main = - f(proc () = raise newException(IOError, "IO")) - -main() diff --git a/tests/effects/tstrict_effects3.nim b/tests/effects/tstrict_effects3.nim deleted file mode 100644 index 027b46474..000000000 --- a/tests/effects/tstrict_effects3.nim +++ /dev/null @@ -1,46 +0,0 @@ -discard """ - action: compile -""" - -{.push warningAsError[Effect]: on.} - -{.experimental: "strictEffects".} - -proc fn(a: int, p1, p2: proc()) {.effectsOf: p1.} = - if a == 7: - p1() - if a<0: - raise newException(ValueError, $a) - -proc main() {.raises: [ValueError].} = - fn(1, proc()=discard, proc() = raise newException(IOError, "foo")) -main() - -# bug #19159 - -import macros - -func mkEnter() = - template helper = - discard - when defined pass: - 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/effects/tstrict_effects_sort.nim b/tests/effects/tstrict_effects_sort.nim deleted file mode 100644 index 8928ed0d3..000000000 --- a/tests/effects/tstrict_effects_sort.nim +++ /dev/null @@ -1,27 +0,0 @@ -discard """ - errormsg: "cmpE can raise an unlisted exception: Exception" - line: 27 -""" - -{.push warningAsError[Effect]: on.} - -{.experimental: "strictEffects".} - -import algorithm - -type - MyInt = distinct int - -var toSort = @[MyInt 1, MyInt 2, MyInt 3] - -proc cmpN(a, b: MyInt): int = - cmp(a.int, b.int) - -proc harmless {.raises: [].} = - toSort.sort cmpN - -proc cmpE(a, b: MyInt): int {.raises: [Exception].} = - cmp(a.int, b.int) - -proc harmfull {.raises: [].} = - toSort.sort cmpE diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 9d20f5d7e..d30326123 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -3,6 +3,17 @@ discard """ """ import tables, streams, parsecsv +# We import the below modules to check that they compile with `strictFuncs`. +# They are otherwise unused in this file. +import + complex, + httpcore, + math, + nre, + rationals, + sequtils, + strutils, + uri type Contig2Reads = TableRef[string, seq[string]] @@ -27,20 +38,3 @@ block: var x = @[0, 1] let z = x &&& 2 - - -func copy[T](x: var openArray[T]; y: openArray[T]) = - for i in 0..high(x): - x[i] = y[i] - -type - R = ref object - a, b: R - data: string - -proc main = - var a, b: array[3, R] - b = [R(data: "a"), R(data: "b"), R(data: "c")] - copy a, b - -main() diff --git a/tests/effects/tstrict_funcs_imports.nim b/tests/effects/tstrict_funcs_imports.nim deleted file mode 100644 index 53f346236..000000000 --- a/tests/effects/tstrict_funcs_imports.nim +++ /dev/null @@ -1,178 +0,0 @@ -discard """ - cmd: "nim $target $options --hints:on --experimental:strictFuncs --experimental:views --threads:on -d:ssl -d:nimCoroutines $file" - targets: "c" -""" -{.warning[UnusedImport]: off.} - -when defined(linux): - import linenoise - -when defined(nimPreviewSlimSystem): - import std/[ - assertions, - formatfloat, - objectdollar, - syncio, - widestrs, - ] - -import - algorithm, - asyncdispatch, - asyncfile, - asyncfutures, - asynchttpserver, - asyncmacro, - asyncnet, - asyncstreams, - atomics, - base64, - bitops, - browsers, - cgi, - chains, - colors, - complex, - cookies, - coro, - cpuinfo, - cpuload, - critbits, - cstrutils, - deques, - distros, - dynlib, - encodings, - endians, - epoll, - fenv, - hashes, - heapqueue, - hotcodereloading, - htmlgen, - htmlparser, - httpclient, - httpcore, - inotify, - intsets, - json, - kqueue, - lenientops, - lexbase, - lists, - locks, - logging, - macrocache, - macros, - marshal, - math, - md5, - memfiles, - mersenne, - mimetypes, - nativesockets, - net, - nimhcr, - # nimprof, - nre, - oids, - options, - os, - osproc, - parsecfg, - parsecsv, - parsejson, - parseopt, - parsesql, - parseutils, - parsexml, - pathnorm, - pegs, - posix_utils, - prelude, - random, - rationals, - rdstdin, - re, - registry, - reservedmem, - rlocks, - ropes, - rtarrays, - selectors, - sequtils, - sets, - sharedlist, - sharedtables, - ssl_certs, - ssl_config, - stats, - streams, - streamwrapper, - strformat, - strmisc, - strscans, - strtabs, - strutils, - sugar, - tables, - terminal, - threadpool, - times, - typeinfo, - typetraits, - unicode, - unidecode, - unittest, - uri, - volatile, - winlean, - xmlparser, - xmltree - -import experimental/[ - diff, -] - -import packages/docutils/[ - highlite, - rst, - rstast, - rstgen, -] - -import std/[ - compilesettings, - decls, - editdistance, - effecttraits, - enumerate, - enumutils, - exitprocs, - isolation, - jsonutils, - logic, - monotimes, - packedsets, - setutils, - sha1, - socketstreams, - stackframes, - sums, - time_t, - varints, - with, - wordwrap, - wrapnils, -] - -import std/private/[ - asciitables, - decode_helpers, - gitutils, - globs, - miscdollars, - since, - strimpl, - underscored_calls, -] diff --git a/tests/effects/tstrict_funcs_imports_js.nim b/tests/effects/tstrict_funcs_imports_js.nim deleted file mode 100644 index 667887ff0..000000000 --- a/tests/effects/tstrict_funcs_imports_js.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - cmd: "nim $target $options --hints:on --experimental:strictFuncs --experimental:views $file" - targets: "js" -""" -{.warning[UnusedImport]: off.} - -import - asyncjs, - dom, - jsconsole, - jsffi, - jsre - -import std/[ - jsbigints, -] - -import std/private/[ - jsutils, -] diff --git a/tests/effects/tstrictfuncs_misc.nim b/tests/effects/tstrictfuncs_misc.nim deleted file mode 100644 index 8c573bb3a..000000000 --- a/tests/effects/tstrictfuncs_misc.nim +++ /dev/null @@ -1,65 +0,0 @@ -discard """ - action: compile -""" - -{.experimental: "strictFuncs".} - -func sortedFake1[T](a: openArray[T]): seq[T] = - for i in 0 .. a.high: result.add a[i] -func sortedFake2[T](a: openArray[T]): seq[T] = - result = newSeq[T](a.len) - for i in 0 .. a.high: result[i] = a[i] -type Foo1 = object -type Foo2 = ref object -block: - let a1 = sortedFake1([Foo1()]) # ok - let a2 = sortedFake1([Foo2()]) # ok -block: - let a1 = sortedFake2([Foo1()]) # ok - let a2 = sortedFake2([Foo2()]) # error: Error: 'sortedFake2' can have side effects - - -import std/sequtils -type Foob = ref object - x: int -let a1 = zip(@[1,2], @[1,2]) # ok -let a2 = zip(@[Foob(x: 1)], @[Foob(x: 2)]) # error in 1.6.0 RC2, but not 1.4.x - - -# bug #20863 -type - Fooc = ref object - -func twice(foo: Fooc) = - var a = newSeq[Fooc](2) - a[0] = foo # No error. - a[1] = foo # Error: 'twice' can have side effects. - -let foo = Fooc() -twice(foo) - -# bug #17387 -import json - -func parseColumn(columnNode: JsonNode) = - let columnName = columnNode["name"].str - -parseColumn(%*{"a": "b"}) - -type - MyTable = object - data: seq[int] - - JsonNode3 = ref object - fields: MyTable - -proc `[]`(t: MyTable, key: string): int = - result = t.data[0] - -proc `[]`(x: JsonNode3, key: string): int = - result = x.fields[key] - -func parseColumn(columnNode: JsonNode3) = - var columnName = columnNode["test"] - -parseColumn(JsonNode3()) diff --git a/tests/enum/m16462_1.nim b/tests/enum/m16462_1.nim deleted file mode 100644 index 631c63256..000000000 --- a/tests/enum/m16462_1.nim +++ /dev/null @@ -1,3 +0,0 @@ -type - Scancode* {.pure.} = enum - SCANCODE_LEFT = 80 \ No newline at end of file diff --git a/tests/enum/m16462_2.nim b/tests/enum/m16462_2.nim deleted file mode 100644 index 631c63256..000000000 --- a/tests/enum/m16462_2.nim +++ /dev/null @@ -1,3 +0,0 @@ -type - Scancode* {.pure.} = enum - SCANCODE_LEFT = 80 \ No newline at end of file diff --git a/tests/enum/mcrossmodule.nim b/tests/enum/mcrossmodule.nim deleted file mode 100644 index e534a202c..000000000 --- a/tests/enum/mcrossmodule.nim +++ /dev/null @@ -1,6 +0,0 @@ - -type - OtherEnum* = enum - Success, Failed, More - -proc some*(x: OtherEnum): bool = x == Success diff --git a/tests/enum/t16462.nim b/tests/enum/t16462.nim deleted file mode 100644 index 9f38286bb..000000000 --- a/tests/enum/t16462.nim +++ /dev/null @@ -1,5 +0,0 @@ -import m16462_1 except Scancode -import m16462_2 - -# bug #16462 -let a = SCANCODE_LEFT \ No newline at end of file diff --git a/tests/enum/tcrossmodule.nim b/tests/enum/tcrossmodule.nim deleted file mode 100644 index c21072198..000000000 --- a/tests/enum/tcrossmodule.nim +++ /dev/null @@ -1,15 +0,0 @@ -import mcrossmodule - -type - MyEnum = enum - Success - -template t = - doAssert some(Success) - -t() - -block: # legacy support for behavior before overloadableEnums - # warning: ambiguous enum field 'Success' assumed to be of type MyEnum - let x = {Success} - doAssert x is set[MyEnum] diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim index 88d85ddcc..aa338ee2c 100644 --- a/tests/enum/tenum.nim +++ b/tests/enum/tenum.nim @@ -6,7 +6,7 @@ ABCDC foo first0second32third64 my value A1my value Bconc2valueCabc4abc -my value A0my value Bconc1valueCabc3abc +my value A0my value Bconc1valueCabc3valueC ''' """ @@ -124,25 +124,25 @@ block tnamedfields: # trick the optimizer with a variable: var x = valueD echo valueA, ord(valueA), valueB, ord(valueB), valueC, valueD, ord(valueD), x - doAssert $x == $valueD, $x - doAssert $x == "abc", $x -block tfakeOptions: + +block toptions: type - TFakeOption = enum - fakeNone, fakeForceFullMake, fakeBoehmGC, fakeRefcGC, fakeRangeCheck, - fakeBoundsCheck, fakeOverflowCheck, fakeNilCheck, fakeAssert, fakeLineDir, - fakeWarns, fakeHints, fakeListCmd, fakeCompileOnly, - fakeSafeCode, # only allow safe code - fakeStyleCheck, fakeOptimizeSpeed, fakeOptimizeSize, fakeGenDynLib, - fakeGenGuiApp, fakeStackTrace + # please make sure we have under 32 options (improves code efficiency!) + TOption = enum + optNone, optForceFullMake, optBoehmGC, optRefcGC, optRangeCheck, + optBoundsCheck, optOverflowCheck, optNilCheck, optAssert, optLineDir, + optWarns, optHints, optListCmd, optCompileOnly, + optSafeCode, # only allow safe code + optStyleCheck, optOptimizeSpeed, optOptimizeSize, optGenDynLib, + optGenGuiApp, optStackTrace - TFakeOptionset = set[TFakeOption] + TOptionset = set[TOption] var - gFakeOptions: TFakeOptionset = {fakeRefcGC, fakeRangeCheck, fakeBoundsCheck, - fakeOverflowCheck, fakeAssert, fakeWarns, fakeHints, fakeLineDir, fakeStackTrace} + gOptions: TOptionset = {optRefcGC, optRangeCheck, optBoundsCheck, + optOverflowCheck, optAssert, optWarns, optHints, optLineDir, optStackTrace} compilerArgs: int gExitcode: int8 @@ -155,24 +155,11 @@ block nonzero: # bug #6959 C let slice = SomeEnum.low..SomeEnum.high -block size_one_byte: # bug #15752 +block size_one_byte: #issue 15752 type Flag = enum Disabled = 0x00 Enabled = 0xFF static: - assert 1 == sizeof(Flag) - -block: # bug #12589 - when not defined(i386): - type - OGRwkbGeometryType {.size: sizeof(cuint).} = enum - wkbPoint25D = 0x80000001.cuint, wkbLineString25D = 0x80000002, - wkbPolygon25D = 0x80000003 - - proc typ(): OGRwkbGeometryType = - return wkbPoint25D - - when not defined(gcRefc): - doAssert $typ() == "wkbPoint25D" + assert 1 == sizeof(Flag) \ No newline at end of file diff --git a/tests/enum/toverloadable_enums.nim b/tests/enum/toverloadable_enums.nim deleted file mode 100644 index 9bb551467..000000000 --- a/tests/enum/toverloadable_enums.nim +++ /dev/null @@ -1,120 +0,0 @@ -discard """ - output: '''B -0 -E2-B''' -joinable: false -""" - -{.experimental: "overloadableEnums".} - -type - E1 = enum - value1, - value2 - E2 = enum - value1, - value2 = 4 - -const - Lookuptable = [ - E1.value1: "1", - value2: "2" - ] - -when false: - const - Lookuptable: array[E1, string] = [ - value1: "1", - value2: "2" - ] - - -proc p(e: E1): int = - # test that the 'case' statement is smart enough: - case e - of value1: echo "A" - of value2: echo "B" - - -let v = p value2 # ERROR: ambiguous! -# (value2|value2) nkClosedSymChoice -> nkSym - -proc x(p: int) = discard -proc x(p: string) = discard - -proc takeCallback(param: proc(p: int)) = discard - -takeCallback x - -echo ord v - -block: # https://github.com/nim-lang/RFCs/issues/8 - type - Enum1 = enum - A, B, C - Enum2 = enum - A, Z - - proc f(e: Enum1): int = ord(e) - proc g(e: Enum2): int = ord(e) - - proc h(e: Enum1): int = ord(e) - proc h(e: Enum2): int = ord(e) - - let fA = f(A) # Type of A is well defined - let gA = g(A) # Same as above - - let hA1 = h(Enum1.A) # A requires disambiguation - let hA2 = h(Enum2.A) # Similarly - let hA3 = h(B) - let hA4 = h(B) - let x = ord(Enum1.A) # Also - doAssert fA == 0 - doAssert gA == 0 - doAssert hA1 == 0 - doAssert hA2 == 0 - doAssert x == 0 - doAssert hA3 == 1 - doAssert hA4 == 1 - -# bug #18769 -proc g3[T](x: T, e: E2): int = - case e - of value1: echo "E2-A" # Error: type mismatch: got 'E1' for 'value1' but expected 'E2 = enum' - 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/tredefinition.nim b/tests/enum/tredefinition.nim deleted file mode 100644 index 615ca6b1c..000000000 --- a/tests/enum/tredefinition.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - cmd: '''nim check --hints:off $file''' - action: reject -nimout: ''' -tredefinition.nim(9, 25) Error: redefinition of 'Key_a'; previous declaration here: tredefinition.nim(9, 18) -''' -""" - -type Key* = enum Key_A, Key_a \ No newline at end of file diff --git a/tests/errmsgs/t10251.nim b/tests/errmsgs/t10251.nim deleted file mode 100644 index 19adf02eb..000000000 --- a/tests/errmsgs/t10251.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - action:reject - cmd: "nim check $options $file" - nimout: ''' -t10251.nim(19, 23) Error: redefinition of 'goo1'; previous declaration here: t10251.nim(19, 11) -''' -""" - -# line 10 -type - Enum1 = enum - foo, bar, baz - Enum2 = enum - foo, bar, baz - - -type - Enum3 {.pure.} = enum # fixed (by accident?) in https://github.com/nim-lang/Nim/pull/18263 - goo0, goo1, goo2, goo1 diff --git a/tests/errmsgs/t10376.nim b/tests/errmsgs/t10376.nim index 814c860dc..2ce16d6a2 100644 --- a/tests/errmsgs/t10376.nim +++ b/tests/errmsgs/t10376.nim @@ -1,7 +1,6 @@ discard """ - matrix: "--mm:refc" errormsg: "finalizer must be a direct reference to a proc" - line: 30 + line: 29 """ type diff --git a/tests/errmsgs/t10489_a.nim b/tests/errmsgs/t10489_a.nim index c762ce876..71a6cc3c4 100644 --- a/tests/errmsgs/t10489_a.nim +++ b/tests/errmsgs/t10489_a.nim @@ -1,5 +1,5 @@ discard """ -errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe.}' for let. Did you mean to call the macro with '()'?" +errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe, locks: 0.}' for let. Did you mean to call the macro with '()'?" line: 9 """ diff --git a/tests/errmsgs/t10489_b.nim b/tests/errmsgs/t10489_b.nim index df05f0e6e..4b0b876e5 100644 --- a/tests/errmsgs/t10489_b.nim +++ b/tests/errmsgs/t10489_b.nim @@ -1,5 +1,5 @@ discard """ -errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe.}' for const. Did you mean to call the macro with '()'?" +errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe, locks: 0.}' for const. Did you mean to call the macro with '()'?" line: 9 """ diff --git a/tests/errmsgs/t10594.nim b/tests/errmsgs/t10594.nim index d27dd6433..c9506c542 100644 --- a/tests/errmsgs/t10594.nim +++ b/tests/errmsgs/t10594.nim @@ -3,5 +3,5 @@ discard """ line: 7 """ -template foo(v: varargs[int]) = addr v +template foo(v: varargs[int]) = unsafeAddr v foo(1, 2) diff --git a/tests/errmsgs/t10734.nim b/tests/errmsgs/t10734.nim deleted file mode 100644 index 4e73db7cd..000000000 --- a/tests/errmsgs/t10734.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - cmd: "nim check $file" - errormsg: "" - nimout: ''' -t10734.nim(19, 1) Error: invalid indentation -t10734.nim(19, 6) Error: invalid indentation -t10734.nim(20, 7) Error: expression expected, but found '[EOF]' -t10734.nim(18, 5) Error: 'proc' is not a concrete type; for a callback without parameters use 'proc()' -t10734.nim(19, 6) Error: undeclared identifier: 'p' -t10734.nim(19, 6) Error: expression 'p' has no type (or is ambiguous) -t10734.nim(19, 6) Error: 'p' cannot be assigned to -t10734.nim(17, 3) Hint: 'T' is declared but not used [XDeclaredButNotUsed] -''' -""" - -type - T = object - a: -proc p = - case \ No newline at end of file diff --git a/tests/errmsgs/t10735.nim b/tests/errmsgs/t10735.nim deleted file mode 100644 index 307acac2d..000000000 --- a/tests/errmsgs/t10735.nim +++ /dev/null @@ -1,41 +0,0 @@ -discard """ - cmd: "nim check $file" - errormsg: "selector must be of an ordinal type, float or string" - nimout: ''' -t10735.nim(38, 5) Error: 'let' symbol requires an initialization -t10735.nim(39, 10) Error: undeclared identifier: 'pos' -t10735.nim(39, 9) Error: type mismatch: got -but expected one of: -proc `[]`(s: string; i: BackwardsIndex): char - first type mismatch at position: 0 -proc `[]`(s: var string; i: BackwardsIndex): var char - first type mismatch at position: 0 -proc `[]`[I: Ordinal; T](a: T; i: I): T - first type mismatch at position: 0 -proc `[]`[Idx, T; U, V: Ordinal](a: array[Idx, T]; x: HSlice[U, V]): seq[T] - first type mismatch at position: 0 -proc `[]`[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T - first type mismatch at position: 0 -proc `[]`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T - first type mismatch at position: 0 -proc `[]`[T, U: Ordinal](s: string; x: HSlice[T, U]): string - first type mismatch at position: 0 -proc `[]`[T; U, V: Ordinal](s: openArray[T]; x: HSlice[U, V]): seq[T] - first type mismatch at position: 0 -proc `[]`[T](s: openArray[T]; i: BackwardsIndex): T - first type mismatch at position: 0 -proc `[]`[T](s: var openArray[T]; i: BackwardsIndex): var T - first type mismatch at position: 0 -template `[]`(s: string; i: int): char - first type mismatch at position: 0 - -expression: `[]`(buf, pos) -t10735.nim(39, 9) Error: selector must be of an ordinal type, float or string -''' - joinable: false -""" - -let buf: cstring -case buf[pos] -else: - case buf[pos] diff --git a/tests/errmsgs/t14444.nim b/tests/errmsgs/t14444.nim deleted file mode 100644 index 143b4542e..000000000 --- a/tests/errmsgs/t14444.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - matrix: "--hints:off" - exitcode: "1" - output: ''' -t14444.nim(13) t14444 -fatal.nim(51) sysFatal -Error: unhandled exception: index out of bounds, the container is empty [IndexDefect] -''' -""" - -when true: # bug #14444 - var i: string - i[10] = 'j' - echo i \ No newline at end of file diff --git a/tests/errmsgs/t16654.nim b/tests/errmsgs/t16654.nim deleted file mode 100644 index 749707c06..000000000 --- a/tests/errmsgs/t16654.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ - cmd: "nim check $options $file" - errormsg: "type mismatch: got but expected 'float'" -""" - -when true: # bug #16654 - func fn[T](a: T, op: proc(a: T): float) = discard - proc main() = - let v = 1 - proc bar(r: auto): auto = v - fn(1, bar) - main() diff --git a/tests/errmsgs/t17460.nim b/tests/errmsgs/t17460.nim deleted file mode 100644 index e377bc48a..000000000 --- a/tests/errmsgs/t17460.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - cmd: "nim check $options $file" - errormsg: "wrong number of variables" -""" - -iterator xclusters*[T](a: openArray[T]; s: static[int]): array[s, T] {.inline.} = - var result: array[s, T] # iterators have no default result variable - var i = 0 - while i < len(a): - for j, x in mpairs(result): - x = a[(i + j) mod len(a)] - yield result - inc(i) - -proc m = - for (i, j, k) in xclusters([1, 2, 3, 4, 5], 3): - echo i, j, k - -m() \ No newline at end of file diff --git a/tests/errmsgs/t18327.nim b/tests/errmsgs/t18327.nim deleted file mode 100644 index 686a1bd0c..000000000 --- a/tests/errmsgs/t18327.nim +++ /dev/null @@ -1,5 +0,0 @@ -discard """ - errormsg: "parameter 'n' requires a type" -""" - -proc fn3(n) = discard \ No newline at end of file diff --git a/tests/errmsgs/t18886.nim b/tests/errmsgs/t18886.nim deleted file mode 100644 index 8ed160c64..000000000 --- a/tests/errmsgs/t18886.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - cmd: "nim check --hints:off $file" - errormsg: "" - nimout: ''' -t18886.nim(18, 24) Error: ambiguous identifier: 'bar' -- you need a helper proc to disambiguate the following: - t18886.bar: proc (i: ptr int){.noSideEffect, gcsafe.} - t18886.bar: proc (i: ptr char){.noSideEffect, gcsafe.} -''' -""" - -type Foo = (proc(_: pointer): void) - - -proc bar(i: ptr[int]) = discard -proc bar(i: ptr[char]) = discard - - -let fooBar = cast[Foo](bar) \ No newline at end of file diff --git a/tests/errmsgs/t18983.nim b/tests/errmsgs/t18983.nim deleted file mode 100644 index 3451875cb..000000000 --- a/tests/errmsgs/t18983.nim +++ /dev/null @@ -1,7 +0,0 @@ -discard """ - errormsg: "illegal recursion in type 'A'" -""" - -type - A* = A - B = (A,) diff --git a/tests/errmsgs/t19224.nim b/tests/errmsgs/t19224.nim deleted file mode 100644 index 7a9ecb2e7..000000000 --- a/tests/errmsgs/t19224.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ -cmd: "nim check --hints:off $file" -errormsg: "" -nimout: ''' -t19224.nim(10, 10) Error: cannot infer element type of items([]) -t19224.nim(12, 10) Error: cannot infer element type of items(@[]) -''' -""" - -for _ in []: discard - -for _ in @[]: discard diff --git a/tests/errmsgs/t19882.nim b/tests/errmsgs/t19882.nim deleted file mode 100644 index 1f2f95ab7..000000000 --- a/tests/errmsgs/t19882.nim +++ /dev/null @@ -1,10 +0,0 @@ - -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 deleted file mode 100644 index 7f3055a5d..000000000 --- a/tests/errmsgs/t19882_2.nim +++ /dev/null @@ -1,5 +0,0 @@ -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/t21257.nim b/tests/errmsgs/t21257.nim deleted file mode 100644 index eecdb1dfe..000000000 --- a/tests/errmsgs/t21257.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - action: compile - cmd: "nim check $file" -""" - -type AC_WINCTRL_Fields* = distinct uint8 - -type AC_STATUSA_WSTATE0* {.pure.} = enum - ABOVE = 0x0, - INSIDE = 0x1, - BELOW = 0x2, - -type AC_WINCTRL_WINTSEL0* {.pure.} = enum - ABOVE = 0x0, - INSIDE = 0x1, - BELOW = 0x2, - OUTSIDE = 0x3, - -proc write*(WINTSEL0: AC_WINCTRL_WINTSEL0 = ABOVE) = - discard diff --git a/tests/errmsgs/t2614.nim b/tests/errmsgs/t2614.nim deleted file mode 100644 index 031ecb9d1..000000000 --- a/tests/errmsgs/t2614.nim +++ /dev/null @@ -1,21 +0,0 @@ -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/t5167_4.nim b/tests/errmsgs/t5167_4.nim index dafd7754d..7a263622b 100644 --- a/tests/errmsgs/t5167_4.nim +++ b/tests/errmsgs/t5167_4.nim @@ -1,5 +1,5 @@ discard """ -errormsg: "type mismatch: got " +errormsg: "type mismatch: got " line: 19 """ diff --git a/tests/errmsgs/t5167_5.nim b/tests/errmsgs/t5167_5.nim index ab6f094f3..a9e260845 100644 --- a/tests/errmsgs/t5167_5.nim +++ b/tests/errmsgs/t5167_5.nim @@ -1,5 +1,5 @@ discard """ -cmd: "nim check --mm:refc $file" +cmd: "nim check $file" errormsg: "'t' has unspecified generic parameters" nimout: ''' t5167_5.nim(10, 16) Error: expression 'system' has no type (or is ambiguous) diff --git a/tests/errmsgs/t5282.nim b/tests/errmsgs/t5282.nim deleted file mode 100644 index 4da49d155..000000000 --- a/tests/errmsgs/t5282.nim +++ /dev/null @@ -1,5 +0,0 @@ -discard """ - errormsg: "illegal recursion in type 'x'" -""" - -type x = distinct x diff --git a/tests/errmsgs/t6608.nim b/tests/errmsgs/t6608.nim index 88cbf42fd..3d82f3cd8 100644 --- a/tests/errmsgs/t6608.nim +++ b/tests/errmsgs/t6608.nim @@ -1,9 +1,10 @@ discard """ cmd: "nim c --hints:off $file" errormsg: "type mismatch: got <>" - nimout: '''t6608.nim(13, 4) Error: type mismatch: got <> + nimout: '''t6608.nim(14, 4) Error: type mismatch: got <> but expected one of: AcceptCB = proc (s: string){.closure.}''' + line: 14 """ type diff --git a/tests/errmsgs/t8064.nim b/tests/errmsgs/t8064.nim deleted file mode 100644 index e7941e36a..000000000 --- a/tests/errmsgs/t8064.nim +++ /dev/null @@ -1,8 +0,0 @@ -import tables - -values - - -discard """ - errormsg: "expression has no type: values" -""" diff --git a/tests/errmsgs/t8610.nim b/tests/errmsgs/t8610.nim index ec99beae5..d3405bc91 100644 --- a/tests/errmsgs/t8610.nim +++ b/tests/errmsgs/t8610.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "invalid type: 'typedesc[int]' for const" + errormsg: "invalid type: 'type int' for const" """ -## bug #8610 +## issue #8610 const Foo = int diff --git a/tests/errmsgs/t9768.nim b/tests/errmsgs/t9768.nim index b5ff58367..b72a158c7 100644 --- a/tests/errmsgs/t9768.nim +++ b/tests/errmsgs/t9768.nim @@ -1,14 +1,13 @@ discard """ - errormsg: "unhandled exception: t9768.nim(24, 3) `a < 4` [AssertionDefect]" - file: "std/assertions.nim" - matrix: "-d:nimPreviewSlimSystem" + errormsg: "unhandled exception:" + file: "system/fatal.nim" nimout: ''' stack trace: (most recent call last) -t9768.nim(29, 33) main -t9768.nim(24, 11) foo1 +t9768.nim(28, 33) main +t9768.nim(23, 11) foo1 ''' """ -import std/assertions + diff --git a/tests/errmsgs/t9908_01.nim b/tests/errmsgs/t9908_01.nim index 99bc8237d..b9d37b67b 100644 --- a/tests/errmsgs/t9908_01.nim +++ b/tests/errmsgs/t9908_01.nim @@ -1,5 +1,5 @@ discard """ -errormsg: "ordinal type expected; given: string" +errormsg: "ordinal type expected" line: 10 """ diff --git a/tests/errmsgs/t9908_02.nim b/tests/errmsgs/t9908_02.nim index 4fc60b3df..7ff3d1ff7 100644 --- a/tests/errmsgs/t9908_02.nim +++ b/tests/errmsgs/t9908_02.nim @@ -1,5 +1,5 @@ discard """ -errormsg: "ordinal type expected; given: float" +errormsg: "ordinal type expected" line: 10 """ diff --git a/tests/errmsgs/tcall_with_default_arg.nim b/tests/errmsgs/tcall_with_default_arg.nim index 44752f1ee..1cc86638f 100644 --- a/tests/errmsgs/tcall_with_default_arg.nim +++ b/tests/errmsgs/tcall_with_default_arg.nim @@ -1,5 +1,5 @@ discard """ -outputsub: '''tcall_with_default_arg.nim(8) fail''' +outputsub: '''tcall_with_default_arg.nim(16) anotherFoo''' exitcode: 1 """ # issue: #5604 diff --git a/tests/errmsgs/tcannot_capture_builtin.nim b/tests/errmsgs/tcannot_capture_builtin.nim index 65afa627e..3b8aae241 100644 --- a/tests/errmsgs/tcannot_capture_builtin.nim +++ b/tests/errmsgs/tcannot_capture_builtin.nim @@ -1,5 +1,5 @@ discard """ -errormsg: "'+' is a built-in and cannot be used as a first-class procedure" +errormsg: "'+' cannot be passed to a procvar" line: 8 """ diff --git a/tests/errmsgs/tcase_stmt.nim b/tests/errmsgs/tcase_stmt.nim deleted file mode 100644 index cf63b9c17..000000000 --- a/tests/errmsgs/tcase_stmt.nim +++ /dev/null @@ -1,29 +0,0 @@ -discard """ - cmd: "nim check --hints:off $file" - errormsg: "" - nimout: ''' -tcase_stmt.nim(22, 7) Error: selector must be of an ordinal type, float or string -tcase_stmt.nim(28, 6) Error: selector must be of an ordinal type, float or string - - - - - - - -''' -""" - - - -# bug #19682 -type A = object - -case A() -else: - discard - -# bug #20283 - -case @[] -else: discard diff --git a/tests/errmsgs/tconceptconstraint.nim b/tests/errmsgs/tconceptconstraint.nim index 066ec5bdb..9ab1708c7 100644 --- a/tests/errmsgs/tconceptconstraint.nim +++ b/tests/errmsgs/tconceptconstraint.nim @@ -1,7 +1,8 @@ discard """ errormsg: "cannot instantiate B" + line: 20 nimout: ''' -got: +got: but expected: ''' """ diff --git a/tests/errmsgs/tconcisetypemismatch.nim b/tests/errmsgs/tconcisetypemismatch.nim deleted file mode 100644 index 4227644ce..000000000 --- a/tests/errmsgs/tconcisetypemismatch.nim +++ /dev/null @@ -1,23 +0,0 @@ -discard """ - cmd: "nim c --hints:off --skipParentCfg $file" - errormsg: "type mismatch" - nimout: ''' -tconcisetypemismatch.nim(23, 47) Error: type mismatch -Expression: int(inNanoseconds(t2 - t1)) / 100.5 - [1] int(inNanoseconds(t2 - t1)): int - [2] 100.5: float64 - -Expected one of (first mismatch at position [#]): -[1] proc `/`(x, y: float): float -[1] proc `/`(x, y: float32): float32 -[2] proc `/`(x, y: int): float -''' -""" - -import std/monotimes -from times import inNanoseconds - -let t1 = getMonotime() -let result = 1 + 2 -let t2 = getMonotime() -echo "Elapsed: ", (t2 - t1).inNanoseconds.int / 100.5 \ No newline at end of file diff --git a/tests/errmsgs/tconcisetypemismatch.nims b/tests/errmsgs/tconcisetypemismatch.nims deleted file mode 100644 index e9dce8147..000000000 --- a/tests/errmsgs/tconcisetypemismatch.nims +++ /dev/null @@ -1,21 +0,0 @@ -switch("path", "$lib/../testament/lib") - # so we can `import stdtest/foo` inside tests - # Using $lib/../ instead of $nim/ so you can use a different nim to run tests - # during local testing, e.g. nim --lib:lib. - -## prevent common user config settings to interfere with testament expectations -## Indifidual tests can override this if needed to test for these options. -switch("colors", "off") - -switch("excessiveStackTrace", "off") - -when (NimMajor, NimMinor, NimPatch) >= (1,5,1): - # to make it easier to test against older nim versions, (best effort only) - switch("filenames", "legacyRelProj") - switch("spellSuggest", "0") - -# for std/unittest -switch("define", "nimUnittestOutputLevel:PRINT_FAILURES") -switch("define", "nimUnittestColor:off") - -hint("Processing", off) diff --git a/tests/errmsgs/tconstinfo.nim b/tests/errmsgs/tconstinfo.nim deleted file mode 100644 index 170972b9f..000000000 --- a/tests/errmsgs/tconstinfo.nim +++ /dev/null @@ -1,7 +0,0 @@ -# https://forum.nim-lang.org/t/9762 - -const foo = "abc" -case 'a' -of foo: echo "should error" #[tt.Error - ^ type mismatch: got but expected 'char']# -else: discard diff --git a/tests/errmsgs/tdeclaredlocs.nim b/tests/errmsgs/tdeclaredlocs.nim deleted file mode 100644 index 926ebf217..000000000 --- a/tests/errmsgs/tdeclaredlocs.nim +++ /dev/null @@ -1,92 +0,0 @@ -discard """ - action: reject - matrix: "--declaredLocs --hints:off" - nimoutFull: true - nimout: ''' -tdeclaredlocs.nim(92, 3) Error: type mismatch: got -but expected one of: -proc fn(a: Bam) [proc declared in tdeclaredlocs.nim(86, 6)] - first type mismatch at position: 1 - required type for a: Bam [object declared in tdeclaredlocs.nim(78, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] -proc fn(a: Goo[MyInt2]) [proc declared in tdeclaredlocs.nim(89, 6)] - first type mismatch at position: 1 - required type for a: Goo[MyInt2{char}] [object declared in tdeclaredlocs.nim(79, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] -proc fn(a: Goo[cint]) [proc declared in tdeclaredlocs.nim(88, 6)] - first type mismatch at position: 1 - required type for a: Goo[cint{int32}] [object declared in tdeclaredlocs.nim(79, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] -proc fn(a: array[3, Bar]) [proc declared in tdeclaredlocs.nim(82, 6)] - first type mismatch at position: 1 - required type for a: array[0..2, Bar] [object declared in tdeclaredlocs.nim(74, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] -proc fn(a: seq[Bar]) [proc declared in tdeclaredlocs.nim(81, 6)] - first type mismatch at position: 1 - required type for a: seq[Bar] [object declared in tdeclaredlocs.nim(74, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] -proc fn(a: seq[MyInt1]) [proc declared in tdeclaredlocs.nim(80, 6)] - first type mismatch at position: 1 - required type for a: seq[MyInt1{int}] [int declared in tdeclaredlocs.nim(72, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] -proc fn(a: set[Baz]) [proc declared in tdeclaredlocs.nim(84, 6)] - first type mismatch at position: 1 - required type for a: set[Baz{enum}] [enum declared in tdeclaredlocs.nim(75, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] -proc fn(a: set[MyInt2]) [proc declared in tdeclaredlocs.nim(83, 6)] - first type mismatch at position: 1 - required type for a: set[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] -proc fn(a: var SetBaz) [proc declared in tdeclaredlocs.nim(85, 6)] - first type mismatch at position: 1 - required type for a: var SetBaz [enum declared in tdeclaredlocs.nim(75, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] -proc fn(a: var ref ptr Bam) [proc declared in tdeclaredlocs.nim(87, 6)] - first type mismatch at position: 1 - required type for a: var ref ptr Bam [object declared in tdeclaredlocs.nim(78, 3)] - but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] - -expression: fn(a) -''' -""" - -#[ -see also: tests/errmsgs/tsigmatch.nim -]# - - - - - - - - - - - - - - -# line 70 -type - MyInt1 = int - MyInt2 = char - Bar = object - Baz = enum k0, k1 - Baz2 = Baz - SetBaz = set[Baz2] - Bam = ref object - Goo[T] = object -proc fn(a: seq[MyInt1]) = discard -proc fn(a: seq[Bar]) = discard -proc fn(a: array[3, Bar]) = discard -proc fn(a: set[MyInt2]) = discard -proc fn(a: set[Baz]) = discard -proc fn(a: var SetBaz) = discard -proc fn(a: Bam) = discard -proc fn(a: var ref ptr Bam) = discard -proc fn(a: Goo[cint]) = discard -proc fn(a: Goo[MyInt2]) = discard - -var a: seq[MyInt2] -fn(a) diff --git a/tests/errmsgs/tdistinct_nil.nim b/tests/errmsgs/tdistinct_nil.nim deleted file mode 100644 index a2b403293..000000000 --- a/tests/errmsgs/tdistinct_nil.nim +++ /dev/null @@ -1,23 +0,0 @@ -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/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim index 66496d364..77515b74f 100644 --- a/tests/errmsgs/tgcsafety.nim +++ b/tests/errmsgs/tgcsafety.nim @@ -1,16 +1,15 @@ discard """ cmd: "nim check $file" -errormsg: "type mismatch: got " +errormsg: "type mismatch: got .}>" nimout: ''' -tgcsafety.nim(31, 18) Error: type mismatch: got +type mismatch: got .}> but expected one of: proc serve(server: AsyncHttpServer; port: Port; callback: proc (request: Request): Future[void] {.closure, gcsafe.}; - address = ""; assumedDescriptorsPerRequest = -1; domain = AF_INET): owned( - Future[void]) + address = ""; assumedDescriptorsPerRequest = -1): owned(Future[void]) first type mismatch at position: 3 required type for callback: proc (request: Request): Future[system.void]{.closure, gcsafe.} - but expression 'cb' is of type: proc (req: Request): Future[system.void] + but expression 'cb' is of type: proc (req: Request): Future[system.void]{.locks: .} This expression is not GC-safe. Annotate the proc with {.gcsafe.} to get extended error information. expression: serve(server, Port(7898), cb) diff --git a/tests/errmsgs/tgenericconstraint.nim b/tests/errmsgs/tgenericconstraint.nim index b51fb3488..e3093fead 100644 --- a/tests/errmsgs/tgenericconstraint.nim +++ b/tests/errmsgs/tgenericconstraint.nim @@ -1,7 +1,8 @@ discard """ errormsg: "cannot instantiate B" + line: 14 nimout: ''' -got: +got: but expected: ''' """ diff --git a/tests/errmsgs/tinteger_literals.nim b/tests/errmsgs/tinteger_literals.nim index f90ab14a1..98c92a227 100644 --- a/tests/errmsgs/tinteger_literals.nim +++ b/tests/errmsgs/tinteger_literals.nim @@ -1,14 +1,15 @@ discard """ - cmd: "nim check $file" +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' +''' """ -# 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' ]# + +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 diff --git a/tests/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim index 268bcf4d5..1fa3805ee 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[in T] {.header: "foo.h", importcpp.} = object + Foo {.header: "foo.h", importcpp.} [in T] = object Bar[out X] = object x: int diff --git a/tests/errmsgs/tnon_concrete_cast.nim b/tests/errmsgs/tnon_concrete_cast.nim index 9e05c736c..e4ae890ce 100644 --- a/tests/errmsgs/tnon_concrete_cast.nim +++ b/tests/errmsgs/tnon_concrete_cast.nim @@ -34,7 +34,7 @@ proc write(rw: var MyReadWrite; value: SomeNumber): void = proc write[T](rw: var MyReadWrite; value: seq[T]): void = rw.write value.len let dst = cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + uint(rw.offset)) - let src = cast[pointer](value[0].addr) + let src = cast[pointer](value[0].unsafeAddr) let size = sizeof(T) * value.len copyMem(dst, src, size) rw.offset += size diff --git a/tests/errmsgs/tproc_mismatch.nim b/tests/errmsgs/tproc_mismatch.nim deleted file mode 100644 index 16f319f3b..000000000 --- a/tests/errmsgs/tproc_mismatch.nim +++ /dev/null @@ -1,74 +0,0 @@ -discard """ - action: reject - cmd: '''nim check --hints:off $options $file''' - nimoutFull: true - nimout: ''' -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(42, 6) Error: type mismatch: got -but expected one of: -proc bar(a: proc ()) - first type mismatch at position: 1 - required type for a: proc (){.closure.} - but expression 'fn1' is of type: proc (){.inline, noSideEffect, gcsafe.} - Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'. - -expression: bar(fn1) -tproc_mismatch.nim(46, 8) Error: type mismatch: got but expected 'proc (){.closure.}' - Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'. -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(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(72, 9) Error: type mismatch: got but expected 'proc (a: int): int{.cdecl.}' - Calling convention mismatch: got '{.nimcall.}', but expected '{.cdecl.}'. -tproc_mismatch.nim(73, 9) Error: type mismatch: got but expected 'proc (a: int): int{.nimcall.}' - Calling convention mismatch: got '{.cdecl.}', but expected '{.nimcall.}'. -''' -""" - - - -block: # CallConv mismatch - func a(a: int, c: float) {.cdecl.} = discard - var b: proc(a: int, c: float) {.noSideEffect.} = a -block: # Parameter CallConv mismatch - proc fn1() {.inline.} = discard - proc bar(a: proc()) = discard - bar(fn1) -block: # CallConv mismatch - proc fn1() {.inline.} = discard - var fn: proc() - fn = fn1 -block: # Pragma mismatch - var a = "" - proc fn1() = a.add "b" - var fn: proc() {.noSideEffect.} - fn = fn1 -block: # Fail match not do to Pragma or CallConv - proc fn1(a: int) = discard - var fn: proc(a: float) - fn = fn1 -block: # Infered noSideEffect assign - type Foo = ref object - x0: int - var g0 = Foo(x0: 1) - proc fn1(a: int) = g0.x0 = a - var fn2: proc(a: int) - var fn3: proc(a: int) {.gcsafe.} - fn2 = fn1 - fn3 = fn1 -block: # Indrection through pragmas - {.pragma: inl1, inline.} - {.pragma: inl2, inline.} - {.pragma: p1, nimcall.} - {.pragma: p2, cdecl.} - var fn1: proc(a: int): int {.inl1, p1.} - var fn2: proc(a: int): int {.inl2, p2.} - fn2 = fn1 - fn1 = fn2 - diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim index b0a008840..c7dfbaf2a 100644 --- a/tests/errmsgs/tproper_stacktrace.nim +++ b/tests/errmsgs/tproper_stacktrace.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--stackTrace:on --hint:all:off --warnings:off" output: '''ok''' """ import strscans, strutils @@ -7,7 +6,7 @@ import strscans, strutils proc raiseTestException*() = raise newException(Exception, "test") -proc matchStackTrace(actualEntries: openArray[StackTraceEntry], expected: string) = +proc matchStackTrace(actualEntries: openarray[StackTraceEntry], expected: string) = var expectedEntries = newSeq[StackTraceEntry]() var i = 0 @@ -77,10 +76,10 @@ when true: bar() const expectedStackTrace = """ - tproper_stacktrace.nim(87) tproper_stacktrace - tproper_stacktrace.nim(77) foo - tproper_stacktrace.nim(74) bar - tproper_stacktrace.nim(8) raiseTestException + tproper_stacktrace.nim(86) tproper_stacktrace + tproper_stacktrace.nim(76) foo + tproper_stacktrace.nim(73) bar + tproper_stacktrace.nim(7) raiseTestException """ verifyStackTrace expectedStackTrace: @@ -94,9 +93,9 @@ when true: bar(x) const expectedStackTrace = """ - tproper_stacktrace.nim(104) tproper_stacktrace - tproper_stacktrace.nim(91) bar - tproper_stacktrace.nim(8) raiseTestException + tproper_stacktrace.nim(103) tproper_stacktrace + tproper_stacktrace.nim(90) bar + tproper_stacktrace.nim(7) raiseTestException """ verifyStackTrace expectedStackTrace: @@ -111,10 +110,10 @@ when true: bar() const expectedStackTrace = """ - tproper_stacktrace.nim(121) tproper_stacktrace - tproper_stacktrace.nim(111) foo - tproper_stacktrace.nim(108) bar - tproper_stacktrace.nim(8) raiseTestException + tproper_stacktrace.nim(120) tproper_stacktrace + tproper_stacktrace.nim(110) foo + tproper_stacktrace.nim(107) bar + tproper_stacktrace.nim(7) raiseTestException """ verifyStackTrace expectedStackTrace: @@ -130,10 +129,10 @@ when true: bar() const expectedStackTrace = """ - tproper_stacktrace.nim(140) tproper_stacktrace - tproper_stacktrace.nim(130) foo - tproper_stacktrace.nim(126) baz - tproper_stacktrace.nim(8) raiseTestException + tproper_stacktrace.nim(139) tproper_stacktrace + tproper_stacktrace.nim(129) foo + tproper_stacktrace.nim(125) baz + tproper_stacktrace.nim(7) raiseTestException """ verifyStackTrace expectedStackTrace: diff --git a/tests/errmsgs/tproper_stacktrace2.nim b/tests/errmsgs/tproper_stacktrace2.nim index 5a6ca3fcb..44b208c87 100644 --- a/tests/errmsgs/tproper_stacktrace2.nim +++ b/tests/errmsgs/tproper_stacktrace2.nim @@ -1,6 +1,5 @@ discard """ - matrix: "--stackTrace:on" - outputsub: '''tproper_stacktrace2.nim(21) main''' + outputsub: '''tproper_stacktrace2.nim(20) main''' exitcode: 1 """ diff --git a/tests/errmsgs/tproper_stacktrace3.nim b/tests/errmsgs/tproper_stacktrace3.nim index 97d63e6ec..c292fa092 100644 --- a/tests/errmsgs/tproper_stacktrace3.nim +++ b/tests/errmsgs/tproper_stacktrace3.nim @@ -1,6 +1,5 @@ discard """ - matrix: "--stackTrace:on" - outputsub: '''tproper_stacktrace3.nim(22) main''' + outputsub: '''tproper_stacktrace3.nim(21) main''' exitcode: 1 """ diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim index 46afe163d..b339e06bf 100644 --- a/tests/errmsgs/treportunused.nim +++ b/tests/errmsgs/treportunused.nim @@ -1,28 +1,16 @@ discard """ - matrix: "--hint:all:off --hint:XDeclaredButNotUsed" - nimoutFull: true nimout: ''' -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] +treportunused.nim(19, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(26, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(22, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(25, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(24, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(23, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(20, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(29, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(27, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(21, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(28, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] ''' action: compile """ @@ -39,27 +27,3 @@ let s8 = 0 var s9: int type s10 = object type s11 = type(1.2) - -# bug #14407 (requires `compiler/nim.cfg` containing define:nimPreviewFloatRoundtrip) -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/errmsgs/tsigmatch.nim b/tests/errmsgs/tsigmatch.nim index 85ed34169..4f40be2d4 100644 --- a/tests/errmsgs/tsigmatch.nim +++ b/tests/errmsgs/tsigmatch.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim check --mm:refc --showAllMismatches:on --hints:off $file" + cmd: "nim check --showAllMismatches:on --hints:off $file" nimout: ''' tsigmatch.nim(111, 4) Error: type mismatch: got but expected one of: @@ -12,34 +12,34 @@ proc f(b: B) but expression 'A()' is of type: A expression: f(A(), "extra") -tsigmatch.nim(125, 6) Error: type mismatch: got <(string, proc (){.gcsafe.})> +tsigmatch.nim(125, 6) Error: type mismatch: got <(string, proc (){.gcsafe, locks: 0.})> but expected one of: proc foo(x: (string, proc ())) first type mismatch at position: 1 required type for x: (string, proc (){.closure.}) - but expression '("foobar", proc () = echo(["Hello!"]))' is of type: (string, proc (){.gcsafe.}) + but expression '("foobar", proc () = echo(["Hello!"]))' is of type: (string, proc (){.gcsafe, locks: 0.}) expression: foo(("foobar", proc () = echo(["Hello!"]))) -tsigmatch.nim(132, 11) Error: type mismatch: got +tsigmatch.nim(132, 11) Error: type mismatch: got but expected one of: proc foo[T, S](op: proc (x: T): S {.cdecl.}): auto first type mismatch at position: 1 required type for op: proc (x: T): S{.cdecl.} - but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe.} + but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe, locks: 0.} proc foo[T, S](op: proc (x: T): S {.safecall.}): auto first type mismatch at position: 1 required type for op: proc (x: T): S{.safecall.} - but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe.} + but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe, locks: 0.} expression: foo(fun) -tsigmatch.nim(143, 13) Error: type mismatch: got +tsigmatch.nim(143, 13) Error: type mismatch: got but expected one of: -proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe.}]) +proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe, locks: 0.}]) first type mismatch at position: 1 - required type for fs: openArray[proc (x: int){.closure, gcsafe.}] - but expression '[proc (x: int) {.gcsafe.} = echo [x]]' is of type: array[0..0, proc (x: int){.gcsafe.}] + required type for fs: openArray[proc (x: int){.closure, gcsafe, locks: 0.}] + but expression '[proc (x: int) {.gcsafe, locks: 0.} = echo [x]]' is of type: array[0..0, proc (x: int){.gcsafe, locks: 0.}] -expression: takesFuncs([proc (x: int) {.gcsafe.} = echo [x]]) +expression: takesFuncs([proc (x: int) {.gcsafe, locks: 0.} = echo [x]]) tsigmatch.nim(149, 4) Error: type mismatch: got but expected one of: proc f(a0: uint8; b: string) @@ -89,9 +89,9 @@ expression: fun1(default(Mystring), "asdf") -#[ -see also: tests/errmsgs/tdeclaredlocs.nim -]# + + + @@ -132,15 +132,15 @@ block: echo foo(fun) block: - # bug #10285 Function signature don't match when inside seq/array/openArray + # bug #10285 Function signature don't match when inside seq/array/openarray # Note: the error message now shows `closure` which helps debugging the issue # out why it doesn't match - proc takesFunc(f: proc (x: int) {.gcsafe.}) = + proc takesFunc(f: proc (x: int) {.gcsafe, locks: 0.}) = echo "takes single Func" - proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe.}]) = + proc takesFuncs(fs: openarray[proc (x: int) {.gcsafe, locks: 0.}]) = echo "takes multiple Func" - takesFunc(proc (x: int) {.gcsafe.} = echo x) # works - takesFuncs([proc (x: int) {.gcsafe.} = echo x]) # fails + takesFunc(proc (x: int) {.gcsafe, locks: 0.} = echo x) # works + takesFuncs([proc (x: int) {.gcsafe, locks: 0.} = echo x]) # fails block: # bug https://github.com/nim-lang/Nim/issues/11061#issuecomment-508970465 diff --git a/tests/errmsgs/tsigmatch2.nim b/tests/errmsgs/tsigmatch2.nim index 31c966337..4996634c9 100644 --- a/tests/errmsgs/tsigmatch2.nim +++ b/tests/errmsgs/tsigmatch2.nim @@ -14,7 +14,7 @@ proc foo(i: Foo): string expression: foo(1.2) tsigmatch2.nim(40, 14) Error: expression '' has no type (or is ambiguous) -tsigmatch2.nim(46, 3) Error: type mismatch: got +tsigmatch2.nim(46, 7) Error: type mismatch: got but expected one of: proc foo(args: varargs[string, myproc]) first type mismatch at position: 1 @@ -44,4 +44,4 @@ block: let temp = 12.isNil proc foo(args: varargs[string, myproc]) = discard foo 1 -static: echo "done" +static: echo "done" \ No newline at end of file diff --git a/tests/errmsgs/ttypeAllowed.nim b/tests/errmsgs/ttypeAllowed.nim index fdb4c70b8..516b616e0 100644 --- a/tests/errmsgs/ttypeAllowed.nim +++ b/tests/errmsgs/ttypeAllowed.nim @@ -2,10 +2,10 @@ discard """ cmd: "nim check $file" errormsg: "" nimout: ''' -ttypeAllowed.nim(13, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for let -ttypeAllowed.nim(17, 7) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for const -ttypeAllowed.nim(21, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for var -ttypeAllowed.nim(26, 10) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for result +ttypeAllowed.nim(13, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for let +ttypeAllowed.nim(17, 7) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for const +ttypeAllowed.nim(21, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for var +ttypeAllowed.nim(26, 10) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for result ''' """ diff --git a/tests/errmsgs/tundeclared_field.nim b/tests/errmsgs/tundeclared_field.nim deleted file mode 100644 index 5668050e0..000000000 --- a/tests/errmsgs/tundeclared_field.nim +++ /dev/null @@ -1,50 +0,0 @@ -discard """ -cmd: '''nim check --hints:off $file''' -action: reject -nimout: ''' -tundeclared_field.nim(25, 12) Error: undeclared field: 'bad1' for type tundeclared_field.A [type declared in tundeclared_field.nim(22, 8)] -tundeclared_field.nim(30, 17) Error: undeclared field: 'bad2' for type tundeclared_field.A [type declared in tundeclared_field.nim(28, 8)] -tundeclared_field.nim(36, 4) Error: undeclared field: 'bad3' for type tundeclared_field.A [type declared in tundeclared_field.nim(33, 8)] -tundeclared_field.nim(42, 12) Error: undeclared field: 'bad4' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)] -tundeclared_field.nim(43, 4) Error: undeclared field: 'bad5' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)] -tundeclared_field.nim(44, 23) Error: undeclared field: 'bad6' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)] -tundeclared_field.nim(46, 19) Error: undeclared field: 'bad7' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)] -tundeclared_field.nim(50, 13) Error: cannot instantiate Foo [type declared in tundeclared_field.nim(49, 8)] -''' -""" - -#[ -xxx in future work, generic instantiations (e.g. `B[int]`) should be shown with their instantiation instead of `tundeclared_field.B`, -maybe using TPreferedDesc.preferResolved or preferMixed -]# -# line 20 -block: - type A = object - a0: int - var a: A - discard a.bad1 - -block: - type A = object - a0: int - var a = A(bad2: 0) - -block: - type A = object - a0: int - var a: A - a.bad3 = 0 - -block: - type B[T] = object - b0: int - var b: B[int] - discard b.bad4 - b.bad5 = 0 - var b2 = B[int](bad6: 0) - type Bi = B[int] - var b3 = Bi(bad7: 0) - -block: - type Foo[T: SomeInteger] = object - var a: Foo[float] diff --git a/tests/errmsgs/tundeclared_routine.nim b/tests/errmsgs/tundeclared_routine.nim deleted file mode 100644 index 2f1320fff..000000000 --- a/tests/errmsgs/tundeclared_routine.nim +++ /dev/null @@ -1,44 +0,0 @@ -discard """ -cmd: '''nim check --hints:off $file''' -action: reject -nimout: ''' -tundeclared_routine.nim(24, 17) Error: attempting to call routine: 'myiter' - found tundeclared_routine.myiter(a: string) [iterator declared in tundeclared_routine.nim(22, 12)] - found tundeclared_routine.myiter() [iterator declared in tundeclared_routine.nim(23, 12)] -tundeclared_routine.nim(29, 28) Error: invalid pragma: myPragma -tundeclared_routine.nim(36, 13) Error: undeclared field: 'bar3' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(33, 8)] - found tundeclared_routine.bar3() [iterator declared in tundeclared_routine.nim(35, 12)] -tundeclared_routine.nim(41, 13) Error: undeclared field: 'bar4' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(39, 8)] -tundeclared_routine.nim(44, 15) Error: attempting to call routine: 'bad5' -''' -""" - - - - - -# line 20 -block: - iterator myiter(a:string): int = discard - iterator myiter(): int = discard - let a = myiter(1) - -block: - proc myPragma():int=discard - iterator myPragma():int=discard - proc myfun(a:int): int {.myPragma.} = 1 - let a = myfun(1) - -block: - type Foo = object - var a = Foo() - iterator bar3():int=discard - let a2 = a.bar3 - -block: - type Foo = object - var a = Foo() - let a2 = a.bar4 - -block: - let a = bad5(1) diff --git a/tests/errmsgs/twrong_at_operator.nim b/tests/errmsgs/twrong_at_operator.nim index 438186f01..ebf6d966b 100644 --- a/tests/errmsgs/twrong_at_operator.nim +++ b/tests/errmsgs/twrong_at_operator.nim @@ -1,16 +1,17 @@ discard """ -errormsg: "type mismatch: got " +errormsg: "type mismatch: got " +line: 22 nimout: ''' -twrong_at_operator.nim(21, 30) Error: type mismatch: got +twrong_at_operator.nim(22, 30) Error: type mismatch: got but expected one of: proc `@`[IDX, T](a: sink array[IDX, T]): seq[T] first type mismatch at position: 1 required type for a: sink array[IDX, T] - but expression '[int]' is of type: array[0..0, typedesc[int]] + but expression '[int]' is of type: array[0..0, type int] proc `@`[T](a: openArray[T]): seq[T] first type mismatch at position: 1 required type for a: openArray[T] - but expression '[int]' is of type: array[0..0, typedesc[int]] + but expression '[int]' is of type: array[0..0, type int] expression: @[int] ''' diff --git a/tests/errmsgs/twrongcolon.nim b/tests/errmsgs/twrongcolon.nim index 06e802eb7..e59e37660 100644 --- a/tests/errmsgs/twrongcolon.nim +++ b/tests/errmsgs/twrongcolon.nim @@ -1,10 +1,11 @@ discard """ -errormsg: "in expression ' do:" +errormsg: "in expression ':" nimout: ''' -twrongcolon.nim(10, 12) Error: in expression ' do: +Error: in expression ': 890': identifier expected, but found '' ''' +line: 11 """ var n: int : 890 diff --git a/tests/errmsgs/undeclared_routime.nim b/tests/errmsgs/undeclared_routime.nim new file mode 100644 index 000000000..7ef12cc0e --- /dev/null +++ b/tests/errmsgs/undeclared_routime.nim @@ -0,0 +1,13 @@ +discard """ +cmd: '''nim c --hints:off $file''' +errormsg: "attempting to call routine: 'myiter'" +nimout: '''undeclared_routime.nim(13, 15) Error: attempting to call routine: 'myiter' + found 'undeclared_routime.myiter(a: string)[iterator declared in undeclared_routime.nim(10, 9)]' + found 'undeclared_routime.myiter()[iterator declared in undeclared_routime.nim(11, 9)]' +''' +""" + +iterator myiter(a:string): int = discard +iterator myiter(): int = discard + +let a = myiter(1) diff --git a/tests/errmsgs/undeclared_routime2.nim b/tests/errmsgs/undeclared_routime2.nim new file mode 100644 index 000000000..3e48b48f4 --- /dev/null +++ b/tests/errmsgs/undeclared_routime2.nim @@ -0,0 +1,9 @@ +discard """ +cmd: '''nim c --hints:off $file''' +errormsg: "invalid pragma: myPragma" +""" + +proc myPragma():int=discard +iterator myPragma():int=discard +proc myfun(a:int): int {.myPragma.} = 1 +let a = myfun(1) diff --git a/tests/errmsgs/undeclared_routime3.nim b/tests/errmsgs/undeclared_routime3.nim new file mode 100644 index 000000000..052adfc08 --- /dev/null +++ b/tests/errmsgs/undeclared_routime3.nim @@ -0,0 +1,13 @@ +discard """ +cmd: '''nim c --hints:off $file''' +errormsg: "undeclared field: 'bar'" +nimout: '''undeclared_routime3.nim(13, 10) Error: undeclared field: 'bar' + found 'undeclared_routime3.bar()[declared in undeclared_routime3.nim(12, 9)]' of kind 'iterator' +''' +""" + + +type Foo = object +var a = Foo() +iterator bar():int=discard +let a = a.bar diff --git a/tests/errmsgs/undeclared_routime4.nim b/tests/errmsgs/undeclared_routime4.nim new file mode 100644 index 000000000..674caa421 --- /dev/null +++ b/tests/errmsgs/undeclared_routime4.nim @@ -0,0 +1,10 @@ +discard """ +cmd: '''nim c --hints:off $file''' +errormsg: "undeclared field: 'bar'" +nimout: '''undeclared_routime4.nim(10, 10) Error: undeclared field: 'bar' +''' +""" + +type Foo = object +var a = Foo() +let a = a.bar diff --git a/tests/errmsgs/undeclared_routime5.nim b/tests/errmsgs/undeclared_routime5.nim new file mode 100644 index 000000000..4394134ab --- /dev/null +++ b/tests/errmsgs/undeclared_routime5.nim @@ -0,0 +1,9 @@ +discard """ +cmd: '''nim c --hints:off $file''' +errormsg: "undeclared identifier: 'myfun'" +nimout: '''undeclared_routime5.nim(9, 9) Error: undeclared identifier: 'myfun' +''' +""" + + +let a = myfun(1) diff --git a/tests/errmsgs/tundeclared_routine_compiles.nim b/tests/errmsgs/undeclared_routime_compiles.nim similarity index 100% rename from tests/errmsgs/tundeclared_routine_compiles.nim rename to tests/errmsgs/undeclared_routime_compiles.nim diff --git a/tests/exception/m21261.nim b/tests/exception/m21261.nim deleted file mode 100644 index 11b12fb5b..000000000 --- a/tests/exception/m21261.nim +++ /dev/null @@ -1 +0,0 @@ -raise (ref Exception)(msg: "something") \ No newline at end of file diff --git a/tests/exception/t16366.nim b/tests/exception/t16366.nim new file mode 100644 index 000000000..4bcad79ed --- /dev/null +++ b/tests/exception/t16366.nim @@ -0,0 +1,11 @@ +discard """ + action: run + exitcode: 0 + targets: "c cpp" + disabled: openbsd +""" + +echo "foo1" +close stdout +doAssertRaises(IOError): + echo "foo" diff --git a/tests/exception/t18620.nim b/tests/exception/t18620.nim deleted file mode 100644 index ee23f8bac..000000000 --- a/tests/exception/t18620.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - matrix: "--gc:arc; --gc:refc" -""" - -proc hello() = - raise newException(ValueError, "You are wrong") - -var flag = false - -try: - hello() -except ValueError as e: - flag = true - doAssert len(getStackTraceEntries(e)) > 0 - doAssert len(getStackTraceEntries(e)) > 0 - -doAssert flag diff --git a/tests/exception/t20613.nim b/tests/exception/t20613.nim deleted file mode 100644 index 6edb69415..000000000 --- a/tests/exception/t20613.nim +++ /dev/null @@ -1,8 +0,0 @@ -discard """ - matrix: "; --panics:on" -""" - -func test = - if 0 > 10: - raiseAssert "hey" -test() diff --git a/tests/exception/t21261.nim b/tests/exception/t21261.nim deleted file mode 100644 index 84817d854..000000000 --- a/tests/exception/t21261.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - exitcode: 1 - outputsub: ''' -m21261.nim(1) m21261 -Error: unhandled exception: something [Exception] -''' -""" - -import m21261 \ No newline at end of file diff --git a/tests/exception/tcpp_imported_exc.nim b/tests/exception/tcpp_imported_exc.nim index 55a58440f..8b96ca635 100644 --- a/tests/exception/tcpp_imported_exc.nim +++ b/tests/exception/tcpp_imported_exc.nim @@ -1,8 +1,6 @@ discard """ -matrix: "--mm:refc" targets: "cpp" -output: ''' -caught as std::exception +output: '''caught as std::exception expected finally1 finally2 @@ -14,7 +12,6 @@ finally 2 expected cpp exception caught ''' -disabled: "windows" # pending bug #18011 """ type @@ -77,7 +74,7 @@ doAssert(getCurrentException() == nil) # raise by pointer and also generic type type - std_vector[T] {.importcpp"std::vector", header"".} = object + std_vector {.importcpp"std::vector", header"".} [T] = 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/exception/texception_inference.nim b/tests/exception/texception_inference.nim deleted file mode 100644 index 7dd01cca1..000000000 --- a/tests/exception/texception_inference.nim +++ /dev/null @@ -1,32 +0,0 @@ -discard """ - output: '''good''' - cmd: "nim c --gc:orc -d:release $file" -""" - -type - Raising[T, E] = object - -proc foo[T, Errors](x: proc (x: Raising[T, Errors])) {.raises: Errors.} = - discard - -proc callback(x: Raising[int, ValueError]) = - echo "callback" - -proc xy() {.raises: [ValueError].} = - foo callback - -proc x[E]() {.raises: [E, IOError].} = - raise newException(E, "text here") - -try: - x[ValueError]() -except ValueError: - echo "good" - -proc callback2(x: Raising[int, IOError]) = - discard - -proc foo2[T, OtherErrors](x: proc(x: Raising[T, OtherErrors])) {.raises: [ValueError, OtherErrors].} = - discard - -foo2 callback2 diff --git a/tests/exception/texceptionbreak.nim b/tests/exception/texceptionbreak.nim index b8ce7eead..6548192c6 100644 --- a/tests/exception/texceptionbreak.nim +++ b/tests/exception/texceptionbreak.nim @@ -29,16 +29,16 @@ echo "2" try: raise newException(OSError, "Problem") except OSError: - block label: - break label + block: + break echo "3" # Fourth Variety -block label: +block: try: raise newException(OSError, "Problem") except OSError: - break label + break echo "4" diff --git a/tests/exception/texceptions.nim b/tests/exception/texceptions.nim index 62d24c934..adee5d1d5 100644 --- a/tests/exception/texceptions.nim +++ b/tests/exception/texceptions.nim @@ -1,6 +1,4 @@ discard """ - disabled: "windows" # no sigsetjmp() there - matrix: "-d:nimStdSetjmp; -d:nimSigSetjmp; -d:nimRawSetjmp; -d:nimBuiltinSetjmp" output: ''' BEFORE @@ -19,7 +17,7 @@ FINALLY echo "" -proc no_exception = +proc no_expcetion = try: echo "BEFORE" @@ -30,7 +28,7 @@ proc no_exception = finally: echo "FINALLY" -try: no_exception() +try: no_expcetion() except: echo "RECOVER" echo "" diff --git a/tests/exception/texceptions2.nim b/tests/exception/texceptions2.nim deleted file mode 100644 index 97fd856a0..000000000 --- a/tests/exception/texceptions2.nim +++ /dev/null @@ -1,130 +0,0 @@ -discard """ - disabled: "posix" # already covered by texceptions.nim - matrix: "-d:nimStdSetjmp; -d:nimRawSetjmp; -d:nimBuiltinSetjmp" - output: ''' - -BEFORE -FINALLY - -BEFORE -EXCEPT -FINALLY -RECOVER - -BEFORE -EXCEPT: IOError: hi -FINALLY -''' -""" - -echo "" - -proc no_exception = - try: - echo "BEFORE" - - except: - echo "EXCEPT" - raise - - finally: - echo "FINALLY" - -try: no_exception() -except: echo "RECOVER" - -echo "" - -proc reraise_in_except = - try: - echo "BEFORE" - raise newException(IOError, "") - - except IOError: - echo "EXCEPT" - raise - - finally: - echo "FINALLY" - -try: reraise_in_except() -except: echo "RECOVER" - -echo "" - -proc return_in_except = - try: - echo "BEFORE" - raise newException(IOError, "hi") - - except: - echo "EXCEPT: ", getCurrentException().name, ": ", getCurrentExceptionMsg() - return - - finally: - echo "FINALLY" - -try: return_in_except() -except: echo "RECOVER" - -block: #10417 - proc moo() {.noreturn.} = discard - - let bar = - try: - 1 - except: - moo() - - doAssert(bar == 1) - -# Make sure the VM handles the exceptions correctly -block: - proc fun1(): seq[int] = - try: - try: - raise newException(ValueError, "xx") - except: - doAssert("xx" == getCurrentExceptionMsg()) - raise newException(KeyError, "yy") - except: - doAssert("yy" == getCurrentExceptionMsg()) - result.add(1212) - try: - try: - raise newException(AssertionDefect, "a") - finally: - result.add(42) - except AssertionDefect: - result.add(99) - finally: - result.add(10) - result.add(4) - result.add(0) - try: - result.add(1) - except KeyError: - result.add(-1) - except ValueError: - result.add(-1) - except IndexDefect: - result.add(2) - except: - result.add(3) - - try: - try: - result.add(1) - return - except: - result.add(-1) - finally: - result.add(2) - except KeyError: - doAssert(false) - finally: - result.add(3) - - let x1 = fun1() - const x2 = fun1() - doAssert(x1 == x2) diff --git a/tests/exception/tsetexceptions.nim b/tests/exception/tsetexceptions.nim deleted file mode 100644 index 0e353f43e..000000000 --- a/tests/exception/tsetexceptions.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - targets: "c cpp js" - joinable: false -""" - -# refs https://github.com/nim-lang/Nim/pull/18247#issuecomment-860877161 - -let ex = newException(CatchableError, "test") -setCurrentException(ex) -doAssert getCurrentException().msg == ex.msg -doAssert getCurrentExceptionMsg() == ex.msg diff --git a/tests/exprs/tresultwarning.nim b/tests/exprs/tresultwarning.nim index 28dabfdb1..32934408e 100644 --- a/tests/exprs/tresultwarning.nim +++ b/tests/exprs/tresultwarning.nim @@ -1,5 +1,5 @@ discard """ - nimout: "tresultwarning.nim(6, 7) Warning: Special variable 'result' is shadowed. [ResultShadowed]" + nimout: "Special variable 'result' is shadowed. [ResultShadowed]" """ proc test(): string = diff --git a/tests/tools/compile/tniminst.nim b/tests/flags/tgenscript.nim similarity index 50% rename from tests/tools/compile/tniminst.nim rename to tests/flags/tgenscript.nim index 78c736af0..d58395a40 100644 --- a/tests/tools/compile/tniminst.nim +++ b/tests/flags/tgenscript.nim @@ -1,5 +1,6 @@ discard """ + targets: "c" action: compile """ -include tools/niminst/niminst \ No newline at end of file +echo "--genscript" diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim index f6216c374..6a87cbe66 100644 --- a/tests/float/tfloat4.nim +++ b/tests/float/tfloat4.nim @@ -8,7 +8,7 @@ proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "", vara proc floatToStr(f: float64): string = var buffer: array[128, char] - c_sprintf(cast[cstring](addr buffer), "%.16e", f) + c_sprintf(addr buffer, "%.16e", f) result = "" for ch in buffer: if ch == '\0': @@ -54,10 +54,4 @@ doAssert 9999999999999999.0 == "9999999999999999.0".parseFloat doAssert 0.999999999999999 == ".999999999999999".parseFloat doAssert 0.9999999999999999 == ".9999999999999999".parseFloat -# bug #18400 -var s = [-13.888888'f32] -assert $s[0] == "-13.888888" -var x = 1.23456789012345'f32 -assert $x == "1.2345679" - echo("passed all tests.") diff --git a/tests/float/tfloat6.nim b/tests/float/tfloat6.nim new file mode 100644 index 000000000..c4cd6e932 --- /dev/null +++ b/tests/float/tfloat6.nim @@ -0,0 +1,23 @@ +discard """ + output: ''' +1e-06 : 1e-06 +1e-06 : 1e-06 +0.001 : 0.001 +1e-06 : 1e-06 +1e-06 : 1e-06 +10.000001 : 10.000001 +100.000001 : 100.000001 +''' + disabled: "windows" +""" + +import strutils + +echo "0.00_0001".parseFloat(), " : ", 1E-6 +echo "0.00__00_01".parseFloat(), " : ", 1E-6 +echo "0.0_01".parseFloat(), " : ", 0.001 +echo "0.00_000_1".parseFloat(), " : ", 1E-6 +echo "0.00000_1".parseFloat(), " : ", 1E-6 + +echo "1_0.00_0001".parseFloat(), " : ", 10.000001 +echo "1__00.00_0001".parseFloat(), " : ", 1_00.000001 diff --git a/tests/float/tfloats.nim b/tests/float/tfloats.nim deleted file mode 100644 index 480396e81..000000000 --- a/tests/float/tfloats.nim +++ /dev/null @@ -1,165 +0,0 @@ -discard """ - matrix: "-d:nimPreviewFloatRoundtrip; -u:nimPreviewFloatRoundtrip" - targets: "c cpp js" -""" - -#[ -xxx merge all or most float tests into this file -]# - -import std/[fenv, math, strutils] -import stdtest/testutils - -proc equalsOrNaNs(a, b: float): bool = - if isNaN(a): isNaN(b) - elif a == 0: - b == 0 and signbit(a) == signbit(b) - else: - a == b - -template reject(a) = - doAssertRaises(ValueError): discard parseFloat(a) - -template main = - block: - proc test(a: string, b: float) = - let a2 = a.parseFloat - doAssert equalsOrNaNs(a2, b), $(a, a2, b) - test "0.00_0001", 1E-6 - test "0.00__00_01", 1E-6 - test "0.0_01", 0.001 - test "0.00_000_1", 1E-6 - test "0.00000_1", 1E-6 - test "1_0.00_0001", 10.000001 - test "1__00.00_0001", 1_00.000001 - test "inf", Inf - test "-inf", -Inf - test "-Inf", -Inf - test "-INF", -Inf - test "NaN", NaN - test "-nan", NaN - test ".1", 0.1 - test "-.1", -0.1 - test "-0", -0.0 - when false: # pending bug #18246 - test "-0", -0.0 - test ".1e-1", 0.1e-1 - test "0_1_2_3.0_1_2_3E+0_1_2", 123.0123e12 - test "0_1_2.e-0", 12e0 - test "0_1_2e-0", 12e0 - test "-0e0", -0.0 - test "-0e-0", -0.0 - - reject "a" - reject "" - reject "e1" - reject "infa" - reject "infe1" - reject "_" - reject "1e" - - when false: # gray area; these numbers should probably be invalid - reject "1_" - reject "1_.0" - reject "1.0_" - - block: # bugs mentioned in https://github.com/nim-lang/Nim/pull/18504#issuecomment-881635317 - block: # example 1 - let a = 0.1+0.2 - doAssert a != 0.3 - when defined(nimPreviewFloatRoundtrip): - doAssert $a == "0.30000000000000004" - else: - whenRuntimeJs: discard - do: doAssert $a == "0.3" - block: # example 2 - const a = 0.1+0.2 - when defined(nimPreviewFloatRoundtrip): - doAssert $($a, a) == """("0.30000000000000004", 0.30000000000000004)""" - else: - whenRuntimeJs: discard - do: doAssert $($a, a) == """("0.3", 0.3)""" - block: # example 3 - const a1 = 0.1+0.2 - let a2 = a1 - doAssert a1 != 0.3 - when defined(nimPreviewFloatRoundtrip): - doAssert $[$a1, $a2] == """["0.30000000000000004", "0.30000000000000004"]""" - else: - whenRuntimeJs: discard - do: doAssert $[$a1, $a2] == """["0.3", "0.3"]""" - - when defined(nimPreviewFloatRoundtrip): - block: # bug #18148 - var a = 1.1'f32 - doAssert $a == "1.1", $a # was failing - - block: # bug #18400 - block: - let a1 = 0.1'f32 - let a2 = 0.2'f32 - let a3 = a1 + a2 - var s = "" - s.addFloat(a3) - whenVMorJs: discard # xxx refs #12884 - do: - doAssert a3 == 0.3'f32 - doAssert $a3 == "0.3" - - block: - let a1 = 0.1 - let a2 = 0.2 - let a3 = a1 + a2 - var s = "" - s.addFloat(a3) - doAssert a3 != 0.3 - doAssert $a3 == "0.30000000000000004" - - block: - var s = [-13.888888'f32] - whenRuntimeJs: discard - do: - doAssert $s == "[-13.888888]" - doAssert $s[0] == "-13.888888" - - block: # bug #7717 - proc test(f: float) = - let f2 = $f - let f3 = parseFloat(f2) - doAssert equalsOrNaNs(f, f3), $(f, f2, f3) - test 1.0 + epsilon(float64) - test 1000000.0000000123 - test log2(100000.0) - test maximumPositiveValue(float32) - test maximumPositiveValue(float64) - test minimumPositiveValue(float32) - test minimumPositiveValue(float64) - - block: # bug #12884 - block: # example 1 - const x0: float32 = 1.32 - let x1 = 1.32 - let x2 = 1.32'f32 - var x3: float32 = 1.32 - doAssert $(x0, x1, x2, x3) == "(1.32, 1.32, 1.32, 1.32)" - block: # example https://github.com/nim-lang/Nim/issues/12884#issuecomment-564967962 - let x = float(1.32'f32) - when nimvm: discard # xxx prints 1.3 - else: - when not defined(js): - doAssert $x == "1.3200000524520874" - doAssert $1.32 == "1.32" - doAssert $1.32'f32 == "1.32" - let x2 = 1.32'f32 - doAssert $x2 == "1.32" - block: - var x = 1.23456789012345'f32 - when nimvm: - discard # xxx, refs #12884 - else: - when not defined(js): - doAssert x == 1.2345679'f32 - doAssert $x == "1.2345679" - -static: main() -main() diff --git a/tests/gc/cyclecollector.nim b/tests/gc/cyclecollector.nim index 2d02a7a3c..7b47758f2 100644 --- a/tests/gc/cyclecollector.nim +++ b/tests/gc/cyclecollector.nim @@ -9,10 +9,7 @@ type proc createCycle(leaf: string): Node = new result result.a = result - when defined(gcArc) or defined(gcOrc): - result.leaf = leaf - else: - shallowCopy result.leaf, leaf + shallowCopy result.leaf, leaf proc main = for i in 0 .. 100_000: diff --git a/tests/gc/gcleak3.nim b/tests/gc/gcleak3.nim index 5e146d69f..588e238e9 100644 --- a/tests/gc/gcleak3.nim +++ b/tests/gc/gcleak3.nim @@ -17,10 +17,14 @@ for i in 0..1024: s.add(obj) proc limit*[t](a: var seq[t]) = - while s.len > 0: + var loop = s.len() - 512 + for i in 0..loop: + #echo i + #GC_fullCollect() if getOccupiedMem() > 3000_000: quit("still a leak!") - s.delete(0) + s.delete(i) s.limit() + echo "no leak: ", getOccupiedMem() diff --git a/tests/gc/panicoverride.nim b/tests/gc/panicoverride.nim deleted file mode 100644 index 0f28b0b72..000000000 --- a/tests/gc/panicoverride.nim +++ /dev/null @@ -1,14 +0,0 @@ - -proc printf(frmt: cstring) {.varargs, importc, header: "", cdecl.} -proc exit(code: int) {.importc, header: "", cdecl.} - -{.push stack_trace: off, profiler:off.} - -proc rawoutput(s: string) = - printf("%s\n", s) - -proc panic(s: string) {.noreturn.} = - rawoutput(s) - exit(1) - -{.pop.} \ No newline at end of file diff --git a/tests/gc/trace_globals.nim b/tests/gc/trace_globals.nim deleted file mode 100644 index f62a15692..000000000 --- a/tests/gc/trace_globals.nim +++ /dev/null @@ -1,33 +0,0 @@ -discard """ - output: ''' -10000000 -10000000 -10000000''' -""" - -# bug #17085 - -#[ -refs https://github.com/nim-lang/Nim/issues/17085#issuecomment-786466595 -with --gc:boehm, this warning sometimes gets generated: -Warning: Repeated allocation of very large block (appr. size 14880768): -May lead to memory leak and poor performance. -nim CI now runs this test with `testWithoutBoehm` to avoid running it with --gc:boehm. -]# - -proc init(): string = - for a in 0..<10000000: - result.add 'c' - -proc f() = - var a {.global.} = init() - var b {.global.} = init() - var c {.global.} = init() - - echo a.len - # `echo` intentional according to - # https://github.com/nim-lang/Nim/pull/17469/files/0c9e94cb6b9ebca9da7cb19a063fba7aa409748e#r600016573 - echo b.len - echo c.len - -f() diff --git a/tests/gc/tstandalone.nim b/tests/gc/tstandalone.nim deleted file mode 100644 index 41dad9ba4..000000000 --- a/tests/gc/tstandalone.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - matrix: "--os:standalone --gc:none" - exitcode: 1 - output: "value out of range" -""" - -type - rangeType = range[0..1] - -var - r: rangeType = 0 - i = 2 - -r = rangeType(i) diff --git a/tests/generics/mdotlookup.nim b/tests/generics/mdotlookup.nim index 3112c133f..470fa0a51 100644 --- a/tests/generics/mdotlookup.nim +++ b/tests/generics/mdotlookup.nim @@ -1,9 +1,9 @@ -proc baz(o: auto): int = 5 # if bar is exported, it works +proc baz(o: any): int = 5 # if bar is exported, it works type MyObj = object x: int -proc foo*(b: auto) = +proc foo*(b: any) = var o: MyObj echo b.baz, " ", o.x.baz, " ", b.baz() diff --git a/tests/generics/module_with_generics.nim b/tests/generics/module_with_generics.nim index 960a694d7..e801a4790 100644 --- a/tests/generics/module_with_generics.nim +++ b/tests/generics/module_with_generics.nim @@ -1,5 +1,5 @@ type - Base[T] {.inheritable.} = ref object + Base[T] = ref object {.inheritable.} value*: T Derived[T] = ref object of Base[T] diff --git a/tests/generics/t13525.nim b/tests/generics/t13525.nim index 1fd84852b..d1b8df78c 100644 --- a/tests/generics/t13525.nim +++ b/tests/generics/t13525.nim @@ -1,6 +1,6 @@ # https://github.com/nim-lang/Nim/issues/13524 template fun(field): untyped = astToStr(field) -proc test1(): string = fun(nonexistent1) -proc test2[T](): string = fun(nonexistent2) # used to cause: Error: undeclared identifier: 'nonexistent2' -doAssert test1() == "nonexistent1" -doAssert test2[int]() == "nonexistent2" +proc test1(): string = fun(nonexistant1) +proc test2[T](): string = fun(nonexistant2) # used to cause: Error: undeclared identifier: 'nonexistant2' +doAssert test1() == "nonexistant1" +doAssert test2[int]() == "nonexistant2" diff --git a/tests/generics/t16639.nim b/tests/generics/t16639.nim deleted file mode 100644 index fc00dfc34..000000000 --- a/tests/generics/t16639.nim +++ /dev/null @@ -1,21 +0,0 @@ -discard """ - action: compile -""" - -type Foo[T] = object - when true: - x: float - -type Bar = object - when true: - x: float - -import std/macros -import std/assertions - -macro test() = - let a = getImpl(bindSym"Foo")[^1] - let b = getImpl(bindSym"Bar")[^1] - doAssert treeRepr(a) == treeRepr(b) - -test() diff --git a/tests/generics/t18859.nim b/tests/generics/t18859.nim deleted file mode 100644 index ca6c3d10b..000000000 --- a/tests/generics/t18859.nim +++ /dev/null @@ -1,17 +0,0 @@ -import macros - -macro symFromDesc(T: typedesc): untyped = - let typ = getType(T) - typ[1] - -template produceType(T: typedesc): untyped = - type - XT = object - x: symFromDesc(T) - - XT - -type - X[T] = produceType(T) - -var x: X[int] diff --git a/tests/generics/t20996.nim b/tests/generics/t20996.nim deleted file mode 100644 index 8aa83c4e2..000000000 --- a/tests/generics/t20996.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - action: compile -""" - -import std/macros - -macro matchMe(x: typed): untyped = - discard x.getTypeImpl - -type - ElementRT = object - Element[Z] = ElementRT # this version is needed, even though we don't use it - -let ar = ElementRT() -matchMe(ar) diff --git a/tests/generics/t500.nim b/tests/generics/t500.nim deleted file mode 100644 index 2486359aa..000000000 --- a/tests/generics/t500.nim +++ /dev/null @@ -1,8 +0,0 @@ -discard """ -action: compile -""" - -type - TTest = tuple[x: range[0..80], y: range[0..25]] - -let x: TTest = (2, 23) diff --git a/tests/generics/t6637.nim b/tests/generics/t6637.nim deleted file mode 100644 index dd4f339a2..000000000 --- a/tests/generics/t6637.nim +++ /dev/null @@ -1,9 +0,0 @@ - -type - Grid2D*[I: SomeInteger, w, h: static[I], T] = object - grid: array[w, array[h, T]] - Grid2DIns = Grid2D[int, 2, 3, uint8] - -let a = Grid2DIns() -doAssert a.grid.len == 2 -doAssert a.grid[0].len == 3 diff --git a/tests/generics/t7446.nim b/tests/generics/t7446.nim deleted file mode 100644 index 71aa8f0e8..000000000 --- a/tests/generics/t7446.nim +++ /dev/null @@ -1,10 +0,0 @@ -proc foo(x: Natural or SomeUnsignedInt):int = - when x is int: - result = 1 - else: - result = 2 -let a = 10 -doAssert foo(a) == 1 - -let b = 10'u8 -doAssert foo(b) == 2 \ No newline at end of file diff --git a/tests/generics/tcan.nim b/tests/generics/tcan.nim index bbefa7233..eea69cdb7 100644 --- a/tests/generics/tcan.nim +++ b/tests/generics/tcan.nim @@ -31,7 +31,7 @@ block tinherit: block tspecialise: type - TGen[T] {.inheritable.} = object + TGen[T] = object {.inheritable.} TSpef = object of TGen[string] diff --git a/tests/generics/tgeneric0.nim b/tests/generics/tgeneric0.nim index b5e1c4bb4..44c34917d 100644 --- a/tests/generics/tgeneric0.nim +++ b/tests/generics/tgeneric0.nim @@ -153,16 +153,3 @@ proc unzip*[T,U](xs: List[tuple[t: T, u: U]]): (List[T], List[U]) = discard proc unzip2*[T,U](xs: List[(T,U)]): (List[T], List[U]) = discard -type - AtomicType = pointer|ptr|int - - Atomic[T: AtomicType] = distinct T - - Block[T: AtomicType] = object - - AtomicContainer[T: AtomicType] = object - b: Atomic[ptr Block[T]] - -# bug #8295 -var x = AtomicContainer[int]() -doAssert (ptr Block[int])(x.b) == nil diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim index 29a73afc6..4cb12f91b 100644 --- a/tests/generics/tgeneric3.nim +++ b/tests/generics/tgeneric3.nim @@ -1,9 +1,9 @@ discard """ output: ''' 312 -1000 -1000 -500 +1000000 +1000000 +500000 0 ''' """ @@ -12,7 +12,7 @@ import strutils type PNode[T,D] = ref TNode[T,D] - TItem[T,D] {.acyclic, pure, final, shallow.} = object + TItem {.acyclic, pure, final, shallow.} [T,D] = 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[T,D] {.acyclic, pure, final, shallow.} = object + TNode {.acyclic, pure, final, shallow.} [T,D] = 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): APath.Nd.slots[i] = move APath.Nd.slots[i - 1] + for i in countdown(APath.Nd.count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1]) APath.Nd.slots[x] = setItem(Akey, Avalue, ANode) @@ -255,39 +255,31 @@ 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: - it1[i] = move left.slots[i] - for i in 0..cCenter-1: - result.slots[i] = move left.slots[cCenter + i] + 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]) result.left = n else : if x < cCenter : - for i in 0..x-1: - it1[i] = move left.slots[i] + for i in 0..x-1: shallowCopy(it1[i], left.slots[i]) it1[x] = setItem(Akey, Avalue, n) - for i in x+1 .. cCenter-1: - it1[i] = move left.slots[i-1] + for i in x+1 .. cCenter-1: shallowCopy(it1[i], 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: - result.slots[i] = move left.slots[cCenter + i] + for i in 0..cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i]) else : - for i in 0..cCenter-1: - it1[i] = move left.slots[i] + for i in 0..cCenter-1: shallowCopy(it1[i], left.slots[i]) x = x - (cCenter + 1) - for i in 0..x-1: - result.slots[i] = move left.slots[cCenter + i + 1] + for i in 0..x-1: shallowCopy(result.slots[i], left.slots[cCenter + i + 1]) result.slots[x] = setItem(Akey, Avalue, n) - for i in x+1 .. cCenter-1: - result.slots[i] = move left.slots[cCenter + i] + for i in x+1 .. cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i]) var w = left.slots[cCenter] Akey = w.key Avalue = w.value result.left = w.node left.count = cCenter - left.slots = move it1 + shallowCopy(left.slots, it1) proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D, Oldvalue: var D): ref TNode[T,D] = @@ -445,7 +437,7 @@ proc test() = var it1 = internalFind(root, 312) echo it1.value - for i in 1..1_000: + for i in 1..1_000_000: root = internalPut(root, i, i, oldvalue) var cnt = 0 diff --git a/tests/generics/tgeneric_recursionlimit.nim b/tests/generics/tgeneric_recursionlimit.nim deleted file mode 100644 index 5fe9b43c6..000000000 --- a/tests/generics/tgeneric_recursionlimit.nim +++ /dev/null @@ -1,123 +0,0 @@ -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/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim index 3068a22f2..365afd407 100644 --- a/tests/generics/tgenerics_issues.nim +++ b/tests/generics/tgenerics_issues.nim @@ -24,9 +24,9 @@ G:0,1:0.1 G:0,1:0.1 H:1:0.1 0 -(foo: none(seq[Foo]), s: "") -(foo: some(@[(a: "world", bar: none(Bar))]), s: "hello,") -@[(a: "hey", bar: none(Bar))] +(foo: None[seq[Foo]], s: "") +(foo: Some(@[(a: "world", bar: None[Bar])]), s: "hello,") +@[(a: "hey", bar: None[Bar])] ''' joinable: false """ @@ -603,7 +603,7 @@ block t7854: block t5864: - proc defaultStatic(s: openArray, N: static[int] = 1): int = N + proc defaultStatic(s: openarray, N: static[int] = 1): int = N proc defaultGeneric[T](a: T = 2): int = a let a = [1, 2, 3, 4].defaultStatic() @@ -774,13 +774,13 @@ block: # issue #9458 Option[T] = object val: T has: bool - + Bar = object proc none(T: typedesc): Option[T] = discard - proc foo[T](self: T; x: Option[Bar] = Bar.none) = + proc foo[T](self: T; x: Option[Bar] = Bar.none) = discard foo(1) @@ -834,7 +834,7 @@ proc getBar(x: string): Bar = Bar(foo: none[seq[Foo]](), s: "") -proc fakeReadLine(): string = "hey" +proc fakeReadLine(): TaintedString = "hey" echo getBar(fakeReadLine()) # causes error @@ -860,35 +860,3 @@ type var a: Tile3[int] -block: # Ensure no segfault from constraint - type - Regex[A: SomeOrdinal] = ref object - val: Regex[A] - MyConstraint = (seq or enum or set) - MyOtherType[A: MyConstraint] = ref object - val: MyOtherType[A] - - var - a = Regex[int]() - b = Regex[bool]() - c = MyOtherType[seq[int]]() - -block: # https://github.com/nim-lang/Nim/issues/20416 - type - Item[T] = object - link:ptr Item[T] - data:T - - KVSeq[A,B] = seq[(A,B)] - - MyTable[A,B] = object - data: KVSeq[A,B] - - Container[T] = object - a: MyTable[int,ref Item[T]] - - proc p1(sg:Container) = discard # Make sure that a non parameterized 'Container' argument still compiles - - proc p2[T](sg:Container[T]) = discard - var v : Container[int] - p2(v) diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim index 4cfa47809..285108cd3 100644 --- a/tests/generics/tgenerics_various.nim +++ b/tests/generics/tgenerics_various.nim @@ -230,8 +230,8 @@ block tvarargs_vs_generics: echo "direct" proc withDirectType[T](arg: T) = echo "generic" - proc withOpenArray(args: openArray[string]) = - echo "openArray" + proc withOpenArray(args: openarray[string]) = + echo "openarray" proc withOpenArray[T](arg: T) = echo "generic" proc withVarargs(args: varargs[string]) = @@ -253,17 +253,3 @@ block: var x: Que[int] doAssert(x.x == 0) - - -# bug #4466 -proc identity[T](t: T): T = t - -proc doSomething[A, B](t: tuple[a: A, b: B]) = discard - -discard identity((c: 1, d: 2)) -doSomething(identity((1, 2))) - -# bug #6231 -proc myProc[T, U](x: T or U) = discard - -myProc[int, string](x = 2) diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim deleted file mode 100644 index ba24b79e9..000000000 --- a/tests/generics/timplicit_and_explicit.nim +++ /dev/null @@ -1,45 +0,0 @@ - -block: # basic test - proc doStuff[T](a: SomeInteger): T = discard - proc doStuff[T;Y](a: SomeInteger, b: Y): Y = discard - assert typeof(doStuff[int](100)) is int - assert typeof(doStuff[int](100, 1.0)) is float - assert typeof(doStuff[int](100, "Hello")) is string - - proc t[T](x: T; z: int | float): seq[T] = result.add(x & $z) - - assert t[string]("Hallo", 2.0) == @["Hallo" & $2.0] - - proc t2[T](z: int | float): seq[T] = result.add($z) - - assert t2[string](2.0) == @[$2.0] - -block: # template test - template someThing[T;Y](a: SomeFloat, b: SomeOrdinal): (T, Y) = (a, b) - assert typeof(someThing[float64, int](1.0, 100)) is (float64, int) - -block: # static test - proc t[T](s: static bool) = discard - proc t2[T](s: static string) = discard - t[string](true) - t2[int]("hello") - t2[string]("world") - t2[float]("test222222") - -block: #11152 - proc f[T](X: typedesc) = discard - f[int](string) - -block: #15622 - proc test1[T](a: T, b: static[string] = "") = discard - test1[int64](123) - proc test2[T](a: T, b: static[string] = "") = discard - test2[int64, static[string]](123) - -block: #4688 - proc convertTo[T](v: int or float): T = (T)(v) - discard convertTo[float](1) - -block: #4164 - proc printStr[T](s: static[string]): T = discard - discard printStr[int]("hello static") \ No newline at end of file diff --git a/tests/generics/tinheritable_importcpp.nim b/tests/generics/tinheritable_importcpp.nim deleted file mode 100644 index 8ee18b70b..000000000 --- a/tests/generics/tinheritable_importcpp.nim +++ /dev/null @@ -1,10 +0,0 @@ -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/tnullary_generics.nim b/tests/generics/tnullary_generics.nim deleted file mode 100644 index c79558ee3..000000000 --- a/tests/generics/tnullary_generics.nim +++ /dev/null @@ -1,26 +0,0 @@ -discard """ - nimout: ''' -hah -hey -hey -hah -''' -""" - -# non-generic -proc foo(s: string) = - static: echo "hah" - echo s - -static: echo "hey" - -foo("hoo") - -# nullary generic -proc bar[](s: string) = - static: echo "hah" - echo s - -static: echo "hey" - -bar("hoo") diff --git a/tests/generics/tparam_binding.nim b/tests/generics/tparam_binding.nim index fa7558613..cd0d58e02 100644 --- a/tests/generics/tparam_binding.nim +++ b/tests/generics/tparam_binding.nim @@ -1,7 +1,6 @@ discard """ - matrix: "--mm:arc; --mm:refc" errormsg: "got " - line: 28 + line: 27 """ type diff --git a/tests/generics/tpointerprocs.nim b/tests/generics/tpointerprocs.nim deleted file mode 100644 index 2bcaf15b3..000000000 --- a/tests/generics/tpointerprocs.nim +++ /dev/null @@ -1,28 +0,0 @@ -discard """ -cmd: "nim check $options --hints:off $file" -action: "reject" -nimout:''' -tpointerprocs.nim(15, 11) Error: 'foo' doesn't have a concrete type, due to unspecified generic parameters. -tpointerprocs.nim(27, 11) Error: cannot instantiate: 'foo[int]'; got 1 typeof(s) but expected 2 -tpointerprocs.nim(27, 14) Error: expression 'foo[int]' has no type (or is ambiguous) -tpointerprocs.nim(28, 11) Error: expression 'bar' has no type (or is ambiguous) -''' -""" - -block: - proc foo(x: int | float): float = result = 1.0 - let - bar = foo - baz = bar - -block: - proc foo(x: int | float): float = result = 1.0 - let - bar = foo[int] - baz = bar - -block: - proc foo(x: int | float, y: int or string): float = result = 1.0 - let - bar = foo[int] - baz = bar \ No newline at end of file diff --git a/tests/generics/tprevent_double_bind.nim b/tests/generics/tprevent_double_bind.nim index d8fc6e5d3..86e080ab6 100644 --- a/tests/generics/tprevent_double_bind.nim +++ b/tests/generics/tprevent_double_bind.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch: got " + errormsg: "type mismatch: got " line: 20 """ diff --git a/tests/generics/trecursivegenerics.nim b/tests/generics/trecursivegenerics.nim deleted file mode 100644 index 1b152b063..000000000 --- a/tests/generics/trecursivegenerics.nim +++ /dev/null @@ -1,96 +0,0 @@ -block: # Replicates #18728 - type - FlipFlop[A, B] = ref object - val: A - next: FlipFlop[B, A] - - Trinary[A, B, C] = ref object - next: Trinary[B, C, A] - - assert typeof(FlipFlop[int, string]().next) is FlipFlop[string, int] - assert typeof(FlipFlop[string, int]().next) is FlipFlop[int, string] - assert typeof(Trinary[int, float, string]().next) is Trinary[float, string, int] - assert typeof(Trinary[int, float, string]().next.next) is Trinary[string, int, float] - var a = FlipFlop[int, string](val: 100, next: FlipFlop[string, int](val: "Hello")) - assert a.val == 100 - assert a.next.val == "Hello" - -block: # 18838 - type - DoublyLinkedNodeObj[T] = object - value: T - - DoublyLinkedNode[T] = ref DoublyLinkedNodeObj[T] - - Item[T] = ref object - link: DoublyLinkedNode[Item[T]] - - Box = object - - proc newDoublyLinkedNode[T](value: T): DoublyLinkedNode[T] = - new(result) - result.value = value - - let link = newDoublyLinkedNode(Item[Box]()) - -import lists -block: - type - Box = object - Item[T] = ref object - link:DoublyLinkedNode[ Item[T] ] - - ItemSimple = ref object - link:DoublyLinkedNode[ ItemSimple ] - - let link = newDoublyLinkedNode( Item[Box]() ) - -block: #18897 - type - SkipListObj[T] = object - over: SkipList[T] - down: SkipList[T] - value: T - - SkipList[T] = ref SkipListObj[T] - - GraphObj[N, E; F: static[int]] = object - nodes: SkipList[Node[N, E]] - - Graph[N, E; F: static[int]] = ref GraphObj[N, E, F] - - Node[N, E] = ref NodeObj[N, E] - - NodeObj[N, E] = object - value: N - incoming: SkipList[Edge[N, E]] - outgoing: SkipList[Edge[N, E]] - - Edge[N, E] = ref EdgeObj[N, E] - - EdgeObj[N, E] = object - value: E - id: int - source: Node[N, E] - target: Node[N, E] - - EdgeResult[N, E] = tuple - source: Node[N, E] - edge: Edge[N, E] - target: Node[N, E] - - proc newSkipList[T](value: T): SkipList[T] = - static: echo T, " ", typeof(result.value) - result = SkipList[T](value: value) - - proc toSkipList[T](values: openArray[T] = @[]): SkipList[T] = - for item in items(values): - if result.isNil: - result = newSkipList(item) - - proc newContainer[N, E, F](graph: Graph[N, E, F]; form: typedesc): auto = - result = toSkipList[form]([]) - - var - result = Graph[int, string, 0]() - result.nodes = result.newContainer(Node[int, string]) \ No newline at end of file diff --git a/tests/generics/tstatic_constrained.nim b/tests/generics/tstatic_constrained.nim deleted file mode 100644 index 3c9201548..000000000 --- a/tests/generics/tstatic_constrained.nim +++ /dev/null @@ -1,79 +0,0 @@ -discard """ - cmd: "nim check --hints:off --warnings:off $file" - action: "reject" - nimout:''' -tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] -got: -but expected: -tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] -got: -but expected: -tstatic_constrained.nim(44, 31) Error: object constructor needs an object type [proxy] -tstatic_constrained.nim(44, 31) Error: expression '' has no type (or is ambiguous) -tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] -got: -but expected: -tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] -got: -but expected: -tstatic_constrained.nim(45, 34) Error: object constructor needs an object type [proxy] -tstatic_constrained.nim(45, 34) Error: expression '' has no type (or is ambiguous) -tstatic_constrained.nim(77, 14) Error: cannot instantiate MyType [type declared in tstatic_constrained.nim(71, 5)] -got: -but expected: -''' -""" -block: - type - MyType[T; X: static T] = object - data: T - MyOtherType[T: float or string, Y: static T] = object - - func f[T,X](a: MyType[T,X]): MyType[T,X] = - when T is string: - MyType[T,X](data: a.data & X) - else: - MyType[T,X](data: a.data + X) - - discard MyType[int, 2](data: 1) - discard MyType[string, "Helelello"](data: "Hmmm") - discard MyType[int, 2](data: 1).f() - discard MyType[string, "Helelello"](data: "Hmmm").f() - discard MyOtherType[float, 1.3]() - discard MyOtherType[string, "Hello"]() - discard MyOtherType[int, 10]() - discard MyOtherType[byte, 10u8]() - -block: - type - Moduloable = concept m, type M - m mod m is M - Addable = concept a, type A - a + a is A - Modulo[T: Moduloable; Mod: static T] = distinct T - ModuloAdd[T: Moduloable or Addable; Mod: static T] = distinct T - ModuAddable = Addable or Moduloable - ModdAddClass[T: ModuAddable; Mod: static T] = distinct T - - proc toMod[T](val: T, modVal: static T): Modulo[T, modVal] = - mixin `mod` - Modulo[T, modVal](val mod modVal) - var - a = 3231.toMod(10) - b = 5483.toMod(10) - discard ModuloAdd[int, 3](0) - discard ModdAddClass[int, 3](0) - -block: - type - MyConstraint = int or string - MyOtherConstraint[T] = object - MyType[T: MyConstraint; Y: static T] = object - MyOtherType[T: MyOtherConstraint; Y: static T] = object - - var - a: MyType[int, 10] - b: MyType[string, "hello"] - c: MyType[float, 10d] - d: MyOtherType[MyOtherConstraint[float],MyOtherConstraint[float]()] - e: MyOtherType[MyOtherConstraint[int], MyOtherConstraint[int]()] \ No newline at end of file diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim index 300da56a6..2af5a7615 100644 --- a/tests/generics/tthread_generic.nim +++ b/tests/generics/tthread_generic.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:refc; --mm:orc" + cmd: "nim $target --hints:on --threads:on $options $file" action: compile """ diff --git a/tests/gensym/tgensymgeneric.nim b/tests/gensym/tgensymgeneric.nim index 6e1df4842..c17a0715f 100644 --- a/tests/gensym/tgensymgeneric.nim +++ b/tests/gensym/tgensymgeneric.nim @@ -21,7 +21,7 @@ template genImpl() = gensymed.x = "abc" else: gensymed.x = 123 - result = move gensymed + shallowCopy(result, gensymed) proc gen[T](x: T): T = genImpl() diff --git a/tests/global/t3505.nim b/tests/global/t3505.nim deleted file mode 100644 index 437a02ae6..000000000 --- a/tests/global/t3505.nim +++ /dev/null @@ -1,41 +0,0 @@ -discard """ -cmd: "nim check $options --hints:off $file" -action: "reject" -nimout: ''' -t3505.nim(22, 22) Error: cannot assign local to global variable -t3505.nim(31, 28) Error: cannot assign local to global variable -t3505.nim(39, 29) Error: cannot assign local to global variable - - - - -''' -""" - - - - - - -proc foo = - let a = 0 - var b {.global.} = a -foo() - -# issue #5132 -proc initX(it: float): int = 8 -proc initX2(it: int): int = it - -proc main() = - var f: float - var x {.global.} = initX2(initX(f)) - -main() - -# issue #20866 -proc foo2() = - iterator bar() {.closure.} = - discard - var g {.global.} = rawProc(bar) - -foo2() \ No newline at end of file diff --git a/tests/ic/config.nims b/tests/ic/config.nims deleted file mode 100644 index a522efb0d..000000000 --- a/tests/ic/config.nims +++ /dev/null @@ -1,3 +0,0 @@ -when defined(windows): - --tlsEmulation:off ---mm:refc diff --git a/tests/ic/mbaseobj.nim b/tests/ic/mbaseobj.nim deleted file mode 100644 index 0f4e4a90d..000000000 --- a/tests/ic/mbaseobj.nim +++ /dev/null @@ -1,7 +0,0 @@ - -type - Base* = ref object of RootObj - s*: string - -method m*(b: Base) {.base.} = - echo "Base ", b.s diff --git a/tests/ic/mcompiletime_counter.nim b/tests/ic/mcompiletime_counter.nim deleted file mode 100644 index 6fc7b3f2a..000000000 --- a/tests/ic/mcompiletime_counter.nim +++ /dev/null @@ -1,15 +0,0 @@ - -import std/macros -import std/macrocache - -const myCounter = CacheCounter"myCounter" - -proc getUniqueId*(): int {.compileTime.} = - inc myCounter - result = myCounter.value - -static: - myCounter.inc(3) - assert myCounter.value == 3 - - diff --git a/tests/ic/mdefconverter.nim b/tests/ic/mdefconverter.nim deleted file mode 100644 index d0a23f801..000000000 --- a/tests/ic/mdefconverter.nim +++ /dev/null @@ -1,2 +0,0 @@ - -converter toBool*(x: int): bool = x != 0 diff --git a/tests/ic/mimports.nim b/tests/ic/mimports.nim deleted file mode 100644 index 50773e001..000000000 --- a/tests/ic/mimports.nim +++ /dev/null @@ -1,9 +0,0 @@ -from mimportsb {.all.} import fnb1, hfnb3 - -proc fn1*(): int = 1 -proc fn2*(): int = 2 -proc hfn3(): int = 3 -proc hfn4(): int = 4 -proc hfn5(): int = 5 - -export mimportsb.fnb2, hfnb3 diff --git a/tests/ic/mimportsb.nim b/tests/ic/mimportsb.nim deleted file mode 100644 index 3cae925c5..000000000 --- a/tests/ic/mimportsb.nim +++ /dev/null @@ -1,4 +0,0 @@ -proc fnb1*(): int = 1 -proc fnb2*(): int = 2 -proc hfnb3(): int = 3 -proc hfnb4(): int = 4 diff --git a/tests/ic/tcompiletime_counter.nim b/tests/ic/tcompiletime_counter.nim deleted file mode 100644 index 695a6ec27..000000000 --- a/tests/ic/tcompiletime_counter.nim +++ /dev/null @@ -1,24 +0,0 @@ -discard """ - output: '''id 4''' -""" - -import mcompiletime_counter - -const intId = getUniqueId() - -echo "id ", intId - -#!EDIT!# - -discard """ - output: '''id 4 5''' -""" - -import mcompiletime_counter - -const - intId = getUniqueId() - floatId = getUniqueId() - -echo "id ", intId, " ", floatId - diff --git a/tests/ic/tconverter.nim b/tests/ic/tconverter.nim deleted file mode 100644 index aecdf4b48..000000000 --- a/tests/ic/tconverter.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - output: "yes" -""" - -import mdefconverter - -echo "yes" - -#!EDIT!# - -discard """ - output: "converted int to bool" -""" - -import mdefconverter - -if 4: - echo "converted int to bool" diff --git a/tests/ic/tgenerics.nim b/tests/ic/tgenerics.nim index 138799e85..bc5c05f4f 100644 --- a/tests/ic/tgenerics.nim +++ b/tests/ic/tgenerics.nim @@ -1,5 +1,6 @@ discard """ output: "bar" + disabled: "true" """ import tables diff --git a/tests/ic/timports.nim b/tests/ic/timports.nim deleted file mode 100644 index 518a689f5..000000000 --- a/tests/ic/timports.nim +++ /dev/null @@ -1,29 +0,0 @@ -import mimports -doAssert fn1() == 1 -doAssert not declared(hfn3) - -#!EDIT!# - -import mimports {.all.} -doAssert fn1() == 1 -doAssert declared(hfn3) -doAssert hfn3() == 3 -doAssert mimports.hfn4() == 4 - -# reexports -doAssert not declared(fnb1) -doAssert not declared(hfnb4) -doAssert fnb2() == 2 -doAssert hfnb3() == 3 - -#!EDIT!# - -from mimports {.all.} import hfn3 -doAssert not declared(fn1) -from mimports {.all.} as bar import fn1 -doAssert fn1() == 1 -doAssert hfn3() == 3 -doAssert not declared(hfn4) -doAssert declared(mimports.hfn4) -doAssert mimports.hfn4() == 4 -doAssert bar.hfn4() == 4 diff --git a/tests/ic/tmethods.nim b/tests/ic/tmethods.nim deleted file mode 100644 index 251f26889..000000000 --- a/tests/ic/tmethods.nim +++ /dev/null @@ -1,28 +0,0 @@ -discard """ - output: '''Base abc''' -""" - -import mbaseobj - -var c = Base(s: "abc") -m c - -#!EDIT!# - -discard """ - output: '''Base abc -Inherited abc''' -""" - -import mbaseobj - -type - Inherited = ref object of Base - -method m(i: Inherited) = - procCall m(Base i) - echo "Inherited ", i.s - -var c = Inherited(s: "abc") -m c - diff --git a/tests/ic/tstdlib_import_changed.nim b/tests/ic/tstdlib_import_changed.nim deleted file mode 100644 index da69a53b5..000000000 --- a/tests/ic/tstdlib_import_changed.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - output: '''yes''' -""" - -echo "yes" - -#!EDIT!# - -discard """ - output: '''yes2''' -""" - -import std / [monotimes] -#discard getMonoTime() -echo "yes2" diff --git a/tests/implicit/timplicit.nim b/tests/implicit/timplicit.nim new file mode 100644 index 000000000..bb701249c --- /dev/null +++ b/tests/implicit/timplicit.nim @@ -0,0 +1,49 @@ +discard """ + output: ''' +1 +2 +3 +4 +2 +88 +timplicit done +''' +""" + + +for x in [1, 2, 3, 4]: + echo x + + +type + TValue* {.pure, final.} = object of RootObj + a: int + PValue = ref TValue + PPValue = ptr PValue + + +var x: PValue +new x +var sp: PPValue = addr x + +sp.a = 2 +if sp.a == 2: echo 2 # with sp[].a the error is gone + +# Test the new auto-deref a little + +{.experimental.} + +proc p(x: var int; y: int) = x += y + +block: + var x: ref int + new(x) + + x.p(44) + + var indirect = p + x.indirect(44) + + echo x[] + + echo "timplicit done" diff --git a/tests/import/buzz/m21496.nim b/tests/import/buzz/m21496.nim deleted file mode 100644 index 7c87e2c04..000000000 --- a/tests/import/buzz/m21496.nim +++ /dev/null @@ -1 +0,0 @@ -proc fb* = echo "buzz!" \ No newline at end of file diff --git a/tests/import/fizz/m21496.nim b/tests/import/fizz/m21496.nim deleted file mode 100644 index 834c11eae..000000000 --- a/tests/import/fizz/m21496.nim +++ /dev/null @@ -1 +0,0 @@ -proc fb* = echo "fizz!" \ No newline at end of file diff --git a/tests/import/t21496.nim b/tests/import/t21496.nim deleted file mode 100644 index 568f2ac51..000000000 --- a/tests/import/t21496.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - errormsg: "redefinition of 'm21496'; previous declaration here: t21496.nim(5, 12)" -""" - -import fizz/m21496, buzz/m21496 - -# bug #21496 - -m21496.fb() diff --git a/tests/importalls/m1.nim b/tests/importalls/m1.nim deleted file mode 100644 index b6ccbf5da..000000000 --- a/tests/importalls/m1.nim +++ /dev/null @@ -1,70 +0,0 @@ -import ./m2 -import ./m3 {.all.} as m3 -from ./m3 as m3Bis import nil - -doAssert m3h2 == 2 -export m3h2 - -export m3Bis.m3p1 - -const foo0* = 2 -const foo1 = bar1 - -const foo1Aux = 2 -export foo1Aux - -doAssert not declared(bar2) -doAssert not compiles(bar2) - -var foo2 = 2 -let foo3 = 2 - -type Foo4 = enum - kg1, kg2 - -type Foo4b {.pure.} = enum - foo4b1, foo4b2 - -type Foo5 = object - z1: string - z2: Foo4 - z3: int - z4*: int - -proc `z3`*(a: Foo5): auto = - a.z3 * 10 - -proc foo6(): auto = 2 -proc foo6b*(): auto = 2 -template foo7: untyped = 2 -macro foo8(): untyped = discard -template foo9(a: int) = discard - -block: - template foo10: untyped = 2 - type Foo11 = enum - kg1b, kg2b - proc foo12(): auto = 2 - -proc initFoo5*(z3: int): Foo5 = Foo5(z3: z3) - -func foo13(): auto = 2 -iterator foo14a(): int = discard -iterator foo14b*(): int = discard -iterator foo14c(): int {.closure.} = discard -iterator foo14d(): int {.inline.} = discard - -# fwd declare -proc foo15(): int -proc foo15(): int = 2 - -proc foo16*(): int -proc foo16(): int = 2 - -proc foo17*(): int -proc foo17*(): int = 2 - -# other -type A1 = distinct int -type A2 = distinct int -converter foo18(x: A1): A2 = discard diff --git a/tests/importalls/m2.nim b/tests/importalls/m2.nim deleted file mode 100644 index 186650f68..000000000 --- a/tests/importalls/m2.nim +++ /dev/null @@ -1,3 +0,0 @@ -const bar1* = 2 -const bar2 = 2 -const bar3 = 3 diff --git a/tests/importalls/m3.nim b/tests/importalls/m3.nim deleted file mode 100644 index caeae2a01..000000000 --- a/tests/importalls/m3.nim +++ /dev/null @@ -1,5 +0,0 @@ -# xxx use below naming convention in other test files: p: public, h: hidden -const m3p1* = 2 -const m3h2 = 2 -const m3h3 = 3 -const m3h4 = 4 diff --git a/tests/importalls/m4.nim b/tests/importalls/m4.nim deleted file mode 100644 index b682b766a..000000000 --- a/tests/importalls/m4.nim +++ /dev/null @@ -1,10 +0,0 @@ -{.warning[UnusedImport]: off.} # xxx bug: this shouldn't be needed since we have `export m3` -import ./m3 {.all.} -import ./m3 as m3b -export m3b -export m3h3 -export m3.m3h4 - -import ./m2 {.all.} as m2b -export m2b except bar3 - diff --git a/tests/importalls/mt0.nim b/tests/importalls/mt0.nim deleted file mode 100644 index 154d30ef9..000000000 --- a/tests/importalls/mt0.nim +++ /dev/null @@ -1,9 +0,0 @@ -import ./m1 as m -doAssert compiles(foo0) -doAssert not compiles(foo1) -doAssert foo6b() == 2 -doAssert m3h2 == 2 - -var f = initFoo5(z3=3) -doAssert f.z3 == 30 -doAssert z3(f) == 30 diff --git a/tests/importalls/mt1.nim b/tests/importalls/mt1.nim deleted file mode 100644 index b7983945c..000000000 --- a/tests/importalls/mt1.nim +++ /dev/null @@ -1,23 +0,0 @@ -import ./m1 {.all.} as m -doAssert foo1 == 2 -doAssert m.foo1 == 2 - -doAssert m.m3h2 == 2 -doAssert m3h2 == 2 -doAssert m.foo1Aux == 2 -doAssert m.m3p1 == 2 - -## field access -import std/importutils -privateAccess(Foo5) -var x = Foo5(z1: "foo", z2: m.kg1) -doAssert x.z1 == "foo" - -var f0: Foo5 -f0.z3 = 3 -doAssert f0.z3 == 3 -var f = initFoo5(z3=3) -doAssert f.z3 == 3 -doAssert z3(f) == 30 -doAssert m.z3(f) == 30 -doAssert not compiles(mt1.`z3`(f)) # z3 is an imported symbol diff --git a/tests/importalls/mt2.nim b/tests/importalls/mt2.nim deleted file mode 100644 index 52edbb69e..000000000 --- a/tests/importalls/mt2.nim +++ /dev/null @@ -1,104 +0,0 @@ -from ./m1 {.all.} as r1 import foo1 -from ./m1 {.all.} as r2 import foo7 - -block: # different symbol kinds - doAssert foo1 == 2 - doAssert r1.foo1 == 2 - doAssert r1.foo2 == 2 - doAssert compiles(foo1) - doAssert compiles(r1.foo2) - doAssert not compiles(foo2) - doAssert not compiles(m3h2) - doAssert r1.foo3 == 2 - - block: # enum - var a: r1.Foo4 - let a1 = r1.kg1 - doAssert a1 == r1.Foo4.kg1 - type A = r1.Foo4 - doAssert a1 == A.kg1 - doAssert not compiles(kg1) - doAssert compiles(A.kg1) - var witness = false - for ai in r1.Foo4: - doAssert ai == a - doAssert ai == a1 - witness = true - break - doAssert witness - - block: # {.pure.} enum - var a: r1.Foo4b - doAssert not compiles(r1.foo4b1) # because pure - doAssert not compiles(foo4b1) - let a1 = r1.Foo4b.foo4b1 - doAssert a1 == a - type A = r1.Foo4b - doAssert a1 == A.foo4b1 - var witness = false - for ai in A: - doAssert ai == a - doAssert ai == a1 - witness = true - break - doAssert witness - - block: # object - doAssert compiles(r1.Foo5) - var a: r1.Foo5 - doAssert compiles(a.z4) - doAssert not compiles(a.z3) - - block: # remaining symbol kinds - doAssert r1.foo6() == 2 - doAssert r1.foo6b() == 2 - doAssert foo7() == 2 - doAssert r2.foo6b() == 2 - - r1.foo8() - r1.foo9(1) - doAssert r1.foo13() == 2 - for a in r1.foo14a(): discard - for a in r1.foo14b(): discard - for a in r1.foo14c(): discard - for a in r1.foo14d(): discard - doAssert r1.foo15() == 2 - doAssert r1.foo16() == 2 - doAssert r1.foo17() == 2 - doAssert compiles(r1.foo18) - doAssert declared(r1.foo18) - - block: # declarations at block scope should not be visible - doAssert declared(foo7) - doAssert declared(r1.foo6) - doAssert not declared(foo10) - doAssert not declared(foo6) - doAssert not declared(r1.Foo11) - doAssert not declared(r1.kg1b) - doAssert not declared(r1.foo12) - doAssert not compiles(r1.foo12()) - -## field access -import std/importutils -privateAccess(r1.Foo5) -var x = r1.Foo5(z1: "foo", z2: r1.kg1) -doAssert x.z1 == "foo" - -var f0: r1.Foo5 -f0.z3 = 3 -doAssert f0.z3 == 3 -var f = r1.initFoo5(z3=3) -doAssert f.z3 == 3 -doAssert r1.z3(f) == 30 - -import ./m1 as r3 -doAssert not declared(foo2) -doAssert not declared(r3.foo2) - -from ./m1 {.all.} as r4 import nil -doAssert not declared(foo2) -doAssert declared(r4.foo2) - -from ./m1 {.all.} import nil -doAssert not declared(foo2) -doAssert declared(m1.foo2) diff --git a/tests/importalls/mt3.nim b/tests/importalls/mt3.nim deleted file mode 100644 index 2bd7fd79d..000000000 --- a/tests/importalls/mt3.nim +++ /dev/null @@ -1,12 +0,0 @@ -import ./m1 {.all.} - # D20201209T194412:here keep this as is, without `as`, so that mt8.nim test keeps - # checking that the original module symbol for `m1` isn't modified and that - # only the alias in `createModuleAlias` is affected. -doAssert declared(m1.foo1) -doAssert foo1 == 2 - - -doAssert m1.foo1 == 2 - -doAssert not compiles(mt3.foo0) # foo0 is an imported symbol -doAssert not compiles(mt3.foo1) # ditto diff --git a/tests/importalls/mt4.nim b/tests/importalls/mt4.nim deleted file mode 100644 index cd7d32aad..000000000 --- a/tests/importalls/mt4.nim +++ /dev/null @@ -1,4 +0,0 @@ -import ./m1 {.all.} except foo1 -doAssert foo2 == 2 -doAssert declared(foo2) -doAssert not compiles(foo1) diff --git a/tests/importalls/mt4b.nim b/tests/importalls/mt4b.nim deleted file mode 100644 index 4578bc54c..000000000 --- a/tests/importalls/mt4b.nim +++ /dev/null @@ -1,3 +0,0 @@ -from ./m1 {.all.} as m2 import nil -doAssert not compiles(foo1) -doAssert m2.foo1 == 2 diff --git a/tests/importalls/mt5.nim b/tests/importalls/mt5.nim deleted file mode 100644 index 5c5785af6..000000000 --- a/tests/importalls/mt5.nim +++ /dev/null @@ -1,9 +0,0 @@ -import ./m1 {.all.} as m2 except foo1 -doAssert foo2 == 2 -doAssert not compiles(foo1) -doAssert m2.foo1 == 2 -doAssert compiles(m2.foo1) - -from system {.all.} as s import ThisIsSystem -doAssert ThisIsSystem -doAssert s.ThisIsSystem diff --git a/tests/importalls/mt6.nim b/tests/importalls/mt6.nim deleted file mode 100644 index ac6f542b6..000000000 --- a/tests/importalls/mt6.nim +++ /dev/null @@ -1,13 +0,0 @@ -import ./m1 {.all.} as m2 -doAssert compiles(foo1) -doAssert compiles(m2.foo1) -doAssert declared(foo1) -doAssert declared(m2.foo0) # public: works fine - -doAssert m2.foo1 == 2 -doAssert declared(m2.foo1) -doAssert not declared(m2.nonexistent) - -# also tests the quoted `""` import -import "."/"m1" {.all.} as m1b -doAssert compiles(m1b.foo1) diff --git a/tests/importalls/mt7.nim b/tests/importalls/mt7.nim deleted file mode 100644 index 45a664610..000000000 --- a/tests/importalls/mt7.nim +++ /dev/null @@ -1,14 +0,0 @@ -include ./m1 -doAssert compiles(foo1) -doAssert compiles(mt7.foo1) -doAssert declared(foo1) -doAssert declared(mt7.foo1) -doAssert declared(mt7.foo0) - -var f0: Foo5 -f0.z3 = 3 -doAssert f0.z3 == 3 -var f = initFoo5(z3=3) -doAssert f.z3 == 3 -doAssert mt7.z3(f) == 30 -doAssert z3(f) == 30 diff --git a/tests/importalls/mt8.nim b/tests/importalls/mt8.nim deleted file mode 100644 index f484e7b01..000000000 --- a/tests/importalls/mt8.nim +++ /dev/null @@ -1,23 +0,0 @@ -#[ -test multiple imports -]# - -{.warning[UnusedImport]: off.} -import ./m1, m2 {.all.}, ./m3 {.all.} - # make sure this keeps using `import ./m1` without as. - -# m1 is regularly imported -doAssert declared(m1.foo0) -doAssert declared(foo0) - -doAssert not declared(m1.foo1) - # if we didn't call `createModuleAlias` even for `import f1 {.all.}`, - # this would fail, see D20201209T194412. - -# m2 -doAssert declared(m2.bar2) -doAssert declared(bar2) - -# m3 -doAssert declared(m3.m3h2) -doAssert declared(m3h2) diff --git a/tests/importalls/mt9.nim b/tests/importalls/mt9.nim deleted file mode 100644 index 42d48ecbd..000000000 --- a/tests/importalls/mt9.nim +++ /dev/null @@ -1,12 +0,0 @@ -# tests re-export of a module with import {.all.} - -import ./m4 - -doAssert m3p1 == 2 -doAssert not declared(m3h2) -doAssert m3h3 == 3 -doAssert m3h4 == 4 - -doAssert bar1 == 2 -doAssert bar2 == 2 -doAssert not declared(bar3) diff --git a/tests/importalls/tmain2.nim b/tests/importalls/tmain2.nim deleted file mode 100644 index 596f0f135..000000000 --- a/tests/importalls/tmain2.nim +++ /dev/null @@ -1,7 +0,0 @@ -discard """ -joinable: false # for clarity, but not necessary -""" - -{.warning[UnusedImport]: off.} -# only import `mt*.nim` here; these depend on `m*.nim` -import "."/[mt0,mt1,mt2,mt3,mt4,mt4b,mt5,mt6,mt7,mt8,mt9] diff --git a/tests/init/tinitchecks_v2.nim b/tests/init/tinitchecks_v2.nim deleted file mode 100644 index 4a8cda028..000000000 --- a/tests/init/tinitchecks_v2.nim +++ /dev/null @@ -1,59 +0,0 @@ -discard """ -cmd: "nim check $file" -action: "compile" -""" - -{.experimental: "strictDefs".} - -proc myopen(f: out File; s: string): bool = - f = default(File) - result = false - -proc main = - var f: File - if myopen(f, "aarg"): - f.close - -proc invalid = - var s: seq[string] - s.add "abc" #[tt.Warning - ^ use explicit initialization of 's' for clarity [Uninit] ]# - -proc valid = - var s: seq[string] = @[] - s.add "abc" # valid! - -main() -invalid() -valid() - -proc branchy(cond: bool) = - var s: seq[string] - if cond: - s = @["y"] - else: - s = @[] - s.add "abc" # valid! - -branchy true - -proc p(x: out int; y: out string; cond: bool) = #[tt.Warning - ^ Cannot prove that 'y' is initialized. This will become a compile time error in the future. [ProveInit] ]# - x = 4 - if cond: - y = "abc" - # error: not every path initializes 'y' - -var gl: int -var gs: string -p gl, gs, false - -proc canRaise(x: int): int = - result = x - raise newException(ValueError, "wrong") - -proc currentlyValid(x: out int; y: out string; cond: bool) = - x = canRaise(45) - y = "abc" # <-- error: not every path initializes 'y' - -currentlyValid gl, gs, false diff --git a/tests/init/tlet.nim b/tests/init/tlet.nim deleted file mode 100644 index e32bedb18..000000000 --- a/tests/init/tlet.nim +++ /dev/null @@ -1,55 +0,0 @@ -{.experimental: "strictDefs".} - -proc bar(x: out string) = - x = "abc" - -template moe = # bug #21043 - try: - discard - except ValueError as e: - echo(e.msg) - -template moe0 {.dirty.} = # bug #21043 - try: - discard - except ValueError as e: - echo(e.msg) - -proc foo() = - block: - let x: string - if true: - x = "abc" - else: - x = "def" - doAssert x == "abc" - block: - let y: string - bar(y) - doAssert y == "abc" - block: - let x: string - if true: - x = "abc" - discard "abc" - else: - x = "def" - discard "def" - doAssert x == "abc" - block: # - let x {.used.} : int - block: # - let x: float - x = 1.234 - doAssert x == 1.234 - - block: - try: - discard - except ValueError as e: - echo(e.msg) - moe() - moe0() - -static: foo() -foo() diff --git a/tests/init/tlet_uninit2.nim b/tests/init/tlet_uninit2.nim deleted file mode 100644 index 174aa28f7..000000000 --- a/tests/init/tlet_uninit2.nim +++ /dev/null @@ -1,5 +0,0 @@ -discard """ - errormsg: "'let' symbol requires an initialization" -""" - -let x: int \ No newline at end of file diff --git a/tests/init/tlet_uninit3.nim b/tests/init/tlet_uninit3.nim deleted file mode 100644 index cb786f8c8..000000000 --- a/tests/init/tlet_uninit3.nim +++ /dev/null @@ -1,24 +0,0 @@ -discard """ - cmd: "nim check $file" - action: "reject" - nimout: ''' -tlet_uninit3.nim(13, 5) Error: 'let' symbol requires an initialization -tlet_uninit3.nim(19, 5) Error: 'x' cannot be assigned to -tlet_uninit3.nim(23, 11) Error: 'let' symbol requires an initialization -''' -""" - -{.experimental: "strictDefs".} - -let global {.used.}: int - -proc foo() = - block: - let x: int - x = 13 - x = 14 - - block: - let x: int - doAssert x == 0 -foo() diff --git a/tests/init/tlet_uninit4.nim b/tests/init/tlet_uninit4.nim deleted file mode 100644 index c57d15ff8..000000000 --- a/tests/init/tlet_uninit4.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ - errormsg: "type mismatch: got " -""" - -{.experimental: "strictDefs".} - -proc foo(x: var string) = - echo x - -proc bar() = - let x: string - foo(x) diff --git a/tests/init/toutparam_subtype.nim b/tests/init/toutparam_subtype.nim deleted file mode 100644 index 3597f1459..000000000 --- a/tests/init/toutparam_subtype.nim +++ /dev/null @@ -1,24 +0,0 @@ -discard """ -cmd: "nim check $file" -action: "compile" -errormsg: "type mismatch: got " -line: 21 -""" - -{.experimental: "strictDefs".} - -type - Superclass[T] = object of RootObj - a: T - Subclass[T] = object of Superclass[T] - s: string - -proc init[T](x: out Superclass[T]) = - x = Superclass(a: 8) - -proc subtypeCheck = - var v: Subclass[int] - init(v) - echo v.s # the 's' field was never initialized! - -subtypeCheck() diff --git a/tests/init/tuninit1.nim b/tests/init/tuninit1.nim index ac3007e8d..fe91733ff 100644 --- a/tests/init/tuninit1.nim +++ b/tests/init/tuninit1.nim @@ -1,10 +1,11 @@ discard """ - nimout: "tuninit1.nim(34, 11) Warning: use explicit initialization of 'y' for clarity [Uninit]" + nimout: "Warning: use explicit initialization of 'y' for clarity [Uninit]" + line:34 action: compile """ import strutils -{.experimental: "strictDefs".} + {.warning[Uninit]:on.} proc p = diff --git a/tests/isolate/tisolate.nim b/tests/isolate/tisolate.nim index a0e71e321..9f3b15441 100644 --- a/tests/isolate/tisolate.nim +++ b/tests/isolate/tisolate.nim @@ -1,17 +1,12 @@ discard """ errormsg: "expression cannot be isolated: select(a, b)" - line: 39 + line: 34 """ import std / isolation import json, streams -proc myParseJson(s: Stream; filename: string): JsonNode = - {.cast(noSideEffect).}: - result = parseJson(s, filename) - - proc f(): seq[int] = @[1, 2, 3] @@ -33,7 +28,7 @@ proc main = discard isolate select(Node(x: "a"), nil) discard isolate select(Node(x: "a"), Node(x: "b")) - discard isolate myParseJson(newFileStream("my.json"), "my.json") + discard isolate parseJson(newFileStream("my.json"), "my.json") var a, b: Node discard isolate select(a, b) diff --git a/tests/isolate/tisolate2.nim b/tests/isolate/tisolate2.nim deleted file mode 100644 index 9bf92d82e..000000000 --- a/tests/isolate/tisolate2.nim +++ /dev/null @@ -1,22 +0,0 @@ -discard """ - errormsg: "expression cannot be isolated: a_to_b(a)" - line: 22 -""" - -# bug #19013 -import std/isolation - -type Z = ref object - i: int - -type A = object - z: Z - -type B = object - z: Z - -func a_to_b(a: A): B = - result = B(z: a.z) - -let a = A(z: Z(i: 3)) -let b = isolate(a_to_b(a)) diff --git a/tests/iter/t1550.nim b/tests/iter/t1550.nim deleted file mode 100644 index 8ad96f0da..000000000 --- a/tests/iter/t1550.nim +++ /dev/null @@ -1,20 +0,0 @@ -type - A[T] = iterator(x: T): T {.gcsafe, closure.} - -iterator aimp[T](x: T): T {.gcsafe, closure.} = - var total = 0 - while (total < 100): - yield total - total += x - -iterator bimp(y: A[int], z:int): int {.gcsafe, closure.} = - for i in y(z): - yield i - -for x in aimp[int](3): - discard x - -var y = aimp[int] -var z = bimp -for x in z(y, 1): - discard x \ No newline at end of file diff --git a/tests/iter/t16076.nim b/tests/iter/t16076.nim deleted file mode 100644 index 2eb409068..000000000 --- a/tests/iter/t16076.nim +++ /dev/null @@ -1,45 +0,0 @@ -discard """ - targets: "c js" -""" - -proc main() = - block: # bug #17485 - type - O = ref object - i: int - - iterator t(o: O): int = - if o != nil: - yield o.i - yield 0 - - proc m = - var data = "" - for i in t(nil): - data.addInt i - - doAssert data == "0" - - m() - - - block: # bug #16076 - type - R = ref object - z: int - - var data = "" - - iterator foo(x: int; y: R = nil): int {.inline.} = - if y == nil: - yield x - else: - yield y.z - - for b in foo(10): - data.addInt b - - doAssert data == "10" - -static: main() -main() diff --git a/tests/iter/t21306.nim b/tests/iter/t21306.nim deleted file mode 100644 index 43fea9c80..000000000 --- a/tests/iter/t21306.nim +++ /dev/null @@ -1,114 +0,0 @@ -# bug #21306 -type - FutureState {.pure.} = enum - Pending, Finished, Cancelled, Failed - - FutureBase = ref object of RootObj - state: FutureState - error: ref CatchableError - id: uint - - Future[T] = ref object of FutureBase - closure: iterator(f: Future[T]): FutureBase {.raises: [Defect, CatchableError, Exception], gcsafe.} - value: T - -template setupFutureBase() = - new(result) - result.state = FutureState.Pending - -proc newFutureImpl[T](): Future[T] = - setupFutureBase() - -template newFuture[T](fromProc: static[string] = ""): Future[T] = - newFutureImpl[T]() - -proc internalRead[T](fut: Future[T]): T = - when T isnot void: - return fut.value - -template await[T](f: Future[T]): untyped = - when declared(chronosInternalRetFuture): - when not declaredInScope(chronosInternalTmpFuture): - var chronosInternalTmpFuture {.inject.}: FutureBase = f - else: - chronosInternalTmpFuture = f - - yield chronosInternalTmpFuture - - when T isnot void: - cast[type(f)](chronosInternalTmpFuture).internalRead() - -type - VerifierError {.pure.} = enum - Invalid - MissingParent - UnviableFork - Duplicate - ProcessingCallback = proc() {.gcsafe, raises: [Defect].} - BlockVerifier = - proc(signedBlock: int): - Future[VerifierError] {.gcsafe, raises: [Defect].} - - SyncQueueKind {.pure.} = enum - Forward, Backward - - SyncRequest[T] = object - kind: SyncQueueKind - index: uint64 - slot: uint64 - count: uint64 - item: T - - SyncResult[T] = object - request: SyncRequest[T] - data: seq[ref int] - - SyncQueue[T] = ref object - kind: SyncQueueKind - readyQueue: seq[SyncResult[T]] - blockVerifier: BlockVerifier - -iterator blocks[T](sq: SyncQueue[T], - sr: SyncResult[T]): ref int = - case sq.kind - of SyncQueueKind.Forward: - for i in countup(0, len(sr.data) - 1): - yield sr.data[i] - of SyncQueueKind.Backward: - for i in countdown(len(sr.data) - 1, 0): - yield sr.data[i] - -proc push[T](sq: SyncQueue[T]; sr: SyncRequest[T]; data: seq[ref int]; - processingCb: ProcessingCallback = nil): Future[void] {. - stackTrace: off, gcsafe.} = - iterator push_436208182(chronosInternalRetFuture: Future[void]): FutureBase {. - closure, gcsafe, raises: [Defect, CatchableError, Exception].} = - block: - template result(): auto {.used.} = - {.fatal: "You should not reference the `result` variable inside" & - " a void async proc".} - - let item = default(SyncResult[T]) - for blk in sq.blocks(item): - let res = await sq.blockVerifier(blk[]) - - var resultFuture = newFuture[void]("push") - resultFuture.closure = push_436208182 - return resultFuture - -type - SomeTPeer = ref object - score: int - -proc getSlice(): seq[ref int] = - discard - -template smokeTest(kkind: SyncQueueKind, start, finish: uint64, - chunkSize: uint64) = - var queue: SyncQueue[SomeTPeer] - var request: SyncRequest[SomeTPeer] - discard queue.push(request, getSlice()) - -for k in {SyncQueueKind.Forward}: - for item in [(uint64(1181), uint64(1399), 41'u64)]: - smokeTest(k, item[0], item[1], item[2]) \ No newline at end of file diff --git a/tests/iter/tarrayiter.nim b/tests/iter/tarrayiter.nim deleted file mode 100644 index eb7ba591a..000000000 --- a/tests/iter/tarrayiter.nim +++ /dev/null @@ -1,14 +0,0 @@ -block: - iterator `[]`(a: int, r: int): int = - for q in 0 .. r: - yield a - - for val in 10[2]: discard - - type Custom = distinct string - - iterator `[]`(a: Custom, r: int): char = - for q in 0 .. r: - yield a.string[q] - - for val in Custom("test")[2]: discard \ No newline at end of file diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim index 85611373c..0ee8e81cc 100644 --- a/tests/iter/tclosureiters.nim +++ b/tests/iter/tclosureiters.nim @@ -21,15 +21,6 @@ discard """ 2 70 0 -(1, 1) -(1, 2) -(1, 3) -(2, 1) -(2, 2) -(2, 3) -(3, 1) -(3, 2) -(3, 3) ''' """ @@ -107,7 +98,7 @@ proc unused = iterator lineIter2*(filename: string): string {.closure.} = var f = open(filename, bufSize=8000) defer: close(f) # <-- commenting defer "solves" the problem - var res = newStringOfCap(80) + var res = TaintedString(newStringOfCap(80)) while f.readLine(res): yield res proc unusedB = @@ -161,12 +152,3 @@ var love = iterator: int {.closure.} = for i in love(): echo i - -# bug #18474 -iterator pairs(): (int, int) {.closure.} = - for i in 1..3: - for j in 1..3: - yield (i, j) - -for pair in pairs(): - echo pair diff --git a/tests/iter/titer.nim b/tests/iter/titer.nim index 3a2450ae3..8e1c13e82 100644 --- a/tests/iter/titer.nim +++ b/tests/iter/titer.nim @@ -62,53 +62,3 @@ type Rule[T] = (int, T) var t: seq[Rule[int]] for (c, t) in t: discard - - - -import std/sugar - -# bug #14165 -iterator log_nodups_hamming(): int {.inline.} = - let lb3 = 1 - let lb4 = 123 - proc mul3(): int = lb3 + lb4 - yield mul3() - -for h in log_nodups_hamming(): - break -for h in log_nodups_hamming(): - break -for h in log_nodups_hamming(): - break - -# bug #18536 -iterator envPairs*(): int = - var foo: seq[int] - proc fun() = - foo = @[] - fun() - yield 3 - -proc main() = - for a in envPairs(): - discard - for a in envPairs(): - discard -static: main() -main() - -# bug #6269 -iterator makeFn(outer_val: int): proc(a: int): int = - for i in 0..1: - yield proc(a:int): int = - return a + i.int - -let v1 = 42 - -let res = collect: - for fn1 in makeFn(v1): - let v2 = fn1(v1) - for fn2 in makeFn(v2): - fn2(v2) - -doAssert res == @[42, 43, 43, 44] diff --git a/tests/iter/titer13.nim b/tests/iter/titer13.nim index 086c40ca4..0d4a399c5 100644 --- a/tests/iter/titer13.nim +++ b/tests/iter/titer13.nim @@ -72,7 +72,7 @@ block: echo a block t5859: - proc flatIterator[T](s: openArray[T]): auto {.noSideEffect.}= + proc flatIterator[T](s: openarray[T]): auto {.noSideEffect.}= result = iterator(): auto = when (T is not seq|array): for item in s: diff --git a/tests/iter/titer2.nim b/tests/iter/titer2.nim index 975cc786c..f60aed73a 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*[A, B] {.final.} = object + TTable* {.final.}[A, B] = object data: TKeyValuePairSeq[A, B] counter: int diff --git a/tests/iter/titer3.nim b/tests/iter/titer3.nim index defd56c98..9dcfd7be5 100644 --- a/tests/iter/titer3.nim +++ b/tests/iter/titer3.nim @@ -26,7 +26,7 @@ var x = [[1, 2, 3], [4, 5, 6]] for y in iter1(x[0]): write(stdout, $y) writeLine(stdout, "") -# ensure closure and inline iterators have the same behaviour regarding +# ensure closure and inline iterators have the same behaviour wrt # parameter passing iterator clo(a: int): int {.closure.} = diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim index adba8a8e3..4a76b2fb0 100644 --- a/tests/iter/titer_issues.nim +++ b/tests/iter/titer_issues.nim @@ -29,19 +29,6 @@ end 9018 @[1, 2] @[1, 2, 3] -1 -nested finally -outer finally -nested finally -outer finally -nested finally -outer finally -nested finally -outer finally -In defer -trying -exception caught -finally block ''' """ @@ -148,7 +135,7 @@ block t3837_chained: block t3221_complex: - iterator permutations[T](ys: openArray[T]): seq[T] = + iterator permutations[T](ys: openarray[T]): seq[T] = var d = 1 c = newSeq[int](ys.len) @@ -241,7 +228,7 @@ block t2023_objiter: block: # bug #13739 - iterator myIter(arg: openArray[int]): int = + iterator myIter(arg: openarray[int]): int = var tmp = 0 let len = arg.len while tmp < len: @@ -264,131 +251,3 @@ block: for x in ff(@[1, 2], @[1, 2, 3]): echo x - - -# bug #19575 - -iterator bb() {.closure.} = - while true: - try: discard - except: break - finally: break - -var a = bb - -iterator cc() {.closure.} = - while true: - try: discard - except: - if true: - break - finally: - if true: - 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 - -# bug #18824 -iterator poc_iterator: int {.closure.} = - block bug18824: - try: - break bug18824 - finally: - echo "In defer" - -for _ in poc_iterator(): - discard - -# bug #20624 -iterator tryFinally() {.closure.} = - block route: - try: - echo "trying" - raise - except: - echo "exception caught" - break route - finally: - echo "finally block" - -var x = tryFinally -x() diff --git a/tests/iter/tmoditer.nim b/tests/iter/tmoditer.nim index 99e5b642d..b92a416fb 100644 --- a/tests/iter/tmoditer.nim +++ b/tests/iter/tmoditer.nim @@ -43,7 +43,7 @@ proc `=copy`(dst: var NonCopyable, src: NonCopyable) {.error.} proc `=sink`(dst: var NonCopyable, src: NonCopyable) = dst.x = src.x -iterator lentItems[T](a: openArray[T]): lent T = +iterator lentItems[T](a: openarray[T]): lent T = for i in 0..a.high: yield a[i] diff --git a/tests/iter/tpermutations.nim b/tests/iter/tpermutations.nim index 30a66460f..c5067ba31 100644 --- a/tests/iter/tpermutations.nim +++ b/tests/iter/tpermutations.nim @@ -12,7 +12,7 @@ perm: 6778800.0 det: 0.0 import sequtils, sugar -iterator permutations*[T](ys: openArray[T]): tuple[perm: seq[T], sign: int] = +iterator permutations*[T](ys: openarray[T]): tuple[perm: seq[T], sign: int] = var d = 1 c = newSeq[int](ys.len) diff --git a/tests/iter/tshallowcopy_closures.nim b/tests/iter/tshallowcopy_closures.nim index 06b04a788..279e7d950 100644 --- a/tests/iter/tshallowcopy_closures.nim +++ b/tests/iter/tshallowcopy_closures.nim @@ -1,5 +1,4 @@ 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 47ba6efa6..35df55c24 100644 --- a/tests/iter/tyieldintry.nim +++ b/tests/iter/tyieldintry.nim @@ -1,16 +1,17 @@ discard """ - matrix: "; --experimental:strictdefs" - targets: "c cpp" +targets: "c" +output: "ok" """ - var closureIterResult = newSeq[int]() +# XXX Investigate why this fails now for 'nim cpp' + proc checkpoint(arg: int) = closureIterResult.add(arg) type - TestError = object of CatchableError - AnotherError = object of CatchableError + TestException = object of Exception + AnotherException = object of Exception proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) = closureIterResult.setLen(0) @@ -20,7 +21,7 @@ proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedRe try: for i in it(): closureIterResult.add(i) - except TestError: + except TestException: exceptionCaught = true if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected: @@ -38,8 +39,8 @@ proc test(it: iterator(): int, expectedResults: varargs[int]) = proc testExc(it: iterator(): int, expectedResults: varargs[int]) = testClosureIterAux(it, true, expectedResults) -proc raiseTestError() = - raise newException(TestError, "Test exception!") +proc raiseException() = + raise newException(TestException, "Test exception!") block: iterator it(): int {.closure.} = @@ -57,8 +58,8 @@ block: yield 0 try: checkpoint(1) - raiseTestError() - except TestError: + raiseException() + except TestException: checkpoint(2) yield 3 checkpoint(4) @@ -88,7 +89,7 @@ block: yield 0 try: yield 1 - raiseTestError() + raiseException() yield 2 finally: checkpoint(3) @@ -102,8 +103,8 @@ block: iterator it(): int {.closure.} = try: try: - raiseTestError() - except AnotherError: + raiseException() + except AnotherException: yield 123 finally: checkpoint(3) @@ -116,8 +117,8 @@ block: iterator it(): int {.closure.} = try: yield 1 - raiseTestError() - except AnotherError: + raiseException() + except AnotherException: checkpoint(123) finally: checkpoint(2) @@ -133,12 +134,12 @@ block: yield 1 try: yield 2 - raiseTestError() - except AnotherError: + raiseException() + except AnotherException: yield 123 finally: yield 3 - except AnotherError: + except AnotherException: yield 124 finally: yield 4 @@ -169,10 +170,10 @@ block: try: try: yield 0 - raiseTestError() + raiseException() finally: checkpoint(1) - except TestError: + except TestException: yield 2 return finally: @@ -187,10 +188,10 @@ block: try: try: yield 0 - raiseTestError() + raiseException() finally: return # Return in finally should stop exception propagation - except AnotherError: + except AnotherException: yield 2 return finally: @@ -227,9 +228,9 @@ block: var foo = 123 let i = try: yield 0 - raiseTestError() + raiseException() 1 - except TestError as e: + except TestException as e: assert(e.msg == "Test exception!") case foo of 1: @@ -261,9 +262,9 @@ block: template tryexcept: int = try: yield 1 - raiseTestError() + raiseException() 123 - except TestError: + except TestException: yield 2 checkpoint(3) 4 @@ -322,11 +323,11 @@ block: for i in 0 .. 1: try: yield 1 - raiseTestError() - except TestError as e: + raiseException() + except TestException as e: doAssert(e.msg == "Test exception!") yield 2 - except AnotherError: + except AnotherException: yield 123 except: yield 1234 @@ -482,25 +483,5 @@ block: # nnkChckRange test(it, 1, 2, 3) -block: #17849 - yield in case subject - template yieldInCase: int = - yield 2 - 3 +echo "ok" - iterator it(): int {.closure.} = - yield 1 - case yieldInCase() - of 1: checkpoint(11) - of 3: checkpoint(13) - else: checkpoint(14) - 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/readme.md b/tests/js/readme.md deleted file mode 100644 index 7fcc722fa..000000000 --- a/tests/js/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -## notes -Prefer moving tests to a non-js directory so that they get tested across all backends automatically. -Ideally, tests/js should be reserved to code that only makes sense in js. - -Note also that tests for a js specific module (e.g.: `std/jsbigints`) belong to `tests/stdlib`, (e.g.: `tests/stdlib/tjsbigints.nim`) diff --git a/tests/js/t11166.nim b/tests/js/t11166.nim index e98ccda10..8dd77925c 100644 --- a/tests/js/t11166.nim +++ b/tests/js/t11166.nim @@ -1,20 +1,4 @@ -discard """ - output: ''' -test1 -test2 -''' -""" - import jsffi -type - C = object - props: int - -var c: C - when compiles(c.props): - echo "test1" - -when not compiles(d.props): - echo "test2" + echo "test" diff --git a/tests/js/t17177.nim b/tests/js/t17177.nim deleted file mode 100644 index fc362cec1..000000000 --- a/tests/js/t17177.nim +++ /dev/null @@ -1,10 +0,0 @@ -import std/asyncjs - -proc fn1(n: int): Future[int] {.async.} = return n -proc main2() = - proc fn2(n: int): Future[int] {.async.} = return n -proc main3(a: auto) = - proc fn3(n: int): Future[int] {.async.} = return n -proc main4() {.async.} = - proc fn4(n: int): Future[int] {.async.} = return n - discard diff --git a/tests/js/t20233.nim b/tests/js/t20233.nim deleted file mode 100644 index 401d14122..000000000 --- a/tests/js/t20233.nim +++ /dev/null @@ -1,7 +0,0 @@ -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/t20235.nim b/tests/js/t20235.nim deleted file mode 100644 index 3a69c2bd6..000000000 --- a/tests/js/t20235.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - action: "run" - output: "0 4" -""" - -proc main = - var s = "" - s.setLen(4) - echo s[0].ord, " ", s.len - -main() diff --git a/tests/js/t21209.nim b/tests/js/t21209.nim deleted file mode 100644 index 4de34f035..000000000 --- a/tests/js/t21209.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - action: "compile" - cmd: "nim check --warning[UnusedImport]:off $file" -""" - -import std/times diff --git a/tests/js/t21209.nims b/tests/js/t21209.nims deleted file mode 100644 index 318e28f97..000000000 --- a/tests/js/t21209.nims +++ /dev/null @@ -1 +0,0 @@ ---b:js \ No newline at end of file diff --git a/tests/js/t21247.nim b/tests/js/t21247.nim deleted file mode 100644 index 5b38787b3..000000000 --- a/tests/js/t21247.nim +++ /dev/null @@ -1,15 +0,0 @@ -import std/typetraits - -type - QueryParams* = distinct seq[(string, string)] - -converter toBase*(params: var QueryParams): var seq[(string, string)] = - params.distinctBase - -proc foo(): QueryParams = - # Issue was that the implicit converter call didn't say that it took the - # address of the parameter it was converting. This led to the parameter not being - # passed as a fat pointer which toBase expected - result.add(("hello", "world")) - -assert foo().distinctBase() == @[("hello", "world")] diff --git a/tests/js/t21439.nim b/tests/js/t21439.nim deleted file mode 100644 index 972356cd0..000000000 --- a/tests/js/t21439.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - action: "compile" -""" - -proc test(a: openArray[string]): proc = - result = proc = - for i in a: - discard i - - -const a = ["t1", "t2"] - -discard test(a) - diff --git a/tests/js/t8821.nim b/tests/js/t8821.nim index 38c88efa8..43cf3f6f2 100644 --- a/tests/js/t8821.nim +++ b/tests/js/t8821.nim @@ -1,3 +1,6 @@ +discard """ + errormsg: "Your case statement contains too many branches, consider using if/else instead!" +""" proc isInt32(i: int): bool = case i @@ -6,4 +9,4 @@ proc isInt32(i: int): bool = else: return false -doAssert isInt32(1) == true \ No newline at end of file +discard isInt32(1) \ No newline at end of file diff --git a/tests/js/t9410.nim b/tests/js/t9410.nim index 042520dc5..78c329a24 100644 --- a/tests/js/t9410.nim +++ b/tests/js/t9410.nim @@ -447,10 +447,10 @@ template tests = doAssert seqOfSeqs == @[@[10, 2], @[30, 4]] when false: - block: # openArray + block: # openarray # Error: internal error: genAddr: nkStmtListExpr var calls = 0 - proc getvarint(x: var openArray[int]): var int = + proc getvarint(x: var openarray[int]): var int = calls += 1 if true: x[1] diff --git a/tests/js/tasync.nim b/tests/js/tasync.nim new file mode 100644 index 000000000..318237651 --- /dev/null +++ b/tests/js/tasync.nim @@ -0,0 +1,33 @@ +discard """ + output: ''' +x +e +''' +""" + +import asyncjs + +# demonstrate forward definition +# for js +proc y(e: int): Future[string] {.async.} + +proc e: int {.discardable.} = + echo "e" + return 2 + +proc x(e: int): Future[void] {.async.} = + var s = await y(e) + if e > 2: + return + echo s + e() + +proc y(e: int): Future[string] {.async.} = + if e > 0: + return await y(0) + else: + return "x" + + +discard x(2) + diff --git a/tests/js/tasyncjs_pragma.nim b/tests/js/tasync_pragma.nim similarity index 93% rename from tests/js/tasyncjs_pragma.nim rename to tests/js/tasync_pragma.nim index 2b6f32e92..916769fad 100644 --- a/tests/js/tasyncjs_pragma.nim +++ b/tests/js/tasync_pragma.nim @@ -5,8 +5,6 @@ t ''' """ -# xxx merge into tasyncjs.nim - import asyncjs, macros macro f*(a: untyped): untyped = diff --git a/tests/js/tasyncjs.nim b/tests/js/tasyncjs.nim deleted file mode 100644 index 3de143643..000000000 --- a/tests/js/tasyncjs.nim +++ /dev/null @@ -1,102 +0,0 @@ -discard """ - output: ''' -x -e -done -''' -""" - -#[ -xxx move this to tests/stdlib/tasyncjs.nim -]# - -import std/asyncjs - -block: - # demonstrate forward definition for js - proc y(e: int): Future[string] {.async.} - - proc e: int {.discardable.} = - echo "e" - return 2 - - proc x(e: int): Future[void] {.async.} = - var s = await y(e) - if e > 2: - return - echo s - e() - - proc y(e: int): Future[string] {.async.} = - if e > 0: - return await y(0) - else: - return "x" - - discard x(2) - -import std/sugar -from std/strutils import contains - -var witness: seq[string] - -proc fn(n: int): Future[int] {.async.} = - if n >= 7: - raise newException(ValueError, "foobar: " & $n) - if n > 0: - var ret = 1 + await fn(n-1) - witness.add $(n, ret) - return ret - else: - return 10 - -proc asyncFact(n: int): Future[int] {.async.} = - if n > 0: result = n * await asyncFact(n-1) - else: result = 1 - -proc asyncIdentity(n: int): Future[int] {.async.} = - if n > 0: result = 1 + await asyncIdentity(n-1) - else: result = 0 - -proc main() {.async.} = - block: # then - let x = await fn(4) - .then((a: int) => a.float) - .then((a: float) => $a) - doAssert x == "14.0" - doAssert witness == @["(1, 11)", "(2, 12)", "(3, 13)", "(4, 14)"] - - doAssert (await fn(2)) == 12 - - let x2 = await fn(4).then((a: int) => (discard)).then(() => 13) - doAssert x2 == 13 - - let x4 = await asyncFact(3).then(asyncIdentity).then(asyncIdentity).then((a:int) => a * 7).then(asyncIdentity) - doAssert x4 == 3 * 2 * 7 - - block: # bug #17177 - proc asyncIdentityNested(n: int): Future[int] {.async.} = return n - let x5 = await asyncFact(3).then(asyncIdentityNested) - doAssert x5 == 3 * 2 - - when false: # xxx pending bug #17254 - let x6 = await asyncFact(3).then((a:int) {.async.} => a * 11) - doAssert x6 == 3 * 2 * 11 - - block: # catch - var reason: Error - await fn(6).then((a: int) => (witness.add $a)).catch((r: Error) => (reason = r)) - doAssert reason == nil - - await fn(7).then((a: int) => (discard)).catch((r: Error) => (reason = r)) - doAssert reason != nil - doAssert reason.name == "Error" - doAssert "foobar: 7" in $reason.message - echo "done" # justified here to make sure we're running this, since it's inside `async` - -block asyncPragmaInType: - type Handler = proc () {.async.} - proc foo() {.async.} = discard - var x: Handler = foo - -discard main() diff --git a/tests/js/tasyncjs_bad.nim b/tests/js/tasyncjs_bad.nim deleted file mode 100644 index b1e5a7bc3..000000000 --- a/tests/js/tasyncjs_bad.nim +++ /dev/null @@ -1,22 +0,0 @@ -discard """ - exitCode: 1 - outputsub: "Error: unhandled exception: foobar: 13" -""" - -# note: this needs `--unhandled-rejections=strict`, see D20210217T215950 - -import std/asyncjs -from std/sugar import `=>` - -proc fn(n: int): Future[int] {.async.} = - if n >= 7: raise newException(ValueError, "foobar: " & $n) - else: result = n - -proc main() {.async.} = - let x1 = await fn(6) - doAssert x1 == 6 - await fn(7).catch((a: Error) => (discard)) - let x3 = await fn(13) - doAssert false # shouldn't go here, should fail before - -discard main() diff --git a/tests/js/tbasics.nim b/tests/js/tbasics.nim index ef84f8bb5..b297bb037 100644 --- a/tests/js/tbasics.nim +++ b/tests/js/tbasics.nim @@ -54,7 +54,7 @@ for x in someGlobal: doAssert(x == 0) proc tdefault = var x = default(int) doAssert(x == 0) - proc inner(v: openArray[string]) = + proc inner(v: openarray[string]) = doAssert(v.len == 0) inner(default(seq[string])) diff --git a/tests/js/tbigint_backend.nim b/tests/js/tbigint_backend.nim index 8f2f6c4f4..db7ebb065 100644 --- a/tests/js/tbigint_backend.nim +++ b/tests/js/tbigint_backend.nim @@ -1,5 +1,6 @@ -import std/private/jsutils - +proc jsTypeOf*[T](x: T): cstring {.importjs: "typeof(#)".} + ## Returns the name of the JsObject's JavaScript type as a cstring. + # xxx replace jsffi.jsTypeOf with this definition and add tests type JsBigIntImpl {.importc: "bigint".} = int type JsBigInt = distinct JsBigIntImpl diff --git a/tests/js/tbyvar.nim b/tests/js/tbyvar.nim index 93724a2f1..a4c60b0b3 100644 --- a/tests/js/tbyvar.nim +++ b/tests/js/tbyvar.nim @@ -39,9 +39,9 @@ proc main = main() -# Test: pass var seq to var openArray +# Test: pass var seq to var openarray var s = @[2, 1] -proc foo(a: var openArray[int]) = a[0] = 123 +proc foo(a: var openarray[int]) = a[0] = 123 proc bar(s: var seq[int], a: int) = doAssert(a == 5) diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim index 4f1c28de3..3137123bf 100644 --- a/tests/js/tclosures.nim +++ b/tests/js/tclosures.nim @@ -22,7 +22,7 @@ asm """ function print (text) { console.log (text); } """ -proc consoleprint (str:cstring): void {.importc: "print", nodecl.} +proc consoleprint (str:cstring): void {.importc: "print", noDecl.} proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join(a, " ")] type CallbackProc {.importc.} = proc () : cstring diff --git a/tests/js/tcsymbol.nim b/tests/js/tcsymbol.nim deleted file mode 100644 index 07e52b9b6..000000000 --- a/tests/js/tcsymbol.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - matrix: "--cc:gcc; --cc:tcc" -""" - -doAssert not defined(gcc) -doAssert not defined(tcc) \ No newline at end of file diff --git a/tests/js/tfieldchecks.nim b/tests/js/tfieldchecks.nim index a0679a349..3916cae74 100644 --- a/tests/js/tfieldchecks.nim +++ b/tests/js/tfieldchecks.nim @@ -30,7 +30,7 @@ block: block: let a0 = addr(obj.f0) echo a0[] - # let a1 = addr(obj.f1) + # let a1 = unsafeAddr(obj.f1) # echo a1[] doAssertRaises(FieldDefect): let a2 = addr(obj.f2) diff --git a/tests/js/tindexdefect.nim b/tests/js/tindexdefect.nim deleted file mode 100644 index 37994ec2e..000000000 --- a/tests/js/tindexdefect.nim +++ /dev/null @@ -1,9 +0,0 @@ -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/js/tjsffi.nim b/tests/js/tjsffi.nim index 2e57f70c1..143ac4a25 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -1,196 +1,291 @@ discard """ output: ''' +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true 3 2 12 Event { name: 'click: test' } Event { name: 'reloaded: test' } Event { name: 'updates: test' } +true +true +true +true +true +true +true ''' """ import jsffi, jsconsole # Tests for JsObject -block: # Test JsObject []= and [] - let obj = newJsObject() - obj["a"] = 11 - obj["b"] = "test" - obj["c"] = "test".cstring - doAssert obj["a"].to(int) == 11 - doAssert obj["c"].to(cstring) == "test".cstring +# Test JsObject []= and [] +block: + proc test(): bool = + let obj = newJsObject() + var working = true + obj["a"] = 11 + obj["b"] = "test" + obj["c"] = "test".cstring + working = working and obj["a"].to(int) == 11 + working = working and obj["c"].to(cstring) == "test".cstring + working + echo test() -block: # Test JsObject .= and . - let obj = newJsObject() - obj.a = 11 - obj.b = "test" - obj.c = "test".cstring - obj.`$!&` = 42 - obj.`while` = 99 - doAssert obj.a.to(int) == 11 - doAssert obj.b.to(string) == "test" - doAssert obj.c.to(cstring) == "test".cstring - doAssert obj.`$!&`.to(int) == 42 - doAssert obj.`while`.to(int) == 99 +# Test JsObject .= and . +block: + proc test(): bool = + let obj = newJsObject() + var working = true + obj.a = 11 + obj.b = "test" + obj.c = "test".cstring + obj.`$!&` = 42 + obj.`while` = 99 + working = working and obj.a.to(int) == 11 + working = working and obj.b.to(string) == "test" + working = working and obj.c.to(cstring) == "test".cstring + working = working and obj.`$!&`.to(int) == 42 + working = working and obj.`while`.to(int) == 99 + working + echo test() -block: # Test JsObject .() - let obj = newJsObject() - obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z) - doAssert obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == cstring"Result is: 6" +# Test JsObject .() +block: + proc test(): bool = + let obj = newJsObject() + obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z) + obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == cstring"Result is: 6" + echo test() -block: # Test JsObject []() - let obj = newJsObject() - obj.a = proc(x, y, z: int, t: string): string = t & $(x + y + z) - let call = obj["a"].to(proc(x, y, z: int, t: string): string) - doAssert call(1, 2, 3, "Result is: ") == "Result is: 6" +# Test JsObject []() +block: + proc test(): bool = + let obj = newJsObject() + obj.a = proc(x, y, z: int, t: string): string = t & $(x + y + z) + let call = obj["a"].to(proc(x, y, z: int, t: string): string) + call(1, 2, 3, "Result is: ") == "Result is: 6" + echo test() # Test JsObject Iterators -block: # testPairs - let obj = newJsObject() - obj.a = 10 - obj.b = 20 - obj.c = 30 - for k, v in obj.pairs: - case $k - of "a": - doAssert v.to(int) == 10 - of "b": - doAssert v.to(int) == 20 - of "c": - doAssert v.to(int) == 30 - else: - doAssert false -block: # testItems - let obj = newJsObject() - obj.a = 10 - obj.b = 20 - obj.c = 30 - for v in obj.items: - doAssert v.to(int) in [10, 20, 30] -block: # testKeys - let obj = newJsObject() - obj.a = 10 - obj.b = 20 - obj.c = 30 - for v in obj.keys: - doAssert $v in ["a", "b", "c"] +block: + proc testPairs(): bool = + let obj = newJsObject() + var working = true + obj.a = 10 + obj.b = 20 + obj.c = 30 + for k, v in obj.pairs: + case $k + of "a": + working = working and v.to(int) == 10 + of "b": + working = working and v.to(int) == 20 + of "c": + working = working and v.to(int) == 30 + else: + return false + working + proc testItems(): bool = + let obj = newJsObject() + var working = true + obj.a = 10 + obj.b = 20 + obj.c = 30 + for v in obj.items: + working = working and v.to(int) in [10, 20, 30] + working + proc testKeys(): bool = + let obj = newJsObject() + var working = true + obj.a = 10 + obj.b = 20 + obj.c = 30 + for v in obj.keys: + working = working and $v in ["a", "b", "c"] + working + proc test(): bool = testPairs() and testItems() and testKeys() + echo test() -block: # Test JsObject equality - {. emit: "var comparison = {a: 22, b: 'test'};" .} - var comparison {. importjs, nodecl .}: JsObject - let obj = newJsObject() - obj.a = 22 - obj.b = "test".cstring - doAssert obj.a == comparison.a and obj.b == comparison.b +# Test JsObject equality +block: + proc test(): bool = + {. emit: "var comparison = {a: 22, b: 'test'};" .} + var comparison {. importjs, nodecl .}: JsObject + let obj = newJsObject() + obj.a = 22 + obj.b = "test".cstring + obj.a == comparison.a and obj.b == comparison.b + echo test() -block: # Test JsObject literal - {. emit: "var comparison = {a: 22, b: 'test'};" .} - var comparison {. importjs, nodecl .}: JsObject - let obj = JsObject{ a: 22, b: "test".cstring } - doAssert obj.a == comparison.a and obj.b == comparison.b +# Test JsObject literal +block: + proc test(): bool = + {. emit: "var comparison = {a: 22, b: 'test'};" .} + var comparison {. importjs, nodecl .}: JsObject + let obj = JsObject{ a: 22, b: "test".cstring } + obj.a == comparison.a and obj.b == comparison.b + echo test() # Tests for JsAssoc -block: # Test JsAssoc []= and [] - let obj = newJsAssoc[int, int]() - obj[1] = 11 - doAssert not compiles(obj["a"] = 11) - doAssert not compiles(obj["a"]) - doAssert not compiles(obj[2] = "test") - doAssert not compiles(obj[3] = "test".cstring) - doAssert obj[1] == 11 +# Test JsAssoc []= and [] +block: + proc test(): bool = + let obj = newJsAssoc[int, int]() + var working = true + obj[1] = 11 + working = working and not compiles(obj["a"] = 11) + working = working and not compiles(obj["a"]) + working = working and not compiles(obj[2] = "test") + working = working and not compiles(obj[3] = "test".cstring) + working = working and obj[1] == 11 + working + echo test() -block: # Test JsAssoc .= and . - let obj = newJsAssoc[cstring, int]() - var working = true - obj.a = 11 - obj.`$!&` = 42 - doAssert not compiles(obj.b = "test") - doAssert not compiles(obj.c = "test".cstring) - doAssert obj.a == 11 - doAssert obj.`$!&` == 42 +# Test JsAssoc .= and . +block: + proc test(): bool = + let obj = newJsAssoc[cstring, int]() + var working = true + obj.a = 11 + obj.`$!&` = 42 + working = working and not compiles(obj.b = "test") + working = working and not compiles(obj.c = "test".cstring) + working = working and obj.a == 11 + working = working and obj.`$!&` == 42 + working + echo test() -block: # Test JsAssoc .() - let obj = newJsAssoc[cstring, proc(e: int): int]() - obj.a = proc(e: int): int = e * e - doAssert obj.a(10) == 100 +# Test JsAssoc .() +block: + proc test(): bool = + let obj = newJsAssoc[cstring, proc(e: int): int]() + obj.a = proc(e: int): int = e * e + obj.a(10) == 100 + echo test() -block: # Test JsAssoc []() - let obj = newJsAssoc[cstring, proc(e: int): int]() - obj.a = proc(e: int): int = e * e - let call = obj["a"] - doAssert call(10) == 100 +# Test JsAssoc []() +block: + proc test(): bool = + let obj = newJsAssoc[cstring, proc(e: int): int]() + obj.a = proc(e: int): int = e * e + let call = obj["a"] + call(10) == 100 + echo test() # Test JsAssoc Iterators -block: # testPairs - let obj = newJsAssoc[cstring, int]() - obj.a = 10 - obj.b = 20 - obj.c = 30 - for k, v in obj.pairs: - case $k - of "a": - doAssert v == 10 - of "b": - doAssert v == 20 - of "c": - doAssert v == 30 - else: - doAssert false -block: # testItems - let obj = newJsAssoc[cstring, int]() - obj.a = 10 - obj.b = 20 - obj.c = 30 - for v in obj.items: - doAssert v in [10, 20, 30] -block: # testKeys - let obj = newJsAssoc[cstring, int]() - obj.a = 10 - obj.b = 20 - obj.c = 30 - for v in obj.keys: - doAssert v in [cstring"a", cstring"b", cstring"c"] +block: + proc testPairs(): bool = + let obj = newJsAssoc[cstring, int]() + var working = true + obj.a = 10 + obj.b = 20 + obj.c = 30 + for k, v in obj.pairs: + case $k + of "a": + working = working and v == 10 + of "b": + working = working and v == 20 + of "c": + working = working and v == 30 + else: + return false + working + proc testItems(): bool = + let obj = newJsAssoc[cstring, int]() + var working = true + obj.a = 10 + obj.b = 20 + obj.c = 30 + for v in obj.items: + working = working and v in [10, 20, 30] + working + proc testKeys(): bool = + let obj = newJsAssoc[cstring, int]() + var working = true + obj.a = 10 + obj.b = 20 + obj.c = 30 + for v in obj.keys: + working = working and v in [cstring"a", cstring"b", cstring"c"] + working + proc test(): bool = testPairs() and testItems() and testKeys() + echo test() -block: # Test JsAssoc equality - {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importjs, nodecl .}: JsAssoc[cstring, int] - let obj = newJsAssoc[cstring, int]() - obj.a = 22 - obj.b = 55 - doAssert obj.a == comparison.a and obj.b == comparison.b +# Test JsAssoc equality +block: + proc test(): bool = + {. emit: "var comparison = {a: 22, b: 55};" .} + var comparison {. importjs, nodecl .}: JsAssoc[cstring, int] + let obj = newJsAssoc[cstring, int]() + obj.a = 22 + obj.b = 55 + obj.a == comparison.a and obj.b == comparison.b + echo test() -block: # Test JsAssoc literal - {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importjs, nodecl .}: JsAssoc[cstring, int] - let obj = JsAssoc[cstring, int]{ a: 22, b: 55 } - doAssert compiles(JsAssoc[int, int]{ 1: 22, 2: 55 }) - doAssert comparison.a == obj.a and comparison.b == obj.b - doAssert not compiles(JsAssoc[cstring, int]{ a: "test" }) +# Test JsAssoc literal +block: + proc test(): bool = + {. emit: "var comparison = {a: 22, b: 55};" .} + var comparison {. importjs, nodecl .}: JsAssoc[cstring, int] + let obj = JsAssoc[cstring, int]{ a: 22, b: 55 } + var working = true + working = working and + compiles(JsAssoc[int, int]{ 1: 22, 2: 55 }) + working = working and + comparison.a == obj.a and comparison.b == obj.b + working = working and + not compiles(JsAssoc[cstring, int]{ a: "test" }) + working + echo test() # Tests for macros on non-JsRoot objects -block: # Test lit +# Test lit +block: type TestObject = object a: int b: cstring - {. emit: "var comparison = {a: 1};" .} - var comparison {. importjs, nodecl .}: TestObject - let obj = TestObject{ a: 1 } - doAssert obj == comparison + proc test(): bool = + {. emit: "var comparison = {a: 1};" .} + var comparison {. importjs, nodecl .}: TestObject + let obj = TestObject{ a: 1 } + obj == comparison + echo test() -block: # Test bindMethod +# Test bindMethod +block: type TestObject = object a: int onWhatever: proc(e: int): int proc handleWhatever(this: TestObject, e: int): int = e + this.a - block: + proc test(): bool = let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever)) - doAssert obj.onWhatever(1) == 10 + obj.onWhatever(1) == 10 + echo test() block: {.emit: "function jsProc(n) { return n; }" .} proc jsProc(x: int32): JsObject {.importjs: "jsProc(#)".} - block: + + proc test() = var x = jsProc(1) var y = jsProc(2) console.log x + y @@ -199,6 +294,8 @@ block: x += jsProc(10) console.log x + test() + block: {.emit: """ @@ -217,7 +314,7 @@ block: on("click") do (e: Event): console.log e - jslib.on("reloaded") do (): + jslib.on("reloaded") do: console.log jsarguments[0] # this test case is different from the above, because @@ -226,13 +323,13 @@ block: console.log jsarguments[0] block: - doAssert jsUndefined == jsNull - doAssert jsUndefined == nil - doAssert jsNull == nil - doAssert jsUndefined.isNil - doAssert jsNull.isNil - doAssert jsNull.isNull - doAssert jsUndefined.isUndefined + echo jsUndefined == jsNull + echo jsUndefined == nil + echo jsNull == nil + echo jsUndefined.isNil + echo jsNull.isNil + echo jsNull.isNull + echo jsUndefined.isUndefined block: # test ** var a = toJs(0) @@ -265,3 +362,4 @@ block: # test ** doAssert to(`**`(a + a, b), int) == 2 doAssert to(`**`(toJs(1) + toJs(1), toJs(2)), int) == 4 + diff --git a/tests/js/tjsffi_old.nim b/tests/js/tjsffi_old.nim index 19f30ee2c..1f149694b 100644 --- a/tests/js/tjsffi_old.nim +++ b/tests/js/tjsffi_old.nim @@ -35,9 +35,6 @@ true ## same as tjsffi, but this test uses the old names: importc and ## importcpp. This test is for backwards compatibility. -# xxx instead of maintaining this near-duplicate test file, just have tests -# that check that importc, importcpp, importjs work and remove this file. - import jsffi, jsconsole # Tests for JsObject @@ -321,7 +318,7 @@ block: on("click") do (e: Event): console.log e - jslib.on("reloaded") do (): + jslib.on("reloaded") do: console.log jsarguments[0] # this test case is different from the above, because diff --git a/tests/js/tlent.nim b/tests/js/tlent.nim deleted file mode 100644 index 2546e5b1d..000000000 --- a/tests/js/tlent.nim +++ /dev/null @@ -1,33 +0,0 @@ -discard """ - output: ''' -hmm -100 -hmm -100 -''' -""" - -# #16800 - -type A = object - b: int -var t = A(b: 100) -block: - proc getValues: lent int = - echo "hmm" - result = t.b - echo getValues() -block: - proc getValues: lent int = - echo "hmm" - t.b - echo getValues() - -when false: # still an issue, #16908 - template main = - iterator fn[T](a:T): lent T = yield a - let a = @[10] - for b in fn(a): echo b - - static: main() - main() diff --git a/tests/js/tnilstrs.nim b/tests/js/tnilstrs.nim index 6c1e4e401..c0048cb24 100644 --- a/tests/js/tnilstrs.nim +++ b/tests/js/tnilstrs.nim @@ -14,12 +14,4 @@ block: var x = "foo".cstring var y: string add(y, x) - doAssert y == "foo" - -block: - type Foo = object - a: string - var foo = Foo(a: "foo") - var y = move foo.a - doAssert foo.a.len == 0 - doAssert y == "foo" + doAssert y == "foo" \ No newline at end of file diff --git a/tests/js/tos.nim b/tests/js/tos.nim index bfe3cd9b4..07eb3aaa3 100644 --- a/tests/js/tos.nim +++ b/tests/js/tos.nim @@ -1,5 +1,3 @@ -# xxx consider merging this in tests/stdlib/tos.nim for increased coverage (with selecting disabling) - static: doAssert defined(nodejs) import os @@ -21,3 +19,29 @@ block: if not isWindows: doAssert cwd.isAbsolute doAssert relativePath(getCurrentDir() / "foo", "bar") == "../foo" + +import std/sequtils + +template main = + putEnv("foo", "bar") + doAssert getEnv("foo") == "bar" + doAssert existsEnv("foo") + + putEnv("foo", "") + doAssert existsEnv("foo") + putEnv("foo", "bar2") + doAssert getEnv("foo") == "bar2" + + when nimvm: + discard + else: + # need support in vmops: envPairs, delEnv + let s = toSeq(envPairs()) + doAssert ("foo", "bar2") in s + doAssert ("foo", "bar") notin s + + delEnv("foo") + doAssert not existsEnv("foo") + +static: main() +main() diff --git a/tests/js/trepr.nim b/tests/js/trepr.nim index a562ad63b..c63b64d3a 100644 --- a/tests/js/trepr.nim +++ b/tests/js/trepr.nim @@ -283,7 +283,7 @@ block bunch: result[] = b var - aa = default(A) + aa: A bb: B = B(a: "inner", b: @['o', 'b', 'j']) cc: A = A(a: 12, b: 1, c: 1.2, d: '\0', e: eC, f: "hello", g: {'A'}, h: {2'i16}, @@ -303,7 +303,7 @@ g = {}, h = {}, i = ["", "", ""], j = @[], -k = -12, +k = 0, l = [a = "", b = @[]], m = nil, diff --git a/tests/js/tseqops.nim b/tests/js/tseqops.nim index 8cfb50886..af664222c 100644 --- a/tests/js/tseqops.nim +++ b/tests/js/tseqops.nim @@ -1,3 +1,11 @@ +discard """ + output: '''(x: 0, y: 0) +(x: 5, y: 0) +@[(x: "2", y: 4), (x: "4", y: 5), (x: "4", y: 5)] +@[(a: "3", b: 3), (a: "1", b: 1), (a: "2", b: 2)] +''' +""" + # bug #4139 type @@ -9,8 +17,8 @@ proc onLoad() = var foo = TestO(x: 0, y: 0) test.add(foo) foo.x = 5 - doAssert $test[0] == "(x: 0, y: 0)" - doAssert $foo == "(x: 5, y: 0)" + echo(test[0]) + echo foo onLoad() @@ -26,7 +34,7 @@ proc foo(x: var seq[MyObj]) = var s = @[MyObj(x: "2", y: 4), MyObj(x: "4", y: 5)] foo(s) -doAssert $s == """@[(x: "2", y: 4), (x: "4", y: 5), (x: "4", y: 5)]""" +echo s # bug #5933 import sequtils @@ -40,9 +48,4 @@ var test = @[Test(a: "1", b: 1), Test(a: "2", b: 2)] test.insert(@[Test(a: "3", b: 3)], 0) -doAssert $test == """@[(a: "3", b: 3), (a: "1", b: 1), (a: "2", b: 2)]""" - -proc hello(): array[5, int] = discard -var x = @(hello()) -x.add(2) -doAssert x == @[0, 0, 0, 0, 0, 2] +echo test diff --git a/tests/js/tsourcemap.nim b/tests/js/tsourcemap.nim deleted file mode 100644 index ff6f6122f..000000000 --- a/tests/js/tsourcemap.nim +++ /dev/null @@ -1,96 +0,0 @@ -discard """ - action: "run" - target: "js" - cmd: "nim js -r -d:nodejs $options --sourceMap:on $file" -""" -import std/[os, json, strutils, sequtils, algorithm, assertions, paths, compilesettings] - -# Implements a very basic sourcemap parser and then runs it on itself. -# Allows to check for basic problems such as bad counts and lines missing (e.g. issue #21052) - -type - SourceMap = object - version: int - sources: seq[string] - names: seq[string] - mappings: string - file: string - - Line = object - line, column: int - file: string - -const - flag = 1 shl 5 - signBit = 0b1 - fourBits = 0b1111 - fiveBits = 0b11111 - mask = (1 shl 5) - 1 - alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - -var b64Table: seq[int] = 0.repeat(max(alphabet.mapIt(it.ord)) + 1) -for i, b in alphabet.pairs: - b64Table[b.ord] = i - -# From https://github.com/juancarlospaco/nodejs/blob/main/src/nodejs/jsfs.nim -proc importFs*() {.importjs: "var fs = require(\"fs\");".} -proc readFileSync*(path: cstring): cstring {.importjs: "(fs.$1(#).toString())".} -importFS() -# Read in needed files -let - jsFileName = string(querySetting(outDir).Path / "tsourcemap.js".Path) - mapFileName = jsFileName & ".map" - - data = parseJson($mapFileName.cstring.readFileSync()).to(SourceMap) - jsFile = $readFileSync(jsFileName.cstring) - -proc decodeVLQ(inp: string): seq[int] = - var - shift, value: int - for v in inp.mapIt(b64Table[it.ord]): - value += (v and mask) shl shift - if (v and flag) > 0: - shift += 5 - continue - result &= (value shr 1) * (if (value and 1) > 0: -1 else: 1) - shift = 0 - value = 0 - - -# Keep track of state -var - line = 0 - source = 0 - name = 0 - column = 0 - jsLine = 1 - lines: seq[Line] - -for gline in data.mappings.split(';'): - jsLine += 1 - var jsColumn = 0 - for item in gline.strip().split(','): - let value = item.decodeVLQ() - doAssert value.len in [0, 1, 4, 5] - if value.len == 0: - continue - jsColumn += value[0] - if value.len >= 4: - source += value[1] - line += value[2] - column += value[3] - lines &= Line(line: line, column: column, file: data.sources[source]) - -let jsLines = jsFile.splitLines().len -# There needs to be a mapping for every line in the JS -# If there isn't then the JS lines wont match up with Nim lines. -# Except we don't care about the final line since that doesn't need to line up -doAssert data.mappings.count(';') == jsLines - 1 - -# Check we can find this file somewhere in the source map -var foundSelf = false -for line in lines: - if "tsourcemap.nim" in line.file: - foundSelf = true - doAssert line.line in 0..] -[FAILED] Bad test - ''' - matrix: "-d:nodejs" - targets: "js" - joinable: false -""" - -# bug #16978 -import unittest -test "Bad test": - var x: cstring = nil - let y = x[0] diff --git a/tests/lent/t16898.nim b/tests/lent/t16898.nim deleted file mode 100644 index a69c6d244..000000000 --- a/tests/lent/t16898.nim +++ /dev/null @@ -1,31 +0,0 @@ -discard """ - errormsg: "invalid type: 'lent QuadraticExt' in this context: 'proc (r: var QuadraticExt, a: lent QuadraticExt, b: lent QuadraticExt){.noSideEffect, gcsafe.}' for proc" -""" - -# bug #16898 -type - Fp[N: static int, T] = object - big: array[N, T] - -type - QuadraticExt* = concept x - ## Quadratic Extension concept (like complex) - type BaseField = auto - x.c0 is BaseField - x.c1 is BaseField - -{.experimental:"views".} - -func prod(r: var QuadraticExt, a, b: lent QuadraticExt) = - discard - -type - Fp2[N: static int, T] = object - c0, c1: Fp[N, T] - -# This should be passed by reference, -# but concepts do not respect the 24 bytes rule -# or `byref` pragma. -var r, a, b: Fp2[6, uint64] - -prod(r, a, b) diff --git a/tests/lent/t17621.nim b/tests/lent/t17621.nim deleted file mode 100644 index e324a963e..000000000 --- a/tests/lent/t17621.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - errormsg: "invalid type: 'lent Test' in this context: 'proc (self: lent Test)' for proc" -""" - -# bug #17621 -{.experimental: "views".} - -type Test = ref object - foo: int - -proc modify(self: lent Test) = - self.foo += 1 - -let test = Test(foo: 12) -modify(test) diff --git a/tests/lent/tbasic_lent_check.nim b/tests/lent/tbasic_lent_check.nim index 92d731451..049fa276a 100644 --- a/tests/lent/tbasic_lent_check.nim +++ b/tests/lent/tbasic_lent_check.nim @@ -17,37 +17,33 @@ proc main = main() template main2 = # bug #15958 - when defined(js): - proc sameAddress[T](a, b: T): bool {.importjs: "(# === #)".} - else: - template sameAddress(a, b): bool = a.unsafeAddr == b.unsafeAddr proc byLent[T](a: T): lent T = a let a = [11,12] let b = @[21,23] let ss = {1, 2, 3, 5} doAssert byLent(a) == [11,12] - doAssert sameAddress(byLent(a), a) + doAssert byLent(a).unsafeAddr == a.unsafeAddr doAssert byLent(b) == @[21,23] - # pending bug #16073 - doAssert sameAddress(byLent(b), b) + when not defined(js): # pending bug #16073 + doAssert byLent(b).unsafeAddr == b.unsafeAddr doAssert byLent(ss) == {1, 2, 3, 5} - doAssert sameAddress(byLent(ss), ss) + doAssert byLent(ss).unsafeAddr == ss.unsafeAddr let r = new(float) r[] = 10.0 - # bug #16073 - doAssert byLent(r)[] == 10.0 + when not defined(js): # pending bug #16073 + doAssert byLent(r)[] == 10.0 when not defined(js): # pending bug https://github.com/timotheecour/Nim/issues/372 let p = create(float) p[] = 20.0 doAssert byLent(p)[] == 20.0 - proc byLent2[T](a: openArray[T]): lent T = a[0] + proc byLent2[T](a: openarray[T]): lent T = a[0] doAssert byLent2(a) == 11 - doAssert sameAddress(byLent2(a), a[0]) + doAssert byLent2(a).unsafeAddr == a[0].unsafeAddr doAssert byLent2(b) == 21 - doAssert sameAddress(byLent2(b), b[0]) + doAssert byLent2(b).unsafeAddr == b[0].unsafeAddr proc byLent3[T](a: varargs[T]): lent T = a[1] let diff --git a/tests/lexer/mlexerutils.nim b/tests/lexer/mlexerutils.nim deleted file mode 100644 index eae7a0006..000000000 --- a/tests/lexer/mlexerutils.nim +++ /dev/null @@ -1,9 +0,0 @@ -import macros - -macro lispReprStr*(a: untyped): untyped = newLit(a.lispRepr) - -macro assertAST*(expected: string, struct: untyped): untyped = - var ast = newLit(struct.treeRepr) - result = quote do: - if `ast` != `expected`: - doAssert false, "\nGot:\n" & `ast`.indent(2) & "\nExpected:\n" & `expected`.indent(2) \ No newline at end of file diff --git a/tests/lexer/tcustom_numeric_literals.nim b/tests/lexer/tcustom_numeric_literals.nim deleted file mode 100644 index 9c49d0c08..000000000 --- a/tests/lexer/tcustom_numeric_literals.nim +++ /dev/null @@ -1,185 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -# Test tkStrNumLit - -import std/[macros, strutils] -import mlexerutils - -# AST checks - -assertAST dedent """ - StmtList - ProcDef - AccQuoted - Ident "\'" - Ident "wrap" - Empty - Empty - FormalParams - Ident "string" - IdentDefs - Ident "number" - Ident "string" - Empty - Empty - Empty - StmtList - Asgn - Ident "result" - Infix - Ident "&" - Infix - Ident "&" - StrLit "[[" - Ident "number" - StrLit "]]"""": - proc `'wrap`(number: string): string = - result = "[[" & number & "]]" - -assertAST dedent """ - StmtList - DotExpr - RStrLit "-38383839292839283928392839283928392839283.928493849385935898243e-50000" - Ident "\'wrap"""": - -38383839292839283928392839283928392839283.928493849385935898243e-50000'wrap - -proc `'wrap`(number: string): string = "[[" & number & "]]" -proc wrap2(number: string): string = "[[" & number & "]]" -doAssert lispReprStr(-1'wrap) == """(DotExpr (RStrLit "-1") (Ident "\'wrap"))""" - -template main = - block: # basic suffix usage - template `'twrap`(number: string): untyped = - number.`'wrap` - proc extraContext(): string = - 22.40'wrap - proc `*`(left, right: string): string = - result = left & "times" & right - proc `+`(left, right: string): string = - result = left & "plus" & right - - doAssert 1'wrap == "[[1]]" - doAssert -1'wrap == "[[-1]]": - "unable to resolve a negative integer-suffix pattern" - doAssert 12345.67890'wrap == "[[12345.67890]]" - doAssert 1'wrap*1'wrap == "[[1]]times[[1]]": - "unable to resolve an operator between two suffixed numeric literals" - doAssert 1'wrap+ -1'wrap == "[[1]]plus[[-1]]": # will generate a compiler warning about inconsistent spacing - "unable to resolve a negative suffixed numeric literal following an operator" - doAssert 1'wrap + -1'wrap == "[[1]]plus[[-1]]" - doAssert 1'twrap == "[[1]]" - doAssert extraContext() == "[[22.40]]": - "unable to return a suffixed numeric literal by an implicit return" - doAssert 0x5a3a'wrap == "[[0x5a3a]]" - doAssert 0o5732'wrap == "[[0o5732]]" - doAssert 0b0101111010101'wrap == "[[0b0101111010101]]" - doAssert -38383839292839283928392839283928392839283.928493849385935898243e-50000'wrap == "[[-38383839292839283928392839283928392839283.928493849385935898243e-50000]]" - doAssert 1234.56'wrap == "[[1234.56]]": - "unable to properly account for context with suffixed numeric literals" - - block: # verify that the i64, f32, etc builtin suffixes still parse correctly - const expectedF32: float32 = 123.125 - proc `'f9`(number: string): string = # proc starts with 'f' just like 'f32' - "[[" & number & "]]" - proc `'f32a`(number: string): string = # looks even more like 'f32' - "[[" & number & "]]" - proc `'d9`(number: string): string = # proc starts with 'd' just like the d suffix - "[[" & number & "]]" - proc `'i9`(number: string): string = # proc starts with 'i' just like 'i64' - "[[" & number & "]]" - proc `'u9`(number: string): string = # proc starts with 'u' just like 'u8' - "[[" & number & "]]" - - doAssert 123.125f32 == expectedF32: - "failing to support non-quoted legacy f32 floating point suffix" - doAssert 123.125'f32 == expectedF32 - doAssert 123.125e0'f32 == expectedF32 - doAssert 1234.56'wrap == 1234.56'f9 - doAssert 1234.56'wrap == 1234.56'f32a - doAssert 1234.56'wrap == 1234.56'd9 - doAssert 1234.56'wrap == 1234.56'i9 - doAssert 1234.56'wrap == 1234.56'u9 - doAssert lispReprStr(1234.56'u9) == """(DotExpr (RStrLit "1234.56") (Ident "\'u9"))""": - "failed to properly build AST for suffix that starts with u" - doAssert -128'i8 == (-128).int8 - - block: # case checks - doAssert 1E2 == 100: - "lexer not handling upper-case exponent" - doAssert 1.0E2 == 100.0 - doAssert 1e2 == 100 - doAssert 0xdeadBEEF'wrap == "[[0xdeadBEEF]]": - "lexer not maintaining original case" - doAssert 0.1E12'wrap == "[[0.1E12]]" - doAssert 0.0e12'wrap == "[[0.0e12]]" - doAssert 0.0e+12'wrap == "[[0.0e+12]]" - doAssert 0.0e-12'wrap == "[[0.0e-12]]" - doAssert 0e-12'wrap == "[[0e-12]]" - - block: # macro and template usage - template `'foo`(a: string): untyped = (a, 2) - doAssert -12'foo == ("-12", 2) - template `'fooplus`(a: string, b: int): untyped = (a, b) - doAssert -12'fooplus(2) == ("-12", 2) - template `'fooplusopt`(a: string, b: int = 99): untyped = (a, b) - doAssert -12'fooplusopt(2) == ("-12", 2) - doAssert -12'fooplusopt() == ("-12", 99) - doAssert -12'fooplusopt == ("-12", 99) - macro `'bar`(a: static string): untyped = newLit(a.repr) - doAssert -12'bar == "\"-12\"" - macro deb(a): untyped = newLit(a.repr) - doAssert deb(-12'bar) == "-12'bar" - - block: # bug 1 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947 - macro deb1(a): untyped = newLit a.repr - macro deb2(a): untyped = newLit a.lispRepr - doAssert deb1(-12'wrap) == "-12'wrap" - doAssert deb1(-12'nonexistent) == "-12'nonexistent" - doAssert deb2(-12'nonexistent) == """(DotExpr (RStrLit "-12") (Ident "\'nonexistent"))""" - when false: # xxx bug: - # this holds: - doAssert deb2(-12.wrap2) == """(DotExpr (IntLit -12) (Sym "wrap2"))""" - doAssert deb2(-12'wrap) == """(DotExpr (RStrLit "-12") (Sym "\'wrap"))""" - # but instead this should hold: - doAssert deb2(-12.wrap2) == """(DotExpr (IntLit -12) (Ident "wrap2"))""" - doAssert deb2(-12'wrap) == """(DotExpr (RStrLit "-12") (Ident "\'wrap"))""" - - block: # bug 2 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947 - template toSuf(`'suf`): untyped = - let x = -12'suf - x - doAssert toSuf(`'wrap`) == "[[-12]]" - - block: # bug 10 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947 - proc `myecho`(a: auto): auto = a - template fn1(): untyped = - let a = "abc" - -12'wrap - template fn2(): untyped = - `myecho` -12'wrap - template fn3(): untyped = - -12'wrap - doAssert fn1() == "[[-12]]" - doAssert fn2() == "[[-12]]" - doAssert fn3() == "[[-12]]" - - when false: # xxx this fails; bug 9 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947 - #[ - possible workaround: use `genAst` (https://github.com/nim-lang/Nim/pull/17426) and this: - let a3 = `'wrap3`("-128") - ]# - block: - macro metawrap(): untyped = - func wrap1(a: string): string = "{" & a & "}" - func `'wrap3`(a: string): string = "{" & a & "}" - result = quote do: - let a1 = wrap1"-128" - let a2 = -128'wrap3 - metawrap() - doAssert a1 == "{-128}" - doAssert a2 == "{-128}" - -static: main() -main() diff --git a/tests/lexer/tlexerspaces.nim b/tests/lexer/tlexerspaces.nim deleted file mode 100644 index 14b16111d..000000000 --- a/tests/lexer/tlexerspaces.nim +++ /dev/null @@ -1,2 +0,0 @@ -discard 12 + 5 -discard 12 + 5 diff --git a/tests/lexer/tunary_minus.nim b/tests/lexer/tunary_minus.nim deleted file mode 100644 index 1641e918c..000000000 --- a/tests/lexer/tunary_minus.nim +++ /dev/null @@ -1,81 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -# Test numeric literals and handling of minus symbol - -import std/[macros, strutils] - -import mlexerutils - -const one = 1 -const minusOne = `-`(one) - -# border cases that *should* generate compiler errors: -assertAST dedent """ - StmtList - Asgn - Ident "x" - Command - IntLit 4 - IntLit -1""": - x = 4 -1 -assertAST dedent """ - StmtList - VarSection - IdentDefs - Ident "x" - Ident "uint" - IntLit -1""": - var x: uint = -1 -template bad() = - x = 4 -1 -doAssert not compiles(bad()) - -template main = - block: # check when a minus (-) is a negative sign for a literal - doAssert -1 == minusOne: - "unable to parse a spaced-prefixed negative int" - doAssert lispReprStr(-1) == """(IntLit -1)""" - doAssert -1.0'f64 == minusOne.float64 - doAssert lispReprStr(-1.000'f64) == """(Float64Lit -1.0)""" - doAssert lispReprStr( -1.000'f64) == """(Float64Lit -1.0)""" - doAssert [-1].contains(minusOne): - "unable to handle negatives after square bracket" - doAssert lispReprStr([-1]) == """(Bracket (IntLit -1))""" - doAssert (-1, 2)[0] == minusOne: - "unable to handle negatives after parenthesis" - doAssert lispReprStr((-1, 2)) == """(TupleConstr (IntLit -1) (IntLit 2))""" - proc x(): int = - var a = 1;-1 # the -1 should act as the return value - doAssert x() == minusOne: - "unable to handle negatives after semi-colon" - - block: - doAssert -0b111 == -7 - doAssert -0xff == -255 - doAssert -128'i8 == (-128).int8 - doAssert $(-128'i8) == "-128" - doAssert -32768'i16 == int16.low - doAssert -2147483648'i32 == int32.low - when int.sizeof > 4: - doAssert -9223372036854775808 == int.low - when not defined(js): - doAssert -9223372036854775808 == int64.low - - block: # check when a minus (-) is an unary op - doAssert -one == minusOne: - "unable to a negative prior to identifier" - - block: # check when a minus (-) is a a subtraction op - doAssert 4-1 == 3: - "unable to handle subtraction sans surrounding spaces with a numeric literal" - doAssert 4-one == 3: - "unable to handle subtraction sans surrounding spaces with an identifier" - doAssert 4 - 1 == 3: - "unable to handle subtraction with surrounding spaces with a numeric literal" - doAssert 4 - one == 3: - "unable to handle subtraction with surrounding spaces with an identifier" - -static: main() -main() diff --git a/tests/lexer/tunicode_operators.nim b/tests/lexer/tunicode_operators.nim deleted file mode 100644 index 6ad40beab..000000000 --- a/tests/lexer/tunicode_operators.nim +++ /dev/null @@ -1,16 +0,0 @@ -#{.experimental: "unicodeOperators".} - -proc `⊙`(x, y: int): int = x * y -proc `⊙=`(x: var int, y: int) = x *= y - -proc `⊞++`(x, y: int): int = x + y - -const a = 9 - -var x = 45 -x ⊙= a⊞++4⊙3 - -var y = 45 -y *= 9 + 4 * 3 - -assert x == y diff --git a/tests/lookups/tredef.nim b/tests/lookups/tredef.nim index 7c1df238e..8f1b38bd1 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) {.redefine.} = bar(a, b) +template foo(a: int, b: string) = bar(a, b) foo(1, "test") block: diff --git a/tests/macros/m18235.nim b/tests/macros/m18235.nim deleted file mode 100644 index 6bb4547e0..000000000 --- a/tests/macros/m18235.nim +++ /dev/null @@ -1,42 +0,0 @@ -import macros - -# Necessary code to update the AST on a symbol across module boundaries when -# processed by a type macro. Used by a test of a corresponding name of this -# file. - -macro eexport(n: typed): untyped = - result = copyNimTree(n) - # turn exported nnkSym -> nnkPostfix(*, nnkIdent), forcing re-sem - result[0] = nnkPostfix.newTree(ident"*").add: - n.name.strVal.ident - -macro unexport(n: typed): untyped = - result = copyNimTree(n) - # turn nnkSym -> nnkIdent, forcing re-sem and dropping any exported-ness - # that might be present - result[0] = n.name.strVal.ident - -proc foo*() {.unexport.} = discard -proc bar() {.eexport.} = discard - -proc foooof*() {.unexport, eexport, unexport.} = discard -proc barrab() {.eexport, unexport, eexport.} = discard - -macro eexportMulti(n: typed): untyped = - # use the call version of `eexport` macro for one or more decls - result = copyNimTree(n) - for i in 0.. int): (int) -> int = doAssert pass2((x, y) => x + y)(4) == 6 -const fun = (x, y: int) {.noSideEffect.} => x + y +fun(x, y: int) {.noSideEffect.} => x + y doAssert typeof(fun) is (proc (x, y: int): int {.nimcall.}) doAssert fun(3, 4) == 7 @@ -68,7 +70,9 @@ m: proc mystuff() = echo "yes" -const typedParamAndPragma = (x, y: int) -> int => x + y +sugarWithPragma() {.m.} => echo "sugarWithPragma called" + +typedParamAndPragma(x, y: int) {.used.} -> int => x + y doAssert typedParamAndPragma(1, 2) == 3 type @@ -78,3 +82,5 @@ type var myBot = Bot() myBot.call = () {.noSideEffect.} => "I'm a bot." doAssert myBot.call() == "I'm a bot." + + diff --git a/tests/macros/tdumpast.nim b/tests/macros/tdumpast.nim index 484b3c2f3..b9d224ab8 100644 --- a/tests/macros/tdumpast.nim +++ b/tests/macros/tdumpast.nim @@ -2,69 +2,34 @@ import macros -block: - template plus(a, b: untyped): untyped {.dirty} = - a + b +template plus(a, b: untyped): untyped {.dirty} = + a + b - macro call(e: untyped): untyped = - result = newCall("foo", newStrLitNode("bar")) +macro call(e: untyped): untyped = + result = newCall("foo", newStrLitNode("bar")) - macro dumpAST(n: untyped): string = - var msg = "" - msg.add "lispRepr:\n" & n.lispRepr & "\n" - msg.add "treeRepr:\n" & n.treeRepr & "\n" +macro dumpAST(n: untyped): untyped = + # dump AST as a side-effect and return the inner node + let n = callsite() + echo n.lispRepr + echo n.treeRepr - var plusAst = getAst(plus(1, 2)) - msg.add "lispRepr:\n" & n.lispRepr & "\n" + var plusAst = getAst(plus(1, 2)) + echo plusAst.lispRepr - var callAst = getAst(call(4)) - msg.add "callAst.lispRepr:\n" & callAst.lispRepr & "\n" + var callAst = getAst(call(4)) + echo callAst.lispRepr - var e = parseExpr("foo(bar + baz)") - msg.add "e.lispRepr:\n" & e.lispRepr & "\n" - result = msg.newLit + var e = parseExpr("foo(bar + baz)") + echo e.lispRepr - let a = dumpAST: - proc add(x, y: int): int = - return x + y - const foo = 3 + result = n[1] - doAssert a == """ -lispRepr: -(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3)))) -treeRepr: -StmtList - ProcDef - Ident "add" - Empty - Empty - FormalParams - Ident "int" - IdentDefs - Ident "x" - Ident "y" - Ident "int" - Empty - Empty - Empty - StmtList - ReturnStmt - Infix - Ident "+" - Ident "x" - Ident "y" - ConstSection - ConstDef - Ident "foo" - Empty - IntLit 3 -lispRepr: -(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3)))) -callAst.lispRepr: -(Call (Ident "foo") (StrLit "bar")) -e.lispRepr: -(Call (Ident "foo") (Infix (Ident "+") (Ident "bar") (Ident "baz"))) -""" +dumpAST: + proc add(x, y: int): int = + return x + y + + proc sub(x, y: int): int = return x - y macro fun() = let n = quote do: @@ -83,79 +48,3 @@ macro fun3(): untyped = int | float | array | seq | object | ptr | pointer | float32 doAssert n.repr == "int | float | array | seq | object | ptr | pointer | float32", n.repr fun3() - -macro fun4() = - let n = quote do: - (a: 1) - doAssert n.repr == "(a: 1)", n.repr -fun4() - -# nkTupleConstr vs nkPar tests: -block: # lispRepr - macro lispRepr2(a: untyped): string = newLit a.lispRepr - - doAssert lispRepr2(()) == """(TupleConstr)""" - doAssert lispRepr2((a: 1)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)))""" - doAssert lispRepr2((a: 1, b: 2)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)) (ExprColonExpr (Ident "b") (IntLit 2)))""" - doAssert lispRepr2((1,)) == """(TupleConstr (IntLit 1))""" - doAssert lispRepr2((1, 2)) == """(TupleConstr (IntLit 1) (IntLit 2))""" - doAssert lispRepr2((1, 2, 3.0)) == """(TupleConstr (IntLit 1) (IntLit 2) (FloatLit 3.0))""" - doAssert lispRepr2((1)) == """(Par (IntLit 1))""" - doAssert lispRepr2((1+2)) == """(Par (Infix (Ident "+") (IntLit 1) (IntLit 2)))""" - -block: # repr - macro repr2(a: untyped): string = newLit a.repr - - doAssert repr2(()) == "()" - doAssert repr2((a: 1)) == "(a: 1)" - doAssert repr2((a: 1, b: 2)) == "(a: 1, b: 2)" - doAssert repr2((1,)) == "(1,)" - doAssert repr2((1, 2)) == "(1, 2)" - doAssert repr2((1, 2, 3.0)) == "(1, 2, 3.0)" - doAssert repr2((1)) == "(1)" - doAssert repr2((1+2)) == "(1 + 2)" - -block: # treeRepr - macro treeRepr2(a: untyped): string = newLit a.treeRepr - macro treeRepr3(a: typed): string = newLit a.treeRepr - - doAssert treeRepr2(1+1 == 2) == """ -Infix - Ident "==" - Infix - Ident "+" - IntLit 1 - IntLit 1 - IntLit 2""" - - proc baz() = discard - proc baz(a: int) = discard - proc baz(a: float) = discard - - doAssert treeRepr3(baz()) == """ -Call - Sym "baz"""" - - let a = treeRepr3(block: - proc bar(a: auto) = baz()) - doAssert a == """ -BlockStmt - Empty - ProcDef - Sym "bar" - Empty - GenericParams - Sym "a:type" - FormalParams - Empty - IdentDefs - Sym "a" - Sym "auto" - Empty - Empty - Bracket - Empty - Empty - StmtList - Call - OpenSymChoice 3 "baz"""" diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index 66722a234..de132f253 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -1,5 +1,5 @@ discard """ - nimout: '''foo = "muhaha" + nimout: '''"muhaha" proc poo(x, y: int) = let y = x echo ["poo"]''' diff --git a/tests/macros/tgettype.nim b/tests/macros/tgettype.nim index d91efe1fe..bd70a1c30 100644 --- a/tests/macros/tgettype.nim +++ b/tests/macros/tgettype.nim @@ -1,85 +1,29 @@ -import std/macros -import stdtest/testutils +discard """ +output: ''' +(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password"))) +(BracketExpr (Sym "typeDesc") (Sym "User")) +''' +""" +import macros -# getType +type + Model = object of RootObj + User = object of Model + name : string + password : string -block: - type - Model = object of RootObj - User = object of Model - name : string - password : string +macro testUser: string = + result = newLit(User.getType.lispRepr) - macro testUser: string = - result = newLit(User.getType.lispRepr) +macro testGeneric(T: typedesc[Model]): string= + result = newLit(T.getType.lispRepr) - macro testGeneric(T: typedesc[Model]): string= - result = newLit(T.getType.lispRepr) +echo testUser +echo User.testGeneric - doAssert testUser == """(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password")))""" - doAssert User.testGeneric == """(BracketExpr (Sym "typeDesc") (Sym "User"))""" +macro assertVoid(e: typed): untyped = + assert(getTypeInst(e).typeKind == ntyVoid) - macro assertVoid(e: typed): untyped = - assert(getTypeInst(e).typeKind == ntyVoid) +proc voidProc() = discard - proc voidProc() = discard - - assertVoid voidProc() - -block: - # refs #18220; not an actual solution (yet) but at least shows what's currently - # possible - - type Callable1[R, T, U] = concept fn - fn(default(T)) is R - fn is U - - # note that typetraits.arity doesn't work - macro arity(a: typed): int = - # number of params - # this is not production code! - let a2 = a.getType[1] # this used to crash nim, with: `vmdeps.nim(292, 25) `false`` - newLit a2.len - 1 - - type Callable2[R, T, U] = concept fn - fn(default(T)) is R - fn is U - arity(U) == 2 - - proc map1[T, R, U](a: T, fn: Callable1[R, T, U]): R = - let fn = U(fn) - # `cast[U](fn)` would also work; - # this is currently needed otherwise, sigmatch errors with: - # Error: attempting to call routine: 'fn' - # found 'fn' [param declared in tgettype.nim(53, 28)] - # this can be fixed in future work - fn(a) - - proc map2[T, R, U](a: T, fn: Callable2[R, T, U]): R = - let fn = U(fn) - fn(a) - - proc fn1(a: int, a2 = 'x'): string = $(a, a2, "fn1") - proc fn2(a: int, a2 = "zoo"): string = $(a, a2, "fn2") - proc fn3(a: int, a2 = "zoo2"): string = $(a, a2, "fn3") - proc fn4(a: int): string {.inline.} = $(a, "fn4") - proc fn5(a: int): string = $(a, "fn5") - - assertAll: - # Callable1 - 1.map1(fn1) == """(1, 'x', "fn1")""" - 1.map1(fn2) == """(1, "zoo", "fn2")""" - 1.map1(fn3) == """(1, "zoo", "fn3")""" - # fn3's optional param is not honored, because fn3 and fn2 yield same - # generic instantiation; this is a caveat with this approach - # There are several possible ways to improve things in future work. - 1.map1(fn4) == """(1, "fn4")""" - 1.map1(fn5) == """(1, "fn5")""" - - # Callable2; prevents passing procs with optional params to avoid above - # mentioned caveat, but more restrictive - not compiles(1.map2(fn1)) - not compiles(1.map2(fn2)) - not compiles(1.map2(fn3)) - 1.map2(fn4) == """(1, "fn4")""" - 1.map2(fn5) == """(1, "fn5")""" +assertVoid voidProc() diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim index e722f14aa..2f9557160 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 {.packed,myAttr,serializationKey: "one".} = object + MyObj = object {.packed,myAttr,serializationKey: "one".} myField {.myAttr2,serializationKey: "two".}: int myField2 {.myAttr3,serializationKey: "three".}: float # field pragmas not currently supported test(MyObj): type - _ {.packed,myAttr,serializationKey: "one".} = object + _ = object {.packed,myAttr,serializationKey: "one".} myField: int myField2: float diff --git a/tests/macros/tmacrogetimpl.nim b/tests/macros/tmacrogetimpl.nim deleted file mode 100644 index 1d996ff29..000000000 --- a/tests/macros/tmacrogetimpl.nim +++ /dev/null @@ -1,31 +0,0 @@ -import macros - -# bug #5034 - -macro copyImpl(srsProc: typed, toSym: untyped) = - result = copyNimTree(getImplTransformed(srsProc)) - result[0] = ident $toSym.toStrLit() - -proc foo1(x: float, one: bool = true): float = - if one: - return 1'f - result = x - -proc bar1(what: string): string = - ## be a little more adversarial with `skResult` - proc buzz: string = - result = "lightyear" - if what == "buzz": - result = "buzz " & buzz() - else: - result = what - return result - -copyImpl(foo1, foo2) -doAssert foo1(1'f) == 1.0 -doAssert foo2(10.0, false) == 10.0 -doAssert foo2(10.0) == 1.0 - -copyImpl(bar1, bar2) -doAssert bar1("buzz") == "buzz lightyear" -doAssert bar1("macros") == "macros" diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim index c588ff7e6..53af4287c 100644 --- a/tests/macros/tmacros1.nim +++ b/tests/macros/tmacros1.nim @@ -60,22 +60,3 @@ macro foo(t: static Tuple): untyped = doAssert t.b == 12345 foo((a: "foo", b: 12345)) - - -# bug #16307 - -macro bug(x: untyped): string = - newLit repr(x) - -let res = bug: - block: - ## one - ## two - ## three - -doAssert res == """ - -block: - ## one - ## two - ## three""" diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index a81c51658..19c706a82 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -64,7 +64,7 @@ block t7723: block t8706: macro varargsLen(args:varargs[untyped]): untyped = - doAssert args.kind == nnkArgList + doAssert args.kind == nnkArglist doAssert args.len == 0 result = newLit(args.len) @@ -484,26 +484,6 @@ func expMin: float {.aadMin.} = 1 echo expMin() -# doubly-typed forward decls -macro noop(x: typed) = x -noop: - proc cally() = discard - -cally() - -noop: - proc barry() - -proc barry() = discard - -# some more: -proc barry2() {.noop.} -proc barry2() = discard - -proc barry3() {.noop.} -proc barry3() {.noop.} = discard - - # issue #15389 block double_sem_for_procs: @@ -518,4 +498,4 @@ block double_sem_for_procs: return x1 + 1.0 result = 10.0 - discard exp(5.0) + discard exp(5.0) \ No newline at end of file diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 2446912ea..00d74cb5c 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -23,8 +23,6 @@ x: some string ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) 0 -0 -0 ''' """ @@ -91,7 +89,7 @@ block tlexerex: -block tcopylineinfo: +block tlineinfo: # issue #5617, feature request type Test = object @@ -103,27 +101,6 @@ block tcopylineinfo: var z = mixer(Test) doAssert z -block tsetgetlineinfo: - # issue #21098, feature request - type Test = object - - macro mixer1(n: typed): untyped = - let x = newIdentNode("echo") - var lineInfo = n.lineInfoObj - x.setLineInfo lineInfo - result = newLit(x.lineInfo == n.lineInfo) - - macro mixer2(n: typed): untyped = - let x = newIdentNode("echo") - var lineInfo = n.lineInfoObj - lineInfo.line += 1 - x.setLineInfo lineInfo - result = newLit(x.lineInfo != n.lineInfo) - - doAssert mixer1(Test) - - doAssert mixer2(Test) - block tdebugstmt: @@ -293,68 +270,3 @@ block: # bug #13511 debugAst: add(foo(), component) - -block: # bug #15118 - macro flop(name: static string) = - let id = genSym(nskType, "env") - let r = - nnkStmtList.newTree( - nnkTypeSection.newTree( - nnkTypeDef.newTree( - id, - newEmptyNode(), - nnkRefTy.newTree( - nnkObjectTy.newTree( - newEmptyNode(), - newEmptyNode(), - nnkRecList.newTree( - nnkIdentDefs.newTree( - newIdentNode(name), - newIdentNode("int"), - newEmptyNode() - ) - ) - ) - ) - ) - ), - - # var f: T - - nnkVarSection.newTree( - nnkIdentDefs.newTree( - newIdentNode("f"), - id, - newEmptyNode() - ) - ), - - # echo f.a - nnkCommand.newTree( - newIdentNode("new"), - newIdentNode("f") - ), - - nnkCommand.newTree( - newIdentNode("echo"), - nnkDotExpr.newTree( - newIdentNode("f"), - newIdentNode(name) - ) - ) - ) - r - - - block: - flop("a") - - block: - flop("b") - -static: - block: - const containsTable = CacheTable"containsTable" - doAssert "foo" notin containsTable - containsTable["foo"] = newLit 42 - doAssert "foo" in containsTable diff --git a/tests/macros/tparsefile.nim b/tests/macros/tparsefile.nim deleted file mode 100644 index a41223f80..000000000 --- a/tests/macros/tparsefile.nim +++ /dev/null @@ -1,11 +0,0 @@ -import macros - -static: - let fn = "mparsefile.nim" - var raised = false - try: - discard parseStmt(staticRead(fn), filename = fn) - except ValueError as e: - raised = true - doAssert e.msg == "mparsefile.nim(4, 1) Error: invalid indentation" - doAssert raised diff --git a/tests/macros/tprochelpers.nim b/tests/macros/tprochelpers.nim deleted file mode 100644 index d95a2ced8..000000000 --- a/tests/macros/tprochelpers.nim +++ /dev/null @@ -1,22 +0,0 @@ -import std/macros -import stdtest/testutils - -macro test1(prc: untyped): untyped = - assertAll: - prc.params.len == 2 - prc.params[1].len == 4 - prc.pragma.len == 2 - - prc.params = nnkFormalParams.newTree( - ident("int") - ) - prc.pragma = newEmptyNode() - - assertAll: - prc.params.len == 1 - prc.pragma.len == 0 - prc - -proc test(a, b: int): int {.gcsafe, raises: [], test1.} = 5 - -type hello = proc(a, b: int): int {.gcsafe, raises: [], test1.} diff --git a/tests/macros/tstaticparamsmacro.nim b/tests/macros/tstaticparamsmacro.nim index 2632ca730..8bd653920 100644 --- a/tests/macros/tstaticparamsmacro.nim +++ b/tests/macros/tstaticparamsmacro.nim @@ -7,9 +7,9 @@ numbers 11 22 AST a -@[(c: 11, d: 22), (c: 33, d: 44)] +[(11, 22), (33, 44)] AST b -(e: @[55, 66], f: @[77, 88]) +([55, 66], [77, 88]) 55 10 20Test diff --git a/tests/macros/tstructuredlogging.nim b/tests/macros/tstructuredlogging.nim index 5e4fdea3f..05bb52a40 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 {.redefine.} = `newScopeDefinition` + template scopeHolder = `newScopeDefinition` template scope(newBindings: untyped) {.dirty.} = mergeScopes(bindSym"scopeHolder", newBindings) diff --git a/tests/macros/ttypenodes.nim b/tests/macros/ttypenodes.nim deleted file mode 100644 index 233ea9780..000000000 --- a/tests/macros/ttypenodes.nim +++ /dev/null @@ -1,16 +0,0 @@ -import macros - -macro makeEnum(): untyped = - newTree(nnkEnumTy, newEmptyNode(), ident"a", ident"b", ident"c") - -macro makeObject(): untyped = - newTree(nnkObjectTy, newEmptyNode(), newEmptyNode(), newTree(nnkRecList, - newTree(nnkIdentDefs, ident"x", ident"y", ident"int", newEmptyNode()))) - -type - Foo = makeEnum() - Bar = makeObject() - -doAssert {a, b, c} is set[Foo] -let bar = Bar(x: 3, y: 4) -doAssert (bar.x, bar.y) == (3, 4) diff --git a/tests/macros/tvtable.nim b/tests/macros/tvtable.nim index 0f322b7d5..dc07a726b 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 {.inheritable.} = object + TBase = object {.inheritable.} vtbl: ptr VTable TUserObject1 = object of TBase diff --git a/tests/magics/t10307.nim b/tests/magics/t10307.nim index b5a93c5c6..b5bbfdfa8 100644 --- a/tests/magics/t10307.nim +++ b/tests/magics/t10307.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim c --mm:refc -d:useGcAssert $file" + cmd: "nim c -d:useGcAssert $file" output: '''running someProc(true) res: yes yes diff --git a/tests/magics/t20938.nim b/tests/magics/t20938.nim deleted file mode 100644 index 7341cbb91..000000000 --- a/tests/magics/t20938.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ - cmd: "nim c --mm:refc $file" - action: "compile" -""" - -template foo(x: typed) = - discard x - -foo: - var x = "hello" - x.shallowCopy("test") - true diff --git a/tests/magics/tmagics.nim b/tests/magics/tmagics.nim index e52912b74..fa138320c 100644 --- a/tests/magics/tmagics.nim +++ b/tests/magics/tmagics.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" output: ''' true true @@ -19,7 +18,7 @@ block tlowhigh: for i in low(a) .. high(a): a[i] = 0 - proc sum(a: openArray[int]): int = + proc sum(a: openarray[int]): int = result = 0 for i in low(a)..high(a): inc(result, a[i]) @@ -56,11 +55,3 @@ block t9442: GC_unref(v2) GC_ref(v3) GC_unref(v3) - -block: # bug #6499 - let x = (chr, 0) - doAssert x[1] == 0 - -block: # bug #12229 - proc foo(T: typedesc) = discard - foo(ref) diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index e2c1b0a04..b9788a81d 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -225,7 +225,7 @@ template raise_or_quit(exception, message: untyped) = template run_custom_proc(parsed_parameter: Tparsed_parameter, custom_validator: Tparameter_callback, - parameter: string) = + parameter: TaintedString) = ## Runs the custom validator if it is not nil. ## ## Pass in the string of the parameter triggering the call. If the @@ -318,7 +318,7 @@ proc echo_help*(expected: seq[Tparameter_specification] = @[], proc parse*(expected: seq[Tparameter_specification] = @[], - type_of_positional_parameters = PK_STRING, args: seq[string] = @[], + type_of_positional_parameters = PK_STRING, args: seq[TaintedString] = @[], bad_prefixes = @["-", "--"], end_of_options = "--", quit_on_failure = true): Tcommandline_results = ## Parses parameters and returns results. diff --git a/tests/manyloc/keineschweine/dependencies/enet/enet.nim b/tests/manyloc/keineschweine/dependencies/enet/enet.nim index 5dee6ae9c..02ebef595 100644 --- a/tests/manyloc/keineschweine/dependencies/enet/enet.nim +++ b/tests/manyloc/keineschweine/dependencies/enet/enet.nim @@ -265,7 +265,7 @@ const ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000 ENET_PEER_FREE_RELIABLE_WINDOWS = 8 -when defined(linux) or true: +when defined(Linux) or true: import posix const ENET_SOCKET_NULL*: cint = -1 @@ -295,7 +295,7 @@ when defined(linux) or true: template ENET_SOCKETSET_CHECK*(sockset, socket: untyped): untyped = FD_ISSET(socket, addr((sockset))) -when defined(windows): +when defined(Windows): ## put the content of win32.h in here diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim b/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim index a0c8a7a3c..ad0d20f1c 100644 --- a/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim +++ b/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim @@ -1,7 +1,7 @@ import streams from strutils import repeat -proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): string = +proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString = var lastChr = length result = s.readStr(length) while lastChr >= 0 and result[lastChr - 1] == padChar: dec(lastChr) @@ -16,7 +16,7 @@ proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') = else: s.write(str) -proc readLEStr*(s: PStream): string = +proc readLEStr*(s: PStream): TaintedString = var len = s.readInt16() result = s.readStr(len) diff --git a/tests/manyloc/keineschweine/dependencies/nake/nake.nim b/tests/manyloc/keineschweine/dependencies/nake/nake.nim index e466ee5e1..bc380986f 100644 --- a/tests/manyloc/keineschweine/dependencies/nake/nake.nim +++ b/tests/manyloc/keineschweine/dependencies/nake/nake.nim @@ -7,10 +7,6 @@ contents thereof. As said in the Olde Country, `Keepe it Gangster'.""" -#[ -xxx remove this? seems mostly duplicate of: tests/manyloc/nake/nake.nim -]# - import strutils, parseopt, tables, os type @@ -64,8 +60,7 @@ when true: args.add " " quit(shell("nim", "c", "-r", "nakefile.nim", args)) else: - import std/exitprocs - addExitProc(proc() {.noconv.} = + addQuitProc(proc() {.noconv.} = var task: string printTaskList: bool diff --git a/tests/manyloc/keineschweine/keineschweine.nim.cfg b/tests/manyloc/keineschweine/keineschweine.nim.cfg index a670e2b77..f335a0e0e 100644 --- a/tests/manyloc/keineschweine/keineschweine.nim.cfg +++ b/tests/manyloc/keineschweine/keineschweine.nim.cfg @@ -7,4 +7,4 @@ path = "dependencies/genpacket" path = "enet_server" debugger = off warning[SmallLshouldNotBeUsed] = off -mm = refc +nilseqs = on diff --git a/tests/manyloc/keineschweine/lib/estreams.nim b/tests/manyloc/keineschweine/lib/estreams.nim index c5e45e0e7..5ab029b52 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: sink T) = +proc write*[T: SomeNumber|bool|char|byte](buffer: PBuffer; val: T) = var v: T - v = val + shallowCopy v, val writeBE buffer, v proc readInt8*(buffer: PBuffer): int8 = diff --git a/tests/manyloc/keineschweine/lib/glut.nim b/tests/manyloc/keineschweine/lib/glut.nim index de9a97456..44a290728 100644 --- a/tests/manyloc/keineschweine/lib/glut.nim +++ b/tests/manyloc/keineschweine/lib/glut.nim @@ -93,7 +93,7 @@ const GLUT_NORMAL* = 0 GLUT_OVERLAY* = 1 -when defined(windows): +when defined(Windows): const # Stroke font constants (use these in GLUT program). GLUT_STROKE_ROMAN* = cast[Pointer](0) GLUT_STROKE_MONO_ROMAN* = cast[Pointer](1) # Bitmap font constants (use these in GLUT program). diff --git a/tests/manyloc/keineschweine/lib/sg_gui.nim b/tests/manyloc/keineschweine/lib/sg_gui.nim index bcf415556..83ad0d1bf 100644 --- a/tests/manyloc/keineschweine/lib/sg_gui.nim +++ b/tests/manyloc/keineschweine/lib/sg_gui.nim @@ -193,28 +193,28 @@ proc setActive*(t: PTextEntry) = if not t.isNil and not t.inputClient.isNil: input_helpers.setActive(t.inputClient) -when false: - proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea = - new(result) - result.messages = @[] - result.pos = position - container.add(result) - proc add*(m: PMessageArea, text: string): PText = - result = messageProto.copy() - result.setString(text) - m.messages.add(result) - let nmsgs = len(m.messages) - var pos = vec2f(m.pos.x, m.pos.y) - for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)): - setPosition(m.messages[i], pos) - pos.y -= 16.0 - proc draw*(window: PRenderWindow; m: PMessageArea) = - let nmsgs = len(m.messages) - if nmsgs == 0: return - for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)): - window.draw(m.messages[i]) +discard """proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea = + new(result) + result.messages = @[] + result.pos = position + container.add(result) +proc add*(m: PMessageArea, text: string): PText = + result = messageProto.copy() + result.setString(text) + m.messages.add(result) + let nmsgs = len(m.messages) + var pos = vec2f(m.pos.x, m.pos.y) + for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)): + setPosition(m.messages[i], pos) + pos.y -= 16.0 +proc draw*(window: PRenderWindow; m: PMessageArea) = + let nmsgs = len(m.messages) + if nmsgs == 0: return + for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)): + window.draw(m.messages[i]) +""" proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea = new(result) result.messages = @[] diff --git a/tests/manyloc/keineschweine/lib/zlib_helpers.nim b/tests/manyloc/keineschweine/lib/zlib_helpers.nim index e51c000c8..895f41759 100644 --- a/tests/manyloc/keineschweine/lib/zlib_helpers.nim +++ b/tests/manyloc/keineschweine/lib/zlib_helpers.nim @@ -1,26 +1,21 @@ -# xxx this test is bad (echo instead of error, etc) - import zip/zlib proc compress*(source: string): string = var sourcelen = source.len - destLen = sourcelen + (sourcelen.float * 0.1).int + 16 + destlen = sourcelen + (sourcelen.float * 0.1).int + 16 result = "" result.setLen destLen - # see http://www.zlib.net/zlib-1.2.11.tar.gz for correct definitions - var destLen2 = destLen.Ulongf - var res = zlib.compress(cstring(result), addr destLen2, cstring(source), sourceLen.Ulong) + var res = zlib.compress(cstring(result), addr destLen, cstring(source), sourceLen) if res != Z_OK: echo "Error occurred: ", res - elif destLen2.int < result.len: - result.setLen(destLen2.int) + elif destLen < result.len: + result.setLen(destLen) proc uncompress*(source: string, destLen: var int): string = result = "" result.setLen destLen - var destLen2 = destLen.Ulongf - var res = zlib.uncompress(cstring(result), addr destLen2, cstring(source), source.len.Ulong) + var res = zlib.uncompress(cstring(result), addr destLen, cstring(source), source.len) if res != Z_OK: echo "Error occurred: ", res diff --git a/tests/manyloc/nake/nake.nim b/tests/manyloc/nake/nake.nim index ce7faab5c..87db5b54d 100644 --- a/tests/manyloc/nake/nake.nim +++ b/tests/manyloc/nake/nake.nim @@ -60,8 +60,7 @@ when true: args.add " " quit(shell("nim", "c", "-r", "nakefile.nim", args)) else: - import std/exitprocs - addExitProc(proc() {.noconv.} = + addQuitProc(proc() {.noconv.} = var task: string printTaskList: bool diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim index fc479a5c2..95058c277 100644 --- a/tests/manyloc/nake/nakefile.nim +++ b/tests/manyloc/nake/nakefile.nim @@ -29,12 +29,11 @@ task "test2", "Build release test build test release build": if shell("nim", ReleaseDefines, ReleaseTestDefines, "compile", ExeName) == 0: shell "."/ExeName -when false: - task "dirserver", "build the directory server": - withDir "server": - if shell("nim", ServerDefines, "compile", "dirserver") != 0: - echo "Failed to build the dirserver" - quit 1 +discard """task "dirserver", "build the directory server": + withDir "server": + if shell("nim", ServerDefines, "compile", "dirserver") != 0: + echo "Failed to build the dirserver" + quit 1""" task "zoneserver", "build the zone server": withDir "enet_server": diff --git a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim index 29e23f9d0..7d787c07b 100644 --- a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim +++ b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim @@ -53,7 +53,7 @@ else: const glRealType* = cGLfloat -proc setUniformV4*[T](loc: GLint, vecs: var openArray[TV4[T]]) = +proc setUniformV4*[T](loc: GLint, vecs: var openarray[TV4[T]]) = glUniform4fv(loc, vecs.len.GLsizei, cast[ptr GLfloat](vecs[0].addr)) proc setUniformV4*[T](loc: GLint, vec: TV4[T]) = diff --git a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim index accc2d96b..3bfaf1cbc 100644 --- a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim +++ b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim @@ -47,7 +47,7 @@ proc newVert*(rect: rect.TRect): seq[TVert] = proc newVertAttrib(i: GLuint, size: GLint, stride: GLsizei, offset: GLvoid): TVertAttrib = TVertAttrib(i: i, size: size, stride: stride, offset: offset) -proc genBuf*[T](vboTarget, objUsage: GLenum, data: var openArray[T]): GLuint = +proc genBuf*[T](vboTarget, objUsage: GLenum, data: var openarray[T]): GLuint = result = 0.GLuint ?glGenBuffers(1, result.addr) ?glBindBuffer(vboTarget, result) diff --git a/tests/manyloc/standalone2/tavr.nim.cfg b/tests/manyloc/standalone2/tavr.nim.cfg index 2a31618d0..e5291969d 100644 --- a/tests/manyloc/standalone2/tavr.nim.cfg +++ b/tests/manyloc/standalone2/tavr.nim.cfg @@ -2,4 +2,3 @@ --cpu:avr --os:standalone --compileOnly ---threads:off diff --git a/tests/metatype/tcps.nim b/tests/metatype/tcps.nim deleted file mode 100644 index 042e32bd6..000000000 --- a/tests/metatype/tcps.nim +++ /dev/null @@ -1,35 +0,0 @@ -discard """ - output: '''10 -string''' -""" - -# bug #18059 -type - C = ref object of RootObj - fn: ContProc - ex: ref Exception - - ContProc = proc (c: C): C {.nimcall.} - -proc noop(c: C): C = c - -type - Env[T] = ref object of C - x: T - -proc foo_cont[U](c: C): C = - proc afterCall[V](c: C): C = - echo Env[V](c).x - - c.fn = afterCall[U] - return noop(c) - -proc foo[T](x: T): C = - return Env[T](fn: foo_cont[T], x: x) - -proc tramp(c: sink C) = - while c != nil and c.fn != nil: - c = c.fn(c) - -tramp foo(10) -tramp foo("string") diff --git a/tests/metatype/tmetatype_various.nim b/tests/metatype/tmetatype_various.nim index c17410a06..9faeeec4a 100644 --- a/tests/metatype/tmetatype_various.nim +++ b/tests/metatype/tmetatype_various.nim @@ -1,5 +1,4 @@ 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 bcf28d331..16a6e56b8 100644 --- a/tests/metatype/tstaticparammacro.nim +++ b/tests/metatype/tstaticparammacro.nim @@ -6,9 +6,9 @@ numbers 11 22 AST a -@[(c: 11, d: 22), (c: 33, d: 44)] +[(11, 22), (33, 44)] AST b -(e: @[55, 66], f: @[77, 88]) +([55, 66], [77, 88]) 55 10 20Test diff --git a/tests/metatype/tstaticvector.nim b/tests/metatype/tstaticvector.nim index 85a66974b..ca16518fe 100644 --- a/tests/metatype/tstaticvector.nim +++ b/tests/metatype/tstaticvector.nim @@ -1,10 +1,9 @@ discard """ - matrix: "--mm:orc" output: '''0 0 2 100 -30.0 TVec[1, system.float32](data: [2.0]) +30.0 [data = [2.0]] ''' """ diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index d3a58853d..b691d3488 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -1,10 +1,8 @@ discard """ - matrix: "--mm:arc; --mm:refc" - output: ''' +output: ''' proc Base proc Child method Base -method Child yield Base yield Child 12 @@ -26,7 +24,8 @@ Base.pr Child.pr Base.me -Child.me #<- bug #2710 +when false: + Child.me #<- bug #2710 for s in Base.it: echo s for s in Child.it: echo s #<- bug #2662 diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index bfaa23057..326faac82 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -1,21 +1,6 @@ import typetraits import macros -block: # toUnsigned, toSigned - var a1: toSigned(int16) - doAssert a1 is int16 - var a2: toSigned(uint16) - doAssert $a2.typeof == "int16" - doAssert toSigned(uint32) is int32 - doAssert uint64.toSigned is int64 - doAssert int64.toSigned is int64 - doAssert int64.toUnsigned is uint64 - doAssert int.toUnsigned is uint - doAssert $uint.toUnsigned == "uint" - # disallowed for now - doAssert not compiles(toUnsigned(range[0..7])) - doAssert not compiles(toSigned(range[0..7])) - block: # isNamedTuple type Foo1 = (a:1,).type type Foo2 = (Field0:1,).type @@ -105,8 +90,6 @@ block distinctBase: Foo[T] = distinct seq[T] var a: Foo[int] doAssert a.type.distinctBase is seq[int] - doAssert seq[int].distinctBase is seq[int] - doAssert "abc".distinctBase == "abc" block: # simplified from https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458 @@ -299,12 +282,7 @@ block genericHead: doAssert not compiles(genericHead(Foo)) type Bar = object doAssert not compiles(genericHead(Bar)) - - when false: # xxx not supported yet - doAssert seq[int].genericHead is seq - when false: # xxx not supported yet, gives: Error: identifier expected - type Hoo[T] = object - doAssert genericHead(Hoo[int])[float] is Hoo[float] + # doAssert seq[int].genericHead is seq block: # elementType iterator myiter(n: int): auto = diff --git a/tests/metatype/typeclassinference.nim b/tests/metatype/typeclassinference.nim index b3f197718..c845e04f7 100644 --- a/tests/metatype/typeclassinference.nim +++ b/tests/metatype/typeclassinference.nim @@ -19,25 +19,3 @@ var ptr1: ptr = addr(str1) var str2: string = "hello, world!" var ptr2: ptr = str2 -block: # built in typeclass inference - proc tupleA(): tuple = return (1, 2) - proc tupleB(): tuple = (1f, 2f) - assert typeof(tupleA()) is (int, int) - assert typeof(tupleB()) is (float32, float32) - - proc a(val: int or float): tuple = - when typeof(val) is int: - (10, 10) - else: - (30f, 30f) - - assert typeof(a(10)) is (int, int) - assert typeof(a(10.0)) is (float32, float32) - - proc b(val: int or float): set = - when typeof(val) is int: - {10u8, 3} - else: - {'a', 'b'} - assert typeof(b(10)) is set[uint8] - assert typeof(b(10.0)) is set[char] \ No newline at end of file diff --git a/tests/metatype/typedesc_as_value.nim b/tests/metatype/typedesc_as_value.nim index 463d23724..69eaf8a5c 100644 --- a/tests/metatype/typedesc_as_value.nim +++ b/tests/metatype/typedesc_as_value.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "invalid type: 'typedesc[int]' for var" + errormsg: "invalid type: 'type int' for var" """ diff --git a/tests/method/t20515.nim b/tests/method/t20515.nim deleted file mode 100644 index 1921f2e46..000000000 --- a/tests/method/t20515.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - errormsg: "Base method 'zzz' requires explicit '{.gcsafe.}' to be GC-safe" - line: 10 -""" - -type - A = ref object of RootObj - B = ref object of A - -method zzz(a: A) {.base.} = - discard - -var s: seq[int] -method zzz(a: B) = - echo s - -proc xxx(someObj: A) {.gcsafe.} = - someObj.zzz() - -xxx(B()) diff --git a/tests/method/tgeneric_methods.nim b/tests/method/tgeneric_methods.nim index ab92c66d8..0e2aeeede 100644 --- a/tests/method/tgeneric_methods.nim +++ b/tests/method/tgeneric_methods.nim @@ -1,5 +1,4 @@ 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 daaa46522..df4c3771a 100644 --- a/tests/method/tmethod_issues.nim +++ b/tests/method/tmethod_issues.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:arc; --mm:refc" output: ''' wof! wof! @@ -10,7 +9,7 @@ type B # bug #1659 -type Animal {.inheritable.} = ref object +type Animal = ref object {.inheritable.} type Dog = ref object of Animal method say(a: Animal): auto {.base.} = "wat!" @@ -160,11 +159,3 @@ 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 c41d04983..fd022717b 100644 --- a/tests/method/tmethod_various.nim +++ b/tests/method/tmethod_various.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:arc; --mm:refc" output: ''' do nothing HELLO WORLD! @@ -12,7 +11,7 @@ method somethin(obj: RootObj) {.base.} = echo "do nothing" type - TNode* {.inheritable.} = object + TNode* = object {.inheritable.} PNode* = ref TNode PNodeFoo* = ref object of TNode @@ -30,7 +29,7 @@ o.somethin() # tmproto type - Obj1 {.inheritable.} = ref object + Obj1 = ref object {.inheritable.} Obj2 = ref object of Obj1 method beta(x: Obj1): int {.base.} diff --git a/tests/method/tmultim.nim b/tests/method/tmultim.nim index 126eb7526..b6fed024e 100644 --- a/tests/method/tmultim.nim +++ b/tests/method/tmultim.nim @@ -16,7 +16,7 @@ do nothing # tmultim2 type - TThing {.inheritable.} = object + TThing = object {.inheritable.} TUnit = object of TThing x: int TParticle = object of TThing @@ -49,7 +49,7 @@ staticCollide(a, b) # tmultim6 type - Thing {.inheritable.} = object + Thing = object {.inheritable.} 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* {.inheritable.} = object + TNode* = object {.inheritable.} PNode* = ref TNode PNodeFoo* = ref object of TNode diff --git a/tests/method/tsingle_methods.nim b/tests/method/tsingle_methods.nim index b564e7d87..40269559a 100644 --- a/tests/method/tsingle_methods.nim +++ b/tests/method/tsingle_methods.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:arc --multimethods:off; --mm:refc --multimethods:off" + cmd: "nim c --multimethods:off $file" output: '''base base base diff --git a/tests/misc/m15316.nim b/tests/misc/m15316.nim deleted file mode 100644 index 188d06946..000000000 --- a/tests/misc/m15316.nim +++ /dev/null @@ -1 +0,0 @@ -proc foo = (if) diff --git a/tests/misc/m15955.nim b/tests/misc/m15955.nim deleted file mode 100644 index 22da345db..000000000 --- a/tests/misc/m15955.nim +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index a71af8121..000000000 --- a/tests/misc/m15955_main.nim +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index 942262a6e..000000000 --- a/tests/misc/m20149.nim +++ /dev/null @@ -1,2 +0,0 @@ -let x = 12 -echo x diff --git a/tests/misc/m20456.nims b/tests/misc/m20456.nims deleted file mode 100644 index 8a9313129..000000000 --- a/tests/misc/m20456.nims +++ /dev/null @@ -1 +0,0 @@ -echo 123 \ No newline at end of file diff --git a/tests/misc/mbetterrun.nim b/tests/misc/mbetterrun.nim deleted file mode 100644 index d4f427af0..000000000 --- a/tests/misc/mbetterrun.nim +++ /dev/null @@ -1,3 +0,0 @@ -const mbetterrunVal {.strdefine.} = "" -static: echo "compiling: " & mbetterrunVal -echo "running: " & mbetterrunVal diff --git a/tests/misc/mfield_defect.nim b/tests/misc/mfield_defect.nim deleted file mode 100644 index 53bfba40e..000000000 --- a/tests/misc/mfield_defect.nim +++ /dev/null @@ -1,30 +0,0 @@ -#[ -ran from trunner -]# - - - - - - -# line 10 -type Kind = enum k0, k1, k2, k3, k4 - -type Foo = object - case kind: Kind - of k0: f0: int - of k1: f1: int - of k2: f2: int - of k3: f3: int - of k4: f4: int - -proc main()= - var foo = Foo(kind: k3, f3: 3) - let s1 = foo.f3 - doAssert s1 == 3 - let s2 = foo.f2 - -when defined case1: - static: main() -when defined case2: - main() diff --git a/tests/misc/mjsondoc.nim b/tests/misc/mjsondoc.nim deleted file mode 100644 index e4642f0b4..000000000 --- a/tests/misc/mjsondoc.nim +++ /dev/null @@ -1,11 +0,0 @@ -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/mspellsuggest.nim b/tests/misc/mspellsuggest.nim deleted file mode 100644 index ad449554f..000000000 --- a/tests/misc/mspellsuggest.nim +++ /dev/null @@ -1,7 +0,0 @@ -proc fooBar4*(a: int) = discard -var fooBar9* = 0 - -var fooCar* = 0 -type FooBar* = int -type FooCar* = int -type GooBa* = int diff --git a/tests/misc/mtlsemulation.h b/tests/misc/mtlsemulation.h deleted file mode 100644 index 992977acd..000000000 --- a/tests/misc/mtlsemulation.h +++ /dev/null @@ -1,37 +0,0 @@ -#include - -struct Foo1 { - /* - uncommenting would give: - error: initializer for thread-local variable must be a constant expression - N_LIB_PRIVATE NIM_THREADVAR Foo1 g1__9brEZhPEldbVrNpdRGmWESA; - */ - // Foo1() noexcept { } - - /* - uncommenting would give: - error: type of thread-local variable has non-trivial destruction - */ - // ~Foo1() { } - int x; -}; - -struct Foo2 { - Foo2() noexcept { } - ~Foo2() { } - int x; -}; - -static int ctorCalls = 0; -static int dtorCalls = 0; - -struct Foo3 { - Foo3() noexcept { - ctorCalls = ctorCalls + 1; - x = 10; - } - ~Foo3() { - dtorCalls = dtorCalls + 1; - } - int x; -}; diff --git a/tests/misc/parsecomb.nim b/tests/misc/parsecomb.nim index 4f149cbf6..4ff2f65d2 100644 --- a/tests/misc/parsecomb.nim +++ b/tests/misc/parsecomb.nim @@ -1,7 +1,3 @@ -discard """ - matrix: "--mm:arc; --mm:refc" -""" - type Input[T] = object toks: seq[T] index: int diff --git a/tests/misc/t11634.nim b/tests/misc/t11634.nim deleted file mode 100644 index 4ecb6a53c..000000000 --- a/tests/misc/t11634.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - action: reject - nimout: ''' -t11634.nim(20, 7) Error: cannot destructure to compile time variable -''' -""" - -type Foo = ref object - val: int - -proc divmod(a, b: Foo): (Foo, Foo) = - ( - Foo(val: a.val div b.val), - Foo(val: a.val mod b.val) - ) - -block: - let a {.compileTime.} = Foo(val: 2) - let b {.compileTime.} = Foo(val: 3) - let (c11634 {.compileTime.}, d11634 {.compileTime.}) = divmod(a, b) diff --git a/tests/misc/t12869.nim b/tests/misc/t12869.nim deleted file mode 100644 index 731a4e95e..000000000 --- a/tests/misc/t12869.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - errormsg: "type mismatch: got but expected 'int'" - line: 12 -""" - -import sugar -from algorithm import sorted, SortOrder - -let a = 5 - -proc sorted*[T](a: openArray[T], key: proc(v: T): int, order = SortOrder.Ascending): seq[T] = - sorted(a, (x, y) => key(x) < key(y), order) - -echo sorted(@[9, 1, 8, 2, 6, 4, 5, 0], (x) => (a - x).abs) diff --git a/tests/misc/t14667.nim b/tests/misc/t14667.nim deleted file mode 100644 index 3034e2841..000000000 --- a/tests/misc/t14667.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ - matrix: "--cc:vcc" - disabled: "linux" - disabled: "bsd" - disabled: "osx" - disabled: "unix" - disabled: "posix" -""" - -type A = tuple -discard () -discard default(A) diff --git a/tests/misc/t15955.nim b/tests/misc/t15955.nim deleted file mode 100644 index 7441e5398..000000000 --- a/tests/misc/t15955.nim +++ /dev/null @@ -1,22 +0,0 @@ -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/t16244.nim b/tests/misc/t16244.nim deleted file mode 100644 index 5e8128736..000000000 --- a/tests/misc/t16244.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - errormsg: "type mismatch: got " - line: 9 -""" - -proc g(): auto = 1 -proc h(): auto = 1.0 - -var a = g() + h() diff --git a/tests/misc/t16264.nim b/tests/misc/t16264.nim deleted file mode 100644 index afe319e6c..000000000 --- a/tests/misc/t16264.nim +++ /dev/null @@ -1,2 +0,0 @@ -import times -doAssert low(Time) == fromUnix(0) \ No newline at end of file diff --git a/tests/misc/t16541.nim b/tests/misc/t16541.nim deleted file mode 100644 index 452327e8f..000000000 --- a/tests/misc/t16541.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ - action: "reject" - -""" - -import strutils, sugar, nre - -proc my_replace*(s: string, r: Regex, by: string | (proc (match: string): string)): string = - nre.replace(s, r, by) - -discard my_replace("abcde", re"[bcd]", match => match.to_upper) == "aBCDe" -discard my_replace("abcde", re"[bcd]", (match: string) => match.to_upper) == "aBCDe" diff --git a/tests/misc/t17286.nim b/tests/misc/t17286.nim deleted file mode 100644 index 3a54e624e..000000000 --- a/tests/misc/t17286.nim +++ /dev/null @@ -1,16 +0,0 @@ -discard """ - cmd: "nim check -b:js $file" - action: "compile" -""" - -# bug #17286 - -import std/compilesettings - -static: - doAssert querySetting(backend) == "js" - doAssert defined(js) - doAssert not defined(c) - -import random -randomize() \ No newline at end of file diff --git a/tests/misc/t18077.nim b/tests/misc/t18077.nim deleted file mode 100644 index 6cd05d575..000000000 --- a/tests/misc/t18077.nim +++ /dev/null @@ -1,21 +0,0 @@ -discard """ - cmd: '''nim doc -d:nimTestsT18077b:4 --doccmd:"-d:nimTestsT18077 -d:nimTestsT18077b:3 --hints:off" $file''' - action: compile -""" - -# bug #18077 - -const nimTestsT18077b {.intdefine.} = 1 - -static: - when defined(nimdoc): - doAssert nimTestsT18077b == 4 - doAssert not defined(nimTestsT18077) - else: - doAssert defined(nimTestsT18077) - doAssert nimTestsT18077b == 3 - -runnableExamples: - const nimTestsT18077b {.intdefine.} = 2 - doAssert nimTestsT18077b == 3 - doAssert defined(nimTestsT18077) diff --git a/tests/misc/t18079.nim b/tests/misc/t18079.nim deleted file mode 100644 index ae64bbff9..000000000 --- a/tests/misc/t18079.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - matrix: "--mm:orc" -""" - -type - Foo = object - y: int - - Bar = object - x: Foo - -proc baz(state: var Bar):int = - state.x.y = 2 - state.x.y -doAssert baz((ref Bar)(x: (new Foo)[])[]) == 2 diff --git a/tests/misc/t19046.nim b/tests/misc/t19046.nim deleted file mode 100644 index b3bfec7ae..000000000 --- a/tests/misc/t19046.nim +++ /dev/null @@ -1,19 +0,0 @@ -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/t20253.nim b/tests/misc/t20253.nim deleted file mode 100644 index d47c36c55..000000000 --- a/tests/misc/t20253.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - errormsg: "'result' requires explicit initialization" - line: 10 -""" - -type Meow {.requiresInit.} = object - init: bool - -proc initMeow(): Meow = - discard diff --git a/tests/misc/t20289.nim b/tests/misc/t20289.nim deleted file mode 100644 index 5a0a269f0..000000000 --- a/tests/misc/t20289.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - action: reject -""" - -type E[T] = object - v: T - -template j[T](R: type E[T], x: untyped): R = R(v: x) -template d[T](O: type E, v: T): E[T] = E[T].j(v) - -proc w[T](): E[T] = - template r(k: int): auto = default(T) - E.d r - -discard w[int]() diff --git a/tests/misc/t20456_2.nim b/tests/misc/t20456_2.nim deleted file mode 100644 index 37e52c452..000000000 --- a/tests/misc/t20456_2.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - joinable: false -""" - -import std/[osproc, os, strformat] -from stdtest/specialpaths import testsDir - -when defined(nimPreviewSlimSystem): - import std/assertions - -const - nim = getCurrentCompilerExe() - file = testsDir / "misc" / "m20456.nims" -doAssert execCmd(fmt"{nim} check {file}") == 0 diff --git a/tests/misc/t21109.nim b/tests/misc/t21109.nim deleted file mode 100644 index 0f7980896..000000000 --- a/tests/misc/t21109.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - action: reject - errormsg: "type expected" - file: "iterators.nim" -""" - - -template b(j: untyped) = j -template m() = discard - -b: - for t, f in @[]: - m() diff --git a/tests/misc/t21443.nim b/tests/misc/t21443.nim deleted file mode 100644 index 70413c5b3..000000000 --- a/tests/misc/t21443.nim +++ /dev/null @@ -1,6 +0,0 @@ -import std/envvars - -# bug #19292 -putEnv("NimPutEnvTest", "test") -# bug #21122 -doAssert getEnv("NimPutEnvTest") == "test" diff --git a/tests/misc/t3482.nim b/tests/misc/t3482.nim deleted file mode 100644 index 33b3b8f40..000000000 --- a/tests/misc/t3482.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - action: reject - nimout: "t3482.nim(13, 8) Error: undeclared identifier: 'output'" -""" -# bug #3482 (correct behavior since 1.4.0, cgen error in 1.2.0) -template foo*(body: typed) = - if true: - body - -proc test = - foo: - var output = "" - echo output.len - -test() diff --git a/tests/misc/t5540.nim b/tests/misc/t5540.nim deleted file mode 100644 index 6a19e70e1..000000000 --- a/tests/misc/t5540.nim +++ /dev/null @@ -1,45 +0,0 @@ -# bug #5540; works in 1.2.0 -# fails in 1.0 (Error: cannot generate VM code for) -# fails in 0.18.0 (Error: type mismatch: got ) - -block: - type - Fruit = object - Yellow = object - a: int - template getColor(x: typedesc[Fruit]): typedesc = Yellow - type - Banana[T] = object - b: T - a: getColor(Fruit) - Apple[T] = object - a: T - b: getColor(T) - block: - var x: Banana[int] - doAssert x.b == 0 - doAssert x.a is Yellow - block: - var x: Apple[Fruit] - doAssert x.b is Yellow - -block: - type - Fruit = object - Yellow = object - a: int - - template getColor(x: typedesc[Fruit]): typedesc = Yellow - - type - Banana[T] = object - b: T - a: getColor(Fruit) - - Apple[T] = object - a: T - b: getColor(T) - - var x: Banana[int] - x.b = 13 - x.a.a = 17 diff --git a/tests/misc/t6549.nim b/tests/misc/t6549.nim deleted file mode 100644 index 1d7393318..000000000 --- a/tests/misc/t6549.nim +++ /dev/null @@ -1,4 +0,0 @@ - -const l = $(range[low(uint64) .. high(uint64)]) -const r = "range 0..18446744073709551615(uint64)" -doAssert l == r diff --git a/tests/misc/t8545.nim b/tests/misc/t8545.nim deleted file mode 100644 index 89957e1d3..000000000 --- a/tests/misc/t8545.nim +++ /dev/null @@ -1,23 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -# bug #8545 - -template bar(a: static[bool]): untyped = int - -proc main() = - proc foo1(a: static[bool]): auto = 1 - doAssert foo1(true) == 1 - - proc foo2(a: static[bool]): bar(a) = 1 - doAssert foo2(true) == 1 - - proc foo3(a: static[bool]): bar(cast[static[bool]](a)) = 1 - doAssert foo3(true) == 1 - - proc foo4(a: static[bool]): bar(static(a)) = 1 - doAssert foo4(true) == 1 - -static: main() -main() diff --git a/tests/misc/t9039.nim b/tests/misc/t9039.nim deleted file mode 100644 index 3271cd34e..000000000 --- a/tests/misc/t9039.nim +++ /dev/null @@ -1,24 +0,0 @@ -discard """ - action: reject - nimout: ''' -t9039.nim(22, 22) Error: type mismatch: got -but expression 'nesting + 1' is of type: int -''' -""" - -# bug #9039; this used to hang in 0.19.0 - - - - - -# line 15 -func default(T: typedesc[array]): T = discard -doAssert default(array[3, int]) == [0, 0, 0] -func shapeBad*[T: not char](s: openArray[T], rank: static[int], nesting = 0, parent_shape = default(array[rank, int])): array[rank, int] = - result = parent_shape - result[nesting] = s.len - when (T is seq|array): - result = shapeBad(s[0], nesting + 1, result) -let a1 = [1, 2, 3].shapeBad(rank = 1) -let a2 = [[1, 2, 3], [4, 5, 6]].shapeBad(rank = 2) diff --git a/tests/misc/t9710.nim b/tests/misc/t9710.nim deleted file mode 100644 index c65cb7bf4..000000000 --- a/tests/misc/t9710.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - matrix: "--debugger:native" -""" -# bug #9710 -for i in 1 || 200: - discard i diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim index 48d4928ac..bac26896d 100644 --- a/tests/misc/taddr.nim +++ b/tests/misc/taddr.nim @@ -32,10 +32,6 @@ 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' @@ -87,7 +83,7 @@ block: # bug #14576 # lots of these used to give: Error: internal error: genAddr: 2 proc byLent[T](a: T): lent T = a - proc byPtr[T](a: T): ptr T = a.addr + proc byPtr[T](a: T): ptr T = a.unsafeAddr block: let a = (10,11) @@ -236,17 +232,8 @@ 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' @@ -266,7 +253,6 @@ 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/misc/tapp_lib_staticlib.nim b/tests/misc/tapp_lib_staticlib.nim deleted file mode 100644 index 92c9acbc3..000000000 --- a/tests/misc/tapp_lib_staticlib.nim +++ /dev/null @@ -1,27 +0,0 @@ -discard """ -joinable: false -""" - -# bug #16949 - -when defined case1: - proc foo(): int {.exportc.} = 10 -elif defined case2: - proc foo(): int {.exportc, dynlib.} = 10 -elif defined caseMain: - proc foo(): int {.importc.} - doAssert foo() == 10 -else: - import stdtest/specialpaths - import std/[os, strformat, strutils, compilesettings] - proc runCmd(cmd: string) = - doAssert execShellCmd(cmd) == 0, $cmd - const - file = currentSourcePath - nim = getCurrentCompilerExe() - mode = querySetting(backend) - proc test(lib, options: string) = - runCmd fmt"{nim} {mode} -o:{lib} --nomain {options} -f {file}" - # runCmd fmt"{nim} r -b:{mode} --passl:{lib} -d:caseMain -f {file}" # pending https://github.com/nim-lang/Nim/pull/16945 - test(buildDir / "libD20210205T172314.a", "--app:staticlib -d:nimLinkerWeakSymbols -d:case1") - test(buildDir / DynlibFormat % "D20210205T172720", "--app:lib -d:case2") diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim index 6d67b1c52..454801a2d 100644 --- a/tests/misc/tcast.nim +++ b/tests/misc/tcast.nim @@ -48,7 +48,7 @@ block: var x: ref int = nil doAssert cast[int](cast[ptr int](x)) == 0 -block: # cast of nil +block: block: static: let a = cast[pointer](nil) @@ -71,33 +71,7 @@ block: # cast of nil static: doAssert cast[RootRef](nil).repr == "nil" - when false: # xxx bug #15730, not fixed yet - block: - static: - doAssert cast[cstring](nil).repr == "nil" - -template main() = - # xxx move all under here to get tested in VM - block: # cast of enum - type Koo = enum k1, k2 - type Goo = enum g1, g2 - type Boo = enum b1 = -1, b2, b3, b4 - type Coo = enum c1 = -1i8, c2, c3, c4 - when nimvm: - # xxx: Error: VM does not support 'cast' from tyEnum to tyEnum - discard - else: - doAssert cast[Koo](k2) == k2 - doAssert cast[Goo](k2) == g2 - doAssert cast[Goo](k2.ord) == g2 - - doAssert b3.ord == 1 - doAssert cast[Koo](b3) == k2 - doAssert cast[Boo](k2) == b3 - - doAssert c3.ord == 1 - doAssert cast[Koo](c3) == k2 - doAssert cast[Coo](k2) == c3 - -static: main() -main() + # Issue #15730, not fixed yet + # block: + # static: + # doAssert cast[cstring](nil).repr == "nil" diff --git a/tests/misc/tconv.nim b/tests/misc/tconv.nim index 5e8eac729..f7d15b0b5 100644 --- a/tests/misc/tconv.nim +++ b/tests/misc/tconv.nim @@ -1,11 +1,5 @@ -discard """ - matrix: "--warningAsError:EnumConv --warningAsError:CStringConv" -""" - template reject(x) = - static: doAssert(not compiles(x)) -template accept(x) = - static: doAssert(compiles(x)) + static: assert(not compiles(x)) reject: const x = int8(300) @@ -61,60 +55,3 @@ block: # issue 3766 proc r(x: static[R]) = echo x r 3.R - - -block: # https://github.com/nim-lang/RFCs/issues/294 - type Koo = enum k1, k2 - type Goo = enum g1, g2 - - accept: Koo(k2) - accept: k2.Koo - accept: k2.int.Goo - - reject: Goo(k2) - reject: k2.Goo - reject: k2.string - - {.push warningAsError[EnumConv]:off.} - discard Goo(k2) - accept: Goo(k2) - accept: k2.Goo - reject: k2.string - {.pop.} - - reject: Goo(k2) - reject: k2.Goo - -reject: - # bug #18550 - proc f(c: char): cstring = - var x = newString(109*1024*1024) - x[0] = c - x - -{.push warning[AnyEnumConv]:on, warningAsError[AnyEnumConv]:on.} - -reject: - type - Foo = enum - one - three - - var va = 2 - var vb = va.Foo - -{.pop.} - -{.push warningAsError[HoleEnumConv]:on.} - -reject: - # bug #12815 - type - Hole = enum - one = 1 - three = 3 - - var va = 2 - var vb = va.Hole - -{.pop.} diff --git a/tests/misc/tdefine.nim b/tests/misc/tdefine.nim index f3fa4711f..f1c6e7a96 100644 --- a/tests/misc/tdefine.nim +++ b/tests/misc/tdefine.nim @@ -1,23 +1,12 @@ discard """ joinable: false -cmd: "nim c $options -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -d:namespaced.define=false -d:double.namespaced.define -r $file" -matrix: "; -d:useGenericDefine" +cmd: "nim c -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -r $file" """ -when defined(useGenericDefine): - {.pragma: booldefine2, define.} - {.pragma: intdefine2, define.} - {.pragma: strdefine2, define.} -else: - - {.pragma: booldefine2, booldefine.} - {.pragma: intdefine2, intdefine.} - {.pragma: strdefine2, strdefine.} - -const booldef {.booldefine2.} = false -const booldef2 {.booldefine2.} = true -const intdef {.intdefine2.} = 0 -const strdef {.strdefine2.} = "" +const booldef {.booldefine.} = false +const booldef2 {.booldefine.} = true +const intdef {.intdefine.} = 0 +const strdef {.strdefine.} = "" doAssert defined(booldef) doAssert defined(booldef2) @@ -28,50 +17,14 @@ doAssert not booldef2 doAssert intdef == 2 doAssert strdef == "foobar" -when defined(useGenericDefine): - block: - const uintdef {.define: "intdef".}: uint = 17 - doAssert intdef == int(uintdef) - const cstrdef {.define: "strdef".}: cstring = "not strdef" - doAssert $cstrdef == strdef - type FooBar = enum foo, bar, foobar - const enumdef {.define: "strdef".} = foo - doAssert $enumdef == strdef - doAssert enumdef == foobar - # Intentionally not defined from command line -const booldef3 {.booldefine2.} = true -const intdef2 {.intdefine2.} = 1 -const strdef2 {.strdefine2.} = "abc" +const booldef3 {.booldefine.} = true +const intdef2 {.intdefine.} = 1 +const strdef2 {.strdefine.} = "abc" type T = object - when booldef3: - field1: int - when intdef2 == 1: - field2: int - when strdef2 == "abc": - 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` {.booldefine2.} = true -doAssert not `namespaced.define` -when defined(useGenericDefine): - const aliasToNamespacedDefine {.define: "namespaced.define".} = not `namespaced.define` -else: - const aliasToNamespacedDefine {.booldefine: "namespaced.define".} = not `namespaced.define` -doAssert aliasToNamespacedDefine == `namespaced.define` - -doAssert defined(double.namespaced.define) -const `double.namespaced.define` {.booldefine2.} = false -doAssert `double.namespaced.define` -when defined(useGenericDefine): - const aliasToDoubleNamespacedDefine {.define: "double.namespaced.define".} = not `double.namespaced.define` -else: - const aliasToDoubleNamespacedDefine {.booldefine: "double.namespaced.define".} = not `double.namespaced.define` -doAssert aliasToDoubleNamespacedDefine == `double.namespaced.define` - -doAssert not defined(namespaced.butnotdefined) + when booldef3: + field1: int + when intdef2 == 1: + field2: int + when strdef2 == "abc": + field3: int \ No newline at end of file diff --git a/tests/misc/tevents.nim b/tests/misc/tevents.nim new file mode 100644 index 000000000..045c9fc5b --- /dev/null +++ b/tests/misc/tevents.nim @@ -0,0 +1,48 @@ +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/theaproots.nim b/tests/misc/theaproots.nim index 2dd345254..1ea3c86b9 100644 --- a/tests/misc/theaproots.nim +++ b/tests/misc/theaproots.nim @@ -28,7 +28,7 @@ proc acc(x: var Foo): var ref Bar = proc test(maybeFoo: var Foo, maybeSeq: var seq[ref Bar], - bars: var openArray[ref Bar], + bars: var openarray[ref Bar], maybeTup: var Tup) = var bb: ref Bar maybeFoo.rmaybe = bb diff --git a/tests/misc/thints_off.nim b/tests/misc/thints_off.nim deleted file mode 100644 index 5a4cadad6..000000000 --- a/tests/misc/thints_off.nim +++ /dev/null @@ -1,4 +0,0 @@ -discard """ - matrix: "--hints:off" -""" -doAssert true diff --git a/tests/misc/tinvalidnewseq.nim b/tests/misc/tinvalidnewseq.nim index 7a95db020..dec00fd4a 100644 --- a/tests/misc/tinvalidnewseq.nim +++ b/tests/misc/tinvalidnewseq.nim @@ -13,7 +13,7 @@ proc parseURL(url: string): TURL = var pattern: string = r"([a-zA-Z]+://)?(\w+?\.)?(\w+)(\.\w+)(:[0-9]+)?(/.+)?" var m: array[0..6, string] #Array with the matches newSeq(m, 7) #ERROR - discard re.match(url, re(pattern), m) + discard regexprs.match(url, re(pattern), m) result = (protocol: m[1], subdomain: m[2], domain: m[3] & m[4], port: m[5], path: m[6].split('/')) diff --git a/tests/misc/tjoinable.nim b/tests/misc/tjoinable.nim deleted file mode 100644 index f23fca0d4..000000000 --- a/tests/misc/tjoinable.nim +++ /dev/null @@ -1,3 +0,0 @@ -# checks that megatest allows duplicate names, see also `tests/testament/tjoinable.nim` -doAssert defined(testing) -doAssert defined(nimMegatest) diff --git a/tests/misc/tlambdadonotation.nim b/tests/misc/tlambdadonotation.nim index 3160c0972..af51efdbf 100644 --- a/tests/misc/tlambdadonotation.nim +++ b/tests/misc/tlambdadonotation.nim @@ -67,7 +67,7 @@ proc main2() = proc foo() = subscriber.consume() - emitter.on_event() do (): + emitter.on_event() do: subscriber.consume() # this works diff --git a/tests/misc/tlocals.nim b/tests/misc/tlocals.nim index e59976102..a6df68224 100644 --- a/tests/misc/tlocals.nim +++ b/tests/misc/tlocals.nim @@ -1,5 +1,4 @@ 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 41ef3fa19..2d9a64461 100644 --- a/tests/misc/tnew.nim +++ b/tests/misc/tnew.nim @@ -1,5 +1,4 @@ discard """ -matrix: "--mm:refc" outputsub: ''' Simple tree node allocation worked! Simple cycle allocation worked! @@ -11,7 +10,6 @@ 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/tnoforward.nim b/tests/misc/tnoforward.nim index 0d9b40c83..b6a71897a 100644 --- a/tests/misc/tnoforward.nim +++ b/tests/misc/tnoforward.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--experimental:codeReordering" output: "10" """ diff --git a/tests/misc/tnoop.nim b/tests/misc/tnoop.nim index f55f2441a..7f5bb1045 100644 --- a/tests/misc/tnoop.nim +++ b/tests/misc/tnoop.nim @@ -1,8 +1,9 @@ discard """ nimout: ''' - found 'a' [var declared in tnoop.nim(10, 3)] + found 'a' [var declared in tnoop.nim(11, 3)] ''' file: "tnoop.nim" + line: 13 errormsg: "attempting to call routine: 'a'" """ diff --git a/tests/misc/tproveinit.nim b/tests/misc/tproveinit.nim deleted file mode 100644 index c9f688309..000000000 --- a/tests/misc/tproveinit.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - joinable: false -""" - -{.warningAsError[ProveInit]:on.} -template main() = - proc fn(): var int = - discard - discard fn() -doAssert not compiles(main()) - -# bug #9901 -import std/[sequtils, times] -proc parseMyDates(line: string): DateTime = - result = parse(line, "yyyy-MM-dd") -var dateStrings = @["2018-12-01", "2018-12-02", "2018-12-03"] -var parsed = dateStrings.map(parseMyDates) -discard parsed diff --git a/tests/misc/tradix.nim b/tests/misc/tradix.nim index f4fb56849..1773a9609 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: uint8 - keys: array[0..31, uint8] + len: int8 + keys: array[0..31, int8] 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: uint8 - keys: array[0..31, uint8] + len: int8 + keys: array[0..31, int8] 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..int(x.len)-1: - if int(x.keys[i]) == a: return x.vals[i] + for i in 0..ze(x.len)-1: + if ze(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..int(x.len)-1: - if int(x.keys[i]) == a: return true + for i in 0..ze(x.len)-1: + if ze(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 = int(x.len) + var L = ze(x.len) for i in 0..L-1: - if int(x.keys[i]) == a: + if ze(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'u8 - x.keys[0] = uint8(a) + x.len = 1'i8 + x.keys[0] = toU8(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 = int(x.len) + var L = ze(x.len) for i in 0..L-1: - if int(x.keys[i]) == a: return true + if ze(x.keys[i]) == a: return true if L <= high(x.keys): - x.keys[L] = uint8(a) + x.keys[L] = toU8(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..int(x.len)-1: - var u = int(x.keys[i]) + for i in 0..ze(x.len)-1: + var u = ze(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'u8 - x.keys[0] = uint8(k) + x.len = 1'i8 + x.keys[0] = toU8(k) r = x return addInner(x.vals[0], a, d-8) case r.kind of rnLinear: var x = cast[ptr TRadixNodeLinear](r) - var L = int(x.len) + var L = ze(x.len) for i in 0..L-1: - if int(x.keys[i]) == k: # already exists + if ze(x.keys[i]) == k: # already exists return addInner(x.vals[i], a, d-8) if L <= high(x.keys): - x.keys[L] = uint8(k) + x.keys[L] = toU8(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[int(x.keys[i])] = x.vals[i] + for i in 0..L-1: y.b[ze(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..int(r.len)-1: - yield (int(r.keys[i]), r.vals[i]) + for i in 0..ze(r.len)-1: + yield (ze(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..int(r.len)-1: - yield int(r.keys[i]) + for i in 0..ze(r.len)-1: + yield ze(r.keys[i]) else: assert(false) iterator elements*(r: PRadixNode): ByteAddress {.inline.} = diff --git a/tests/misc/trfc405.nim b/tests/misc/trfc405.nim deleted file mode 100644 index 8c967eb77..000000000 --- a/tests/misc/trfc405.nim +++ /dev/null @@ -1,84 +0,0 @@ - -{.experimental: "flexibleOptionalParams".} - -# https://github.com/nim-lang/RFCs/issues/405 - -template main = - template fn1(a = 1, b = 2, body): auto = (a, b, astToStr(body)) - let a1 = fn1(10, 20): - foo - doAssert a1 == (10, 20, "\nfoo") - - template fn2(a = 1, b = 2, body): auto = (a, b, astToStr(body)) - let a2 = fn2(a = 10): foo - doAssert a2 == (10, 2, "\nfoo") - let a2b = fn2(b = 20): foo - doAssert a2b == (1, 20, "\nfoo") - - template fn3(x: int, a = 1, b = 2, body): auto = (a, b, astToStr(body)) - let a3 = fn3(3, 10, 20): foo - doAssert a3 == (10, 20, "\nfoo") - let a3b = fn3(3, a = 10): foo - doAssert a3b == (10, 2, "\nfoo") - - template fn4(x: int, y: int, body): auto = (x, y, astToStr(body)) - let a4 = fn4(1, 2): foo - doAssert a4 == (1, 2, "\nfoo") - - template fn5(x = 1, y = 2, body: untyped = 3): auto = (x, y, astToStr(body)) - doAssert compiles(fn5(1, 2, foo)) - doAssert not compiles(fn5(1, foo)) - - block: - # with an overload - var witness = 0 - template fn6() = discard - template fn6(procname: string, body: untyped): untyped = witness.inc - fn6("abc"): discard - assert witness == 1 - - block: - # with overloads - var witness = 0 - template fn6() = discard - template fn6(a: int) = discard - template fn6(procname: string, body: untyped): untyped = witness.inc - fn6("abc"): discard - assert witness == 1 - - template fn6(b = 1.5, body: untyped): untyped = witness.inc - fn6(1.3): discard - assert witness == 2 - - block: - var witness = 0 - template fn6(a: int) = discard - template fn6(a: string) = discard - template fn6(ignore: string, b = 1.5, body: untyped): untyped = witness.inc - fn6(""): - foobar1 - foobar2 - doAssert witness == 1 - fn6(""): discard - doAssert witness == 2 - - block: # multi block args - template fn8(a = 1, b = 2, body1: untyped, body2: untyped): auto = (a, b, astToStr(body1), astToStr(body2)) - let a1 = fn8(): - foobar1 - foobar2 - do: - foobar3 - foobar4 - doAssert a1 == (1, 2, "\nfoobar1\nfoobar2", "\nfoobar3\nfoobar4") - - let a2 = fn8(b = 20): - foobar1 - foobar2 - do: - foobar3 - foobar4 - doAssert a2 == (1, 20, "\nfoobar1\nfoobar2", "\nfoobar3\nfoobar4") - -static: main() -main() diff --git a/tests/misc/trfc_341.nim b/tests/misc/trfc_341.nim deleted file mode 100644 index 37cf675c6..000000000 --- a/tests/misc/trfc_341.nim +++ /dev/null @@ -1,27 +0,0 @@ -# test for https://github.com/nim-lang/RFCs/issues/341 -import std/json -import std/jsonutils -import std/macros - -macro fn1(a: untyped): string = newLit a.lispRepr - -doAssert fn1(a.?b.c) == """(DotExpr (Infix (Ident ".?") (Ident "a") (Ident "b")) (Ident "c"))""" - -template `.?`(a: JsonNode, b: untyped{ident}): JsonNode = - a[astToStr(b)] - -proc identity[T](a: T): T = a -proc timesTwo[T](a: T): T = a * 2 - -template main = - let a = (a1: 1, a2: "abc", a3: (a4: 2.5)) - let j = a.toJson - doAssert j.?a1.getInt == 1 - doAssert j.?a3.?a4.getFloat == 2.5 - doAssert j.?a3.?a4.getFloat.timesTwo == 5.0 - doAssert j.?a3.identity.?a4.getFloat.timesTwo == 5.0 - doAssert j.identity.?a3.identity.?a4.identity.getFloat.timesTwo == 5.0 - doAssert j.identity.?a3.?a4.identity.getFloat.timesTwo == 5.0 - -static: main() -main() diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index aca19f72d..63aac0ede 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -6,10 +6,8 @@ discard """ ## tests that don't quite fit the mold and are easier to handle via `execCmdEx` ## A few others could be added to here to simplify code. ## Note: this test is a bit slow but tests a lot of things; please don't disable. -## Note: if needed, we could use `matrix: "-d:case1; -d:case2"` to split this -## into several independent tests while retaining the common test helpers. -import std/[strformat,os,osproc,unittest,compilesettings] +import std/[strformat,os,osproc,unittest] from std/sequtils import toSeq,mapIt from std/algorithm import sorted import stdtest/[specialpaths, unittest_light] @@ -23,29 +21,23 @@ proc isDots(a: string): bool = a.startsWith(".") and a.strip(chars = {'.'}) == "" const + defaultHintsOff = "--hint:successx:off --hint:exec:off --hint:link:off --hint:cc:off --hint:conf:off --hint:processing:off --hint:QuitCalled:off" + # useful when you want to turn only some hints on, and some common ones off. + # pending https://github.com/timotheecour/Nim/issues/453, simplify to: `--hints:off` nim = getCurrentCompilerExe() - mode = querySetting(backend) + mode = + when defined(c): "c" + elif defined(cpp): "cpp" + else: static: doAssert false nimcache = buildDir / "nimcacheTrunner" # instead of `querySetting(nimcacheDir)`, avoids stomping on other parallel tests -proc runNimCmd(file, options = "", rtarg = ""): auto = +proc runCmd(file, options = ""): auto = let fileabs = testsDir / file.unixToNativePath - # doAssert fileabs.fileExists, fileabs # disabled because this allows passing `nim r --eval:code fakefile` - let cmd = fmt"{nim} {mode} --hint:all:off {options} {fileabs} {rtarg}" + doAssert fileabs.fileExists, fileabs + let cmd = fmt"{nim} {mode} {options} --hints:off {fileabs}" result = execCmdEx(cmd) - when false: # for debugging - echo cmd - echo result[0] & "\n" & $result[1] - -proc runNimCmdChk(file, options = "", rtarg = "", status = 0): string = - let (ret, status2) = runNimCmd(file, options, rtarg = rtarg) - doAssert status2 == status, $(file, options, status, status2) & "\n" & ret - ret - -proc genShellCmd(filename: string): string = - let filename = filename.quoteShell - when defined(windows): "cmd /c " & filename # or "cmd /c " ? - else: "sh " & filename + when false: echo result[0] & "\n" & result[1] # for debugging when defined(nimTrunnerFfi): block: # mevalffi @@ -64,8 +56,8 @@ when defined(nimTrunnerFfi): hello world stderr hi stderr """ - let output = runNimCmdChk("vm/mevalffi.nim", fmt"{opt} --warnings:off --experimental:compiletimeFFI") - doAssert output == fmt""" + let (output, exitCode) = runCmd("vm/mevalffi.nim", fmt"{opt} --experimental:compiletimeFFI") + let expected = fmt""" {prefix}foo foo:100 foo:101 @@ -73,13 +65,12 @@ foo:102:103 foo:102:103:104 foo:0.03:asdf:103:105 ret=[s1:foobar s2:foobar age:25 pi:3.14] -""", output +""" + doAssert output == expected, output + doAssert exitCode == 0 -elif not defined(nimTestsTrunnerDebugging): - # don't run twice the same test with `nimTrunnerFfi` - # use `-d:nimTestsTrunnerDebugging` for debugging convenience when you want to just run 1 test - import std/strutils - import std/json +else: # don't run twice the same test + import std/[strutils] template check2(msg) = doAssert msg in output, output block: # tests with various options `nim doc --project --index --docroot` @@ -104,7 +95,7 @@ elif not defined(nimTestsTrunnerDebugging): of 5: nimcache / htmldocsDirname else: file.parentDir / htmldocsDirname - var cmd = fmt"{nim} doc --index:on --filenames:abs --hint:successX:on --nimcache:{nimcache} {options[i]} {file}" + var cmd = fmt"{nim} doc --index:on --listFullPaths --hint:successX:on --nimcache:{nimcache} {options[i]} {file}" removeDir(htmldocsDir) let (outp, exitCode) = execCmdEx(cmd) check exitCode == 0 @@ -154,16 +145,17 @@ sub/mmain.idx""", context else: doAssert false block: # mstatic_assert - let (output, exitCode) = runNimCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad") + let (output, exitCode) = runCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad") check2 "sizeof(bool) == 2" check exitCode != 0 block: # ABI checks let file = "misc/msizeof5.nim" block: - discard runNimCmdChk(file, "-d:checkAbi") + let (output, exitCode) = runCmd(file, "-d:checkAbi") + doAssert exitCode == 0, output block: - let (output, exitCode) = runNimCmd(file, "-d:checkAbi -d:caseBad") + let (output, exitCode) = runCmd(file, "-d:checkAbi -d:caseBad") # on platforms that support _StaticAssert natively, errors will show full context, e.g.: # error: static_assert failed due to requirement 'sizeof(unsigned char) == 8' # "backend & Nim disagree on size for: BadImportcType{int64} [declared in mabi_check.nim(1, 6)]" @@ -216,39 +208,6 @@ 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}" @@ -265,20 +224,8 @@ 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 cmd = fmt"{nim} r {defaultHintsOff} --hint:conf tests/newconfig/bar/mfoo.nim" let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) doAssert exitCode == 0 let dir = getCurrentDir() @@ -294,20 +241,6 @@ tests/newconfig/bar/mfoo.nims""".splitLines expected.add &"Hint: used config file '{b}' [Conf]\n" doAssert outp.endsWith expected, outp & "\n" & expected - block: # bug #8219 - let file = "tests/newconfig/mconfigcheck.nims" - let cmd = fmt"{nim} check --hints:off {file}" - check execCmdEx(cmd) == ("", 0) - - block: # mfoo2.customext - let filename = testsDir / "newconfig/foo2/mfoo2.customext" - let cmd = fmt"{nim} e --hint:conf {filename}" - let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) - doAssert exitCode == 0 - var expected = &"Hint: used config file '{filename}' [Conf]\n" - doAssert outp.endsWith "123" & "\n" & expected - - block: # nim --eval let opt = "--hints:off" check fmt"""{nim} {opt} --eval:"echo defined(nimscript)"""".execCmdEx == ("true\n", 0) @@ -315,7 +248,7 @@ tests/newconfig/bar/mfoo.nims""".splitLines check fmt"""{nim} r -b:js {opt} --eval:"echo defined(js)"""".execCmdEx == ("true\n", 0) block: # `hintProcessing` dots should not interfere with `static: echo` + friends - let cmd = fmt"""{nim} r --hint:all:off --hint:processing -f --eval:"static: echo 1+1"""" + let cmd = fmt"""{nim} r {defaultHintsOff} --hint:processing -f --eval:"static: echo 1+1"""" let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) template check3(cond) = doAssert cond, $(outp,) doAssert exitCode == 0 @@ -329,7 +262,7 @@ tests/newconfig/bar/mfoo.nims""".splitLines check3 "2" in outp block: # nim secret - let opt = "--hint:all:off --hint:processing" + let opt = fmt"{defaultHintsOff} --hint:processing" template check3(cond) = doAssert cond, $(outp,) for extra in ["", "--stdout"]: let cmd = fmt"""{nim} secret {opt} {extra}""" @@ -342,8 +275,8 @@ tests/newconfig/bar/mfoo.nims""".splitLines when not defined(windows): check3 lines.len == 5 check3 lines[0].isDots - # check3 lines[1].isDots # todo nim secret might use parsing pipeline - check3 lines[2].dup(removePrefix(">>> ")) == "3" # prompt depends on `nimUseLinenoise` + check3 lines[1].dup(removePrefix(">>> ")) == "3" # prompt depends on `nimUseLinenoise` + check3 lines[2].isDots check3 lines[3] == "ab" check3 lines[4] == "" else: @@ -354,87 +287,3 @@ tests/newconfig/bar/mfoo.nims""".splitLines let (outp, exitCode) = run "echo 1+2; quit(2)" check3 "3" in outp doAssert exitCode == 2 - - block: # nimBetterRun - let file = "misc/mbetterrun.nim" - const nimcache2 = buildDir / "D20210423T185116" - removeDir nimcache2 - # related to `-d:nimBetterRun` - let opt = fmt"-r --usenimcache --nimcache:{nimcache2}" - var ret = "" - for a in @["v1", "v2", "v1", "v3"]: - ret.add runNimCmdChk(file, fmt"{opt} -d:mbetterrunVal:{a}") - ret.add runNimCmdChk(file, fmt"{opt} -d:mbetterrunVal:v2", rtarg = "arg1 arg2") - # rt arguments should not cause a recompilation - doAssert ret == """ -compiling: v1 -running: v1 -compiling: v2 -running: v2 -running: v1 -compiling: v3 -running: v3 -running: v2 -""", ret - - block: # nim dump - let cmd = fmt"{nim} dump --dump.format:json -d:D20210428T161003 --hints:off ." - let (ret, status) = execCmdEx(cmd) - doAssert status == 0 - let j = ret.parseJson - # sanity checks - doAssert "D20210428T161003" in j["defined_symbols"].to(seq[string]) - doAssert j["version"].to(string) == NimVersion - doAssert j["nimExe"].to(string) == getCurrentCompilerExe() - - block: # genscript - const nimcache2 = buildDir / "D20210524T212851" - removeDir(nimcache2) - let input = "tgenscript_fakefile" # no need for a real file, --eval is good enough - let output = runNimCmdChk(input, fmt"""--genscript --nimcache:{nimcache2.quoteShell} --eval:"echo(12345)" """) - doAssert output.len == 0, output - let ext = when defined(windows): ".bat" else: ".sh" - let filename = fmt"compile_{input}{ext}" # synchronize with `generateScript` - doAssert fileExists(nimcache2/filename), nimcache2/filename - let (outp, status) = execCmdEx(genShellCmd(filename), options = {poStdErrToStdOut}, workingDir = nimcache2) - doAssert status == 0, outp - let (outp2, status2) = execCmdEx(nimcache2 / input, options = {poStdErrToStdOut}) - doAssert outp2 == "12345\n", outp2 - doAssert status2 == 0 - - block: # UnusedImport - proc fn(opt: string, expected: string) = - let output = runNimCmdChk("pragmas/mused3.nim", fmt"--warning:all:off --warning:UnusedImport --hint:DuplicateModuleImport {opt}") - doAssert output == expected, opt & "\noutput:\n" & output & "expected:\n" & expected - fn("-d:case1"): """ -mused3.nim(13, 8) Warning: imported and not used: 'mused3b' [UnusedImport] -""" - fn("-d:case2"): "" - fn("-d:case3"): "" - fn("-d:case4"): "" - fn("-d:case5"): "" - fn("-d:case6"): "" - fn("-d:case7"): "" - fn("-d:case8"): "" - fn("-d:case9"): "" - fn("-d:case10"): "" - when false: - fn("-d:case11"): """ - Warning: imported and not used: 'm2' [UnusedImport] - """ - fn("-d:case12"): """ -mused3.nim(75, 10) Hint: duplicate import of 'mused3a'; previous import here: mused3.nim(74, 10) [DuplicateModuleImport] -""" - - block: # FieldDefect - proc fn(opt: string, expected: string) = - let output = runNimCmdChk("misc/mfield_defect.nim", fmt"-r --warning:all:off --declaredlocs {opt}", status = 1) - doAssert expected in output, opt & "\noutput:\n" & output & "expected:\n" & expected - fn("-d:case1"): """mfield_defect.nim(25, 15) Error: field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'""" - fn("-d:case2 --gc:refc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'""" - fn("-d:case1 -b:js"): """mfield_defect.nim(25, 15) Error: field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'""" - fn("-d:case2 -b:js"): """field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'""" - # 3 instead of k3, because of lack of RTTI - fn("-d:case2 --gc:arc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = 3'""" -else: - discard # only during debugging, tests added here will run with `-d:nimTestsTrunnerDebugging` enabled diff --git a/tests/misc/trunner_special.nim b/tests/misc/trunner_special.nim deleted file mode 100644 index 50a2e4d5a..000000000 --- a/tests/misc/trunner_special.nim +++ /dev/null @@ -1,31 +0,0 @@ -discard """ - targets: "c cpp" - joinable: false -""" - -#[ -Runs tests that require special treatment, e.g. because they rely on 3rd party code -or require external networking. - -xxx test all tests/untestable/* here, possibly with adjustments to make running times reasonable -]# - -import std/[strformat,os,unittest,compilesettings] -import stdtest/specialpaths - -const - nim = getCurrentCompilerExe() - mode = querySetting(backend) - -proc runCmd(cmd: string) = - let ret = execShellCmd(cmd) - check ret == 0 # allows more than 1 failure - -proc main = - let options = fmt"-b:{mode} --hints:off" - 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 --mm:refc {testsDir}/untestable/thttpclient_ssl_remotenetwork.nim" - -main() diff --git a/tests/misc/tsimplesort.nim b/tests/misc/tsimplesort.nim index 395db55e6..e4a8e0b37 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*[A, B] {.final, myShallow.} = object + TTable* {.final, myShallow.}[A, B] = object data: TKeyValuePairSeq[A, B] counter: int @@ -112,7 +112,7 @@ proc initTable*[A, B](initialSize=64): TTable[A, B] = result.counter = 0 newSeq(result.data, initialSize) -proc toTable*[A, B](pairs: openArray[tuple[key: A, +proc toTable*[A, B](pairs: openarray[tuple[key: A, val: B]]): TTable[A, B] = ## creates a new hash table that contains the given `pairs`. result = initTable[A, B](nextPowerOfTwo(pairs.len+10)) @@ -137,7 +137,8 @@ proc `$`*[A, B](t: TTable[A, B]): string = # ------------------------------ count tables ------------------------------- type - TCountTable*[A] {.final, myShallow.} = object ## table that counts the number of each key + TCountTable* {.final, myShallow.}[ + A] = object ## table that counts the number of each key data: seq[tuple[key: A, val: int]] counter: int diff --git a/tests/misc/tsizeof3.nim b/tests/misc/tsizeof3.nim index f0ba8c4d0..1215b4bcd 100644 --- a/tests/misc/tsizeof3.nim +++ b/tests/misc/tsizeof3.nim @@ -27,32 +27,3 @@ type static: doAssert(compiles(offsetOf(Payload, vals))) - - -type - GoodboySave* {.bycopy.} = object - saveCount: uint8 - savePoint: uint16 - shards: uint32 - friendCount: uint8 - friendCards: set[0..255] - locationsKnown: set[0..127] - locationsUnlocked: set[0..127] - pickupsObtained: set[0..127] - pickupsUsed: set[0..127] - pickupCount: uint8 - -block: # bug #20914 - block: - proc csizeof[T](a: T): int {.importc:"sizeof", nodecl.} - - var s: GoodboySave - doAssert sizeof(s) == 108 - doAssert csizeof(s) == static(sizeof(s)) - - block: - proc calignof[T](a: T): int {.importc:"alignof", header: "".} - - var s: set[0..256] - doAssert alignof(s) == 1 - doAssert calignof(s) == static(alignof(s)) diff --git a/tests/misc/tslices.nim b/tests/misc/tslices.nim index 987b50de1..d063c5ebf 100644 --- a/tests/misc/tslices.nim +++ b/tests/misc/tslices.nim @@ -11,9 +11,10 @@ verichtetd # Test the new slices. +import strutils + var mystr = "Abgrund" -# mystr[..1] = "Zu" # deprecated -mystr[0..1] = "Zu" +mystr[..1] = "Zu" mystr[4..4] = "5" @@ -22,8 +23,7 @@ type var myarr: array[TEnum, int] = [1, 2, 3, 4, 5, 6] myarr[e1..e3] = myarr[e4..e6] -# myarr[..e3] = myarr[e4..e6] # deprecated -myarr[0..e3] = myarr[e4..e6] +myarr[..e3] = myarr[e4..e6] for x in items(myarr): stdout.write(x) echo() @@ -46,8 +46,7 @@ echo mystr mystr[4..4] = "u" # test full replacement -# mystr[.. ^2] = "egerichtet" # deprecated -mystr[0 .. ^2] = "egerichtet" +mystr[.. ^2] = "egerichtet" echo mystr diff --git a/tests/misc/tspellsuggest.nim b/tests/misc/tspellsuggest.nim deleted file mode 100644 index 345458bb1..000000000 --- a/tests/misc/tspellsuggest.nim +++ /dev/null @@ -1,45 +0,0 @@ -discard """ - # pending bug #16521 (bug 12) use `matrix` - cmd: "nim c --spellsuggest:15 --hints:off $file" - action: "reject" - nimout: ''' -tspellsuggest.nim(45, 13) Error: undeclared identifier: 'fooBar' -candidates (edit distance, scope distance); see '--spellSuggest': - (1, 0): 'fooBar8' - (1, 1): 'fooBar7' - (1, 3): 'fooBar1' - (1, 3): 'fooBar2' - (1, 3): 'fooBar3' - (1, 3): 'fooBar4' - (1, 3): 'fooBar5' - (1, 3): 'fooBar6' - (1, 5): 'FooBar' - (1, 5): 'fooBar4' - (1, 5): 'fooBar9' - (1, 5): 'fooCar' - (2, 5): 'FooCar' - (2, 5): 'GooBa' - (3, 0): 'fooBarBaz' -''' -""" - -# tests `--spellsuggest:num` - - - -# line 30 -import ./mspellsuggest - -var fooBar1 = 0 -let fooBar2 = 0 -const fooBar3 = 0 -proc fooBar4() = discard -template fooBar5() = discard -macro fooBar6() = discard - -proc main = - var fooBar7 = 0 - block: - var fooBar8 = 0 - const fooBarBaz = 0 - let x = fooBar diff --git a/tests/misc/tspellsuggest2.nim b/tests/misc/tspellsuggest2.nim deleted file mode 100644 index bf76cc208..000000000 --- a/tests/misc/tspellsuggest2.nim +++ /dev/null @@ -1,45 +0,0 @@ -discard """ - # pending bug #16521 (bug 12) use `matrix` - cmd: "nim c --spellsuggest --hints:off $file" - action: "reject" - nimout: ''' -tspellsuggest2.nim(45, 13) Error: undeclared identifier: 'fooBar' -candidates (edit distance, scope distance); see '--spellSuggest': - (1, 0): 'fooBar8' - (1, 1): 'fooBar7' - (1, 3): 'fooBar1' - (1, 3): 'fooBar2' - (1, 3): 'fooBar3' - (1, 3): 'fooBar4' - (1, 3): 'fooBar5' - (1, 3): 'fooBar6' - (1, 5): 'FooBar' - (1, 5): 'fooBar4' - (1, 5): 'fooBar9' - (1, 5): 'fooCar' -''' -""" - -# tests `--spellsuggest` - - - - - - -# line 30 -import ./mspellsuggest - -var fooBar1 = 0 -let fooBar2 = 0 -const fooBar3 = 0 -proc fooBar4() = discard -template fooBar5() = discard -macro fooBar6() = discard - -proc main = - var fooBar7 = 0 - block: - var fooBar8 = 0 - const fooBarBaz = 0 - let x = fooBar diff --git a/tests/misc/tspellsuggest3.nim b/tests/misc/tspellsuggest3.nim deleted file mode 100644 index 9b0b84602..000000000 --- a/tests/misc/tspellsuggest3.nim +++ /dev/null @@ -1,21 +0,0 @@ -discard """ - # pending bug #16521 (bug 12) use `matrix` - cmd: "nim c --spellsuggest:4 --hints:off $file" - action: "reject" - nimout: ''' -tspellsuggest3.nim(21, 1) Error: undeclared identifier: 'fooBar' -candidates (edit distance, scope distance); see '--spellSuggest': - (1, 2): 'FooBar' - (1, 2): 'fooBar4' - (1, 2): 'fooBar9' - (1, 2): 'fooCar' -''' -""" - -import ./mspellsuggest -import ./mspellsuggest -import ./mspellsuggest -import ./mspellsuggest - - -fooBar diff --git a/tests/misc/ttlsemulation.nim b/tests/misc/ttlsemulation.nim deleted file mode 100644 index 767a9bd4e..000000000 --- a/tests/misc/ttlsemulation.nim +++ /dev/null @@ -1,76 +0,0 @@ -discard """ - disabled: i386 - matrix: "-d:nimTtlsemulationCase1 --threads --tlsEmulation:on; -d:nimTtlsemulationCase2 --threads --tlsEmulation:off; -d:nimTtlsemulationCase3 --threads" - targets: "c cpp" -""" - -#[ -tests for: `.cppNonPod`, `--tlsEmulation` -]# - -import std/sugar - -block: - # makes sure the logic in config/nim.cfg or testament doesn't interfere with `--tlsEmulation` so we test the right thing. - when defined(nimTtlsemulationCase1): - doAssert compileOption("tlsEmulation") - elif defined(nimTtlsemulationCase2): - doAssert not compileOption("tlsEmulation") - elif defined(nimTtlsemulationCase3): - when defined(osx): - doAssert not compileOption("tlsEmulation") - else: - doAssert false - -block: - proc main1(): int = - var g0 {.threadvar.}: int - g0.inc - g0 - let s = collect: - for i in 0..<3: main1() - doAssert s == @[1,2,3] - -when defined(cpp): # bug #16752 - when defined(windows) and defined(nimTtlsemulationCase2): - discard # xxx this failed with exitCode 1 - else: - type Foo1 {.importcpp: "Foo1", header: "mtlsemulation.h".} = object - x: cint - type Foo2 {.cppNonPod, importcpp: "Foo2", header: "mtlsemulation.h".} = object - x: cint - - var ctorCalls {.importcpp.}: cint - var dtorCalls {.importcpp.}: cint - type Foo3 {.cppNonPod, importcpp: "Foo3", header: "mtlsemulation.h".} = object - x: cint - - proc sub(i: int) = - var g1 {.threadvar.}: Foo1 - var g2 {.threadvar.}: Foo2 - var g3 {.threadvar.}: Foo3 - discard g1 - discard g2 - - # echo (g3.x, ctorCalls, dtorCalls) - when compileOption("tlsEmulation"): - # xxx bug - discard - else: - doAssert g3.x.int == 10 + i - doAssert ctorCalls == 2 - doAssert dtorCalls == 1 - g3.x.inc - - proc main() = - doAssert ctorCalls == 0 - doAssert dtorCalls == 0 - block: - var f3: Foo3 - doAssert f3.x == 10 - doAssert ctorCalls == 1 - doAssert dtorCalls == 1 - - for i in 0..<3: - sub(i) - main() diff --git a/tests/misc/tunsignedconv.nim b/tests/misc/tunsignedconv.nim index c32f85b4d..b9463b5f0 100644 --- a/tests/misc/tunsignedconv.nim +++ b/tests/misc/tunsignedconv.nim @@ -57,8 +57,8 @@ const x1 = cast[uint](-1) discard $(x1,) # bug #13698 -let n2: csize_t = 1 -doAssert $n2.int32 == "1" +let n: csize = 1 # xxx should that be csize_t or is that essential here? +doAssert $n.int32 == "1" # bug #14616 @@ -66,8 +66,8 @@ let limit = 1'u64 let rangeVar = 0'u64 ..< limit -when not defined(gcRefc): - doAssert repr(rangeVar) == """0 .. 0""", repr(rangeVar) +doAssert repr(rangeVar) == """[a = 0, +b = 0]""" # bug #15210 @@ -79,19 +79,3 @@ except RangeDefect: success = true doAssert success, "conversion should fail at runtime" - -template main() = - # xxx move all tests under here so it gets tested in CT and RT - block: # bug #17572 - type T = distinct uint64 - func f(x: uint64): auto = - let a = T(x) - (x, a.uint64) - const x = 1'u64 shl 63 or 7 - const b = T(x) - doAssert b.uint64 == 9223372036854775815'u64 - doAssert $b.uint64 == "9223372036854775815" - doAssert f(x) == (9223372036854775815'u64, 9223372036854775815'u64) - -static: main() -main() diff --git a/tests/misc/tupcomingfeatures.nim b/tests/misc/tupcomingfeatures.nim new file mode 100644 index 000000000..d37ce85cf --- /dev/null +++ b/tests/misc/tupcomingfeatures.nim @@ -0,0 +1,35 @@ +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/tvarious1.nim b/tests/misc/tvarious1.nim index 9c912ee8e..9c0b541db 100644 --- a/tests/misc/tvarious1.nim +++ b/tests/misc/tvarious1.nim @@ -46,13 +46,3 @@ echo value var ys = @[4.1, 5.6, 7.2, 1.7, 9.3, 4.4, 3.2] #var x = int(ys.high / 2) #echo ys[x] # Works echo ys[int(ys.high / 2)] # Doesn't work - - -# bug #19680 -var here = "" -when stderr is static: - doAssert false -else: - here = "works" - -doAssert here == "works" diff --git a/tests/misc/tvarnums.nim b/tests/misc/tvarnums.nim index 498099c49..2aef242e1 100644 --- a/tests/misc/tvarnums.nim +++ b/tests/misc/tvarnums.nim @@ -7,7 +7,7 @@ import strutils type - TBuffer = array[0..10, uint8] + TBuffer = array[0..10, int8] 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] = uint8(ord(a >% 63'i32) shl 7 or (ord(x < 0'i32) shl 6) or (int(a) and 63)) + b[0] = toU8(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] = uint8(ord(a >% 127'i32) shl 7 or (int(a) and 127)) + b[i] = toU8(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] = uint8(ord(a >% 63'i64) shl 7 or (ord(x < 0'i64) shl 6) or int(a and 63)) + b[0] = toU8(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] = uint8(ord(a >% 127'i64) shl 7 or int(a and 127)) + b[i] = toU8(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 = int64(b[0]) and 63 + result = ze64(b[0]) and 63 var i = 0 Shift = 6'i64 - while (int(b[i]) and 128) != 0: + while (ze(b[i]) and 128) != 0: inc(i) - result = result or ((int64(b[i]) and 127) shl Shift) + result = result or ((ze64(b[i]) and 127) shl Shift) inc(Shift, 7) - if (int(b[0]) and 64) != 0: # sign bit set? + if (ze(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(b[0]) and 63 + result = int32 ze(b[0]) and 63 var i = 0 Shift = 6'i32 - while (int(b[i]) and 128) != 0: + while (ze(b[i]) and 128) != 0: inc(i) - result = result or ((int32(b[i]) and 127'i32) shl Shift) + result = result or ((int32(ze(b[i])) and 127'i32) shl Shift) Shift = Shift + 7'i32 - if (int(b[0]) and (1 shl 6)) != 0: # sign bit set? + if (ze(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/misc/tvcc.nim b/tests/misc/tvcc.nim deleted file mode 100644 index 10533729c..000000000 --- a/tests/misc/tvcc.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - matrix: "--cc:vcc" - disabled: "linux" - disabled: "bsd" - disabled: "osx" - disabled: "unix" - disabled: "posix" -""" -doAssert true diff --git a/tests/misc/twarningaserror.nim b/tests/misc/twarningaserror.nim deleted file mode 100644 index 6f7b76095..000000000 --- a/tests/misc/twarningaserror.nim +++ /dev/null @@ -1,35 +0,0 @@ -discard """ - joinable: false -""" - -#[ -tests: hintAsError, warningAsError -]# - -template fn1 = - {.hintAsError[ConvFromXtoItselfNotNeeded]:on.} - proc fn(a: string) = discard a.string - {.hintAsError[ConvFromXtoItselfNotNeeded]:off.} - -template fn2 = - {.hintAsError[ConvFromXtoItselfNotNeeded]:on.} - proc fn(a: string) = discard a - {.hintAsError[ConvFromXtoItselfNotNeeded]:off.} - -template gn1 = - {.warningAsError[ProveInit]:on.} - proc fn(): var int = discard - discard fn() - {.warningAsError[ProveInit]:off.} - -template gn2 = - {.warningAsError[ProveInit]:on.} - proc fn(): int = discard - discard fn() - {.warningAsError[ProveInit]:off.} - -doAssert not compiles(fn1()) -doAssert compiles(fn2()) - -doAssert not compiles(gn1()) -doAssert compiles(gn2()) diff --git a/tests/mmaptest.nim b/tests/mmaptest.nim index 33010606f..7a93cdd45 100644 --- a/tests/mmaptest.nim +++ b/tests/mmaptest.nim @@ -19,11 +19,8 @@ proc `+!!`(p: pointer, size: int): pointer {.inline.} = result = cast[pointer](cast[int](p) + size) const - PageShift = when defined(nimPage256) or defined(cpu16): 8 - elif defined(nimPage512): 9 - elif defined(nimPage1k): 10 - else: 12 # \ # my tests showed no improvements for using larger page sizes. - + PageShift = when defined(cpu16): 8 else: 12 # \ + # my tests showed no improvements for using larger page sizes. PageSize = 1 shl PageShift var p = osAllocPages(3 * PageSize) diff --git a/tests/modules/a/module_name_clashes.nim b/tests/modules/a/module_name_clashes.nim deleted file mode 100644 index 209526e22..000000000 --- a/tests/modules/a/module_name_clashes.nim +++ /dev/null @@ -1,8 +0,0 @@ -# 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 deleted file mode 100644 index 6a10cac33..000000000 --- a/tests/modules/b/module_name_clashes.nim +++ /dev/null @@ -1,3 +0,0 @@ -# See `tmodule_name_clashes` - -type B* = object diff --git a/tests/modules/t8665.nim b/tests/modules/t8665.nim index 7cfdbdb00..74d31452f 100644 --- a/tests/modules/t8665.nim +++ b/tests/modules/t8665.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--experimental:codeReordering" action: compile """ diff --git a/tests/modules/texplicit_system_import.nim b/tests/modules/texplicit_system_import.nim index 0a4cedc71..bc4d018bf 100644 --- a/tests/modules/texplicit_system_import.nim +++ b/tests/modules/texplicit_system_import.nim @@ -1,9 +1,9 @@ +##. import system except `+` - discard """ errormsg: "undeclared identifier: '+'" line: 9 """ - - +# Testament requires that the initial """ occurs before the 40th byte +# in the file. No kidding... echo 4+5 diff --git a/tests/modules/tmodule_name_clashes.nim b/tests/modules/tmodule_name_clashes.nim deleted file mode 100644 index 814d5d152..000000000 --- a/tests/modules/tmodule_name_clashes.nim +++ /dev/null @@ -1,17 +0,0 @@ -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/modules/treorder.nim b/tests/modules/treorder.nim index 626d9684b..c81715cd8 100644 --- a/tests/modules/treorder.nim +++ b/tests/modules/treorder.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--experimental:codeReordering -d:testdef" + cmd: "nim -d:testdef $target $file" output: '''works 34 34 defined diff --git a/tests/modules/tselfimport.nim b/tests/modules/tselfimport.nim index ba5d9b4cf..7e50bef7c 100644 --- a/tests/modules/tselfimport.nim +++ b/tests/modules/tselfimport.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "module 'tselfimport' cannot import itself" + errormsg: "A module cannot import itself" file: "tselfimport.nim" line: 7 """ diff --git a/tests/navigator/minclude.nim b/tests/navigator/minclude.nim deleted file mode 100644 index f65ebfab9..000000000 --- a/tests/navigator/minclude.nim +++ /dev/null @@ -1,2 +0,0 @@ -# An include file. -foo(3) diff --git a/tests/navigator/tincludefile.nim b/tests/navigator/tincludefile.nim deleted file mode 100644 index a913d0736..000000000 --- a/tests/navigator/tincludefile.nim +++ /dev/null @@ -1,29 +0,0 @@ -discard """ - disabled: true - cmd: "nim check $options --defusages:$file,12,7 $file" - nimout: '''def tincludefile_temp.nim(11, 10) -usage tincludefile_temp.nim(12, 8) - ''' -""" - - - -proc foo(x: int) = - echo x - -foo(3) -echo "yes", 1 != 3 - -#!EDIT!# -discard """ - cmd: "nim check $options --defusages:$file/../minclude.nim,2,2 $file" - nimout: '''def tincludefile_temp.nim(10, 6) -usage minclude.nim(2, 1) - ''' -""" - - -proc foo(x: int) = - echo x - -include minclude diff --git a/tests/navigator/tnav1.nim b/tests/navigator/tnav1.nim deleted file mode 100644 index e76c921f3..000000000 --- a/tests/navigator/tnav1.nim +++ /dev/null @@ -1,33 +0,0 @@ -discard """ - disabled: true - cmd: "nim check $options --defusages:$file,12,7 $file" - nimout: '''def tnav1_temp.nim(11, 10) -usage tnav1_temp.nim(12, 8) - ''' -""" - -import std / [times] - -proc foo(x: int) = - echo x - -foo(3) -echo "yes", 1 != 3 - -#!EDIT!# -discard """ - cmd: "nim check $options --defusages:$file,15,2 $file" - nimout: '''def tnav1_temp.nim(12, 6) -usage tnav1_temp.nim(15, 1) - ''' -""" - - -import std / [times] - -proc foo(x: int) = - echo x - -foo(3) -echo "yes", 1 != 3 - diff --git a/tests/newconfig/foo2/mfoo2.customext b/tests/newconfig/foo2/mfoo2.customext deleted file mode 100644 index 66c8b1d15..000000000 --- a/tests/newconfig/foo2/mfoo2.customext +++ /dev/null @@ -1,2 +0,0 @@ -doAssert defined(nimscript) -echo "123" diff --git a/tests/newconfig/mconfigcheck.nims b/tests/newconfig/mconfigcheck.nims deleted file mode 100644 index 8df6715f6..000000000 --- a/tests/newconfig/mconfigcheck.nims +++ /dev/null @@ -1,9 +0,0 @@ -mode = ScriptMode.Verbose -proc build() = - echo "building nim... " - exec "sleep 10" - exec "nonexistant command" - echo getCurrentDir() - -echo "hello" -build() diff --git a/tests/newconfig/tfoo.nim b/tests/newconfig/tfoo.nim index 0c6ded470..6654202c5 100644 --- a/tests/newconfig/tfoo.nim +++ b/tests/newconfig/tfoo.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim default --hint:cc:off --hint:cc $file" - output: '''hello world! 0.5 true''' + output: '''hello world! 0.5''' nimout: '''[NimScript] exec: gcc -v''' """ @@ -10,4 +10,4 @@ when not defined(definedefine): import math, mfriends discard gen[int]() -echo "hello world! ", ln 2.0, " ", compileOption("opt", "speed") +echo "hello world! ", ln 2.0 diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index 0cb950227..6f0048afb 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -3,8 +3,6 @@ mode = ScriptMode.Whatif exec "gcc -v" ---define:release - --forceBuild --path: "../friends" diff --git a/tests/nimdoc/t17615.nim b/tests/nimdoc/t17615.nim deleted file mode 100644 index 77ae35a15..000000000 --- a/tests/nimdoc/t17615.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - cmd: "nim doc -r $file" - errormsg: "runnableExamples must appear before the first non-comment statement" - line: 10 -""" - -func fn*() = - ## foo - discard - runnableExamples: - assert true diff --git a/tests/nimdoc/trunnableexamples.nim b/tests/nimdoc/trunnableexamples.nim index 1188cdd1b..6232011cb 100644 --- a/tests/nimdoc/trunnableexamples.nim +++ b/tests/nimdoc/trunnableexamples.nim @@ -1,31 +1,19 @@ discard """ -cmd: "nim doc --doccmd:--hints:off --hints:off $file" +cmd: "nim doc --doccmd:-d:testFooExternal --hints:off $file" action: "compile" -nimoutFull: true nimout: ''' foo1 foo2 foo3 foo5 +foo6 foo7 -in examplesInTemplate1 -doc in outer -doc in inner1 -doc in inner2 foo8 foo9 -foo6 ''' joinable: false """ -#[ -pending bug #18077, use instead: -cmd: "nim doc --doccmd:'-d:testFooExternal --hints:off' --hints:off $file" -and merge trunnableexamples2 back here -]# -{.define(testFooExternal).} - proc fun*() = runnableExamples: block: # `defer` only allowed inside a block @@ -43,7 +31,7 @@ proc fun*() = proc fun*()=echo "foo5" fun() - runnableExamples("--experimental:codeReordering --warnings:off"): + runnableExamples: # `codeReordering` only allowed at top level {.experimental: "codeReordering".} proc fun1() = fun2() @@ -65,7 +53,7 @@ when true: # issue #12746 runnableExamples: try: discard - except CatchableError: + except: # just the general except will work discard @@ -105,7 +93,7 @@ when true: # runnableExamples with rdoccmd template fun3Impl(): untyped = runnableExamples(rdoccmd="-d:foo"): - nonexistent + nonexistant # bugfix: this shouldn't be semchecked when `runnableExamples` # has more than 1 argument discard @@ -121,72 +109,6 @@ when true: # runnableExamples with rdoccmd # passing seq (to run with multiple compilation options) runnableExamples(@["-b:cpp", "-b:js"]): discard -when true: # bug #16993 - template examplesInTemplate1*(cond: untyped) = - ## in examplesInTemplate1 - runnableExamples: - echo "in examplesInTemplate1" - discard - examplesInTemplate1 true - examplesInTemplate1 true - examplesInTemplate1 true - -when true: # bug #18054 - template outer*(body: untyped) = - ## outer template doc string. - runnableExamples: - echo "doc in outer" - ## - template inner1*() = - ## inner1 template doc string. - runnableExamples: - echo "doc in inner1" - ## - - template inner2*() = - ## inner2 template doc string. - runnableExamples: - echo "doc in inner2" - body - outer: - inner1() - inner2() - -when true: # bug #17835 - template anyItFake*(s, pred: untyped): bool = - ## Foo - runnableExamples: discard - true - - proc anyItFakeMain*(n: seq[int]): bool = - result = anyItFake(n, it == 0) - # this was giving: Error: runnableExamples must appear before the first non-comment statement - -runnableExamples: - block: # bug #17279 - when int.sizeof == 8: - let x = 0xffffffffffffffff - doAssert x == -1 - - # bug #13491 - block: - proc fun(): int = doAssert false - doAssertRaises(AssertionDefect, (discard fun())) - - block: - template foo(body) = discard - foo (discard) - - block: - template fn(body: untyped): untyped = true - doAssert(fn do: nonexistent) - import std/macros - macro foo*(x, y) = - result = newLetStmt(x[0][0], x[0][1]) - foo: - a = 1 - do: discard - # also check for runnableExamples at module scope runnableExamples: block: @@ -198,13 +120,3 @@ runnableExamples: # note: there are yet other examples where putting runnableExamples at module # scope is needed, for example when using an `include` before an `import`, etc. - -##[ -snippet: - -.. code-block:: Nim - :test: - - doAssert defined(testFooExternal) - -]## diff --git a/tests/nimdoc/trunnableexamples2.nim b/tests/nimdoc/trunnableexamples2.nim deleted file mode 100644 index 5a437744e..000000000 --- a/tests/nimdoc/trunnableexamples2.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ -cmd: "nim doc --doccmd:-d:testFooExternal --hints:off $file" -action: "compile" -joinable: false -""" - -# pending bug #18077, merge back inside trunnableexamples.nim -when true: # runnableExamples with rdoccmd - runnableExamples "-d:testFoo -d:testBar": - doAssert defined(testFoo) and defined(testBar) - doAssert defined(testFooExternal) diff --git a/tests/niminaction/Chapter1/various1.nim b/tests/niminaction/Chapter1/various1.nim index 21553dc40..4e2cb463d 100644 --- a/tests/niminaction/Chapter1/various1.nim +++ b/tests/niminaction/Chapter1/various1.nim @@ -32,7 +32,7 @@ block: # Block added due to clash. let dog = Dog() dog.bark() #<2> -import sequtils, sugar, strutils +import sequtils, future, strutils let list = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"] list.map( (x: string) -> (string, string) => (x.split[0], x.split[1]) diff --git a/tests/niminaction/Chapter2/resultaccept.nim b/tests/niminaction/Chapter2/resultaccept.nim index 390f7b329..7dd976b40 100644 --- a/tests/niminaction/Chapter2/resultaccept.nim +++ b/tests/niminaction/Chapter2/resultaccept.nim @@ -22,7 +22,7 @@ proc resultVar2: string = result.add("returned") doAssert implicit() == "I will be returned" -doAssert discarded().len == 0 +doAssert discarded() == nil doAssert explicit() == "I will be returned" doAssert resultVar() == "I will be returned" doAssert resultVar2() == "I will be returned" \ No newline at end of file diff --git a/tests/niminaction/Chapter2/various2.nim b/tests/niminaction/Chapter2/various2.nim index 921f38c7d..488c361a2 100644 --- a/tests/niminaction/Chapter2/various2.nim +++ b/tests/niminaction/Chapter2/various2.nim @@ -140,7 +140,7 @@ let numbers = @[1, 2, 3, 4, 5, 6] let odd = filter(numbers, proc (x: int): bool = x mod 2 != 0) doAssert odd == @[1, 3, 5] -import sequtils, sugar +import sequtils, future let numbers1 = @[1, 2, 3, 4, 5, 6] let odd1 = filter(numbers1, (x: int) -> bool => x mod 2 != 0) assert odd1 == @[1, 3, 5] @@ -149,7 +149,7 @@ proc isValid(x: int, validator: proc (x: int): bool) = if validator(x): echo(x, " is valid") else: echo(x, " is NOT valid") -import sugar +import future proc isValid2(x: int, validator: (x: int) -> bool) = if validator(x): echo(x, " is valid") else: echo(x, " is NOT valid") @@ -193,7 +193,7 @@ doAssertRaises(IndexDefect): block: var list = newSeq[string](3) - assert list[0].len == 0 + assert list[0] == nil list[0] = "Foo" list[1] = "Bar" list[2] = "Baz" diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim index c7cdf7db4..4e028a048 100644 --- a/tests/niminaction/Chapter3/various3.nim +++ b/tests/niminaction/Chapter3/various3.nim @@ -1,5 +1,4 @@ discard """ -matrix: "--mm:refc" output: ''' Future is no longer empty, 42 ''' diff --git a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim index 67d9323f2..a7d4ebe00 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim @@ -1,5 +1,4 @@ discard """ -disabled: true output: "Database created successfully!" """ diff --git a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim index 1b521521c..fe39278fb 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim @@ -1,7 +1,5 @@ discard """ -disabled: true action: compile -matrix: "--threads:off" """ import asyncdispatch, times diff --git a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim index c8beb4a30..a3cab4cba 100644 --- a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim +++ b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim @@ -1,5 +1,4 @@ discard """ -disabled: true outputsub: "All tests finished successfully!" """ diff --git a/tests/niminaction/Chapter8/sdl/sdl.nim b/tests/niminaction/Chapter8/sdl/sdl.nim index 212f7b022..7ba154cae 100644 --- a/tests/niminaction/Chapter8/sdl/sdl.nim +++ b/tests/niminaction/Chapter8/sdl/sdl.nim @@ -1,8 +1,8 @@ -when defined(windows): +when defined(Windows): const libName* = "SDL2.dll" -elif defined(linux) or defined(freebsd) or defined(netbsd): +elif defined(Linux) or defined(freebsd) or defined(netbsd): const libName* = "libSDL2.so" -elif defined(macosx): +elif defined(MacOsX): const libName* = "libSDL2.dylib" elif defined(openbsd): const libName* = "libSDL2.so.0.6" diff --git a/tests/notnil/tnotnil5.nim b/tests/notnil/tnotnil5.nim deleted file mode 100644 index 2dcb7f7c3..000000000 --- a/tests/notnil/tnotnil5.nim +++ /dev/null @@ -1,28 +0,0 @@ -discard """ - matrix: "--threads:on" -""" - -{.experimental: "parallel".} -{.experimental: "notnil".} -import threadpool - -type - AO = object - x: int - - A = ref AO not nil - -proc process(a: A): A = - return A(x: a.x+1) - -proc processMany(ayys: openArray[A]): seq[A] = - var newAs: seq[FlowVar[A]] - - parallel: - for a in ayys: - newAs.add(spawn process(a)) - for newAflow in newAs: - let newA = ^newAflow - if isNil(newA): - return @[] - result.add(newA) diff --git a/tests/objects/m19342.c b/tests/objects/m19342.c deleted file mode 100644 index 113f65309..000000000 --- a/tests/objects/m19342.c +++ /dev/null @@ -1,12 +0,0 @@ -struct Node -{ - int data[25]; -}; - - -struct Node hello(int name) { - struct Node x = {999, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9, - 1, 2, 3, 4, 5}; - return x; -} \ No newline at end of file diff --git a/tests/objects/mobject_default_value.nim b/tests/objects/mobject_default_value.nim deleted file mode 100644 index 224549501..000000000 --- a/tests/objects/mobject_default_value.nim +++ /dev/null @@ -1,15 +0,0 @@ -type - Clean = object - mem: int - Default* = object - poi: int = 12 - clc: Clean - se*: range[0'i32 .. high(int32)] - - NonDefault* = object - poi: int - - PrellDeque*[T] = object - pendingTasks*: range[0'i32 .. high(int32)] - head: T - tail: T diff --git a/tests/objects/t17437.nim b/tests/objects/t17437.nim deleted file mode 100644 index b5c0e0525..000000000 --- a/tests/objects/t17437.nim +++ /dev/null @@ -1,22 +0,0 @@ -discard """ - cmd: "nim check $file" - errormsg: "" - nimout: ''' -t17437.nim(20, 16) Error: undeclared identifier: 'x' -t17437.nim(20, 16) Error: expression 'x' has no type (or is ambiguous) -t17437.nim(20, 19) Error: incorrect object construction syntax -t17437.nim(20, 19) Error: incorrect object construction syntax -t17437.nim(20, 12) Error: expression '' has no type (or is ambiguous) -''' -""" - -# bug #17437 invalid object construction should result in error - -type - V = ref object - x, y: int - -proc m = - var v = V(x: x, y) - -m() diff --git a/tests/objects/t19342.nim b/tests/objects/t19342.nim deleted file mode 100644 index d40d15a4b..000000000 --- a/tests/objects/t19342.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - targets: "c cpp" -""" - -{.compile: "m19342.c".} - -# bug #19342 -type - Node* {.bycopy.} = object - data: array[25, cint] - -proc myproc(name: cint): Node {.importc: "hello", cdecl.} - -proc parse = - let node = myproc(10) - doAssert node.data[0] == 999 - -parse() diff --git a/tests/objects/t19342_2.nim b/tests/objects/t19342_2.nim deleted file mode 100644 index 6f6d0f2b3..000000000 --- a/tests/objects/t19342_2.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - targets: "c cpp" -""" - -{.compile: "m19342.c".} - -# bug #19342 -type - Node* {.byRef.} = object - data: array[25, cint] - -proc myproc(name: cint): Node {.importc: "hello", cdecl.} - -proc parse = - let node = myproc(10) - doAssert node.data[0] == 999 - -parse() \ No newline at end of file diff --git a/tests/objects/t4318.nim b/tests/objects/t4318.nim index beadd6909..34ff722f5 100644 --- a/tests/objects/t4318.nim +++ b/tests/objects/t4318.nim @@ -1,8 +1,3 @@ -discard """ - matrix: "--mm:refc" -""" - - type A = object of RootObj B = object of A diff --git a/tests/objects/tdefaultfieldscheck.nim b/tests/objects/tdefaultfieldscheck.nim deleted file mode 100644 index d6feb2988..000000000 --- a/tests/objects/tdefaultfieldscheck.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - cmd: "nim check --hints:off $file" - errormsg: "" - nimout: -''' -tdefaultfieldscheck.nim(14, 17) Error: type mismatch: got but expected 'int' -tdefaultfieldscheck.nim(15, 20) Error: type mismatch: got but expected 'string' -tdefaultfieldscheck.nim(17, 16) Error: type mismatch: got but expected 'int' -''' -""" - -type - Date* = object - name: int = "string" - time: string = 12 - goal: float = 7 - fun: int = 1.4 - -echo default(Date) \ No newline at end of file diff --git a/tests/objects/tdefaultrangetypescheck.nim b/tests/objects/tdefaultrangetypescheck.nim deleted file mode 100644 index 71e7ac59b..000000000 --- a/tests/objects/tdefaultrangetypescheck.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - errormsg: "cannot convert 0 to range 1..5(int)" - line: 9 -""" - -type - Point = object - y: int - x: range[1..5] = 0 - -echo default(Point) diff --git a/tests/objects/tobj_asgn_dont_slice.nim b/tests/objects/tobj_asgn_dont_slice.nim index ce67c4490..2e36b65a3 100644 --- a/tests/objects/tobj_asgn_dont_slice.nim +++ b/tests/objects/tobj_asgn_dont_slice.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" outputsub: '''ObjectAssignmentDefect''' exitcode: "1" """ diff --git a/tests/objects/tobjconstr.nim b/tests/objects/tobjconstr.nim index ee5a5b221..1e4d89d68 100644 --- a/tests/objects/tobjconstr.nim +++ b/tests/objects/tobjconstr.nim @@ -50,30 +50,27 @@ 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 -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 +# 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) - # 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) - 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 cf4a694b4..6253edab0 100644 --- a/tests/objects/tobjconstr2.nim +++ b/tests/objects/tobjconstr2.nim @@ -15,8 +15,8 @@ echo s[0].x # bug #563 type - Foo {.inheritable.} = - object + Foo = + object {.inheritable.} x: int Bar = diff --git a/tests/objects/tobject.nim b/tests/objects/tobject.nim index a185bebcb..543a86376 100644 --- a/tests/objects/tobject.nim +++ b/tests/objects/tobject.nim @@ -1,3 +1,4 @@ +import unittest type Obj = object foo: int @@ -9,9 +10,9 @@ block: # object basic methods block: # it should convert an object to a string var obj = makeObj(1) # Should be "obj: (foo: 1)" or similar. - doAssert($obj == "(foo: 1)") + check($obj == "(foo: 1)") block: # it should test equality based on fields - doAssert(makeObj(1) == makeObj(1)) + check(makeObj(1) == makeObj(1)) # bug #10203 @@ -57,18 +58,3 @@ block: # bug #14698 x1: int x3: seq[int] doAssert t[].sizeof == Foo1.sizeof - -# bug #147 -type - TValue* {.pure, final.} = object of RootObj - a: int - PValue = ref TValue - PPValue = ptr PValue - - -var x: PValue -new x -var sp: PPValue = addr x - -sp.a = 2 -doAssert sp.a == 2 diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim deleted file mode 100644 index 59af943e0..000000000 --- a/tests/objects/tobject_default_value.nim +++ /dev/null @@ -1,596 +0,0 @@ -discard """ - matrix: "-d:nimPreviewRangeDefault --mm:refc; -d:nimPreviewRangeDefault --warningAsError:ProveInit --mm:orc" - targets: "c cpp js" -""" - -import std/[times, macros, tables] - -type - Guess = object - poi: DateTime - - GuessDistinct = distinct Guess - -block: - var x: Guess - discard Guess() - - var y: GuessDistinct - - discard y - - discard GuessDistinct(x) - - -import mobject_default_value - -block: - let x = Default() - doAssert x.se == 0'i32 - -block: - let x = default(Default) - doAssert x.se == 0'i32 -# echo Default(poi: 12) -# echo Default(poi: 17) - -# echo NonDefault(poi: 77) - -block: - var x: Default - doAssert x.se == 0'i32 - -type - ObjectBase = object of RootObj - value = 12 - - ObjectBaseDistinct = distinct ObjectBase - - DinstinctInObject = object - data: ObjectBaseDistinct - - Object = object of ObjectBase - time: float = 1.2 - date: int - scale: range[1..10] - - Object2 = object - name: Object - - Object3 = object - obj: Object2 - - ObjectTuple = tuple - base: ObjectBase - typ: int - obj: Object - - TupleInObject = object - size = 777 - data: ObjectTuple - - Ref = ref object of ObjectBase - - RefInt = ref object of Ref - data = 73 - - Ref2 = ref object of ObjectBase - - RefInt2 = ref object of Ref - data = 73 - -var t {.threadvar.}: Default -# var m1, m2 {.threadvar.}: Default - -block: - doAssert t.se == 0'i32 - -block: # ARC/ORC cannot bind destructors twice, so it cannot - # be moved into main - block: - var x: Ref - new(x) - doAssert x.value == 12, "Ref.value = " & $x.value - - var y: RefInt - new(y) - doAssert y.value == 12 - doAssert y.data == 73 - - block: - var x: Ref2 - new(x, proc (x: Ref2) {.nimcall.} = discard "call Ref") - doAssert x.value == 12, "Ref.value = " & $x.value - - proc call(x: RefInt2) = - discard "call RefInt" - - var y: RefInt2 - new(y, call) - doAssert y.value == 12 - doAssert y.data == 73 - -template main {.dirty.} = - block: # bug #16744 - type - R = range[1..10] - Obj = object - r: R - - var - rVal: R = default(R) # Works fine - objVal = default(Obj) - - doAssert rVal == 0 # it should be 1 - doAssert objVal.r == 1 - - block: # bug #16744 - type - R = range[1..10] - Obj = object - r: R - - var - rVal: R = default(R) # Works fine - objVal = Obj() - - doAssert rVal == 0 # it should be 1 - doAssert objVal.r == 1 - - block: # bug #3608 - type - abc = ref object - w: range[2..100] - - proc createABC(): abc = - new(result) - result.w = 20 - - doAssert createABC().w == 20 - - block: - var x = new ObjectBase - doAssert x.value == 12 - - proc hello(): ref ObjectBase = - new result - - let z = hello() - doAssert z.value == 12 - - block: - var base = ObjectBase() - var x: ObjectBaseDistinct = ObjectBaseDistinct(base) - doAssert ObjectBase(x).value == 12 - let y = ObjectBaseDistinct(default(ObjectBase)) - doAssert ObjectBase(y).value == 12 - - let m = ObjectBaseDistinct(ObjectBase()) - doAssert ObjectBase(m).value == 12 - - proc hello(): ObjectBaseDistinct = - result = ObjectBaseDistinct(default(ObjectBase)) - - let z = hello() - doAssert ObjectBase(z).value == 12 - - block: - var x: DinstinctInObject - x.data = ObjectBaseDistinct(default(ObjectBase)) - - doAssert ObjectBase(x.data).value == 12 - - block: - var x = Object() - doAssert x.value == 12 - doAssert x.time == 1.2 - doAssert x.scale == 1 - - let y = default(Object) - doAssert y.value == 12 - doAssert y.time == 1.2 - doAssert y.scale == 1 - - var x1, x2, x3 = default(Object) - doAssert x1.value == 12 - doAssert x1.time == 1.2 - doAssert x1.scale == 1 - doAssert x2.value == 12 - doAssert x2.time == 1.2 - doAssert x2.scale == 1 - doAssert x3.value == 12 - doAssert x3.time == 1.2 - doAssert x3.scale == 1 - - block: - var x = new Object - doAssert x[] == default(Object) - - block: - var x = default(Object2) - doAssert x.name.value == 12 - doAssert x.name.time == 1.2 - doAssert x.name.scale == 1 - - block: - let x = Object2() - doAssert x.name.value == 12 - doAssert x.name.time == 1.2 - doAssert x.name.scale == 1 - - block: - var x: ref Object2 - new x - doAssert x[] == default(Object2) - - block: - var x = default(Object3) - doAssert x.obj.name.value == 12 - doAssert x.obj.name.time == 1.2 - doAssert x.obj.name.scale == 1 - - block: - var x = Object3() - doAssert x.obj.name.value == 12 - doAssert x.obj.name.time == 1.2 - doAssert x.obj.name.scale == 1 - - when nimvm: - # todo - discard "fixme" - else: - when defined(gcArc) or defined(gcOrc): - block: #seq - var x = newSeq[Object](10) - let y = x[0] - doAssert y.value == 12 - doAssert y.time == 1.2 - doAssert y.scale == 1 - - block: - var x: seq[Object] - setLen(x, 5) - let y = x[^1] - doAssert y.value == 12 - doAssert y.time == 1.2 - doAssert y.scale == 1 - - block: - var my = @[1, 2, 3, 4, 5] - my.setLen(0) - my.setLen(5) - doAssert my == @[0, 0, 0, 0, 0] - - block: - var my = "hello" - my.setLen(0) - my.setLen(5) - doAssert $(@my) == """@['\x00', '\x00', '\x00', '\x00', '\x00']""" - - block: # array - var x: array[10, Object] = default(array[10, Object]) - let y = x[0] - doAssert y.value == 12 - doAssert y.time == 1.2 - doAssert y.scale == 1 - - block: # array - var x {.noinit.}: array[10, Object] - discard x - - block: # tuple - var x = default(ObjectTuple) - doAssert x.base.value == 12 - doAssert x.typ == 0 - doAssert x.obj.time == 1.2 - doAssert x.obj.date == 0 - doAssert x.obj.scale == 1 - doAssert x.obj.value == 12 - - block: # tuple in object - var x = default(TupleInObject) - doAssert x.data.base.value == 12 - doAssert x.data.typ == 0 - doAssert x.data.obj.time == 1.2 - doAssert x.data.obj.date == 0 - doAssert x.data.obj.scale == 1 - doAssert x.data.obj.value == 12 - doAssert x.size == 777 - - type - ObjectArray = object - data: array[10, Object] - - block: - var x = default(ObjectArray) - let y = x.data[0] - doAssert y.value == 12 - doAssert y.time == 1.2 - doAssert y.scale == 1 - - block: - var x: PrellDeque[int] - doAssert x.pendingTasks == 0 - - type - Color = enum - Red, Blue, Yellow - - ObjectVarint = object - case kind: Color - of Red: - data: int = 10 - of Blue: - fill = "123" - of Yellow: - time = 1.8'f32 - - ObjectVarint1 = object - case kind: Color = Blue - of Red: - data1: int = 10 - of Blue: - fill2 = "123" - cry: float - of Yellow: - time3 = 1.8'f32 - him: int - - block: - var x = ObjectVarint(kind: Red) - doAssert x.kind == Red - doAssert x.data == 10 - - block: - var x = ObjectVarint(kind: Blue) - doAssert x.kind == Blue - doAssert x.fill == "123" - - block: - var x = ObjectVarint(kind: Yellow) - doAssert x.kind == Yellow - doAssert typeof(x.time) is float32 - - block: - var x = default(ObjectVarint1) - doAssert x.kind == Blue - doAssert x.fill2 == "123" - x.cry = 326 - - type - ObjectVarint2 = object - case kind: Color - of Red: - data: int = 10 - of Blue: - fill = "123" - of Yellow: - time = 1.8'f32 - - block: - var x = ObjectVarint2(kind: Blue) - doAssert x.fill == "123" - - block: - type - Color = enum - Red, Blue, Yellow - - type - ObjectVarint3 = object # fixme it doesn't work with static - case kind: Color = Blue - of Red: - data1: int = 10 - of Blue: - case name: Color = Blue - of Blue: - go = 12 - else: - temp = 66 - fill2 = "123" - cry: float - of Yellow: - time3 = 1.8'f32 - him: int - - block: - var x = default(ObjectVarint3) - doAssert x.kind == Blue - doAssert x.name == Blue - doAssert x.go == 12 - - block: - var x = ObjectVarint3(kind: Blue, name: Red, temp: 99) - doAssert x.kind == Blue - doAssert x.name == Red - doAssert x.temp == 99 - - block: - type - Default = tuple - id: int = 1 - obj: ObjectBase - name: string - - Class = object - def: Default - - Member = object - def: Default = (id: 777, obj: ObjectBase(), name: "fine") - - block: - var x = default(Default) - doAssert x.id == 1 - doAssert x.obj == default(ObjectBase) - doAssert x.name == "" - - block: - var x = default(Class) - doAssert x.def == default(Default) - doAssert x.def.id == 1 - doAssert x.def.obj == default(ObjectBase) - doAssert x.def.name == "" - - block: - var x = default(Member) - doAssert x.def.id == 777 - doAssert x.def.obj == default(ObjectBase) - doAssert x.def.name == "fine" - - block: - var x {.noinit.} = 12 - doAssert x == 12 - - type - Pure = object - id: int = 12 - - var y {.noinit.}: Pure - doAssert y.id == 0 - - var z {.noinit.}: Pure = Pure(id: 77) - doAssert z.id == 77 - - block: # bug #20681 - type A = object - d: DateTime = DateTime() - - let x = default(A) - doAssert $x == "(d: Uninitialized DateTime)" - - block: # bug #20715 - block: - type - Foo = enum - A - B - - Bar = object - case foo: Foo - of A: - t: range[-1..2] - else: discard - - var d = default(Bar) - doAssert d.t == -1 - - block: - type - Foo = enum - A - B - - Bar = object - case foo: Foo - of A: - t: range[0..2] - else: discard - - var d = default(Bar) - doAssert d.t == 0 - - block: # bug #20740 - block: - proc foo(x: static DateTime = Datetime()) = - discard - - foo() - - block: - macro foo(x: static DateTime) = - discard x - - macro foo2: untyped = - var x = DateTime() - - result = quote do: - foo(`x`) - - foo2() - - - block: # issue #20699 - type - Either[A,B] = object - case kind:bool - of false: - b: B - of true: - a: A - O = object of RootRef - - proc oToEither(o:O):Either[O,void] = - Either[O,void](kind:true,a: o) - - discard oToEither(O()) - - block: # bug #20695 - type - Default = object - tabs: Table[string, int] = initTable[string, int]() - - let d = default(Default) - doAssert d.tabs.len == 0 - - block: - type - Default = object - tabs: Table[string, int] = Table[string, int]() - - let d = default(Default) - doAssert d.tabs.len == 0 - - - block: - type DjangoDateTime = distinct DateTime - - type Default = object - data: DjangoDateTime = DjangoDateTime(DateTime()) - - let x = default(Default) - doAssert x.data is DjangoDateTime - - block: - type DjangoDateTime = distinct DateTime - - type Default = object - data = DjangoDateTime(DateTime()) - - let x = default(Default) - doAssert x.data is DjangoDateTime - - block: - type - Result2 = object - case o: bool - of false: - e: float - of true: - v {.requiresInit.} : int = 1 - - proc startSessionSync(): Result2 = - return Result2(o: true) - - proc mainSync = - let ff = startSessionSync() - doAssert ff.v == 1 - - mainSync() - - block: - type - Result2 = object - v {.requiresInit.} : int = 1 - - proc startSessionSync(): Result2 = - return Result2() - - proc mainSync = - let ff = startSessionSync() - doAssert ff.v == 1 - - mainSync() - - -static: main() -main() diff --git a/tests/objects/tobjects_various.nim b/tests/objects/tobjects_various.nim index 55db9312e..315193de9 100644 --- a/tests/objects/tobjects_various.nim +++ b/tests/objects/tobjects_various.nim @@ -31,7 +31,7 @@ block tobject2: block tofopr: type - TMyType {.inheritable.} = object + TMyType = object {.inheritable.} len: int data: string @@ -105,16 +105,3 @@ block t7244: proc test(foo: var Foo) = discard proc test(bar: var Bar) = test(Foo(bar)) - - -import std/macros - -#bug #20856 -macro ensureImplWorksOnConstr(t: typed): untyped = - expectKind(t, nnkObjConstr) - doAssert t[0].getTypeInst.getImpl.repr == "A = object" - doAssert t[0].getImpl.repr == "A = object" - -type A = object - -ensureImplWorksOnConstr(A()) diff --git a/tests/objects/tunsafenew.nim b/tests/objects/tunsafenew.nim deleted file mode 100644 index 6c1b33cd9..000000000 --- a/tests/objects/tunsafenew.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - errormsg: "conversion from int literal(-1) to Natural is invalid" -""" - -type - Obj = object - case b: bool - else: discard -var o: ref Obj -unsafeNew(o, -1) \ No newline at end of file diff --git a/tests/objects/tunsafenew2.nim b/tests/objects/tunsafenew2.nim deleted file mode 100644 index 83112bcfc..000000000 --- a/tests/objects/tunsafenew2.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ -valgrind: "leaks" -matrix: "-d:useMalloc" -targets: "c cpp" -""" - -type - Obj = object - case b: bool - else: discard - a: UncheckedArray[byte] - -var o: ref Obj -unsafeNew(o, sizeof(Obj) + 512) -zeroMem(addr o.a, 512) diff --git a/tests/objvariant/t14581.nim b/tests/objvariant/t14581.nim deleted file mode 100644 index 72ba32f18..000000000 --- a/tests/objvariant/t14581.nim +++ /dev/null @@ -1,25 +0,0 @@ -discard """ - matrix: "--gc:refc; --gc:arc" - output: "abc: @[(kind: A, x: 0)]" -""" - -import std/tables - -type E = enum - A, B - -type O = object - case kind: E - of A: - x: int - of B: - y: int - -proc someTable(): Table[string, seq[O]] = - result = initTable[string, seq[O]]() - result["abc"] = @[O(kind: A)] - -const t = someTable() - -for k, v in t: - echo k, ": ", v diff --git a/tests/objvariant/tcheckedfield1.nim b/tests/objvariant/tcheckedfield1.nim index 4a6c49f66..69b099f24 100644 --- a/tests/objvariant/tcheckedfield1.nim +++ b/tests/objvariant/tcheckedfield1.nim @@ -1,5 +1,6 @@ discard """ - nimout: "tcheckedfield1.nim(39, 6) Warning: cannot prove that field 'x.s' is accessible [ProveField]" + nimout: "Warning: cannot prove that field 'x.s' is accessible [ProveField]" + line:51 action: run output: "abc abc" """ diff --git a/tests/objvariant/treassign.nim b/tests/objvariant/treassign.nim index 527204616..2938b30a3 100644 --- a/tests/objvariant/treassign.nim +++ b/tests/objvariant/treassign.nim @@ -25,34 +25,3 @@ t.curr = TokenObject(kind: Token.bar, bar: BasicNumber(value: 12.34)) t.curr = TokenObject(kind: Token.foo, foo: "foo") echo "SUCCESS" - -proc passToVar(x: var Token) = discard - -{.cast(uncheckedAssign).}: - passToVar(t.curr.kind) - - t.curr = TokenObject(kind: t.curr.kind, foo: "abc") - - t.curr.kind = Token.foo - - -block: - type - TokenKind = enum - strLit, intLit - Token = object - case kind*: TokenKind - of strLit: - s*: string - of intLit: - i*: int64 - - var t = Token(kind: strLit, s: "abc") - - {.cast(uncheckedAssign).}: - - # inside the 'cast' section it is allowed to assign to the 't.kind' field directly: - t.kind = intLit - - {.cast(uncheckedAssign).}: - t.kind = strLit \ No newline at end of file diff --git a/tests/openarray/topena1.nim b/tests/openarray/topena1.nim index 380c57f2a..ed3a0cedb 100644 --- a/tests/openarray/topena1.nim +++ b/tests/openarray/topena1.nim @@ -6,4 +6,4 @@ discard """ # Tests a special bug var - x: ref openArray[string] #ERROR_MSG invalid type + x: ref openarray[string] #ERROR_MSG invalid type diff --git a/tests/openarray/topenarray.nim b/tests/openarray/topenarray.nim deleted file mode 100644 index c752becf2..000000000 --- a/tests/openarray/topenarray.nim +++ /dev/null @@ -1,53 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -proc fn1[T](a: openArray[T]): seq[T] = - for ai in a: result.add ai - -proc fn2[T](a: var openArray[T]): seq[T] = - for ai in a: result.add ai - -proc fn3[T](a: var openArray[T]) = - for i, ai in mpairs(a): ai = i * 10 - -proc main = - var a = [1,2,3,4,5] - - doAssert fn1(a.toOpenArray(1,3)) == @[2,3,4] - - doAssert fn2(toOpenArray(a, 1, 3)) == @[2,3,4] - doAssert fn2(a.toOpenArray(1,3)) == @[2,3,4] - - fn3(a.toOpenArray(1,3)) - when defined(js): discard # xxx bug #15952: `a` left unchanged - else: doAssert a == [1, 0, 10, 20, 5] - - block: # bug #12521 - block: - type slice[T] = openArray[T] - - # Proc using that alias - proc testing(sl: slice[int]): seq[int] = - for item in sl: - result.add item - - let mySeq = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - doAssert testing(mySeq) == mySeq - doAssert testing(mySeq[2..^2]) == mySeq[2..^2] - - block: - type slice = openArray[int] - - # Proc using that alias - proc testing(sl: slice): seq[int] = - for item in sl: - result.add item - - let mySeq = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - doAssert testing(mySeq) == mySeq - doAssert testing(mySeq[2..^2]) == mySeq[2..^2] - - -main() -static: main() diff --git a/tests/openarray/topenarrayrepr.nim b/tests/openarray/topenarrayrepr.nim index fc40d88c3..3784d4bbb 100644 --- a/tests/openarray/topenarrayrepr.nim +++ b/tests/openarray/topenarrayrepr.nim @@ -2,12 +2,12 @@ discard """ output: "5 - [1]" """ type - TProc = proc (n: int, m: openArray[int64]) {.nimcall.} + TProc = proc (n: int, m: openarray[int64]) {.nimcall.} proc Foo(x: int, P: TProc) = P(x, [ 1'i64 ]) -proc Bar(n: int, m: openArray[int64]) = +proc Bar(n: int, m: openarray[int64]) = echo($n & " - " & repr(m)) Foo(5, Bar) #OUT 5 - [1] diff --git a/tests/openarray/topenlen.nim b/tests/openarray/topenlen.nim index 164241b85..83d58ac5c 100644 --- a/tests/openarray/topenlen.nim +++ b/tests/openarray/topenlen.nim @@ -5,7 +5,7 @@ discard """ proc choose(b: openArray[string]): string = return b[0] -proc p(a, b: openArray[string]): int = +proc p(a, b: openarray[string]): int = result = a.len + b.len - 1 for j in 0 .. a.len: inc(result) discard choose(a) diff --git a/tests/openarray/tptrarrayderef.nim b/tests/openarray/tptrarrayderef.nim index 1b7ef0df0..5e77430d1 100644 --- a/tests/openarray/tptrarrayderef.nim +++ b/tests/openarray/tptrarrayderef.nim @@ -15,14 +15,14 @@ var raa = [11,12,13] #bug #3586 -proc mutate[T](arr:openArray[T], brr: openArray[T]) = +proc mutate[T](arr:openarray[T], brr: openArray[T]) = for i in 0..arr.len-1: doAssert(arr[i] == brr[i]) mutate(arr, arr) #bug #2240 -proc f(a: openArray[int], b: openArray[int]) = +proc f(a: openarray[int], b: openArray[int]) = for i in 0..a.len-1: doAssert(a[i] == b[i]) @@ -37,7 +37,7 @@ ra[2] = 13 f(ra[], raa) #bug #2240b -proc fillBuffer(buf: var openArray[char]) = +proc fillBuffer(buf: var openarray[char]) = for i in 0..buf.len-1: buf[i] = chr(i) diff --git a/tests/osproc/treadlines.nim b/tests/osproc/treadlines.nim index bb6a7f129..3a8303321 100644 --- a/tests/osproc/treadlines.nim +++ b/tests/osproc/treadlines.nim @@ -1,19 +1,16 @@ discard """ - output: ''' -Error: cannot open 'a.nim' + output: '''Error: cannot open 'a.nim' Error: cannot open 'b.nim' ''' targets: "c" """ import osproc -from std/os import getCurrentCompilerExe var ps: seq[Process] # compile & run 2 progs in parallel -const nim = getCurrentCompilerExe() for prog in ["a", "b"]: - ps.add startProcess(nim, "", - ["r", "--hint:Conf:off", "--hint:Processing:off", prog], + ps.add startProcess("nim", "", + ["r", "--hint[Conf]=off", "--hint[Processing]=off", prog], options = {poUsePath, poDaemon, poStdErrToStdOut}) for p in ps: diff --git a/tests/overflow/toverflow.nim b/tests/overflow/toverflow.nim deleted file mode 100644 index 972f929c6..000000000 --- a/tests/overflow/toverflow.nim +++ /dev/null @@ -1,82 +0,0 @@ -discard """ - output: "ok" - matrix: "--overflowChecks:off; --overflowChecks:off --b:js" -""" -# Tests nim's ability to detect overflows - -{.push overflowChecks: on.} - -var - a = high(int) - b = -2 - overflowDetected = false - -try: - echo(b - a) -except OverflowDefect: - overflowDetected = true - -{.pop.} # overflow check - -doAssert(overflowDetected) - -block: # Overflow checks in a proc - var - a = high(int) - b = -2 - overflowDetected = false - - {.push overflowChecks: on.} - proc foo() = - let c = b - a - {.pop.} - - try: - foo() - except OverflowDefect: - overflowDetected = true - - doAssert(overflowDetected) - -block: # Overflow checks in a forward declared proc - var - a = high(int) - b = -2 - overflowDetected = false - - proc foo() - - {.push overflowChecks: on.} - proc foo() = - let c = b - a - {.pop.} - - try: - foo() - except OverflowDefect: - overflowDetected = true - - doAssert(overflowDetected) - -block: # Overflow checks doesn't affect fwd declaration - var - a = high(int) - b = -2 - overflowDetected = false - - {.push overflowChecks: on.} - proc foo() - {.pop.} - - proc foo() = - let c = b - a - - try: - foo() - except OverflowDefect: - overflowDetected = true - - doAssert(not overflowDetected) - - -echo "ok" diff --git a/tests/overflow/tdistinct_range.nim b/tests/overflw/tdistinct_range.nim similarity index 100% rename from tests/overflow/tdistinct_range.nim rename to tests/overflw/tdistinct_range.nim diff --git a/tests/overflow/toverflow_reorder.nim b/tests/overflw/toverflw.nim similarity index 96% rename from tests/overflow/toverflow_reorder.nim rename to tests/overflw/toverflw.nim index fcf7b0c82..164e16e5c 100644 --- a/tests/overflow/toverflow_reorder.nim +++ b/tests/overflw/toverflw.nim @@ -1,5 +1,3 @@ -{.experimental: "codeReordering".} - discard """ output: "ok" cmd: "nim $target --overflowChecks:off $options $file" @@ -14,7 +12,7 @@ var overflowDetected = false try: - echo b - a + writeLine(stdout, b - a) except OverflowDefect: overflowDetected = true diff --git a/tests/overflow/toverflow2.nim b/tests/overflw/toverflw2.nim similarity index 100% rename from tests/overflow/toverflow2.nim rename to tests/overflw/toverflw2.nim diff --git a/tests/overflow/tovfint.nim b/tests/overflw/tovfint.nim similarity index 100% rename from tests/overflow/tovfint.nim rename to tests/overflw/tovfint.nim diff --git a/tests/overload/t8829.nim b/tests/overload/t8829.nim index fe0dbf2bf..a688e18cb 100644 --- a/tests/overload/t8829.nim +++ b/tests/overload/t8829.nim @@ -4,7 +4,7 @@ block: template `[]`[T](p: ptr T, span: Slice[int]): untyped = toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b) - doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == + doAssert $cast[ptr uint8](txt[0].unsafeAddr)[0 ..< txt.len] == "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]" @@ -14,5 +14,5 @@ block: template `[]`[T](p: ptr T, span: Slice[int]): untyped = toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b) - doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == + doAssert $cast[ptr uint8](txt[0].unsafeAddr)[0 ..< txt.len] == "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]" diff --git a/tests/overload/toverl4.nim b/tests/overload/toverl4.nim index 21cedaa96..537925674 100644 --- a/tests/overload/toverl4.nim +++ b/tests/overload/toverl4.nim @@ -1,6 +1,5 @@ discard """ - output: '''true -5.0''' + output: '''true''' """ #bug #592 @@ -76,26 +75,3 @@ proc add*[TKey, TData](root: var PElement[TKey, TData], key: TKey, data: TData) var tree = PElement[int, int](kind: ElementKind.inner, key: 0, left: nil, right: nil) let result = add(tree, 1, 1) echo(result) - -# bug #3748 -type - Foo = object - bar: int - -proc bar(cur: Foo, val: int, s:seq[string]) = - discard cur.bar - -proc does_fail(): Foo = - let a = @["a"] - result.bar(5, a) - -doAssert does_fail().bar == 0 - -# bug #20645 - -type Zzz[Gen] = object - -proc testZ(z: Zzz) = - echo z.Gen(5) - -testZ(Zzz[float]()) diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim index 0741fce60..f913ce3ee 100644 --- a/tests/overload/toverload_various.nim +++ b/tests/overload/toverload_various.nim @@ -264,8 +264,8 @@ proc init*[T](hmctx: HMAC[T], key: ptr byte, ulen: uint) = const sizeBlock = hmctx.sizeBlock echo sizeBlock -proc hmac*[A, B](HashType: typedesc, key: openArray[A], - data: openArray[B]) = +proc hmac*[A, B](HashType: typedesc, key: openarray[A], + data: openarray[B]) = var ctx: HMAC[HashType] ctx.init(nil, 0) @@ -395,7 +395,7 @@ block: template bar2[F,T](x: FooUn[F,T]): int = 1 template bar2[F,T1,T2](x: FooBi[F,T1,T2]): int = 2 - proc test(x: auto, n: int) = + proc test(x: any, n: int) = doAssert(foo1(x) == n) doAssert(foo2(x) == n) doAssert(bar1(x) == n) diff --git a/tests/overload/tproc_types_dont_like_subtypes.nim b/tests/overload/tproc_types_dont_like_subtypes.nim deleted file mode 100644 index 6774be156..000000000 --- a/tests/overload/tproc_types_dont_like_subtypes.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - errormsg: "got " - line: 20 -""" - -type - A = ref object of RootObj - B = ref object of A - - P = proc (a: A) - -# bug #16325 - -proc doThings(a: A, p: P) = - p(a) - -var x = proc (b: B) {.closure.} = - echo "B" - -doThings(B(), x) diff --git a/tests/overload/tselfderef.nim b/tests/overload/tselfderef.nim new file mode 100644 index 000000000..96f1da42a --- /dev/null +++ b/tests/overload/tselfderef.nim @@ -0,0 +1,20 @@ +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/overload/tstatic_with_converter.nim b/tests/overload/tstatic_with_converter.nim index e830e8a22..8209e8399 100644 --- a/tests/overload/tstatic_with_converter.nim +++ b/tests/overload/tstatic_with_converter.nim @@ -42,7 +42,7 @@ proc `^`(x: vfloat, exp: static[float]): vfloat = pow(x, exp) proc `$`(x: vfloat): string = - let y = cast[ptr float](addr x) + let y = cast[ptr float](unsafeAddr x) # xxx not sure if intentional in this issue, but this returns "" echo y[] diff --git a/tests/package/stdlib/stdlib.nimble b/tests/package/stdlib/stdlib.nimble deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/package/stdlib/system.nim b/tests/package/stdlib/system.nim deleted file mode 100644 index 475f8ec5b..000000000 --- a/tests/package/stdlib/system.nim +++ /dev/null @@ -1,2 +0,0 @@ -# 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 deleted file mode 100644 index e8226a82d..000000000 --- a/tests/package/tstdlib_name_not_special.nim +++ /dev/null @@ -1,3 +0,0 @@ -# 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/t10913.nim b/tests/parallel/t10913.nim deleted file mode 100644 index d8459ecd0..000000000 --- a/tests/parallel/t10913.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - matrix: "--threads:on" - errormsg: "'spawn'ed function cannot have a 'typed' or 'untyped' parameter" -""" - -# bug #10913 - -import threadpool - -proc useParallel*[T](unused: T) = - # use a generic T here to show the problem. - {.push experimental: "parallel".} - parallel: - for i in 0..4: - spawn echo "echo in parallel" - sync() - - {.pop.} - -useParallel(1) diff --git a/tests/parallel/t7535.nim b/tests/parallel/t7535.nim deleted file mode 100644 index 052dcdc3a..000000000 --- a/tests/parallel/t7535.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - matrix: "--threads:on" - errormsg: "'spawn' takes a call expression; got: proc (x: uint32) = echo [x]" -""" - -import threadpool - -# bug #7535 -proc print_parallel_nok(r: uint32) = - for x in 0..r: - spawn (proc (x: uint32) = echo x) diff --git a/tests/parallel/t9691.nim b/tests/parallel/t9691.nim deleted file mode 100644 index bbf2b1bc7..000000000 --- a/tests/parallel/t9691.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - matrix: "--threads:on" - errormsg: "'spawn'ed function cannot have a 'typed' or 'untyped' parameter" -""" - -# bug #9691 - -import threadpool -spawn echo() diff --git a/tests/parallel/tconvexhull.nim b/tests/parallel/tconvexhull.nim index a89aa910b..ebadb874d 100644 --- a/tests/parallel/tconvexhull.nim +++ b/tests/parallel/tconvexhull.nim @@ -1,7 +1,8 @@ discard """ - matrix: "--mm:refc" output: ''' ''' + +ccodeCheck: "\\i ! @'deepCopy(' .*" """ # parallel convex hull for Nim bigbreak diff --git a/tests/parallel/tdeepcopy.nim b/tests/parallel/tdeepcopy.nim index 96ca15ca3..499ea94d4 100644 --- a/tests/parallel/tdeepcopy.nim +++ b/tests/parallel/tdeepcopy.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" output: ''' 13 abc called deepCopy for int diff --git a/tests/parallel/tdeepcopy2.nim b/tests/parallel/tdeepcopy2.nim index e8305173d..a9caab604 100644 --- a/tests/parallel/tdeepcopy2.nim +++ b/tests/parallel/tdeepcopy2.nim @@ -1,5 +1,4 @@ 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 6892e7383..edcc30ece 100644 --- a/tests/parallel/tdisjoint_slice1.nim +++ b/tests/parallel/tdisjoint_slice1.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" outputsub: "EVEN 28" """ diff --git a/tests/parallel/tflowvar.nim b/tests/parallel/tflowvar.nim index e44b29a87..9d93bc7c8 100644 --- a/tests/parallel/tflowvar.nim +++ b/tests/parallel/tflowvar.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" output: '''foobarfoobar bazbearbazbear diff --git a/tests/parallel/tgc_unsafe2.nim b/tests/parallel/tgc_unsafe2.nim index 7d98dafcb..40af728fb 100644 --- a/tests/parallel/tgc_unsafe2.nim +++ b/tests/parallel/tgc_unsafe2.nim @@ -1,8 +1,9 @@ discard """ errormsg: "'consumer' is not GC-safe as it calls 'track'" - nimout: '''tgc_unsafe2.nim(21, 6) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory [GcUnsafe2] -tgc_unsafe2.nim(25, 6) Warning: 'track' is not GC-safe as it calls 'trick' [GcUnsafe2] -tgc_unsafe2.nim(27, 6) Error: 'consumer' is not GC-safe as it calls 'track' + line: 28 + nimout: '''tgc_unsafe2.nim(22, 6) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory [GcUnsafe2] +tgc_unsafe2.nim(26, 6) Warning: 'track' is not GC-safe as it calls 'trick' [GcUnsafe2] +tgc_unsafe2.nim(28, 6) Error: 'consumer' is not GC-safe as it calls 'track' ''' """ diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim index f4c92319b..b1eb7e7c5 100644 --- a/tests/parallel/tguard1.nim +++ b/tests/parallel/tguard1.nim @@ -5,7 +5,7 @@ output: "90" when false: template lock(a, b: ptr Lock; body: stmt) = - if cast[int](a) < cast[int](b): + if cast[ByteAddress](a) < cast[ByteAddress](b): pthread_mutex_lock(a) pthread_mutex_lock(b) else: diff --git a/tests/parallel/tinvalid_array_bounds.nim b/tests/parallel/tinvalid_array_bounds.nim index 8dc93c33f..15bf526df 100644 --- a/tests/parallel/tinvalid_array_bounds.nim +++ b/tests/parallel/tinvalid_array_bounds.nim @@ -1,7 +1,6 @@ discard """ - matrix: "--mm:refc" errormsg: "cannot prove (i)..(i) disjoint from (i + 1)..(i + 1)" - line: 21 + line: 20 """ import threadpool diff --git a/tests/parallel/tlet_spawn.nim b/tests/parallel/tlet_spawn.nim index 853ffc443..62341d8f0 100644 --- a/tests/parallel/tlet_spawn.nim +++ b/tests/parallel/tlet_spawn.nim @@ -4,7 +4,7 @@ done999 999 ''' """ -import std/[threadpool, os] +import threadpool proc foo(): int = 999 @@ -17,12 +17,3 @@ proc main = echo "done", f, " ", b main() - -# bug #13781 -proc thread(): string = - os.sleep(1000) - return "ok" - -var fv = spawn thread() -sync() -doAssert ^fv == "ok" diff --git a/tests/parallel/tmissing_deepcopy.nim b/tests/parallel/tmissing_deepcopy.nim index ea77936ad..7803439fa 100644 --- a/tests/parallel/tmissing_deepcopy.nim +++ b/tests/parallel/tmissing_deepcopy.nim @@ -1,5 +1,4 @@ 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 51762187d..72d008bbd 100644 --- a/tests/parallel/tnon_disjoint_slice1.nim +++ b/tests/parallel/tnon_disjoint_slice1.nim @@ -1,7 +1,6 @@ discard """ - matrix: "--mm:refc" errormsg: "cannot prove (i)..(i) disjoint from (i + 1)..(i + 1)" - line: 21 + line: 20 """ import threadpool diff --git a/tests/parallel/tparfind.nim b/tests/parallel/tparfind.nim index cf1bc9336..4b3610c67 100644 --- a/tests/parallel/tparfind.nim +++ b/tests/parallel/tparfind.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" output: "500" """ diff --git a/tests/parallel/tpi.nim b/tests/parallel/tpi.nim index cd965d585..1abed6f23 100644 --- a/tests/parallel/tpi.nim +++ b/tests/parallel/tpi.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" output: '''3.141792613595791 3.141792613595791''' """ diff --git a/tests/parallel/tsendtwice.nim b/tests/parallel/tsendtwice.nim index 6bf5e5ebb..03b7fda47 100644 --- a/tests/parallel/tsendtwice.nim +++ b/tests/parallel/tsendtwice.nim @@ -7,7 +7,7 @@ ob2 @[] ob @[] ob3 @[] ''' - matrix: "--mm:refc" + cmd: "nim c -r --threads:on $file" """ # bug #4776 diff --git a/tests/parallel/tsimple_array_checks.nim b/tests/parallel/tsimple_array_checks.nim index ab292f935..650b809e0 100644 --- a/tests/parallel/tsimple_array_checks.nim +++ b/tests/parallel/tsimple_array_checks.nim @@ -61,15 +61,3 @@ maino() # Doesn't work outside a proc when true: main() - -block two: - proc f(a: openArray[int]) = - discard - - proc main() = - var a: array[0..9, int] = [0,1,2,3,4,5,6,7,8,9] - parallel: - spawn f(a[0..2]) - - - main() \ No newline at end of file diff --git a/tests/parallel/tsysspawn.nim b/tests/parallel/tsysspawn.nim index b7ecd1264..09a77b358 100644 --- a/tests/parallel/tsysspawn.nim +++ b/tests/parallel/tsysspawn.nim @@ -5,7 +5,7 @@ discard """ 2 2 ''' - matrix: "--mm:refc" + cmd: "nim $target --threads:on $options $file" """ import threadpool diff --git a/tests/parallel/tuseafterdef.nim b/tests/parallel/tuseafterdef.nim index e73f1b794..f1ae6e923 100644 --- a/tests/parallel/tuseafterdef.nim +++ b/tests/parallel/tuseafterdef.nim @@ -1,7 +1,6 @@ discard """ - matrix: "--mm:refc" errormsg: "(k)..(k) not disjoint from (k)..(k)" - line: 24 + line: 23 action: compile """ diff --git a/tests/parallel/twaitany.nim b/tests/parallel/twaitany.nim index d57c5f40f..b58cadd86 100644 --- a/tests/parallel/twaitany.nim +++ b/tests/parallel/twaitany.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" output: '''true''' """ diff --git a/tests/parser/t12274.nim b/tests/parser/t12274.nim deleted file mode 100644 index 6b7c9f55a..000000000 --- a/tests/parser/t12274.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - joinable: false -""" - -var s: seq[int] -s.add block: - let i = 1 - i -s.add try: - 2 -except: - 3 -doAssert s == @[1, 2] diff --git a/tests/parser/t15667.nim b/tests/parser/t15667.nim deleted file mode 100644 index fcb862825..000000000 --- a/tests/parser/t15667.nim +++ /dev/null @@ -1,61 +0,0 @@ -discard """ - cmd: "nim check $options $file" - action: "reject" - nimout: ''' -t15667.nim(23, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(22, 13) ? -t15667.nim(28, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(26, 13) ? -t15667.nim(33, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(31, 25) ? -t15667.nim(42, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(38, 12) ? -t15667.nim(56, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(55, 13) ? -t15667.nim(61, 48) Error: expression expected, but found ',' -''' -""" - - - - - - - -# line 20 -block: - proc fn1() - discard - -block: - proc fn2() - # - discard - -block: - proc fn3() {.exportc.} - # - discard - -block: # complex example - proc asdfasdfsd() {. exportc, - inline - .} # foo - #[ - bar - ]# - discard - -block: # xxx this doesn't work yet (only a bare `invalid indentation` error) - proc fn5() - ## - discard - -block: # ditto - proc fn6*() - ## foo bar - runnableExamples: discard - -block: - proc fn8() - runnableExamples: - discard - discard - -# semiStmtList loop issue -proc bar(k:static bool):SomeNumber = (when k: 3, else: 3.0) diff --git a/tests/parser/t19662.nim b/tests/parser/t19662.nim deleted file mode 100644 index 7a1864ac6..000000000 --- a/tests/parser/t19662.nim +++ /dev/null @@ -1,6 +0,0 @@ - var i: int # bug #19662 -echo i - -discard """ - errormsg: "invalid indentation" -""" \ No newline at end of file diff --git a/tests/parser/t20922.nim b/tests/parser/t20922.nim deleted file mode 100644 index 110610fb1..000000000 --- a/tests/parser/t20922.nim +++ /dev/null @@ -1,35 +0,0 @@ -discard """ - cmd: "nim check $options --verbosity:0 --hints:off $file" - action: "reject" - nimout: ''' -t20922.nim(26, 5) Error: expression expected, but found ':' -t20922.nim(34, 7) Error: expression expected, but found ':' -t20922.nim(35, 5) Error: ':' or '=' expected, but got 'keyword of' -Error: in expression ' '+'': identifier expected, but found '' -t20922.nim(26, 7) Error: attempting to call undeclared routine: '' -Error: in expression ' '+'': identifier expected, but found '' -t20922.nim(26, 7) Error: attempting to call undeclared routine: '' -t20922.nim(26, 7) Error: expression '' cannot be called -t20922.nim(26, 7) Error: expression '' has no type (or is ambiguous) -t20922.nim(26, 7) Error: VM problem: dest register is not set -''' -""" -# original test case issue #20922 -type Token = enum - incDataPtr, - incDataPtrByte - -proc mapInstrToToken(instr: char): Token = - case instr: - of '>': - incDataPtr - of: '+': - incDataPtrByte - -# same issue with `of` in object branches (different parser procs calling `exprList`) -type - Bar = enum A, B - Foo = object - case kind: Bar - of: x: int - of B: y: float diff --git a/tests/parser/tcommand_as_expr.nim b/tests/parser/tcommand_as_expr.nim index f37c34f63..b25ec4bd8 100644 --- a/tests/parser/tcommand_as_expr.nim +++ b/tests/parser/tcommand_as_expr.nim @@ -36,12 +36,3 @@ echo f -4 echo int -1 # doesn't compile echo int `-` 1 # compiles - -var num = 1 -num += int 2 -doAssert num == 3 - -import options -var opt = some some none int -opt = some some none int -opt = some none Option[int] diff --git a/tests/parser/tcommandequals.nim b/tests/parser/tcommandequals.nim deleted file mode 100644 index f41b318ac..000000000 --- a/tests/parser/tcommandequals.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - output: ''' -5 -''' -""" - -proc foo(a, b: int) = - echo a + b - -foo a = 2, b = 3 - -import macros - -macro bar(args: varargs[untyped]): untyped = - doAssert args[0].kind == nnkExprEqExpr - -bar "a" = 1 diff --git a/tests/parser/tcommandindent.nim b/tests/parser/tcommandindent.nim deleted file mode 100644 index 449c218db..000000000 --- a/tests/parser/tcommandindent.nim +++ /dev/null @@ -1,16 +0,0 @@ -when false: # parse the following - let foo = Obj( - field1: proc (src: pointer, srcLen: Natural) - {.nimcall, gcsafe, raises: [IOError, Defect].} = - var file = FileOutputStream(s).file - - implementWrites s.buffers, src, srcLen, "FILE", - writeStartAddr, writeLen, - file.writeBuffer(writeStartAddr, writeLen) - , - field2: proc {.nimcall, gcsafe, raises: [IOError, Defect].} = - flushFile FileOutputStream(s).file - , - field3: proc () {.nimcall, gcsafe, raises: [IOError, Defect].} = - close FileOutputStream(s).file - ) diff --git a/tests/parser/tdo.nim b/tests/parser/tdo.nim index 382d03398..7bd1f7411 100644 --- a/tests/parser/tdo.nim +++ b/tests/parser/tdo.nim @@ -1,12 +1,8 @@ discard """ - output: ''' + output: '''true true true -true -true inner B -running with pragma -ran with pragma -''' +true inner B''' """ template withValue(a, b, c, d, e: untyped) = @@ -81,14 +77,3 @@ proc main2 = echo "true inner B" main2() - -proc withPragma(foo: int, bar: proc() {.raises: [].}) = - echo "running with pragma" - bar() - -withPragma(3) do {.raises: [].}: - echo "ran with pragma" - -doAssert not (compiles do: - withPragma(3) do {.raises: [].}: - raise newException(Exception)) diff --git a/tests/parser/tdoc_comments.nim b/tests/parser/tdoc_comments.nim index 3c86e69ea..fa1374b45 100644 --- a/tests/parser/tdoc_comments.nim +++ b/tests/parser/tdoc_comments.nim @@ -69,7 +69,3 @@ type MyEnum3* = enum value5 ## only document the enum value - -# bug #18847 -proc close*() = ## asdfasdfsdfa - discard ## adsfasdfads diff --git a/tests/parser/tdoublenotnil.nim b/tests/parser/tdoublenotnil.nim deleted file mode 100644 index c61008c54..000000000 --- a/tests/parser/tdoublenotnil.nim +++ /dev/null @@ -1,3 +0,0 @@ -when false: - type Foo = Bar not nil not nil #[tt.Error - ^ invalid indentation]# diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim index 6cd4a8350..c27bbf321 100644 --- a/tests/parser/tpostexprblocks.nim +++ b/tests/parser/tpostexprblocks.nim @@ -406,105 +406,6 @@ StmtList DiscardStmt Empty IntLit 0 - Command - Ident "foo390" - Call - Ident "x" - Do - Empty - Empty - Empty - FormalParams - Empty - IdentDefs - Ident "y" - Empty - Empty - Empty - Empty - StmtList - DiscardStmt - Empty - Do - Empty - Empty - Empty - FormalParams - Ident "int" - IdentDefs - Ident "z" - Empty - Empty - Empty - Empty - StmtList - DiscardStmt - Empty - Do - Empty - Empty - Empty - FormalParams - Ident "int" - IdentDefs - Ident "w" - Ident "int" - Empty - Empty - Empty - StmtList - DiscardStmt - Empty - StmtList - DiscardStmt - Empty - OfBranch - Ident "a" - StmtList - DiscardStmt - Empty - OfBranch - TupleConstr - Ident "a" - Ident "b" - StmtList - DiscardStmt - Empty - ElifBranch - Ident "a" - StmtList - DiscardStmt - Empty - ElifBranch - TupleConstr - Ident "a" - Ident "b" - StmtList - DiscardStmt - Empty - ExceptBranch - Ident "a" - StmtList - DiscardStmt - Empty - ExceptBranch - TupleConstr - Ident "a" - Ident "b" - StmtList - DiscardStmt - Empty - Finally - StmtList - DiscardStmt - Empty - - Call - Ident "foo" - Finally - StmtList - DiscardStmt - Empty ''' """ @@ -639,30 +540,3 @@ dumpTree: foo380.add((quote do: discard )[0]) - - foo390 x do (y): - discard - do (z) -> int: - discard - do (w: int) -> int: - discard - do: - discard - of a: - discard - of (a, b): - discard - elif a: - discard - elif (a, b): - discard - except a: - discard - except (a, b): - discard - finally: - discard - - foo: - finally: - discard diff --git a/tests/parser/tprecedence.nim b/tests/parser/tprecedence.nim index 9be79543b..66a2922db 100644 --- a/tests/parser/tprecedence.nim +++ b/tests/parser/tprecedence.nim @@ -54,10 +54,3 @@ 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/tprocexprasstmt.nim b/tests/parser/tprocexprasstmt.nim deleted file mode 100644 index a02dde6f6..000000000 --- a/tests/parser/tprocexprasstmt.nim +++ /dev/null @@ -1,3 +0,0 @@ -func r(): auto = - func(): int = 2 -discard r()() diff --git a/tests/parser/tstmtlists.nim b/tests/parser/tstmtlists.nim index 158c93340..b6c7e4a94 100644 --- a/tests/parser/tstmtlists.nim +++ b/tests/parser/tstmtlists.nim @@ -50,8 +50,6 @@ hello 9 hello 10 -lucky -lucky ''' """ @@ -158,23 +156,10 @@ template dim2: int = else: int.high) -template dim3: int = +template dim: int = ( if int.high == 0: int.high else: int.high) -# lenient indentation: - -echo (if 0 == 1: - "0 == 1" -else: - "lucky") - -# bug #16426 -echo (when 0 == 1: - "0 == 1" -else: - "lucky") - diff --git a/tests/parser/ttry.nim b/tests/parser/ttry.nim deleted file mode 100644 index 190b0b8dc..000000000 --- a/tests/parser/ttry.nim +++ /dev/null @@ -1,27 +0,0 @@ -# bug #21144 -block: - try: - let c = try: - 10 - except ValueError as exc: - 10 - except ValueError as exc: - discard - -if true: - block: - let c = try: - 10 - except ValueError as exc: - 10 - except OSError: - 99 - - -try: - let c = try: - 10 - except ValueError as exc: - 10 -except ValueError as exc: - discard \ No newline at end of file diff --git a/tests/parser/ttypecommandcomma.nim b/tests/parser/ttypecommandcomma.nim deleted file mode 100644 index 7ca59a799..000000000 --- a/tests/parser/ttypecommandcomma.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - errormsg: "invalid indentation" - line: 8 - column: 19 -""" - -type - Foo = call(1, 2), 3: - 4 \ No newline at end of file diff --git a/tests/parser/ttypecommandindent1.nim b/tests/parser/ttypecommandindent1.nim deleted file mode 100644 index 9aa274e2a..000000000 --- a/tests/parser/ttypecommandindent1.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - errormsg: "invalid indentation" - line: 9 - column: 3 -""" - -type - Foo = call x, y, z: - abc diff --git a/tests/parser/ttypecommandindent2.nim b/tests/parser/ttypecommandindent2.nim deleted file mode 100644 index 0883df90f..000000000 --- a/tests/parser/ttypecommandindent2.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - errormsg: "invalid indentation" - line: 10 - column: 5 -""" - -type - Foo = call x, y, z: - abc - do: - def diff --git a/tests/parser/ttypecommandindent3.nim b/tests/parser/ttypecommandindent3.nim deleted file mode 100644 index f9fdcc1e4..000000000 --- a/tests/parser/ttypecommandindent3.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - errormsg: "expression expected, but found 'keyword do'" - line: 10 - column: 1 -""" - -type - Foo = call x, y, z: - abc -do: - def diff --git a/tests/parser/ttypeexprobject.nim b/tests/parser/ttypeexprobject.nim deleted file mode 100644 index 6895f1731..000000000 --- a/tests/parser/ttypeexprobject.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - errormsg: "invalid indentation" - line: 10 - column: 14 -""" - -type - A = (object | tuple | int) - B = int | object | tuple - C = object | tuple | int # issue #8846 diff --git a/tests/parser/ttypeexprs.nim b/tests/parser/ttypeexprs.nim deleted file mode 100644 index e40efc7d9..000000000 --- a/tests/parser/ttypeexprs.nim +++ /dev/null @@ -1,25 +0,0 @@ -proc foo[T: ptr int | ptr string](x: T) = discard -var x = "abc" -foo(addr x) - -let n = 3'u32 -type Double = ( - when n.sizeof == 4: uint64 - elif n.sizeof == 2: uint32 - else: uint16 -) - -type - A = (ref | ptr | pointer) - B = pointer | ptr | ref - C = ref | ptr | pointer - -template `+`(a, b): untyped = (b, a) -template `*`(a, b): untyped = (a, b) - -doAssert (ref int + ref float * ref string + ref bool) is - (ref bool, ((ref float, ref string), ref int)) -type X = ref int + ref float * ref string + ref bool -doAssert X is (ref bool, ((ref float, ref string), ref int)) - -type SomePointer = proc | ref | ptr | pointer diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim index 9a1ccb1a5..2c322b44b 100644 --- a/tests/parser/ttypemodifiers.nim +++ b/tests/parser/ttypemodifiers.nim @@ -21,7 +21,7 @@ StmtList Ident "PtrTuple" Empty PtrTy - TupleConstr + Par Ident "int" Ident "string" TypeDef @@ -43,14 +43,14 @@ StmtList Ident "RefTupleType" Empty RefTy - TupleConstr + Par Ident "int" Ident "string" TypeDef Ident "RefTupleVars" Empty RefTy - TupleConstr + Par Ident "a" Ident "b" TypeDef @@ -80,7 +80,7 @@ StmtList Empty Command Ident "static" - TupleConstr + Par Ident "int" Ident "string" TypeDef @@ -155,7 +155,7 @@ StmtList Empty Command Ident "type" - TupleConstr + Par Ident "a" Ident "b" TypeDef @@ -163,7 +163,7 @@ StmtList Empty Command Ident "type" - TupleConstr + Par Ident "int" Ident "string" TypeDef @@ -287,7 +287,7 @@ StmtList IdentDefs Ident "refTuple2" RefTy - TupleConstr + Par Ident "int" Ident "string" Empty @@ -524,3 +524,4 @@ dumpTree: static: staticStmtList1 staticStmtList2 + diff --git a/tests/parser/ttypesectioncalls.nim b/tests/parser/ttypesectioncalls.nim deleted file mode 100644 index 003444fc5..000000000 --- a/tests/parser/ttypesectioncalls.nim +++ /dev/null @@ -1,328 +0,0 @@ -discard """ -nimout: ''' -StmtList - TypeSection - TypeDef - Ident "A" - Empty - Call - Ident "call" - IntLit 1 - TypeSection - TypeDef - Ident "B" - Empty - Command - Ident "call" - IntLit 2 - TypeDef - Ident "C" - Empty - Call - Ident "call" - StmtList - IntLit 3 - TypeDef - Ident "D" - Empty - Call - Ident "call" - StmtList - IntLit 4 - TypeSection - TypeDef - Ident "E" - Empty - Call - Ident "call" - IntLit 5 - IntLit 6 - TypeDef - Ident "F" - Empty - Command - Ident "call" - IntLit 7 - IntLit 8 - TypeDef - Ident "G" - Empty - Call - Ident "call" - IntLit 9 - StmtList - IntLit 10 - TypeDef - Ident "H" - Empty - Call - Ident "call" - IntLit 11 - StmtList - IntLit 12 - TypeDef - Ident "I" - Empty - Command - Ident "call" - IntLit 13 - StmtList - IntLit 14 - TypeDef - Ident "J" - Empty - Command - Ident "call" - IntLit 15 - StmtList - IntLit 16 - TypeSection - TypeDef - Ident "K" - Empty - Call - Ident "call" - IntLit 17 - IntLit 18 - IntLit 19 - TypeDef - Ident "L" - Empty - Command - Ident "call" - IntLit 20 - IntLit 21 - IntLit 22 - TypeDef - Ident "M" - Empty - Call - Ident "call" - IntLit 23 - IntLit 24 - StmtList - IntLit 25 - TypeDef - Ident "N" - Empty - Command - Ident "call" - IntLit 26 - IntLit 27 - StmtList - IntLit 28 - TypeDef - Ident "O" - Empty - Command - Ident "call" - IntLit 29 - IntLit 30 - StmtList - IntLit 31 - TypeSection - TypeDef - Ident "P" - Empty - Command - Ident "call" - TupleConstr - IntLit 32 - IntLit 33 - Infix - Ident "+" - Infix - Ident "*" - IntLit 34 - IntLit 35 - IntLit 36 - StmtList - IntLit 37 - TypeDef - Ident "R" - Empty - Command - Ident "call" - Infix - Ident "@" - TupleConstr - IntLit 38 - IntLit 39 - Infix - Ident "shl" - IntLit 40 - IntLit 41 - Infix - Ident "-" - Infix - Ident "*" - IntLit 42 - IntLit 43 - IntLit 44 - StmtList - IntLit 45 - TypeDef - Ident "S" - Empty - Command - Ident "call" - IntLit 46 - StmtList - IntLit 47 - StmtList - IntLit 48 - TypeDef - Ident "T" - Empty - Call - Ident "call" - StmtList - IntLit 49 - StmtList - IntLit 50 - StmtList - IntLit 51 -a: IntLit 1 -a: IntLit 2 -a: StmtList - IntLit 3 -a: StmtList - IntLit 4 -a: IntLit 5 -b: IntLit 6 -a: IntLit 7 -b: IntLit 8 -a: IntLit 9 -b: StmtList - IntLit 10 -a: IntLit 11 -b: StmtList - IntLit 12 -a: IntLit 13 -b: StmtList - IntLit 14 -a: IntLit 15 -b: StmtList - IntLit 16 -a: IntLit 17 -b: IntLit 18 -c: IntLit 19 -a: IntLit 20 -b: IntLit 21 -c: IntLit 22 -a: IntLit 23 -b: IntLit 24 -c: StmtList - IntLit 25 -a: IntLit 26 -b: IntLit 27 -c: StmtList - IntLit 28 -a: IntLit 29 -b: IntLit 30 -c: StmtList - IntLit 31 -a: TupleConstr - IntLit 32 - IntLit 33 -b: Infix - Ident "+" - Infix - Ident "*" - IntLit 34 - IntLit 35 - IntLit 36 -c: StmtList - IntLit 37 -a: Infix - Ident "@" - TupleConstr - IntLit 38 - IntLit 39 - Infix - Ident "shl" - IntLit 40 - IntLit 41 -b: Infix - Ident "-" - Infix - Ident "*" - IntLit 42 - IntLit 43 - IntLit 44 -c: StmtList - IntLit 45 -a: IntLit 46 -b: StmtList - IntLit 47 -c: StmtList - IntLit 48 -a: StmtList - IntLit 49 -b: StmtList - IntLit 50 -c: StmtList - IntLit 51 -''' -""" -import macros - -macro call(a): untyped = - echo "a: ", a.treeRepr - result = ident"int" -macro call(a, b): untyped = - echo "a: ", a.treeRepr - echo "b: ", b.treeRepr - result = ident"int" -macro call(a, b, c): untyped = - echo "a: ", a.treeRepr - echo "b: ", b.treeRepr - echo "c: ", c.treeRepr - result = ident"int" - -macro sections(x): untyped = - echo x.treeRepr - result = newStmtList(x) - for ts in x: - for td in ts: - let t = td[0] - result.add quote do: - doAssert `t` is int - -sections: - type A = call(1) - type - B = call 2 - C = call: 3 - D = call(): 4 - type - E = call(5, 6) - F = call 7, 8 - G = call(9): 10 - H = call(11): - 12 - I = call 13: 14 - J = call 15: - 16 - type - K = call(17, 18, 19) - L = call 20, 21, 22 - M = call(23, 24): 25 - N = call 26, 27: 28 - O = call 29, 30: - 31 - type - P = call (32, 33), 34 * 35 + 36: - 37 - R = call (38, 39) @ 40 shl 41, 42 * 43 - 44: - 45 - S = call 46: - 47 - do: - 48 - T = call: - 49 - do: - 50 - do: - 51 diff --git a/tests/pragmas/mused2a.nim b/tests/pragmas/mused2a.nim deleted file mode 100644 index d9b2bb9bf..000000000 --- a/tests/pragmas/mused2a.nim +++ /dev/null @@ -1,23 +0,0 @@ -import std/strutils - -from std/os import fileExists - -import std/typetraits as typetraits2 -from std/setutils import complement - - - - - -proc fn1() = discard -proc fn2*() = discard - - -let fn4 = 0 -let fn5* = 0 - - -const fn7 = 0 -const fn8* = 0 - -type T1 = object diff --git a/tests/pragmas/mused2b.nim b/tests/pragmas/mused2b.nim deleted file mode 100644 index 39c92b964..000000000 --- a/tests/pragmas/mused2b.nim +++ /dev/null @@ -1,3 +0,0 @@ -import mused2c -export mused2c - diff --git a/tests/pragmas/mused2c.nim b/tests/pragmas/mused2c.nim deleted file mode 100644 index a374e634e..000000000 --- a/tests/pragmas/mused2c.nim +++ /dev/null @@ -1 +0,0 @@ -proc baz*() = discard \ No newline at end of file diff --git a/tests/pragmas/mused3.nim b/tests/pragmas/mused3.nim deleted file mode 100644 index 0beec1d44..000000000 --- a/tests/pragmas/mused3.nim +++ /dev/null @@ -1,76 +0,0 @@ -#[ -ran from trunner -]# - - - - - - -# line 10 -when defined case1: - from mused3a import nil - from mused3b import nil - mused3a.fn1() - -when defined case2: - from mused3a as m1 import nil - m1.fn1() - -when defined case3: - from mused3a import fn1 - fn1() - -when defined case4: - from mused3a as m1 import fn1 - m1.fn1() - -when defined case5: - import mused3a as m1 - fn1() - -when defined case6: - import mused3a except nonexistent - fn1() - -when defined case7: - import mused3a - mused3a.fn1() - -when defined case8: - # re-export test - import mused3a except nonexistent - gn1() - -when defined case9: - # re-export test - import mused3a - gn1() - -when defined case10: - #[ - edge case which happens a lot in compiler code: - don't report UnusedImport for mused3b here even though it works without `import mused3b`, - because `a.b0.f0` is accessible from both mused3a and mused3b (fields are given implicit access) - ]# - import mused3a - import mused3b - var a: Bar - discard a.b0.f0 - -when false: - when defined case11: - #[ - xxx minor bug: this should give: - Warning: imported and not used: 'm2' [UnusedImport] - but doesn't, because currently implementation in `markOwnerModuleAsUsed` - only looks at `fn1`, not fully qualified call `m1.fn1() - ]# - from mused3a as m1 import nil - from mused3a as m2 import nil - m1.fn1() - -when defined case12: - import mused3a - import mused3a - fn1() diff --git a/tests/pragmas/mused3a.nim b/tests/pragmas/mused3a.nim deleted file mode 100644 index c33d1ecb3..000000000 --- a/tests/pragmas/mused3a.nim +++ /dev/null @@ -1,41 +0,0 @@ -when defined case1: - proc fn1*() = discard - -when defined case2: - proc fn1*() = discard - -when defined case3: - proc fn1*() = discard - proc fn2*() = discard - -when defined case4: - proc fn1*() = discard - proc fn2*() = discard - -when defined case5: - proc fn1*() = discard - -when defined case6: - proc fn1*() = discard - -when defined case7: - proc fn1*() = discard - -when defined case8: - import mused3b - export mused3b - -when defined case9: - import mused3b - export mused3b - -when defined case10: - import mused3b - type Bar* = object - b0*: Foo - -when defined case11: - proc fn1*() = discard - -when defined case12: - proc fn1*() = discard diff --git a/tests/pragmas/mused3b.nim b/tests/pragmas/mused3b.nim deleted file mode 100644 index de288bb08..000000000 --- a/tests/pragmas/mused3b.nim +++ /dev/null @@ -1,12 +0,0 @@ -when defined case1: - proc gn1*()=discard - -when defined case8: - proc gn1*()=discard - -when defined case9: - proc gn1*()=discard - -when defined case10: - type Foo* = object - f0*: int diff --git a/tests/pragmas/t12640.nim b/tests/pragmas/t12640.nim index c85185699..60177d034 100644 --- a/tests/pragmas/t12640.nim +++ b/tests/pragmas/t12640.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" nimout: '''1 2 3 @@ -11,7 +10,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/t8741.nim b/tests/pragmas/t8741.nim index 221c732b0..61a449c01 100644 --- a/tests/pragmas/t8741.nim +++ b/tests/pragmas/t8741.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim check --hint:processing:off $file" + cmd: "nim check --hint[processing]:off $file" errormsg: "3 is not two" nimout: '''t8741.nim(13, 9) Error: cannot attach a custom pragma to 'a' t8741.nim(29, 15) template/generic instantiation of `onlyTwo` from here diff --git a/tests/pragmas/tcompile_missing_file.nim b/tests/pragmas/tcompile_missing_file.nim deleted file mode 100644 index fd90bd57d..000000000 --- a/tests/pragmas/tcompile_missing_file.nim +++ /dev/null @@ -1,5 +0,0 @@ -discard """ - joinable: false - errormsg: "cannot find: noexist.c" -""" -{.compile: "noexist.c".} diff --git a/tests/pragmas/tcompile_pragma.nim b/tests/pragmas/tcompile_pragma.nim index 5b99352dd..4e09a7501 100644 --- a/tests/pragmas/tcompile_pragma.nim +++ b/tests/pragmas/tcompile_pragma.nim @@ -1,6 +1,5 @@ discard """ output: '''34''' - joinable: false """ {.compile("cfunction.c", "-DNUMBER_HERE=34").} diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index ad25cad98..e9dac753d 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -17,26 +17,10 @@ block: MyObj = object myField1, myField2 {.myAttr: "hi".}: int - MyGenericObj[T] = object - myField1, myField2 {.myAttr: "hi".}: int - - MyOtherObj = MyObj - - var o: MyObj static: doAssert o.myField2.hasCustomPragma(myAttr) doAssert(not o.myField1.hasCustomPragma(myAttr)) - doAssert(not o.myField1.hasCustomPragma(MyObj)) - doAssert(not o.myField1.hasCustomPragma(MyOtherObj)) - - var ogen: MyGenericObj[int] - static: - doAssert ogen.myField2.hasCustomPragma(myAttr) - doAssert(not ogen.myField1.hasCustomPragma(myAttr)) - doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj)) - doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj)) - import custom_pragma block: # A bit more advanced case @@ -172,23 +156,13 @@ block: proc generic_proc[T]() = doAssert Annotated.hasCustomPragma(simpleAttr) + #-------------------------------------------------------------------------- # Pragma on proc type -type - MyAnnotatedProcType {.defaultValue(4).} = proc(x: int) - -let a {.defaultValue(4).}: proc(x: int) = nil -var b: MyAnnotatedProcType = nil -var c: proc(x: int): void {.defaultValue(5).} = nil -var d {.defaultValue(44).}: MyAnnotatedProcType = nil +let a: proc(x: int) {.defaultValue(5).} = nil static: - doAssert hasCustomPragma(a, defaultValue) - doAssert hasCustomPragma(MyAnnotatedProcType, defaultValue) - doAssert hasCustomPragma(b, defaultValue) - doAssert hasCustomPragma(typeof(c), defaultValue) - doAssert getCustomPragmaVal(d, defaultValue) == 44 - doAssert getCustomPragmaVal(typeof(d), defaultValue) == 4 + doAssert hasCustomPragma(a.type, defaultValue) # bug #8371 template thingy {.pragma.} @@ -281,10 +255,6 @@ block: doAssert input.treeRepr & "\n" == expectedRepr return input - macro expectedAstRepr(expectedRepr: static[string], input: untyped): untyped = - doAssert input.repr == expectedRepr - return input - const procTypeAst = """ ProcTy FormalParams @@ -303,10 +273,20 @@ ProcTy static: doAssert Foo is proc(x: int): Future[void] const asyncProcTypeAst = """ -proc (s: string): Future[void] {..}""" - # using expectedAst would show `OpenSymChoice` for Future[void], which is fragile. +ProcTy + FormalParams + BracketExpr + Ident "Future" + Ident "void" + IdentDefs + Ident "s" + Ident "string" + Empty + Pragma +""" + type - Bar = proc (s: string) {.async, expectedAstRepr(asyncProcTypeAst).} + Bar = proc (s: string) {.async, expectedAst(asyncProcTypeAst).} static: doAssert Bar is proc(x: string): Future[void] @@ -398,109 +378,3 @@ block: b {.world.}: int 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.} - - type TObj = object - a {.myAttr.}: int - - macro hasMyAttr(t: typedesc): untyped = - let objTy = t.getType[1].getType - let recList = objTy[2] - let sym = recList[0] - assert sym.kind == nnkSym and sym.eqIdent("a") - let hasAttr = sym.hasCustomPragma(myAttr) - newLit(hasAttr) - - doAssert hasMyAttr(TObj) - - -# bug #11415 -template noserialize() {.pragma.} - -type - Point[T] = object - x, y: T - - ReplayEventKind = enum - FoodAppeared, FoodEaten, DirectionChanged - - ReplayEvent = object - case kind: ReplayEventKind - of FoodEaten, FoodAppeared: # foodPos is in multiple branches - foodPos {.noserialize.}: Point[float] - of DirectionChanged: - playerPos: float -let ev = ReplayEvent( - kind: FoodEaten, - foodPos: Point[float](x: 5.0, y: 1.0) - ) - -doAssert ev.foodPos.hasCustomPragma(noserialize) - - -when false: - # misc - {.pragma: haha.} - {.pragma: hoho.} - template hehe(key, val: string, haha) {.pragma.} - - type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int - - assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") - - template hehe(key, val: int) {.pragma.} - - var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3 - - # 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/thintprocessing.nim b/tests/pragmas/thintprocessing.nim deleted file mode 100644 index c608bc6e4..000000000 --- a/tests/pragmas/thintprocessing.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - disabled: windows - matrix: "--hint:processing" - nimout: ''' -compile start -.. -warn_module.nim(6, 6) Hint: 'test' is declared but not used [XDeclaredButNotUsed] -compile end -''' -""" - -static: - echo "compile start" - -import warn_module - -static: - echo "compile end" diff --git a/tests/pragmas/tlocks.nim b/tests/pragmas/tlocks.nim index 5d6fcdd9c..ba66a2dca 100644 --- a/tests/pragmas/tlocks.nim +++ b/tests/pragmas/tlocks.nim @@ -1,3 +1,4 @@ + type SomeBase* = ref object of RootObj type SomeDerived* = ref object of SomeBase memberProc*: proc () diff --git a/tests/pragmas/tnoreturn.nim b/tests/pragmas/tnoreturn.nim index 6a58055fe..6d0466df3 100644 --- a/tests/pragmas/tnoreturn.nim +++ b/tests/pragmas/tnoreturn.nim @@ -1,5 +1,4 @@ discard """ -matrix: "--mm:refc" ccodeCheck: "\\i @'__attribute__((noreturn))' .*" action: compile """ diff --git a/tests/pragmas/tpragmas_misc.nim b/tests/pragmas/tpragmas_misc.nim index 6dc2e6b80..247fa471e 100644 --- a/tests/pragmas/tpragmas_misc.nim +++ b/tests/pragmas/tpragmas_misc.nim @@ -10,61 +10,3 @@ block: static: doAssert defined(tpragmas_misc_def) {.undef(tpragmas_misc_def).} static: doAssert not defined(tpragmas_misc_def) - -block: # (partial fix) bug #15920 - block: # var template pragmas don't work in templates - template foo(expr) = - expr - proc fun1()= - let a {.foo.} = 1 - template fun2()= - let a {.foo.} = 1 - fun1() # ok - fun2() # WAS bug - - template foo2() = discard # distractor (template or other symbol kind) - block: - template foo2(expr) = - expr - proc fun1()= - let a {.foo2.} = 1 - template fun2()= - let a {.foo2.} = 1 - fun1() # ok - fun2() # bug: Error: invalid pragma: foo2 - - block: # template pragmas don't work for templates, #18212 - # adapted from $nim/lib/std/private/since.nim - # case without overload - template since3(version: (int, int), body: untyped) {.dirty.} = - when (NimMajor, NimMinor) >= version: - body - when true: # bug - template fun3(): int {.since3: (1, 3).} = 12 - - block: # ditto, w - # case with overload - template since2(version: (int, int), body: untyped) {.dirty.} = - when (NimMajor, NimMinor) >= version: - body - template since2(version: (int, int, int), body: untyped) {.dirty.} = - when (NimMajor, NimMinor, NimPatch) >= version: - body - when true: # bug - template fun3(): int {.since2: (1, 3).} = 12 - -when true: # D20210801T100514:here - from macros import genSym - block: - template fn() = - var ret {.gensym.}: int # must special case template pragmas so it doesn't get confused - discard ret - fn() - static: discard genSym() - -block: # issue #10994 - macro foo(x): untyped = x - template bar {.pragma.} - - proc a {.bar.} = discard # works - proc b {.bar, foo.} = discard # doesn't diff --git a/tests/pragmas/tpragmas_reorder.nim b/tests/pragmas/tpragmas_reorder.nim deleted file mode 100644 index c4b1a6b0a..000000000 --- a/tests/pragmas/tpragmas_reorder.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - matrix: "--experimental:codeReordering" -""" - -runnableExamples: - import strtabs - var t = newStringTable() - t["name"] = "John" - t["city"] = "Monaco" - doAssert t.len == 2 - doAssert t.hasKey "name" - doAssert "name" in t - -include "system/inclrtl" - -{.pragma: rtlFunc, rtl.} - -proc hasKey*(): bool {.rtlFunc.} = - discard \ No newline at end of file diff --git a/tests/pragmas/tpush.nim b/tests/pragmas/tpush.nim index f2779ea70..5ecfe9704 100644 --- a/tests/pragmas/tpush.nim +++ b/tests/pragmas/tpush.nim @@ -13,15 +13,3 @@ proc WarnMe() = x: int echo(x) -# bug #11852 -proc foo(x: string, y: int, res: int) = - {.push checks: off} - var a: ptr char = unsafeAddr(x[y]) - {.pop.} - if x.len > y: - doAssert ord(a[]) == 51 - else: - doAssert x.len + 48 == res - -foo("", 0, 48) -foo("abc", 40, 51) diff --git a/tests/pragmas/treorder.nim b/tests/pragmas/treorder.nim index 72e8808b3..09a98ef6a 100644 --- a/tests/pragmas/treorder.nim +++ b/tests/pragmas/treorder.nim @@ -1,5 +1,4 @@ discard """ -matrix: "--experimental:codeReordering" output:'''0 1 2 diff --git a/tests/pragmas/ttypedef_macro.nim b/tests/pragmas/ttypedef_macro.nim deleted file mode 100644 index dd4c87757..000000000 --- a/tests/pragmas/ttypedef_macro.nim +++ /dev/null @@ -1,66 +0,0 @@ -import macros - -macro makeref(s): untyped = - expectKind s, nnkTypeDef - result = newTree(nnkTypeDef, s[0], s[1], newTree(nnkRefTy, s[2])) - -type - Obj {.makeref.} = object - a: int - -doAssert Obj is ref -doAssert Obj(a: 3)[].a == 3 - -macro multiply(amount: static int, s): 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: 2.} = object - x, y, z: int - Baz = object - -let bar1 = Bar1(x: 1, y: 2, z: 3) -let bar2 = Bar2(x: bar1.x, y: bar1.y, z: bar1.z) -doAssert Bar1 isnot Bar2 -doAssert not declared(Bar) -doAssert not declared(Bar3) - -# https://github.com/nim-lang/RFCs/issues/219 - -macro inferKind(td): untyped = - let name = $td[0].basename - var rhs = td[2] - while rhs.kind in {nnkPtrTy, nnkRefTy}: rhs = rhs[0] - if rhs.kind != nnkObjectTy: - result = td - else: - for n in rhs[^1]: - if n.kind == nnkRecCase and n[0][^2].eqIdent"_": - let kindTypeName = ident(name & "Kind") - let en = newTree(nnkEnumTy, newEmptyNode()) - for i in 1 ..< n.len: - let branch = n[i] - if branch.kind == nnkOfBranch: - for j in 0 ..< branch.len - 1: - en.add(branch[j]) - n[0][^2] = kindTypeName - return newTree(nnkTypeSection, - newTree(nnkTypeDef, kindTypeName, newEmptyNode(), en), - td) - -type Node {.inferKind.} = ref object - case kind: _ - of opValue: value: int - of opAdd, opSub, opMul, opCall: kids: seq[Node] - -doAssert opValue is NodeKind -let node = Node(kind: opMul, kids: @[ - Node(kind: opValue, value: 3), - Node(kind: opValue, value: 5) -]) -doAssert node.kind == opMul -doAssert node.kids[0].value * node.kids[1].value == 15 diff --git a/tests/pragmas/tused2.nim b/tests/pragmas/tused2.nim deleted file mode 100644 index f80c198d8..000000000 --- a/tests/pragmas/tused2.nim +++ /dev/null @@ -1,46 +0,0 @@ -discard """ - matrix: "--hint:all:off --hint:XDeclaredButNotUsed --path:." - joinable: false - nimoutFull: true - nimout: ''' -mused2a.nim(12, 6) Hint: 'fn1' is declared but not used [XDeclaredButNotUsed] -mused2a.nim(16, 5) Hint: 'fn4' is declared but not used [XDeclaredButNotUsed] -mused2a.nim(20, 7) Hint: 'fn7' is declared but not used [XDeclaredButNotUsed] -mused2a.nim(23, 6) Hint: 'T1' is declared but not used [XDeclaredButNotUsed] -mused2a.nim(1, 11) Warning: imported and not used: 'strutils' [UnusedImport] -mused2a.nim(3, 9) Warning: imported and not used: 'os' [UnusedImport] -mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport] -mused2a.nim(6, 9) Warning: imported and not used: 'setutils' [UnusedImport] -tused2.nim(42, 8) Warning: imported and not used: 'mused2a' [UnusedImport] -tused2.nim(45, 11) Warning: imported and not used: 'strutils' [UnusedImport] -''' -""" - - - - - - - - - - - - - - - - - - - - - - -# line 40 - -import mused2a -import mused2b - -import std/strutils -baz() diff --git a/tests/pragmas/tuserpragma2.nim b/tests/pragmas/tuserpragma2.nim index c3f31cd5e..ce16c4649 100644 --- a/tests/pragmas/tuserpragma2.nim +++ b/tests/pragmas/tuserpragma2.nim @@ -3,10 +3,9 @@ discard """ file: "tuserpragma2.nim" line: 11 """ -{.push warningAsError[Effect]: on.} + # bug #7216 {.pragma: my_pragma, raises: [].} proc test1 {.my_pragma.} = raise newException(Exception, "msg") -{.pop.} diff --git a/tests/pragmas/tvar_macro.nim b/tests/pragmas/tvar_macro.nim deleted file mode 100644 index 3fb6e3e53..000000000 --- a/tests/pragmas/tvar_macro.nim +++ /dev/null @@ -1,128 +0,0 @@ -import macros - -block: # test usage - macro modify(sec) = - result = copy sec - result[0][0] = ident(repr(result[0][0]) & "Modified") - - block: - let foo {.modify.} = 3 - doAssert fooModified == 3 - - block: # in section - let - a = 1 - b {.modify.} = 2 - c = 3 - doAssert (a, bModified, c) == (1, 2, 3) - -block: # with single argument - macro appendToName(name: static string, sec) = - result = sec - result[0][0] = ident(repr(result[0][0]) & name) - - block: - let foo {.appendToName: "Bar".} = 3 - doAssert fooBar == 3 - - block: - let - a = 1 - b {.appendToName("").} = 2 - c = 3 - doAssert (a, b, c) == (1, 2, 3) - -macro appendToNameAndAdd(name: static string, incr: static int, sec) = - result = sec - result[0][0] = ident(repr(result[0][0]) & name) - result[0][2] = infix(result[0][2], "+", newLit(incr)) - -block: # with multiple arguments - block: - let foo {.appendToNameAndAdd("Bar", 5).} = 3 - doAssert fooBar == 8 - - block: - let - a = 1 - b {.appendToNameAndAdd("", 15).} = 2 - c = 3 - doAssert (a, b, c) == (1, 17, 3) - -block: # in other kinds of sections - block: - const - a = 1 - b {.appendToNameAndAdd("", 15).} = 2 - c = 3 - doAssert (a, b, c) == (1, 17, 3) - doAssert static(b) == b - - block: - var - a = 1 - b {.appendToNameAndAdd("", 15).} = 2 - c = 3 - doAssert (a, b, c) == (1, 17, 3) - b += a - c += b - doAssert (a, b, c) == (1, 18, 21) - -block: # with other pragmas - macro appendToNameAndAdd(name: static string, incr, sec) = - result = sec - result[0][0][0] = ident(repr(result[0][0][0]) & name) - result[0][0][1].add(ident"deprecated") - result[0][2] = infix(result[0][2], "+", incr) - - var - a = 1 - foo {.exportc: "exportedFooBar", appendToNameAndAdd("Bar", {'0'..'9'}), used.} = {'a'..'z', 'A'..'Z'} - b = 2 - - doAssert (a, b) == (1, 2) - - let importedFooBar {.importc: "exportedFooBar", nodecl.}: set[char] - - doAssert importedFooBar == fooBar #[tt.Warning - ^ fooBar is deprecated - ]# - - -block: # with stropping - macro `cast`(def) = - let def = def[0] - let - lhs = def[0] - typ = def[1] - ex = def[2] - addrTyp = if typ.kind == nnkEmpty: typ else: newTree(nnkPtrTy, typ) - result = quote do: - let tmp: `addrTyp` = unsafeAddr(`ex`) - template `lhs`: untyped = tmp[] - - macro assign(def) = - result = getAst(`cast`(def)) - - block: - let s = @["foo", "bar"] - let a {.`assign`.} = s[0] - doAssert a == "foo" - doAssert a[0].addr == s[0][0].addr - - block: - let - s = @["foo", "bar"] - a {.`cast`.} = s[0] - doAssert a == "foo" - doAssert a[0].addr == s[0][0].addr - -block: # bug #15920 - macro foo(def) = - result = def - proc fun1()= - let a {.foo.} = 1 - template fun2()= - let a {.foo.} = 1 - fun1() # ok - fun2() # BUG diff --git a/tests/pragmas/twarning_off.nim b/tests/pragmas/twarning_off.nim index ccf07b9c4..d5d31d4c2 100644 --- a/tests/pragmas/twarning_off.nim +++ b/tests/pragmas/twarning_off.nim @@ -1,6 +1,7 @@ discard """ nimout: ''' compile start +.. warn_module.nim(6, 6) Hint: 'test' is declared but not used [XDeclaredButNotUsed] compile end ''' diff --git a/tests/proc/t15949.nim b/tests/proc/t15949.nim deleted file mode 100644 index bc3fddc84..000000000 --- a/tests/proc/t15949.nim +++ /dev/null @@ -1,20 +0,0 @@ -# bug #15949 - -discard """ -errormsg: "parameter 'a' requires a type" -nimout: ''' -t15949.nim(20, 14) Error: parameter 'a' requires a type''' -""" - - -# line 10 -proc procGood(a, b = 1): (int, int) = (a, b) - -doAssert procGood() == (1, 1) -doAssert procGood(b = 3) == (1, 3) -doAssert procGood(a = 2) == (2, 1) -doAssert procGood(a = 5, b = 6) == (5, 6) - -# The type (and default value propagation breaks in the below example -# as semicolon is used instead of comma. -proc procBad(a; b = 1): (int, int) = (a, b) diff --git a/tests/proc/t17157.nim b/tests/proc/t17157.nim deleted file mode 100644 index 020e93fce..000000000 --- a/tests/proc/t17157.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - errormsg: "'untyped' is only allowed in templates and macros or magic procs" -""" - -template something(op: proc (v: untyped): void): void = - discard diff --git a/tests/proc/t19795.nim b/tests/proc/t19795.nim deleted file mode 100644 index 677ec0a63..000000000 --- a/tests/proc/t19795.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - matrix: "--mm:arc" -""" - -# bug #19795 -# bug #21085 - -type Vector = seq[int] - -var vect: Vector = newSeq[int](5) -doAssert vect == @[0, 0, 0, 0, 0] - -# Needed to get the problem. Could also use "var". -let vectCopy = vect - -# Then some procedure definition is needed to get the problem. -proc p(): int = 3 - -doAssert vect == @[0, 0, 0, 0, 0] \ No newline at end of file diff --git a/tests/proc/tillegalreturntype.nim b/tests/proc/tillegalreturntype.nim index 1076f7f75..799cad53a 100644 --- a/tests/proc/tillegalreturntype.nim +++ b/tests/proc/tillegalreturntype.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim check --hints:off $file" + cmd: "nim check $file" errormsg: "" nimout: ''' tillegalreturntype.nim(11, 11) Error: return type 'typed' is only valid for macros and templates diff --git a/tests/proc/tlambdapragma.nim b/tests/proc/tlambdapragma.nim deleted file mode 100644 index daa952b1e..000000000 --- a/tests/proc/tlambdapragma.nim +++ /dev/null @@ -1,7 +0,0 @@ -discard """ - errormsg: "invalid pragma: exportc" -""" - -let _ = proc () {.exportc.} = - # this would previously cause a codegen error - discard diff --git a/tests/proc/tunderscoreparam.nim b/tests/proc/tunderscoreparam.nim deleted file mode 100644 index c2f6cf8ad..000000000 --- a/tests/proc/tunderscoreparam.nim +++ /dev/null @@ -1,109 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -import std/[assertions, sequtils] - -proc test() = - block: - proc ok(_, _, a: int): int = - doAssert not compiles(_) - a - doassert ok(4, 2, 5) == 5 - - block: - proc ok(_: int, _: int, a: int): int = a - doAssert ok(4, 2, 5) == 5 - - block: - proc ok(_: int, _: float, a: int): int = a - doAssert ok(1, 2.0, 5) == 5 - - block: - proc ok(_: int, _: float, _: string, a: int): int = a - doAssert ok(1, 2.6, "5", 5) == 5 - - block: - proc ok[T](_, _, a: T): T = - doAssert not compiles(_) - a - doAssert ok(4, 2, 5) == 5 - doAssert ok("a", "b", "c") == "c" - doAssert not compiles(ok(1, 2, "a")) - - block: - let ok = proc (_, _, a: int): int = - doAssert not compiles(_) - a - doAssert ok(4, 2, 5) == 5 - - block: - proc foo(lam: proc (_, _, a: int): int): int = - lam(4, 2, 5) - doAssert foo(proc (_, _, a: auto): auto = - doAssert not compiles(_) - a) == 5 - - block: - iterator fn(_, _: int, c: int): int = yield c - doAssert toSeq(fn(1,2,3)) == @[3] - - block: - template ok(_, _, a: int): int = a - doAssert ok(4, 2, 5) == 5 - - block: - doAssert not (compiles do: - template bad(_: int): int = _ - discard bad(3)) - - block: - template ok(_: int, _: int, a: int): int = a - doAssert ok(4, 2, 5) == 5 - - block: - template ok(_: int, _: float, a: int): int = a - doAssert ok(1, 2.0, 5) == 5 - - block: - template ok(_: int, _: float, _: string, a: int): int = a - doAssert ok(1, 2.6, "5", 5) == 5 - - block: - template main2() = - iterator fn(_, _: int, c: int): int = yield c - main2() - - block: - template main = - proc foo(_: int) = - let a = _ - doAssert not compiles(main()) - -proc closureTest() = - var x = 0 - - block: - proc foo(_, _: int) = x += 5 - - foo(1, 2) - doAssert x == 5 - - block: - proc foo(_: int, _: float) = x += 5 - - foo(1, 2) - doAssert x == 10 - - block: - proc foo(_: int, _: float, _: string) = x += 5 - - foo(1, 2, "5") - doAssert x == 15 - -static: test() -test() - -when not defined(js): - static: closureTest() -closureTest() diff --git a/tests/range/t19678.nim b/tests/range/t19678.nim deleted file mode 100644 index 88f7eff89..000000000 --- a/tests/range/t19678.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - cmd: "nim check --hints:off $file" - errormsg: "" - nimout: ''' -t19678.nim(13, 13) Error: range of string is invalid - - - -''' -""" - -case "5": - of "0" .. "9": - discard - else: - discard - diff --git a/tests/range/tcompiletime_range_checks.nim b/tests/range/tcompiletime_range_checks.nim index 29e2c48a8..37095e0b7 100644 --- a/tests/range/tcompiletime_range_checks.nim +++ b/tests/range/tcompiletime_range_checks.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim check --hint:Processing:off --hint:Conf:off $file" + cmd: "nim check --hint[Processing]:off --hint[Conf]:off $file" errormsg: "18446744073709551615 can't be converted to int8" nimout: '''tcompiletime_range_checks.nim(36, 21) Error: 2147483648 can't be converted to int32 tcompiletime_range_checks.nim(37, 23) Error: -1 can't be converted to uint64 diff --git a/tests/range/trange.nim b/tests/range/trange.nim index abfa7d474..c864387f6 100644 --- a/tests/range/trange.nim +++ b/tests/range/trange.nim @@ -74,37 +74,37 @@ block tn8vsint16: import strutils block tcolors: - type TColor = distinct uint32 + type TColor = distinct int32 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(uint32(c), 6) + result = "#" & toHex(int32(c), 6) echo rgb(34, 55, 255) - block: + when false: type - TColor = distinct uint32 - TColorComponent = distinct uint8 + TColor = distinct int32 + TColorComponent = distinct int8 proc red(a: TColor): TColorComponent = - result = TColorComponent(uint32(a) and 0xff'u32) + result = TColorComponent(int32(a) and 0xff'i32) proc green(a: TColor): TColorComponent = - result = TColorComponent(uint32(a) shr 8'u32 and 0xff'u32) + result = TColorComponent(int32(a) shr 8'i32 and 0xff'i32) proc blue(a: TColor): TColorComponent = - result = TColorComponent(uint32(a) shr 16'u32 and 0xff'u32) + result = TColorComponent(int32(a) shr 16'i32 and 0xff'i32) 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(int(uint8(a)) + int(uint8(b)), 255)) + result = TColorComponent(min(ze(int8(a)) + ze(int8(b)), 255)) proc `+` (a, b: TColor): TColor = ## saturated arithmetic for colors makes sense, I think: - return rgb(int(red(a) +! red(b)), int(green(a) +! green(b)), int(blue(a) +! blue(b))) + return rgb(red(a) +! red(b), green(a) +! green(b), blue(a) +! blue(b)) - discard rgb(34, 55, 255) + rgb(34, 55, 255) block: type @@ -142,15 +142,3 @@ var a: array[4'u, string] for i in 0.. +#else +#include +#include /* for sleep(3) */ +#endif +#include +#include +#include +#include +#include + +#define RUNTIME (15*60) + + +typedef void (*pFunc)(void); + +int main(int argc, char* argv[]) +{ + int i; + void* hndl; + pFunc status; + pFunc count; + pFunc checkOccupiedMem; + +#ifdef WIN + hndl = (void*) LoadLibrary((char const*)"./tests/realtimeGC/shared.dll"); + status = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"status"); + count = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"count"); + checkOccupiedMem = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"checkOccupiedMem"); +#else /* OSX || NIX xxx: OSX assumes dylib*/ + hndl = (void*) dlopen((char const*)"./tests/realtimeGC/libshared.so", RTLD_LAZY); + status = (pFunc) dlsym(hndl, (char const*)"status"); + count = (pFunc) dlsym(hndl, (char const*)"count"); + checkOccupiedMem = (pFunc) dlsym(hndl, (char const*)"checkOccupiedMem"); +#endif + + assert(hndl); + assert(status); + assert(count); + assert(checkOccupiedMem); + + time_t startTime = time((time_t*)0); + time_t runTime = (time_t)(RUNTIME); + time_t accumTime = 0; + while (accumTime < runTime) { + for (i = 0; i < 10; i++) + count(); + /* printf("1. sleeping...\n"); */ + sleep(1); + for (i = 0; i < 10; i++) + status(); + /* printf("2. sleeping...\n"); */ + sleep(1); + checkOccupiedMem(); + accumTime = time((time_t*)0) - startTime; + /* printf("--- Minutes left to run: %d\n", (int)(runTime-accumTime)/60); */ + } + printf("Cleaning up the shared object pointer...\n"); +#ifdef WIN + FreeLibrary((HMODULE)hndl); +#else /* OSX || NIX */ + dlclose(hndl); +#endif + printf("Done\n"); + return 0; +} diff --git a/tests/realtimeGC/readme.txt b/tests/realtimeGC/readme.txt new file mode 100644 index 000000000..c5837ee14 --- /dev/null +++ b/tests/realtimeGC/readme.txt @@ -0,0 +1,10 @@ +Test the realtime GC without linking nimrtl.dll/so. + +To build by hand and run the test for 35 minutes: + $ nim r --threads:on -d:runtimeSecs:2100 tests/realtimeGC/nmain.nim + +``` +xxx do we still need tests/realtimeGC/cmain.c? +if so, tests/realtimeGC/cmain.c needs to updated and factorized with nmain.nim to avoid duplication (even if it's a C file) +``` + $ gcc -o tests/realtimeGC/cmain tests/realtimeGC/cmain.c -ldl \ No newline at end of file diff --git a/tests/realtimeGC/tmain.nim b/tests/realtimeGC/tmain.nim index ca0177e3d..f1515a549 100644 --- a/tests/realtimeGC/tmain.nim +++ b/tests/realtimeGC/tmain.nim @@ -9,13 +9,6 @@ these dont' seem needed --debuginfo nor these from the previous main.nim.cfg: --app:console ]# -#[ -Test the realtime GC without linking nimrtl.dll/so. - -To build by hand and run the test for 35 minutes: -`nim r --threads:on -d:runtimeSecs:2100 tests/realtimeGC/tmain.nim` -]# - import times, os, strformat, strutils from stdtest/specialpaths import buildDir # import threadpool @@ -23,10 +16,9 @@ from stdtest/specialpaths import buildDir const runtimeSecs {.intdefine.} = 5 const file = "shared.nim" -const dllname = buildDir / (DynlibFormat % "shared_D20210524T180506") +const dllname = buildDir / (DynlibFormat % file) static: - # D20210524T180826:here we compile the dependency on the fly let nim = getCurrentCompilerExe() let (output, exitCode) = gorgeEx(fmt"{nim} c -o:{dllname} --debuginfo --app:lib --threads:on -d:release -d:useRealtimeGC {file}") doAssert exitCode == 0, output diff --git a/tests/sandwich/generic_library.nim b/tests/sandwich/generic_library.nim deleted file mode 100644 index 43e7bd65f..000000000 --- a/tests/sandwich/generic_library.nim +++ /dev/null @@ -1,6 +0,0 @@ - -proc libraryFunc*[T](x: T) = - mixin mixedIn, indirectlyMixedIn - echo mixedIn() - echo indirectlyMixedIn() - diff --git a/tests/sandwich/helper_module.nim b/tests/sandwich/helper_module.nim deleted file mode 100644 index d003bf044..000000000 --- a/tests/sandwich/helper_module.nim +++ /dev/null @@ -1,3 +0,0 @@ - -proc indirectlyMixedIn*: int = - 200 diff --git a/tests/sandwich/module_using_generic_library.nim b/tests/sandwich/module_using_generic_library.nim deleted file mode 100644 index bbb0d92a4..000000000 --- a/tests/sandwich/module_using_generic_library.nim +++ /dev/null @@ -1,12 +0,0 @@ - -import - generic_library, helper_module - -proc mixedIn: int = 100 - -proc makeUseOfLibrary*[T](x: T) = - bind mixedIn, indirectlyMixedIn - libraryFunc(x) - -when isMainModule: - makeUseOfLibrary "test" diff --git a/tests/sandwich/tmain.nim b/tests/sandwich/tmain.nim deleted file mode 100644 index aa50bfb04..000000000 --- a/tests/sandwich/tmain.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - output: '''100 -200''' -""" - -import - module_using_generic_library - -makeUseOfLibrary "test" diff --git a/tests/sets/m17385.nim b/tests/sets/m17385.nim deleted file mode 100644 index 3919e067a..000000000 --- a/tests/sets/m17385.nim +++ /dev/null @@ -1,11 +0,0 @@ -import std/sets - -type - Diff*[T] = object - data: T - -proc test*[T](diff: Diff[T]) = - var bPopular = initHashSet[T]() - for element in bPopular.items(): - echo element - diff --git a/tests/sets/t15435.nim b/tests/sets/t15435.nim deleted file mode 100644 index 5ead7e641..000000000 --- a/tests/sets/t15435.nim +++ /dev/null @@ -1,29 +0,0 @@ -# bug #15435 -discard """ -errormsg: "type mismatch: got " -nimout: '''t15435.nim(28, 13) Error: type mismatch: got -but expected one of: -proc `<`[T](x, y: set[T]): bool - first type mismatch at position: 2 - required type for y: set[T] - but expression 'x' is of type: set[range 1..5(uint8)] -20 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them - -expression: {1'u8, 5} < x''' -""" - - - - - - -## line 20 -var - x: set[range[1u8..5u8]] - -x.incl(1) -x.incl(3) -x.incl(5) - -if {1u8, 5} < x: - echo "successful" diff --git a/tests/sets/t17385.nim b/tests/sets/t17385.nim deleted file mode 100644 index cc08b4882..000000000 --- a/tests/sets/t17385.nim +++ /dev/null @@ -1,4 +0,0 @@ -import m17385 - -let a = Diff[int]() -a.test() diff --git a/tests/sets/t20997.nim b/tests/sets/t20997.nim deleted file mode 100644 index b320eee1a..000000000 --- a/tests/sets/t20997.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - joinable: false -""" - -{.passC: "-flto".} -{.passL: "-flto".} - -template f(n: int) = discard card(default(set[range[0 .. (1 shl n) - 1]])) -f( 7) -f( 8) -f( 9) -f(10) -f(11) -f(12) -f(13) -f(14) -f(15) -f(16) diff --git a/tests/sets/t5792.nim b/tests/sets/t5792.nim deleted file mode 100644 index 297a1fc15..000000000 --- a/tests/sets/t5792.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - matrix: "--gc:refc; --gc:arc" -""" - -type - T = enum - a - b - c - U = object - case k: T - of a: - x: int - of {b, c} - {a}: - y: int - -doAssert U(k: b, y: 1).y == 1 diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim index 517c0b89c..dbbeed543 100644 --- a/tests/sets/tsets.nim +++ b/tests/sets/tsets.nim @@ -1,57 +1,207 @@ -# Test builtin sets +discard """ +output: ''' +Ha ein F ist in s! +false +''' +""" +# Test the handling of sets -# xxx these tests are not very good, this should be revisited. - -when defined nimTestsTsetsGenerate: - # to generate enums for this test - var ret: string - for i in 0..<276: - ret.add "k" & $i & ", " - echo ret +import + strutils proc testSets(s: var set[char]) = s = {'A', 'B', 'C', 'E'..'G'} + {'Z'} + s # test sets if the first element is different from 0: -block: - type - TAZ = range['a'..'z'] - TAZset = set[TAZ] - FakeTokType = enum - 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, k101, k102, k103, k104, k105, k106, k107, k108, k109, k110, k111, k112, k113, k114, k115, k116, k117, k118, k119, k120, k121, k122, k123, k124, k125, k126, k127, k128, k129, k130, k131, k132, k133, k134, k135, k136, k137, k138, k139, k140, k141, k142, k143, k144, k145, k146, k147, k148, k149, k150, k151, k152, k153, k154, k155, k156, k157, k158, k159, k160, k161, k162, k163, k164, k165, k166, k167, k168, k169, k170, k171, k172, k173, k174, k175, k176, k177, k178, k179, k180, k181, k182, k183, k184, k185, k186, k187, k188, k189, k190, k191, k192, k193, k194, k195, k196, k197, k198, k199, k200, k201, k202, k203, k204, k205, k206, k207, k208, k209, k210, k211, k212, k213, k214, k215, k216, k217, k218, k219, k220, k221, k222, k223, k224, k225, k226, k227, k228, k229, k230, k231, k232, k233, k234, k235, k236, k237, k238, k239, k240, k241, k242, k243, k244, k245, k246, k247, k248, k249 - FakeTokTypeRange = range[k2..k101] - FakeTokTypes = set[FakeTokTypeRange] +type + TAZ = range['a'..'z'] + TAZset = set[TAZ] - const toktypes: FakeTokTypes = {FakeTokTypeRange(k2)..pred(k64), k72..k74} + TTokType* = enum + tkInvalid, tkEof, + tkSymbol, + tkAddr, tkAnd, tkAs, tkAsm, tkBlock, tkBreak, tkCase, tkCast, tkConst, + tkContinue, tkConverter, tkDiscard, tkDiv, tkElif, tkElse, tkEnd, tkEnum, + tkExcept, tkException, tkFinally, tkFor, tkFrom, tkGeneric, tkIf, tkImplies, + tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda, tkMacro, + tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc, + tkPtr, tkRaise, tkRecord, tkRef, tkReturn, tkShl, tkShr, tkTemplate, tkTry, + tkType, tkVar, tkWhen, tkWhere, tkWhile, tkWith, tkWithout, tkXor, tkYield, + tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit, + tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit, tkCharLit, + tkRCharLit, tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, + tkCurlyRi, tkBracketDotLe, tkBracketDotRi, + tkCurlyDotLe, tkCurlyDotRi, + tkParDotLe, tkParDotRi, + tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr, + tkComment, tkAccent, tkInd, tkSad, tkDed, + tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr + TTokTypeRange = range[tkSymbol..tkDed] + TTokTypes* = set[TTokTypeRange] - var - s: set[char] - a: TAZset - s = {'0'..'9'} - testSets(s) - doAssert 'F' in s - a = {} #{'a'..'z'} - for x in low(TAZ) .. high(TAZ): - incl(a, x) - doAssert x in a +const + toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), + tkStrLit..tkTripleStrLit} + +var + s: set[char] + a: TAZset +s = {'0'..'9'} +testSets(s) +if 'F' in s: write(stdout, "Ha ein F ist in s!\n") +else: write(stdout, "BUG: F ist nicht in s!\n") +a = {} #{'a'..'z'} +for x in low(TAZ) .. high(TAZ): + incl(a, x) + if x in a: discard + else: write(stdout, "BUG: something not in a!\n") + +for x in low(TTokTypeRange) .. high(TTokTypeRange): + if x in tokTypes: + discard + #writeLine(stdout, "the token '$1' is in the set" % repr(x)) + +#OUT Ha ein F ist in s! - for x in low(FakeTokTypeRange) .. high(FakeTokTypeRange): - if x in tokTypes: - discard type - FakeMsgKind* = enum - 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, k101, k102, k103, k104, k105, k106, k107, k108, k109, k110, k111, k112, k113, k114, k115, k116, k117, k118, k119, k120, k121, k122, k123, k124, k125, k126, k127, k128, k129, k130, k131, k132, k133, k134, k135, k136, k137, k138, k139, k140, k141, k142, k143, k144, k145, k146, k147, k148, k149, k150, k151, k152, k153, k154, k155, k156, k157, k158, k159, k160, k161, k162, k163, k164, k165, k166, k167, k168, k169, k170, k171, k172, k173, k174, k175, k176, k177, k178, k179, k180, k181, k182, k183, k184, k185, k186, k187, k188, k189, k190, k191, k192, k193, k194, k195, k196, k197, k198, k199, k200, k201, k202, k203, k204, k205, k206, k207, k208, k209, k210, k211, k212, k213, k214, k215, k216, k217, k218, k219, k220, k221, k222, k223, k224, k225, k226, k227, k228, k229, k230, k231, k232, k233, k234, k235, k236, k237, k238, k239, k240, k241, k242, k243, k244, k245, k246, k247, k248, k249, k250, k251, k252, k253, k254, k255, k256, k257, k258, k259, k260, k261, k262, k263, k264, k265, k266, k267, k268, k269, k270, k271, k272, k273, k274, k275, + TMsgKind* = enum + errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated, + errXCompilerDoesNotSupportCpp, errStringLiteralExpected, + errIntLiteralExpected, errInvalidCharacterConstant, + errClosingTripleQuoteExpected, errClosingQuoteExpected, + errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, + errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter, + errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected, + errNewlineExpected, + errInvalidModuleName, + errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, + errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, + errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, + errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, + errExceptionExpected, errExceptionAlreadyHandled, + errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, + errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, + errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, + errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, + errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, + errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound, + errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, + errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, + errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, + errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected, + errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, + errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, + errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, + errConstantDivisionByZero, errOrdinalTypeExpected, + errOrdinalOrFloatTypeExpected, errOverOrUnderflow, + errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, + errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, + errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, + errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, + errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, + errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected, + errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, + errMagicOnlyInSystem, errPowerOfTwoExpected, + errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, + errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, + errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, + errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, + errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, + errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, + errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, + errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, + errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, + errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, + errVarForOutParamNeeded, + errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, + errAmbiguousCallXYZ, errWrongNumberOfArguments, + errXCannotBePassedToProcVar, + errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, + errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, + errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, + errInvalidOrderInArrayConstructor, + errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, + errOptionExpected, errXisNoLabel, errNotAllCasesCovered, + errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, + errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, + errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, + errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, + errXNotAllowedHere, errInvalidControlFlowX, + errXisNoType, errCircumNeedsPointer, errInvalidExpression, + errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, + errNamedExprNotAllowed, errXExpectsOneTypeParam, + errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, + errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, + errNoReturnTypeDeclared, + errInvalidCommandX, errXOnlyAtModuleScope, + errXNeedsParamObjectType, + errTemplateInstantiationTooNested, errInstantiationFrom, + errInvalidIndexValueForTuple, errCommandExpectsFilename, + errMainModuleMustBeSpecified, + errXExpected, + errTIsNotAConcreteType, + errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, + errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, + errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, + errOnlyACallOpCanBeDelegator, errUsingNoSymbol, + errMacroBodyDependsOnGenericTypes, + errDestructorNotGenericEnough, + errInlineIteratorsAsProcParams, + errXExpectsTwoArguments, + errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, + errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, + errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, + errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, + errXhasSideEffects, errIteratorExpected, errLetNeedsInit, + errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, + errXCannotBeClosure, errXMustBeCompileTime, + errCannotInferTypeOfTheLiteral, + errCannotInferReturnType, + errGenericLambdaNotAllowed, + errCompilerDoesntSupportTarget, + errUser, + warnCannotOpenFile, + warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, + warnDeprecated, warnConfigDeprecated, + warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, + warnUnknownSubstitutionX, warnLanguageXNotSupported, + warnFieldXNotSupported, warnCommentXIgnored, + warnNilStatement, warnTypelessParam, + warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, + warnEachIdentIsTuple, + warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, + warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, + warnUser, + hintSuccess, hintSuccessX, + hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, + hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, + hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, + hintConditionAlwaysTrue, hintName, hintPattern, + hintUser -doAssert pred(k260) == k259 +const + fatalMin* = errUnknown + fatalMax* = errInternal + errMin* = errUnknown + errMax* = errUser + warnMin* = warnCannotOpenFile + warnMax* = pred(hintSuccess) + hintMin* = hintSuccess + hintMax* = high(TMsgKind) type - FakeMsgKind2 = range[k230..high(FakeMsgKind)] - FakeMsgKind3 = set[FakeMsgKind2] + TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints + TNoteKinds* = set[TNoteKind] -var gNotes: FakeMsgKind3 = {low(FakeMsgKind2)..high(FakeMsgKind2)} - {k233, k235} +var + gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - + {warnUninit, warnProveField, warnProveIndex, warnGcUnsafe} -doAssert k233 notin gNotes + +#import compiler.msgs + +echo warnUninit in gNotes # 7555 doAssert {-1.int8, -2, -2}.card == 2 @@ -68,24 +218,3 @@ type Foo = enum let x = { Foo1, Foo2 } # bug #8425 - -block: - # bug #2880 - type - FakeMsgKind = enum - 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[FakeMsgKind.k50..high(FakeMsgKind)] - FakeMsgKind2s = set[FakeMsgKind2] - - const - a1: array[0..0, FakeMsgKind2s] = [{low(FakeMsgKind2)..high(FakeMsgKind2)} - {FakeMsgKind.k99}] - a2 = a1[0] - - var - s1: FakeMsgKind2s = a1[0] - s2: FakeMsgKind2s = a2 - - doAssert k99 notin s1 - doAssert k99 notin s2 diff --git a/tests/sets/tsets_various.nim b/tests/sets/tsets_various.nim index 419bcfdcc..3e468c8f9 100644 --- a/tests/sets/tsets_various.nim +++ b/tests/sets/tsets_various.nim @@ -1,12 +1,14 @@ discard """ - targets: "c cpp js" + output: ''' +set is empty +''' """ -import std/[sets, hashes] -from std/sequtils import toSeq -from std/algorithm import sorted -from stdtest/testutils import whenVMorJs +import sets, hashes + +from sequtils import toSeq +from algorithm import sorted proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted template sortedItems(t: untyped): untyped = sorted(toSeq(t)) @@ -20,12 +22,10 @@ block tsetpop: discard a.pop() doAssert len(a) == 0 - var msg = "" try: echo a.pop() except KeyError as e: - msg = e.msg - doAssert msg == "set is empty" + echo e.msg @@ -254,33 +254,3 @@ block: # test correctness after a number of inserts/deletes testDel(): (var t: HashSet[int]) testDel(): (var t: OrderedSet[int]) - - -template main() = - # xxx move all tests inside this - block: - let a = {true, false} - doAssert $a == "{false, true}" - doAssert a.len == 2 - - block: - let a = {false .. true} - doAssert $a == "{false, true}" - doAssert a.len == 2 - - block: - let a = {false .. false} - doAssert $a == "{false}" - doAssert a.len == 1 - - block: # bug #16123 - whenVMorJs: discard - do: - type CallType = proc() {.closure.} - var setA = initHashSet[CallType]() - let foo = proc() = discard - setA.incl(foo) - doAssert setA.contains(foo) - -static: main() -main() diff --git a/tests/showoff/tdrdobbs_examples.nim b/tests/showoff/tdrdobbs_examples.nim index c61e177dc..0e10c6dd8 100644 --- a/tests/showoff/tdrdobbs_examples.nim +++ b/tests/showoff/tdrdobbs_examples.nim @@ -40,12 +40,12 @@ const msb3999 = mostSignificantBit(3999) echo msb3999, " ", mostSignificantBit(0), " ", square(44) -proc filter[T](a: openArray[T], predicate: proc (x: T): bool): seq[T] = +proc filter[T](a: openarray[T], predicate: proc (x: T): bool): seq[T] = result = @[] # @[] constructs the empty seq for x in a: if predicate(x): result.add(x) -proc map[T, S](a: openArray[T], fn: proc (x: T): S): seq[S] = +proc map[T, S](a: openarray[T], fn: proc (x: T): S): seq[S] = newSeq(result, a.len) for i in 0 ..< a.len: result[i] = fn(a[i]) diff --git a/tests/slice/tdistinct.nim b/tests/slice/tdistinct.nim deleted file mode 100644 index d99b529a7..000000000 --- a/tests/slice/tdistinct.nim +++ /dev/null @@ -1,2 +0,0 @@ -type Foo = distinct uint64 -const slice = 0 ..< 42.Foo diff --git a/tests/specialops/tcallops.nim b/tests/specialops/tcallops.nim deleted file mode 100644 index c541a0c1d..000000000 --- a/tests/specialops/tcallops.nim +++ /dev/null @@ -1,49 +0,0 @@ -import macros - -{.experimental: "callOperator".} - -type Foo[T: proc] = object - callback: T - -macro `()`(foo: Foo, args: varargs[untyped]): untyped = - result = newCall(newDotExpr(foo, ident"callback")) - for a in args: - result.add(a) - -var f1Calls = 0 -var f = Foo[proc()](callback: proc() = inc f1Calls) -doAssert f1Calls == 0 -f() -doAssert f1Calls == 1 -var f2Calls = 0 -f.callback = proc() = inc f2Calls -doAssert f2Calls == 0 -f() -doAssert f2Calls == 1 - -let g = Foo[proc (x: int): int](callback: proc (x: int): int = x * 2 + 1) -doAssert g(15) == 31 - -proc `()`(args: varargs[string]): string = - result = "(" - for a in args: result.add(a) - result.add(')') - -let a = "1" -let b = "2" -let c = "3" - -doAssert a(b) == "(12)" -doAssert a.b(c) == `()`(b, a, c) -doAssert (a.b)(c) == `()`(a.b, c) -doAssert `()`(a.b, c) == `()`(`()`(b, a), c) - -block: # bug #1072 - var x: int - - proc foo(some:int):int = some - proc `()`(l,r:string): string = discard - - block: - var foo = 42 - doAssert x.foo == 0 diff --git a/tests/specialops/tcallprecedence.nim b/tests/specialops/tcallprecedence.nim deleted file mode 100644 index 6116f83d5..000000000 --- a/tests/specialops/tcallprecedence.nim +++ /dev/null @@ -1,44 +0,0 @@ -import macros - -{.experimental: "dotOperators".} -{.experimental: "callOperator".} - -block: - template `.()`(foo: int, args: varargs[untyped]): untyped {.used.} = - ".()" - - template `()`(foo: int, args: varargs[untyped]): untyped = - "()" - - let a = (b: 1) - let c = 3 - - doAssert a.b(c) == "()" - doAssert not compiles(a(c)) - doAssert (a.b)(c) == "()" - -macro `()`(args: varargs[typed]): untyped = - result = newLit("() " & args.treeRepr) - -macro `.`(args: varargs[typed]): untyped = - result = newLit(". " & args.treeRepr) - -block: - let a = 1 - let b = 2 - doAssert a.b == `()`(b, a) - -block: - let a = 1 - proc b(): int {.used.} = 2 - doAssert a.b == `.`(a, b) - -block: - let a = 1 - proc b(x: int): int = x + 1 - let c = 3 - - doAssert a.b(c) == `.`(a, b, c) - doAssert a(b) == `()`(a, b) - doAssert (a.b)(c) == `()`(a.b, c) - doAssert a.b == b(a) diff --git a/tests/specialops/tdotops.nim b/tests/specialops/tdotops.nim index ca5eee665..b1c75ab33 100644 --- a/tests/specialops/tdotops.nim +++ b/tests/specialops/tdotops.nim @@ -17,14 +17,6 @@ one param call to c with 10 ''' """ -block: - type Foo = object - var a: Foo - template `.`(a: Foo, b: untyped): untyped = astToStr(b) - template callme(a, f): untyped = a.f - doAssert callme(a, f2) == "f2" # not `f` - doAssert a.callme(f3) == "f3" - type T1 = object x*: int diff --git a/tests/specialops/terrmsgs.nim b/tests/specialops/terrmsgs.nim deleted file mode 100644 index d1a790e54..000000000 --- a/tests/specialops/terrmsgs.nim +++ /dev/null @@ -1,76 +0,0 @@ -discard """ -action: reject -cmd: '''nim check $options $file''' -matrix: "; -d:testWithout" -""" - -when not defined(testWithout): # test for same errors before and after - {.experimental: "dotOperators".} - {.experimental: "callOperator".} - -# issue #13063 - -block: - type Foo = object - type Bar = object - x1: int - var b: Bar - block: - template `.`(a: Foo, b: untyped): untyped = 123 - echo b.x #[tt.Error - ^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]# - block: - template `.()`(a: Foo, b: untyped): untyped = 123 - echo b.x() #[tt.Error - ^ attempting to call undeclared routine: 'x']# - block: - template `.=`(a: Foo, b: untyped, c: untyped) = b = c - b.x = 123 #[tt.Error - ^ undeclared field: 'x=' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]# - # yeah it says x= but does it matter in practice - block: - template `()`(a: Foo, b: untyped, c: untyped) = echo "something" - - # completely undeclared:: - xyz(123) #[tt.Error - ^ undeclared identifier: 'xyz']# - - # already declared routine: - min(123) #[tt.Error - ^ type mismatch: got ]# - - # non-routine type shows `()` overloads: - b(123) #[tt.Error - ^ attempting to call routine: 'b']# - - echo b.x #[tt.Error - ^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]# - echo b.x() #[tt.Error - ^ attempting to call undeclared routine: 'x']# - -# issue #7777 - -import macros - -block: - type TestType = object - private_field: string - - when false: - template getField(obj, field: untyped): untyped = obj.field - - macro `.`(obj: TestType, field: untyped): untyped = - let private = newIdentNode("private_" & $field) - result = quote do: - `obj`.getField(`private`) #[tt.Error - ^ attempting to call undeclared routine: 'getField']# - - var tt: TestType - discard tt.field - -block: # related to issue #6981 - proc `()`(a:string, b:string):string = a & b - proc mewSeq[T](a,b:int)=discard - proc mewSeq[T](c:int)= discard - mewSeq[int]() #[tt.Error - ^ type mismatch: got <>]# diff --git a/tests/specialops/tnewseq.nim b/tests/specialops/tnewseq.nim deleted file mode 100644 index 970a5a8c2..000000000 --- a/tests/specialops/tnewseq.nim +++ /dev/null @@ -1,22 +0,0 @@ -# issue #6981 - -import std/assertions - -{.experimental: "callOperator".} - -block: # issue #6981 - proc `()`(a:string, b:string):string = a & b - - var s = newSeq[int](3) - - doAssert s == @[0, 0, 0] - -block: # generalized example from #6981 - proc mewSeq[T](a: int)=discard - proc mewSeq[T]()= discard - mewSeq[int]() - -block: # issue #9831 - type Foo = object - proc `()`(foo: Foo) = discard - let x = newSeq[int]() diff --git a/tests/statictypes/t20148.nim b/tests/statictypes/t20148.nim deleted file mode 100644 index d74a7755e..000000000 --- a/tests/statictypes/t20148.nim +++ /dev/null @@ -1,8 +0,0 @@ -type Percent = range[0.0 .. 1.0] -# type Percent = float # using unlimited `float` works fine - -proc initColor*(alpha: Percent): bool = - echo alpha - -const moduleInstanceStyle = initColor(1) -# let moduleInstanceStyle = initColor(1) # using runtime conversion works fine diff --git a/tests/statictypes/t9255.nim b/tests/statictypes/t9255.nim index aec2fbaf5..99ca2a23c 100644 --- a/tests/statictypes/t9255.nim +++ b/tests/statictypes/t9255.nim @@ -1,6 +1,6 @@ discard """ errormsg: ''' -type mismatch: got +type mismatch: got ''' line: 13 """ diff --git a/tests/statictypes/tstatic.nim b/tests/statictypes/tstatic.nim deleted file mode 100644 index aaa63534b..000000000 --- a/tests/statictypes/tstatic.nim +++ /dev/null @@ -1,56 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -template main() = - block: # bug #17589 - #[ - # all those gave some variation of the same bug: - 'intVal' is not accessible using discriminant 'kind' of type 'TFullReg' - 'floatVal' is not accessible using discriminant 'kind' of type 'TFullReg' - ]# - block: - proc f(a: static uint64): uint64 = - a - const x = 3'u64 - static: doAssert f(x) == 3'u64 - doAssert f(x) == 3'u64 - - block: - proc f(a: static uint64): uint64 = - a - const x = 3'u64 - static: doAssert f(x) == 3'u64 - doAssert f(x) == 3'u64 - - block: - proc foo(x: uint8): uint8 = x - proc f(a: static uint8): auto = foo(a) - const x = 3'u8 - static: doAssert f(x) == 3'u8 - doAssert f(x) == 3'u8 - - block: - template foo2(x: float) = - let b = x == 0 - proc foo(x: float) = foo2(x) - proc f(a: static float) = foo(a) - const x = 1.0 - static: f(x) - - block: - proc foo(x: int32) = - let b = x == 0 - proc f(a: static int32) = foo(a) - static: f(32767) # was working - static: f(32768) # was failing because >= int16.high (see isInt16Lit) - - block: # bug #14585 - const foo_m0ninv = 0x1234'u64 - proc foo(m0ninv: static uint64) = - let b = $m0ninv - static: - foo(foo_m0ninv) - -static: main() -main() diff --git a/tests/statictypes/tstaticimportcpp.nim b/tests/statictypes/tstaticimportcpp.nim index ec4696379..125dcb98d 100644 --- a/tests/statictypes/tstaticimportcpp.nim +++ b/tests/statictypes/tstaticimportcpp.nim @@ -23,12 +23,12 @@ struct SimpleStruct { """ .} type - GenericIntType[N: static[int]; T] {.importcpp: "GenericIntType<'0, '1>".} = object + GenericIntType {.importcpp: "GenericIntType<'0, '1>".} [N: static[int]; T] = object data: array[N, T] - GenericIntTypeAlt[N: static[int]; T] {.importcpp: "GenericIntType".} = object + GenericIntTypeAlt {.importcpp: "GenericIntType".} [N: static[int]; T] = object - GenericTType[T] {.importcpp: "GenericTType<'0>".} = object + GenericTType {.importcpp: "GenericTType<'0>".} [T] = object field: T GenInt4 = GenericIntType[4, int] diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 5df3f35fd..41c060138 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -1,16 +1,7 @@ discard """ -nimoutFull: true nimout: ''' staticAlialProc instantiated with 358 staticAlialProc instantiated with 368 -0: Foo -1: Bar -0: Foo -1: Bar -0: Foo -1: Bar -0: Foo -1: Bar ''' output: ''' 16 @@ -22,7 +13,6 @@ heyho Val1 Val1 ''' -matrix: "--hints:off" """ import macros @@ -255,6 +245,9 @@ echo t.foo, u.bar #------------------------------------------------------------------------------ # issue #9679 +discard """ + output: '''''' +""" type Foo*[T] = object bar*: int @@ -271,7 +264,7 @@ block: fails(foo) -import tables +import macros, tables var foo{.compileTime.} = [ "Foo", @@ -296,10 +289,8 @@ macro fooParam(x: static array[2, string]): untyped = echo i, ": ", val macro barParam(x: static Table[int, string]): untyped = - let barParamInsides = proc(i: int, val: string): NimNode = - echo i, ": ", val for i, val in x: - discard barParamInsides(i, val) + echo i, ": ", val fooM() barM() diff --git a/tests/stdlib/concurrency/atomicSample.nim b/tests/stdlib/concurrency/atomicSample.nim deleted file mode 100644 index d56d867df..000000000 --- a/tests/stdlib/concurrency/atomicSample.nim +++ /dev/null @@ -1,9 +0,0 @@ -import atomics - -type - AtomicWithGeneric*[T] = object - value: Atomic[T] - -proc initAtomicWithGeneric*[T](value: T): AtomicWithGeneric[T] = - result.value.store(value) - diff --git a/tests/stdlib/concurrency/tatomic_import.nim b/tests/stdlib/concurrency/tatomic_import.nim deleted file mode 100644 index e8faaae20..000000000 --- a/tests/stdlib/concurrency/tatomic_import.nim +++ /dev/null @@ -1,11 +0,0 @@ -import atomicSample - -block crossFileObjectContainingAGenericWithAComplexObject: - discard initAtomicWithGeneric[string]("foo") - -block crossFileObjectContainingAGenericWithAnInteger: - discard initAtomicWithGeneric[int](1) - discard initAtomicWithGeneric[int8](1) - discard initAtomicWithGeneric[int16](1) - discard initAtomicWithGeneric[int32](1) - discard initAtomicWithGeneric[int64](1) diff --git a/tests/stdlib/concurrency/tatomics.nim b/tests/stdlib/concurrency/tatomics.nim index 9cfdce83d..260d00990 100644 --- a/tests/stdlib/concurrency/tatomics.nim +++ b/tests/stdlib/concurrency/tatomics.nim @@ -1,8 +1,6 @@ # test atomic operations -import std/[atomics, bitops] -import std/assertions - +import atomics, bitops type Object = object @@ -609,23 +607,3 @@ block clear: doAssert not location.testAndSet location.clear(moRelease) doAssert not location.testAndSet - -block: # bug #18844 - when not defined(cpp): # cpp pending pr #18836 - type - Deprivation = object of RootObj - memes: Atomic[int] - Zoomer = object - dopamine: Deprivation - - block: - var x = Deprivation() - var y = Zoomer() - doAssert x.memes.load == 0 - doAssert y.dopamine.memes.load == 0 - - block: - var x: Deprivation - var y: Zoomer - doAssert x.memes.load == 0 - doAssert y.dopamine.memes.load == 0 diff --git a/tests/stdlib/concurrency/tatomics_size.nim b/tests/stdlib/concurrency/tatomics_size.nim index 7b43787fb..49387c0c1 100644 --- a/tests/stdlib/concurrency/tatomics_size.nim +++ b/tests/stdlib/concurrency/tatomics_size.nim @@ -2,7 +2,6 @@ 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 deleted file mode 100644 index cf97152ba..000000000 --- a/tests/stdlib/config.nims +++ /dev/null @@ -1,4 +0,0 @@ -switch("styleCheck", "usages") -switch("styleCheck", "error") -switch("define", "nimPreviewSlimSystem") -switch("define", "nimPreviewCstringConversion") \ No newline at end of file diff --git a/tests/stdlib/mgenast.nim b/tests/stdlib/mgenast.nim deleted file mode 100644 index b0904847e..000000000 --- a/tests/stdlib/mgenast.nim +++ /dev/null @@ -1,57 +0,0 @@ -import std/genasts -import std/macros - -# Using a enum instead of, say, int, to make apparent potential bugs related to -# forgetting converting to NimNode via newLit, see bug #9607 - -type Foo* = enum kfoo0, kfoo1, kfoo2, kfoo3, kfoo4 - -proc myLocalPriv(): auto = kfoo1 -proc myLocalPriv2(): auto = kfoo1 -macro bindme2*(): untyped = - genAst: myLocalPriv() -macro bindme3*(): untyped = - ## myLocalPriv must be captured explicitly - genAstOpt({kDirtyTemplate}, myLocalPriv): myLocalPriv() - -macro bindme4*(): untyped = - ## calling this won't compile because `myLocalPriv` isn't captured - genAstOpt({kDirtyTemplate}): myLocalPriv() - -macro bindme5UseExpose*(): untyped = - genAst: myLocalPriv2() - -macro bindme5UseExposeFalse*(): untyped = - genAstOpt({kDirtyTemplate}): myLocalPriv2() - -# example from bug #7889 -from std/streams import newStringStream, readData, writeData - -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) - -macro bindme6UseExposeFalse*(): untyped = - ## with `kDirtyTemplate`, requires passing all referenced symbols - ## which can be tedious - 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) - - -proc locafun1(): auto = "in locafun1" -proc locafun2(): auto = "in locafun2" -# locafun3 in caller scope only -macro mixinExample*(): untyped = - genAst: - mixin locafun1 - (locafun1(), locafun2(), locafun3()) diff --git a/tests/stdlib/mimportutils.nim b/tests/stdlib/mimportutils.nim deleted file mode 100644 index 678d9ec02..000000000 --- a/tests/stdlib/mimportutils.nim +++ /dev/null @@ -1,38 +0,0 @@ -type - A* = object - a0*: int - ha1: float - B = object - b0*: int - hb1: float - C* = ref object - c0: int - hc1: float - D* = ptr object - d0: int - hd1: float - PA* = ref A - PtA* = ptr A - E*[T] = object - he1: int - FSub[T1, T2] = object - h3: T1 - h4: T2 - F*[T1, T2] = ref FSub[T1, T2] - G*[T] = ref E[T] - H3*[T] = object - h5: T - H2*[T] = H3[T] - H1*[T] = ref H2[T] - H*[T] = H1[T] - - Pity[T] = object - a: T - PityRef*[T] = ref Pity[T] - Hope*[T] = ref object - a: T - -type BAalias* = typeof(B.default) - # typeof is not a transparent abstraction, creates a `tyAlias` - -proc initB*(): B = B() diff --git a/tests/stdlib/mintsets.nim b/tests/stdlib/mintsets.nim index 98786e9ba..b4d9ed516 100644 --- a/tests/stdlib/mintsets.nim +++ b/tests/stdlib/mintsets.nim @@ -1,5 +1,4 @@ import std/intsets -import std/assertions proc test1*[]() = let a = initIntSet() diff --git a/tests/stdlib/nre/misc.nim b/tests/stdlib/nre/misc.nim index b7df08ee9..dbb0ecdf9 100644 --- a/tests/stdlib/nre/misc.nim +++ b/tests/stdlib/nre/misc.nim @@ -6,8 +6,8 @@ block: # Misc tests check("перевірка".replace(re"(*U)\w", "") == "") block: # empty or non-empty match - check("abc".findAll(re"|.").join(":") == ":a::b::c:") - check("abc".findAll(re".|").join(":") == "a:b:c:") + check("abc".findall(re"|.").join(":") == ":a::b::c:") + check("abc".findall(re".|").join(":") == "a:b:c:") check("abc".replace(re"|.", "x") == "xxxxxxx") check("abc".replace(re".|", "x") == "xxxx") diff --git a/tests/stdlib/t10231.nim b/tests/stdlib/t10231.nim index 3d09721aa..3b2b684f3 100644 --- a/tests/stdlib/t10231.nim +++ b/tests/stdlib/t10231.nim @@ -5,7 +5,6 @@ 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 866bdb45f..07d2ff137 100644 --- a/tests/stdlib/t14139.nim +++ b/tests/stdlib/t14139.nim @@ -1,5 +1,4 @@ -import std/heapqueue -import std/assertions +import heapqueue var test_queue : HeapQueue[int] diff --git a/tests/stdlib/t15663.nim b/tests/stdlib/t15663.nim index 8e8bfd9a8..1ad5677fd 100644 --- a/tests/stdlib/t15663.nim +++ b/tests/stdlib/t15663.nim @@ -3,7 +3,5 @@ discard """ output: "Test" """ -import std/widestrs - let ws = newWideCString("Test") -echo ws +echo ws \ No newline at end of file diff --git a/tests/stdlib/t15835.nim b/tests/stdlib/t15835.nim new file mode 100644 index 000000000..bddfa87aa --- /dev/null +++ b/tests/stdlib/t15835.nim @@ -0,0 +1,17 @@ +import json + +type + Foo = object + ii*: int + data*: JsonNode + +block: + const jt = """{"ii": 123, "data": ["some", "data"]}""" + let js = parseJson(jt) + discard js.to(Foo) + +block: + const jt = """{"ii": 123}""" + let js = parseJson(jt) + doAssertRaises(KeyError): + echo js.to(Foo) diff --git a/tests/stdlib/t21406.nim b/tests/stdlib/t21406.nim deleted file mode 100644 index 5b96227ce..000000000 --- a/tests/stdlib/t21406.nim +++ /dev/null @@ -1,5 +0,0 @@ -import std/[times, strformat] -import std/assertions - -doAssert fmt"{getTime()}" == $getTime() -doAssert fmt"{now()}" == $now() diff --git a/tests/stdlib/t7686.nim b/tests/stdlib/t7686.nim index 9902cfcb5..c174dfb51 100644 --- a/tests/stdlib/t7686.nim +++ b/tests/stdlib/t7686.nim @@ -1,5 +1,4 @@ -import std/strutils -import std/assertions +import strutils type MyEnum = enum diff --git a/tests/stdlib/t8925.nim b/tests/stdlib/t8925.nim index c55e93e73..dbf55fd88 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/misc/t9091.nim b/tests/stdlib/t9091.nim similarity index 70% rename from tests/misc/t9091.nim rename to tests/stdlib/t9091.nim index 6e7a98ca5..8419479a7 100644 --- a/tests/misc/t9091.nim +++ b/tests/stdlib/t9091.nim @@ -1,5 +1,10 @@ -# bug #9091 - +discard """ + targets: "c" + output: "test AObj" + action: "compile" + exitcode: 0 + timeout: 60.0 +""" import streams block: @@ -13,8 +18,6 @@ block: let mi = new Mine str.write(mi) - str.setPosition 0 - doAssert str.readAll == "sure" block: type @@ -24,10 +27,10 @@ block: proc foo(a: int): string = "" proc test(args: varargs[string, foo]) = - doAssert false + echo "varargs" proc test(a: AObj) = - discard + echo "test AObj" let x = AObj() test(x) diff --git a/tests/stdlib/t9710.nim b/tests/stdlib/t9710.nim new file mode 100644 index 000000000..f3ed860df --- /dev/null +++ b/tests/stdlib/t9710.nim @@ -0,0 +1,11 @@ +discard """ + cmd: "nim c -r --debugger:native --panics:on $options $file" + targets: "c" + nimout: "" + action: "run" + exitcode: 0 + timeout: 60.0 +""" + +for i in 1 || 200: + discard i diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 83a84f956..47a8d327b 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -1,12 +1,9 @@ discard """ - targets: "c js" output:'''@["3", "2", "1"] ''' """ #12928,10456 - -import std/[sequtils, algorithm, json, sugar] -import std/assertions +import sequtils, algorithm, json proc test() = try: @@ -102,10 +99,10 @@ block: doAssert binarySearch(noData, 7) == -1 let oneData = @[1] doAssert binarySearch(oneData, 1) == 0 - doAssert binarySearch(oneData, 7) == -1 + doAssert binarySearch(onedata, 7) == -1 let someData = @[1, 3, 4, 7] doAssert binarySearch(someData, 1) == 0 - doAssert binarySearch(someData, 7) == 3 + doAssert binarySearch(somedata, 7) == 3 doAssert binarySearch(someData, -1) == -1 doAssert binarySearch(someData, 5) == -1 doAssert binarySearch(someData, 13) == -1 @@ -116,159 +113,3 @@ block: doAssert binarySearch(moreData, 6) == -1 doAssert binarySearch(moreData, 4711) == 4 doAssert binarySearch(moreData, 4712) == -1 - -# merge -proc main() = - block: - var x = @[1, 7, 8, 11, 21, 33, 45, 99] - var y = @[6, 7, 9, 12, 57, 66] - - var merged: seq[int] - merged.merge(x, y) - doAssert merged.isSorted - doAssert merged == sorted(x & y) - - block: - var x = @[111, 88, 76, 56, 45, 31, 22, 19, 11, 3] - var y = @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13] - - var merged: seq[int] - merged.merge(x, y, (x, y) => -system.cmp(x, y)) - doAssert merged.isSorted((x, y) => -system.cmp(x, y)) - doAssert merged == sorted(x & y, SortOrder.Descending) - - block: - var x: seq[int] = @[] - var y = @[1] - - var merged: seq[int] - merged.merge(x, y) - doAssert merged.isSorted - doAssert merged.isSorted(SortOrder.Descending) - doAssert merged == @[1] - - block: - var x = [1, 3, 5, 5, 7] - var y: seq[int] = @[] - - var merged: seq[int] - merged.merge(x, y) - doAssert merged.isSorted - doAssert merged == @x - - block: - var x = [1, 3, 5, 5, 7] - var y: seq[int] = @[] - - var merged: seq[int] = @[1, 2, 3, 5, 6, 56, 99, 2, 34] - merged.merge(x, y) - doAssert merged == @[1, 2, 3, 5, 6, 56, 99, 2, 34, 1, 3, 5, 5, 7] - - - block: - var x: array[0, int] - var y = [1, 4, 6, 7, 9] - - var merged: seq[int] - merged.merge(x, y) - doAssert merged.isSorted - doAssert merged == @y - - block: - var x: array[0, int] - var y: array[0, int] - - var merged: seq[int] - merged.merge(x, y) - doAssert merged.isSorted - doAssert merged.len == 0 - - block: - var x: array[0, int] - var y: array[0, int] - - var merged: seq[int] = @[99, 99, 99] - merged.setLen(0) - merged.merge(x, y) - doAssert merged.isSorted - doAssert merged.len == 0 - - block: - var x: seq[int] - var y: seq[int] - - var merged: seq[int] - merged.merge(x, y) - doAssert merged.isSorted - doAssert merged.len == 0 - - block: - type - Record = object - id: int - - proc r(id: int): Record = - Record(id: id) - - proc cmp(x, y: Record): int = - if x.id == y.id: return 0 - if x.id < y.id: return -1 - result = 1 - - var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)] - var y = @[r(4), r(7), r(12), r(13), r(77), r(99)] - - var merged: seq[Record] = @[] - merged.merge(x, y, cmp) - doAssert merged.isSorted(cmp) - doAssert merged.len == 12 - - block: - type - Record = object - id: int - - proc r(id: int): Record = - Record(id: id) - - proc ascendingCmp(x, y: Record): int = - if x.id == y.id: return 0 - if x.id < y.id: return -1 - result = 1 - - proc descendingCmp(x, y: Record): int = - if x.id == y.id: return 0 - if x.id < y.id: return 1 - result = -1 - - var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)] - var y = @[r(4), r(7), r(12), r(13), r(77), r(99)] - - var merged: seq[Record] - merged.setLen(0) - merged.merge(x, y, ascendingCmp) - doAssert merged.isSorted(ascendingCmp) - doAssert merged == sorted(x & y, ascendingCmp) - - reverse(x) - reverse(y) - - merged.setLen(0) - merged.merge(x, y, descendingCmp) - doAssert merged.isSorted(descendingCmp) - doAssert merged == sorted(x & y, ascendingCmp, SortOrder.Descending) - - reverse(x) - reverse(y) - merged.setLen(0) - merged.merge(x, y, proc (x, y: Record): int = -descendingCmp(x, y)) - doAssert merged.isSorted(proc (x, y: Record): int = -descendingCmp(x, y)) - doAssert merged == sorted(x & y, ascendingCmp) - - - var x: seq[(int, int)] - x.merge([(1,1)], [(1,2)], (a,b) => a[0] - b[0]) - doAssert x == @[(1, 1), (1, 2)] - -static: main() -main() diff --git a/tests/stdlib/tarithmetics.nim b/tests/stdlib/tarithmetics.nim deleted file mode 100644 index a69334e71..000000000 --- a/tests/stdlib/tarithmetics.nim +++ /dev/null @@ -1,49 +0,0 @@ -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 - -template main = - # put all arithmetic tests - - block tshr: - block: # Signed types - let - a1 = -3 - a2 = -2 - b1 = -4'i8 - b2 = 1'i8 - c1 = -5'i16 - c2 = 1'i16 - d1 = -7i32 - d2 = 1'i32 - e1 = -9'i64 - e2 = 1'i64 - doAssert a1 shr a2 == -1 - doAssert b1 shr b2 == -2 - doAssert c1 shr c2 == -3 - doAssert d1 shr d2 == -4 - doAssert e1 shr e2 == -5 - - block: # Unsigned types - let - a1 = 3'u - a2 = 2'u - b1 = 2'u8 - b2 = 1'u8 - c1 = 5'u16 - c2 = 1'u16 - d1 = 6'u32 - d2 = 1'u32 - e1 = 8'u64 - e2 = 1'u64 - doAssert a1 shr a2 == 0 - doAssert b1 shr b2 == 1 - doAssert c1 shr c2 == 2 - doAssert d1 shr d2 == 3 - doAssert e1 shr e2 == 4 - -static: main() -main() diff --git a/tests/stdlib/tasynchttpserver.nim b/tests/stdlib/tasynchttpserver.nim index 8cf0d0ced..77ad7a071 100644 --- a/tests/stdlib/tasynchttpserver.nim +++ b/tests/stdlib/tasynchttpserver.nim @@ -7,7 +7,6 @@ discard """ import strutils from net import TimeoutError -import std/assertions import httpclient, asynchttpserver, asyncdispatch, asyncfutures @@ -40,7 +39,7 @@ proc test200() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == $Http200) + doAssert(response.status == Http200) doAssert(body == "Hello World, 200") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "16") @@ -61,7 +60,7 @@ proc test404() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == $Http404) + doAssert(response.status == Http404) doAssert(body == "Hello World, 404") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "16") @@ -82,7 +81,7 @@ proc testCustomEmptyHeaders() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == $Http200) + doAssert(response.status == Http200) doAssert(body == "Hello World, 200") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "16") @@ -105,16 +104,16 @@ proc testCustomContentLength() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == $Http200) + doAssert(response.status == Http200) doAssert(body == "") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "0") runTest(handler, request, test) -waitFor(test200()) -waitFor(test404()) -waitFor(testCustomEmptyHeaders()) -waitFor(testCustomContentLength()) +waitfor(test200()) +waitfor(test404()) +waitfor(testCustomEmptyHeaders()) +waitfor(testCustomContentLength()) echo "OK" diff --git a/tests/stdlib/tasynchttpserver_transferencoding.nim b/tests/stdlib/tasynchttpserver_transferencoding.nim deleted file mode 100644 index dae87be82..000000000 --- a/tests/stdlib/tasynchttpserver_transferencoding.nim +++ /dev/null @@ -1,91 +0,0 @@ -discard """ - matrix: "--gc:arc --threads:on; --gc:arc --threads:on -d:danger; --threads:on" - disabled: "freebsd" -""" - -import httpclient, asynchttpserver, asyncdispatch, asyncfutures -import net - -import std/asyncnet -import std/nativesockets -import std/assertions - -const postBegin = """ -POST / HTTP/1.1 -Transfer-Encoding:chunked - -""" - -template genTest(input, expected: string) = - proc handler(request: Request, future: Future[bool]) {.async, gcsafe.} = - doAssert(request.body == expected) - doAssert(request.headers.hasKey("Transfer-Encoding")) - doAssert(not request.headers.hasKey("Content-Length")) - future.complete(true) - await request.respond(Http200, "Good") - - proc sendData(data: string, port: Port) {.async.} = - var socket = newSocket() - defer: socket.close() - - socket.connect("127.0.0.1", port) - socket.send(data) - - proc runTest(): Future[bool] {.async.} = - var handlerFuture = newFuture[bool]("runTest") - let data = postBegin & input - let server = newAsyncHttpServer() - server.listen(Port(0)) - - proc wrapper(request: Request): Future[void] {.gcsafe, closure.} = - handler(request, handlerFuture) - - asyncCheck sendData(data, server.getPort) - asyncCheck server.acceptRequest(wrapper) - doAssert await handlerFuture - - server.close() - return true - - doAssert waitFor runTest() - -block: - const expected = "hello=world" - const input = ("b\r\n" & - "hello=world\r\n" & - "0\r\n" & - "\r\n") - genTest(input, expected) -block: - const expected = "hello encoding" - const input = ("e\r\n" & - "hello encoding\r\n" & - "0\r\n" & - "\r\n") - genTest(input, expected) -block: - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding - const expected = "MozillaDeveloperNetwork" - const input = ("7\r\n" & - "Mozilla\r\n" & - "9\r\n" & - "Developer\r\n" & - "7\r\n" & - "Network\r\n" & - "0\r\n" & - "\r\n") - genTest(input, expected) -block: - # https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example - const expected = "Wikipedia in \r\n\r\nchunks." - const input = ("4\r\n" & - "Wiki\r\n" & - "6\r\n" & - "pedia \r\n" & - "E\r\n" & - "in \r\n" & - "\r\n" & - "chunks.\r\n" & - "0\r\n" & - "\r\n") - genTest(input, expected) diff --git a/tests/stdlib/tbase64.nim b/tests/stdlib/tbase64.nim index 60fa3865d..19b126437 100644 --- a/tests/stdlib/tbase64.nim +++ b/tests/stdlib/tbase64.nim @@ -1,11 +1,14 @@ discard """ - targets: "c js" + output: '''YQ==''' + nimout: '''YQ==''' """ -import std/assertions -import std/base64 +import base64 -template main() = - doAssert encode("a") == "YQ==" +import base64 +static: echo encode("a") +echo encode("a") + +proc main() = doAssert encode("Hello World") == "SGVsbG8gV29ybGQ=" doAssert encode("leasure.") == "bGVhc3VyZS4=" doAssert encode("easure.") == "ZWFzdXJlLg==" @@ -27,15 +30,19 @@ template main() = const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.", "asure.", longText, testInputExpandsTo76, testInputExpands] - doAssert encodeMime("foobarbaz", lineLen=4) == "Zm9v\r\nYmFy\r\nYmF6" + doAssert encodeMIME("foobarbaz", lineLen=4) == "Zm9v\r\nYmFy\r\nYmF6" doAssert decode("Zm9v\r\nYmFy\r\nYmF6") == "foobarbaz" for t in items(tests): doAssert decode(encode(t)) == t - doAssert decode(encodeMime(t, lineLen=40)) == t - doAssert decode(encodeMime(t, lineLen=76)) == t + doAssert decode(encodeMIME(t, lineLen=40)) == t + doAssert decode(encodeMIME(t, lineLen=76)) == t - doAssertRaises(ValueError): discard decode("SGVsbG\x008gV29ybGQ=") + const invalid = "SGVsbG\x008gV29ybGQ=" + try: + doAssert decode(invalid) == "will throw error" + except ValueError: + discard block base64urlSafe: doAssert encode("c\xf7>", safe = true) == "Y_c-" @@ -52,5 +59,4 @@ template main() = doAssert encode("", safe = true) == "" doAssert encode("the quick brown dog jumps over the lazy fox", safe = true) == "dGhlIHF1aWNrIGJyb3duIGRvZyBqdW1wcyBvdmVyIHRoZSBsYXp5IGZveA==" -static: main() main() diff --git a/tests/stdlib/tbitops.nim b/tests/stdlib/tbitops.nim index c90943a93..2baa1c718 100644 --- a/tests/stdlib/tbitops.nim +++ b/tests/stdlib/tbitops.nim @@ -2,12 +2,12 @@ discard """ nimout: "OK" output: ''' OK +OK ''' """ import bitops -import std/assertions -proc main() = +proc main1() = const U8 = 0b0011_0010'u8 const I8 = 0b0011_0010'i8 const U16 = 0b00100111_00101000'u16 @@ -21,120 +21,120 @@ proc main() = const U64C = 0b00101010_11110101_10001111_00101000_00000100_00000000_00000100_00000000'u64 const I64C = 0b00101010_11110101_10001111_00101000_00000100_00000000_00000100_00000000'i64 - doAssert (U8 and U8) == bitand(U8,U8) - doAssert (I8 and I8) == bitand(I8,I8) - doAssert (U16 and U16) == bitand(U16,U16) - doAssert (I16 and I16) == bitand(I16,I16) - doAssert (U32 and U32) == bitand(U32,U32) - doAssert (I32 and I32) == bitand(I32,I32) - doAssert (U64A and U64B) == bitand(U64A,U64B) - doAssert (I64A and I64B) == bitand(I64A,I64B) - doAssert (U64A and U64B and U64C) == bitand(U64A,U64B,U64C) - doAssert (I64A and I64B and I64C) == bitand(I64A,I64B,I64C) + doAssert( (U8 and U8) == bitand(U8,U8) ) + doAssert( (I8 and I8) == bitand(I8,I8) ) + doAssert( (U16 and U16) == bitand(U16,U16) ) + doAssert( (I16 and I16) == bitand(I16,I16) ) + doAssert( (U32 and U32) == bitand(U32,U32) ) + doAssert( (I32 and I32) == bitand(I32,I32) ) + doAssert( (U64A and U64B) == bitand(U64A,U64B) ) + doAssert( (I64A and I64B) == bitand(I64A,I64B) ) + doAssert( (U64A and U64B and U64C) == bitand(U64A,U64B,U64C) ) + doAssert( (I64A and I64B and I64C) == bitand(I64A,I64B,I64C) ) - doAssert (U8 or U8) == bitor(U8,U8) - doAssert (I8 or I8) == bitor(I8,I8) - doAssert (U16 or U16) == bitor(U16,U16) - doAssert (I16 or I16) == bitor(I16,I16) - doAssert (U32 or U32) == bitor(U32,U32) - doAssert (I32 or I32) == bitor(I32,I32) - doAssert (U64A or U64B) == bitor(U64A,U64B) - doAssert (I64A or I64B) == bitor(I64A,I64B) - doAssert (U64A or U64B or U64C) == bitor(U64A,U64B,U64C) - doAssert (I64A or I64B or I64C) == bitor(I64A,I64B,I64C) + doAssert( (U8 or U8) == bitor(U8,U8) ) + doAssert( (I8 or I8) == bitor(I8,I8) ) + doAssert( (U16 or U16) == bitor(U16,U16) ) + doAssert( (I16 or I16) == bitor(I16,I16) ) + doAssert( (U32 or U32) == bitor(U32,U32) ) + doAssert( (I32 or I32) == bitor(I32,I32) ) + doAssert( (U64A or U64B) == bitor(U64A,U64B) ) + doAssert( (I64A or I64B) == bitor(I64A,I64B) ) + doAssert( (U64A or U64B or U64C) == bitor(U64A,U64B,U64C) ) + doAssert( (I64A or I64B or I64C) == bitor(I64A,I64B,I64C) ) - doAssert (U8 xor U8) == bitxor(U8,U8) - doAssert (I8 xor I8) == bitxor(I8,I8) - doAssert (U16 xor U16) == bitxor(U16,U16) - doAssert (I16 xor I16) == bitxor(I16,I16) - doAssert (U32 xor U32) == bitxor(U32,U32) - doAssert (I32 xor I32) == bitxor(I32,I32) - doAssert (U64A xor U64B) == bitxor(U64A,U64B) - doAssert (I64A xor I64B) == bitxor(I64A,I64B) - doAssert (U64A xor U64B xor U64C) == bitxor(U64A,U64B,U64C) - doAssert (I64A xor I64B xor I64C) == bitxor(I64A,I64B,I64C) + doAssert( (U8 xor U8) == bitxor(U8,U8) ) + doAssert( (I8 xor I8) == bitxor(I8,I8) ) + doAssert( (U16 xor U16) == bitxor(U16,U16) ) + doAssert( (I16 xor I16) == bitxor(I16,I16) ) + doAssert( (U32 xor U32) == bitxor(U32,U32) ) + doAssert( (I32 xor I32) == bitxor(I32,I32) ) + doAssert( (U64A xor U64B) == bitxor(U64A,U64B) ) + doAssert( (I64A xor I64B) == bitxor(I64A,I64B) ) + doAssert( (U64A xor U64B xor U64C) == bitxor(U64A,U64B,U64C) ) + doAssert( (I64A xor I64B xor I64C) == bitxor(I64A,I64B,I64C) ) - doAssert not(U8) == bitnot(U8) - doAssert not(I8) == bitnot(I8) - doAssert not(U16) == bitnot(U16) - doAssert not(I16) == bitnot(I16) - doAssert not(U32) == bitnot(U32) - doAssert not(I32) == bitnot(I32) - doAssert not(U64A) == bitnot(U64A) - doAssert not(I64A) == bitnot(I64A) + doAssert( not(U8) == bitnot(U8) ) + doAssert( not(I8) == bitnot(I8) ) + doAssert( not(U16) == bitnot(U16) ) + doAssert( not(I16) == bitnot(I16) ) + doAssert( not(U32) == bitnot(U32) ) + doAssert( not(I32) == bitnot(I32) ) + doAssert( not(U64A) == bitnot(U64A) ) + doAssert( not(I64A) == bitnot(I64A) ) - doAssert U64A.fastLog2 == 62 - doAssert I64A.fastLog2 == 62 - doAssert U64A.countLeadingZeroBits == 1 - doAssert I64A.countLeadingZeroBits == 1 - doAssert U64A.countTrailingZeroBits == 0 - doAssert I64A.countTrailingZeroBits == 0 - doAssert U64A.firstSetBit == 1 - doAssert I64A.firstSetBit == 1 - doAssert U64A.parityBits == 1 - doAssert I64A.parityBits == 1 - doAssert U64A.countSetBits == 29 - doAssert I64A.countSetBits == 29 - doAssert U64A.rotateLeftBits(37) == 0b00101001_00001111_01000010_00101000_10000111_11101111_10010001_01010011'u64 - doAssert U64A.rotateRightBits(37) == 0b01010100_11001010_01000011_11010000_10001010_00100001_11111011_11100100'u64 + doAssert( U64A.fastLog2 == 62) + doAssert( I64A.fastLog2 == 62) + doAssert( U64A.countLeadingZeroBits == 1) + doAssert( I64A.countLeadingZeroBits == 1) + doAssert( U64A.countTrailingZeroBits == 0) + doAssert( I64A.countTrailingZeroBits == 0) + doAssert( U64A.firstSetBit == 1) + doAssert( I64A.firstSetBit == 1) + doAssert( U64A.parityBits == 1) + doAssert( I64A.parityBits == 1) + doAssert( U64A.countSetBits == 29) + doAssert( I64A.countSetBits == 29) + doAssert( U64A.rotateLeftBits(37) == 0b00101001_00001111_01000010_00101000_10000111_11101111_10010001_01010011'u64) + doAssert( U64A.rotateRightBits(37) == 0b01010100_11001010_01000011_11010000_10001010_00100001_11111011_11100100'u64) - doAssert U64B.firstSetBit == 36 - doAssert I64B.firstSetBit == 36 + doAssert( U64B.firstSetBit == 36) + doAssert( I64B.firstSetBit == 36) - doAssert U32.fastLog2 == 31 - doAssert I32.fastLog2 == 31 - doAssert U32.countLeadingZeroBits == 0 - doAssert I32.countLeadingZeroBits == 0 - doAssert U32.countTrailingZeroBits == 4 - doAssert I32.countTrailingZeroBits == 4 - doAssert U32.firstSetBit == 5 - doAssert I32.firstSetBit == 5 - doAssert U32.parityBits == 0 - doAssert I32.parityBits == 0 - doAssert U32.countSetBits == 16 - doAssert I32.countSetBits == 16 - doAssert U32.rotateLeftBits(21) == 0b01001010_00011010_10110011_10011011'u32 - doAssert U32.rotateRightBits(21) == 0b11100110_11010010_10000110_10101100'u32 + doAssert( U32.fastLog2 == 31) + doAssert( I32.fastLog2 == 31) + doAssert( U32.countLeadingZeroBits == 0) + doAssert( I32.countLeadingZeroBits == 0) + doAssert( U32.countTrailingZeroBits == 4) + doAssert( I32.countTrailingZeroBits == 4) + doAssert( U32.firstSetBit == 5) + doAssert( I32.firstSetBit == 5) + doAssert( U32.parityBits == 0) + doAssert( I32.parityBits == 0) + doAssert( U32.countSetBits == 16) + doAssert( I32.countSetBits == 16) + doAssert( U32.rotateLeftBits(21) == 0b01001010_00011010_10110011_10011011'u32) + doAssert( U32.rotateRightBits(21) == 0b11100110_11010010_10000110_10101100'u32) - doAssert U16.fastLog2 == 13 - doAssert I16.fastLog2 == 13 - doAssert U16.countLeadingZeroBits == 2 - doAssert I16.countLeadingZeroBits == 2 - doAssert U16.countTrailingZeroBits == 3 - doAssert I16.countTrailingZeroBits == 3 - doAssert U16.firstSetBit == 4 - doAssert I16.firstSetBit == 4 - doAssert U16.parityBits == 0 - doAssert I16.parityBits == 0 - doAssert U16.countSetBits == 6 - doAssert I16.countSetBits == 6 - doAssert U16.rotateLeftBits(12) == 0b10000010_01110010'u16 - doAssert U16.rotateRightBits(12) == 0b01110010_10000010'u16 + doAssert( U16.fastLog2 == 13) + doAssert( I16.fastLog2 == 13) + doAssert( U16.countLeadingZeroBits == 2) + doAssert( I16.countLeadingZeroBits == 2) + doAssert( U16.countTrailingZeroBits == 3) + doAssert( I16.countTrailingZeroBits == 3) + doAssert( U16.firstSetBit == 4) + doAssert( I16.firstSetBit == 4) + doAssert( U16.parityBits == 0) + doAssert( I16.parityBits == 0) + doAssert( U16.countSetBits == 6) + doAssert( I16.countSetBits == 6) + doAssert( U16.rotateLeftBits(12) == 0b10000010_01110010'u16) + doAssert( U16.rotateRightBits(12) == 0b01110010_10000010'u16) - doAssert U8.fastLog2 == 5 - doAssert I8.fastLog2 == 5 - doAssert U8.countLeadingZeroBits == 2 - doAssert I8.countLeadingZeroBits == 2 - doAssert U8.countTrailingZeroBits == 1 - doAssert I8.countTrailingZeroBits == 1 - doAssert U8.firstSetBit == 2 - doAssert I8.firstSetBit == 2 - doAssert U8.parityBits == 1 - doAssert I8.parityBits == 1 - doAssert U8.countSetBits == 3 - doAssert I8.countSetBits == 3 - doAssert U8.rotateLeftBits(3) == 0b10010001'u8 - doAssert U8.rotateRightBits(3) == 0b0100_0110'u8 + doAssert( U8.fastLog2 == 5) + doAssert( I8.fastLog2 == 5) + doAssert( U8.countLeadingZeroBits == 2) + doAssert( I8.countLeadingZeroBits == 2) + doAssert( U8.countTrailingZeroBits == 1) + doAssert( I8.countTrailingZeroBits == 1) + doAssert( U8.firstSetBit == 2) + doAssert( I8.firstSetBit == 2) + doAssert( U8.parityBits == 1) + doAssert( I8.parityBits == 1) + doAssert( U8.countSetBits == 3) + doAssert( I8.countSetBits == 3) + doAssert( U8.rotateLeftBits(3) == 0b10010001'u8) + doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8) template test_undefined_impl(ffunc: untyped; expected: int; is_static: bool) = - doAssert ffunc(0'u8) == expected - doAssert ffunc(0'i8) == expected - doAssert ffunc(0'u16) == expected - doAssert ffunc(0'i16) == expected - doAssert ffunc(0'u32) == expected - doAssert ffunc(0'i32) == expected - doAssert ffunc(0'u64) == expected - doAssert ffunc(0'i64) == expected + doAssert( ffunc(0'u8) == expected) + doAssert( ffunc(0'i8) == expected) + doAssert( ffunc(0'u16) == expected) + doAssert( ffunc(0'i16) == expected) + doAssert( ffunc(0'u32) == expected) + doAssert( ffunc(0'i32) == expected) + doAssert( ffunc(0'u64) == expected) + doAssert( ffunc(0'i64) == expected) template test_undefined(ffunc: untyped; expected: int) = test_undefined_impl(ffunc, expected, false) @@ -151,106 +151,106 @@ proc main() = test_undefined(fastLog2, -1) # check for undefined behavior with rotate by zero. - doAssert U8.rotateLeftBits(0) == U8 - doAssert U8.rotateRightBits(0) == U8 - doAssert U16.rotateLeftBits(0) == U16 - doAssert U16.rotateRightBits(0) == U16 - doAssert U32.rotateLeftBits(0) == U32 - doAssert U32.rotateRightBits(0) == U32 - doAssert U64A.rotateLeftBits(0) == U64A - doAssert U64A.rotateRightBits(0) == U64A + doAssert( U8.rotateLeftBits(0) == U8) + doAssert( U8.rotateRightBits(0) == U8) + doAssert( U16.rotateLeftBits(0) == U16) + doAssert( U16.rotateRightBits(0) == U16) + doAssert( U32.rotateLeftBits(0) == U32) + doAssert( U32.rotateRightBits(0) == U32) + doAssert( U64A.rotateLeftBits(0) == U64A) + doAssert( U64A.rotateRightBits(0) == U64A) # check for undefined behavior with rotate by integer width. - doAssert U8.rotateLeftBits(8) == U8 - doAssert U8.rotateRightBits(8) == U8 - doAssert U16.rotateLeftBits(16) == U16 - doAssert U16.rotateRightBits(16) == U16 - doAssert U32.rotateLeftBits(32) == U32 - doAssert U32.rotateRightBits(32) == U32 - doAssert U64A.rotateLeftBits(64) == U64A - doAssert U64A.rotateRightBits(64) == U64A + doAssert( U8.rotateLeftBits(8) == U8) + doAssert( U8.rotateRightBits(8) == U8) + doAssert( U16.rotateLeftBits(16) == U16) + doAssert( U16.rotateRightBits(16) == U16) + doAssert( U32.rotateLeftBits(32) == U32) + doAssert( U32.rotateRightBits(32) == U32) + doAssert( U64A.rotateLeftBits(64) == U64A) + doAssert( U64A.rotateRightBits(64) == U64A) block: # basic mask operations (mutating) var v: uint8 v.setMask(0b1100_0000) v.setMask(0b0000_1100) - doAssert v == 0b1100_1100 + doAssert(v == 0b1100_1100) v.flipMask(0b0101_0101) - doAssert v == 0b1001_1001 + doAssert(v == 0b1001_1001) v.clearMask(0b1000_1000) - doAssert v == 0b0001_0001 + doAssert(v == 0b0001_0001) v.clearMask(0b0001_0001) - doAssert v == 0b0000_0000 + doAssert(v == 0b0000_0000) v.setMask(0b0001_1110) - doAssert v == 0b0001_1110 + doAssert(v == 0b0001_1110) v.mask(0b0101_0100) - doAssert v == 0b0001_0100 + doAssert(v == 0b0001_0100) block: # basic mask operations (non-mutating) let v = 0b1100_0000'u8 - doAssert v.masked(0b0000_1100) == 0b0000_0000 - doAssert v.masked(0b1000_1100) == 0b1000_0000 - doAssert v.setMasked(0b0000_1100) == 0b1100_1100 - doAssert v.setMasked(0b1000_1110) == 0b1100_1110 - doAssert v.flipMasked(0b1100_1000) == 0b0000_1000 - doAssert v.flipMasked(0b0000_1100) == 0b1100_1100 + doAssert(v.masked(0b0000_1100) == 0b0000_0000) + doAssert(v.masked(0b1000_1100) == 0b1000_0000) + doAssert(v.setMasked(0b0000_1100) == 0b1100_1100) + doAssert(v.setMasked(0b1000_1110) == 0b1100_1110) + doAssert(v.flipMasked(0b1100_1000) == 0b0000_1000) + doAssert(v.flipMasked(0b0000_1100) == 0b1100_1100) let t = 0b1100_0110'u8 - doAssert t.clearMasked(0b0100_1100) == 0b1000_0010 - doAssert t.clearMasked(0b1100_0000) == 0b0000_0110 + doAssert(t.clearMasked(0b0100_1100) == 0b1000_0010) + doAssert(t.clearMasked(0b1100_0000) == 0b0000_0110) block: # basic bitslice opeartions let a = 0b1111_1011'u8 - doAssert a.bitsliced(0 .. 3) == 0b1011 - doAssert a.bitsliced(2 .. 3) == 0b10 - doAssert a.bitsliced(4 .. 7) == 0b1111 + doAssert(a.bitsliced(0 .. 3) == 0b1011) + doAssert(a.bitsliced(2 .. 3) == 0b10) + doAssert(a.bitsliced(4 .. 7) == 0b1111) # same thing, but with exclusive ranges. - doAssert a.bitsliced(0 ..< 4) == 0b1011 - doAssert a.bitsliced(2 ..< 4) == 0b10 - doAssert a.bitsliced(4 ..< 8) == 0b1111 + doAssert(a.bitsliced(0 ..< 4) == 0b1011) + doAssert(a.bitsliced(2 ..< 4) == 0b10) + doAssert(a.bitsliced(4 ..< 8) == 0b1111) # mutating var b = 0b1111_1011'u8 b.bitslice(1 .. 3) - doAssert b == 0b101 + doAssert(b == 0b101) # loop test: let c = 0b1111_1111'u8 for i in 0 .. 7: - doAssert c.bitsliced(i .. 7) == c shr i + doAssert(c.bitsliced(i .. 7) == c shr i) block: # bitslice versions of mask operations (mutating) var a = 0b1100_1100'u8 let b = toMask[uint8](2 .. 3) a.mask(b) - doAssert a == 0b0000_1100 + doAssert(a == 0b0000_1100) a.setMask(4 .. 7) - doAssert a == 0b1111_1100 + doAssert(a == 0b1111_1100) a.flipMask(1 .. 3) - doAssert a == 0b1111_0010 + doAssert(a == 0b1111_0010) a.flipMask(2 .. 4) - doAssert a == 0b1110_1110 + doAssert(a == 0b1110_1110) a.clearMask(2 .. 4) - doAssert a == 0b1110_0010 + doAssert(a == 0b1110_0010) a.mask(0 .. 3) - doAssert a == 0b0000_0010 + doAssert(a == 0b0000_0010) # composition of mask from slices: let c = bitor(toMask[uint8](2 .. 3), toMask[uint8](5 .. 7)) - doAssert c == 0b1110_1100'u8 + doAssert(c == 0b1110_1100'u8) block: # bitslice versions of mask operations (non-mutating) let a = 0b1100_1100'u8 - doAssert a.masked(toMask[uint8](2 .. 3)) == 0b0000_1100 - doAssert a.masked(2 .. 3) == 0b0000_1100 - doAssert a.setMasked(0 .. 3) == 0b1100_1111 - doAssert a.setMasked(3 .. 4) == 0b1101_1100 - doAssert a.flipMasked(0 .. 3) == 0b1100_0011 - doAssert a.flipMasked(0 .. 7) == 0b0011_0011 - doAssert a.flipMasked(2 .. 3) == 0b1100_0000 - doAssert a.clearMasked(2 .. 3) == 0b1100_0000 - doAssert a.clearMasked(3 .. 6) == 0b1000_0100 + doAssert(a.masked(toMask[uint8](2 .. 3)) == 0b0000_1100) + doAssert(a.masked(2 .. 3) == 0b0000_1100) + doAssert(a.setMasked(0 .. 3) == 0b1100_1111) + doAssert(a.setMasked(3 .. 4) == 0b1101_1100) + doAssert(a.flipMasked(0 .. 3) == 0b1100_0011) + doAssert(a.flipMasked(0 .. 7) == 0b0011_0011) + doAssert(a.flipMasked(2 .. 3) == 0b1100_0000) + doAssert(a.clearMasked(2 .. 3) == 0b1100_0000) + doAssert(a.clearMasked(3 .. 6) == 0b1000_0100) block: # single bit operations var v: uint8 @@ -264,8 +264,8 @@ proc main() = doAssert v == 0b1000_0010 v.flipBit(1) doAssert v == 0b1000_0000 - doAssert v.testBit(7) - doAssert not v.testBit(6) + doAssert v.testbit(7) + doAssert not v.testbit(6) block: # multi bit operations var v: uint8 @@ -287,7 +287,7 @@ proc main() = block: proc testReverseBitsInvo(x: SomeUnsignedInt) = - doAssert reverseBits(reverseBits(x)) == x + doAssert(reverseBits(reverseBits(x)) == x) proc testReverseBitsPerType(x, reversed: uint64) = doAssert reverseBits(x) == reversed @@ -329,9 +329,6 @@ proc main() = echo "OK" - # bug #7587 - doAssert popcount(0b11111111'i8) == 8 - block: # not ready for vm because exception is compile error try: var v: uint32 @@ -344,7 +341,172 @@ block: # not ready for vm because exception is compile error doAssert false -main() +main1() static: # test everything on vm as well - main() + main1() + + + +proc main2() = + const U8 = 0b0011_0010'u8 + const I8 = 0b0011_0010'i8 + const U16 = 0b00100111_00101000'u16 + const I16 = 0b00100111_00101000'i16 + const U32 = 0b11010101_10011100_11011010_01010000'u32 + const I32 = 0b11010101_10011100_11011010_01010000'i32 + const U64A = 0b01000100_00111111_01111100_10001010_10011001_01001000_01111010_00010001'u64 + const I64A = 0b01000100_00111111_01111100_10001010_10011001_01001000_01111010_00010001'i64 + const U64B = 0b00110010_11011101_10001111_00101000_00000000_00000000_00000000_00000000'u64 + const I64B = 0b00110010_11011101_10001111_00101000_00000000_00000000_00000000_00000000'i64 + + doAssert( U64A.fastLog2 == 62) + doAssert( I64A.fastLog2 == 62) + doAssert( U64A.countLeadingZeroBits == 1) + doAssert( I64A.countLeadingZeroBits == 1) + doAssert( U64A.countTrailingZeroBits == 0) + doAssert( I64A.countTrailingZeroBits == 0) + doAssert( U64A.firstSetBit == 1) + doAssert( I64A.firstSetBit == 1) + doAssert( U64A.parityBits == 1) + doAssert( I64A.parityBits == 1) + doAssert( U64A.countSetBits == 29) + doAssert( I64A.countSetBits == 29) + doAssert( U64A.rotateLeftBits(37) == 0b00101001_00001111_01000010_00101000_10000111_11101111_10010001_01010011'u64) + doAssert( U64A.rotateRightBits(37) == 0b01010100_11001010_01000011_11010000_10001010_00100001_11111011_11100100'u64) + + doAssert( U64B.firstSetBit == 36) + doAssert( I64B.firstSetBit == 36) + + doAssert( U32.fastLog2 == 31) + doAssert( I32.fastLog2 == 31) + doAssert( U32.countLeadingZeroBits == 0) + doAssert( I32.countLeadingZeroBits == 0) + doAssert( U32.countTrailingZeroBits == 4) + doAssert( I32.countTrailingZeroBits == 4) + doAssert( U32.firstSetBit == 5) + doAssert( I32.firstSetBit == 5) + doAssert( U32.parityBits == 0) + doAssert( I32.parityBits == 0) + doAssert( U32.countSetBits == 16) + doAssert( I32.countSetBits == 16) + doAssert( U32.rotateLeftBits(21) == 0b01001010_00011010_10110011_10011011'u32) + doAssert( U32.rotateRightBits(21) == 0b11100110_11010010_10000110_10101100'u32) + + doAssert( U16.fastLog2 == 13) + doAssert( I16.fastLog2 == 13) + doAssert( U16.countLeadingZeroBits == 2) + doAssert( I16.countLeadingZeroBits == 2) + doAssert( U16.countTrailingZeroBits == 3) + doAssert( I16.countTrailingZeroBits == 3) + doAssert( U16.firstSetBit == 4) + doAssert( I16.firstSetBit == 4) + doAssert( U16.parityBits == 0) + doAssert( I16.parityBits == 0) + doAssert( U16.countSetBits == 6) + doAssert( I16.countSetBits == 6) + doAssert( U16.rotateLeftBits(12) == 0b10000010_01110010'u16) + doAssert( U16.rotateRightBits(12) == 0b01110010_10000010'u16) + + doAssert( U8.fastLog2 == 5) + doAssert( I8.fastLog2 == 5) + doAssert( U8.countLeadingZeroBits == 2) + doAssert( I8.countLeadingZeroBits == 2) + doAssert( U8.countTrailingZeroBits == 1) + doAssert( I8.countTrailingZeroBits == 1) + doAssert( U8.firstSetBit == 2) + doAssert( I8.firstSetBit == 2) + doAssert( U8.parityBits == 1) + doAssert( I8.parityBits == 1) + doAssert( U8.countSetBits == 3) + doAssert( I8.countSetBits == 3) + doAssert( U8.rotateLeftBits(3) == 0b10010001'u8) + doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8) + + static : + # test bitopts at compile time with vm + doAssert( U8.fastLog2 == 5) + doAssert( I8.fastLog2 == 5) + doAssert( U8.countLeadingZeroBits == 2) + doAssert( I8.countLeadingZeroBits == 2) + doAssert( U8.countTrailingZeroBits == 1) + doAssert( I8.countTrailingZeroBits == 1) + doAssert( U8.firstSetBit == 2) + doAssert( I8.firstSetBit == 2) + doAssert( U8.parityBits == 1) + doAssert( I8.parityBits == 1) + doAssert( U8.countSetBits == 3) + doAssert( I8.countSetBits == 3) + doAssert( U8.rotateLeftBits(3) == 0b10010001'u8) + doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8) + + + + template test_undefined_impl(ffunc: untyped; expected: int; is_static: bool) = + doAssert( ffunc(0'u8) == expected) + doAssert( ffunc(0'i8) == expected) + doAssert( ffunc(0'u16) == expected) + doAssert( ffunc(0'i16) == expected) + doAssert( ffunc(0'u32) == expected) + doAssert( ffunc(0'i32) == expected) + doAssert( ffunc(0'u64) == expected) + doAssert( ffunc(0'i64) == expected) + + template test_undefined(ffunc: untyped; expected: int) = + test_undefined_impl(ffunc, expected, false) + static: + test_undefined_impl(ffunc, expected, true) + + when defined(noUndefinedBitOpts): + # check for undefined behavior with zero. + test_undefined(countSetBits, 0) + test_undefined(parityBits, 0) + test_undefined(firstSetBit, 0) + test_undefined(countLeadingZeroBits, 0) + test_undefined(countTrailingZeroBits, 0) + test_undefined(fastLog2, -1) + + # check for undefined behavior with rotate by zero. + doAssert( U8.rotateLeftBits(0) == U8) + doAssert( U8.rotateRightBits(0) == U8) + doAssert( U16.rotateLeftBits(0) == U16) + doAssert( U16.rotateRightBits(0) == U16) + doAssert( U32.rotateLeftBits(0) == U32) + doAssert( U32.rotateRightBits(0) == U32) + doAssert( U64A.rotateLeftBits(0) == U64A) + doAssert( U64A.rotateRightBits(0) == U64A) + + # check for undefined behavior with rotate by integer width. + doAssert( U8.rotateLeftBits(8) == U8) + doAssert( U8.rotateRightBits(8) == U8) + doAssert( U16.rotateLeftBits(16) == U16) + doAssert( U16.rotateRightBits(16) == U16) + doAssert( U32.rotateLeftBits(32) == U32) + doAssert( U32.rotateRightBits(32) == U32) + doAssert( U64A.rotateLeftBits(64) == U64A) + doAssert( U64A.rotateRightBits(64) == U64A) + + static: # check for undefined behavior with rotate by zero. + doAssert( U8.rotateLeftBits(0) == U8) + doAssert( U8.rotateRightBits(0) == U8) + doAssert( U16.rotateLeftBits(0) == U16) + doAssert( U16.rotateRightBits(0) == U16) + doAssert( U32.rotateLeftBits(0) == U32) + doAssert( U32.rotateRightBits(0) == U32) + doAssert( U64A.rotateLeftBits(0) == U64A) + doAssert( U64A.rotateRightBits(0) == U64A) + + # check for undefined behavior with rotate by integer width. + doAssert( U8.rotateLeftBits(8) == U8) + doAssert( U8.rotateRightBits(8) == U8) + doAssert( U16.rotateLeftBits(16) == U16) + doAssert( U16.rotateRightBits(16) == U16) + doAssert( U32.rotateLeftBits(32) == U32) + doAssert( U32.rotateRightBits(32) == U32) + doAssert( U64A.rotateLeftBits(64) == U64A) + doAssert( U64A.rotateRightBits(64) == U64A) + + echo "OK" + +main2() + diff --git a/tests/stdlib/tbitops.nim.cfg b/tests/stdlib/tbitops.nim.cfg index 013e8d38e..f0d7668a7 100644 --- a/tests/stdlib/tbitops.nim.cfg +++ b/tests/stdlib/tbitops.nim.cfg @@ -1 +1 @@ --d:noUndefinedBitOpts +-d:noUndefinedBitOps diff --git a/tests/stdlib/tbitops_utils.nim b/tests/stdlib/tbitops_utils.nim deleted file mode 100644 index 7a64ea68d..000000000 --- a/tests/stdlib/tbitops_utils.nim +++ /dev/null @@ -1,15 +0,0 @@ -import std/private/bitops_utils -import std/assertions - -template chk(a, b) = - let a2 = castToUnsigned(a) - doAssert a2 == b - doAssert type(a2) is type(b) - doAssert type(b) is type(a2) - -chk 1'i8, 1'u8 -chk -1'i8, 255'u8 -chk 1'u8, 1'u8 -chk 1'u, 1'u -chk -1, cast[uint](-1) -chk -1'i64, cast[uint64](-1) diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim index 7a52dc89b..993728712 100644 --- a/tests/stdlib/tcgi.nim +++ b/tests/stdlib/tcgi.nim @@ -1,6 +1,5 @@ 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 deleted file mode 100644 index 5c0f71772..000000000 --- a/tests/stdlib/tcmdline.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ - targets: "c js" - joinable: false -""" - -import std/os -import std/assertions - -var params = paramCount() -doAssert params == 0 -doAssert paramStr(0).len > 0 -doAssert commandLineParams().len == 0 diff --git a/tests/stdlib/tcomplex.nim b/tests/stdlib/tcomplex.nim index c7666be84..15267b905 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 4fe104dfc..95b431b19 100644 --- a/tests/stdlib/tcookies.nim +++ b/tests/stdlib/tcookies.nim @@ -1,10 +1,4 @@ -discard """ - targets: "c js" -""" - - -import std/[cookies, times, strtabs] -import std/assertions +import cookies, times, strtabs let expire = fromUnix(0) + 1.seconds diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim index 0c2e1d6fa..b350cb280 100644 --- a/tests/stdlib/tcritbits.nim +++ b/tests/stdlib/tcritbits.nim @@ -3,7 +3,6 @@ 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 deleted file mode 100644 index d7fdd7738..000000000 --- a/tests/stdlib/tcstring.nim +++ /dev/null @@ -1,93 +0,0 @@ -discard """ - targets: "c cpp js" - matrix: "--gc:refc; --gc:arc" -""" - -from std/sugar import collect -from stdtest/testutils import whenRuntimeJs, whenVMorJs -import std/assertions - -template testMitems() = - block: - var a = "abc" - var b = a.cstring - let s = collect: - for bi in mitems(b): - if bi == 'b': bi = 'B' - bi - whenRuntimeJs: - discard # xxx mitems should give CT error instead of @['\x00', '\x00', '\x00'] - do: - doAssert s == @['a', 'B', 'c'] - - block: - var a = "abc\0def" - var b = a.cstring - let s = collect: - for bi in mitems(b): - if bi == 'b': bi = 'B' - bi - whenRuntimeJs: - discard # ditto - do: - doAssert s == @['a', 'B', 'c'] - -proc mainProc() = - testMitems() - -template main() = - block: # bug #13859 - let str = "abc".cstring - doAssert len(str).int8 == 3 - doAssert len(str).int16 == 3 - doAssert len(str).int32 == 3 - var str2 = "cde".cstring - doAssert len(str2).int8 == 3 - doAssert len(str2).int16 == 3 - doAssert len(str2).int32 == 3 - - const str3 = "abc".cstring - doAssert len(str3).int32 == 3 - doAssert len("abc".cstring).int16 == 3 - doAssert len("abc".cstring).float32 == 3.0 - - block: # bug #17159 - block: - var a = "abc" - var b = a.cstring - doAssert $(b, ) == """("abc",)""" - let s = collect: - for bi in b: bi - doAssert s == @['a', 'b', 'c'] - - block: - var a = "abc\0def" - var b = a.cstring - let s = collect: - for bi in b: bi - whenRuntimeJs: - doAssert $(b, ) == """("abc\x00def",)""" - doAssert s == @['a', 'b', 'c', '\x00', 'd', 'e', 'f'] - do: - doAssert $(b, ) == """("abc",)""" - doAssert s == @['a', 'b', 'c'] - - block: - when defined(gcArc): # xxx SIGBUS - discard - else: - mainProc() - when false: # xxx bug vm: Error: unhandled exception: 'node' is not accessible using discriminant 'kind' of type 'TFullReg' [FieldDefect] - testMitems() - - block: # bug #13321: [codegen] --gc:arc does not properly emit cstring, results in SIGSEGV - let a = "hello".cstring - doAssert $a == "hello" - doAssert $a[0] == "h" - doAssert $a[4] == "o" - whenVMorJs: discard # xxx this should work in vm, refs https://github.com/timotheecour/Nim/issues/619 - do: - doAssert a[a.len] == '\0' - -static: main() -main() diff --git a/tests/stdlib/tcstrutils.nim b/tests/stdlib/tcstrutils.nim index ec2b8596c..ba3b1de68 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 deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/stdlib/tdb.nims b/tests/stdlib/tdb.nims deleted file mode 100644 index d31d0b26f..000000000 --- a/tests/stdlib/tdb.nims +++ /dev/null @@ -1 +0,0 @@ ---styleCheck:off \ No newline at end of file diff --git a/tests/stdlib/tdb_mysql.nim b/tests/stdlib/tdb_mysql.nim deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/stdlib/tdecls.nim b/tests/stdlib/tdecls.nim index 5cf352cfb..3567639e0 100644 --- a/tests/stdlib/tdecls.nim +++ b/tests/stdlib/tdecls.nim @@ -1,10 +1,6 @@ -discard """ - targets: "c cpp js" -""" -import std/assertions import std/decls -template fun() = +block: var s = @[10,11,12] var a {.byaddr.} = s[0] a+=100 @@ -13,18 +9,15 @@ template fun() = var b {.byaddr.}: int = s[0] doAssert a.addr == b.addr - when false: - # template specific redeclaration issue - # see https://github.com/nim-lang/Nim/issues/8275 - doAssert not compiles(block: - # redeclaration not allowed - var foo = 0 - var foo {.byaddr.} = s[0]) + doAssert not compiles(block: + # redeclaration not allowed + var foo = 0 + var foo {.byaddr.} = s[0]) - doAssert not compiles(block: - # ditto - var foo {.byaddr.} = s[0] - var foo {.byaddr.} = s[0]) + doAssert not compiles(block: + # ditto + var foo {.byaddr.} = s[0] + var foo {.byaddr.} = s[0]) block: var b {.byaddr.} = s[1] # redeclaration ok in sub scope @@ -41,9 +34,37 @@ template fun() = doAssert compiles(block: var b2 {.byaddr.}: int = s[2]) -proc fun2() = fun() -fun() -fun2() -static: fun2() -when false: # pending bug #13887 - static: fun() +## We can define custom pragmas in user code +template byUnsafeAddr(lhs, typ, expr) = + when typ is type(nil): + let tmp = unsafeAddr(expr) + else: + let tmp: ptr typ = unsafeAddr(expr) + template lhs: untyped = tmp[] + +block: + let s = @["foo", "bar"] + let a {.byUnsafeAddr.} = s[0] + doAssert a == "foo" + doAssert a[0].unsafeAddr == s[0][0].unsafeAddr + +block: # nkAccQuoted + # shows using a keyword, which requires nkAccQuoted + template `cast`(lhs, typ, expr) = + when typ is type(nil): + let tmp = unsafeAddr(expr) + else: + let tmp: ptr typ = unsafeAddr(expr) + template lhs: untyped = tmp[] + + block: + let s = @["foo", "bar"] + let a {.`byUnsafeAddr`.} = s[0] + doAssert a == "foo" + doAssert a[0].unsafeAddr == s[0][0].unsafeAddr + + block: + let s = @["foo", "bar"] + let a {.`cast`.} = s[0] + doAssert a == "foo" + doAssert a[0].unsafeAddr == s[0][0].unsafeAddr diff --git a/tests/stdlib/tdecode_helpers.nim b/tests/stdlib/tdecode_helpers.nim index 1c0735e05..626a014fc 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 fcfc278a9..99208d4cf 100644 --- a/tests/stdlib/tdeques.nim +++ b/tests/stdlib/tdeques.nim @@ -1,11 +1,8 @@ discard """ - matrix: "--gc:refc; --gc:orc" - targets: "c cpp js" + targets: "c js" """ import std/deques -from std/sequtils import toSeq -import std/assertions block: proc index(self: Deque[int], idx: Natural): int = @@ -128,7 +125,7 @@ block: foo(1, 5) foo(3, 2) -import std/sets +import sets block t13310: proc main() = @@ -140,55 +137,3 @@ block t13310: static: main() - - -proc main() = - block: - let a = [10, 20, 30].toDeque - doAssert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)] - - block: - let q = [7, 9].toDeque - doAssert 7 in q - doAssert q.contains(7) - doAssert 8 notin q - - block: - let a = [10, 20, 30, 40, 50].toDeque - doAssert $a == "[10, 20, 30, 40, 50]" - doAssert a.peekFirst == 10 - doAssert len(a) == 5 - - block: - let a = [10, 20, 30, 40, 50].toDeque - doAssert $a == "[10, 20, 30, 40, 50]" - doAssert a.peekLast == 50 - doAssert len(a) == 5 - - block: - var a = [10, 20, 30, 40, 50].toDeque - doAssert $a == "[10, 20, 30, 40, 50]" - doAssert a.popFirst == 10 - doAssert $a == "[20, 30, 40, 50]" - - block: - var a = [10, 20, 30, 40, 50].toDeque - doAssert $a == "[10, 20, 30, 40, 50]" - doAssert a.popLast == 50 - doAssert $a == "[10, 20, 30, 40]" - - block: - var a = [10, 20, 30, 40, 50].toDeque - doAssert $a == "[10, 20, 30, 40, 50]" - clear(a) - doAssert len(a) == 0 - - block: # bug #21278 - var a = [10, 20, 30, 40].toDeque - - a.shrink(fromFirst = 0, fromLast = 1) - doAssert $a == "[10, 20, 30]" - - -static: main() -main() diff --git a/tests/stdlib/tdiff.nim b/tests/stdlib/tdiff.nim index cb9cebb3a..694ac6198 100644 --- a/tests/stdlib/tdiff.nim +++ b/tests/stdlib/tdiff.nim @@ -4,7 +4,6 @@ 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 deleted file mode 100644 index 300ce76dc..000000000 --- a/tests/stdlib/tdochelpers.nim +++ /dev/null @@ -1,220 +0,0 @@ -discard """ - output: ''' - -[Suite] Integration with Nim -''' -""" - -# tests for dochelpers.nim module - -import ../../lib/packages/docutils/[rstast, rst, dochelpers] -import unittest -import std/assertions - -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) - 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 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 - 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 expected2 = LangSymbol(symKind: "", name: "type") - check "`type`_".fromRst == expected2 - check "[type]".fromMd == expected2 - - let expected3 = LangSymbol(symKind: "", name: "[]") - check "`[]`_".fromRst == expected3 - # Markdown syntax for this case is NOT [[]] - check "[`[]`]".fromMd == expected3 - - let expected4 = LangSymbol(symKind: "", name: "Xyz") - check "`X Y Z`_".fromRst == expected4 - check "[X Y Z]".fromMd == expected4 - - test "simple proc parsing": - let expected = LangSymbol(symKind: "proc", name: "f") - check "`proc f`_".fromRst == expected - check "[proc f]".fromMd == expected - - test "another backticked name": - 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 expected = LangSymbol(symKind: "proc", name: "f", - parametersProvided: true) - 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 expected = LangSymbol(symKind: "", name: "f", - parameters: @[("G[int]", "")], - parametersProvided: true) - 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]`_".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 == expected) - check(input2 == expected) - check(input3 == expected) - - test "advanced proc parsing with Nim identifier normalization": - 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]", - parameters: @[ - ("a", "openarray[T]"), - ("key", "K"), - ("cmp", "proc(x:T;y:K):int")], - parametersProvided: true, - outType: "") - 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.})`_""" - let expected = LangSymbol(symKind: "", - name: "binarysearch", - generics: "[T,K]", - parameters: @[ - ("a", "openarray[T]"), - ("key", "K"), - ("cmp", "proc(x:T;y:K):int")], - parametersProvided: true, - outType: "") - 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`_""" - 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.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`_""" - 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]", - parameters: @[("a", "openarray[T]"), - ("idx", "int")], - parametersProvided: true, - outType: "T") - 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`_" - let inputMd = "[walkDir iterator]" - let expected = LangSymbol(symKind: "iterator", - name: "walkdir") - check input.fromRst == expected - check inputMd.fromMd == expected - - test "postfix symbol specifier #2": - 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]", - parameters: @[("a", "openarray[T]"), - ("idx", "int")], - parametersProvided: true, - outType: "T") - check input1.fromRst == expected - check input2.fromRst == expected - check input1md.fromMd == expected - check input2md.fromMd == expected - - test "type of type": - 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 - - test "prefixed module": - let inputRst = "`module std / paths`_" - let inputMd = "[module std / paths]" - let expected = LangSymbol(symKind: "module", - name: "std/paths") - check inputRst.fromRst == expected - check inputMd.fromMd == expected - - test "postfixed module": - let inputRst = "`std / paths module`_" - let inputMd = "[std / paths module]" - let expected = LangSymbol(symKind: "module", - name: "std/paths") - check inputRst.fromRst == expected - check inputMd.fromMd == expected diff --git a/tests/stdlib/teditdistance.nim b/tests/stdlib/teditdistance.nim index b3b323647..433535635 100644 --- a/tests/stdlib/teditdistance.nim +++ b/tests/stdlib/teditdistance.nim @@ -1,5 +1,4 @@ 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 deleted file mode 100644 index 10d79f5d0..000000000 --- a/tests/stdlib/tencodings.nim +++ /dev/null @@ -1,99 +0,0 @@ -import std/encodings -import std/assertions - -var fromGBK = open("utf-8", "gbk") -var toGBK = open("gbk", "utf-8") - -var fromGB2312 = open("utf-8", "gb2312") -var toGB2312 = open("gb2312", "utf-8") - - -block: - let data = "\215\237\186\243\178\187\214\170\204\236\212\218\203\174\163\172\194\250\180\178\208\199\195\206\209\185\208\199\186\211" - doAssert fromGBK.convert(data) == "醉后不知天在水,满床星梦压星河" - -block: - let data = "万两黄金容易得,知心一个也难求" - doAssert toGBK.convert(data) == "\205\242\193\189\187\198\189\240\200\221\210\215\181\195\163\172\214\170\208\196\210\187\184\246\210\178\196\209\199\243" - - -block: - let data = "\215\212\208\197\200\203\201\250\182\254\176\217\196\234\163\172\187\225\181\177\203\174\187\247\200\253\199\167\192\239" - doAssert fromGB2312.convert(data) == "自信人生二百年,会当水击三千里" - -block: - let data = "谁怕?一蓑烟雨任平生" - doAssert toGB2312.convert(data) == "\203\173\197\194\163\191\210\187\203\242\209\204\211\234\200\206\198\189\201\250" - - -when defined(windows): - block should_throw_on_unsupported_conversions: - let original = "some string" - - doAssertRaises(EncodingError): - discard convert(original, "utf-8", "utf-32") - - doAssertRaises(EncodingError): - discard convert(original, "utf-8", "unicodeFFFE") - - doAssertRaises(EncodingError): - discard convert(original, "utf-8", "utf-32BE") - - doAssertRaises(EncodingError): - discard convert(original, "unicodeFFFE", "utf-8") - - doAssertRaises(EncodingError): - discard convert(original, "utf-32", "utf-8") - - doAssertRaises(EncodingError): - discard convert(original, "utf-32BE", "utf-8") - - block should_convert_from_utf16_to_utf8: - let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" - let result = convert(original, "utf-8", "utf-16") - doAssert(result == "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82") - - block should_convert_from_utf16_to_win1251: - let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" - let result = convert(original, "windows-1251", "utf-16") - doAssert(result == "\xf2\xe5\xf1\xf2") - - block should_convert_from_win1251_to_koi8r: - let original = "\xf2\xe5\xf1\xf2" # win1251 test string "тест" - let result = convert(original, "koi8-r", "windows-1251") - doAssert(result == "\xd4\xc5\xd3\xd4") - - block should_convert_from_koi8r_to_win1251: - let original = "\xd4\xc5\xd3\xd4" # koi8r test string "тест" - let result = convert(original, "windows-1251", "koi8-r") - doAssert(result == "\xf2\xe5\xf1\xf2") - - block should_convert_from_utf8_to_win1251: - let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" - let result = convert(original, "windows-1251", "utf-8") - doAssert(result == "\xf2\xe5\xf1\xf2") - - block should_convert_from_utf8_to_utf16: - let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" - let result = convert(original, "utf-16", "utf-8") - doAssert(result == "\x42\x04\x35\x04\x41\x04\x42\x04") - - block should_handle_empty_string_for_any_conversion: - let original = "" - var result = convert(original, "utf-16", "utf-8") - doAssert(result == "") - result = convert(original, "utf-8", "utf-16") - doAssert(result == "") - result = convert(original, "windows-1251", "koi8-r") - doAssert(result == "") - - -block: - let - orig = "öäüß" - cp1252 = convert(orig, "CP1252", "UTF-8") - ibm850 = convert(cp1252, "ibm850", "CP1252") - current = getCurrentEncoding() - doAssert orig == "\195\182\195\164\195\188\195\159" - doAssert ibm850 == "\148\132\129\225" - doAssert convert(ibm850, current, "ibm850") == orig diff --git a/tests/stdlib/tenumerate.nim b/tests/stdlib/tenumerate.nim index b15b9e2db..7a1c2d10a 100644 --- a/tests/stdlib/tenumerate.nim +++ b/tests/stdlib/tenumerate.nim @@ -1,5 +1,4 @@ import std/enumerate -import std/assertions let a = @[1, 3, 5, 7] diff --git a/tests/stdlib/tenumutils.nim b/tests/stdlib/tenumutils.nim deleted file mode 100644 index 63c563739..000000000 --- a/tests/stdlib/tenumutils.nim +++ /dev/null @@ -1,38 +0,0 @@ -discard """ - targets: "c js" -""" - -import std/enumutils -from std/sequtils import toSeq -import std/assertions - -template main = - block: # items - type A = enum a0 = 2, a1 = 4, a2 - type B[T] = enum b0 = 2, b1 = 4 - doAssert A.toSeq == [a0, a1, a2] - doAssert B[float].toSeq == [B[float].b0, B[float].b1] - - block: # symbolName - block: - type A2 = enum a20, a21, a22 - doAssert $a21 == "a21" - doAssert a21.symbolName == "a21" - proc `$`(a: A2): string = "foo" - doAssert $a21 == "foo" - doAssert a21.symbolName == "a21" - var a = a22 - doAssert $a == "foo" - doAssert a.symbolName == "a22" - - type B = enum - b0 = (10, "kb0") - b1 = "kb1" - b2 - let b = B.low - doAssert b.symbolName == "b0" - doAssert $b == "kb0" - static: doAssert B.high.symbolName == "b2" - -static: main() -main() diff --git a/tests/stdlib/tenvvars.nim b/tests/stdlib/tenvvars.nim deleted file mode 100644 index 03a46a013..000000000 --- a/tests/stdlib/tenvvars.nim +++ /dev/null @@ -1,162 +0,0 @@ -discard """ - matrix: "--threads:on" - joinable: false - targets: "c js cpp" -""" - -import std/envvars -from std/sequtils import toSeq -import stdtest/testutils -import std/[assertions] - -when not defined(js): - import std/typedthreads - -# "LATIN CAPITAL LETTER AE" in UTF-8 (0xc386) -const unicodeUtf8 = "\xc3\x86" - -template main = - block: # delEnv, existsEnv, getEnv, envPairs - for val in ["val", "", unicodeUtf8]: # ensures empty val works too - const key = "NIM_TESTS_TOSENV_KEY" - doAssert not existsEnv(key) - - putEnv(key, "tempval") - doAssert existsEnv(key) - doAssert getEnv(key) == "tempval" - - putEnv(key, val) # change a key that already exists - doAssert existsEnv(key) - doAssert getEnv(key) == val - - doAssert (key, val) in toSeq(envPairs()) - delEnv(key) - doAssert (key, val) notin toSeq(envPairs()) - doAssert not existsEnv(key) - delEnv(key) # deleting an already deleted env var - doAssert not existsEnv(key) - - block: - doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "") == "" - doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", " ") == " " - doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "defval") == "defval" - - whenVMorJs: discard # xxx improve - do: - doAssertRaises(OSError, putEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE")) - doAssertRaises(OSError, putEnv("", "NEW_DUMMY_VALUE")) - doAssert not existsEnv("") - doAssert not existsEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE") - doAssert not existsEnv("NIM_TESTS_TOSENV_PUT") - -static: main() -main() - -when defined(windows): - import std/widestrs - 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 - var thr: Thread[void] - proc threadFunc {.thread.} = putEnv("foo", "fooVal2") - - putEnv("foo", "fooVal1") - doAssert getEnv("foo") == "fooVal1" - createThread(thr, threadFunc) - joinThreads(thr) - 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/tfdleak.nim b/tests/stdlib/tfdleak.nim index 1ac746e48..79d7ee0d0 100644 --- a/tests/stdlib/tfdleak.nim +++ b/tests/stdlib/tfdleak.nim @@ -7,9 +7,6 @@ discard """ import os, osproc, strutils, nativesockets, net, selectors, memfiles, asyncdispatch, asyncnet - -import std/[assertions, syncio] - when defined(windows): import winlean @@ -59,7 +56,7 @@ proc isValidHandle(f: int): bool = proc main() = if paramCount() == 0: # Parent process - let f = syncio.open("__test_fdleak", fmReadWrite) + let f = system.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 c26681217..22387607f 100644 --- a/tests/stdlib/tfdleak_multiple.nim +++ b/tests/stdlib/tfdleak_multiple.nim @@ -3,7 +3,6 @@ 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 a486b8a9d..5bcd1ea7c 100644 --- a/tests/stdlib/tfenv.nim +++ b/tests/stdlib/tfenv.nim @@ -1,5 +1,4 @@ import std/fenv -import std/assertions func is_significant(x: float): bool = diff --git a/tests/stdlib/tfilesanddirs.nim b/tests/stdlib/tfilesanddirs.nim deleted file mode 100644 index a1920d4f2..000000000 --- a/tests/stdlib/tfilesanddirs.nim +++ /dev/null @@ -1,36 +0,0 @@ -import std/[paths, files, dirs, appdirs] - -from stdtest/specialpaths import buildDir -import std/[syncio, assertions] - -block fileOperations: - let files = @[Path"these.txt", Path"are.x", Path"testing.r", Path"files.q"] - let dirs = @[Path"some", Path"created", Path"test", Path"dirs"] - - let dname = Path"__really_obscure_dir_name" - - createDir(dname.Path) - doAssert dirExists(Path(dname)) - - # Test creating files and dirs - for dir in dirs: - createDir(Path(dname/dir)) - doAssert dirExists(Path(dname/dir)) - - for file in files: - let fh = open(string(dname/file), fmReadWrite) # createFile - fh.close() - doAssert fileExists(Path(dname/file)) - -block: # getCacheDir - doAssert getCacheDir().dirExists - -block: # moveFile - let tempDir = getTempDir() / Path("D20221022T151608") - createDir(tempDir) - defer: removeDir(tempDir) - -block: # moveDir - let tempDir = getTempDir() / Path("D20220609T161443") - createDir(tempDir) - defer: removeDir(tempDir) diff --git a/tests/stdlib/tfrexp1.nim b/tests/stdlib/tfrexp1.nim index 6b4c3b6d3..21ecc7491 100644 --- a/tests/stdlib/tfrexp1.nim +++ b/tests/stdlib/tfrexp1.nim @@ -1,9 +1,9 @@ discard """ targets: "js c cpp" + output: '''ok''' """ -import std/math -import std/assertions +import math const manualTest = false @@ -43,12 +43,4 @@ when manualTest: else: frexp_test(-200000.0, 200000.0, 0.125) - -doAssert frexp(8.0) == (0.5, 4) -doAssert frexp(-8.0) == (-0.5, 4) -doAssert frexp(0.0) == (0.0, 0) - -block: - var x: int - doAssert frexp(5.0, x) == 0.625 - doAssert x == 3 +echo "ok" diff --git a/tests/stdlib/tgenast.nim b/tests/stdlib/tgenast.nim deleted file mode 100644 index d99c9312e..000000000 --- a/tests/stdlib/tgenast.nim +++ /dev/null @@ -1,274 +0,0 @@ -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 = - block: - macro bar(x0: static Foo, x1: Foo, x2: Foo, xignored: Foo): untyped = - let s0 = "not captured!" - let s1 = "not captured!" - let xignoredLocal = kfoo4 - - # newLit optional: - let x3 = newLit kfoo4 - let x3b = kfoo4 - - result = genAstOpt({kDirtyTemplate}, s1=true, s2="asdf", x0, x1=x1, x2, x3, x3b): - doAssert not declared(xignored) - doAssert not declared(xignoredLocal) - (s1, s2, s0, x0, x1, x2, x3, x3b) - - let s0 = "caller scope!" - - doAssert bar(kfoo1, kfoo2, kfoo3, kfoo4) == - (true, "asdf", "caller scope!", kfoo1, kfoo2, kfoo3, kfoo4, kfoo4) - - block: - # doesn't have limitation mentioned in https://github.com/nim-lang/RFCs/issues/122#issue-401636535 - macro abc(name: untyped): untyped = - result = genAst(name): - type name = object - - abc(Bar) - doAssert Bar.default == Bar() - - block: - # backticks parser limitations / ambiguities not are an issue with `genAst`: - # (#10326 #9745 are fixed but `quote do` still has underlying ambiguity issue - # with backticks) - type Foo = object - a: int - - macro m1(): untyped = - # result = quote do: # Error: undeclared identifier: 'a1' - result = genAst: - template `a1=`(x: var Foo, val: int) = - x.a = val - - m1() - var x0: Foo - x0.a1 = 10 - doAssert x0 == Foo(a: 10) - - block: - # avoids bug #7375 - macro fun(b: static[bool], b2: bool): untyped = - result = newStmtList() - macro foo(c: bool): untyped = - var b = false - result = genAst(b, c): - fun(b, c) - - foo(true) - - block: - # avoids bug #7589 - # since `==` works with genAst, the problem goes away - macro foo2(): untyped = - # result = quote do: # Error: '==' cannot be passed to a procvar - result = genAst: - `==`(3,4) - doAssert not foo2() - - block: - # avoids bug #7726 - # expressions such as `a.len` are just passed as arguments to `genAst`, and - # caller scope is not polluted with definitions such as `let b = newLit a.len` - macro foo(): untyped = - let a = @[1, 2, 3, 4, 5] - result = genAst(a, b = a.len): # shows 2 ways to get a.len - (a.len, b) - doAssert foo() == (5, 5) - - block: - # avoids bug #9607 - proc fun1(info:LineInfo): string = "bar1" - proc fun2(info:int): string = "bar2" - - macro bar2(args: varargs[untyped]): untyped = - let info = args.lineInfoObj - let fun1 = bindSym"fun1" # optional; we can remove this and also the - # capture of fun1, as show in next example - result = genAst(info, fun1): - (fun1(info), fun2(info.line)) - doAssert bar2() == ("bar1", "bar2") - - macro bar3(args: varargs[untyped]): untyped = - let info = args.lineInfoObj - result = genAst(info): - (fun1(info), fun2(info.line)) - doAssert bar3() == ("bar1", "bar2") - - macro bar(args: varargs[untyped]): untyped = - let info = args.lineInfoObj - let fun1 = bindSym"fun1" - let fun2 = bindSym"fun2" - result = genAstOpt({kDirtyTemplate}, info): - (fun1(info), fun2(info.line)) - doAssert bar() == ("bar1", "bar2") - - block: - # example from bug #7889 works - # after changing method call syntax to regular call syntax; this is a - # limitation described in bug #7085 - # note that `quote do` would also work after that change in this example. - doAssert bindme2() == kfoo1 - doAssert bindme3() == kfoo1 - doAssert not compiles(bindme4()) # correctly gives Error: undeclared identifier: 'myLocalPriv' - proc myLocalPriv2(): auto = kfoo2 - doAssert bindme5UseExpose() == kfoo1 - - # example showing hijacking behavior when using `kDirtyTemplate` - doAssert bindme5UseExposeFalse() == kfoo2 - # local `myLocalPriv2` hijacks symbol `mgenast.myLocalPriv2`. In most - # use cases this is probably not what macro writer intends as it's - # surprising; hence `kDirtyTemplate` is not the default. - - when nimvm: # disabled because `newStringStream` is used - discard - else: - bindme6UseExpose() - bindme6UseExposeFalse() - - block: - macro mbar(x3: Foo, x3b: static Foo): untyped = - var x1=kfoo3 - var x2=newLit kfoo3 - var x4=kfoo3 - var xLocal=kfoo3 - - proc funLocal(): auto = kfoo4 - - result = genAst(x1, x2, x3, x4): - # local x1 overrides remote x1 - when false: - # one advantage of using `kDirtyTemplate` is that these would hold: - doAssert not declared xLocal - doAssert not compiles(echo xLocal) - # however, even without it, we at least correctly generate CT error - # if trying to use un-captured symbol; this correctly gives: - # Error: internal error: environment misses: xLocal - echo xLocal - - proc foo1(): auto = - # note that `funLocal` is captured implicitly, according to hygienic - # template rules; with `kDirtyTemplate` it would not unless - # captured in `genAst` capture list explicitly - (a0: xRemote, a1: x1, a2: x2, a3: x3, a4: x4, a5: funLocal()) - - return result - - proc main()= - var xRemote=kfoo1 - var x1=kfoo2 - mbar(kfoo4, kfoo4) - doAssert foo1() == (a0: kfoo1, a1: kfoo3, a2: kfoo3, a3: kfoo4, a4: kfoo3, a5: kfoo4) - - main() - - block: - # With `kDirtyTemplate`, the example from #8220 works. - # See https://nim-lang.github.io/Nim/strformat.html#limitations for - # an explanation of why {.dirty.} is needed. - macro foo(): untyped = - result = genAstOpt({kDirtyTemplate}): - let bar = "Hello, World" - &"Let's interpolate {bar} in the string" - doAssert foo() == "Let's interpolate Hello, World in the string" - - - block: # nested application of genAst - macro createMacro(name, obj, field: untyped): untyped = - result = genAst(obj = newDotExpr(obj, field), lit = 10, name, field): - # can't reuse `result` here, would clash - macro name(arg: untyped): untyped = - genAst(arg2=arg): # somehow `arg2` rename is needed - (obj, astToStr(field), lit, arg2) - - var x = @[1, 2, 3] - createMacro foo, x, len - doAssert (foo 20) == (3, "len", 10, 20) - - block: # test with kNoNewLit - macro bar(): untyped = - let s1 = true - template boo(x): untyped = - fun(x) - result = genAstOpt({kNoNewLit}, s1=newLit(s1), s1b=s1): (s1, s1b) - doAssert bar() == (true, 1) - - block: # sanity check: check passing `{}` also works - macro bar(): untyped = - result = genAstOpt({}, s1=true): s1 - doAssert bar() == true - - block: # test passing function and type symbols - proc z1(): auto = 41 - type Z4 = type(1'i8) - macro bar(Z1: typedesc): untyped = - proc z2(): auto = 42 - proc z3[T](a: T): auto = 43 - let Z2 = genAst(): - type(true) - let z4 = genAst(): - proc myfun(): auto = 44 - myfun - type Z3 = type(1'u8) - result = genAst(z4, Z1, Z2): - # z1, z2, z3, Z3, Z4 are captured automatically - # z1, z2, z3 can optionally be specified in capture list - (z1(), z2(), z3('a'), z4(), $Z1, $Z2, $Z3, $Z4) - type Z1 = type('c') - doAssert bar(Z1) == (41, 42, 43, 44, "char", "bool", "uint8", "int8") - - block: # fix bug #11986 - proc foo(): auto = - var s = { 'a', 'b' } - # var n = quote do: `s` # would print {97, 98} - var n = genAst(s): s - n.repr - static: doAssert foo() == "{'a', 'b'}" - - block: # also from #11986 - macro foo(): untyped = - var s = { 'a', 'b' } - # quote do: - # let t = `s` - # $typeof(t) # set[range 0..65535(int)] - genAst(s): - let t = s - $typeof(t) - doAssert foo() == "set[char]" - - block: - macro foo(): untyped = - type Foo = object - template baz2(a: int): untyped = a*10 - macro baz3(a: int): untyped = newLit 13 - result = newStmtList() - - result.add genAst(Foo, baz2, baz3) do: # shows you can pass types, templates etc - var x: Foo - $($typeof(x), baz2(3), baz3(4)) - - let ret = genAst() do: # shows you don't have to, since they're inject'd - var x: Foo - $($typeof(x), baz2(3), baz3(4)) - doAssert foo() == """("Foo", 30, 13)""" - - block: # illustrates how symbol visiblity can be controlled precisely using `mixin` - proc locafun1(): auto = "in locafun1 (caller scope)" # this will be used because of `mixin locafun1` => explicit hijacking is ok - proc locafun2(): auto = "in locafun2 (caller scope)" # this won't be used => no hijacking - proc locafun3(): auto = "in locafun3 (caller scope)" - doAssert mixinExample() == ("in locafun1 (caller scope)", "in locafun2", "in locafun3 (caller scope)") - -static: main() -main() diff --git a/tests/stdlib/tgetaddrinfo.nim b/tests/stdlib/tgetaddrinfo.nim index a8bcecb0c..ed8ec8b68 100644 --- a/tests/stdlib/tgetaddrinfo.nim +++ b/tests/stdlib/tgetaddrinfo.nim @@ -6,7 +6,6 @@ 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 d0413f757..19de193e4 100644 --- a/tests/stdlib/tgetfileinfo.nim +++ b/tests/stdlib/tgetfileinfo.nim @@ -1,10 +1,8 @@ discard """ output: "pcDir\npcFile\npcLinkToDir\npcLinkToFile\n" - joinable: false """ import os, strutils -import std/[syncio, assertions] # Cases # 1 - String : Existing File : Symlink true # 2 - String : Existing File : Symlink false @@ -127,37 +125,10 @@ proc testGetFileInfo = echo pcLinkToDir echo pcLinkToFile - doAssert dirInfo.isSpecial == false - doAssert fileInfo.isSpecial == false - when defined(posix): - doAssert linkDirInfo.isSpecial == false - doAssert linkFileInfo.isSpecial == false - removeDir(dirPath) removeFile(filePath) when defined(posix): removeFile(linkDirPath) removeFile(linkFilePath) - # Test that `isSpecial` is set correctly - block: - when defined(posix): - let - tmp = getTempDir() - fifoPath = tmp / "test-fifo" - linkFifoPath = tmp / "test-link-fifo" - - doAssert execShellCmd("mkfifo " & fifoPath) == 0 - createSymlink(fifoPath, linkFifoPath) - - let - fifoInfo = getFileInfo(fifoPath) - linkFifoInfo = getFileInfo(linkFifoPath) - - doAssert fifoInfo.isSpecial == true - doAssert linkFifoInfo.isSpecial == true - - removeFile(fifoPath) - removeFile(linkFifoPath) - testGetFileInfo() diff --git a/tests/stdlib/tgetprotobyname.nim b/tests/stdlib/tgetprotobyname.nim index e524510b2..b4df05102 100644 --- a/tests/stdlib/tgetprotobyname.nim +++ b/tests/stdlib/tgetprotobyname.nim @@ -1,5 +1,18 @@ +discard """ + cmd: "nim c -r --styleCheck:hint --panics:on $options $file" + targets: "c" + nimout: "" + action: "run" + exitcode: 0 + timeout: 60.0 +""" + import nativesockets -import std/assertions + + +when not defined(netbsd): + # Ref: https://github.com/nim-lang/Nim/issues/15452 - NetBSD doesn't define an `ip` protocol + doAssert getProtoByName("ip") == 0 doAssert getProtoByName("ipv6") == 41 doAssert getProtoByName("tcp") == 6 diff --git a/tests/stdlib/tglobs.nim b/tests/stdlib/tglobs.nim deleted file mode 100644 index 69ff31938..000000000 --- a/tests/stdlib/tglobs.nim +++ /dev/null @@ -1,21 +0,0 @@ -import std/private/globs -import std/assertions - -template main = - when defined(windows): - doAssert nativeToUnixPath("C:") == "/C" - doAssert nativeToUnixPath(r"D:\") == "/D/" - doAssert nativeToUnixPath(r"E:\a") == "/E/a" - doAssert nativeToUnixPath(r"E:\a1\") == "/E/a1/" - doAssert nativeToUnixPath(r"E:\a1\bc") == "/E/a1/bc" - doAssert nativeToUnixPath(r"\a1\bc") == "/a1/bc" - doAssert nativeToUnixPath(r"a1\bc") == "a1/bc" - doAssert nativeToUnixPath("a1") == "a1" - doAssert nativeToUnixPath("") == "" - doAssert nativeToUnixPath(".") == "." - doAssert nativeToUnixPath("..") == ".." - doAssert nativeToUnixPath(r"..\") == "../" - doAssert nativeToUnixPath(r"..\..\.\") == "../.././" - -static: main() -main() diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index caae79213..17640387a 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -3,8 +3,7 @@ discard """ """ import std/hashes -from stdtest/testutils import disableVm, whenVMorJs -import std/assertions + when not defined(js) and not defined(cpp): block: @@ -87,8 +86,9 @@ block largeSize: # longer than 4 characters doAssert hash(xx, 0, 3) == hash(ssl, 0, 3) proc main() = + + doAssert hash(0.0) == hash(0) - # bug #16061 doAssert hash(cstring"abracadabra") == 97309975 doAssert hash(cstring"abracadabra") == hash("abracadabra") @@ -115,110 +115,6 @@ proc main() = doAssert hash(-9999.283456) != 0 doAssert hash(84375674.0) != 0 - block: # bug #16555 - proc fn(): auto = - # avoids hardcoding values - var a = "abc\0def" - var b = a.cstring - result = (hash(a), hash(b)) - doAssert result[0] != result[1] - when not defined(js): - doAssert fn() == static(fn()) - else: - # xxx this is a tricky case; consistency of hashes for cstring's containing - # '\0\' matters for c backend but less for js backend since such strings - # are much less common in js backend; we make vm for js backend consistent - # with c backend instead of js backend because FFI code (or other) could - # run at CT, expecting c semantics. - discard - - block: # hash(object) - type - Obj = object - x: int - y: string - Obj2[T] = object - x: int - y: string - Obj3 = object - x: int - y: string - Obj4 = object - case t: bool - of false: - x: int - of true: - y: int - z: int - Obj5 = object - case t: bool - of false: - x: int - of true: - y: int - z: int - - proc hash(a: Obj2): Hash = hash(a.x) - proc hash(a: Obj3): Hash = hash((a.x,)) - proc hash(a: Obj5): Hash = - case a.t - of false: hash(a.x) - of true: hash(a.y) - - doAssert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2")) - doAssert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2")) - doAssert hash(Obj2[float](x: 520, y: "Nim")) != hash(Obj2[float](x: 521, y: "Nim2")) - doAssert hash(Obj3(x: 520, y: "Nim")) == hash(Obj3(x: 520, y: "Nim2")) - - doAssert hash(Obj4(t: false, x: 1)) == hash(Obj4(t: false, x: 1)) - doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: false, x: 2)) - doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: true, y: 1)) - - doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: false, x: 2)) - doAssert hash(Obj5(t: false, x: 1)) == hash(Obj5(t: true, y: 1)) - doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: true, y: 2)) - - block: # hash(ref|ptr|pointer) - var a: array[10, uint8] - # disableVm: - whenVMorJs: - # pending fix proposed in https://github.com/nim-lang/Nim/issues/15952#issuecomment-786312417 - discard - do: - doAssert a[0].addr.hash != a[1].addr.hash - doAssert cast[pointer](a[0].addr).hash == a[0].addr.hash - - block: # hash(ref) - type A = ref object - x: int - let a = A(x: 3) - disableVm: # xxx Error: VM does not support 'cast' from tyRef to tyPointer - let ha = a.hash - doAssert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`. - a.x = 4 - doAssert ha == a.hash # the hash only depends on the address - - block: # hash(proc) - proc fn(a: int): auto = a*2 - doAssert fn isnot "closure" - doAssert fn is (proc) - const fn2 = fn - let fn3 = fn - whenVMorJs: discard - do: - doAssert hash(fn2) == hash(fn) - doAssert hash(fn3) == hash(fn) - - block: # hash(closure) - proc outer() = - var a = 0 - proc inner() = a.inc - doAssert inner is "closure" - let inner2 = inner - whenVMorJs: discard - do: - doAssert hash(inner2) == hash(inner) - outer() static: main() main() diff --git a/tests/stdlib/theapqueue.nim b/tests/stdlib/theapqueue.nim index bb40b6f93..20e23e878 100644 --- a/tests/stdlib/theapqueue.nim +++ b/tests/stdlib/theapqueue.nim @@ -1,5 +1,5 @@ -import std/heapqueue -import std/assertions +import heapqueue + proc toSortedSeq[T](h: HeapQueue[T]): seq[T] = var tmp = h @@ -7,96 +7,47 @@ proc toSortedSeq[T](h: HeapQueue[T]): seq[T] = while tmp.len > 0: result.add(pop(tmp)) -proc heapProperty[T](h: HeapQueue[T]): bool = - for k in 0 .. h.len - 2: # the last element is always a leaf - let left = 2 * k + 1 - if left < h.len and h[left] < h[k]: - return false - let right = left + 1 - if right < h.len and h[right] < h[k]: - return false - true +block: # Simple sanity test + var heap = initHeapQueue[int]() + let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] + for item in data: + push(heap, item) + doAssert(heap == data.toHeapQueue) + doAssert(heap[0] == 0) + doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) -template main() = - block: # simple sanity test - var heap = initHeapQueue[int]() - let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] - for item in data: - push(heap, item) - doAssert(heap == data.toHeapQueue) - doAssert(heap[0] == 0) - doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) +block: # Test del + var heap = initHeapQueue[int]() + let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] + for item in data: push(heap, item) - block: # test del - var heap = initHeapQueue[int]() - let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] - for item in data: push(heap, item) + heap.del(0) + doAssert(heap[0] == 1) - heap.del(0) - doAssert(heap[0] == 1) + heap.del(heap.find(7)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9]) - heap.del(heap.find(7)) - doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9]) + heap.del(heap.find(5)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9]) - heap.del(heap.find(5)) - doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9]) + heap.del(heap.find(6)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9]) - heap.del(heap.find(6)) - doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9]) + heap.del(heap.find(2)) + doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9]) - heap.del(heap.find(2)) - doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9]) + doAssert(heap.find(2) == -1) - doAssert(heap.find(2) == -1) +block: # Test del last + var heap = initHeapQueue[int]() + let data = [1, 2, 3] + for item in data: push(heap, item) - block: # test del last - var heap = initHeapQueue[int]() - let data = [1, 2, 3] - for item in data: push(heap, item) + heap.del(2) + doAssert(heap.toSortedSeq == @[1, 2]) - heap.del(2) - doAssert(heap.toSortedSeq == @[1, 2]) + heap.del(1) + doAssert(heap.toSortedSeq == @[1]) - heap.del(1) - doAssert(heap.toSortedSeq == @[1]) - - heap.del(0) - doAssert(heap.toSortedSeq == @[]) - - block: # testing the heap proeprty - var heap = [1, 4, 2, 5].toHeapQueue - doAssert heapProperty(heap) - - heap.push(42) - doAssert heapProperty(heap) - heap.push(0) - doAssert heapProperty(heap) - heap.push(3) - doAssert heapProperty(heap) - heap.push(3) - doAssert heapProperty(heap) - - # [0, 3, 1, 4, 42, 2, 3, 5] - - discard heap.pop() - doAssert heapProperty(heap) - discard heap.pop() - doAssert heapProperty(heap) - - heap.del(2) - doAssert heapProperty(heap) - - # [2, 3, 5, 4, 42] - - discard heap.replace(12) - doAssert heapProperty(heap) - discard heap.replace(1) - doAssert heapProperty(heap) - - discard heap.pushpop(2) - doAssert heapProperty(heap) - discard heap.pushpop(0) - doAssert heapProperty(heap) - -static: main() -main() + heap.del(0) + doAssert(heap.toSortedSeq == @[]) diff --git a/tests/stdlib/thighlite.nim b/tests/stdlib/thighlite.nim deleted file mode 100644 index d88bc7fec..000000000 --- a/tests/stdlib/thighlite.nim +++ /dev/null @@ -1,42 +0,0 @@ - -import unittest, strutils -import ../../lib/packages/docutils/highlite -import std/objectdollar - -block: # Nim tokenizing - test "string literals and escape seq": - check("\"ok1\\nok2\\nok3\"".tokenize(langNim) == - @[("\"ok1", gtStringLit), ("\\n", gtEscapeSequence), ("ok2", gtStringLit), - ("\\n", gtEscapeSequence), ("ok3\"", gtStringLit) - ]) - check("\"\"\"ok1\\nok2\\nok3\"\"\"".tokenize(langNim) == - @[("\"\"\"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( - dedent""" - $ nim c file.nim - out: file [SuccessX]""" - .tokenize(langConsole) == - @[("$ ", gtPrompt), ("nim", gtProgram), - (" ", gtWhitespace), ("c", gtOption), (" ", gtWhitespace), - ("file.nim", gtIdentifier), ("\n", gtWhitespace), - ("out: file [SuccessX]", gtProgramOutput) - ]) - -block: # bug #21232 - let code = "/" - var toknizr: GeneralTokenizer - - initGeneralTokenizer(toknizr, code) - - getNextToken(toknizr, langC) - check $toknizr == """(kind: gtOperator, start: 0, length: 1, buf: "/", pos: 1, state: gtEof, lang: langC)""" diff --git a/tests/stdlib/thtmlparser.nim b/tests/stdlib/thtmlparser.nim index a27d41fe6..f35785b25 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 c731c81b0..6a634d90f 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -1,22 +1,15 @@ discard """ cmd: "nim c --threads:on -d:ssl $file" - disabled: "openbsd" - disabled: "freebsd" - disabled: "windows" + exitcode: 0 + output: "OK" + disabled: true """ -#[ -disabled: see https://github.com/timotheecour/Nim/issues/528 -]# - import strutils from net import TimeoutError import nativesockets, os, httpclient, asyncdispatch -import std/[assertions, syncio] -from stdtest/testutils import enableRemoteNetworking - const manualTests = false proc makeIPv6HttpServer(hostname: string, port: Port, @@ -24,7 +17,7 @@ proc makeIPv6HttpServer(hostname: string, port: Port, let fd = createNativeSocket(AF_INET6) setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1) var aiList = getAddrInfo(hostname, port, AF_INET6) - if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: + if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: freeAddrInfo(aiList) raiseOSError(osLastError()) freeAddrInfo(aiList) @@ -46,7 +39,7 @@ proc makeIPv6HttpServer(hostname: string, port: Port, proc asyncTest() {.async.} = var client = newAsyncHttpClient() - var resp = await client.request("http://example.com/", HttpGet) + var resp = await client.request("http://example.com/") doAssert(resp.code.is2xx) var body = await resp.body body = await resp.body # Test caching @@ -55,7 +48,7 @@ proc asyncTest() {.async.} = resp = await client.request("http://example.com/404") doAssert(resp.code.is4xx) doAssert(resp.code == Http404) - doAssert(resp.status == $Http404) + doAssert(resp.status == Http404) resp = await client.request("https://google.com/") doAssert(resp.code.is2xx or resp.code.is3xx) @@ -109,14 +102,14 @@ proc asyncTest() {.async.} = proc syncTest() = var client = newHttpClient() - var resp = client.request("http://example.com/", HttpGet) + var resp = client.request("http://example.com/") doAssert(resp.code.is2xx) doAssert("Example Domain" in resp.body) resp = client.request("http://example.com/404") doAssert(resp.code.is4xx) doAssert(resp.code == Http404) - doAssert(resp.status == $Http404) + doAssert(resp.status == Http404) resp = client.request("https://google.com/") doAssert(resp.code.is2xx or resp.code.is3xx) @@ -151,12 +144,6 @@ proc syncTest() = client.close() - # SIGSEGV on HEAD body read: issue #16743 - block: - let client = newHttpClient() - let resp = client.head("http://httpbin.org/head") - doAssert(resp.body == "") - when false: # Disabled for now because it causes troubles with AppVeyor # Timeout test. @@ -179,7 +166,7 @@ proc ipv6Test() = client.close() ipv6Test() +syncTest() +waitFor(asyncTest()) -when enableRemoteNetworking: - syncTest() - waitFor(asyncTest()) +echo "OK" diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim index feacd3e57..71745c9df 100644 --- a/tests/stdlib/thttpclient_ssl.nim +++ b/tests/stdlib/thttpclient_ssl.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim $target --mm:refc -d:ssl $options $file" + cmd: "nim $target --threads:on -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, @@ -101,7 +101,7 @@ when not defined(windows): let t = spawn runServer(port) sleep(100) - var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyNone)) + var client = newHttpClient() try: log "client: connect" discard client.getContent("https://127.0.0.1:12345") @@ -129,19 +129,3 @@ when not defined(windows): msg.contains("certificate verify failed")): echo "CVerifyPeer exception: " & msg check(false) - - test "HttpClient with CVerifyPeerUseEnvVars": - const port = 12346.Port - let t = spawn runServer(port) - sleep(100) - - putEnv("SSL_CERT_FILE", getCurrentDir() / certFile) - var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeerUseEnvVars)) - try: - log "client: connect" - discard client.getContent("https://127.0.0.1:12346") - except: - let msg = getCurrentExceptionMsg() - log "client: exception: " & msg - log "getContent should not have raised an exception" - fail() diff --git a/tests/stdlib/thttpclient_standalone.nim b/tests/stdlib/thttpclient_standalone.nim deleted file mode 100644 index 2f432eede..000000000 --- a/tests/stdlib/thttpclient_standalone.nim +++ /dev/null @@ -1,59 +0,0 @@ -discard """ - cmd: "nim c --threads:on $file" -""" - -import asynchttpserver, httpclient, asyncdispatch, strutils, net - -import std/assertions - -block: # bug #16436 - proc startServer(): AsyncHttpServer = - result = newAsyncHttpServer() - result.listen(Port(0)) - - proc processRequest(server: AsyncHttpServer) {.async.} = - proc cb(req: Request) {.async.} = - let headers = { "Content-length": "15"} # Provide invalid content-length - await req.respond(Http200, "Hello World", headers.newHttpHeaders()) - - await server.acceptRequest(cb) - - proc runClient(port: Port) {.async.} = - let c = newAsyncHttpClient(headers = {"Connection": "close"}.newHttpHeaders) - discard await c.getContent("http://127.0.0.1:" & $port) - doAssert false, "should fail earlier" - - let server = startServer() - asyncCheck processRequest(server) - let port = server.getPort() - doAssertRaises(ProtocolError): - waitFor runClient(port) - -block: # bug #14794 (And test for presence of content-length header when using postContent) - proc startServer(): AsyncHttpServer = - result = newAsyncHttpServer() - result.listen(Port(0)) - - proc runServer(server: AsyncHttpServer) {.async.} = - proc cb(req: Request) {.async.} = - doAssert(req.body.endsWith(httpNewLine), "Multipart body does not end with a newline.") - # this next line is probably not required because asynchttpserver does not call - # the callback when there is no content-length header. It instead errors with - # Error: unhandled exception: 411 Length Required - # Added for good measure in case the server becomes more permissive. - doAssert(req.headers.hasKey("content-length"), "Content-Length header is not present.") - asyncCheck req.respond(Http200, "OK") - - await server.acceptRequest(cb) - - proc runClient(port: Port) {.async.} = - let c = newAsyncHttpClient() - let data = newMultipartData() - data.add("file.txt", "This is intended to be an example text file.\r\nThis would be the second line.\r\n") - discard await c.postContent("http://127.0.0.1:" & $port, multipart = data) - c.close() - - let server = startServer() - let port = server.getPort() - asyncCheck runServer(server) - waitFor runClient(port) diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim index 3b6b1efa0..6f88e9536 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -1,5 +1,4 @@ import httpcore, strutils -import std/assertions block: block HttpCode: diff --git a/tests/stdlib/timportutils.nim b/tests/stdlib/timportutils.nim deleted file mode 100644 index 33afd7def..000000000 --- a/tests/stdlib/timportutils.nim +++ /dev/null @@ -1,147 +0,0 @@ -import std/[importutils, assertions] -import stdtest/testutils -import mimportutils - -template main = - block: # privateAccess - assertAll: - var a: A - var b = initB() # B is private - compiles(a.a0) - compiles(b.b0) - not compiles(a.ha1) - not compiles(b.hb1) - - block: - assertAll: - privateAccess A - compiles(a.ha1) - a.ha1 == 0.0 - not compiles(a.hb1) - privateAccess b.typeof - b.hb1 = 3 - type B2 = b.typeof - let b2 = B2(b0: 4, hb1: 5) - b.hb1 == 3 - b2 == B2(b0: 4, hb1: 5) - - assertAll: - not compiles(a.ha1) - not compiles(b.hb1) - - block: - assertAll: - not compiles(C(c0: 1, hc1: 2)) - privateAccess C - let c = C(c0: 1, hc1: 2) - c.hc1 == 2 - - block: - assertAll: - not compiles(E[int](he1: 1)) - privateAccess E[int] - var e = E[int](he1: 1) - e.he1 == 1 - e.he1 = 2 - e.he1 == 2 - e.he1 += 3 - e.he1 == 5 - # xxx caveat: this currently compiles but in future, we may want - # to make `privateAccess E[int]` only affect a specific instantiation; - # note that `privateAccess E` does work to cover all instantiations. - var e2 = E[float](he1: 1) - - block: - assertAll: - not compiles(E[int](he1: 1)) - privateAccess E - var e = E[int](he1: 1) - e.he1 == 1 - - block: - assertAll: - not compiles(F[int, int](h3: 1)) - privateAccess F[int, int] - var e = F[int, int](h3: 1) - e.h3 == 1 - - block: - assertAll: - not compiles(F[int, int](h3: 1)) - privateAccess F[int, int].default[].typeof - var e = F[int, int](h3: 1) - e.h3 == 1 - - block: - assertAll: - var a = G[int]() - var b = a.addr - privateAccess b.type - discard b.he1 - discard b[][].he1 - - block: - assertAll: - privateAccess H[int] - var a = H[int](h5: 2) - - block: - assertAll: - privateAccess PA - var pa = PA(a0: 1, ha1: 2) - pa.ha1 == 2 - pa.ha1 = 3 - pa.ha1 == 3 - - block: - assertAll: - var b = BAalias() - not compiles(b.hb1) - privateAccess BAalias - discard b.hb1 - - block: - assertAll: - var a = A(a0: 1) - var a2 = a.addr - not compiles(a2.ha1) - privateAccess PtA - a2.type is PtA - a2.ha1 = 2 - a2.ha1 == 2 - a.ha1 = 3 - a2.ha1 == 3 - - block: - disableVm: - assertAll: - var a = A.create() - defer: dealloc(a) - a is PtA - a.typeof is PtA - not compiles(a.ha1) - privateAccess a.typeof - a.ha1 = 2 - a.ha1 == 2 - a[].ha1 = 3 - a.ha1 == 3 - - block: - disableVm: - assertAll: - var a = A.create() - defer: dealloc(a) - privateAccess PtA - a.ha1 == 0 - - block: - privateAccess PityRef - let x = PityRef[int](a: 1) # works - doAssert x.a == 1 - - privateAccess Hope - let y = Hope[int](a: 1) - doAssert y.a == 1 - -static: main() -main() diff --git a/tests/stdlib/tio.nim b/tests/stdlib/tio.nim deleted file mode 100644 index ca411379c..000000000 --- a/tests/stdlib/tio.nim +++ /dev/null @@ -1,56 +0,0 @@ -# xxx move to here other tests that belong here; io is a proper module - -import std/os -from stdtest/specialpaths import buildDir -import std/[assertions, syncio] - -block: # readChars - let file = buildDir / "D20201118T205105.txt" - let s = "he\0l\0lo" - writeFile(file, s) - defer: removeFile(file) - let f = open(file) - defer: close(f) - let n = f.getFileInfo.blockSize - var buf = newString(n) - template fn = - let n2 = f.readChars(buf) - doAssert n2 == s.len - doAssert buf[0.. json => T2 => json2` is such that json2 = json - let j = %t - doAssert $j == expected, $j - doAssert %(j.to(T)) == j - -proc testRoundtripVal[T](t: T, expected: string) = - # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T` - # note that this isn't always possible, e.g. for pointer-like types or nans - let j = %t - doAssert $j == expected, $j - let j2 = ($j).parseJson - doAssert $j2 == expected, $(j2, t) - let t2 = j2.to(T) - doAssert t2 == t - doAssert $(%* t2) == expected # sanity check, because -0.0 = 0.0 but their json representation differs +import std/[json,parsejson,strutils,streams] let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}""" # nil passthrough @@ -51,7 +22,7 @@ for i in 0 .. 10000: except: discard # memory diff should less than 4M -doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # todo fixme doesn;t work for ORC +doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # test `$` @@ -263,119 +234,4 @@ doAssert isRefSkipDistinct(MyDistinct) doAssert isRefSkipDistinct(MyOtherDistinct) let x = parseJson("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999") - doAssert x.kind == JString - -block: # bug #15835 - type - Foo = object - ii*: int - data*: JsonNode - - block: - const jt = """{"ii": 123, "data": ["some", "data"]}""" - let js = parseJson(jt) - discard js.to(Foo) - - block: - const jt = """{"ii": 123}""" - let js = parseJson(jt) - doAssertRaises(KeyError): - echo js.to(Foo) - -type - ContentNodeKind* = enum - P, - Br, - Text, - ContentNode* = object - case kind*: ContentNodeKind - of P: pChildren*: seq[ContentNode] - of Br: nil - of Text: textStr*: string - -let mynode = ContentNode(kind: P, pChildren: @[ - ContentNode(kind: Text, textStr: "mychild"), - ContentNode(kind: Br) -]) - -doAssert $mynode == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])""" - -let jsonNode = %*mynode -doAssert $jsonNode == """{"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]}""" -doAssert $jsonNode.to(ContentNode) == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])""" - -block: # bug #17383 - testRoundtrip(int32.high): "2147483647" - testRoundtrip(uint32.high): "4294967295" - when int.sizeof == 4: - testRoundtrip(int.high): "2147483647" - testRoundtrip(uint.high): "4294967295" - else: - testRoundtrip(int.high): "9223372036854775807" - testRoundtrip(uint.high): "18446744073709551615" - when not defined(js): - testRoundtrip(int64.high): "9223372036854775807" - testRoundtrip(uint64.high): "18446744073709551615" - -block: # bug #18007 - testRoundtrip([NaN, Inf, -Inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]""" - # pending https://github.com/nim-lang/Nim/issues/18025 use: - # testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0]) - let inf = float32(Inf) - testRoundtrip([float32(NaN), inf, -inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]""" - when not defined(js): # because of Infinity vs inf - testRoundtripVal([inf, -inf, 0.0, -0.0, 1.0]): """["inf","-inf",0.0,-0.0,1.0]""" - let a = parseJson($(%NaN)).to(float) - doAssert a.isNaN - - whenRuntimeJs: discard # refs bug #18009 - do: - testRoundtripVal(0.0): "0.0" - testRoundtripVal(-0.0): "-0.0" - -block: # bug #15397, bug #13196 - testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" - testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" - -block: - let a = "18446744073709551615" - let b = a.parseJson - doAssert b.kind == JString - let c = $b - when defined(js): - doAssert c == "18446744073709552000" - else: - doAssert c == "18446744073709551615" - -block: - let a = """ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ - [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ -""" - - when not defined(js): - try: - discard parseJson(a) - except JsonParsingError: - doAssert getCurrentExceptionMsg().contains("] expected") diff --git a/tests/stdlib/tjson_unmarshall.nim b/tests/stdlib/tjson_unmarshall.nim new file mode 100644 index 000000000..69bed3ac9 --- /dev/null +++ b/tests/stdlib/tjson_unmarshall.nim @@ -0,0 +1,31 @@ +discard """ + output: ''' +Original: (kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)]) +jsonNode: {"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]} +Reversed: (kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)]) +''' +""" + +import json + +type + ContentNodeKind* = enum + P, + Br, + Text, + ContentNode* = object + case kind*: ContentNodeKind + of P: pChildren*: seq[ContentNode] + of Br: nil + of Text: textStr*: string + +let mynode = ContentNode(kind: P, pChildren: @[ + ContentNode(kind: Text, textStr: "mychild"), + ContentNode(kind: Br) +]) + +echo "Original: " & $mynode + +let jsonNode = %*mynode +echo "jsonNode: " & $jsonNode +echo "Reversed: " & $jsonNode.to(ContentNode) diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index 9c1fa833d..938030d8e 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -4,7 +4,6 @@ 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. @@ -260,7 +259,7 @@ proc testJson() = colors: array[2, BirdColor] var red = BirdColor(name: "red", rgb: [1.0, 0.0, 0.0]) - var blue = BirdColor(name: "blue", rgb: [0.0, 0.0, 1.0]) + var blue = Birdcolor(name: "blue", rgb: [0.0, 0.0, 1.0]) var b = Bird(age: 3, height: 1.734, name: "bardo", colors: [red, blue]) let jnode = %b let data = jnode.to(Bird) @@ -498,7 +497,7 @@ proc testJson() = doAssert array[3, float](t.arr) == [1.0,2.0,7.0] doAssert MyRef(t.person).name == "boney" - doAssert MyObj(t.distfruit).color == 11 + doAssert MyObj(t.distFruit).color == 11 doAssert t.dog.name == "honey" doAssert t.fruit.color == 10 doAssert seq[string](t.emails) == @["abc", "123"] @@ -634,18 +633,6 @@ proc testJson() = except KeyError: doAssert getCurrentExceptionMsg().contains ".member.list[2].value" - block: - # Enum indexed array test - type Test = enum - one, two, three, four, five - let a = [ - one: 300, - two: 20, - three: 10, - four: 0, - five: -10 - ] - doAssert (%* a).to(a.typeof) == a testJson() diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 1681f4a09..f56c32719 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -4,33 +4,16 @@ discard """ import std/jsonutils 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 let j = t.toJson - doAssert $j == expected, "\n" & $j & "\n" & expected + doAssert $j == expected, $j doAssert j.jsonTo(T).toJson == j var t2: T t2.fromJson(j) doAssert t2.toJson == j -proc testRoundtripVal[T](t: T, expected: string) = - # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T` - # note that this isn't always possible, e.g. for pointer-like types. - let j = t.toJson - let j2 = $j - doAssert j2 == expected, j2 - let j3 = j2.parseJson - let t2 = j3.jsonTo(T) - doAssert t2 == t - doAssert $t2.toJson == j2 # still needed, because -0.0 = 0.0 but their json representation differs - import tables, sets, algorithm, sequtils, options, strtabs -from strutils import contains type Foo = ref object id: int @@ -38,14 +21,7 @@ type Foo = ref object proc `==`(a, b: Foo): bool = a.id == b.id -type MyEnum = enum me0, me1 = "me1Alt", me2, me3, me4 - -proc `$`(a: MyEnum): string = - # putting this here pending https://github.com/nim-lang/Nim/issues/13747 - if a == me2: "me2Modif" - else: system.`$`(a) - -template fn() = +template fn() = block: # toJson, jsonTo type Foo = distinct float testRoundtrip('x', """120""") @@ -64,114 +40,34 @@ template fn() = # https://github.com/nim-lang/Nim/issues/12282 testRoundtrip(Foo(1.5)): """1.5""" - block: # OrderedTable + block: testRoundtrip({"z": "Z", "y": "Y"}.toOrderedTable): """{"z":"Z","y":"Y"}""" - doAssert toJson({"z": 10, "": 11}.newTable).`$`.contains """"":11""" # allows hash to change - testRoundtrip({"z".cstring: 1, "".cstring: 2}.toOrderedTable): """{"z":1,"":2}""" - testRoundtrip({"z": (f1: 'f'), }.toTable): """{"z":{"f1":102}}""" + when not defined(js): # pending https://github.com/nim-lang/Nim/issues/14574 + testRoundtrip({"z": (f1: 'f'), }.toTable): """{"z":{"f1":102}}""" - block: # StringTable + block: testRoundtrip({"name": "John", "city": "Monaco"}.newStringTable): """{"mode":"modeCaseSensitive","table":{"city":"Monaco","name":"John"}}""" block: # complex example let t = {"z": "Z", "y": "Y"}.newStringTable type A = ref object a1: string - let a = (1.1, "fo", 'x', @[10,11], [true, false], [t,newStringTable()], [0'i8,3'i8], -4'i16, (foo: 0.5'f32, bar: A(a1: "abc"), bar2: A.default, cstring1: "foo", cstring2: "", cstring3: cstring(nil))) + let a = (1.1, "fo", 'x', @[10,11], [true, false], [t,newStringTable()], [0'i8,3'i8], -4'i16, (foo: 0.5'f32, bar: A(a1: "abc"), bar2: A.default)) testRoundtrip(a): - """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null,"cstring1":"foo","cstring2":"","cstring3":null}]""" + """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null}]""" block: # edge case when user defined `==` doesn't handle `nil` well, e.g.: # https://github.com/nim-lang/nimble/blob/63695f490728e3935692c29f3d71944d83bb1e83/src/nimblepkg/version.nim#L105 testRoundtrip(@[Foo(id: 10), nil]): """[{"id":10},null]""" - block: # enum + block: type Foo = enum f1, f2, f3, f4, f5 type Bar = enum b1, b2, b3, b4 let a = [f2: b2, f3: b3, f4: b4] doAssert b2.ord == 1 # explains the `1` testRoundtrip(a): """[1,2,3]""" - block: # JsonNode - let a = ((1, 2.5, "abc").toJson, (3, 4.5, "foo")) - testRoundtripVal(a): """[[1,2.5,"abc"],[3,4.5,"foo"]]""" - - block: - template toInt(a): untyped = cast[int](a) - - let a = 3.toJson - let b = (a, a) - - let c1 = b.toJson - doAssert c1[0].toInt == a.toInt - doAssert c1[1].toInt == a.toInt - - let c2 = b.toJson(ToJsonOptions(jsonNodeMode: joptJsonNodeAsCopy)) - doAssert c2[0].toInt != a.toInt - doAssert c2[1].toInt != c2[0].toInt - doAssert c2[1] == c2[0] - - let c3 = b.toJson(ToJsonOptions(jsonNodeMode: joptJsonNodeAsObject)) - doAssert $c3 == """[{"isUnquoted":false,"kind":2,"num":3},{"isUnquoted":false,"kind":2,"num":3}]""" - - block: # ToJsonOptions - let a = (me1, me2) - doAssert $a.toJson() == "[1,2]" - doAssert $a.toJson(ToJsonOptions(enumMode: joptEnumSymbol)) == """["me1","me2"]""" - doAssert $a.toJson(ToJsonOptions(enumMode: joptEnumString)) == """["me1Alt","me2Modif"]""" - - block: # set - type Foo = enum f1, f2, f3, f4, f5 - type Goo = enum g1 = 10, g2 = 15, g3 = 17, g4 - let a = ({f1, f3}, {1'u8, 7'u8}, {'0'..'9'}, {123'u16, 456, 789, 1121, 1122, 1542}, {g2, g3}) - testRoundtrip(a): """[[0,2],[1,7],[48,49,50,51,52,53,54,55,56,57],[123,456,789,1121,1122,1542],[15,17]]""" - - block: # bug #17383 - block: - let a = (int32.high, uint32.high) - testRoundtrip(a): "[2147483647,4294967295]" - when int.sizeof > 4: - block: - let a = (int64.high, uint64.high) - testRoundtrip(a): "[9223372036854775807,18446744073709551615]" - block: - let a = (int.high, uint.high) - when int.sizeof == 4: - testRoundtrip(a): "[2147483647,4294967295]" - else: - testRoundtrip(a): "[9223372036854775807,18446744073709551615]" - - block: # bug #18007 - testRoundtrip((NaN, Inf, -Inf, 0.0, -0.0, 1.0)): """["nan","inf","-inf",0.0,-0.0,1.0]""" - testRoundtrip((float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0)): """["nan","inf","-inf",0.0,-0.0,1.0]""" - testRoundtripVal((Inf, -Inf, 0.0, -0.0, 1.0)): """["inf","-inf",0.0,-0.0,1.0]""" - doAssert ($NaN.toJson).parseJson.jsonTo(float).isNaN - - block: # bug #18009; unfixable unless we change parseJson (which would have overhead), - # but at least we can guarantee that the distinction between 0.0 and -0.0 is preserved. - let a = (0, 0.0, -0.0, 0.5, 1, 1.0) - testRoundtripVal(a): "[0,0.0,-0.0,0.5,1,1.0]" - let a2 = $($a.toJson).parseJson - whenRuntimeJs: - doAssert a2 == "[0,0,-0.0,0.5,1,1]" - do: - doAssert a2 == "[0,0.0,-0.0,0.5,1,1.0]" - let b = a2.parseJson.jsonTo(type(a)) - doAssert not b[1].signbit - doAssert b[2].signbit - doAssert not b[3].signbit - - block: # bug #15397, bug #13196 - let a = 0.1 - let x = 0.12345678901234567890123456789 - let b = (a + 0.2, 0.3, x) - testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]" - - testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" - testRoundtripVal(epsilon(float64)): "2.220446049250313e-16" - testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" - block: # case object type Foo = object x0: float @@ -227,9 +123,6 @@ template fn() = a: int b: string c: float - type Bar = object - foo: Foo - boo: string var f: seq[Foo] try: fromJson(f, parseJson """[{"b": "bbb"}]""") @@ -238,12 +131,6 @@ template fn() = doAssert true fromJson(f, parseJson """[{"b": "bbb"}]""", Joptions(allowExtraKeys: true, allowMissingKeys: true)) doAssert f == @[Foo(a: 0, b: "bbb", c: 0.0)] - var b: Bar - fromJson(b, parseJson """{"foo": {"b": "bbb"}}""", Joptions(allowExtraKeys: true, allowMissingKeys: true)) - doAssert b == Bar(foo: Foo(a: 0, b: "bbb", c: 0.0)) - block: # jsonTo with `opt` - let b2 = """{"foo": {"b": "bbb"}}""".parseJson.jsonTo(Bar, Joptions(allowExtraKeys: true, allowMissingKeys: true)) - doAssert b2 == Bar(foo: Foo(a: 0, b: "bbb", c: 0.0)) block testHashSet: testRoundtrip(HashSet[string]()): "[]" @@ -312,18 +199,6 @@ template fn() = doAssert guide == AboutLifeUniverseAndEverythingElse( question: "6*9=?", answer: 42) - block refObject: #bug 17986 - type A = ref object - case is_a: bool - of true: - a: int - else: - b: int - - var a = A() - fromJson(a, """{"is_a": true, "a":1, "extra_key": 1}""".parseJson, Joptions(allowExtraKeys: true)) - doAssert $a[] == "(is_a: true, a: 1)" - block testAllowMissingKeys: var guide = AboutLifeUniverseAndEverythingElse( question: "6*9=?", answer: 54) @@ -410,34 +285,6 @@ template fn() = doAssert foo.c == 0 doAssert foo.c0 == 42 - - block testInvalidTupleLength: - let json = parseJson("[0]") - # Should raise ValueError instead of index error - doAssertRaises(ValueError): - discard json.jsonTo((int, int)) - - type - InnerEnum = enum - A - B - C - InnerObject = object - x: string - y: InnerEnum - - block testOptionsArePassedWhenDeserialising: - let json = parseJson("""{"x": "hello"}""") - let inner = json.jsonTo(Option[InnerObject], Joptions(allowMissingKeys: true)) - doAssert inner.isSome() - doAssert inner.get().x == "hello" - doAssert inner.get().y == A - - block testOptionsArePassedWhenSerialising: - let inner = some InnerObject(x: "hello", y: A) - let json = inner.toJson(ToJsonOptions(enumMode: joptEnumSymbol)) - doAssert $json == """{"x":"hello","y":"A"}""" - when false: ## TODO: Implement support for nested variant objects allowing the tests ## bellow to pass. @@ -462,7 +309,7 @@ template fn() = """{"b": true, "bt": false, "btf": "test"}""" testRoundtrip(Variant(b: true, bt: true, btt: 'c')): """{"b": true, "bt": true, "btt": "c"}""" - + # TODO: Add additional tests with missing and extra JSON keys, both when # allowed and forbidden analogous to the tests for the not nested # variant objects. diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index 701fb7974..b5a3d3f7e 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -2,275 +2,149 @@ discard """ targets: "c js" """ -import std/[lists, sequtils] -import std/assertions +import lists, sequtils, std/enumerate, std/sugar const data = [1, 2, 3, 4, 5, 6] +block SinglyLinkedListTest1: + var L: SinglyLinkedList[int] + for d in items(data): L.prepend(d) + for d in items(data): L.append(d) + doAssert($L == "[6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6]") -template main = - block SinglyLinkedListTest1: - var L: SinglyLinkedList[int] - for d in items(data): L.prepend(d) - for d in items(data): L.add(d) - doAssert($L == "[6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6]") + doAssert(4 in L) - doAssert(4 in L) +block SinglyLinkedListTest2: + var L: SinglyLinkedList[string] + for d in items(data): L.prepend($d) + doAssert($L == """["6", "5", "4", "3", "2", "1"]""") - block SinglyLinkedListTest2: - var L: SinglyLinkedList[string] - for d in items(data): L.prepend($d) - doAssert($L == """["6", "5", "4", "3", "2", "1"]""") - - doAssert("4" in L) + doAssert("4" in L) - block DoublyLinkedListTest1: - var L: DoublyLinkedList[int] - for d in items(data): L.prepend(d) - for d in items(data): L.add(d) - L.remove(L.find(1)) - doAssert($L == "[6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6]") +block DoublyLinkedListTest1: + var L: DoublyLinkedList[int] + for d in items(data): L.prepend(d) + for d in items(data): L.append(d) + L.remove(L.find(1)) + doAssert($L == "[6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6]") - doAssert(4 in L) + doAssert(4 in L) - block SinglyLinkedRingTest1: - var L: SinglyLinkedRing[int] - L.prepend(4) - doAssert($L == "[4]") - L.prepend(4) +block SinglyLinkedRingTest1: + var L: SinglyLinkedRing[int] + L.prepend(4) + doAssert($L == "[4]") + L.prepend(4) - doAssert($L == "[4, 4]") - doAssert(4 in L) + doAssert($L == "[4, 4]") + doAssert(4 in L) - block DoublyLinkedRingTest1: - var L: DoublyLinkedRing[int] - L.prepend(4) - doAssert($L == "[4]") - L.prepend(4) +block DoublyLinkedRingTest1: + var L: DoublyLinkedRing[int] + L.prepend(4) + doAssert($L == "[4]") + L.prepend(4) - doAssert($L == "[4, 4]") - doAssert(4 in L) + doAssert($L == "[4, 4]") + doAssert(4 in L) - L.add(3) - L.add(5) - doAssert($L == "[4, 4, 3, 5]") + L.append(3) + L.append(5) + doAssert($L == "[4, 4, 3, 5]") - L.remove(L.find(3)) - L.remove(L.find(5)) - L.remove(L.find(4)) - L.remove(L.find(4)) - doAssert($L == "[]") - doAssert(4 notin L) + L.remove(L.find(3)) + L.remove(L.find(5)) + L.remove(L.find(4)) + L.remove(L.find(4)) + doAssert($L == "[]") + doAssert(4 notin L) - block tlistsToString: +block tlistsToString: + block: + var l = initDoublyLinkedList[int]() + l.append(1) + l.append(2) + l.append(3) + doAssert $l == "[1, 2, 3]" + block: + var l = initDoublyLinkedList[string]() + l.append("1") + l.append("2") + l.append("3") + doAssert $l == """["1", "2", "3"]""" + block: + var l = initDoublyLinkedList[char]() + l.append('1') + l.append('2') + l.append('3') + doAssert $l == """['1', '2', '3']""" + +template testCommon(initList, toList) = + + block: # toSinglyLinkedList, toDoublyLinkedList + let l = seq[int].default + doAssert l.toList.toSeq == [] + doAssert [1].toList.toSeq == [1] + doAssert [1, 2, 3].toList.toSeq == [1, 2, 3] + + block copy: + doAssert array[0, int].default.toList.copy.toSeq == [] + doAssert [1].toList.copy.toSeq == [1] + doAssert [1, 2].toList.copy.toSeq == [1, 2] + doAssert [1, 2, 3].toList.copy.toSeq == [1, 2, 3] + type Foo = ref object + x: int + var f0 = Foo(x: 0) + let f1 = Foo(x: 1) + var a = [f0].toList + var b = a.copy + b.append f1 + doAssert a.toSeq == [f0] + doAssert b.toSeq == [f0, f1] + f0.x = 42 + doAssert a.head.value.x == 42 + doAssert b.head.value.x == 42 + + block: # add, addMoved block: - var l = initDoublyLinkedList[int]() - l.add(1) - l.add(2) - l.add(3) - doAssert $l == "[1, 2, 3]" + var + l0 = initList[int]() + l1 = [1].toList + l2 = [2, 3].toList + l3 = [4, 5, 6].toList + l0.add l3 + l1.add l3 + l2.addMoved l3 + doAssert l0.toSeq == [4, 5, 6] + doAssert l1.toSeq == [1, 4, 5, 6] + doAssert l2.toSeq == [2, 3, 4, 5, 6] + doAssert l3.toSeq == [] + l2.add l3 # re-adding l3 that was destroyed is now a no-op + doAssert l2.toSeq == [2, 3, 4, 5, 6] + doAssert l3.toSeq == [] block: - var l = initDoublyLinkedList[string]() - l.add("1") - l.add("2") - l.add("3") - doAssert $l == """["1", "2", "3"]""" + var + l0 = initList[int]() + l1 = [1].toList + l2 = [2, 3].toList + l3 = [4, 5, 6].toList + l3.addMoved l0 + l2.addMoved l1 + doAssert l3.toSeq == [4, 5, 6] + doAssert l2.toSeq == [2, 3, 1] + l3.add l0 + doAssert l3.toSeq == [4, 5, 6] block: - var l = initDoublyLinkedList[char]() - l.add('1') - l.add('2') - l.add('3') - doAssert $l == """['1', '2', '3']""" + var c = [0, 1].toList + c.addMoved c + let s = collect: + for i, ci in enumerate(c): + if i == 6: break + ci + doAssert s == [0, 1, 0, 1, 0, 1] - # Copied here until it is merged into sequtils - template take(a: untyped, max: int): untyped = - type T = typeof(block: (for ai in a: ai)) - var ret: seq[T] - var i = 0 - if max > 0: - for ai in a: - ret.add ai - i.inc - if i >= max: break - ret - - template testCommon(initList, toList) = - - block: # toSinglyLinkedList, toDoublyLinkedList - let l = seq[int].default - doAssert l.toList.toSeq == [] - doAssert [1].toList.toSeq == [1] - doAssert [1, 2, 3].toList.toSeq == [1, 2, 3] - - block copy: - doAssert array[0, int].default.toList.copy.toSeq == [] - doAssert [1].toList.copy.toSeq == [1] - doAssert [1, 2].toList.copy.toSeq == [1, 2] - doAssert [1, 2, 3].toList.copy.toSeq == [1, 2, 3] - type Foo = ref object - x: int - var f0 = Foo(x: 0) - let f1 = Foo(x: 1) - var a = [f0].toList - var b = a.copy - b.add f1 - doAssert a.toSeq == [f0] - doAssert b.toSeq == [f0, f1] - f0.x = 42 - doAssert a.head.value.x == 42 - doAssert b.head.value.x == 42 - - block: # add, addMoved - block: - var - l0 = initList[int]() - l1 = [1].toList - l2 = [2, 3].toList - l3 = [4, 5, 6].toList - l0.add l3 - l1.add l3 - l2.addMoved l3 - doAssert l0.toSeq == [4, 5, 6] - doAssert l1.toSeq == [1, 4, 5, 6] - doAssert l2.toSeq == [2, 3, 4, 5, 6] - doAssert l3.toSeq == [] - l2.add l3 # re-adding l3 that was destroyed is now a no-op - doAssert l2.toSeq == [2, 3, 4, 5, 6] - doAssert l3.toSeq == [] - block: - var - l0 = initList[int]() - l1 = [1].toList - l2 = [2, 3].toList - l3 = [4, 5, 6].toList - l3.addMoved l0 - l2.addMoved l1 - doAssert l3.toSeq == [4, 5, 6] - doAssert l2.toSeq == [2, 3, 1] - l3.add l0 - doAssert l3.toSeq == [4, 5, 6] - block: - var c = [0, 1].toList - c.addMoved c - doAssert c.take(6) == [0, 1, 0, 1, 0, 1] - - block: # prepend, prependMoved - block: - var - l0 = initList[int]() - l1 = [1].toList - l2 = [2, 3].toList - l3 = [4, 5, 6].toList - l0.prepend l3 - l1.prepend l3 - doAssert l3.toSeq == [4, 5, 6] - l2.prependMoved l3 - doAssert l0.toSeq == [4, 5, 6] - doAssert l1.toSeq == [4, 5, 6, 1] - doAssert l2.toSeq == [4, 5, 6, 2, 3] - doAssert l3.toSeq == [] - l2.prepend l3 # re-prepending l3 that was destroyed is now a no-op - doAssert l2.toSeq == [4, 5, 6, 2, 3] - doAssert l3.toSeq == [] - block: - var - l0 = initList[int]() - l1 = [1].toList - l2 = [2, 3].toList - l3 = [4, 5, 6].toList - l3.prependMoved l0 - l2.prependMoved l1 - doAssert l3.toSeq == [4, 5, 6] - doAssert l2.toSeq == [1, 2, 3] - l3.prepend l0 - doAssert l3.toSeq == [4, 5, 6] - block: - var c = [0, 1].toList - c.prependMoved c - doAssert c.take(6) == [0, 1, 0, 1, 0, 1] - - block remove: - var l = [0, 1, 2, 3].toList - let - l0 = l.head - l1 = l0.next - l2 = l1.next - l3 = l2.next - l.remove l0 - doAssert l.toSeq == [1, 2, 3] - l.remove l2 - doAssert l.toSeq == [1, 3] - l.remove l2 - doAssert l.toSeq == [1, 3] - l.remove l3 - doAssert l.toSeq == [1] - l.remove l1 - doAssert l.toSeq == [] - # Cycle preservation - var a = [10, 11, 12].toList - a.addMoved a - doAssert a.take(6) == @[10, 11, 12, 10, 11, 12] - a.remove a.head.next - doAssert a.take(6) == @[10, 12, 10, 12, 10, 12] - a.remove a.head - doAssert a.take(6) == @[12, 12, 12, 12, 12, 12] - - testCommon initSinglyLinkedList, toSinglyLinkedList - testCommon initDoublyLinkedList, toDoublyLinkedList - - block remove: # return value check - var l = [0, 1, 2, 3].toSinglyLinkedList - let n = l.head.next.next - doAssert l.remove(n) == true - doAssert l.toSeq == [0, 1, 3] - doAssert l.remove(n) == false - doAssert l.toSeq == [0, 1, 3] - doAssert l.remove(l.head) == true - doAssert l.toSeq == [1, 3] - doAssert l.remove(l.head.next) == true - doAssert l.toSeq == [1] - doAssert l.remove(l.head) == true - doAssert l.toSeq == [] - - block issue19297: # add (appends a shallow copy) - var a: SinglyLinkedList[int] - var b: SinglyLinkedList[int] - - doAssert a.toSeq == @[] - a.add(1) - doAssert a.toSeq == @[1] - a.add(b) - doAssert a.toSeq == @[1] - a.add(2) - doAssert a.toSeq == @[1, 2] - - block issue19314: # add (appends a shallow copy) - var a: DoublyLinkedList[int] - var b: DoublyLinkedList[int] - - doAssert a.toSeq == @[] - a.add(1) - doAssert a.toSeq == @[1] - a.add(b) - doAssert a.toSeq == @[1] - a.add(2) - doAssert a.toSeq == @[1, 2] - - block RemoveLastNodeFromSinglyLinkedList: - var list = initSinglyLinkedList[string]() - let n1 = newSinglyLinkedNode("sonic") - let n2 = newSinglyLinkedNode("the") - let n3 = newSinglyLinkedNode("tiger") - let n4 = newSinglyLinkedNode("hedgehog") - list.add(n1) - list.add(n2) - list.add(n3) - list.remove(n3) - list.add(n4) - doAssert list.toSeq == @["sonic", "the", "hedgehog"] - -static: main() -main() +testCommon initSinglyLinkedList, toSinglyLinkedList +testCommon initDoublyLinkedList, toDoublyLinkedList diff --git a/tests/stdlib/tlocks.nim b/tests/stdlib/tlocks.nim index 9ce9afd13..e567cfde8 100644 --- a/tests/stdlib/tlocks.nim +++ b/tests/stdlib/tlocks.nim @@ -1,11 +1,10 @@ discard """ - targets: "c cpp js" - matrix: "--threads:on" + output: '''3''' + cmd: "nim $target --threads:on $options $file" """ #bug #6049 import uselocks -import std/assertions var m = createMyType[int]() -doAssert m.use() == 3 +echo $m.use() diff --git a/tests/stdlib/tlwip.nim b/tests/stdlib/tlwip.nim index fc53be592..7f7822236 100644 --- a/tests/stdlib/tlwip.nim +++ b/tests/stdlib/tlwip.nim @@ -1,6 +1,6 @@ discard """ targets: "c" - cmd: "nim $target --compileOnly --os:freertos --gc:arc $options $file" + cmd: "nim $target --os:freertos --gc:arc $options $file" disabled: "bsd" disabled: "windows" action: compile diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim index 9e3ebee83..0cc1e340c 100644 --- a/tests/stdlib/tmacros.nim +++ b/tests/stdlib/tmacros.nim @@ -1,11 +1,4 @@ -#[ -xxx macros tests need to be reorganized to makes sure each API is tested once -See also: - tests/macros/tdumpast.nim for treeRepr + friends -]# - -import std/macros -import std/assertions +import macros block: # hasArgOfName macro m(u: untyped): untyped = @@ -14,308 +7,3 @@ block: # hasArgOfName doAssert not hasArgOfName(params u,"nonexistent") proc p(s: string; i,j,k: int; b: bool; xs,ys: seq[int] = @[]) {.m.} = discard - -block: # bug #17454 - proc f(v: NimNode): string {.raises: [].} = $v - -block: # unpackVarargs - block: - proc bar1(a: varargs[int]): string = - for ai in a: result.add " " & $ai - proc bar2(a: varargs[int]) = - let s1 = bar1(a) - let s2 = unpackVarargs(bar1, a) # `unpackVarargs` makes no difference here - doAssert s1 == s2 - bar2(1, 2, 3) - bar2(1) - bar2() - - block: - template call1(fun: typed; args: varargs[untyped]): untyped = - unpackVarargs(fun, args) - template call2(fun: typed; args: varargs[untyped]): untyped = - # fun(args) # works except for last case with empty `args`, pending bug #9996 - when varargsLen(args) > 0: fun(args) - else: fun() - - proc fn1(a = 0, b = 1) = discard (a, b) - - call1(fn1) - call1(fn1, 10) - call1(fn1, 10, 11) - - call2(fn1) - call2(fn1, 10) - call2(fn1, 10, 11) - - block: - template call1(fun: typed; args: varargs[typed]): untyped = - unpackVarargs(fun, args) - template call2(fun: typed; args: varargs[typed]): untyped = - # xxx this would give a confusing error message: - # required type for a: varargs[typed] [varargs] but expression '[10]' is of type: varargs[typed] [varargs] - when varargsLen(args) > 0: fun(args) - else: fun() - macro toString(a: varargs[typed, `$`]): string = - var msg = genSym(nskVar, "msg") - result = newStmtList() - result.add quote do: - var `msg` = "" - for ai in a: - result.add quote do: `msg`.add $`ai` - result.add quote do: `msg` - doAssert call1(toString) == "" - doAssert call1(toString, 10) == "10" - doAssert call1(toString, 10, 11) == "1011" - -block: # SameType - type - A = int - B = distinct int - C = object - Generic[T, Y] = object - macro isSameType(a, b: typed): untyped = - newLit(sameType(a, b)) - - static: - assert Generic[int, int].isSameType(Generic[int, int]) - assert Generic[A, string].isSameType(Generic[int, string]) - assert not Generic[A, string].isSameType(Generic[B, string]) - assert not Generic[int, string].isSameType(Generic[int, int]) - assert isSameType(int, A) - assert isSameType(10, 20) - assert isSameType("Hello", "world") - assert not isSameType("Hello", cstring"world") - assert not isSameType(int, B) - assert not isSameType(int, Generic[int, int]) - assert not isSameType(C, string) - assert not isSameType(C, int) - - - #[ - # compiler sameType fails for the following, read more in `types.nim`'s `sameTypeAux`. - type - D[T] = C - G[T] = T - static: - assert isSameType(D[int], C) - assert isSameType(D[int], D[float]) - assert isSameType(G[float](1.0), float(1.0)) - assert isSameType(float(1.0), G[float](1.0)) - ]# - - type Tensor[T] = object - data: T - - macro testTensorInt(x: typed): untyped = - let - tensorIntType = getTypeInst(Tensor[int])[1] - xTyp = x.getTypeInst - - newLit(xTyp.sameType(tensorIntType)) - - var - x: Tensor[int] - x1 = Tensor[float]() - x2 = Tensor[A]() - x3 = Tensor[B]() - - static: - assert testTensorInt(x) - assert not testTensorInt(x1) - assert testTensorInt(x2) - assert not testTensorInt(x3) - -block: # extractDocCommentsAndRunnables - macro checkRunnables(prc: untyped) = - let runnables = prc.body.extractDocCommentsAndRunnables() - doAssert runnables[0][0].eqIdent("runnableExamples") - - macro checkComments(comment: static[string], prc: untyped) = - let comments = prc.body.extractDocCommentsAndRunnables() - doAssert comments[0].strVal == comment - - proc a() {.checkRunnables.} = - runnableExamples: discard - discard - - proc b() {.checkRunnables.} = - runnableExamples "-d:ssl": discard - discard - - 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" - -block hasCustomPragmaGeneric: - template examplePragma() {.pragma.} - type - Foo[T] {.examplePragma.} = object - x {.examplePragma.}: T - var f: Foo[string] - doAssert f.hasCustomPragma(examplePragma) - doAssert f.x.hasCustomPragma(examplePragma) - -block getCustomPragmaValGeneric: - template examplePragma(x: int) {.pragma.} - type - Foo[T] {.examplePragma(42).} = object - x {.examplePragma(25).}: T - var f: Foo[string] - doAssert f.getCustomPragmaVal(examplePragma) == 42 - doAssert f.x.getCustomPragmaVal(examplePragma) == 25 - -block: # bug #21326 - macro foo(body: untyped): untyped = - let a = body.lineInfoObj() - let aLit = a.newLit - result = quote do: - doAssert $`a` == $`aLit` - - foo: - let c = 1 - - template name(a: LineInfo): untyped = - discard a # `aLit` works though - - macro foo3(body: untyped): untyped = - let a = body.lineInfoObj() - # let ax = newLit(a) - result = getAst(name(a)) - - foo3: - let c = 1 - -block: # bug #7375 - macro fails(b: static[bool]): untyped = - doAssert b == false - result = newStmtList() - - macro foo(): untyped = - - var b = false - - ## Fails - result = quote do: - fails(`b`) - - foo() - - macro someMacro(): untyped = - template tmpl(boolean: bool) = - when boolean: - discard "it's true!" - else: - doAssert false - result = getAst(tmpl(true)) - - someMacro() - -block: - macro foo(): untyped = - result = quote do: `littleEndian` - - doAssert littleEndian == foo() - -block: - macro eqSym(x, y: untyped): untyped = - let eq = $x == $y # Unfortunately eqIdent compares to string. - result = quote do: `eq` - - var r, a, b: int - - template fma(result: var int, a, b: int, op: untyped) = - # fused multiple-add - when eqSym(op, `+=`): - discard "+=" - else: - discard "+" - - fma(r, a, b, `+=`) - -block: - template test(boolArg: bool) = - static: - doAssert typeof(boolArg) is bool - let x: bool = boolArg # compile error here, because boolArg became an int - - macro testWrapped1(boolArg: bool): untyped = - # forwarding boolArg directly works - result = getAst(test(boolArg)) - - macro testWrapped2(boolArg: bool): untyped = - # forwarding boolArg via a local variable also works - let b = boolArg - result = getAst(test(b)) - - macro testWrapped3(boolArg: bool): untyped = - # but using a literal `true` as a local variable will be converted to int - let b = true - result = getAst(test(b)) - - test(true) # ok - testWrapped1(true) # ok - testWrapped2(true) # ok - testWrapped3(true) - -block: - macro foo(): untyped = - var s = { 'a', 'b' } - quote do: - let t = `s` - doAssert $typeof(t) == "set[char]" - - foo() - -block: # bug #9607 - proc fun1(info:LineInfo): string = "bar" - proc fun2(info:int): string = "bar" - - macro echoL(args: varargs[untyped]): untyped = - let info = args.lineInfoObj - let fun1 = bindSym"fun1" - let fun2 = bindSym"fun2" - - # this would work instead - # result = newCall(bindSym"fun2", info.line.newLit) - - result = quote do: - - # BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil - `fun1`(`info`) - - macro echoM(args: varargs[untyped]): untyped = - let info = args.lineInfoObj - let fun1 = bindSym"fun1" - let fun2 = bindSym"fun2" - - # this would work instead - # result = newCall(bindSym"fun2", info.line.newLit) - - result = quote do: - - # BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil - `fun2`(`info`.line) - - - doAssert echoL() == "bar" - doAssert echoM() == "bar" - -block: - macro hello[T](x: T): untyped = - result = quote do: - let m: `T` = `x` - discard m - - hello(12) diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index f972332a2..d76be73f3 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -1,24 +1,32 @@ discard """ - matrix: "--mm:orc; --mm:refc" + output: '''{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"} +true +true +alpha 100 +omega 200 +Some(null) +None[JsonNode] +(numeric: "") +hello world +''' +joinable: false """ -import std/marshal -import std/[assertions, objectdollar] +#[ +joinable: false pending https://github.com/nim-lang/Nim/issues/9754 +]# -# TODO: add static tests +import marshal -proc testit[T](x: T): string = $$to[T]($$x) +template testit(x) = discard $$to[typeof(x)]($$x) -template check1 = - let test1: array[0..1, array[0..4, string]] = [ - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]] - doAssert testit(test1) == - """[["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]]""" - let test2: tuple[name: string, s: int] = ("tuple test", 56) - doAssert testit(test2) == """{"Field0": "tuple test", "Field1": 56}""" - -static: check1() -check1() +var x: array[0..4, array[0..4, string]] = [ + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"]] +testit(x) +var test2: tuple[name: string, s: int] = ("tuple test", 56) +testit(test2) type TE = enum @@ -49,86 +57,87 @@ proc buildList(): PNode = result.prev.next = result result.prev.prev = result.next -let test3 = TestObj(test: 42, test2: blah) -doAssert testit(test3) == - """{"test": 42, "asd": 0, "test2": "blah", "help": ""}""" +var test3: TestObj +test3.test = 42 +test3.test2 = blah +testit(test3) var test4: ref tuple[a, b: string] new(test4) test4.a = "ref string test: A" test4.b = "ref string test: B" -discard testit(test4) # serialization uses the pointer address, which is not consistent +testit(test4) -let test5 = @[(0,1),(2,3),(4,5)] -doAssert testit(test5) == - """[{"Field0": 0, "Field1": 1}, {"Field0": 2, "Field1": 3}, {"Field0": 4, "Field1": 5}]""" +var test5 = @[(0,1),(2,3),(4,5)] +testit(test5) -let test6: set[char] = {'A'..'Z', '_'} -doAssert testit(test6) == - """[65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 95]""" +var test7 = buildList() +testit(test7) -let test7 = buildList() -discard testit(test7) # serialization uses the pointer address, which is not consistent +var test6: set[char] = {'A'..'Z', '_'} +testit(test6) # bug #1352 -block: - type - Entity = object of RootObj - name: string - Person = object of Entity - age: int - bio: string - blob: string +type + Entity = object of RootObj + name: string - let instance1 = Person(name: "Cletus", age: 12, - bio: "Я Cletus", - blob: "ABC\x80") - doAssert $$instance1 == """{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}""" - doAssert to[Person]($$instance1).bio == instance1.bio - doAssert to[Person]($$instance1).blob == instance1.blob + Person = object of Entity + age: int + bio: string + blob: string -# bug #5757 -block: - type - Something = object - x: string - y: int +var instance1 = Person(name: "Cletus", age: 12, + bio: "Я Cletus", + blob: "ABC\x80") +echo($$instance1) +echo(to[Person]($$instance1).bio == instance1.bio) # true +echo(to[Person]($$instance1).blob == instance1.blob) # true - let data1 = """{"x": "alpha", "y": 100}""" - let data2 = """{"x": "omega", "y": 200}""" +# bug 5757 - var r = to[Something](data1) - doAssert $r.x & " " & $r.y == "alpha 100" - r = to[Something](data2) - doAssert $r.x & " " & $r.y == "omega 200" +type + Something = object + x: string + y: int -block: - type - Foo = object - a1: string - a2: string - a3: seq[string] - a4: seq[int] - a5: seq[int] - a6: seq[int] - var foo = Foo(a2: "", a4: @[], a6: @[1]) - foo.a6.setLen 0 - doAssert $$foo == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}""" - doAssert testit(foo) == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}""" +var data1 = """{"x": "alpha", "y": 100}""" +var data2 = """{"x": "omega", "y": 200}""" -import std/[options, json] +var r = to[Something](data1) + +echo r.x, " ", r.y + +r = to[Something](data2) + +echo r.x, " ", r.y + + +type + Foo = object + a1: string + a2: string + a3: seq[string] + a4: seq[int] + a5: seq[int] + a6: seq[int] +var foo = Foo(a2: "", a4: @[], a6: @[1]) +foo.a6.setLen 0 +doAssert $$foo == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}""" +testit(foo) + +import options, json # bug #15934 block: let a1 = some(newJNull()) a2 = none(JsonNode) - doAssert $($$a1).to[:Option[JsonNode]] == "some(null)" - doAssert $($$a2).to[:Option[JsonNode]] == "none(JsonNode)" - doAssert ($$a1).to[:Option[JsonNode]] == some(newJNull()) - doAssert ($$a2).to[:Option[JsonNode]] == none(JsonNode) + echo ($$a1).to[:Option[JsonNode]] + echo ($$a2).to[:Option[JsonNode]] + # bug #15620 block: @@ -139,55 +148,10 @@ block: numeric: string 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: @[])""" + echo test # bug #16022 block: - let p: proc (): string = proc (): string = "hello world" - let poc = to[typeof(p)]($$p) - doAssert poc() == "hello world" - -block: - type - A {.inheritable.} = object - B = object of A - f: int - - let a: ref A = new(B) - doAssert $$a[] == "{}" # not "{f: 0}" - -template checkMarshal(data: typed) = - let orig = data - let m = $$orig - - let old = to[typeof(orig)](m) - doAssert data == old - -template main() = - type - Book = object - page: int - name: string - - let book = Book(page: 12, name: "persona") - - checkMarshal(486) - checkMarshal(3.14) - checkMarshal("azure sky") - checkMarshal(book) - checkMarshal([1, 2, 3]) - checkMarshal(@[1.5, 2.7, 3.9, 4.2]) - checkMarshal(@["dream", "is", "possible"]) - -static: main() -main() + let p: proc () = proc () = echo "hello world" + let poc = (to[typeof(p)]($$p)) + poc() diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 66c1f8ca0..e5cb58eba 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -6,281 +6,320 @@ discard """ # xxx: there should be a test with `-d:nimTmathCase2 -d:danger --passc:-ffast-math`, # but it requires disabling certain lines with `when not defined(nimTmathCase2)` -import std/math -import std/assertions +import std/[math, random, os] +import std/[unittest] +import std/[sets, tables] # Function for approximate comparison of floats -proc `==~`(x, y: float): bool = abs(x - y) < 1e-9 +proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9) +block: # random int + block: # there might be some randomness + var set = initHashSet[int](128) -template main() = - block: + for i in 1..1000: + incl(set, rand(high(int))) + check len(set) == 1000 + + block: # single number bounds work + var rand: int + for i in 1..1000: + rand = rand(1000) + check rand < 1000 + check rand > -1 + + block: # slice bounds work + var rand: int + for i in 1..1000: + rand = rand(100..1000) + when defined(js): # xxx bug: otherwise fails + check rand <= 1000 + else: + check rand < 1000 + check rand >= 100 + + block: # again gives new numbers + var rand1 = rand(1000000) when not defined(js): - # check for no side effect annotation - proc mySqrt(num: float): float {.noSideEffect.} = - # xxx unused - sqrt(num) + os.sleep(200) - # check gamma function - doAssert gamma(5.0) == 24.0 # 4! - doAssert almostEqual(gamma(0.5), sqrt(PI)) - doAssert almostEqual(gamma(-0.5), -2 * sqrt(PI)) - doAssert lgamma(1.0) == 0.0 # ln(1.0) == 0.0 - doAssert almostEqual(lgamma(0.5), 0.5 * ln(PI)) - doAssert erf(6.0) > erf(5.0) - doAssert erfc(6.0) < erfc(5.0) + var rand2 = rand(1000000) + check rand1 != rand2 - block: # sgn() tests - doAssert sgn(1'i8) == 1 - doAssert sgn(1'i16) == 1 - doAssert sgn(1'i32) == 1 - doAssert sgn(1'i64) == 1 - doAssert sgn(1'u8) == 1 - doAssert sgn(1'u16) == 1 - doAssert sgn(1'u32) == 1 - doAssert sgn(1'u64) == 1 - doAssert sgn(-12342.8844'f32) == -1 - doAssert sgn(123.9834'f64) == 1 - doAssert sgn(0'i32) == 0 - doAssert sgn(0'f32) == 0 - doAssert sgn(-0.0'f64) == 0 - doAssert sgn(NegInf) == -1 - doAssert sgn(Inf) == 1 - doAssert sgn(NaN) == 0 - block: # fac() tests - when nimvm: discard - else: +block: # random float + block: # there might be some randomness + var set = initHashSet[float](128) + + for i in 1..100: + incl(set, rand(1.0)) + check len(set) == 100 + + block: # single number bounds work + var rand: float + for i in 1..1000: + rand = rand(1000.0) + check rand < 1000.0 + check rand > -1.0 + + block: # slice bounds work + var rand: float + for i in 1..1000: + rand = rand(100.0..1000.0) + check rand < 1000.0 + check rand >= 100.0 + + block: # again gives new numbers + + var rand1:float = rand(1000000.0) + when not defined(js): + os.sleep(200) + + var rand2:float = rand(1000000.0) + check rand1 != rand2 + +block: # cumsum + block: # cumsum int seq return + let counts = [ 1, 2, 3, 4 ] + check counts.cumsummed == [ 1, 3, 6, 10 ] + + block: # cumsum float seq return + let counts = [ 1.0, 2.0, 3.0, 4.0 ] + check counts.cumsummed == [ 1.0, 3.0, 6.0, 10.0 ] + + block: # cumsum int in-place + var counts = [ 1, 2, 3, 4 ] + counts.cumsum + check counts == [ 1, 3, 6, 10 ] + + block: # cumsum float in-place + var counts = [ 1.0, 2.0, 3.0, 4.0 ] + counts.cumsum + check counts == [ 1.0, 3.0, 6.0, 10.0 ] + +block: # random sample + block: # "non-uniform array sample unnormalized int CDF + let values = [ 10, 20, 30, 40, 50 ] # values + let counts = [ 4, 3, 2, 1, 0 ] # weights aka unnormalized probabilities + var histo = initCountTable[int]() + let cdf = counts.cumsummed # unnormalized CDF + for i in 0 ..< 5000: + histo.inc(sample(values, cdf)) + check histo.len == 4 # number of non-zero in `counts` + # Any one bin is a binomial random var for n samples, each with prob p of + # adding a count to k; E[k]=p*n, Var k=p*(1-p)*n, approximately Normal for + # big n. So, P(abs(k - p*n)/sqrt(p*(1-p)*n))>3.0) =~ 0.0027, while + # P(wholeTestFails) =~ 1 - P(binPasses)^4 =~ 1 - (1-0.0027)^4 =~ 0.01. + for i, c in counts: + if c == 0: + check values[i] notin histo + continue + let p = float(c) / float(cdf[^1]) + let n = 5000.0 + let expected = p * n + let stdDev = sqrt(n * p * (1.0 - p)) + check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev + + block: # non-uniform array sample normalized float CDF + let values = [ 10, 20, 30, 40, 50 ] # values + let counts = [ 0.4, 0.3, 0.2, 0.1, 0 ] # probabilities + var histo = initCountTable[int]() + let cdf = counts.cumsummed # normalized CDF + for i in 0 ..< 5000: + histo.inc(sample(values, cdf)) + check histo.len == 4 # number of non-zero in ``counts`` + for i, c in counts: + if c == 0: + check values[i] notin histo + continue + let p = float(c) / float(cdf[^1]) + let n = 5000.0 + let expected = p * n + let stdDev = sqrt(n * p * (1.0 - p)) + # NOTE: like unnormalized int CDF test, P(wholeTestFails) =~ 0.01. + check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev + +block: # ^ + block: # compiles for valid types + check: compiles(5 ^ 2) + check: compiles(5.5 ^ 2) + check: compiles(5.5 ^ 2.int8) + check: compiles(5.5 ^ 2.uint) + check: compiles(5.5 ^ 2.uint8) + check: not compiles(5.5 ^ 2.2) + +block: + when not defined(js): + # Check for no side effect annotation + proc mySqrt(num: float): float {.noSideEffect.} = + # xxx unused + return sqrt(num) + + # check gamma function + doAssert(gamma(5.0) == 24.0) # 4! + doAssert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 + doAssert(erf(6.0) > erf(5.0)) + doAssert(erfc(6.0) < erfc(5.0)) + + block: # prod + doAssert prod([1, 2, 3, 4]) == 24 + doAssert prod([1.5, 3.4]).almostEqual 5.1 + let x: seq[float] = @[] + doAssert prod(x) == 1.0 + + block: # round() tests + # Round to 0 decimal places + doAssert round(54.652) == 55.0 + doAssert round(54.352) == 54.0 + doAssert round(-54.652) == -55.0 + doAssert round(-54.352) == -54.0 + doAssert round(0.0) == 0.0 + + block: # splitDecimal() tests + doAssert splitDecimal(54.674).intpart == 54.0 + doAssert splitDecimal(54.674).floatpart ==~ 0.674 + doAssert splitDecimal(-693.4356).intpart == -693.0 + doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356 + doAssert splitDecimal(0.0).intpart == 0.0 + doAssert splitDecimal(0.0).floatpart == 0.0 + + block: # trunc tests for vcc + doAssert(trunc(-1.1) == -1) + doAssert(trunc(1.1) == 1) + doAssert(trunc(-0.1) == -0) + doAssert(trunc(0.1) == 0) + + #special case + doAssert(classify(trunc(1e1000000)) == fcInf) + doAssert(classify(trunc(-1e1000000)) == fcNegInf) + when not defined(nimTmathCase2): + doAssert(classify(trunc(0.0/0.0)) == fcNan) + doAssert(classify(trunc(0.0)) == fcZero) + + #trick the compiler to produce signed zero + let + f_neg_one = -1.0 + f_zero = 0.0 + f_nan = f_zero / f_zero + + doAssert(classify(trunc(f_neg_one*f_zero)) == fcNegZero) + + doAssert(trunc(-1.1'f32) == -1) + doAssert(trunc(1.1'f32) == 1) + doAssert(trunc(-0.1'f32) == -0) + doAssert(trunc(0.1'f32) == 0) + doAssert(classify(trunc(1e1000000'f32)) == fcInf) + doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf) + when not defined(nimTmathCase2): + doAssert(classify(trunc(f_nan.float32)) == fcNan) + doAssert(classify(trunc(0.0'f32)) == fcZero) + + block: # sgn() tests + doAssert sgn(1'i8) == 1 + doAssert sgn(1'i16) == 1 + doAssert sgn(1'i32) == 1 + doAssert sgn(1'i64) == 1 + doAssert sgn(1'u8) == 1 + doAssert sgn(1'u16) == 1 + doAssert sgn(1'u32) == 1 + doAssert sgn(1'u64) == 1 + doAssert sgn(-12342.8844'f32) == -1 + doAssert sgn(123.9834'f64) == 1 + doAssert sgn(0'i32) == 0 + doAssert sgn(0'f32) == 0 + doAssert sgn(NegInf) == -1 + doAssert sgn(Inf) == 1 + doAssert sgn(NaN) == 0 + + block: # fac() tests try: discard fac(-1) except AssertionDefect: discard - doAssert fac(0) == 1 - doAssert fac(1) == 1 - doAssert fac(2) == 2 - doAssert fac(3) == 6 - doAssert fac(4) == 24 - doAssert fac(5) == 120 + doAssert fac(0) == 1 + doAssert fac(1) == 1 + doAssert fac(2) == 2 + doAssert fac(3) == 6 + doAssert fac(4) == 24 - block: # floorMod/floorDiv - doAssert floorDiv(8, 3) == 2 - doAssert floorMod(8, 3) == 2 + block: # floorMod/floorDiv + doAssert floorDiv(8, 3) == 2 + doAssert floorMod(8, 3) == 2 - doAssert floorDiv(8, -3) == -3 - doAssert floorMod(8, -3) == -1 + doAssert floorDiv(8, -3) == -3 + doAssert floorMod(8, -3) == -1 - doAssert floorDiv(-8, 3) == -3 - doAssert floorMod(-8, 3) == 1 + doAssert floorDiv(-8, 3) == -3 + doAssert floorMod(-8, 3) == 1 - doAssert floorDiv(-8, -3) == 2 - doAssert floorMod(-8, -3) == -2 + doAssert floorDiv(-8, -3) == 2 + doAssert floorMod(-8, -3) == -2 - doAssert floorMod(8.0, -3.0) == -1.0 - doAssert floorMod(-8.5, 3.0) == 0.5 + doAssert floorMod(8.0, -3.0) == -1.0 + doAssert floorMod(-8.5, 3.0) == 0.5 - block: # euclDiv/euclMod - doAssert euclDiv(8, 3) == 2 - doAssert euclMod(8, 3) == 2 + block: # euclDiv/euclMod + doAssert euclDiv(8, 3) == 2 + doAssert euclMod(8, 3) == 2 - doAssert euclDiv(8, -3) == -2 - doAssert euclMod(8, -3) == 2 + doAssert euclDiv(8, -3) == -2 + doAssert euclMod(8, -3) == 2 - doAssert euclDiv(-8, 3) == -3 - doAssert euclMod(-8, 3) == 1 + doAssert euclDiv(-8, 3) == -3 + doAssert euclMod(-8, 3) == 1 - doAssert euclDiv(-8, -3) == 3 - doAssert euclMod(-8, -3) == 1 + doAssert euclDiv(-8, -3) == 3 + doAssert euclMod(-8, -3) == 1 - doAssert euclMod(8.0, -3.0) == 2.0 - doAssert euclMod(-8.5, 3.0) == 0.5 + doAssert euclMod(8.0, -3.0) == 2.0 + doAssert euclMod(-8.5, 3.0) == 0.5 - doAssert euclDiv(9, 3) == 3 - doAssert euclMod(9, 3) == 0 + doAssert euclDiv(9, 3) == 3 + doAssert euclMod(9, 3) == 0 - doAssert euclDiv(9, -3) == -3 - doAssert euclMod(9, -3) == 0 + doAssert euclDiv(9, -3) == -3 + doAssert euclMod(9, -3) == 0 - doAssert euclDiv(-9, 3) == -3 - doAssert euclMod(-9, 3) == 0 + doAssert euclDiv(-9, 3) == -3 + doAssert euclMod(-9, 3) == 0 - doAssert euclDiv(-9, -3) == 3 - doAssert euclMod(-9, -3) == 0 + doAssert euclDiv(-9, -3) == 3 + doAssert euclMod(-9, -3) == 0 - block: # ceilDiv - doAssert ceilDiv(8, 3) == 3 - doAssert ceilDiv(8, 4) == 2 - doAssert ceilDiv(8, 5) == 2 - doAssert ceilDiv(11, 3) == 4 - doAssert ceilDiv(12, 3) == 4 - doAssert ceilDiv(13, 3) == 5 - doAssert ceilDiv(41, 7) == 6 - doAssert ceilDiv(0, 1) == 0 - doAssert ceilDiv(1, 1) == 1 - doAssert ceilDiv(1, 2) == 1 - doAssert ceilDiv(2, 1) == 2 - doAssert ceilDiv(2, 2) == 1 - doAssert ceilDiv(0, high(int)) == 0 - doAssert ceilDiv(1, high(int)) == 1 - doAssert ceilDiv(0, high(int) - 1) == 0 - doAssert ceilDiv(1, high(int) - 1) == 1 - doAssert ceilDiv(high(int) div 2, high(int) div 2 + 1) == 1 - doAssert ceilDiv(high(int) div 2, high(int) div 2 + 2) == 1 - doAssert ceilDiv(high(int) div 2 + 1, high(int) div 2) == 2 - doAssert ceilDiv(high(int) div 2 + 2, high(int) div 2) == 2 - doAssert ceilDiv(high(int) div 2 + 1, high(int) div 2 + 1) == 1 - doAssert ceilDiv(high(int), 1) == high(int) - doAssert ceilDiv(high(int) - 1, 1) == high(int) - 1 - doAssert ceilDiv(high(int) - 1, 2) == high(int) div 2 - doAssert ceilDiv(high(int) - 1, high(int)) == 1 - doAssert ceilDiv(high(int) - 1, high(int) - 1) == 1 - doAssert ceilDiv(high(int) - 1, high(int) - 2) == 2 - doAssert ceilDiv(high(int), high(int)) == 1 - doAssert ceilDiv(high(int), high(int) - 1) == 2 - doAssert ceilDiv(255'u8, 1'u8) == 255'u8 - doAssert ceilDiv(254'u8, 2'u8) == 127'u8 - when not defined(danger): - doAssertRaises(AssertionDefect): discard ceilDiv(41, 0) - doAssertRaises(AssertionDefect): discard ceilDiv(41, -1) - doAssertRaises(AssertionDefect): discard ceilDiv(-1, 1) - doAssertRaises(AssertionDefect): discard ceilDiv(-1, -1) - doAssertRaises(AssertionDefect): discard ceilDiv(254'u8, 3'u8) - doAssertRaises(AssertionDefect): discard ceilDiv(255'u8, 2'u8) + block: # log + doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0) + doAssert log2(8.0'f64) == 3.0'f64 + doAssert log2(4.0'f64) == 2.0'f64 + doAssert log2(2.0'f64) == 1.0'f64 + doAssert log2(1.0'f64) == 0.0'f64 + doAssert classify(log2(0.0'f64)) == fcNegInf - block: # splitDecimal() tests - doAssert splitDecimal(54.674).intpart == 54.0 - doAssert splitDecimal(54.674).floatpart ==~ 0.674 - doAssert splitDecimal(-693.4356).intpart == -693.0 - doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356 - doAssert splitDecimal(0.0).intpart == 0.0 - doAssert splitDecimal(0.0).floatpart == 0.0 - - block: # trunc tests for vcc - doAssert trunc(-1.1) == -1 - doAssert trunc(1.1) == 1 - doAssert trunc(-0.1) == -0 - doAssert trunc(0.1) == 0 - - # special case - doAssert classify(trunc(1e1000000)) == fcInf - doAssert classify(trunc(-1e1000000)) == fcNegInf - when not defined(nimTmathCase2): - doAssert classify(trunc(0.0/0.0)) == fcNan - doAssert classify(trunc(0.0)) == fcZero - - # trick the compiler to produce signed zero - let - f_neg_one = -1.0 - f_zero = 0.0 - f_nan = f_zero / f_zero - - doAssert classify(trunc(f_neg_one*f_zero)) == fcNegZero - - doAssert trunc(-1.1'f32) == -1 - doAssert trunc(1.1'f32) == 1 - doAssert trunc(-0.1'f32) == -0 - doAssert trunc(0.1'f32) == 0 - doAssert classify(trunc(1e1000000'f32)) == fcInf - doAssert classify(trunc(-1e1000000'f32)) == fcNegInf - when not defined(nimTmathCase2): - doAssert classify(trunc(f_nan.float32)) == fcNan - doAssert classify(trunc(0.0'f32)) == fcZero - - block: # log - doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0) - doAssert log2(8.0'f64) == 3.0'f64 - doAssert log2(4.0'f64) == 2.0'f64 - doAssert log2(2.0'f64) == 1.0'f64 - doAssert log2(1.0'f64) == 0.0'f64 - doAssert classify(log2(0.0'f64)) == fcNegInf - - doAssert log2(8.0'f32) == 3.0'f32 - doAssert log2(4.0'f32) == 2.0'f32 - doAssert log2(2.0'f32) == 1.0'f32 - doAssert log2(1.0'f32) == 0.0'f32 - doAssert classify(log2(0.0'f32)) == fcNegInf - - block: # cumsum - block: # cumsum int seq return - let counts = [1, 2, 3, 4] - doAssert counts.cumsummed == @[1, 3, 6, 10] - let empty: seq[int] = @[] - doAssert empty.cumsummed == @[] - - block: # cumsum float seq return - let counts = [1.0, 2.0, 3.0, 4.0] - doAssert counts.cumsummed == @[1.0, 3.0, 6.0, 10.0] - let empty: seq[float] = @[] - doAssert empty.cumsummed == @[] - - block: # cumsum int in-place - var counts = [1, 2, 3, 4] - counts.cumsum - doAssert counts == [1, 3, 6, 10] - var empty: seq[int] = @[] - empty.cumsum - doAssert empty == @[] - - block: # cumsum float in-place - var counts = [1.0, 2.0, 3.0, 4.0] - counts.cumsum - doAssert counts == [1.0, 3.0, 6.0, 10.0] - var empty: seq[float] = @[] - empty.cumsum - doAssert empty == @[] - - block: # ^ compiles for valid types - doAssert: compiles(5 ^ 2) - doAssert: compiles(5.5 ^ 2) - doAssert: compiles(5.5 ^ 2.int8) - doAssert: compiles(5.5 ^ 2.uint) - doAssert: compiles(5.5 ^ 2.uint8) - doAssert: not compiles(5.5 ^ 2.2) + doAssert log2(8.0'f32) == 3.0'f32 + doAssert log2(4.0'f32) == 2.0'f32 + doAssert log2(2.0'f32) == 1.0'f32 + doAssert log2(1.0'f32) == 0.0'f32 + doAssert classify(log2(0.0'f32)) == fcNegInf +template main = + # xxx wrap all under `main` so it also gets tested in vm. block: # isNaN doAssert NaN.isNaN doAssert not Inf.isNaN doAssert isNaN(Inf - Inf) - doAssert not isNaN(0.0) - doAssert not isNaN(3.1415926) - doAssert not isNaN(0'f32) - - block: # signbit - doAssert not signbit(0.0) - doAssert signbit(-0.0) - doAssert signbit(-0.1) - doAssert not signbit(0.1) - - doAssert not signbit(Inf) - doAssert signbit(-Inf) - doAssert not signbit(NaN) - - let x1 = NaN - let x2 = -NaN - let x3 = -x1 - - doAssert isNaN(x1) - doAssert isNaN(x2) - doAssert isNaN(x3) - doAssert not signbit(x1) - doAssert signbit(x2) - doAssert signbit(x3) block: # copySign - doAssert copySign(10.0, 1.0) == 10.0 doAssert copySign(10.0, -1.0) == -10.0 doAssert copySign(-10.0, -1.0) == -10.0 doAssert copySign(-10.0, 1.0) == 10.0 doAssert copySign(float(10), -1.0) == -10.0 - doAssert copySign(10.0'f64, 1.0) == 10.0 doAssert copySign(10.0'f64, -1.0) == -10.0 doAssert copySign(-10.0'f64, -1.0) == -10.0 doAssert copySign(-10.0'f64, 1.0) == 10.0 doAssert copySign(10'f64, -1.0) == -10.0 - doAssert copySign(10.0'f32, 1.0) == 10.0 doAssert copySign(10.0'f32, -1.0) == -10.0 doAssert copySign(-10.0'f32, -1.0) == -10.0 doAssert copySign(-10.0'f32, 1.0) == 10.0 @@ -310,22 +349,8 @@ template main() = doAssert copySign(-NaN, 0.0).isNaN doAssert copySign(-NaN, -0.0).isNaN - doAssert copySign(-1.0, NaN) == 1.0 - doAssert copySign(-1.0, -NaN) == -1.0 - doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0 - - block: # almostEqual - doAssert almostEqual(3.141592653589793, 3.1415926535897936) - doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32) - doAssert almostEqual(Inf, Inf) - doAssert almostEqual(-Inf, -Inf) - doAssert not almostEqual(Inf, -Inf) - doAssert not almostEqual(-Inf, Inf) - doAssert not almostEqual(Inf, NaN) - doAssert not almostEqual(NaN, NaN) - - block: # round - block: # Round to 0 decimal places + block: # round() tests + # Round to 0 decimal places doAssert round(54.652) == 55.0 doAssert round(54.352) == 54.0 doAssert round(-54.652) == -55.0 @@ -346,18 +371,15 @@ template main() = doAssert round(2.5'f32) == 3.0'f32 doAssert round(2.5'f64) == 3.0'f64 - block: # func round*[T: float32|float64](x: T, places: int): T - doAssert round(54.345, 0) == 54.0 - template fn(x) = - doAssert round(x, 2).almostEqual 54.35 - doAssert round(x, 2).almostEqual 54.35 - doAssert round(x, -1).almostEqual 50.0 - doAssert round(x, -2).almostEqual 100.0 - doAssert round(x, -3).almostEqual 0.0 - fn(54.346) - fn(54.346'f32) + when nimvm: + discard + else: + when not defined(js): + doAssert copySign(-1.0, -NaN) == 1.0 + doAssert copySign(10.0, -NaN) == 10.0 + doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0 # fails in VM - block: # abs + block: doAssert 1.0 / abs(-0.0) == Inf doAssert 1.0 / abs(0.0) == Inf doAssert -1.0 / abs(-0.0) == -Inf @@ -370,75 +392,5 @@ template main() = doAssert abs(NaN).isNaN doAssert abs(-NaN).isNaN - block: # classify - doAssert classify(0.3) == fcNormal - doAssert classify(-0.3) == fcNormal - doAssert classify(5.0e-324) == fcSubnormal - doAssert classify(-5.0e-324) == fcSubnormal - doAssert classify(0.0) == fcZero - doAssert classify(-0.0) == fcNegZero - doAssert classify(NaN) == fcNan - doAssert classify(0.3 / 0.0) == fcInf - doAssert classify(Inf) == fcInf - doAssert classify(-0.3 / 0.0) == fcNegInf - doAssert classify(-Inf) == fcNegInf - - block: # sum - let empty: seq[int] = @[] - doAssert sum(empty) == 0 - doAssert sum([1, 2, 3, 4]) == 10 - doAssert sum([-4, 3, 5]) == 4 - - block: # prod - let empty: seq[int] = @[] - doAssert prod(empty) == 1 - doAssert prod([1, 2, 3, 4]) == 24 - doAssert prod([-4, 3, 5]) == -60 - doAssert almostEqual(prod([1.5, 3.4]), 5.1) - let x: seq[float] = @[] - doAssert prod(x) == 1.0 - - block: # clamp range - doAssert clamp(10, 1..5) == 5 - doAssert clamp(3, 1..5) == 3 - doAssert clamp(5, 1..5) == 5 - doAssert clamp(42.0, 1.0 .. 3.1415926535) == 3.1415926535 - doAssert clamp(NaN, 1.0 .. 2.0).isNaN - doAssert clamp(-Inf, -Inf .. -1.0) == -Inf - type A = enum a0, a1, a2, a3, a4, a5 - doAssert a1.clamp(a2..a4) == a2 - doAssert clamp((3, 0), (1, 0) .. (2, 9)) == (2, 9) - - block: # edge cases - doAssert sqrt(-4.0).isNaN - - doAssert ln(0.0) == -Inf - doAssert ln(-0.0) == -Inf - doAssert ln(-12.0).isNaN - - doAssert log10(0.0) == -Inf - doAssert log10(-0.0) == -Inf - doAssert log10(-12.0).isNaN - - doAssert log2(0.0) == -Inf - doAssert log2(-0.0) == -Inf - doAssert log2(-12.0).isNaN - - when nimvm: discard - else: - doAssert frexp(0.0) == (0.0, 0) - doAssert frexp(-0.0) == (-0.0, 0) - doAssert classify(frexp(-0.0)[0]) == fcNegZero - - when not defined(js): - doAssert gamma(0.0) == Inf - doAssert gamma(-0.0) == -Inf - doAssert gamma(-1.0).isNaN - - doAssert lgamma(0.0) == Inf - doAssert lgamma(-0.0) == Inf - doAssert lgamma(-1.0) == Inf - - static: main() main() diff --git a/tests/stdlib/tmd5.nim b/tests/stdlib/tmd5.nim index 254eefea9..88a7b8d37 100644 --- a/tests/stdlib/tmd5.nim +++ b/tests/stdlib/tmd5.nim @@ -1,17 +1,7 @@ -discard """ - targets: "c cpp js" -""" - import md5 -import std/assertions -proc main() {.raises: [].} = - doAssert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == - "a3cca2b2aa1e3b5b3b5aad99a8529074") - doAssert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") == - "7e716d0e702df0505fc72e2b89467910") - doAssert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e") - -main() - -static: main() +doAssert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == + "a3cca2b2aa1e3b5b3b5aad99a8529074") +doAssert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") == + "7e716d0e702df0505fc72e2b89467910") +doAssert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e") diff --git a/tests/stdlib/tmemfiles1.nim b/tests/stdlib/tmemfiles1.nim index 33657256c..21a65369f 100644 --- a/tests/stdlib/tmemfiles1.nim +++ b/tests/stdlib/tmemfiles1.nim @@ -1,6 +1,4 @@ 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 c79f85ebf..1b249898e 100644 --- a/tests/stdlib/tmemfiles2.nim +++ b/tests/stdlib/tmemfiles2.nim @@ -4,9 +4,6 @@ discard """ Half read size: 10 Data: Hello''' """ import memfiles, os -import std/syncio - - const fn = "test.mmap" var @@ -15,9 +12,8 @@ var if fileExists(fn): removeFile(fn) -# Create a new file, data all zeros, starting at size 10 -mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 10, allowRemap=true) -mm.resize 20 # resize up to 20 +# Create a new file, data all zeros +mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20) mm.close() # read, change diff --git a/tests/stdlib/tmemlinesBuf.nim b/tests/stdlib/tmemlinesBuf.nim index 7bd89d4f2..97ad751ee 100644 --- a/tests/stdlib/tmemlinesBuf.nim +++ b/tests/stdlib/tmemlinesBuf.nim @@ -1,9 +1,15 @@ -import std/[memfiles, assertions] +discard """ +output: "15" +disabled: "appveyor" +""" + +import memfiles var inp = memfiles.open("tests/stdlib/tmemlinesBuf.nim") -var buffer: string = "" +var buffer: TaintedString = "" var lineCount = 0 for line in lines(inp, buffer): lineCount += 1 close(inp) -doAssert lineCount == 9, $lineCount # this file's number of lines + +echo lineCount diff --git a/tests/stdlib/tmemmapstreams.nim b/tests/stdlib/tmemmapstreams.nim index 9cfae62c7..dd011d777 100644 --- a/tests/stdlib/tmemmapstreams.nim +++ b/tests/stdlib/tmemmapstreams.nim @@ -12,8 +12,6 @@ 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 553037011..25b5d526a 100644 --- a/tests/stdlib/tmemory.nim +++ b/tests/stdlib/tmemory.nim @@ -1,4 +1,3 @@ -import std/assertions block: # cmpMem type @@ -13,4 +12,4 @@ block: # cmpMem doAssert cmpMem(a.addr, b.addr, sizeof(SomeHash)) > 0 doAssert cmpMem(b.addr, a.addr, sizeof(SomeHash)) < 0 - doAssert cmpMem(a.addr, c.addr, sizeof(SomeHash)) == 0 + doAssert cmpMem(a.addr, c.unsafeAddr, sizeof(SomeHash)) == 0 diff --git a/tests/stdlib/tmersenne.nim b/tests/stdlib/tmersenne.nim deleted file mode 100644 index 64450a045..000000000 --- a/tests/stdlib/tmersenne.nim +++ /dev/null @@ -1,13 +0,0 @@ -import std/mersenne -import std/assertions - -template main() = - var mt = newMersenneTwister(2525) - - doAssert mt.getNum == 407788156'u32 - doAssert mt.getNum == 1071751096'u32 - doAssert mt.getNum == 3805347140'u32 - - -static: main() -main() diff --git a/tests/stdlib/tmimetypes.nim b/tests/stdlib/tmimetypes.nim deleted file mode 100644 index 8263e37fd..000000000 --- a/tests/stdlib/tmimetypes.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - targets: "c js" -""" - -import std/mimetypes -import std/assertions - - -template main() = - var m = newMimetypes() - doAssert m.getMimetype("mp4") == "video/mp4" - doAssert m.getExt("application/json") == "json" - m.register("foo", "baa") - doAssert m.getMimetype("foo") == "baa" - # see also `runnableExamples`. - # xxx we should have a way to avoid duplicating code between runnableExamples and tests - -static: main() -main() diff --git a/tests/stdlib/tmisc_issues.nim b/tests/stdlib/tmisc_issues.nim deleted file mode 100644 index b5a02e614..000000000 --- a/tests/stdlib/tmisc_issues.nim +++ /dev/null @@ -1,19 +0,0 @@ -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 171604e33..c0ced7cab 100644 --- a/tests/stdlib/tmitems.nim +++ b/tests/stdlib/tmitems.nim @@ -62,7 +62,6 @@ block: block: var x = "foobar" - prepareMutation(x) var y = cast[cstring](addr x[0]) for c in y.mitems: inc c @@ -76,7 +75,6 @@ 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 f10fef591..cf25ef025 100644 --- a/tests/stdlib/tmonotimes.nim +++ b/tests/stdlib/tmonotimes.nim @@ -1,9 +1,4 @@ -discard """ - targets: "c js" -""" - -import std/[monotimes, times] -import std/assertions +import std/monotimes, times let d = initDuration(nanoseconds = 10) let t1 = getMonoTime() diff --git a/tests/stdlib/tnativesockets.nim b/tests/stdlib/tnativesockets.nim index b1bbf32c2..b0cfd09cf 100644 --- a/tests/stdlib/tnativesockets.nim +++ b/tests/stdlib/tnativesockets.nim @@ -1,26 +1,29 @@ -import std/nativesockets -import stdtest/testutils -import std/assertions +discard """ + cmd: "nim c -r --styleCheck:hint --panics:on $options $file" + targets: "c" + nimout: "" + action: "run" + exitcode: 0 + timeout: 60.0 +""" + +import nativesockets -block: - let hostname = getHostname() - doAssert hostname.len > 0 when defined(windows): - assertAll: - toInt(IPPROTO_IP) == 0 - toInt(IPPROTO_ICMP) == 1 - toInt(IPPROTO_TCP) == 6 - toInt(IPPROTO_UDP) == 17 - toInt(IPPROTO_IPV6) == 41 - toInt(IPPROTO_ICMPV6) == 58 - toInt(IPPROTO_RAW) == 20 + doAssert toInt(IPPROTO_IP) == 0.cint + doAssert toInt(IPPROTO_ICMP) == 1.cint + doAssert toInt(IPPROTO_TCP) == 6.cint + doAssert toInt(IPPROTO_UDP) == 17.cint + doAssert toInt(IPPROTO_IPV6) == 41.cint + doAssert toInt(IPPROTO_ICMPV6) == 58.cint + doAssert toInt(IPPROTO_RAW) == 20.cint - # no changes to enum value - ord(IPPROTO_TCP) == 6 - ord(IPPROTO_UDP) == 17 - ord(IPPROTO_IP) == 18 - ord(IPPROTO_IPV6) == 19 - ord(IPPROTO_RAW) == 20 - ord(IPPROTO_ICMP) == 21 - ord(IPPROTO_ICMPV6) == 22 + # no changes to enum value + doAssert ord(IPPROTO_TCP) == 6 + doAssert ord(IPPROTO_UDP) == 17 + doAssert ord(IPPROTO_IP) == 18 + doAssert ord(IPPROTO_IPV6) == 19 + doAssert ord(IPPROTO_RAW) == 20 + doAssert ord(IPPROTO_ICMP) == 21 + doAssert ord(IPPROTO_ICMPV6) == 22 diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim index 06ff44c3d..b19d31f6c 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -4,7 +4,6 @@ outputsub: "" import net, nativesockets import unittest -import std/assertions block: # isIpAddress tests block: # 127.0.0.1 is valid @@ -51,41 +50,6 @@ block: # parseIpAddress tests expect(ValueError): discard parseIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652") - block: # ipv4-compatible ipv6 address (embedded ipv4 address) - check parseIpAddress("::ffff:10.0.0.23") == parseIpAddress("::ffff:0a00:0017") - - block: # octal number in ipv4 address - expect(ValueError): - discard parseIpAddress("010.8.8.8") - expect(ValueError): - discard parseIpAddress("8.010.8.8") - - block: # hexadecimal number in ipv4 address - expect(ValueError): - discard parseIpAddress("0xc0.168.0.1") - expect(ValueError): - discard parseIpAddress("192.0xa8.0.1") - - block: # less than 4 numbers in ipv4 address - expect(ValueError): - discard parseIpAddress("127.0.1") - - block: # octal number in embedded ipv4 address - expect(ValueError): - discard parseIpAddress("::ffff:010.8.8.8") - expect(ValueError): - discard parseIpAddress("::ffff:8.010.8.8") - - block: # hexadecimal number in embedded ipv4 address - expect(ValueError): - discard parseIpAddress("::ffff:0xc0.168.0.1") - expect(ValueError): - discard parseIpAddress("::ffff:192.0xa8.0.1") - - block: # less than 4 numbers in embedded ipv4 address - expect(ValueError): - discard parseIpAddress("::ffff:127.0.1") - block: # "IpAddress/Sockaddr conversion" proc test(ipaddrstr: string) = var ipaddr_1 = parseIpAddress(ipaddrstr) @@ -94,7 +58,7 @@ block: # "IpAddress/Sockaddr conversion" doAssert($ipaddrstr == $ipaddr_1) var sockaddr: Sockaddr_storage - var socklen: SockLen + var socklen: Socklen var ipaddr_2: IpAddress var port_2: Port diff --git a/tests/stdlib/tnet_ll.nim b/tests/stdlib/tnet_ll.nim index 13b56dbb9..affa21947 100644 --- a/tests/stdlib/tnet_ll.nim +++ b/tests/stdlib/tnet_ll.nim @@ -1,51 +1,43 @@ -discard """ - action: run - output: ''' - -[Suite] inet_ntop tests -''' -""" - -when defined(windows): - import winlean -elif defined(posix): - import posix -else: - {.error: "Unsupported OS".} - -import unittest, strutils - -suite "inet_ntop tests": - - setup: - when defined(windows): - var wsa: WSAData - discard wsaStartup(0x101'i16, wsa.addr) - - test "IP V4": - # regular - var ip4 = InAddr() - ip4.s_addr = 0x10111213'u32 - check: ip4.s_addr == 0x10111213'u32 - - var buff: array[0..255, char] - let r = inet_ntop(AF_INET, cast[pointer](ip4.s_addr.addr), cast[cstring](buff[0].addr), buff.len.int32) - let res = if r == nil: "" else: $r - check: res == "19.18.17.16" - - test "IP V6": - when defined(windows): - let ipv6Support = (getVersion() and 0xff) > 0x5 - else: - let ipv6Support = true - - var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001] - var buff: array[0..255, char] - let r = inet_ntop(AF_INET6, cast[pointer](ip6[0].addr), cast[cstring](buff[0].addr), buff.len.int32) - let res = if r == nil: "" else: $r - check: not ipv6Support or res == "10:110:20:120:30:130:40:140" - - test "InAddr": - # issue 19244 - var ip4 = InAddr(s_addr: 0x10111213'u32) - check: ip4.s_addr == 0x10111213'u32 +discard """ + action: run + output: ''' + +[Suite] inet_ntop tests +''' +""" + +when defined(windows): + import winlean +elif defined(posix): + import posix +else: + {.error: "Unsupported OS".} + +import unittest, strutils + +suite "inet_ntop tests": + + setup: + when defined(windows): + var wsa: WSAData + discard wsaStartup(0x101'i16, wsa.addr) + + test "IP V4": + var ip4 = 0x10111213 + var buff: array[0..255, char] + let r = inet_ntop(AF_INET, ip4.addr, buff[0].addr, buff.sizeof.int32) + let res = if r == nil: "" else: $r + check: res == "19.18.17.16" + + + test "IP V6": + when defined(windows): + let ipv6Support = (getVersion() and 0xff) > 0x5 + else: + let ipv6Support = true + + var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001] + var buff: array[0..255, char] + let r = inet_ntop(AF_INET6, ip6[0].addr, buff[0].addr, buff.sizeof.int32) + let res = if r == nil: "" else: $r + check: not ipv6Support or res == "10:110:20:120:30:130:40:140" diff --git a/tests/stdlib/tnetconnect.nim b/tests/stdlib/tnetconnect.nim deleted file mode 100644 index ae654aed9..000000000 --- a/tests/stdlib/tnetconnect.nim +++ /dev/null @@ -1,30 +0,0 @@ -discard """ - disabled: "i386" - matrix: "-d:ssl" -""" - -import std/net -from std/strutils import `%` -from stdtest/testutils import enableRemoteNetworking - -# bug #15215 -proc test() = - let ctx = newContext() - - proc fn(url: string) = - let socket = newSocket() - defer: close(socket) - connect(socket, url, Port(443), 5000) # typically 20 could be enough - send(socket, "GET / HTTP/1.0\nHost: $#\nConnection: close\n\n" % [url]) - wrapSocket(ctx, socket) - - # trying 2 sites makes it more resilent: refs #17458 this could give: - # * Call to 'connect' timed out. [TimeoutError] - # * No route to host [OSError] - try: - fn("www.nim-lang.org") - except TimeoutError, OSError: - fn("www.google.com") - -when enableRemoteNetworking: - test() diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim index a1e147ad5..f940bd630 100644 --- a/tests/stdlib/tnetdial.nim +++ b/tests/stdlib/tnetdial.nim @@ -2,10 +2,10 @@ discard """ cmd: "nim c --threads:on $file" exitcode: 0 output: "OK" + disabled: "travis" """ import os, net, nativesockets, asyncdispatch -import std/[assertions, typedthreads] ## Test for net.dial @@ -15,7 +15,7 @@ proc initIPv6Server(hostname: string, port: Port): AsyncFD = let fd = createNativeSocket(AF_INET6) setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1) var aiList = getAddrInfo(hostname, port, AF_INET6) - if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: + if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: freeAddrInfo(aiList) raiseOSError(osLastError()) freeAddrInfo(aiList) diff --git a/tests/stdlib/tntpath.nim b/tests/stdlib/tntpath.nim deleted file mode 100644 index dce0cf6f8..000000000 --- a/tests/stdlib/tntpath.nim +++ /dev/null @@ -1,46 +0,0 @@ -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 deleted file mode 100644 index cf78fa255..000000000 --- a/tests/stdlib/tobjectdollar.nim +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index dd5b84c51..000000000 --- a/tests/stdlib/toids.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - matrix: "--mm:refc; --mm:orc" -""" - -import std/oids -import std/assertions - -block: # genOid - let x = genOid() - doAssert ($x).len == 24 - -block: - let x = genOid() - let y = parseOid(cstring($x)) - doAssert x == y diff --git a/tests/stdlib/topenssl.nim b/tests/stdlib/topenssl.nim deleted file mode 100644 index 3209437de..000000000 --- a/tests/stdlib/topenssl.nim +++ /dev/null @@ -1,42 +0,0 @@ -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==" - -proc rsaPublicEncrypt(fr: string): string = - let mKey = "-----BEGIN PUBLIC KEY-----\n" & PubKey.wrapWords(64) & "\n-----END PUBLIC KEY-----" - let bio = bioNew(bioSMem()) - doAssert BIO_write(bio, mKey.cstring, mKey.len.cint) >= 0 - let rsa = PEM_read_bio_RSA_PUBKEY(bio, nil, nil, nil) - doAssert rsa != nil - doAssert BIO_free(bio) >= 0 - result = newString(RSA_size(rsa)) - let frdata = cast[ptr uint8](fr.cstring) - var todata = cast[ptr uint8](result.cstring) - doAssert RSA_public_encrypt(fr.len.cint, frdata, todata, rsa, RSA_PKCS1_PADDING) != -1 - RSA_free(rsa) - -proc rasPrivateDecrypt(fr: string): string = - let mKey = "-----BEGIN RSA PRIVATE KEY-----\n" & PrivateKey.wrapWords(64) & "\n-----END RSA PRIVATE KEY-----" - let bio = bioNew(bioSMem()) - doAssert BIO_write(bio, mKey.cstring, mKey.len.cint) >= 0 - let rsa = PEM_read_bio_RSAPrivateKey(bio, nil, nil, nil) - doAssert rsa != nil - doAssert BIO_free(bio) >= 0 - let rsaLen = RSA_size(rsa) - result = newString(rsaLen) - let frdata = cast[ptr uint8](fr.cstring) - var todata = cast[ptr uint8](result.cstring) - let lenOrig = RSA_private_decrypt(rsaLen, frdata, todata, rsa, RSA_PKCS1_PADDING) - doAssert lenOrig >= 0 and lenOrig < result.len - doAssert result[lenOrig] == '\0' - result.setLen lenOrig - RSA_free(rsa) - -let res = "TEST" -let miwen = rsaPublicEncrypt(res) -let mingwen = rasPrivateDecrypt(miwen) -doAssert mingwen == res - diff --git a/tests/stdlib/toptions.nim b/tests/stdlib/toptions.nim index 6065425b9..4764ce05f 100644 --- a/tests/stdlib/toptions.nim +++ b/tests/stdlib/toptions.nim @@ -4,9 +4,6 @@ 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 @@ -85,8 +82,8 @@ proc main() = doAssert(stringNone.get("Correct") == "Correct") block stringify: - doAssert($(some("Correct")) == "some(\"Correct\")") - doAssert($(stringNone) == "none(string)") + doAssert($(some("Correct")) == "Some(\"Correct\")") + doAssert($(stringNone) == "None[string]") disableJsVm: block map_with_a_void_result: @@ -158,7 +155,7 @@ proc main() = name: string let nobody = none(Named) - doAssert($nobody == "none(Named)") + doAssert($nobody == "None[Named]") # "$ on type with name()" block: @@ -166,7 +163,7 @@ proc main() = myname: string let noperson = none(Person) - doAssert($noperson == "none(Person)") + doAssert($noperson == "None[Person]") # "Ref type with overloaded `==`" block: @@ -193,7 +190,7 @@ proc main() = block: let x = none(cstring) doAssert x.isNone - doAssert $x == "none(cstring)" + doAssert $x == "None[cstring]" static: main() diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index b2891ef1b..c053c16f2 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -22,13 +22,10 @@ __really_obscure_dir_name/test Raises Raises ''' - joinable: false """ # test os path creation, iteration, and deletion 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"] @@ -40,28 +37,23 @@ block fileOperations: doAssert dirExists(dname) block: # copyFile, copyFileToDir - doAssertRaises(OSError): copyFile(dname/"nonexistent.txt", dname/"nonexistent.txt") + doAssertRaises(OSError): copyFile(dname/"nonexistant.txt", dname/"nonexistant.txt") let fname = "D20201009T112235" let fname2 = "D20201009T112235.2" - let str = "foo1\0foo2\nfoo3\0" - let file = dname/fname - let file2 = dname/fname2 - writeFile(file, str) - doAssert readFile(file) == str + writeFile(dname/fname, "foo") let sub = "sub" - doAssertRaises(OSError): copyFile(file, dname/sub/fname2) - doAssertRaises(OSError): copyFileToDir(file, dname/sub) - doAssertRaises(ValueError): copyFileToDir(file, "") - copyFile(file, file2) - doAssert fileExists(file2) - doAssert readFile(file2) == str + doAssertRaises(OSError): copyFile(dname/fname, dname/sub/fname2) + doAssertRaises(OSError): copyFileToDir(dname/fname, dname/sub) + doAssertRaises(ValueError): copyFileToDir(dname/fname, "") + copyFile(dname/fname, dname/fname2) + doAssert fileExists(dname/fname2) createDir(dname/sub) - copyFileToDir(file, dname/sub) + copyFileToDir(dname/fname, dname/sub) doAssert fileExists(dname/sub/fname) removeDir(dname/sub) doAssert not dirExists(dname/sub) - removeFile(file) - removeFile(file2) + removeFile(dname/fname) + removeFile(dname/fname2) # Test creating files and dirs for dir in dirs: @@ -157,160 +149,6 @@ 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: - const symlinksAreHandled = not defined(windows) - const dname = buildDir/"D20210116T140629" - const subDir = dname/"sub" - const subDir2 = dname/"sub2" - const brokenSymlinkName = "D20210101T191320_BROKEN_SYMLINK" - const brokenSymlink = dname/brokenSymlinkName - const brokenSymlinkSrc = "D20210101T191320_nonexistent" - const brokenSymlinkCopy = brokenSymlink & "_COPY" - const brokenSymlinkInSubDir = subDir/brokenSymlinkName - const brokenSymlinkInSubDir2 = subDir2/brokenSymlinkName - - createDir(subDir) - createSymlink(brokenSymlinkSrc, brokenSymlink) - - # Test copyFile - when symlinksAreHandled: - doAssertRaises(OSError): - copyFile(brokenSymlink, brokenSymlinkCopy) - doAssertRaises(OSError): - copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkFollow}) - copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkIgnore}) - doAssert not fileExists(brokenSymlinkCopy) - copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkAsIs}) - when symlinksAreHandled: - doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc - removeFile(brokenSymlinkCopy) - else: - doAssert not fileExists(brokenSymlinkCopy) - doAssertRaises(AssertionDefect): - copyFile(brokenSymlink, brokenSymlinkCopy, - {cfSymlinkAsIs, cfSymlinkFollow}) - - # Test copyFileWithPermissions - when symlinksAreHandled: - doAssertRaises(OSError): - copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy) - doAssertRaises(OSError): - copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy, - options = {cfSymlinkFollow}) - copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy, - options = {cfSymlinkIgnore}) - doAssert not fileExists(brokenSymlinkCopy) - copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy, - options = {cfSymlinkAsIs}) - when symlinksAreHandled: - doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc - removeFile(brokenSymlinkCopy) - else: - doAssert not fileExists(brokenSymlinkCopy) - doAssertRaises(AssertionDefect): - copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy, - options = {cfSymlinkAsIs, cfSymlinkFollow}) - - # Test copyFileToDir - when symlinksAreHandled: - doAssertRaises(OSError): - copyFileToDir(brokenSymlink, subDir) - doAssertRaises(OSError): - copyFileToDir(brokenSymlink, subDir, {cfSymlinkFollow}) - copyFileToDir(brokenSymlink, subDir, {cfSymlinkIgnore}) - doAssert not fileExists(brokenSymlinkInSubDir) - copyFileToDir(brokenSymlink, subDir, {cfSymlinkAsIs}) - when symlinksAreHandled: - doAssert expandSymlink(brokenSymlinkInSubDir) == brokenSymlinkSrc - removeFile(brokenSymlinkInSubDir) - else: - doAssert not fileExists(brokenSymlinkInSubDir) - - createSymlink(brokenSymlinkSrc, brokenSymlinkInSubDir) - - # Test copyDir - copyDir(subDir, subDir2) - when symlinksAreHandled: - doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc - else: - doAssert not fileExists(brokenSymlinkInSubDir2) - removeDir(subDir2) - - # Test copyDirWithPermissions - copyDirWithPermissions(subDir, subDir2) - when symlinksAreHandled: - doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc - else: - doAssert not fileExists(brokenSymlinkInSubDir2) - removeDir(subDir2) - - # Test moveFile - moveFile(brokenSymlink, brokenSymlinkCopy) - when not defined(windows): - doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc - else: - doAssert symlinkExists(brokenSymlinkCopy) - removeFile(brokenSymlinkCopy) - - # Test moveDir - moveDir(subDir, subDir2) - when not defined(windows): - doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc - else: - doAssert symlinkExists(brokenSymlinkInSubDir2) - - removeDir(dname) - -block: # moveFile - let tempDir = getTempDir() / "D20210609T151608" - createDir(tempDir) - defer: removeDir(tempDir) - - writeFile(tempDir / "a.txt", "") - moveFile(tempDir / "a.txt", tempDir / "b.txt") - doAssert not fileExists(tempDir / "a.txt") - doAssert fileExists(tempDir / "b.txt") - removeFile(tempDir / "b.txt") - - createDir(tempDir / "moveFile_test") - writeFile(tempDir / "moveFile_test/a.txt", "") - moveFile(tempDir / "moveFile_test/a.txt", tempDir / "moveFile_test/b.txt") - doAssert not fileExists(tempDir / "moveFile_test/a.txt") - doAssert fileExists(tempDir / "moveFile_test/b.txt") - removeDir(tempDir / "moveFile_test") - - createDir(tempDir / "moveFile_test") - writeFile(tempDir / "a.txt", "") - moveFile(tempDir / "a.txt", tempDir / "moveFile_test/b.txt") - doAssert not fileExists(tempDir / "a.txt") - doAssert fileExists(tempDir / "moveFile_test/b.txt") - removeDir(tempDir / "moveFile_test") - -block: # moveDir - let tempDir = getTempDir() / "D20210609T161443" - createDir(tempDir) - defer: removeDir(tempDir) - - createDir(tempDir / "moveDir_test") - moveDir(tempDir / "moveDir_test/", tempDir / "moveDir_test_dest") - doAssert not dirExists(tempDir / "moveDir_test") - doAssert dirExists(tempDir / "moveDir_test_dest") - removeDir(tempDir / "moveDir_test_dest") - - createDir(tempDir / "moveDir_test") - writeFile(tempDir / "moveDir_test/a.txt", "") - moveDir(tempDir / "moveDir_test", tempDir / "moveDir_test_dest") - doAssert not dirExists(tempDir / "moveDir_test") - doAssert not fileExists(tempDir / "moveDir_test/a.txt") - doAssert dirExists(tempDir / "moveDir_test_dest") - doAssert fileExists(tempDir / "moveDir_test_dest/a.txt") - removeDir(tempDir / "moveDir_test_dest") - import times block modificationTime: # Test get/set modification times @@ -335,7 +173,7 @@ block walkDirRec: doAssert p.startsWith("walkdir_test") var s: seq[string] - for p in walkDirRec("walkdir_test", {pcFile}, {pcDir}, relative = true): + for p in walkDirRec("walkdir_test", {pcFile}, {pcDir}, relative=true): s.add(p) doAssert s.len == 2 @@ -344,13 +182,11 @@ block walkDirRec: removeDir("walkdir_test") -import std/sequtils - block: # walkDir doAssertRaises(OSError): - for a in walkDir("nonexistent", checkDir = true): discard + for a in walkDir("nonexistant", checkDir = true): discard doAssertRaises(OSError): - for p in walkDirRec("nonexistent", checkDir = true): discard + for p in walkDirRec("nonexistant", checkDir = true): discard when not defined(windows): block walkDirRelative: @@ -360,21 +196,6 @@ block: # walkDir doAssert k == pcLinkToDir removeDir("walkdir_test") - when defined(posix): - block walkDirSpecial: - createDir("walkdir_test") - doAssert execShellCmd("mkfifo walkdir_test/fifo") == 0 - createSymlink("fifo", "walkdir_test/fifo_link") - let withSpecialFiles = toSeq(walkDir("walkdir_test", relative = true)) - doAssert (withSpecialFiles.len == 2 and - (pcFile, "fifo") in withSpecialFiles and - (pcLinkToFile, "fifo_link") in withSpecialFiles) - # now Unix special files are excluded from walkdir output: - let skipSpecialFiles = toSeq(walkDir("walkdir_test", relative = true, - skipSpecial = true)) - doAssert skipSpecialFiles.len == 0 - removeDir("walkdir_test") - block normalizedPath: doAssert normalizedPath("") == "" block relative: @@ -420,7 +241,7 @@ block absolutePath: doAssertRaises(ValueError): discard absolutePath("a", "b") doAssert absolutePath("a") == getCurrentDir() / "a" doAssert absolutePath("a", "/b") == "/b" / "a" - when defined(posix): + when defined(Posix): doAssert absolutePath("a", "/b/") == "/b" / "a" doAssert absolutePath("a", "/b/c") == "/b/c" / "a" doAssert absolutePath("/a", "b/") == "/a" @@ -529,11 +350,7 @@ 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" - - # `//` 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/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim" doAssert relativePath("", "/users/moo", '/') == "" doAssert relativePath("foo", "", '/') == "foo" @@ -578,20 +395,20 @@ block ospaths: doAssert joinPath("/", "") == unixToNativePath"/" doAssert joinPath("/" / "") == unixToNativePath"/" # weird test case... doAssert joinPath("/", "/a/b/c") == unixToNativePath"/a/b/c" - doAssert joinPath("foo/", "") == unixToNativePath"foo/" - doAssert joinPath("foo/", "abc") == unixToNativePath"foo/abc" - doAssert joinPath("foo//./", "abc/.//") == unixToNativePath"foo/abc/" - doAssert joinPath("foo", "abc") == unixToNativePath"foo/abc" - doAssert joinPath("", "abc") == unixToNativePath"abc" + doAssert joinPath("foo/","") == unixToNativePath"foo/" + doAssert joinPath("foo/","abc") == unixToNativePath"foo/abc" + doAssert joinPath("foo//./","abc/.//") == unixToNativePath"foo/abc/" + doAssert joinPath("foo","abc") == unixToNativePath"foo/abc" + doAssert joinPath("","abc") == unixToNativePath"abc" - doAssert joinPath("zook/.", "abc") == unixToNativePath"zook/abc" + doAssert joinPath("gook/.","abc") == unixToNativePath"gook/abc" - # controversial: inconsistent with `joinPath("zook/.","abc")` + # controversial: inconsistent with `joinPath("gook/.","abc")` # on linux, `./foo` and `foo` are treated a bit differently for executables # but not `./foo/bar` and `foo/bar` doAssert joinPath(".", "/lib") == unixToNativePath"./lib" - doAssert joinPath(".", "abc") == unixToNativePath"./abc" - + doAssert joinPath(".","abc") == unixToNativePath"./abc" + # cases related to issue #13455 doAssert joinPath("foo", "", "") == "foo" doAssert joinPath("foo", "") == "foo" @@ -627,8 +444,16 @@ block getTempDir: else: doAssert getTempDir() == "/tmp/" -block: # getCacheDir - doAssert getCacheDir().dirExists +block osenv: + block delEnv: + const dummyEnvVar = "DUMMY_ENV_VAR" # This env var wouldn't be likely to exist to begin with + doAssert existsEnv(dummyEnvVar) == false + putEnv(dummyEnvVar, "1") + doAssert existsEnv(dummyEnvVar) == true + delEnv(dummyEnvVar) + doAssert existsEnv(dummyEnvVar) == false + delEnv(dummyEnvVar) # deleting an already deleted env var + doAssert existsEnv(dummyEnvVar) == false block isRelativeTo: doAssert isRelativeTo("/foo", "/") @@ -647,18 +472,7 @@ block: # quoteShellWindows doAssert quoteShellWindows("aaa\"") == "aaa\\\"" doAssert quoteShellWindows("") == "\"\"" -block: # quoteShellCommand - when defined(windows): - doAssert quoteShellCommand(["a b c", "d", "e"]) == """"a b c" d e""" - doAssert quoteShellCommand(["""ab"c""", r"\", "d"]) == """ab\"c \ d""" - doAssert quoteShellCommand(["""ab"c""", """ \""", "d"]) == """ab\"c " \\" d""" - doAssert quoteShellCommand(["""a\\\b""", """de fg""", "h"]) == """a\\\b "de fg" h""" - doAssert quoteShellCommand(["""a\"b""", "c", "d"]) == """a\\\"b c d""" - doAssert quoteShellCommand(["""a\\b c""", "d", "e"]) == """"a\\b c" d e""" - doAssert quoteShellCommand(["""a\\b\ c""", "d", "e"]) == """"a\\b\ c" d e""" - doAssert quoteShellCommand(["ab", ""]) == """ab """"" - -block: # quoteShellPosix +block: # quoteShellWindows doAssert quoteShellPosix("aaa") == "aaa" doAssert quoteShellPosix("aaa a") == "'aaa a'" doAssert quoteShellPosix("") == "''" @@ -680,7 +494,7 @@ block: # normalizePathEnd doAssert "//".normalizePathEnd(false) == "/" doAssert "foo.bar//".normalizePathEnd == "foo.bar" doAssert "bar//".normalizePathEnd(trailingSep = true) == "bar/" - when defined(windows): + when defined(Windows): doAssert r"C:\foo\\".normalizePathEnd == r"C:\foo" doAssert r"C:\foo".normalizePathEnd(trailingSep = true) == r"C:\foo\" # this one is controversial: we could argue for returning `D:\` instead, @@ -689,121 +503,7 @@ block: # normalizePathEnd doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\" doAssert "/".normalizePathEnd == r"\" - -import sugar - -block: # normalizeExe - doAssert "".dup(normalizeExe) == "" - when defined(posix): - doAssert "foo".dup(normalizeExe) == "./foo" - doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" - when defined(windows): - doAssert "foo".dup(normalizeExe) == "foo" - -block: # isAdmin - let isAzure = existsEnv("TF_BUILD") # xxx factor with testament.specs.isAzure - # In Azure on Windows tests run as an admin user - 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 sugar - -block: # normalizeExe - doAssert "".dup(normalizeExe) == "" - when defined(posix): - doAssert "foo".dup(normalizeExe) == "./foo" - doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" - when defined(windows): - doAssert "foo".dup(normalizeExe) == "foo" - -block: # isAdmin - let isAzure = existsEnv("TF_BUILD") # xxx factor with testament.specs.isAzure - # In Azure on Windows tests run as an admin user - 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() - -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"] - - -# https://github.com/nim-lang/Nim/pull/19643#issuecomment-1235102314 -block: # isValidFilename +block: # isValidFilename # Negative Tests. doAssert not isValidFilename("abcd", maxLen = 2) doAssert not isValidFilename("0123456789", maxLen = 8) @@ -829,3 +529,13 @@ block: # isValidFilename doAssert isValidFilename("ux.bat") doAssert isValidFilename("nim.nim") doAssert isValidFilename("foo.log") + +import sugar + +block: # normalizeExe + doAssert "".dup(normalizeExe) == "" + when defined(posix): + doAssert "foo".dup(normalizeExe) == "./foo" + doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" + when defined(windows): + doAssert "foo".dup(normalizeExe) == "foo" diff --git a/tests/stdlib/tos_unc.nim b/tests/stdlib/tos_unc.nim index fc74a4b9d..e55de11ce 100644 --- a/tests/stdlib/tos_unc.nim +++ b/tests/stdlib/tos_unc.nim @@ -4,7 +4,6 @@ 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 deleted file mode 100644 index 20264102f..000000000 --- a/tests/stdlib/tosenv.nim +++ /dev/null @@ -1,163 +0,0 @@ -discard """ - matrix: "--threads" - joinable: false - targets: "c js cpp" -""" - -import std/os -from std/sequtils import toSeq -import stdtest/testutils - -when defined(nimPreviewSlimSystem): - 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", "", unicodeUtf8]: # ensures empty val works too - const key = "NIM_TESTS_TOSENV_KEY" - doAssert not existsEnv(key) - - putEnv(key, "tempval") - doAssert existsEnv(key) - doAssert getEnv(key) == "tempval" - - putEnv(key, val) # change a key that already exists - doAssert existsEnv(key) - doAssert getEnv(key) == val - - doAssert (key, val) in toSeq(envPairs()) - delEnv(key) - doAssert (key, val) notin toSeq(envPairs()) - doAssert not existsEnv(key) - delEnv(key) # deleting an already deleted env var - doAssert not existsEnv(key) - - block: - doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "") == "" - doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", " ") == " " - doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "defval") == "defval" - - whenVMorJs: discard # xxx improve - do: - doAssertRaises(OSError, putEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE")) - doAssertRaises(OSError, putEnv("", "NEW_DUMMY_VALUE")) - doAssert not existsEnv("") - doAssert not existsEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE") - doAssert not existsEnv("NIM_TESTS_TOSENV_PUT") - -static: main() -main() - -when defined(windows): - import std/widestrs - 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): - when defined(nimPreviewSlimSystem): - import std/typedthreads - block: # bug #18533 - var thr: Thread[void] - proc threadFunc {.thread.} = putEnv("foo", "fooVal2") - - putEnv("foo", "fooVal1") - doAssert getEnv("foo") == "fooVal1" - createThread(thr, threadFunc) - joinThreads(thr) - 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/toserrors.nim b/tests/stdlib/toserrors.nim deleted file mode 100644 index e907dfe63..000000000 --- a/tests/stdlib/toserrors.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - action: compile -""" - -import std/oserrors - -let x1 = osLastError() -raiseOSError(x1) -echo osErrorMsg(x1) diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index e86f3853b..ab8024746 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -9,11 +9,10 @@ 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 - proc c_exit2(code: cint): void {.importc: "_exit", header: "".} + proc c_exit2(code: c_int): void {.importc: "_exit", header: "".} import os var a = 0 proc fun(b = 0) = @@ -30,12 +29,6 @@ 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": @@ -120,17 +113,7 @@ else: # main driver runTest("exit_0", 0) runTest("exitnow_139", 139) runTest("c_exit2_139", 139) - when defined(posix): - runTest("quit_139", 127) # The quit value gets saturated to 127 - else: - 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 + runTest("quit_139", 139) import std/streams @@ -212,19 +195,19 @@ else: # main driver # bugfix: windows stdin.close was a noop and led to blocking reads proc startProcessTest(command: string, options: set[ProcessOption] = { poStdErrToStdOut, poUsePath}, input = ""): tuple[ - output: string, + output: TaintedString, exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = var p = startProcess(command, options = options + {poEvalCommand}) var outp = outputStream(p) if input.len > 0: inputStream(p).write(input) close inputStream(p) - result = ("", -1) - var line = newStringOfCap(120) + result = (TaintedString"", -1) + var line = newStringOfCap(120).TaintedString while true: if outp.readLine(line): - result[0].add(line) - result[0].add("\n") + result[0].string.add(line.string) + result[0].string.add("\n") else: result[1] = peekExitCode(p) if result[1] != -1: break @@ -240,7 +223,7 @@ else: # main driver p.inputStream.flush() var line = "" var s: seq[string] - while p.outputStream.readLine(line): + while p.outputStream.readLine(line.TaintedString): s.add line doAssert s == @["10"] @@ -252,15 +235,15 @@ else: # main driver deferScoped: p.close() do: var sout: seq[string] - while p.outputStream.readLine(x): sout.add x + while p.outputStream.readLine(x.TaintedString): sout.add x doAssert sout == @["start ta_out", "to stdout", "to stdout", "to stderr", "to stderr", "to stdout", "to stdout", "end ta_out"] block: # startProcess stderr (replaces old test `tstderr` + `ta_out`) var p = startProcess(output, dir, options={}) deferScoped: p.close() do: var serr, sout: seq[string] - while p.errorStream.readLine(x): serr.add x - while p.outputStream.readLine(x): sout.add x + while p.errorStream.readLine(x.TaintedString): serr.add x + while p.outputStream.readLine(x.TaintedString): sout.add x doAssert serr == @["to stderr", "to stderr"] doAssert sout == @["start ta_out", "to stdout", "to stdout", "to stdout", "to stdout", "end ta_out"] @@ -289,29 +272,10 @@ else: # main driver stripLineEnd(result[0]) doAssert result == ("12", 0) when not defined(windows): - doAssert execCmdEx("ls --nonexistent").exitCode != 0 + doAssert execCmdEx("ls --nonexistant").exitCode != 0 when false: # bug: on windows, this raises; on posix, passes - doAssert execCmdEx("nonexistent").exitCode != 0 + doAssert execCmdEx("nonexistant").exitCode != 0 when defined(posix): doAssert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) doAssert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) - - block: # bug #17749 - let output = compileNimProg("-d:case_testfile4", "D20210417T011153") - var p = startProcess(output, dir) - let inp = p.inputStream - var count = 0 - when defined(windows): - # xxx we should make osproc.hsWriteData raise IOError on windows, consistent - # with posix; we could also (in addition) make IOError a subclass of OSError. - type SIGPIPEError = OSError - else: - type SIGPIPEError = IOError - doAssertRaises(SIGPIPEError): - for i in 0..<100000: - count.inc - inp.writeLine "ok" # was giving SIGPIPE and crashing - doAssert count >= 100 - doAssert waitForExit(p) == QuitFailure - close(p) # xxx isn't that missing in other places? diff --git a/tests/stdlib/tosprocterminate.nim b/tests/stdlib/tosprocterminate.nim index 08b379569..c02e0a8da 100644 --- a/tests/stdlib/tosprocterminate.nim +++ b/tests/stdlib/tosprocterminate.nim @@ -5,9 +5,8 @@ discard """ """ import os, osproc, times, std / monotimes -import std/assertions -when defined(windows): +when defined(Windows): const ProgramWhichDoesNotEnd = "notepad" elif defined(openbsd): const ProgramWhichDoesNotEnd = "/bin/cat" diff --git a/tests/stdlib/tpackedsets.nim b/tests/stdlib/tpackedsets.nim index 2c69f6b1b..d0149adc5 100644 --- a/tests/stdlib/tpackedsets.nim +++ b/tests/stdlib/tpackedsets.nim @@ -4,8 +4,6 @@ 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 16f12bc9e..5c077bbda 100644 --- a/tests/stdlib/tparsecfg.nim +++ b/tests/stdlib/tparsecfg.nim @@ -2,8 +2,7 @@ discard """ targets: "c js" """ -import parsecfg, streams, sequtils -import std/assertions +import parsecfg, streams when not defined(js): from stdtest/specialpaths import buildDir @@ -40,14 +39,19 @@ var ss = newStringStream() dict1.writeConfig(ss) ## Reading a configuration file. -let dict2 = loadConfig(newStringStream(ss.data)) -doAssert dict2.getSectionValue("", "charset") == "utf-8" -doAssert dict2.getSectionValue("Package", "--threads") == "on" -doAssert dict2.getSectionValue("Package", "name") == "hello" -doAssert dict2.getSectionValue("Author", "name") == "lihf8515" -doAssert dict2.getSectionValue("Author", "qq") == "10214028" -doAssert dict2.getSectionValue("Author", "email") == "lihaifeng@wxm.com" -doAssert toSeq(dict2.sections) == @["", "Package", "Author"] +var dict2 = loadConfig(newStringStream(ss.data)) +var charset = dict2.getSectionValue("", "charset") +var threads = dict2.getSectionValue("Package", "--threads") +var pname = dict2.getSectionValue("Package", "name") +var name = dict2.getSectionValue("Author", "name") +var qq = dict2.getSectionValue("Author", "qq") +var email = dict2.getSectionValue("Author", "email") +doAssert charset == "utf-8" +doAssert threads == "on" +doAssert pname == "hello" +doAssert name == "lihf8515" +doAssert qq == "10214028" +doAssert email == "lihaifeng@wxm.com" ## Modifying a configuration file. var dict3 = loadConfig(newStringStream(ss.data)) diff --git a/tests/stdlib/tparsecsv.nim b/tests/stdlib/tparsecsv.nim index a879019f6..0d004d45d 100644 --- a/tests/stdlib/tparsecsv.nim +++ b/tests/stdlib/tparsecsv.nim @@ -1,6 +1,5 @@ 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/tparseipv6.nim b/tests/stdlib/tparseipv6.nim index 9b9c464c7..3e1c23e58 100644 --- a/tests/stdlib/tparseipv6.nim +++ b/tests/stdlib/tparseipv6.nim @@ -8,23 +8,23 @@ const positives = [ "::f:8:a8f:218.17.235.229", "::b:228.19.241.2", - "::8:c:a:f:8.35.8.96", - "::3:e:a:bc:4.19.2.9", + "::8:c:a:f:8.35.8.096", + "::3:e:a:bc:04.19.2.9", "::2:212.242.248.19", "::df:a5f:3.250.208.9", "::8:c:5:e63:250.208.249.0", "::b:f:181.12.9.98", - "::a:f8:77.8.243.232", - "::a:b:85:e4d9:252.9.229.56", + "::a:f8:77.08.243.232", + "::a:b:85:e4d9:252.9.229.056", "941:c:8a:c:e::917", "e8:7a:e:ad:88a:8:203.235.225.46", - "139c:9e::f8:254.8.21.249", + "139c:9e::f8:254.08.21.249", "b38:f0:e::f9:89.6.12.18", "ef::8", "5::ab", "a::8:255.247.96.253", "b:c0::c:254.248.95.254", - "::8c:2:99.251.24.3", + "::8c:2:99.251.024.3", "98::c:247.240.249.57", "9::9", "628::f1ed:f", @@ -106,68 +106,68 @@ const "::315", "::a:a", "::aed3:a", - "f0eb:0:e8:b:c:a:254.98.233.17", + "f0eb:0:e8:b:c:a:254.098.233.17", "bfa:7fc:c66d:15:e9a:ded:254.119.9.9", - "d:ffa8:9:a:879:3:202.39.8.245", + "d:ffa8:9:a:879:3:202.39.08.245", "8e:2:8:fa8a:f1d1:1aa8:252.254.245.81", - "5:d4:a:e9:8:8:6.38.98.253", - "9c5:4:a5c:f:a6:8c9d:5.250.8.2", + "5:d4:a:e9:8:8:06.38.98.253", + "9c5:4:a5c:f:a6:8c9d:05.250.8.2", "d19a:2:f808:be:f:c:98.86.197.249", - "8:26ac:8:8:cb:f:242.0.254.85", + "8:26ac:8:8:cb:f:242.00.254.85", "38:e:1:0b88:f:0:8.89.248.92", - "e7:ff96:a:f:f:b:253.91.52.195", + "e7:ff96:a:f:f:b:253.91.052.195", "d:8:2:5:894:5:254.0.240.199", "2:98:9:8aa:9c8f:fa:252.98.248.17", - "e9:d4f:890:ccbe:5:8:88.200.228.216", + "e9:d4f:890:ccbe:5:8:088.200.228.216", "3:3:9:5:6a:df5:255.251.8.12", - "0280:3:8:8:4:9:255.0.251.249", + "0280:3:8:8:4:9:255.000.251.249", "8:af7:db:aa:0:9:238.248.250.255", - "ff:ee:9a:9252:a:289:59.83.18.255", + "ff:ee:9a:9252:a:289:059.083.18.255", "9f6:5:fc9:b:a89:a:142.1.250.254", "e:981a:da:bf94:9:f8:254.242.18.95", "3c:1:4:f2:89:f:8.91.255.14", - "e::9a2:c:9.50.80.8", + "e::9a2:c:9.050.80.8", "9::4a:07:fb:211.241.254.228", "9be::2:e:215.189.48.188", - "f::f:d:69.148.99.168", + "f::f:d:069.148.99.168", "f::a:97.18.240.47", "c::a98e:1:251.253.252.254", "668::82:214.87.208.9", "9c0::cf0:ecb:253.208.238.255", - "a::0:f1:210.240.238.49", - "8::a:1:251.238.34.9", + "a::0:f1:210.240.238.049", + "8::a:1:251.238.34.09", "81:dfe::b8:8.255.249.248", - "d3::7:b:9:83.189.8.244", - "8::9:8:8:0.7.11.252", + "d3::7:b:9:83.189.08.244", + "8::9:8:8:00.7.11.252", "2:8::c:a8:250.221.9.249", "2::f:99.8.249.247", "c:22f5::5:2c:243.15.79.89", "e:8e::da:251.243.255.2", "f15f:9::a:255.70.247.218", - "f:b::9f38:31.220.94.22", - "9::9a48:3.98.249.119", + "f:b::9f38:31.220.94.022", + "9::9a48:03.98.249.119", "d:d:9b87::2d:a:249.253.38.8", "d86d:99b::a9b:5:242.236.8.244", "eb:3::f:9cf:1.253.1.228", "b::ba2:255.247.114.64", - "2f:ec:bcb::9:219.254.250.94", + "2f:ec:bcb::9:219.254.250.094", "da8a:f6::a:e0:19.251.241.251", - "5e:c1::a:21.250.8.254", + "5e:c1::a:021.250.8.254", "c:9::8c9b:248.219.212.252", "2:a::8d4a:216.255.198.223", - "1f::66:255.30.8.150", + "1f::66:255.30.08.150", "bc2b:8f::2ff9:6.245.99.230", "a:8::a8:9.251.246.255", - "f:7:7::98:6.14.1.208", + "f:7:7::98:06.14.1.208", "e:2::9:218.249.255.254", "79:f::6:250.255.98.246", - "47:9:fb9f::9:38.136.17.251", + "47:9:fb9f::9:038.136.17.251", "ed::a:247.9.23.239", "6f::f1:88.254.119.9", "a::d:218.199.236.0", - "fc88::9:203.196.4.95", - "::8.48.255.85", - "::253.7.255.36", + "fc88::9:203.196.04.95", + "::8.048.255.85", + "::253.07.255.36", "9:d::253.7.178.229", "::250.84.158.253", "::8.55.204.248", @@ -175,47 +175,39 @@ const "df9:88ca::248.255.108.17", "8e9b::250.206.0.82", "::209.8.254.209", - "::247.88.8.8", + "::247.088.8.8", "::cb:f:ba41:250.208.19.249", "::fe:0e8:243.240.229.5", "::c:223.251.5.226", - "::8:8.3.8.250", + "::8:08.03.8.250", "::f:8.88.11.255", - "::fda:48:aa:5.189.7.2", - "::8:c3f:f:240.6.212.255", + "::fda:48:aa:05.189.07.2", + "::8:c3f:f:240.06.212.255", "::f:0aa:244.123.99.16", - "::c9b5:c:34.8.90.196", + "::c9b5:c:034.8.090.196", "::98:c9:254.14.241.81" ] negatives = ["foo.bar", "::::::::::::", "yet another failure", - "de:6:c:ab5:6a::9:252.6.6.249", - "f9:5f7:fa38:9:b::b6:9.255.248.252", + "de:6:c:ab5:6a::9:252.06.06.249", + "f9:5f7:fa38:9:b::b6:09.255.248.252", "97:c:5b:81:8a::f5dd:144.252.250.9", - "9:8:cd:8:a9::f:247.255.9.255", + "9:8:cd:8:a9::f:247.255.09.255", "18:1:8c:2:3::9:8.254.252.139", - "e:c298:3:e:a::bb12:254.246.5.250", + "e:c298:3:e:a::bb12:254.246.05.250", "e:e:c:8e:fd::8:253.8.49.231", "9:97f:f:e929:8a::c9:0.8.252.10", - "0df:b24:7:89:c::2b:16.249.240.92", + "0df:b24:7:89:c::2b:16.249.240.092", "b:8f5f:485:c:9a::84c:178.7.249.34", - "::3:e:a:bc:091.19.2.9", - "::a:f8:77.08.243.232", - "::8c:2:99.251.029.3", - "::8:c:a:f:8.35.8.096", - "d:ffa8:9:a:879:3:0202.39.8.245", - "139c:9e::f8:254.07.21.249", - "f0eb:0:e8:b:c:a:254.233.043.17", - "::a:b:85:e4d9:252.9.229.056", ] -proc ok(pos: openArray[string]) = +proc ok(pos: openarray[string]) = for p in pos: if not isIpAddress(p): echo "failure ", p -proc notok(neg: openArray[string]) = +proc notok(neg: openarray[string]) = for n in neg: if isIpAddress(n): echo "failure ", n diff --git a/tests/stdlib/tparsesql.nim b/tests/stdlib/tparsesql.nim index cfd8ad148..ba9e601a1 100644 --- a/tests/stdlib/tparsesql.nim +++ b/tests/stdlib/tparsesql.nim @@ -2,21 +2,9 @@ discard """ targets: "c js" """ import parsesql -import std/assertions -doAssert treeRepr(parseSql("INSERT INTO STATS VALUES (10, 5.5); ") -) == """ - -nkStmtList - nkInsert - nkIdent STATS - nkNone - nkValueList - nkIntegerLit 10 - nkNumericLit 5.5""" - -doAssert $parseSql("SELECT foo FROM table;") == "select foo from table;" -doAssert $parseSql(""" +doAssert $parseSQL("SELECT foo FROM table;") == "select foo from table;" +doAssert $parseSQL(""" SELECT CustomerName, ContactName, @@ -32,26 +20,26 @@ SELECT Country FROM table;""") == "select CustomerName, ContactName, Address, City, PostalCode, Country, CustomerName, ContactName, Address, City, PostalCode, Country from table;" -doAssert $parseSql("SELECT foo FROM table limit 10") == "select foo from table limit 10;" -doAssert $parseSql("SELECT foo, bar, baz FROM table limit 10") == "select foo, bar, baz from table limit 10;" -doAssert $parseSql("SELECT foo AS bar FROM table") == "select foo as bar from table;" -doAssert $parseSql("SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table") == "select foo as foo_prime, bar as bar_prime, baz as baz_prime from table;" -doAssert $parseSql("SELECT * FROM table") == "select * from table;" -doAssert $parseSql("SELECT count(*) FROM table") == "select count(*) from table;" -doAssert $parseSql("SELECT count(*) as 'Total' FROM table") == "select count(*) as 'Total' from table;" -doAssert $parseSql("SELECT count(*) as 'Total', sum(a) as 'Aggr' FROM table") == "select count(*) as 'Total', sum(a) as 'Aggr' from table;" +doAssert $parseSQL("SELECT foo FROM table limit 10") == "select foo from table limit 10;" +doAssert $parseSQL("SELECT foo, bar, baz FROM table limit 10") == "select foo, bar, baz from table limit 10;" +doAssert $parseSQL("SELECT foo AS bar FROM table") == "select foo as bar from table;" +doAssert $parseSQL("SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table") == "select foo as foo_prime, bar as bar_prime, baz as baz_prime from table;" +doAssert $parseSQL("SELECT * FROM table") == "select * from table;" +doAssert $parseSQL("SELECT count(*) FROM table") == "select count(*) from table;" +doAssert $parseSQL("SELECT count(*) as 'Total' FROM table") == "select count(*) as 'Total' from table;" +doAssert $parseSQL("SELECT count(*) as 'Total', sum(a) as 'Aggr' FROM table") == "select count(*) as 'Total', sum(a) as 'Aggr' from table;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM table WHERE a = b and c = d """) == "select * from table where a = b and c = d;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM table WHERE not b """) == "select * from table where not b;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM @@ -60,63 +48,63 @@ WHERE a and not b """) == "select * from table where a and not b;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM table ORDER BY 1 """) == "select * from table order by 1;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM table GROUP BY 1 ORDER BY 1 """) == "select * from table group by 1 order by 1;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM table ORDER BY 1 LIMIT 100 """) == "select * from table order by 1 limit 100;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM table WHERE a = b and c = d or n is null and not b + 1 = 3 """) == "select * from table where a = b and c = d or n is null and not b + 1 = 3;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM table WHERE (a = b and c = d) or (n is null and not b + 1 = 3) """) == "select * from table where(a = b and c = d) or (n is null and not b + 1 = 3);" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM table HAVING a = b and c = d """) == "select * from table having a = b and c = d;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT a, b FROM table GROUP BY a """) == "select a, b from table group by a;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT a, b FROM table GROUP BY 1, 2 """) == "select a, b from table group by 1, 2;" -doAssert $parseSql("SELECT t.a FROM t as t") == "select t.a from t as t;" +doAssert $parseSQL("SELECT t.a FROM t as t") == "select t.a from t as t;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT a, b FROM ( SELECT * FROM t ) """) == "select a, b from(select * from t);" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT a, b FROM ( SELECT * FROM t ) as foo """) == "select a, b from(select * from t) as foo;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT a, b FROM ( SELECT * FROM ( SELECT * FROM ( @@ -128,49 +116,49 @@ SELECT a, b FROM ( ) as inner5 """) == "select a, b from(select * from(select * from(select * from(select * from innerTable as inner1) as inner2) as inner3) as inner4) as inner5;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT a, b FROM (SELECT * FROM a), (SELECT * FROM b), (SELECT * FROM c) """) == "select a, b from(select * from a),(select * from b),(select * from c);" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM Products WHERE Price BETWEEN 10 AND 20; """) == "select * from Products where Price between 10 and 20;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT id FROM a JOIN b ON a.id == b.id """) == "select id from a join b on a.id == b.id;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT id FROM a JOIN (SELECT id from c) as b ON a.id == b.id """) == "select id from a join(select id from c) as b on a.id == b.id;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT id FROM a INNER JOIN b ON a.id == b.id """) == "select id from a inner join b on a.id == b.id;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT id FROM a OUTER JOIN b ON a.id == b.id """) == "select id from a outer join b on a.id == b.id;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT id FROM a CROSS JOIN b ON a.id == b.id """) == "select id from a cross join b on a.id == b.id;" -doAssert $parseSql(""" +doAssert $parseSQL(""" CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); CREATE TABLE holidays ( num_weeks int, @@ -180,54 +168,31 @@ CREATE INDEX table1_attr1 ON table1(attr1); SELECT * FROM myTab WHERE col1 = 'happy'; """) == "create type happiness as enum ('happy' , 'very happy' , 'ecstatic' ); create table holidays(num_weeks int , happiness happiness );; create index table1_attr1 on table1(attr1 );; select * from myTab where col1 = 'happy';" -doAssert $parseSql(""" +doAssert $parseSQL(""" INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country) VALUES ('Cardinal', 'Tom B. Erichsen', 'Skagen 21', 'Stavanger', '4006', 'Norway'); """) == "insert into Customers (CustomerName , ContactName , Address , City , PostalCode , Country ) values ('Cardinal' , 'Tom B. Erichsen' , 'Skagen 21' , 'Stavanger' , '4006' , 'Norway' );" -doAssert $parseSql(""" +doAssert $parseSQL(""" INSERT INTO TableName DEFAULT VALUES """) == "insert into TableName default values;" -doAssert $parseSql(""" +doAssert $parseSQL(""" UPDATE Customers SET ContactName = 'Alfred Schmidt', City= 'Frankfurt' WHERE CustomerID = 1; """) == "update Customers set ContactName = 'Alfred Schmidt' , City = 'Frankfurt' where CustomerID = 1;" -doAssert treeRepr(parseSql("""UPDATE Customers - SET ContactName = 'Alice', City= 'Frankfurt';""") -) == """ +doAssert $parseSQL("DELETE FROM table_name;") == "delete from table_name;" -nkStmtList - nkUpdate - nkIdent Customers - nkAsgn - nkIdent ContactName - nkStringLit Alice - nkAsgn - nkIdent City - nkStringLit Frankfurt - nkNone""" +doAssert $parseSQL("DELETE * FROM table_name;") == "delete from table_name;" -doAssert $parseSql("DELETE FROM table_name;") == "delete from table_name;" - -doAssert treeRepr(parseSql("DELETE FROM table_name;") -) == """ - -nkStmtList - nkDelete - nkIdent table_name - nkNone""" - -doAssert $parseSql("DELETE * FROM table_name;") == "delete from table_name;" - -doAssert $parseSql(""" +doAssert $parseSQL(""" --Select all: SELECT * FROM Customers; """) == "select * from Customers;" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT * FROM Customers WHERE (CustomerName LIKE 'L%' OR CustomerName LIKE 'R%' /*OR CustomerName LIKE 'S%' OR CustomerName LIKE 'T%'*/ OR CustomerName LIKE 'W%') @@ -236,9 +201,9 @@ ORDER BY CustomerName; """) == "select * from Customers where(CustomerName like 'L%' or CustomerName like 'R%' or CustomerName like 'W%') and Country = 'USA' order by CustomerName;" # parse quoted keywords as identifires -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT `SELECT`, `FROM` as `GROUP` FROM `WHERE`; """) == """select "SELECT", "FROM" as "GROUP" from "WHERE";""" -doAssert $parseSql(""" +doAssert $parseSQL(""" SELECT "SELECT", "FROM" as "GROUP" FROM "WHERE"; """) == """select "SELECT", "FROM" as "GROUP" from "WHERE";""" diff --git a/tests/stdlib/tparseutils.nim b/tests/stdlib/tparseutils.nim index 218dd08f6..3bc54dff1 100644 --- a/tests/stdlib/tparseutils.nim +++ b/tests/stdlib/tparseutils.nim @@ -1,100 +1,43 @@ -discard """ - targets: "c cpp" -""" +import parseutils +import sequtils -import std/[parseutils, sequtils, sugar, formatfloat] -import std/assertions +let input = "$test{} $this is ${an{ example}} " +let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), + (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")] +doAssert toSeq(interpolatedFragments(input)) == expected -proc test() = - let input = "$test{} $this is ${an{ example}} " - let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), - (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")] - doAssert toSeq(interpolatedFragments(input)) == expected +var value = 0 +discard parseHex("0x38", value) +doAssert value == 56 - var value = 0 - discard parseHex("0x38", value) - doAssert value == 56 +value = -1 +doAssert(parseSaturatedNatural("848", value) == 3) +doAssert value == 848 - value = -1 - doAssert(parseSaturatedNatural("848", value) == 3) - doAssert value == 848 +value = -1 +discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value) +doAssert value == high(int) - value = -1 - discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value) - doAssert value == high(int) +value = -1 +discard parseSaturatedNatural("9223372036854775808", value) +doAssert value == high(int) - value = -1 - discard parseSaturatedNatural("9223372036854775808", value) - doAssert value == high(int) +value = -1 +discard parseSaturatedNatural("9223372036854775807", value) +doAssert value == high(int) - value = -1 - discard parseSaturatedNatural("9223372036854775807", value) - doAssert value == high(int) +value = -1 +discard parseSaturatedNatural("18446744073709551616", value) +doAssert value == high(int) - value = -1 - discard parseSaturatedNatural("18446744073709551616", value) - doAssert value == high(int) +value = -1 +discard parseSaturatedNatural("18446744073709551615", value) +doAssert value == high(int) - value = -1 - discard parseSaturatedNatural("18446744073709551615", value) - doAssert value == high(int) +value = -1 +doAssert(parseSaturatedNatural("1_000_000", value) == 9) +doAssert value == 1_000_000 - value = -1 - doAssert(parseSaturatedNatural("1_000_000", value) == 9) - doAssert value == 1_000_000 - - var i64Value: int64 - discard parseBiggestInt("9223372036854775807", i64Value) - doAssert i64Value == 9223372036854775807 - - block: - var f: float - let res = collect: - for x in ["9.123456789012345+","11.123456789012345+","9.123456789012345-","8.123456789012345+","9.12345678901234-","9.123456789012345"]: - (parseFloat(x, f, 0), $f) - doAssert res == @[(17, "9.123456789012344"), (18, "11.123456789012344"), - (17, "9.123456789012344"), (17, "8.123456789012344"), - (16, "9.12345678901234"), (17, "9.123456789012344")] - -test() -static: test() - -block: # With this included, static: test() crashes the compiler (from a - # VM problem with parseSize calling parseFloat). - var sz: int64 - template checkParseSize(s, expectLen, expectVal) = - if (let got = parseSize(s, sz); got != expectLen): - raise newException(IOError, "got len " & $got & " != " & $expectLen) - if sz != expectVal: - raise newException(IOError, "got sz " & $sz & " != " & $expectVal) - # STRING LEN SZ - # Good, complete parses - checkParseSize "1 b" , 4, 1 - checkParseSize "1 B" , 4, 1 - checkParseSize "1k" , 2, 1000 - checkParseSize "1 kib" , 5, 1024 - checkParseSize "1 ki" , 4, 1024 - checkParseSize "1mi" , 3, 1048576 - checkParseSize "1 mi" , 4, 1048576 - checkParseSize "1 mib" , 5, 1048576 - checkParseSize "1 Mib" , 5, 1048576 - checkParseSize "1 MiB" , 5, 1048576 - checkParseSize "1.23GiB", 7, 1320702444 # 1320702443.52 rounded - checkParseSize "0.001k" , 6, 1 - checkParseSize "0.0004k", 7, 0 - checkParseSize "0.0006k", 7, 1 - # Incomplete parses - checkParseSize "1 " , 1, 1 # Trailing white IGNORED - checkParseSize "1 B " , 4, 1 # Trailing white IGNORED - checkParseSize "1 B/s" , 4, 1 # Trailing junk IGNORED - checkParseSize "1 kX" , 3, 1000 - checkParseSize "1 kiX" , 4, 1024 - checkParseSize "1j" , 1, 1 # Unknown prefix IGNORED - checkParseSize "1 jib" , 2, 1 # Unknown prefix post space - checkParseSize "1 ji" , 3, 1 - # Bad parses; `sz` should stay last good|incomplete value - checkParseSize "-1b" , 0, 1 # Negative numbers - checkParseSize "abc" , 0, 1 # Non-numeric - checkParseSize " 12" , 0, 1 # Leading white - # Value Edge cases - checkParseSize "9223372036854775807", 19, int64.high +var i64Value: int64 +discard parseBiggestInt("9223372036854775807", i64Value) +doAssert i64Value == 9223372036854775807 diff --git a/tests/stdlib/tparsopt.nim b/tests/stdlib/tparsopt.nim index f3a9a9798..54a470cb3 100644 --- a/tests/stdlib/tparsopt.nim +++ b/tests/stdlib/tparsopt.nim @@ -9,8 +9,6 @@ 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 deleted file mode 100644 index 1cd913084..000000000 --- a/tests/stdlib/tpathnorm.nim +++ /dev/null @@ -1,35 +0,0 @@ -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/tpaths.nim b/tests/stdlib/tpaths.nim deleted file mode 100644 index 2d1a3e2ca..000000000 --- a/tests/stdlib/tpaths.nim +++ /dev/null @@ -1,230 +0,0 @@ -import std/paths -import std/assertions -import pathnorm -from std/private/ospaths2 {.all.} import joinPathImpl -import std/sugar - - -proc normalizePath*(path: Path; dirSep = DirSep): Path = - result = Path(pathnorm.normalizePath(path.string, dirSep)) - -func `==`(x, y: Path): bool = - x.string == y.string - -func joinPath*(parts: varargs[Path]): Path = - var estimatedLen = 0 - var state = 0 - for p in parts: estimatedLen += p.string.len - var res = newStringOfCap(estimatedLen) - for i in 0..high(parts): - joinPathImpl(res, state, parts[i].string) - result = Path(res) - - -func joinPath(head, tail: Path): Path {.inline.} = - head / tail - -block absolutePath: - doAssertRaises(ValueError): discard absolutePath(Path"a", Path"b") - doAssert absolutePath(Path"a") == getCurrentDir() / Path"a" - doAssert absolutePath(Path"a", Path"/b") == Path"/b" / Path"a" - when defined(posix): - doAssert absolutePath(Path"a", Path"/b/") == Path"/b" / Path"a" - doAssert absolutePath(Path"a", Path"/b/c") == Path"/b/c" / Path"a" - doAssert absolutePath(Path"/a", Path"b/") == Path"/a" - -block splitFile: - doAssert splitFile(Path"") == (Path"", Path"", "") - doAssert splitFile(Path"abc/") == (Path"abc", Path"", "") - doAssert splitFile(Path"/") == (Path"/", Path"", "") - doAssert splitFile(Path"./abc") == (Path".", Path"abc", "") - doAssert splitFile(Path".txt") == (Path"", Path".txt", "") - doAssert splitFile(Path"abc/.txt") == (Path"abc", Path".txt", "") - doAssert splitFile(Path"abc") == (Path"", Path"abc", "") - doAssert splitFile(Path"abc.txt") == (Path"", Path"abc", ".txt") - doAssert splitFile(Path"/abc.txt") == (Path"/", Path"abc", ".txt") - doAssert splitFile(Path"/foo/abc.txt") == (Path"/foo", Path"abc", ".txt") - doAssert splitFile(Path"/foo/abc.txt.gz") == (Path"/foo", Path"abc.txt", ".gz") - doAssert splitFile(Path".") == (Path"", Path".", "") - doAssert splitFile(Path"abc/.") == (Path"abc", Path".", "") - doAssert splitFile(Path"..") == (Path"", Path"..", "") - doAssert splitFile(Path"a/..") == (Path"a", Path"..", "") - doAssert splitFile(Path"/foo/abc....txt") == (Path"/foo", Path"abc...", ".txt") - -# execShellCmd is tested in tosproc - -block ospaths: - doAssert unixToNativePath(Path"") == Path"" - doAssert unixToNativePath(Path".") == Path($CurDir) - doAssert unixToNativePath(Path"..") == Path($ParDir) - doAssert isAbsolute(unixToNativePath(Path"/")) - doAssert isAbsolute(unixToNativePath(Path"/", Path"a")) - doAssert isAbsolute(unixToNativePath(Path"/a")) - doAssert isAbsolute(unixToNativePath(Path"/a", Path"a")) - doAssert isAbsolute(unixToNativePath(Path"/a/b")) - doAssert isAbsolute(unixToNativePath(Path"/a/b", Path"a")) - doAssert unixToNativePath(Path"a/b") == joinPath(Path"a", Path"b") - - when defined(macos): - doAssert unixToNativePath(Path"./") == Path":" - doAssert unixToNativePath(Path"./abc") == Path":abc" - doAssert unixToNativePath(Path"../abc") == Path"::abc" - doAssert unixToNativePath(Path"../../abc") == Path":::abc" - doAssert unixToNativePath(Path"/abc", Path"a") == Path"abc" - doAssert unixToNativePath(Path"/abc/def", Path"a") == Path"abc:def" - elif doslikeFileSystem: - doAssert unixToNativePath(Path"./") == Path(".\\") - doAssert unixToNativePath(Path"./abc") == Path(".\\abc") - doAssert unixToNativePath(Path"../abc") == Path("..\\abc") - doAssert unixToNativePath(Path"../../abc") == Path("..\\..\\abc") - doAssert unixToNativePath(Path"/abc", Path"a") == Path("a:\\abc") - doAssert unixToNativePath(Path"/abc/def", Path"a") == Path("a:\\abc\\def") - else: - #Tests for unix - doAssert unixToNativePath(Path"./") == Path"./" - doAssert unixToNativePath(Path"./abc") == Path"./abc" - doAssert unixToNativePath(Path"../abc") == Path"../abc" - doAssert unixToNativePath(Path"../../abc") == Path"../../abc" - doAssert unixToNativePath(Path"/abc", Path"a") == Path"/abc" - doAssert unixToNativePath(Path"/abc/def", Path"a") == Path"/abc/def" - - block extractFilenameTest: - doAssert extractFilename(Path"") == Path"" - when defined(posix): - doAssert extractFilename(Path"foo/bar") == Path"bar" - doAssert extractFilename(Path"foo/bar.txt") == Path"bar.txt" - doAssert extractFilename(Path"foo/") == Path"" - doAssert extractFilename(Path"/") == Path"" - when doslikeFileSystem: - doAssert extractFilename(Path(r"foo\bar")) == Path"bar" - doAssert extractFilename(Path(r"foo\bar.txt")) == Path"bar.txt" - doAssert extractFilename(Path(r"foo\")) == Path"" - doAssert extractFilename(Path(r"C:\")) == Path"" - - block lastPathPartTest: - doAssert lastPathPart(Path"") == Path"" - when defined(posix): - doAssert lastPathPart(Path"foo/bar.txt") == Path"bar.txt" - doAssert lastPathPart(Path"foo/") == Path"foo" - doAssert lastPathPart(Path"/") == Path"" - when doslikeFileSystem: - doAssert lastPathPart(Path(r"foo\bar.txt")) == Path"bar.txt" - doAssert lastPathPart(Path(r"foo\")) == Path"foo" - - template canon(x): Path = normalizePath(Path(x), '/') - doAssert canon"/foo/../bar" == Path"/bar" - doAssert canon"foo/../bar" == Path"bar" - - doAssert canon"/f/../bar///" == Path"/bar" - doAssert canon"f/..////bar" == Path"bar" - - doAssert canon"../bar" == Path"../bar" - doAssert canon"/../bar" == Path"/../bar" - - doAssert canon("foo/../../bar/") == Path"../bar" - doAssert canon("./bla/blob/") == Path"bla/blob" - doAssert canon(".hiddenFile") == Path".hiddenFile" - doAssert canon("./bla/../../blob/./zoo.nim") == Path"../blob/zoo.nim" - - doAssert canon("C:/file/to/this/long") == Path"C:/file/to/this/long" - doAssert canon("") == Path"" - doAssert canon("foobar") == Path"foobar" - doAssert canon("f/////////") == Path"f" - - doAssert relativePath(Path"/foo/bar//baz.nim", Path"/foo", '/') == Path"bar/baz.nim" - doAssert normalizePath(Path"./foo//bar/../baz", '/') == Path"foo/baz" - - doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/other/bad", '/') == Path"../../me/bar/z.nim" - - doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/other", '/') == Path"../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(Path"/Users///me/bar//z.nim", Path"//Users/", '/') == Path"me/bar/z.nim" - doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/me", '/') == Path"bar/z.nim" - doAssert relativePath(Path"", Path"/users/moo", '/') == Path"" - doAssert relativePath(Path"foo", Path"", '/') == Path"foo" - doAssert relativePath(Path"/foo", Path"/Foo", '/') == (when FileSystemCaseSensitive: Path"../foo" else: Path".") - doAssert relativePath(Path"/Foo", Path"/foo", '/') == (when FileSystemCaseSensitive: Path"../Foo" else: Path".") - doAssert relativePath(Path"/foo", Path"/fOO", '/') == (when FileSystemCaseSensitive: Path"../foo" else: Path".") - doAssert relativePath(Path"/foO", Path"/foo", '/') == (when FileSystemCaseSensitive: Path"../foO" else: Path".") - - doAssert relativePath(Path"foo", Path".", '/') == Path"foo" - doAssert relativePath(Path".", Path".", '/') == Path"." - doAssert relativePath(Path"..", Path".", '/') == Path".." - - doAssert relativePath(Path"foo", Path"foo") == Path"." - doAssert relativePath(Path"", Path"foo") == Path"" - doAssert relativePath(Path"././/foo", Path"foo//./") == Path"." - - doAssert relativePath(getCurrentDir() / Path"bar", Path"foo") == Path"../bar".unixToNativePath - doAssert relativePath(Path"bar", getCurrentDir() / Path"foo") == Path"../bar".unixToNativePath - - when doslikeFileSystem: - doAssert relativePath(r"c:\foo.nim".Path, r"C:\".Path) == r"foo.nim".Path - doAssert relativePath(r"c:\foo\bar\baz.nim".Path, r"c:\foo".Path) == r"bar\baz.nim".Path - doAssert relativePath(r"c:\foo\bar\baz.nim".Path, r"d:\foo".Path) == r"c:\foo\bar\baz.nim".Path - doAssert relativePath(r"\foo\baz.nim".Path, r"\foo".Path) == r"baz.nim".Path - doAssert relativePath(r"\foo\bar\baz.nim".Path, r"\bar".Path) == r"..\foo\bar\baz.nim".Path - doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foo\bar".Path) == r"baz.nim".Path - doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foO\bar".Path) == r"baz.nim".Path - doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\bar\bar".Path) == r"\\foo\bar\baz.nim".Path - doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foo\car".Path) == r"\\foo\bar\baz.nim".Path - doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\goo\bar".Path) == r"\\foo\bar\baz.nim".Path - doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"c:\".Path) == r"\\foo\bar\baz.nim".Path - doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\foo".Path) == r"\\foo\bar\baz.nim".Path - doAssert relativePath(r"c:\foo.nim".Path, r"\foo".Path) == r"c:\foo.nim".Path - - doAssert joinPath(Path"usr", Path"") == unixToNativePath(Path"usr") - doAssert joinPath(Path"usr", Path"") == (Path"usr").dup(add Path"") - doAssert joinPath(Path"", Path"lib") == Path"lib" - doAssert joinPath(Path"", Path"lib") == Path"".dup(add Path"lib") - doAssert joinPath(Path"", Path"/lib") == unixToNativePath(Path"/lib") - doAssert joinPath(Path"", Path"/lib") == unixToNativePath(Path"/lib") - doAssert joinPath(Path"usr/", Path"/lib") == Path"usr/".dup(add Path"/lib") - doAssert joinPath(Path"", Path"") == unixToNativePath(Path"") # issue #13455 - doAssert joinPath(Path"", Path"") == Path"".dup(add Path"") - doAssert joinPath(Path"", Path"/") == unixToNativePath(Path"/") - doAssert joinPath(Path"", Path"/") == Path"".dup(add Path"/") - doAssert joinPath(Path"/", Path"/") == unixToNativePath(Path"/") - doAssert joinPath(Path"/", Path"/") == Path"/".dup(add Path"/") - doAssert joinPath(Path"/", Path"") == unixToNativePath(Path"/") - doAssert joinPath(Path"/" / Path"") == unixToNativePath(Path"/") # weird test case... - doAssert joinPath(Path"/", Path"/a/b/c") == unixToNativePath(Path"/a/b/c") - doAssert joinPath(Path"foo/", Path"") == unixToNativePath(Path"foo/") - doAssert joinPath(Path"foo/", Path"abc") == unixToNativePath(Path"foo/abc") - doAssert joinPath(Path"foo//./", Path"abc/.//") == unixToNativePath(Path"foo/abc/") - doAssert Path"foo//./".dup(add Path"abc/.//") == unixToNativePath(Path"foo/abc/") - doAssert joinPath(Path"foo", Path"abc") == unixToNativePath(Path"foo/abc") - doAssert Path"foo".dup(add Path"abc") == unixToNativePath(Path"foo/abc") - doAssert joinPath(Path"", Path"abc") == unixToNativePath(Path"abc") - - doAssert joinPath(Path"zook/.", Path"abc") == unixToNativePath(Path"zook/abc") - - # controversial: inconsistent with `joinPath("zook/.","abc")` - # on linux, `./foo` and `foo` are treated a bit differently for executables - # but not `./foo/bar` and `foo/bar` - doAssert joinPath(Path".", Path"/lib") == unixToNativePath(Path"./lib") - doAssert joinPath(Path".", Path"abc") == unixToNativePath(Path"./abc") - - # cases related to issue #13455 - doAssert joinPath(Path"foo", Path"", Path"") == Path"foo" - doAssert joinPath(Path"foo", Path"") == Path"foo" - doAssert joinPath(Path"foo/", Path"") == unixToNativePath(Path"foo/") - doAssert joinPath(Path"foo/", Path".") == Path"foo" - doAssert joinPath(Path"foo", Path"./") == unixToNativePath(Path"foo/") - doAssert joinPath(Path"foo", Path"", Path"bar/") == unixToNativePath(Path"foo/bar/") - - # issue #13579 - doAssert joinPath(Path"/foo", Path"../a") == unixToNativePath(Path"/a") - doAssert joinPath(Path"/foo/", Path"../a") == unixToNativePath(Path"/a") - doAssert joinPath(Path"/foo/.", Path"../a") == unixToNativePath(Path"/a") - doAssert joinPath(Path"/foo/.b", Path"../a") == unixToNativePath(Path"/foo/a") - doAssert joinPath(Path"/foo///", Path"..//a/") == unixToNativePath(Path"/a/") - doAssert joinPath(Path"foo/", Path"../a") == unixToNativePath(Path"a") - - when doslikeFileSystem: - doAssert joinPath(Path"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\", Path"..\\..\\VC\\vcvarsall.bat") == r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat".Path - doAssert joinPath(Path"C:\\foo", Path"..\\a") == r"C:\a".Path - doAssert joinPath(Path"C:\\foo\\", Path"..\\a") == r"C:\a".Path \ No newline at end of file diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index ab2a6d395..99b7dc6f4 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -1,5 +1,5 @@ discard """ - targets: "c cpp js" + targets: "c js" output: ''' PEG AST traversal output ------------------------ @@ -51,10 +51,8 @@ Event parser output ''' """ -when defined(nimHasEffectsOf): - {.experimental: "strictEffects".} - -import std/[strutils, streams, pegs, assertions] +import strutils, streams +import pegs const indent = " " @@ -106,9 +104,9 @@ block: block: var - pStack {.threadvar.}: seq[string] - valStack {.threadvar.}: seq[float] - opStack {.threadvar.}: string + pStack: seq[string] = @[] + valStack: seq[float] = @[] + opStack = "" let parseArithExpr = pegAst.eventParser: pkNonTerminal: @@ -127,7 +125,7 @@ block: discard of "Sum", "Product": try: - let val {.used.} = matchStr.parseFloat + let val = matchStr.parseFloat except ValueError: if valStack.len > 1 and opStack.len > 0: valStack[^2] = case opStack[^1] @@ -149,186 +147,3 @@ block: echo "-------------------" let pLen = parseArithExpr(txt) doAssert txt.len == pLen - - -import std/importutils - -block: - proc pegsTest() = - 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'") - doAssert(not match("W_HI_L", peg"\y 'while'")) - doAssert(not match("W_HI_Le", peg"\y v'while'")) - doAssert match("W_HI_Le", peg"y'while'") - - doAssert($ +digits == $peg"\d+") - doAssert "0158787".match(peg"\d+") - doAssert "ABC 0232".match(peg"\w+\s+\d+") - doAssert "ABC".match(peg"\d+ / \w+") - - var accum: seq[string] = @[] - for word in split("00232this02939is39an22example111", peg"\d+"): - accum.add(word) - doAssert(accum == @["this", "is", "an", "example"]) - - doAssert matchLen("key", ident) == 3 - - var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) - doAssert matchLen("key1= cal9", pattern) == 11 - - var ws = newNonTerminal("ws", 1, 1) - ws.rule = *whitespace - - var expr = newNonTerminal("expr", 1, 1) - expr.rule = sequence(capture(ident), *sequence( - nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) - - var c: Captures - var s = "a+b + c +d+e+f" - doAssert rawMatch(s, expr.rule, 0, c) == len(s) - var a = "" - for i in 0..c.ml-1: - a.add(substr(s, c.matches[i][0], c.matches[i][1])) - doAssert a == "abcdef" - #echo expr.rule - - #const filename = "lib/devel/peg/grammar.txt" - #var grammar = parsePeg(newFileStream(filename, fmRead), filename) - #echo "a <- [abc]*?".match(grammar) - doAssert find("_____abc_______", term("abc"), 2) == 5 - doAssert match("_______ana", peg"A <- 'ana' / . A") - doAssert match("abcs%%%", peg"A <- ..A / .A / '%'") - - var matches: array[0..MaxSubpatterns-1, string] - if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": - doAssert matches[0] == "abc" - else: - doAssert false - - var g2 = peg"""S <- A B / C D - A <- 'a'+ - B <- 'b'+ - C <- 'c'+ - D <- 'd'+ - """ - doAssert($g2 == "((A B) / (C D))") - doAssert match("cccccdddddd", g2) - doAssert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") == - "var1<-keykey; var2<-key2key2") - doAssert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") == - "$1<-$2$2; $1<-$2$2") - doAssert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") - - if "aaaaaa" =~ peg"'aa' !. / ({'a'})+": - doAssert matches[0] == "a" - else: - doAssert false - - if match("abcdefg", peg"c {d} ef {g}", matches, 2): - doAssert matches[0] == "d" - doAssert matches[1] == "g" - else: - doAssert false - - accum = @[] - for x in findAll("abcdef", peg".", 3): - accum.add(x) - doAssert(accum == @["d", "e", "f"]) - - for x in findAll("abcdef", peg"^{.}", 3): - doAssert x == "d" - - if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": - doAssert matches[0] == "f" - doAssert matches[1] == "a, b" - else: - doAssert false - - doAssert match("eine übersicht und außerdem", peg"(\letter \white*)+") - # ß is not a lower cased letter?! - doAssert match("eine übersicht und auerdem", peg"(\lower \white*)+") - doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") - doAssert(not match("456678", peg"(\letter)+")) - - doAssert("var1 = key; var2 = key2".replacef( - peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == - "var1<-keykey;var2<-key2key2") - - doAssert match("prefix/start", peg"^start$", 7) - - if "foo" =~ peg"{'a'}?.*": - doAssert matches[0].len == 0 - else: doAssert false - - if "foo" =~ peg"{''}.*": - doAssert matches[0] == "" - else: doAssert false - - if "foo" =~ peg"{'foo'}": - doAssert matches[0] == "foo" - else: doAssert false - - let empty_test = peg"^\d*" - let str = "XYZ" - - doAssert(str.find(empty_test) == 0) - doAssert(str.match(empty_test)) - - proc handleMatches(m: int, n: int, c: openArray[string]): string = - result = "" - - if m > 0: - result.add ", " - - result.add case n: - of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'" - of 1: toLowerAscii(c[0]) & ": ''" - else: "" - - doAssert("Var1=key1;var2=Key2; VAR3". - replace(peg"{\ident}('='{\ident})* ';'* \s*", - handleMatches) == "var1: 'key1', var2: 'Key2', var3: ''") - - - doAssert "test1".match(peg"""{@}$""") - doAssert "test2".match(peg"""{(!$ .)*} $""") - - doAssert "abbb".match(peg"{a} {b} $2 $^1") - doAssert "abBA".match(peg"{a} {b} i$2 i$^2") - - doAssert "abba".match(peg"{a} {b} $^1 {} $^1") - - block: - let grammar = peg""" -program <- {''} stmt* $ -stmt <- call / block -call <- 'call()' EOL -EOL <- \n / $ -block <- 'block:' \n indBody -indBody <- {$^1 ' '+} stmt ($^1 stmt)* {} -""" - let program = """ -call() -block: - block: - call() - call() - call() -call() -""" - var c: Captures - doAssert program.len == program.rawMatch(grammar, 0, c) - doAssert c.ml == 1 - - pegsTest() - static: - pegsTest() - diff --git a/tests/stdlib/tposix.nim b/tests/stdlib/tposix.nim index d06c5cd56..14f1fd6e2 100644 --- a/tests/stdlib/tposix.nim +++ b/tests/stdlib/tposix.nim @@ -7,7 +7,6 @@ outputsub: "" when not defined(windows): import posix - import std/[assertions, syncio] var u: Utsname @@ -18,46 +17,3 @@ when not defined(windows): writeLine(stdout, u.nodename) writeLine(stdout, u.release) writeLine(stdout, u.machine) - - when not (defined(nintendoswitch) or defined(macos) or defined(macosx)): - block: - type Message = object - value: int - - const MQ_PATH: cstring = "/top_level_file" - const MQ_PRIORITY: cuint = 170 - const MQ_MESSAGE_SIZE: csize_t = csize_t(sizeof(Message)) - - let mqd_a: posix.MqAttr = MqAttr(mq_maxmsg: 10, mq_msgsize: clong(MQ_MESSAGE_SIZE)) - let writable: posix.Mqd = posix.mq_open( - MQ_PATH, - posix.O_CREAT or posix.O_WRONLY or posix.O_NONBLOCK, - posix.S_IRWXU, - addr(mqd_a) - ) - let readable: posix.Mqd = posix.mq_open( - MQ_PATH, - posix.O_RDONLY or posix.O_NONBLOCK, - posix.S_IRWXU, - addr(mqd_a) - ) - - let sent: Message = Message(value: 88) - block: - let success: int = writable.mq_send( - cast[cstring](sent.addr), - MQ_MESSAGE_SIZE, - MQ_PRIORITY - ) - doAssert success == 0, $success - - block: - var buffer: Message - var priority: cuint - let bytesRead: int = readable.mq_receive( - cast[cstring](buffer.addr), - MQ_MESSAGE_SIZE, - priority - ) - doAssert buffer == sent - doAssert bytesRead == int(MQ_MESSAGE_SIZE) diff --git a/tests/stdlib/tprelude.nim b/tests/stdlib/tprelude.nim deleted file mode 100644 index 47f46b511..000000000 --- a/tests/stdlib/tprelude.nim +++ /dev/null @@ -1,16 +0,0 @@ -discard """ - targets: "c js" - matrix: "; -d:nimTestTpreludeCase1" -""" - -when defined nimTestTpreludeCase1: - include std/prelude -else: - include prelude - -import std/assertions - -template main() = - doAssert toSeq(1..3) == @[1,2,3] -static: main() -main() diff --git a/tests/stdlib/tpunycode.nim b/tests/stdlib/tpunycode.nim new file mode 100644 index 000000000..596c5ef63 --- /dev/null +++ b/tests/stdlib/tpunycode.nim @@ -0,0 +1,5 @@ +import punycode + +doAssert(decode(encode("", "bücher")) == "bücher") +doAssert(decode(encode("münchen")) == "münchen") +doAssert encode("xn--", "münchen") == "xn--mnchen-3ya" diff --git a/tests/stdlib/tquit.nim b/tests/stdlib/tquit.nim index 12385e57b..0b4a479dc 100644 --- a/tests/stdlib/tquit.nim +++ b/tests/stdlib/tquit.nim @@ -1,18 +1,13 @@ discard """ -disabled: true output: ''' just exiting... ''' joinable: false """ -# Test `addQuitProc` (now deprecated by `addExitProc`) - -import std/syncio +# Test `addQuitProc` proc myExit() {.noconv.} = write(stdout, "just exiting...\n") -{.push warning[deprecated]: off.} addQuitProc(myExit) -{.pop.} diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim index ef71c3442..f8797944e 100644 --- a/tests/stdlib/trandom.nim +++ b/tests/stdlib/trandom.nim @@ -1,15 +1,13 @@ discard """ - joinable: false # to avoid messing with global rand state + joinable: false targets: "c js" """ -import std/[assertions, formatfloat] -import std/[random, math, stats, sets, tables] -when not defined(js): - import std/os + +import std/[random, stats] randomize(233) -proc main() = +proc main = var occur: array[1000, int] for i in 0..100_000: @@ -23,10 +21,9 @@ proc main() = doAssert a in [[0,1], [1,0]] doAssert rand(0) == 0 - when not defined(nimscript): - doAssert sample("a") == 'a' + doAssert sample("a") == 'a' - when compileOption("rangeChecks") and not defined(nimscript): + when compileOption("rangeChecks"): doAssertRaises(RangeDefect): discard rand(-1) @@ -36,18 +33,18 @@ proc main() = # don't use causes integer overflow doAssert compiles(rand[int](low(int) .. high(int))) + main() +import math + block: when not defined(js): - doAssert almostEqual(rand(12.5), 7.355175342026979) - doAssert almostEqual(rand(2233.3322), 499.342386778917) + doAssert almostEqual(rand(12.5), 4.012897747078944) + doAssert almostEqual(rand(2233.3322), 879.702755321298) type DiceRoll = range[0..6] - when not defined(js): - doAssert rand(DiceRoll).int == 3 - else: - doAssert rand(DiceRoll).int == 6 + doAssert rand(DiceRoll).int == 4 var rs: RunningStat for j in 1..5: @@ -67,208 +64,3 @@ block: if rand(5.DiceRoll) < 3: flag = true doAssert flag # because of: rand(max: int): int - - -block: # random int - block: # there might be some randomness - var set = initHashSet[int](128) - - for i in 1..1000: - incl(set, rand(high(int))) - doAssert len(set) == 1000 - - block: # single number bounds work - var rand: int - for i in 1..1000: - rand = rand(1000) - doAssert rand <= 1000 - doAssert rand >= 0 - - block: # slice bounds work - var rand: int - for i in 1..1000: - rand = rand(100..1000) - doAssert rand <= 1000 - doAssert rand >= 100 - - block: # again gives new numbers - var rand1 = rand(1000000) - when not (defined(js) or defined(nimscript)): - os.sleep(200) - - var rand2 = rand(1000000) - doAssert rand1 != rand2 - -block: # random float - block: # there might be some randomness - var set = initHashSet[float](128) - - for i in 1..100: - incl(set, rand(1.0)) - doAssert len(set) == 100 - - block: # single number bounds work - var rand: float - for i in 1..1000: - rand = rand(1000.0) - doAssert rand <= 1000.0 - doAssert rand >= 0.0 - - block: # slice bounds work - var rand: float - for i in 1..1000: - rand = rand(100.0..1000.0) - doAssert rand <= 1000.0 - doAssert rand >= 100.0 - - block: # again gives new numbers - var rand1: float = rand(1000000.0) - when not (defined(js) or defined(nimscript)): - os.sleep(200) - - var rand2: float = rand(1000000.0) - doAssert rand1 != rand2 - -block: # random sample - block: # "non-uniform array sample unnormalized int CDF - let values = [10, 20, 30, 40, 50] # values - let counts = [4, 3, 2, 1, 0] # weights aka unnormalized probabilities - var histo = initCountTable[int]() - let cdf = counts.cumsummed # unnormalized CDF - for i in 0 ..< 5000: - histo.inc(sample(values, cdf)) - doAssert histo.len == 4 # number of non-zero in `counts` - # Any one bin is a binomial random var for n samples, each with prob p of - # adding a count to k; E[k]=p*n, Var k=p*(1-p)*n, approximately Normal for - # big n. So, P(abs(k - p*n)/sqrt(p*(1-p)*n))>3.0) =~ 0.0027, while - # P(wholeTestFails) =~ 1 - P(binPasses)^4 =~ 1 - (1-0.0027)^4 =~ 0.01. - for i, c in counts: - if c == 0: - doAssert values[i] notin histo - continue - let p = float(c) / float(cdf[^1]) - let n = 5000.0 - let expected = p * n - let stdDev = sqrt(n * p * (1.0 - p)) - doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev - - block: # non-uniform array sample normalized float CDF - let values = [10, 20, 30, 40, 50] # values - let counts = [0.4, 0.3, 0.2, 0.1, 0] # probabilities - var histo = initCountTable[int]() - let cdf = counts.cumsummed # normalized CDF - for i in 0 ..< 5000: - histo.inc(sample(values, cdf)) - doAssert histo.len == 4 # number of non-zero in ``counts`` - for i, c in counts: - if c == 0: - doAssert values[i] notin histo - continue - let p = float(c) / float(cdf[^1]) - let n = 5000.0 - let expected = p * n - let stdDev = sqrt(n * p * (1.0 - p)) - # NOTE: like unnormalized int CDF test, P(wholeTestFails) =~ 0.01. - doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev - -block: - # 0 is a valid seed - var r = initRand(0) - doAssert r.rand(1.0) != r.rand(1.0) - r = initRand(10) - doAssert r.rand(1.0) != r.rand(1.0) - # changing the seed changes the sequence - var r1 = initRand(123) - var r2 = initRand(124) - doAssert r1.rand(1.0) != r2.rand(1.0) - -block: # bug #17467 - let n = 1000 - for i in -n .. n: - var r = initRand(i) - let x = r.rand(1.0) - doAssert x > 1e-4, $(x, i) - # This used to fail for each i in 0..<26844, i.e. the 1st produced value - # was predictable and < 1e-4, skewing distributions. - -const withUint = false # pending exporting `proc rand[T: uint | uint64](r: var Rand; max: T): T =` - -block: # bug #16360 - var r = initRand() - template test(a) = - let a2 = a - block: - let a3 = r.rand(a2) - doAssert a3 <= a2 - doAssert a3.type is a2.type - block: - let a3 = rand(a2) - doAssert a3 <= a2 - doAssert a3.type is a2.type - when withUint: - test cast[uint](int.high) - test cast[uint](int.high) + 1 - when not defined(js): - # pending bug #16411 - test uint64.high - test uint64.high - 1 - test uint.high - 2 - test uint.high - 1 - test uint.high - test int.high - test int.high - 1 - test int.high - 2 - test 0 - when withUint: - test 0'u - test 0'u64 - -block: # bug #16296 - var r = initRand() - template test(x) = - let a2 = x - let a3 = r.rand(a2) - doAssert a3 <= a2.b - doAssert a3 >= a2.a - doAssert a3.type is a2.a.type - test(-2 .. int.high-1) - test(int.low .. int.high) - test(int.low+1 .. int.high) - test(int.low .. int.high-1) - test(int.low .. 0) - test(int.low .. -1) - test(int.low .. 1) - test(int64.low .. 1'i64) - when not defined(js): - # pending bug #16411 - test(10'u64 .. uint64.high) - -block: # bug #17670 - when not defined(js): - # pending bug #16411 - type UInt48 = range[0'u64..2'u64^48-1] - let x = rand(UInt48) - doAssert x is UInt48 - -block: # bug #17898 - # Checks whether `initRand()` generates unique states. - # size should be 2^64, but we don't have time and space. - - # Disable this test for js until js gets proper skipRandomNumbers. - when not defined(js): - const size = 1000 - var - rands: array[size, Rand] - randSet: HashSet[Rand] - for i in 0.. 0 +doAssert(a == a) +doAssert( (a-a) == z) +doAssert( (a+b) == o) +doAssert( (a/b) == o) +doAssert( (a*b) == 1 // 4) +doAssert( (3/a) == 6 // 1) +doAssert( (a/3) == 1 // 6) +doAssert(a*b == 1 // 4) +doAssert(tt*z == z) +doAssert(10*a == tt) +doAssert(a*10 == tt) +doAssert(tt/10 == a) +doAssert(a-m1 == 3 // 2) +doAssert(a+m1 == -1 // 2) +doAssert(m1+tt == 16 // 4) +doAssert(m1-tt == 6 // -1) - doAssert o == o - doAssert o >= o - doAssert not(o > o) - doAssert cmp(o, o) == 0 - doAssert cmp(z, z) == 0 - doAssert hash(o) == hash(o) +doAssert(z < o) +doAssert(z <= o) +doAssert(z == z) +doAssert(cmp(z, o) < 0) +doAssert(cmp(o, z) > 0) - doAssert a == b - doAssert a >= b - doAssert not(b > a) - doAssert cmp(a, b) == 0 - doAssert hash(a) == hash(b) +doAssert(o == o) +doAssert(o >= o) +doAssert(not(o > o)) +doAssert(cmp(o, o) == 0) +doAssert(cmp(z, z) == 0) +doAssert(hash(o) == hash(o)) - var x = 1 // 3 +doAssert(a == b) +doAssert(a >= b) +doAssert(not(b > a)) +doAssert(cmp(a, b) == 0) +doAssert(hash(a) == hash(b)) - x *= 5 // 1 - doAssert x == 5 // 3 - x += 2 // 9 - doAssert x == 17 // 9 - x -= 9 // 18 - doAssert x == 25 // 18 - x /= 1 // 2 - doAssert x == 50 // 18 +var x = 1//3 - var y = 1 // 3 +x *= 5//1 +doAssert(x == 5//3) +x += 2 // 9 +doAssert(x == 17//9) +x -= 9//18 +doAssert(x == 25//18) +x /= 1//2 +doAssert(x == 50//18) - y *= 4 - doAssert y == 4 // 3 - y += 5 - doAssert y == 19 // 3 - y -= 2 - doAssert y == 13 // 3 - y /= 9 - doAssert y == 13 // 27 +var y = 1//3 - doAssert toRational(5) == 5 // 1 - doAssert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 - doAssert toInt(z) == 0 +y *= 4 +doAssert(y == 4//3) +y += 5 +doAssert(y == 19//3) +y -= 2 +doAssert(y == 13//3) +y /= 9 +doAssert(y == 13//27) - when sizeof(int) == 8: - doAssert toRational(0.98765432) == 2111111029 // 2137499919 - doAssert toRational(PI) == 817696623 // 260280919 - when sizeof(int) == 4: - doAssert toRational(0.98765432) == 80 // 81 - doAssert toRational(PI) == 355 // 113 +doAssert toRational(5) == 5//1 +doAssert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 +doAssert toInt(z) == 0 - doAssert toRational(0.1) == 1 // 10 - doAssert toRational(0.9) == 9 // 10 +when sizeof(int) == 8: + doAssert toRational(0.98765432) == 2111111029 // 2137499919 + doAssert toRational(PI) == 817696623 // 260280919 +when sizeof(int) == 4: + doAssert toRational(0.98765432) == 80 // 81 + doAssert toRational(PI) == 355 // 113 - doAssert toRational(0.0) == 0 // 1 - doAssert toRational(-0.25) == 1 // -4 - doAssert toRational(3.2) == 16 // 5 - doAssert toRational(0.33) == 33 // 100 - doAssert toRational(0.22) == 11 // 50 - doAssert toRational(10.0) == 10 // 1 +doAssert toRational(0.1) == 1 // 10 +doAssert toRational(0.9) == 9 // 10 - doAssert (1 // 1) div (3 // 10) == 3 - doAssert (-1 // 1) div (3 // 10) == -3 - doAssert (3 // 10) mod (1 // 1) == 3 // 10 - doAssert (-3 // 10) mod (1 // 1) == -3 // 10 - doAssert floorDiv(1 // 1, 3 // 10) == 3 - doAssert floorDiv(-1 // 1, 3 // 10) == -4 - doAssert floorMod(3 // 10, 1 // 1) == 3 // 10 - doAssert floorMod(-3 // 10, 1 // 1) == 7 // 10 +doAssert toRational(0.0) == 0 // 1 +doAssert toRational(-0.25) == 1 // -4 +doAssert toRational(3.2) == 16 // 5 +doAssert toRational(0.33) == 33 // 100 +doAssert toRational(0.22) == 11 // 50 +doAssert toRational(10.0) == 10 // 1 - when sizeof(int) == 8: - doAssert almostEqual(PI.toRational.toFloat, PI) - -static: main() -main() +doAssert (1//1) div (3//10) == 3 +doAssert (-1//1) div (3//10) == -3 +doAssert (3//10) mod (1//1) == 3//10 +doAssert (-3//10) mod (1//1) == -3//10 +doAssert floorDiv(1//1, 3//10) == 3 +doAssert floorDiv(-1//1, 3//10) == -4 +doAssert floorMod(3//10, 1//1) == 3//10 +doAssert floorMod(-3//10, 1//1) == 7//10 diff --git a/tests/stdlib/tre.nim b/tests/stdlib/tre.nim index 3986934c4..ea1b5af32 100644 --- a/tests/stdlib/tre.nim +++ b/tests/stdlib/tre.nim @@ -1,5 +1,4 @@ import std/re -import std/assertions proc testAll() = doAssert match("(a b c)", rex"\( .* \)") @@ -100,19 +99,10 @@ proc testAll() = accum.add($x) doAssert(accum == @["a","b","c"]) - block: # bug #9306 + block: + # bug #9306 doAssert replace("bar", re"^", "foo") == "foobar" + doAssert replace("foo", re"", "-") == "-foo" doAssert replace("foo", re"$", "bar") == "foobar" - - block: # bug #9437 - doAssert replace("foo", re"", "-") == "-f-o-o-" - doAssert replace("ooo", re"o", "-") == "---" - - block: # bug #14468 - accum = @[] - for word in split("this is an example", re"\b"): - accum.add(word) - doAssert(accum == @["this", " ", "is", " ", "an", " ", "example"]) - testAll() diff --git a/tests/stdlib/tregex.nim b/tests/stdlib/tregex.nim index cf80f8122..21f4e6743 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 deleted file mode 100644 index 4956f8196..000000000 --- a/tests/stdlib/tregistry.nim +++ /dev/null @@ -1,16 +0,0 @@ -discard """ - disabled: "unix" - matrix: "--gc:refc; --gc:arc" -""" - -when defined(windows): - import std/registry - import std/assertions - - block: # bug #14010 - let path = "Environment" - let key = "D20210328T202842_key" - let val = "D20210328T202842_val" - let handle = HKEY_CURRENT_USER - setUnicodeValue("Environment", key, val, handle) - doAssert getUnicodeValue(path, key, handle) == val diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index 0511d1004..1d89d37b2 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -5,12 +5,7 @@ discard """ # if excessive, could remove 'cpp' from targets -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 +from strutils import endsWith, contains template main() = doAssert repr({3,5}) == "{3, 5}" @@ -65,262 +60,10 @@ template main() = doAssert repr(arr) == "[1, 2, 3]" block: # bug #7878 - proc reprOpenarray(variable: var openArray[int]): string = repr(variable) + proc reprOpenarray(variable: var openarray[int]): string = repr(variable) when defined(js): discard # BUG: doesn't work else: doAssert reprOpenarray(arr) == "[1, 2, 3]" - block: # bug #17292 repr with `do` - template foo(a, b, c, d) = discard - block: - let a = deb: - foo(1, 2, 3, 4) - doAssert a == "foo(1, 2, 3, 4)" - block: - let a = deb: - foo(1, 2, 3): 4 - doAssert a == """ -foo(1, 2, 3): - 4""" - - block: - let a = deb: - foo(1, 2): 3 - do: 4 - doAssert a == """ -foo(1, 2): - 3 -do: - 4""" - - block: - let a = deb: - foo(1): 3 - do: 3 - do: 4 - doAssert a == """ -foo(1): - 3 -do: - 3 -do: - 4""" - - block: - let a = deb: - foo(1): - 3 - do: - discard - 3 - do: - discard - 4 - - doAssert a == """ -foo(1): - 3 -do: - discard - 3 -do: - discard - 4""" - - block: - let a = deb: - foo: 1 - do: 2 - do: 3 - do: 4 - doAssert a == """ -foo: - 1 -do: - 2 -do: - 3 -do: - 4""" - - block: # bug #17292 repr with `(discard)` (`discard` would result in illegal code) - let a = deb: - let f {.inject.} = () => (discard) - doAssert a == """ -let f {.inject.} = () => - (discard )""" - - let a2 = deb: - block: - discard - discard - - block: - when true: discard - - # let a = b => discard # illegal - discard b => (discard) # legal - - block: - return - doAssert a2 == """ -block: - discard -discard -block: - when true: - discard -discard b => - (discard ) -block: - return""" - - block: # bug #17292 (bug 4) - let a = deb: - proc `=destroy`() = discard - proc `'foo`(): int = discard - proc `foo bar baz`(): int = discard - let a2 = """ -proc `=destroy`() = - discard - -proc `'foo`(): int = - discard - -proc `foo bar baz`(): int = - discard""" - doAssert a2 == a - - block: # setters: `foo=` - let a = deb: - proc `foo=`() = discard - doAssert a == """ -proc `foo=`() = - discard""" - - block: # bug #14850 - block: - let a = deb: - template bar(): untyped = - foo1: - discard - 4 - foo2(1): - discard - 4 - foo3(1): - discard - 4 - do: 1 - do: 2 - x.add foo4 - x.add: foo5: 3 - x.add foo6 do: 4 - a.add(foo7 do: - echo "baz" - 4) - - doAssert a == """ -template bar(): untyped = - foo1: - discard - 4 - foo2(1): - discard - 4 - foo3(1): - discard - 4 - do: - 1 - do: - 2 - x.add foo4 - x.add: - foo5: - 3 - x.add foo6 do: - 4 - a.add(foo7 do: - echo "baz" - 4)""" - - block: # one liner doc comments - let a1 = deb: - func fn1(): int = 1 ## comment - func fn2(): int = 1 - ## comment - let a2 = debTyped: - func fn1(): int = 1 ## comment - func fn2(): int = 1 - ## comment - doAssert a1 == """ -func fn1(): int = - ## comment - 1 - -func fn2(): int = - ## comment - 1""" - doAssert a2 == """ -func fn1(): int = - ## comment - result = 1 - -func fn2(): int = - ## comment - result = 1""" - - block: # block calls - let a = deb: - foo(a, b, (c, d)): - e - f - do: g - of h: i - elif j: k - except m: n - do () -> u: v - finally: o - - a + b: - c - d - do: - e - f - else: g - - *a: b - do: c - - doAssert a == """foo(a, b, (c, d)): - e - f -do: - g -of h: - i -elif j: - k -except m: - n -do -> u: - v -finally: - o -a + b: - c - d -do: - e - f -else: - g -*a: - b -do: - c""" - static: main() main() diff --git a/tests/stdlib/tropes.nim b/tests/stdlib/tropes.nim index 6d41e9e44..0c95d5c5f 100644 --- a/tests/stdlib/tropes.nim +++ b/tests/stdlib/tropes.nim @@ -1,103 +1,73 @@ -discard """ - targets: "c js" -""" +import ropes -import std/ropes -import std/assertions -template main() = - block: - let r: Rope = nil - doAssert r[0] == '\0' - doAssert $r == "" +block: + let r: Rope = nil + doAssert r[0] == '\0' - block: - var - r1 = rope("Hello, ") - r2 = rope("Nim-Lang") +block: + var + r1 = rope("Hello") + r2 = rope("Nim-Lang") - let r = r1 & r2 - let s = $r - doAssert s == "Hello, Nim-Lang" - for i in 0 ..< r.len: - doAssert r[i] == s[i] + let r = r1 & r2 + let s = $r + for i in 0 ..< r.len: + doAssert r[i] == s[i] - doAssert r[66] == '\0' + doAssert r[66] == '\0' - block: - let r = rope("Hello, Nim-Lang") +block: + let r = rope("Hello, Nim-Lang") - let s = $r - doAssert s == "Hello, Nim-Lang" - for i in 0 ..< r.len: - doAssert r[i] == s[i] + let s = $r + for i in 0 ..< r.len: + doAssert r[i] == s[i] - doAssert r[66] == '\0' + doAssert r[66] == '\0' - block: - var r: Rope - r.add rope("Nim ") - r.add rope("is ") - r.add rope("a ") - r.add rope("great ") - r.add rope("language") +block: + var r: Rope + r.add rope("Nim ") + r.add rope("is ") + r.add rope("a ") + r.add rope("great ") + r.add rope("language") - let s = $r - doAssert s == "Nim is a great language" - for i in 0 ..< r.len: - doAssert r[i] == s[i] + let s = $r + for i in 0 ..< r.len: + doAssert r[i] == s[i] - doAssert r[66] == '\0' + doAssert r[66] == '\0' - block: - var r: Rope - r.add rope("My Conquest") - r.add rope(" is ") - r.add rope("the Sea of Stars") +block: + var r: Rope + r.add rope("My Conquest") + r.add rope(" is ") + r.add rope("the Sea of Stars") - let s = $r - doAssert s == "My Conquest is the Sea of Stars" - for i in 0 ..< r.len: - doAssert r[i] == s[i] + let s = $r + for i in 0 ..< r.len: + doAssert r[i] == s[i] - doAssert r[66] == '\0' + doAssert r[66] == '\0' - block: - var r: Rope - r.add rope("My Conquest") - r.add rope(" is ") - r.add rope("the Sea of Stars") +block: + var r: Rope + r.add rope("My Conquest") + r.add rope(" is ") + r.add rope("the Sea of Stars") - doAssert $r == "My Conquest is the Sea of Stars" + var i: int + for item in r: + doAssert r[i] == item + inc i - var i: int - for item in r: - doAssert r[i] == item - inc i + doAssert r[66] == '\0' - doAssert r[66] == '\0' +block: + let r1 = "$1 $2 $3" % [rope("Nim"), rope("is"), rope("a great language")] + doAssert $r1 == "Nim is a great language" - block: - let r1 = "$1 $2 $3" % [rope("Nim"), rope("is"), rope("a great language")] - doAssert $r1 == "Nim is a great language" - - let r2 = "$# $# $#" % [rope("Nim"), rope("is"), rope("a great language")] - doAssert $r2 == "Nim is a great language" - - block: # `[]` - let r1 = rope("Hello, Nim!") - - doAssert r1[-2] == '\0' - doAssert r1[0] == 'H' - doAssert r1[7] == 'N' - doAssert r1[22] == '\0' - - let r2 = rope("Hello") & rope(", Nim!") - - doAssert r2[-2] == '\0' - doAssert r2[0] == 'H' - doAssert r2[7] == 'N' - doAssert r2[22] == '\0' - -static: main() -main() + let r2 = "$# $# $#" % [rope("Nim"), rope("is"), rope("a great language")] + doAssert $r2 == "Nim is a great language" diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index da787d274..0645e4150 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -1,1412 +1,22 @@ discard """ output: ''' -[Suite] RST parsing - -[Suite] RST tables - -[Suite] RST indentation - -[Suite] Markdown indentation - -[Suite] Warnings - [Suite] RST include directive - -[Suite] RST escaping - -[Suite] RST inline markup ''' """ # tests for rst module -import ../../lib/packages/docutils/[rstgen, rst, rstast] -import unittest, strutils -import std/private/miscdollars +import ../../lib/packages/docutils/rstgen +import ../../lib/packages/docutils/rst +import unittest import os -import std/[assertions, syncio] - -const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} -const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled} - -proc toAst(input: string, - rstOptions: RstParseOptions = preferMarkdown, - error: ref string = nil, - warnings: ref seq[string] = nil): string = - ## If `error` is nil then no errors should be generated. - ## The same goes for `warnings`. - proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind, - arg: string) = - let mc = msgkind.whichMsgClass - let a = $msgkind % arg - var message: string - toLocation(message, filename, line, col + ColRstOffset) - message.add " $1: $2" % [$mc, a] - if mc == mcError: - if error == nil: - raise newException(EParseError, "[unexpected error] " & message) - error[] = message - # we check only first error because subsequent ones may be meaningless - raise newException(EParseError, "") - else: - doAssert warnings != nil, "unexpected RST warning '" & message & "'" - warnings[].add message - try: - const filen = "input" - - proc myFindFile(filename: string): string = - # we don't find any files in online mode: - result = "" - - var (rst, _, _) = rstParse(input, filen, line=LineRstInit, column=ColRstInit, - rstOptions, myFindFile, nil, testMsgHandler) - result = treeRepr(rst) - except EParseError as e: - if e.msg != "": - result = e.msg - -suite "RST parsing": - test "References are whitespace-neutral and case-insensitive": - # refname is 'lexical-analysis', the same for all the 3 variants: - check(dedent""" - Lexical Analysis - ================ - - Ref. `Lexical Analysis`_ or `Lexical analysis`_ or `lexical analysis`_. - """.toAst == - dedent""" - rnInner - rnHeadline level=1 anchor='lexical-analysis' - rnLeaf 'Lexical' - rnLeaf ' ' - rnLeaf 'Analysis' - rnParagraph - rnLeaf 'Ref' - rnLeaf '.' - rnLeaf ' ' - rnInternalRef - rnInner - rnLeaf 'Lexical' - rnLeaf ' ' - rnLeaf 'Analysis' - rnLeaf 'lexical-analysis' - rnLeaf ' ' - rnLeaf 'or' - rnLeaf ' ' - rnInternalRef - rnInner - rnLeaf 'Lexical' - rnLeaf ' ' - rnLeaf 'analysis' - rnLeaf 'lexical-analysis' - rnLeaf ' ' - rnLeaf 'or' - rnLeaf ' ' - rnInternalRef - rnInner - rnLeaf 'lexical' - rnLeaf ' ' - rnLeaf 'analysis' - rnLeaf 'lexical-analysis' - rnLeaf '.' - rnLeaf ' ' - """) - - test "RST quoted literal blocks": - let expected = - dedent""" - rnInner - rnLeaf 'Paragraph' - rnLeaf ':' - rnLiteralBlock - rnLeaf '>x' - """ - - check(dedent""" - Paragraph:: - - >x""".toAst(rstOptions = preferRst) == expected) - - check(dedent""" - Paragraph:: - - >x""".toAst(rstOptions = preferRst) == expected) - - test "RST quoted literal blocks, :: at a separate line": - let expected = - dedent""" - rnInner - rnInner - rnLeaf 'Paragraph' - rnLiteralBlock - rnLeaf '>x - >>y' - """ - - check(dedent""" - Paragraph - - :: - - >x - >>y""".toAst(rstOptions = preferRst) == expected) - - check(dedent""" - Paragraph - - :: - - >x - >>y""".toAst(rstOptions = preferRst) == expected) - - test "Markdown quoted blocks": - check(dedent""" - Paragraph. - >x""".toAst == - dedent""" - rnInner - rnLeaf 'Paragraph' - rnLeaf '.' - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnLeaf 'x' - """) - - # bug #17987 - check(dedent""" - foo https://github.com/nim-lang/Nim/issues/8258 - - > bar""".toAst == - dedent""" - rnInner - rnInner - rnLeaf 'foo' - rnLeaf ' ' - rnStandaloneHyperlink - rnLeaf 'https://github.com/nim-lang/Nim/issues/8258' - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnLeaf 'bar' - """) - - let expected = dedent""" - rnInner - rnLeaf 'Paragraph' - rnLeaf '.' - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnInner - rnLeaf 'x1' - rnLeaf ' ' - rnLeaf 'x2' - rnMarkdownBlockQuoteItem quotationDepth=2 - rnInner - rnLeaf 'y1' - rnLeaf ' ' - rnLeaf 'y2' - rnMarkdownBlockQuoteItem quotationDepth=1 - rnLeaf 'z' - """ - - check(dedent""" - Paragraph. - >x1 x2 - >>y1 y2 - >z""".toAst == expected) - - check(dedent""" - Paragraph. - > x1 x2 - >> y1 y2 - > z""".toAst == expected) - - check(dedent""" - >x - >y - >z""".toAst == - dedent""" - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnInner - rnLeaf 'x' - rnLeaf ' ' - rnLeaf 'y' - rnLeaf ' ' - rnLeaf 'z' - """) - - check(dedent""" - > z - > > >y - """.toAst == - dedent""" - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnLeaf 'z' - rnMarkdownBlockQuoteItem quotationDepth=3 - rnLeaf 'y' - """) - - test "Markdown quoted blocks: lazy": - let expected = dedent""" - rnInner - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=2 - rnInner - rnLeaf 'x' - rnLeaf ' ' - rnLeaf 'continuation1' - rnLeaf ' ' - rnLeaf 'continuation2' - rnParagraph - rnLeaf 'newParagraph' - """ - check(dedent""" - >>x - continuation1 - continuation2 - - newParagraph""".toAst == expected) - - check(dedent""" - >> x - continuation1 - continuation2 - - newParagraph""".toAst == expected) - - # however mixing more than 1 non-lazy line and lazy one(s) splits quote - # in our parser, which appeared the easiest way to handle such cases: - var warnings = new seq[string] - check(dedent""" - >> x - >> continuation1 - continuation2 - - newParagraph""".toAst(warnings=warnings) == - dedent""" - rnInner - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=2 - rnLeaf 'x' - rnMarkdownBlockQuoteItem quotationDepth=2 - rnInner - rnLeaf 'continuation1' - rnLeaf ' ' - rnLeaf 'continuation2' - rnParagraph - rnLeaf 'newParagraph' - """) - check(warnings[] == @[ - "input(2, 1) Warning: RST style: two or more quoted lines " & - "are followed by unquoted line 3"]) - - test "Markdown quoted blocks: not lazy": - # here is where we deviate from CommonMark specification: 'bar' below is - # not considered as continuation of 2-level '>> foo' quote. - check(dedent""" - >>> foo - > bar - >> baz - """.toAst() == - dedent""" - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=3 - rnLeaf 'foo' - rnMarkdownBlockQuoteItem quotationDepth=1 - rnLeaf 'bar' - rnMarkdownBlockQuoteItem quotationDepth=2 - rnLeaf 'baz' - """) - - - test "Markdown quoted blocks: inline markup works": - check(dedent""" - > hi **bold** text - """.toAst == dedent""" - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnInner - rnLeaf 'hi' - rnLeaf ' ' - rnStrongEmphasis - rnLeaf 'bold' - rnLeaf ' ' - rnLeaf 'text' - """) - - test "Markdown quoted blocks: blank line separator": - let expected = dedent""" - rnInner - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnInner - rnLeaf 'x' - rnLeaf ' ' - rnLeaf 'y' - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnInner - rnLeaf 'z' - rnLeaf ' ' - rnLeaf 't' - """ - check(dedent""" - >x - >y - - > z - > t""".toAst == expected) - - check(dedent""" - >x - y - - > z - t""".toAst == expected) - - test "Markdown quoted blocks: nested body blocks/elements work #1": - let expected = dedent""" - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnBulletList - rnBulletItem - rnInner - rnLeaf 'x' - rnBulletItem - rnInner - rnLeaf 'y' - """ - - check(dedent""" - > - x - - y - """.toAst == expected) - - # TODO: if bug #17340 point 28 is resolved then this may work: - # check(dedent""" - # > - x - # - y - # """.toAst == expected) - - check(dedent""" - > - x - > - y - """.toAst == expected) - - check(dedent""" - > - > - x - > - > - y - > - """.toAst == expected) - - test "Markdown quoted blocks: nested body blocks/elements work #2": - let expected = dedent""" - rnAdmonition adType=note - [nil] - [nil] - rnDefList - rnDefItem - rnDefName - rnLeaf 'deflist' - rnLeaf ':' - rnDefBody - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=2 - rnInner - rnLeaf 'quote' - rnLeaf ' ' - rnLeaf 'continuation' - """ - - check(dedent""" - .. Note:: deflist: - >> quote - continuation - """.toAst(rstOptions = preferRst) == expected) - - check(dedent""" - .. Note:: - deflist: - >> quote - continuation - """.toAst(rstOptions = preferRst) == expected) - - check(dedent""" - .. Note:: - deflist: - >> quote - >> continuation - """.toAst(rstOptions = preferRst) == expected) - - # spaces are not significant between `>`: - check(dedent""" - .. Note:: - deflist: - > > quote - > > continuation - """.toAst(rstOptions = preferRst) == expected) - - test "Markdown quoted blocks: de-indent handled well": - check(dedent""" - > - > - x - > - y - > - > Paragraph. - """.toAst(rstOptions = preferRst) == dedent""" - rnMarkdownBlockQuote - rnMarkdownBlockQuoteItem quotationDepth=1 - rnInner - rnBlockQuote - rnBulletList - rnBulletItem - rnInner - rnLeaf 'x' - rnBulletItem - rnInner - rnLeaf 'y' - rnParagraph - rnLeaf 'Paragraph' - rnLeaf '.' - """) - - 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""" - ```` - 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""" - 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 - - -b desc2 - """.toAst == - dedent""" - rnOptionList - rnOptionListItem order=1 - rnOptionGroup - rnLeaf '-' - rnLeaf 'a' - rnDescription - rnLeaf 'desc1' - rnOptionListItem order=2 - rnOptionGroup - rnLeaf '-' - rnLeaf 'b' - rnDescription - rnLeaf 'desc2' - """) - - test "definition list does not gobble up the following blocks": - check(dedent""" - defName - defBody - - -b desc2 - """.toAst(rstOptions = preferRst) == - dedent""" - rnInner - rnDefList - rnDefItem - rnDefName - rnLeaf 'defName' - rnDefBody - rnInner - rnLeaf 'defBody' - rnOptionList - rnOptionListItem order=1 - rnOptionGroup - rnLeaf '-' - rnLeaf 'b' - rnDescription - rnLeaf 'desc2' - """) - - test "RST comment": - check(dedent""" - .. comment1 - comment2 - someParagraph""".toAst == - dedent""" - rnLeaf 'someParagraph' - """) - - check(dedent""" - .. - comment1 - comment2 - someParagraph""".toAst == - dedent""" - rnLeaf 'someParagraph' - """) - - test "check that additional line right after .. ends comment": - check(dedent""" - .. - - notAcomment1 - notAcomment2 - someParagraph""".toAst(rstOptions = preferRst) == - dedent""" - rnInner - rnBlockQuote - rnInner - rnLeaf 'notAcomment1' - rnLeaf ' ' - rnLeaf 'notAcomment2' - rnParagraph - 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""" - .. - comment1 - - - comment2 - someParagraph""".toAst == - dedent""" - rnLeaf 'someParagraph' - """) - - test "using .. as separator b/w directives and block quotes": - check(dedent""" - .. note:: someNote - - .. - - someBlockQuote""".toAst(rstOptions = preferRst) == - dedent""" - rnInner - rnAdmonition adType=note - [nil] - [nil] - rnLeaf 'someNote' - rnBlockQuote - rnInner - rnLeaf 'someBlockQuote' - """) - - test "no redundant blank lines in literal blocks": - check(dedent""" - Check:: - - - code - - """.toAst(rstOptions = preferRst) == - dedent""" - rnInner - rnLeaf 'Check' - rnLeaf ':' - rnLiteralBlock - rnLeaf 'code' - """) - - test "Markdown indented code blocks": - check(dedent""" - See - - some code""".toAst == - dedent""" - rnInner - rnInner - rnLeaf 'See' - rnLiteralBlock - rnLeaf 'some code' - """) - - # not a code block -- no blank line before: - check(dedent""" - See - some code""".toAst == - dedent""" - rnInner - rnLeaf 'See' - rnLeaf ' ' - rnLeaf 'some' - rnLeaf ' ' - 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 """ - * - bullet1 - - bullet2 - * - bullet3 - - bullet4 - """ - let output = input.toAst - check(output == dedent""" - rnBulletList - rnBulletItem - rnBulletList - rnBulletItem - rnInner - rnLeaf 'bullet1' - rnBulletItem - rnInner - rnLeaf 'bullet2' - rnBulletItem - rnBulletList - rnBulletItem - rnInner - rnLeaf 'bullet3' - rnBulletItem - rnInner - rnLeaf 'bullet4' - """) - - test "nested markup blocks": - let input = dedent""" - #) .. Hint:: .. Error:: none - #) .. Warning:: term0 - Definition0 - #) some - paragraph1 - #) term1 - Definition1 - term2 - Definition2 - """ - check(input.toAst(rstOptions = preferRst) == dedent""" - rnEnumList labelFmt=1) - rnEnumItem - rnAdmonition adType=hint - [nil] - [nil] - rnAdmonition adType=error - [nil] - [nil] - rnLeaf 'none' - rnEnumItem - rnAdmonition adType=warning - [nil] - [nil] - rnDefList - rnDefItem - rnDefName - rnLeaf 'term0' - rnDefBody - rnInner - rnLeaf 'Definition0' - rnEnumItem - rnInner - rnLeaf 'some' - rnLeaf ' ' - rnLeaf 'paragraph1' - rnEnumItem - rnDefList - rnDefItem - rnDefName - rnLeaf 'term1' - rnDefBody - rnInner - rnLeaf 'Definition1' - rnDefItem - rnDefName - rnLeaf 'term2' - rnDefBody - rnInner - rnLeaf 'Definition2' - """) - - test "code-block parsing": - let input1 = dedent""" - .. code-block:: nim - :test: "nim c $1" - - template additive(typ: typedesc) = - discard - """ - let input2 = dedent""" - .. code-block:: nim - :test: "nim c $1" - - template additive(typ: typedesc) = - discard - """ - let input3 = dedent""" - .. code-block:: nim - :test: "nim c $1" - template additive(typ: typedesc) = - discard - """ - let inputWrong = dedent""" - .. code-block:: nim - :test: "nim c $1" - - template additive(typ: typedesc) = - discard - """ - let ast = dedent""" - rnCodeBlock - rnDirArg - rnLeaf 'nim' - rnFieldList - rnField - rnFieldName - rnLeaf 'test' - rnFieldBody - rnInner - rnLeaf '"' - rnLeaf 'nim' - rnLeaf ' ' - rnLeaf 'c' - rnLeaf ' ' - rnLeaf '$' - rnLeaf '1' - rnLeaf '"' - rnField - rnFieldName - rnLeaf 'default-language' - rnFieldBody - rnLeaf 'Nim' - rnLiteralBlock - rnLeaf 'template additive(typ: typedesc) = - discard' - """ - check input1.toAst == ast - check input2.toAst == ast - check input3.toAst == ast - # "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 "Markdown indentation": - test "Markdown paragraph indentation": - # Additional spaces (<=3) of indentation does not break the paragraph. - # TODO: in 2nd case de-indentation causes paragraph to break, this is - # reasonable but does not seem to conform the Markdown spec. - check(dedent""" - Start1 - stop1 - - Start2 - stop2 - """.toAst == - dedent""" - rnInner - rnParagraph - rnLeaf 'Start1' - rnLeaf ' ' - rnLeaf 'stop1' - rnParagraph - rnLeaf 'Start2' - rnParagraph - rnLeaf 'stop2' - rnLeaf ' ' - """) - -suite "Warnings": - test "warnings for broken footnotes/links/substitutions": - let input = dedent""" - firstParagraph - - footnoteRef [som]_ - - link `a broken Link`_ - - substitution |undefined subst| - - link short.link_ - - lastParagraph - """ - var warnings = new seq[string] - 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'", - "input(7, 15) Warning: unknown substitution 'undefined subst'", - "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""" - - - firstParagraph - - here brokenLink_""") - let input = ".. include:: other.rst" - var warnings = new seq[string] - let output = input.toAst(warnings=warnings) - check warnings[] == @["other.rst(5, 6) Warning: broken link 'brokenLink'"] - check(output == dedent""" - rnInner - rnParagraph - rnLeaf 'firstParagraph' - rnParagraph - rnLeaf 'here' - rnLeaf ' ' - rnRstRef - rnLeaf 'brokenLink' - """) - removeFile("other.rst") - - test "warnings for ambiguous links (references + anchors)": - # Reference like `x`_ generates a link alias x that may clash with others - let input = dedent""" - Manual reference: `foo <#foo,string,string>`_ - - .. _foo: - - Paragraph. - - Ref foo_ - """ - var warnings = new seq[string] - let output = input.toAst(warnings=warnings) - check(warnings[] == @[ - dedent """ - input(7, 5) Warning: ambiguous doc link `foo` - clash: - (3, 8): (manual directive anchor) - (1, 45): (implicitly-generated hyperlink alias)""" - ]) - # reference should be resolved to the manually set anchor: - check(output == - dedent""" - rnInner - rnParagraph - rnLeaf 'Manual' - rnLeaf ' ' - rnLeaf 'reference' - rnLeaf ':' - rnLeaf ' ' - rnHyperlink - rnInner - rnLeaf 'foo' - rnInner - rnLeaf '#foo,string,string' - rnParagraph anchor='foo' - rnLeaf 'Paragraph' - rnLeaf '.' - rnParagraph - rnLeaf 'Ref' - rnLeaf ' ' - rnInternalRef - rnInner - rnLeaf 'foo' - rnLeaf 'foo' - rnLeaf ' ' - """) suite "RST include directive": test "Include whole": "other.rst".writeFile("**test1**") let input = ".. include:: other.rst" - doAssert "test1" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + doAssert "test1" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") test "Include starting from": @@ -1420,7 +30,7 @@ OtherStart .. include:: other.rst :start-after: OtherStart """ - check "Visible" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") test "Include everything before": @@ -1434,7 +44,7 @@ And this should **NOT** be visible in `docs.html` .. include:: other.rst :end-before: OtherEnd """ - doAssert "Visible" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") @@ -1452,7 +62,7 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - check "Visible" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") @@ -1472,360 +82,5 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - doAssert "Visible" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") - -suite "RST escaping": - test "backspaces": - check("""\ this""".toAst == dedent""" - rnLeaf 'this' - """) - - check("""\\ this""".toAst == dedent""" - rnInner - rnLeaf '\' - rnLeaf ' ' - rnLeaf 'this' - """) - - check("""\\\ this""".toAst == dedent""" - rnInner - rnLeaf '\' - rnLeaf 'this' - """) - - check("""\\\\ this""".toAst == dedent""" - rnInner - rnLeaf '\' - rnLeaf '\' - rnLeaf ' ' - rnLeaf 'this' - """) - -suite "RST inline markup": - test "* and ** surrounded by spaces are not inline markup": - check("a * b * c ** d ** e".toAst == dedent""" - rnInner - rnLeaf 'a' - rnLeaf ' ' - rnLeaf '*' - rnLeaf ' ' - rnLeaf 'b' - rnLeaf ' ' - rnLeaf '*' - rnLeaf ' ' - rnLeaf 'c' - rnLeaf ' ' - rnLeaf '**' - rnLeaf ' ' - rnLeaf 'd' - rnLeaf ' ' - rnLeaf '**' - rnLeaf ' ' - rnLeaf 'e' - """) - - test "end-string has repeating symbols": - check("*emphasis content****".toAst == dedent""" - rnEmphasis - rnLeaf 'emphasis' - rnLeaf ' ' - rnLeaf 'content' - rnLeaf '***' - """) - - check("""*emphasis content\****""".toAst == dedent""" - rnEmphasis - rnLeaf 'emphasis' - rnLeaf ' ' - rnLeaf 'content' - rnLeaf '*' - rnLeaf '**' - """) # exact configuration of leafs with * is not really essential, - # only total number of * is essential - - check("**strong content****".toAst == dedent""" - rnStrongEmphasis - rnLeaf 'strong' - rnLeaf ' ' - rnLeaf 'content' - rnLeaf '**' - """) - - check("""**strong content*\****""".toAst == dedent""" - rnStrongEmphasis - rnLeaf 'strong' - rnLeaf ' ' - rnLeaf 'content' - rnLeaf '*' - rnLeaf '*' - rnLeaf '*' - """) - - check("``lit content`````".toAst == dedent""" - rnInlineLiteral - rnLeaf 'lit' - rnLeaf ' ' - rnLeaf 'content' - rnLeaf '```' - """) - - test "interpreted text parsing: code fragments": - check(dedent""" - .. default-role:: option - - `--gc:refc`""".toAst == - dedent""" - rnInner - rnDefaultRole - rnDirArg - rnLeaf 'option' - [nil] - [nil] - rnParagraph - rnCodeFragment - rnInner - rnLeaf '--' - rnLeaf 'gc' - rnLeaf ':' - rnLeaf 'refc' - rnLeaf 'option' - """) - - test """interpreted text can be ended with \` """: - let output = (".. default-role:: literal\n" & """`\``""").toAst - check(output.endsWith """ - rnParagraph - rnInlineLiteral - rnLeaf '`'""" & "\n") - - let output2 = """`\``""".toAst - check(output2 == dedent""" - rnInlineCode - rnDirArg - rnLeaf 'nim' - [nil] - rnLiteralBlock - rnLeaf '`' - """) - - let output3 = """`proc \`+\``""".toAst - check(output3 == dedent""" - rnInlineCode - rnDirArg - rnLeaf 'nim' - [nil] - rnLiteralBlock - rnLeaf 'proc `+`' - """) - - check("""`\\`""".toAst == - dedent""" - rnInlineCode - rnDirArg - rnLeaf 'nim' - [nil] - rnLiteralBlock - rnLeaf '\\' - """) - - test "Markdown-style code/backtick": - # no whitespace is required before ` - check("`try`...`except`".toAst == - dedent""" - rnInner - rnInlineCode - rnDirArg - rnLeaf 'nim' - [nil] - rnLiteralBlock - rnLeaf 'try' - rnLeaf '...' - rnInlineCode - rnDirArg - rnLeaf 'nim' - [nil] - rnLiteralBlock - rnLeaf 'except' - """) - - - test """inline literals can contain \ anywhere""": - check("""``\``""".toAst == dedent""" - rnInlineLiteral - rnLeaf '\' - """) - - check("""``\\``""".toAst == dedent""" - rnInlineLiteral - rnLeaf '\' - rnLeaf '\' - """) - - check("""``\```""".toAst == dedent""" - rnInlineLiteral - rnLeaf '\' - rnLeaf '`' - """) - - check("""``\\```""".toAst == dedent""" - rnInlineLiteral - rnLeaf '\' - rnLeaf '\' - rnLeaf '`' - """) - - check("""``\````""".toAst == dedent""" - rnInlineLiteral - rnLeaf '\' - rnLeaf '`' - rnLeaf '`' - """) - - test "references with _ at the end": - check(dedent""" - .. _lnk: https - - lnk_""".toAst == - dedent""" - rnHyperlink - rnInner - rnLeaf 'lnk' - rnInner - rnLeaf 'https' - """) - - test "not a hyper link": - check(dedent""" - .. _lnk: https - - lnk___""".toAst == - dedent""" - rnInner - rnLeaf 'lnk' - rnLeaf '___' - """) - - test "no punctuation in the end of a standalone URI is allowed": - check(dedent""" - [see (http://no.org)], end""".toAst(rstOptions = preferRst) == - dedent""" - rnInner - rnLeaf '[' - rnLeaf 'see' - rnLeaf ' ' - rnLeaf '(' - rnStandaloneHyperlink - rnLeaf 'http://no.org' - rnLeaf ')' - rnLeaf ']' - rnLeaf ',' - rnLeaf ' ' - rnLeaf 'end' - """) - - # but `/` at the end is OK - check( - dedent""" - See http://no.org/ end""".toAst == - dedent""" - rnInner - rnLeaf 'See' - rnLeaf ' ' - rnStandaloneHyperlink - rnLeaf 'http://no.org/' - rnLeaf ' ' - rnLeaf 'end' - """) - - # a more complex URL with some made-up ending '&='. - # Github Markdown would include final &= and - # so would rst2html.py in contradiction with RST spec. - check( - dedent""" - See https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO&= end""".toAst == - dedent""" - rnInner - rnLeaf 'See' - rnLeaf ' ' - rnStandaloneHyperlink - rnLeaf 'https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO' - rnLeaf '&' - rnLeaf '=' - rnLeaf ' ' - 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""" - https://en.wikipedia.org/wiki/APL_((programming_language)))""".toAst == - dedent""" - rnInner - rnStandaloneHyperlink - rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))' - rnLeaf ')' - """) - - # the same for Markdown-style link: - check(dedent""" - [foo [bar]](https://en.wikipedia.org/wiki/APL_((programming_language))))""".toAst == - dedent""" - rnInner - rnHyperlink - rnLeaf 'foo [bar]' - rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))' - rnLeaf ')' - """) - - # unbalanced (here behavior is more RST-like actually): - check(dedent""" - https://en.wikipedia.org/wiki/APL_(programming_language(""".toAst == - dedent""" - rnInner - rnStandaloneHyperlink - rnLeaf 'https://en.wikipedia.org/wiki/APL_(programming_language' - rnLeaf '(' - """) - - # unbalanced [, but still acceptable: - check(dedent""" - [my {link example](http://example.com/bracket_(symbol_[))""".toAst == - dedent""" - rnHyperlink - rnLeaf 'my {link example' - rnLeaf 'http://example.com/bracket_(symbol_[)' - """) - - test "not a Markdown link": - # bug #17340 (27) `f` will be considered as a protocol and blocked as unsafe - var warnings = new seq[string] - check("[T](f: var Foo)".toAst(warnings = warnings) == - dedent""" - rnInner - rnLeaf '[' - rnLeaf 'T' - rnLeaf ']' - rnLeaf '(' - rnLeaf 'f' - rnLeaf ':' - rnLeaf ' ' - rnLeaf 'var' - rnLeaf ' ' - rnLeaf 'Foo' - rnLeaf ')' - """) - check(warnings[] == @["input(1, 5) Warning: broken link 'f'"]) diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 8c07b35f0..7acb23c5b 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -7,52 +7,6 @@ outputsub: "" 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 = preferMarkdown, - error: ref string = nil, - warnings: ref seq[string] = nil): string = - ## If `error` is nil then no errors should be generated. - ## The same goes for `warnings`. - proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind, - arg: string) = - let mc = msgkind.whichMsgClass - let a = $msgkind % arg - var message: string - toLocation(message, filename, line, col + ColRstOffset) - message.add " $1: $2" % [$mc, a] - if mc == mcError: - if error == nil: - raise newException(EParseError, "[unexpected error] " & message) - error[] = message - # we check only first error because subsequent ones may be meaningless - raise newException(EParseError, "") - else: - doAssert warnings != nil, "unexpected RST warning '" & message & "'" - warnings[].add message - try: - result = rstToHtml(input, rstOptions, defaultConfig(), - msgHandler=testMsgHandler) - except EParseError as e: - if e.msg != "": - result = e.msg - -# inline code tags (for parsing originated from highlite.nim) -proc id(str: string): string = """""" & str & "" -proc op(str: string): string = """""" & str & "" -proc pu(str: string): string = """""" & str & "" -proc optionListLabel(opt: string): string = - """
      """ & - opt & - "
      " - suite "YAML syntax highlighting": test "Basics": @@ -67,7 +21,7 @@ suite "YAML syntax highlighting": ? key : value ...""" - let output = input.toHtml({}) + let output = rstTohtml(input, {}, defaultConfig()) doAssert output == """
      %YAML 1.2
       ---
       a string: string
      @@ -93,7 +47,7 @@ suite "YAML syntax highlighting":
           another literal block scalar:
             |+ # comment after header
            allowed, since more indented than parent"""
      -    let output = input.toHtml({})
      +    let output = rstToHtml(input, {}, defaultConfig())
           doAssert output == """
      a literal block scalar: |
         some text
         # not a comment
      @@ -119,7 +73,7 @@ suite "YAML syntax highlighting":
           % not a directive
           ...
           %TAG ! !foo:"""
      -    let output = input.toHtml({})
      +    let output = rstToHtml(input, {}, defaultConfig())
           doAssert output == """
      %YAML 1.2
       ---
       %not a directive
      @@ -140,7 +94,7 @@ suite "YAML syntax highlighting":
             more numbers: [-783, 11e78],
             not numbers: [ 42e, 0023, +32.37, 8 ball]
           }"""
      -    let output = input.toHtml({})
      +    let output = rstToHtml(input, {}, defaultConfig())
           doAssert output == """
      {
         "quoted string": 42,
         'single quoted string': false,
      @@ -149,25 +103,6 @@ suite "YAML syntax highlighting":
         not numbers: [ 42e, 0023, +32.37, 8 ball]
       }
      """ - test "Directives: warnings": - let input = dedent""" - .. non-existent-warning: Paragraph. - - .. another.wrong:warning::: Paragraph. - """ - var warnings = new seq[string] - let output = input.toHtml(warnings=warnings) - check output == "" - doAssert warnings[].len == 2 - check "(1, 24) Warning: RST style:" in warnings[0] - check "double colon :: may be missing at end of 'non-existent-warning'" in warnings[0] - check "(3, 25) Warning: RST style:" in warnings[1] - check "RST style: too many colons for a directive (should be ::)" in warnings[1] - - test "not a directive": - let input = "..warning:: I am not a warning." - check input.toHtml == input - test "Anchors, Aliases, Tags": let input = """.. code-block:: yaml --- !!map @@ -176,7 +111,7 @@ suite "YAML syntax highlighting": : !localtag foo alias: *anchor """ - let output = input.toHtml({}) + let output = rstToHtml(input, {}, defaultConfig()) doAssert output == """
      --- !!map
       !!str string: !<tag:yaml.org,2002:int> 42
       ? &anchor !!seq []:
      @@ -196,7 +131,7 @@ suite "YAML syntax highlighting":
           example.com/not/a#comment:
             ?not a map key
           """
      -    let output = input.toHtml({})
      +    let output = rstToHtml(input, {}, defaultConfig())
           doAssert output == """
      ...
        %a string:
         a:string:not:a:map
      @@ -216,12 +151,14 @@ suite "RST/Markdown general":
             "Hello world!"
       
         test "Markdown links":
      -    check("(( [Nim](https://nim-lang.org/) ))".toHtml ==
      -        """(( Nim ))""")
      -    check("(([Nim](https://nim-lang.org/)))".toHtml ==
      -        """((Nim))""")
      -    check("[[Nim](https://nim-lang.org/)]".toHtml ==
      -        """[Nim]""")
      +    let
      +      a = rstToHtml("(( [Nim](https://nim-lang.org/) ))", {roSupportMarkdown}, defaultConfig())
      +      b = rstToHtml("(([Nim](https://nim-lang.org/)))", {roSupportMarkdown}, defaultConfig())
      +      c = rstToHtml("[[Nim](https://nim-lang.org/)]", {roSupportMarkdown}, defaultConfig())
      +
      +    doAssert a == """(( Nim ))"""
      +    doAssert b == """((Nim))"""
      +    doAssert c == """[Nim]"""
       
         test "Markdown tables":
           let input1 = """
      @@ -232,23 +169,18 @@ suite "RST/Markdown general":
       | E1 \| text   |
       |              | F2 without pipe
       not in table"""
      -    let output1 = input1.toHtml
      -    #[
      -    TODO: `\|` inside a table cell should render as `|`
      -        `|` outside a table cell should render as `\|`
      -    consistently with markdown, see https://stackoverflow.com/a/66557930/1426932
      -    ]#
      -    check(output1 == """
      -
      +    let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig())
      +    doAssert output1 == """
      A1 headerA2 | not fooled
      - + -
      A1 headerA2 | not fooled
      C1C2 bold
      D1 """ & id"code" & " " & op"\|" & """D2
      D1 code |D2
      E1 | text
      F2 without pipe

      not in table

      """) +

      not in table

      +""" let input2 = """ | A1 header | A2 | | --- | --- |""" - let output2 = input2.toHtml + let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) doAssert output2 == """
      A1 headerA2
      """ @@ -265,7 +197,7 @@ A2 A3 A4 A5 ==== === """ let output1 = rstToLatex(input1, {}) - doAssert "{LL}" in output1 # 2 columns + doAssert "{|X|X|}" in output1 # 2 columns doAssert count(output1, "\\\\") == 4 # 4 rows for cell in ["H0", "H1", "A0", "A1", "A2", "A3", "A4", "A5"]: doAssert cell in output1 @@ -280,7 +212,7 @@ A0 A1 X Ax Y ==== === = """ let output2 = rstToLatex(input2, {}) - doAssert "{LLL}" in output2 # 3 columns + doAssert "{|X|X|X|}" in output2 # 3 columns doAssert count(output2, "\\\\") == 2 # 2 rows for cell in ["H0", "H1", "H", "A0", "A1", "X", "Ax", "Y"]: doAssert cell in output2 @@ -290,7 +222,7 @@ A0 A1 X let input1 = """ Check that a few punctuation symbols are not parsed as adornments: :word1: word2 .... word3 """ - let output1 = input1.toHtml + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) discard output1 test "RST sections": @@ -298,7 +230,7 @@ Check that a few punctuation symbols are not parsed as adornments: Long chapter name ''''''''''''''''''' """ - let output1 = input1.toHtml + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) doAssert "Long chapter name" in output1 and "Some chapter" in output6 - - # check that overline and underline match - let input7 = dedent """ - ------------ - Some chapter - ----------- - """ - var error7 = new string - let output7 = input7.toHtml(error=error7) - check(error7[] == "input(1, 1) Error: new section expected (underline " & - "\'-----------\' does not match overline \'------------\')") - - let input8 = dedent """ - ----------- - Overflow - ----------- - """ - var error8 = new string - let output8 = input8.toHtml(error=error8) - check(error8[] == "input(1, 1) Error: new section expected (overline " & - "\'-----------\' is too short)") - - # check that hierarchy of title styles works - let input9good = dedent """ - Level1 - ====== - - Level2 - ------ - - Level3 - ~~~~~~ - - L1 - == - - Another2 - -------- - - More3 - ~~~~~ - - """ - let output9good = input9good.toHtml(preferRst) - doAssert "

      Level1

      " in output9good - doAssert "

      Level2

      " in output9good - doAssert "

      Level3

      " in output9good - doAssert "

      L1

      " in output9good - doAssert "

      Another2

      " in output9good - doAssert "

      More3

      " in output9good - - # check that swap causes an exception - let input9Bad = dedent """ - Level1 - ====== - - Level2 - ------ - - Level3 - ~~~~~~ - - L1 - == - - More - ~~~~ - - Another - ------- - - """ - var error9Bad = new string - 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 " & - "lines 12..15: underline -----)") - - test "RST sections overline": - # the same as input9good but with overline headings - # first overline heading has a special meaning: document title - let input = dedent """ - ====== - Title0 - ====== - - +++++++++ - SubTitle0 - +++++++++ - - ------ - Level1 - ------ - - Level2 - ------ - - ~~~~~~ - Level3 - ~~~~~~ - - -- - L1 - -- - - Another2 - -------- - - ~~~~~ - More3 - ~~~~~ - - """ - var rstGenera: RstGenerator - var output: string - let (rst, files, _) = rstParse(input, "", 1, 1, {}) - rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames = files) - rstGenera.renderRstToOut(rst, output) - doAssert rstGenera.meta[metaTitle] == "Title0" - doAssert rstGenera.meta[metaSubtitle] == "SubTitle0" - doAssert "

      Level1

      " in output - doAssert "

      Level2

      " in output - doAssert "

      Level3

      " in output - doAssert "

      L1

      " in output - doAssert "

      Another2

      " in output - doAssert "

      More3

      " in output - - test "RST sections overline 2": - # check that a paragraph prevents interpreting overlines as document titles - let input = dedent """ - Paragraph - - ====== - Title0 - ====== - - +++++++++ - SubTitle0 - +++++++++ - """ - var rstGenera: RstGenerator - var output: string - let (rst, files, _) = rstParse(input, "", 1, 1, {}) - rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) - rstGenera.renderRstToOut(rst, output) - doAssert rstGenera.meta[metaTitle] == "" - doAssert rstGenera.meta[metaSubtitle] == "" - doAssert "

      Title0

      " in output - doAssert "

      SubTitle0

      " in output - - test "RST+Markdown sections": - # check that RST and Markdown headings don't interfere - let input = dedent """ - ====== - Title0 - ====== - - MySection1a - +++++++++++ - - # MySection1b - - MySection1c - +++++++++++ - - ##### MySection5a - - MySection2a - ----------- - """ - var rstGenera: RstGenerator - var output: string - let (rst, files, _) = rstParse(input, "", 1, 1, {roSupportMarkdown}) - rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) - rstGenera.renderRstToOut(rst, output) - doAssert rstGenera.meta[metaTitle] == "Title0" - doAssert rstGenera.meta[metaSubtitle] == "" - doAssert output == - "\n

      MySection1a

      " & # RST - "\n

      MySection1b

      " & # Markdown - "\n

      MySection1c

      " & # RST - "\n
      MySection5a
      " & # Markdown - "\n

      MySection2a

      " # RST - - test "RST inline text": - let input1 = "GC_step" - 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`_? .. _my favorite programming language: https://nim-lang.org""" - let output1 = input1.toHtml + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) doAssert "" in output1 test "Markdown code block": @@ -640,97 +316,28 @@ Test literal block ``` let x = 1 ``` """ - let output1 = input1.toHtml({roSupportMarkdown, roPreferMarkdown}) + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) doAssert """" & - id"foo" & op"." & id"bar" & "") - check("""`foo\`\`bar`""".toHtml == - """""" & - id"foo" & pu"`" & pu"`" & id"bar" & "") - check("""`foo\`bar`""".toHtml == - """""" & - id"foo" & pu"`" & id"bar" & "") - check("""`\`bar`""".toHtml == - """""" & - pu"`" & id"bar" & "") - check("""`a\b\x\\ar`""".toHtml == - """""" & - id"a" & op"""\""" & id"b" & op"""\""" & id"x" & op"""\\""" & id"ar" & - "") - - test "inline literal": - check """``foo.bar``""".toHtml == """foo.bar""" - check """``foo\bar``""".toHtml == """foo\bar""" - check """``f\`o\\o\b`ar``""".toHtml == """f\`o\\o\b`ar""" - - test "default-role": - # nim(default) -> literal -> nim -> code(=literal) - let input = dedent""" - Par1 `value1`. - - .. default-role:: literal - - Par2 `value2`. - - .. default-role:: nim - - Par3 `value3`. - - .. default-role:: code - - Par4 `value4`.""" - let p1 = """Par1 """ & id"value1" & "." - let p2 = """

      Par2 value2.

      """ - let p3 = """

      Par3 """ & id"value3" & ".

      " - let p4 = """

      Par4 value4.

      """ - let expected = p1 & p2 & "\n" & p3 & "\n" & p4 - check( - input.toHtml(NoSandboxOpts) == expected - ) - - test "role directive": - let input = dedent""" - .. role:: y(code) - :language: yaml - - .. role:: brainhelp(code) - :language: brainhelp - """ - var warnings = new seq[string] - let output = input.toHtml( - NoSandboxOpts, - warnings=warnings - ) - check(warnings[].len == 1 and "language 'brainhelp' not supported" in warnings[0]) - test "RST comments": let input1 = """ - Check that comment disappears: .. some comment """ - let output1 = input1.toHtml + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) doAssert output1 == "Check that comment disappears:" - test "RST line blocks + headings": - let input = """ + test "RST line blocks": + let input1 = """ ===== Test1 ===== @@ -741,36 +348,35 @@ Test1 | other line """ + var option: bool var rstGenera: RstGenerator - var output: string - let (rst, files, _) = rstParse(input, "", 1, 1, {}) - rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) - rstGenera.renderRstToOut(rst, output) + var output1: string + rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", {}) + rstGenera.renderRstToOut(rstParse(input1, "", 1, 1, option, {}), output1) doAssert rstGenera.meta[metaTitle] == "Test1" # check that title was not overwritten to '|' - doAssert output == "



      line block
      other line

      " - let output1l = rstToLatex(input, {}) + doAssert output1 == "



      line block
      other line

      " + let output1l = rstToLatex(input1, {}) doAssert "line block\n\n" in output1l doAssert "other line\n\n" in output1l doAssert output1l.count("\\vspace") == 2 + 2 # +2 surrounding paddings - test "RST line blocks": let input2 = dedent""" Paragraph1 - + | Paragraph2""" - let output2 = input2.toHtml - doAssert "Paragraph1


      Paragraph2

      " == output2 + let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) + doAssert "Paragraph1


      Paragraph2

      \n" == output2 let input3 = dedent""" | xxx | yyy | zzz""" - let output3 = input3.toHtml + let output3 = rstToHtml(input3, {roSupportMarkdown}, defaultConfig()) doAssert "xxx
      " in output3 doAssert "yyy
      " in output3 doAssert "zzz
      " in output3 @@ -778,10 +384,10 @@ Test1 # check that '| ' with a few spaces is still parsed as new line let input4 = dedent""" | xxx - | + | | zzz""" - let output4 = input4.toHtml + let output4 = rstToHtml(input4, {roSupportMarkdown}, defaultConfig()) doAssert "xxx

      " in output4 doAssert "zzz
      " in output4 @@ -804,7 +410,7 @@ Test1 5. line5 5 """ - let output1 = input1.toHtml + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) for i in 1..5: doAssert ($i & ". line" & $i) notin output1 doAssert ("
    • line" & $i & " " & $i & "
    • ") in output1 @@ -826,7 +432,7 @@ Test1 8. line8 """ - let output2 = input2.toHtml + let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) for i in [3, 4, 5, 7, 8]: doAssert ($i & ". line" & $i) notin output2 doAssert ("
    • line" & $i & "
    • ") in output2 @@ -836,7 +442,7 @@ Test1 1. a) string1 2. string2 """ - let output3 = input3.toHtml + let output3 = rstToHtml(input3, {roSupportMarkdown}, defaultConfig()) doAssert count(output3, "
        ") == 2 doAssert "
      1. string1
      2. " in output3 and "
      3. string2
      4. " in output3 @@ -852,7 +458,7 @@ Test1 c) string5 e) string6 """ - let output4 = input4.toHtml + let output4 = rstToHtml(input4, {roSupportMarkdown}, defaultConfig()) doAssert count(output4, "
          ") == 4 for enumerator in [9, 12]: @@ -872,7 +478,7 @@ Test1 #) string5 #) string6 """ - let output5 = input5.toHtml + let output5 = rstToHtml(input5, {roSupportMarkdown}, defaultConfig()) doAssert count(output5, "
            ") == 2 doAssert count(output5, "
          1. ") == 5 @@ -884,7 +490,7 @@ Test1 #. string2 #. string3 """ - let output5a = input5a.toHtml + let output5a = rstToHtml(input5a, {roSupportMarkdown}, defaultConfig()) doAssert count(output5a, "
              ") == 1 doAssert count(output5a, "
            1. ") == 3 @@ -896,7 +502,7 @@ Test1 #. string2 #. string3 """ - let output6 = input6.toHtml + let output6 = rstToHtml(input6, {roSupportMarkdown}, defaultConfig()) doAssert count(output6, "
                ") == 1 doAssert count(output6, "
              1. ") == 3 @@ -909,29 +515,12 @@ Test1 #. string2 #. string3 """ - let output7 = input7.toHtml + let output7 = rstToHtml(input7, {roSupportMarkdown}, defaultConfig()) doAssert count(output7, "
                  ") == 1 doAssert count(output7, "
                1. ") == 3 doAssert "start=\"3\"" in output7 and "class=\"upperalpha simple\"" in output7 - # check that it's not recognized as enum.list without indentation on 2nd line - let input8 = dedent """ - Paragraph. - - A. stringA - B. stringB - C. string1 - string2 - """ - var warnings8 = new seq[string] - let output8 = input8.toHtml(warnings = warnings8) - check(warnings8[].len == 1) - check("input(6, 1) Warning: RST style: \n" & - "not enough indentation on line 6" in warnings8[0]) - doAssert output8 == "Paragraph.
                    " & - "
                  1. stringA
                  2. \n
                  3. stringB
                  4. \n
                  \n

                  C. string1 string2

                  " - test "Markdown enumerated lists": let input1 = dedent """ Below are 2 enumerated lists: Markdown-style (5 items) and RST (1 item) @@ -944,7 +533,7 @@ Test1 #. lineA """ - let output1 = input1.toHtml + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) for i in 1..5: doAssert ($i & ". line" & $i) notin output1 doAssert ("
                2. line" & $i & "
                3. ") in output1 @@ -970,216 +559,12 @@ Test1 * line5 5 """ - let output1 = input1.toHtml + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) for i in 1..5: doAssert ("
                4. line" & $i & " " & $i & "
                5. ") in output1 doAssert count(output1, "
                    ") == 1 - test "Nim RST footnotes and citations": - # check that auto-label footnote enumerated properly after a manual one - let input1 = dedent """ - .. [1] Body1. - .. [#note] Body2 - - Ref. [#note]_ - """ - let output1 = input1.toHtml(preferRst) - doAssert output1.count(">[1]") == 1 - doAssert output1.count(">[2]") == 2 - doAssert "href=\"#footnote-note\"" in output1 - doAssert ">[-1]" notin output1 - doAssert "Body1." in output1 - doAssert "Body2" in output1 - - # check that there are NO footnotes/citations, only comments: - let input2 = dedent """ - .. [1 #] Body1. - .. [# note] Body2. - .. [wrong citation] That gives you a comment. - - .. [not&allowed] That gives you a comment. - - Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_. - """ - let output2 = input2.toHtml(preferRst) - doAssert output2 == "Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_." - - # check that auto-symbol footnotes work: - let input3 = dedent """ - Ref. [*]_ and [*]_ and [*]_. - - .. [*] Body1 - .. [*] Body2. - - - .. [*] Body3. - .. [*] Body4 - - And [*]_. - """ - 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 - doAssert output3.count("href=\"#footnotesym-3\">[***]") == 2 - doAssert output3.count("href=\"#footnotesym-4\">[^]") == 2 - # footnote group - doAssert output3.count("
                    " & - "
                    ") == 1 - # footnotes - doAssert output3.count("
                    " & - "[*]
                    ") == 1 - doAssert output3.count("
                    " & - "[**]
                    ") == 1 - doAssert output3.count("
                    " & - "[***]
                    ") == 1 - doAssert output3.count("
                    " & - "[^]
                    ") == 1 - for i in 1 .. 4: doAssert ("Body" & $i) in output3 - - # check manual, auto-number and auto-label footnote enumeration - let input4 = dedent """ - .. [3] Manual1. - .. [#] Auto-number1. - .. [#mylabel] Auto-label1. - .. [#note] Auto-label2. - .. [#] Auto-number2. - - Ref. [#note]_ and [#]_ and [#]_. - """ - let output4 = input4.toHtml(preferRst) - doAssert ">[-1]" notin output1 - let order = @[ - "footnote-3", "[3]", "Manual1.", - "footnoteauto-1", "[1]", "Auto-number1", - "footnote-mylabel", "[2]", "Auto-label1", - "footnote-note", "[4]", "Auto-label2", - "footnoteauto-2", "[5]", "Auto-number2", - ] - for i in 0 .. order.len-2: - let pos1 = output4.find(order[i]) - let pos2 = output4.find(order[i+1]) - doAssert pos1 >= 0 - doAssert pos2 >= 0 - doAssert pos1 < pos2 - - # forgot [#]_ - let input5 = dedent """ - .. [3] Manual1. - .. [#] Auto-number1. - .. [#note] Auto-label2. - - Ref. [#note]_ - """ - var error5 = new string - 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") - - # extra [*]_ - let input6 = dedent """ - Ref. [*]_ - - .. [*] Auto-Symbol. - - Ref. [*]_ - """ - var error6 = new string - 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") - - let input7 = dedent """ - .. [Some:CITATION-2020] Citation. - - Ref. [some:citation-2020]_. - """ - 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 - doAssert output3.count("
                    " & - "
                    ") == 1 - - let input8 = dedent """ - .. [Some] Citation. - - Ref. [som]_. - """ - var warnings8 = new seq[string] - 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: - let input9 = dedent """ - .. [Some] Citation. - - .. _`internal anchor`: - - .. [Another] Citation. - .. just comment. - .. [Third] Citation. - - Paragraph1. - - Paragraph2 ref `internal anchor`_. - """ - let output9 = input9.toHtml - # _`internal anchor` got erased: - check "href=\"#internal-anchor\"" notin output9 - check "href=\"#citation-another\"" in output9 - doAssert output9.count("
                    " & - "
                    ") == 1 - doAssert output9.count("
                    ") == 3 - doAssert "just comment" notin output9 - - # check that nested citations/footnotes work - let input10 = dedent """ - Paragraph1 [#]_. - - .. [First] Citation. - - .. [#] Footnote. - - .. [Third] Citation. - """ - let output10 = input10.toHtml(preferRst) - doAssert output10.count("
                    " & - "
                    ") == 3 - doAssert output10.count("
                    ") == 3 - doAssert "[First]" in output10 - doAssert "[1]" in output10 - doAssert "[Third]" in output10 - - let input11 = ".. [note]\n" # should not crash - let output11 = input11.toHtml - doAssert "[note]" in output11 - - # check that references to auto-numbered footnotes work - let input12 = dedent """ - Ref. [#]_ and [#]_ STOP. - - .. [#] Body1. - .. [#] Body3 - .. [2] Body2. - """ - let output12 = input12.toHtml(preferRst) - let orderAuto = @[ - "#footnoteauto-1", "[1]", - "#footnoteauto-2", "[3]", - "STOP.", - "Body1.", "Body3", "Body2." - ] - for i in 0 .. orderAuto.len-2: - let pos1 = output12.find(orderAuto[i]) - let pos2 = output12.find(orderAuto[i+1]) - doAssert pos1 >= 0 - doAssert pos2 >= 0 - doAssert pos1 < pos2 - test "Nim (RST extension) code-block": # check that presence of fields doesn't consume the following text as # its code (which is a literal block) @@ -1188,65 +573,9 @@ Test1 :number-lines: 0 Paragraph1""" - let output0 = input0.toHtml + let output0 = rstToHtml(input0, {roSupportMarkdown}, defaultConfig()) doAssert "

                    Paragraph1

                    " in output0 - test "Nim code-block :number-lines:": - let input = dedent """ - .. code-block:: nim - :number-lines: 55 - - x - y - """ - check "
                    55\n56\n
                    " in input.toHtml - - test "Nim code-block indentation": - let input = dedent """ - .. code-block:: nim - :number-lines: 55 - - x - """ - let output = input.toHtml - check "
                    55\n
                    " in output - check "x" in output - - test "Nim code-block indentation": - let input = dedent """ - .. code-block:: nim - :number-lines: 55 - let a = 1 - """ - var error = new string - let output = input.toHtml(error=error) - check(error[] == "input(2, 3) Error: invalid field: " & - "extra arguments were given to number-lines: ' let a = 1'") - check "" == output - - test "code-block warning": - let input = dedent """ - .. code:: Nim - :unsupportedField: anything - - .. code:: unsupportedLang - - anything - - ```anotherLang - someCode - ``` - """ - let warnings = new seq[string] - let output = input.toHtml(warnings=warnings) - check(warnings[] == @[ - "input(2, 4) Warning: field 'unsupportedField' not supported", - "input(4, 11) Warning: language 'unsupportedLang' not supported", - "input(8, 4) Warning: language 'anotherLang' not supported" - ]) - check(output == "
                    anything
                    " & - "

                    \nsomeCode

                    ") - test "RST admonitions": # check that all admonitions are implemented let input0 = dedent """ @@ -1261,9 +590,7 @@ Test1 .. tip:: endOf tip .. warning:: endOf warning """ - let output0 = input0.toHtml( - NoSandboxOpts - ) + let output0 = rstToHtml(input0, {roSupportMarkdown}, defaultConfig()) for a in ["admonition", "attention", "caution", "danger", "error", "hint", "important", "note", "tip", "warning" ]: doAssert "endOf " & a & "
                    " in output0 @@ -1274,9 +601,7 @@ Test1 Test paragraph. """ - let output1 = input1.toHtml( - NoSandboxOpts - ) + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) doAssert "endOfError
                    " in output1 doAssert "

                    Test paragraph.

                    " in output1 doAssert "class=\"admonition admonition-error\"" in output1 @@ -1288,9 +613,7 @@ Test1 Test paragraph. """ - let output2 = input2.toHtml( - NoSandboxOpts - ) + let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) doAssert "endOfError Test2p.
                    " in output2 doAssert "

                    Test paragraph.

                    " in output2 doAssert "class=\"admonition admonition-error\"" in output2 @@ -1298,326 +621,10 @@ Test1 let input3 = dedent """ .. note:: endOfNote """ - let output3 = input3.toHtml( - NoSandboxOpts - ) + let output3 = rstToHtml(input3, {roSupportMarkdown}, defaultConfig()) doAssert "endOfNote
                    " in output3 doAssert "class=\"admonition admonition-info\"" in output3 - test "RST internal links": - let input1 = dedent """ - Start. - - .. _target000: - - Paragraph. - - .. _target001: - - * bullet list - * Y - - .. _target002: - - 1. enumeration list - 2. Y - - .. _target003: - - term 1 - Definition list 1. - - .. _target004: - - | line block - - .. _target005: - - :a: field list value - - .. _target006: - - -a option description - - .. _target007: - - :: - - Literal block - - .. _target008: - - Doctest blocks are not implemented. - - .. _target009: - - block quote - - .. _target010: - - ===== ===== ======= - A B A and B - ===== ===== ======= - False False False - ===== ===== ======= - - .. _target100: - - .. CAUTION:: admonition - - .. _target101: - - .. code:: nim - - const pi = 3.14 - - .. _target102: - - .. code-block:: - - const pi = 3.14 - - Paragraph2. - - .. _target202: - - ---- - - That was a transition. - """ - let output1 = input1.toHtml( - preferRst - ) - doAssert "

                    target300" in output2 - doAssert "href=\"#subsectiona\">target301" in output2 - doAssert "href=\"#citation-cit2020\">target103" in output2 - - let output2l = rstToLatex(input2, {}) - doAssert "\\label{section-xyz}\\hypertarget{section-xyz}{}" in output2l - doAssert "\\hyperlink{section-xyz}{target300}" in output2l - doAssert "\\hyperlink{subsectiona}{target301}" in output2l - - test "RST internal links (inline)": - let input1 = dedent """ - Paragraph with _`some definition`. - - Ref. `some definition`_. - """ - let output1 = input1.toHtml - doAssert "some definition" in output1 - doAssert "Ref. some definition" in output1 - - test "RST references (additional symbols)": - # check that ., _, -, +, : are allowed symbols in references without ` ` - let input1 = dedent """ - sec.1 - ----- - - 2-other:sec+c_2 - ^^^^^^^^^^^^^^^ - - .. _link.1_2021: - - Paragraph - - Ref. sec.1_! and 2-other:sec+c_2_;and link.1_2021_. - """ - let output1 = input1.toHtml - doAssert "id=\"secdot1\"" in output1 - doAssert "id=\"Z2minusothercolonsecplusc-2\"" in output1 - check "id=\"linkdot1-2021\"" in output1 - let ref1 = "sec.1" - let ref2 = "2-other:sec+c_2" - let ref3 = "link.1_2021" - let refline = "Ref. " & ref1 & "! and " & ref2 & ";and " & ref3 & "." - doAssert refline in output1 - - test "Option lists 1": - # check that "* b" is not consumed by previous bullet item because of - # incorrect indentation handling in option lists - let input = dedent """ - * a - -m desc - -n very long - desc - * b""" - let output = input.toHtml - check(output.count("") == 2) - check(output.count("

                    desc
                    """ in - output) - check(optionListLabel("-n") & - """
                    very long desc
                    """ in - output) - - test "Option lists 2": - # check that 2nd option list is not united with the 1st - let input = dedent """ - * a - -m desc - -n very long - desc - -d option""" - let output = input.toHtml - check(output.count("desc
    """ in - output) - check(optionListLabel("-n") & - """
    very long desc
    """ in - output) - check(optionListLabel("-d") & - """
    option
    """ in - output) - check "

    option

    " notin output - - test "Option list 3 (double /)": - let input = dedent """ - * a - //compile compile1 - //doc doc1 - cont - -d option""" - let output = input.toHtml - check(output.count("compile1""" in - output) - check(optionListLabel("doc") & - """
    doc1 cont
    """ in - output) - check(optionListLabel("-d") & - """
    option
    """ in - output) - check "

    option

    " notin output - - test "Roles: subscript prefix/postfix": - let expected = "See some text." - check "See :subscript:`some text`.".toHtml == expected - check "See `some text`:subscript:.".toHtml == expected - - test "Roles: correct parsing from beginning of line": - let expected = "3He is an isotope of helium." - check """:superscript:`3`\ He is an isotope of helium.""".toHtml == expected - check """:sup:`3`\ He is an isotope of helium.""".toHtml == expected - check """`3`:sup:\ He is an isotope of helium.""".toHtml == expected - check """`3`:superscript:\ He is an isotope of helium.""".toHtml == expected - - test "Roles: warnings": - let input = dedent""" - See function :py:func:`spam`. - - See also `egg`:py:class:. - """ - var warnings = new seq[string] - let output = input.toHtml(warnings=warnings) - doAssert warnings[].len == 2 - check "(1, 14) Warning: " in warnings[0] - check "language 'py:func' not supported" in warnings[0] - check "(3, 15) Warning: " in warnings[1] - check "language 'py:class' not supported" in warnings[1] - check("""

    See function spam.

    """ & "\n" & - """

    See also egg.

    """ == - output) - - test "(not) Roles: check escaping 1": - let expected = """See :subscript:""" & - """""" & id"some" & " " & id"text" & - "." - check """See \:subscript:`some text`.""".toHtml == expected - check """See :subscript\:`some text`.""".toHtml == expected - - test "(not) Roles: check escaping 2": - check("""See :subscript:\`some text\`.""".toHtml == - "See :subscript:`some text`.") - - test "Field list": - check(":field: text".toHtml == - """""" & - """""" & - """""" & - """""" & "\n
    field:text
    ") - - test "Field list: body after newline": - let output = dedent""" - :field: - text1""".toHtml - check "field:" in output - check "" in output - - test "Field list (incorrect)": - check ":field:text".toHtml == ":field:text" - suite "RST/Code highlight": test "Basic Python code highlight": let pythonCode = """ @@ -1633,59 +640,3 @@ suite "RST/Code highlight": check strip(rstToHtml(pythonCode, {}, newStringTable(modeCaseSensitive))) == strip(expected) - - -suite "invalid targets": - test "invalid image target": - let input1 = dedent """.. image:: /images/myimage.jpg - :target: https://bar.com - :alt: Alt text for the image""" - let output1 = input1.toHtml - check output1 == """Alt text for the image""" - - let input2 = dedent """.. image:: /images/myimage.jpg - :target: javascript://bar.com - :alt: Alt text for the image""" - let output2 = input2.toHtml - check output2 == """Alt text for the image""" - - let input3 = dedent """.. image:: /images/myimage.jpg - :target: bar.com - :alt: Alt text for the image""" - let output3 = input3.toHtml - check output3 == """Alt text for the image""" - - test "invalid links": - check("(([Nim](https://nim-lang.org/)))".toHtml == - """((Nim))""") - # unknown protocol is treated just like plain text, not a link - var warnings = new seq[string] - check("(([Nim](javascript://nim-lang.org/)))".toHtml(warnings=warnings) == - """(([Nim](javascript://nim-lang.org/)))""") - check(warnings[] == @["input(1, 9) Warning: broken link 'javascript'"]) - warnings[].setLen 0 - check("`Nim `_".toHtml(warnings=warnings) == - """Nim <javascript://nim-lang.org/>""") - check(warnings[] == @["input(1, 33) Warning: broken link 'javascript'"]) - -suite "local file inclusion": - test "cannot include files in sandboxed mode": - var error = new string - discard ".. include:: ./readme.md".toHtml(error=error) - check(error[] == "input(1, 11) Error: disabled directive: 'include'") - - test "code-block file directive is disabled": - var error = new string - discard ".. code-block:: nim\n :file: ./readme.md".toHtml(error=error) - check(error[] == "input(2, 20) Error: disabled directive: 'file'") - - test "code-block file directive is disabled - Markdown": - var error = new string - discard "```nim file = ./readme.md\n```".toHtml(error=error) - check(error[] == "input(1, 23) Error: disabled directive: 'file'") - -proc documentToHtml*(doc: string, isMarkdown: bool = false): string {.gcsafe.} = - var options = {roSupportMarkdown} - if isMarkdown: - options.incl roPreferMarkdown - result = rstToHtml(doc, options, defaultConfig()) diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index 176c00214..385e6e651 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -1,17 +1,6 @@ -discard """ - targets: "c js" -""" - -# xxx move all tests under `main` - import std/sequtils import strutils from algorithm import sorted -import std/assertions - -{.experimental: "strictEffects".} -{.push warningAsError[Effect]: on.} -{.experimental: "strictFuncs".} # helper for testing double substitution side effects which are handled # by `evalOnceAs` @@ -201,6 +190,17 @@ block: # keepIf test keepIf(floats, proc(x: float): bool = x > 10) doAssert floats == @[13.0, 12.5, 10.1] +block: # delete tests + let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] + var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] + dest.delete(3, 8) + doAssert outcome == dest, """\ + Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] + is [1,1,1,1,1,1,1,1]""" + var x = @[1, 2, 3] + x.delete(100, 100) + doAssert x == @[1, 2, 3] + block: # insert tests var dest = @[1, 1, 1, 1, 1, 1, 1, 1] let @@ -298,45 +298,43 @@ block: # toSeq test doAssert myIter.toSeq == @[1, 2] doAssert toSeq(myIter) == @[1, 2] - when not defined(js): - # pending #4695 - block: - iterator myIter(): int {.closure.} = - yield 1 - yield 2 + block: + iterator myIter(): int {.closure.} = + yield 1 + yield 2 - doAssert myIter.toSeq == @[1, 2] - doAssert toSeq(myIter) == @[1, 2] + doAssert myIter.toSeq == @[1, 2] + doAssert toSeq(myIter) == @[1, 2] + + block: + proc myIter(): auto = + iterator ret(): int {.closure.} = + yield 1 + yield 2 + result = ret + + doAssert myIter().toSeq == @[1, 2] + doAssert toSeq(myIter()) == @[1, 2] + + block: + proc myIter(n: int): auto = + var counter = 0 + iterator ret(): int {.closure.} = + while counter < n: + yield counter + counter.inc + result = ret block: - proc myIter(): auto = - iterator ret(): int {.closure.} = - yield 1 - yield 2 - result = ret - - doAssert myIter().toSeq == @[1, 2] - doAssert toSeq(myIter()) == @[1, 2] - + let myIter3 = myIter(3) + doAssert myIter3.toSeq == @[0, 1, 2] block: - proc myIter(n: int): auto = - var counter = 0 - iterator ret(): int {.closure.} = - while counter < n: - yield counter - counter.inc - result = ret - - block: - let myIter3 = myIter(3) - doAssert myIter3.toSeq == @[0, 1, 2] - block: - let myIter3 = myIter(3) - doAssert toSeq(myIter3) == @[0, 1, 2] - block: - # makes sure this does not hang forever - doAssert myIter(3).toSeq == @[0, 1, 2] - doAssert toSeq(myIter(3)) == @[0, 1, 2] + let myIter3 = myIter(3) + doAssert toSeq(myIter3) == @[0, 1, 2] + block: + # makes sure this does not hang forever + doAssert myIter(3).toSeq == @[0, 1, 2] + doAssert toSeq(myIter(3)) == @[0, 1, 2] block: # tests https://github.com/nim-lang/Nim/issues/7187 @@ -454,88 +452,4 @@ block: for i in 0.. 0: - strip(a, args) - else: - strip(a) - a - -proc main() = - block: # strip - block: # bug #17173 - var a = " vhellov " - strip(a) - doAssert a == "vhellov" - - doAssert strip2(" vhellov ") == "vhellov" - doAssert strip2(" vhellov ", leading = false) == " vhellov" - doAssert strip2(" vhellov ", trailing = false) == "vhellov " - doAssert strip2("vhellov", chars = {'v'}) == "hello" - doAssert strip2("vhellov", leading = false, chars = {'v'}) == "vhello" - doAssert strip2("blaXbla", chars = {'b', 'a'}) == "laXbl" - doAssert strip2("blaXbla", chars = {'b', 'a', 'l'}) == "X" - doAssert strip2("xxxxxx", chars={'x'}) == "" - doAssert strip2("x", chars={'x'}) == "" - doAssert strip2("x", chars={'1'}) == "x" - doAssert strip2("", chars={'x'}) == "" - doAssert strip2("xxx xxx", chars={'x'}) == " " - doAssert strip2("xxx wind", chars={'x'}) == " wind" - doAssert strip2("xxx iii", chars={'i'}) == "xxx " - - block: - var a = "xxx iii" - doAssert a.dup(strip(chars = {'i'})) == "xxx " - doAssert a.dup(strip(chars = {' '})) == "xxx iii" - doAssert a.dup(strip(chars = {'x'})) == " iii" - doAssert a.dup(strip(chars = {'x', ' '})) == "iii" - doAssert a.dup(strip(chars = {'x', 'i'})) == " " - doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 - - block: - var a = "x i" - doAssert a.dup(strip(chars = {'i'})) == "x " - doAssert a.dup(strip(chars = {' '})) == "x i" - doAssert a.dup(strip(chars = {'x'})) == " i" - doAssert a.dup(strip(chars = {'x', ' '})) == "i" - doAssert a.dup(strip(chars = {'x', 'i'})) == " " - doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 - - block: - var a = "" - doAssert a.dup(strip(chars = {'i'})).len == 0 - doAssert a.dup(strip(chars = {' '})).len == 0 - doAssert a.dup(strip(chars = {'x'})).len == 0 - doAssert a.dup(strip(chars = {'x', ' '})).len == 0 - doAssert a.dup(strip(chars = {'x', 'i'})).len == 0 - doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 - - block: - var a = " " - doAssert a.dup(strip(chars = {'i'})) == " " - doAssert a.dup(strip(chars = {' '})).len == 0 - doAssert a.dup(strip(chars = {'x'})) == " " - doAssert a.dup(strip(chars = {'x', ' '})).len == 0 - doAssert a.dup(strip(chars = {'x', 'i'})) == " " - doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 - - block: # setSlice - var a = "Hello, Nim!" - doAssert a.dup(setSlice(7 .. 9)) == "Nim" - doAssert a.dup(setSlice(0 .. 0)) == "H" - doAssert a.dup(setSlice(0 .. 1)) == "He" - doAssert a.dup(setSlice(0 .. 10)) == a - doAssert a.dup(setSlice(1 .. 0)).len == 0 - doAssert a.dup(setSlice(20 .. -1)).len == 0 - - doAssertRaises(AssertionDefect): - discard a.dup(setSlice(-1 .. 1)) - - doAssertRaises(AssertionDefect): - discard a.dup(setSlice(1 .. 11)) - - block: # add - var a0 = "hi" - var b0 = "foobar" - when nimvm: - discard # pending bug #15952 - else: - a0.add b0.toOpenArray(1,3) - doAssert a0 == "hioob" - proc fn(c: openArray[char]): string = - result.add c - doAssert fn("def") == "def" - doAssert fn(['d','\0', 'f'])[2] == 'f' - -static: main() -main() diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index 891b38e20..c2ceed624 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--gc:refc; --gc:arc" input: "Arne" output: ''' Hello! What is your name? @@ -16,7 +15,7 @@ GROOT """ -import std/[syncio, streams, assertions] +import streams block tstreams: @@ -41,7 +40,7 @@ block tstreams2: block tstreams3: try: var fs = openFileStream("shouldneverexist.txt") - except IOError: + except IoError: echo "threw exception" static: @@ -50,12 +49,6 @@ block tstreams3: echo line s.close - -block: - let fs = newFileStream("amissingfile.txt") - defer: fs.close() - doAssert isNil(fs) - # bug #12410 var a = newStringStream "hehohihahuhyh" @@ -81,23 +74,3 @@ block: doAssert(ss.peekLine(str)) doAssert(str == "uick brown fox jumped over the lazy dog.") doAssert(ss.getPosition == 5) # haven't moved - # bug #19707 - Ensure we dont error with writing over literals on arc/orc - ss.setPosition(0) - ss.write("hello") - ss.setPosition(0) - doAssert(ss.peekStr(5) == "hello") - -# bug #19716 -static: # Ensure streams it doesnt break with nimscript on arc/orc #19716 - let s = newStringStream("a") - doAssert s.data == "a" - -template main = - var strm = newStringStream("abcde") - var buffer = "12345" - doAssert strm.readDataStr(buffer, 0..3) == 4 - doAssert buffer == "abcd5" - strm.close() - -static: main() -main() diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index d332e8375..be5f1f304 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -1,565 +1,515 @@ -# xxx: test js target +discard """ +action: "run" +output: '''Received (name: "Foo", species: "Bar")''' +""" + +# issue #7632 import genericstrformat -import std/[strformat, strutils, times, tables, json] +import strutils, times -import std/[assertions, formatfloat] -import std/objectdollar -proc main() = - block: # issue #7632 - doAssert works(5) == "formatted 5" - doAssert fails0(6) == "formatted 6" - doAssert fails(7) == "formatted 7" - doAssert fails2[0](8) == "formatted 8" +doAssert works(5) == "formatted 5" +doAssert fails0(6) == "formatted 6" +doAssert fails(7) == "formatted 7" +doAssert fails2[0](8) == "formatted 8" - block: # other tests - type Obj = object +# other tests - proc `$`(o: Obj): string = "foobar" +import strformat - # for custom types, formatValue needs to be overloaded. - template formatValue(result: var string; value: Obj; specifier: string) = - result.formatValue($value, specifier) +type Obj = object - var o: Obj - doAssert fmt"{o}" == "foobar" - doAssert fmt"{o:10}" == "foobar " +proc `$`(o: Obj): string = "foobar" - doAssert fmt"{o=}" == "o=foobar" - doAssert fmt"{o=:10}" == "o=foobar " +# for custom types, formatValue needs to be overloaded. +template formatValue(result: var string; value: Obj; specifier: string) = + result.formatValue($value, specifier) - block: # see issue #7933 - var str = "abc" - doAssert fmt">7.1 :: {str:>7.1}" == ">7.1 :: a" - doAssert fmt">7.2 :: {str:>7.2}" == ">7.2 :: ab" - doAssert fmt">7.3 :: {str:>7.3}" == ">7.3 :: abc" - doAssert fmt">7.9 :: {str:>7.9}" == ">7.9 :: abc" - doAssert fmt">7.0 :: {str:>7.0}" == ">7.0 :: " - doAssert fmt" 7.1 :: {str:7.1}" == " 7.1 :: a " - doAssert fmt" 7.2 :: {str:7.2}" == " 7.2 :: ab " - doAssert fmt" 7.3 :: {str:7.3}" == " 7.3 :: abc " - doAssert fmt" 7.9 :: {str:7.9}" == " 7.9 :: abc " - doAssert fmt" 7.0 :: {str:7.0}" == " 7.0 :: " - doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: a " - doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: ab " - doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: abc " - doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: abc " - doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " +var o: Obj +doAssert fmt"{o}" == "foobar" +doAssert fmt"{o:10}" == "foobar " - doAssert fmt">7.1 :: {str=:>7.1}" == ">7.1 :: str= a" - doAssert fmt">7.2 :: {str=:>7.2}" == ">7.2 :: str= ab" - doAssert fmt">7.3 :: {str=:>7.3}" == ">7.3 :: str= abc" - doAssert fmt">7.9 :: {str=:>7.9}" == ">7.9 :: str= abc" - doAssert fmt">7.0 :: {str=:>7.0}" == ">7.0 :: str= " - doAssert fmt" 7.1 :: {str=:7.1}" == " 7.1 :: str=a " - doAssert fmt" 7.2 :: {str=:7.2}" == " 7.2 :: str=ab " - doAssert fmt" 7.3 :: {str=:7.3}" == " 7.3 :: str=abc " - doAssert fmt" 7.9 :: {str=:7.9}" == " 7.9 :: str=abc " - doAssert fmt" 7.0 :: {str=:7.0}" == " 7.0 :: str= " - doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str= a " - doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str= ab " - doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str= abc " - doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str= abc " - doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str= " - str = "äöüe\u0309\u0319o\u0307\u0359" - doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: ä " - doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: äö " - doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: äöü " - doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " +doAssert fmt"{o=}" == "o=foobar" +doAssert fmt"{o=:10}" == "o=foobar " - doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str= ä " - doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str= äö " - doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str= äöü " - doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str= " - # this is actually wrong, but the unicode module has no support for graphemes - doAssert fmt"^7.4 :: {str:^7.4}" == "^7.4 :: äöüe " - doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: äöüe\u0309\u0319o\u0307\u0359" - doAssert fmt"^7.4 :: {str=:^7.4}" == "^7.4 :: str= äöüe " - doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str=äöüe\u0309\u0319o\u0307\u0359" +# see issue #7933 +var str = "abc" +doAssert fmt">7.1 :: {str:>7.1}" == ">7.1 :: a" +doAssert fmt">7.2 :: {str:>7.2}" == ">7.2 :: ab" +doAssert fmt">7.3 :: {str:>7.3}" == ">7.3 :: abc" +doAssert fmt">7.9 :: {str:>7.9}" == ">7.9 :: abc" +doAssert fmt">7.0 :: {str:>7.0}" == ">7.0 :: " +doAssert fmt" 7.1 :: {str:7.1}" == " 7.1 :: a " +doAssert fmt" 7.2 :: {str:7.2}" == " 7.2 :: ab " +doAssert fmt" 7.3 :: {str:7.3}" == " 7.3 :: abc " +doAssert fmt" 7.9 :: {str:7.9}" == " 7.9 :: abc " +doAssert fmt" 7.0 :: {str:7.0}" == " 7.0 :: " +doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: a " +doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: ab " +doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: abc " +doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: abc " +doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " - block: # see issue #7932 - doAssert fmt"{15:08}" == "00000015" # int, works - doAssert fmt"{1.5:08}" == "000001.5" # float, works - doAssert fmt"{1.5:0>8}" == "000001.5" # workaround using fill char works for positive floats - doAssert fmt"{-1.5:0>8}" == "0000-1.5" # even that does not work for negative floats - doAssert fmt"{-1.5:08}" == "-00001.5" # works - doAssert fmt"{1.5:+08}" == "+00001.5" # works - doAssert fmt"{1.5: 08}" == " 00001.5" # works +doAssert fmt">7.1 :: {str=:>7.1}" == ">7.1 :: str= a" +doAssert fmt">7.2 :: {str=:>7.2}" == ">7.2 :: str= ab" +doAssert fmt">7.3 :: {str=:>7.3}" == ">7.3 :: str= abc" +doAssert fmt">7.9 :: {str=:>7.9}" == ">7.9 :: str= abc" +doAssert fmt">7.0 :: {str=:>7.0}" == ">7.0 :: str= " +doAssert fmt" 7.1 :: {str=:7.1}" == " 7.1 :: str=a " +doAssert fmt" 7.2 :: {str=:7.2}" == " 7.2 :: str=ab " +doAssert fmt" 7.3 :: {str=:7.3}" == " 7.3 :: str=abc " +doAssert fmt" 7.9 :: {str=:7.9}" == " 7.9 :: str=abc " +doAssert fmt" 7.0 :: {str=:7.0}" == " 7.0 :: str= " +doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str= a " +doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str= ab " +doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str= abc " +doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str= abc " +doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str= " +str = "äöüe\u0309\u0319o\u0307\u0359" +doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: ä " +doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: äö " +doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: äöü " +doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " - doAssert fmt"{15=:08}" == "15=00000015" # int, works - doAssert fmt"{1.5=:08}" == "1.5=000001.5" # float, works - doAssert fmt"{1.5=:0>8}" == "1.5=000001.5" # workaround using fill char works for positive floats - doAssert fmt"{-1.5=:0>8}" == "-1.5=0000-1.5" # even that does not work for negative floats - doAssert fmt"{-1.5=:08}" == "-1.5=-00001.5" # works - doAssert fmt"{1.5=:+08}" == "1.5=+00001.5" # works - doAssert fmt"{1.5=: 08}" == "1.5= 00001.5" # works +doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str= ä " +doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str= äö " +doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str= äöü " +doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str= " +# this is actually wrong, but the unicode module has no support for graphemes +doAssert fmt"^7.4 :: {str:^7.4}" == "^7.4 :: äöüe " +doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: äöüe\u0309\u0319o\u0307\u0359" - block: # only add explicitly requested sign if value != -0.0 (neg zero) - doAssert fmt"{-0.0:g}" == "-0" - doAssert fmt"{-0.0:+g}" == "-0" - doAssert fmt"{-0.0: g}" == "-0" - doAssert fmt"{0.0:g}" == "0" - doAssert fmt"{0.0:+g}" == "+0" - doAssert fmt"{0.0: g}" == " 0" +doAssert fmt"^7.4 :: {str=:^7.4}" == "^7.4 :: str= äöüe " +doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str=äöüe\u0309\u0319o\u0307\u0359" - doAssert fmt"{-0.0=:g}" == "-0.0=-0" - doAssert fmt"{-0.0=:+g}" == "-0.0=-0" - doAssert fmt"{-0.0=: g}" == "-0.0=-0" - doAssert fmt"{0.0=:g}" == "0.0=0" - doAssert fmt"{0.0=:+g}" == "0.0=+0" - doAssert fmt"{0.0=: g}" == "0.0= 0" +# see issue #7932 +doAssert fmt"{15:08}" == "00000015" # int, works +doAssert fmt"{1.5:08}" == "000001.5" # float, works +doAssert fmt"{1.5:0>8}" == "000001.5" # workaround using fill char works for positive floats +doAssert fmt"{-1.5:0>8}" == "0000-1.5" # even that does not work for negative floats +doAssert fmt"{-1.5:08}" == "-00001.5" # works +doAssert fmt"{1.5:+08}" == "+00001.5" # works +doAssert fmt"{1.5: 08}" == " 00001.5" # works - block: # seq format - let data1 = [1'i64, 10000'i64, 10000000'i64] - let data2 = [10000000'i64, 100'i64, 1'i64] +doAssert fmt"{15=:08}" == "15=00000015" # int, works +doAssert fmt"{1.5=:08}" == "1.5=000001.5" # float, works +doAssert fmt"{1.5=:0>8}" == "1.5=000001.5" # workaround using fill char works for positive floats +doAssert fmt"{-1.5=:0>8}" == "-1.5=0000-1.5" # even that does not work for negative floats +doAssert fmt"{-1.5=:08}" == "-1.5=-00001.5" # works +doAssert fmt"{1.5=:+08}" == "1.5=+00001.5" # works +doAssert fmt"{1.5=: 08}" == "1.5= 00001.5" # works - proc formatValue(result: var string; value: (array|seq|openArray); specifier: string) = - result.add "[" - for i, it in value: - if i != 0: - result.add ", " - result.formatValue(it, specifier) - result.add "]" +# only add explicitly requested sign if value != -0.0 (neg zero) +doAssert fmt"{-0.0:g}" == "-0" +doAssert fmt"{-0.0:+g}" == "-0" +doAssert fmt"{-0.0: g}" == "-0" +doAssert fmt"{0.0:g}" == "0" +doAssert fmt"{0.0:+g}" == "+0" +doAssert fmt"{0.0: g}" == " 0" - doAssert fmt"data1: {data1:8} #" == "data1: [ 1, 10000, 10000000] #" - doAssert fmt"data2: {data2:8} =" == "data2: [10000000, 100, 1] =" +doAssert fmt"{-0.0=:g}" == "-0.0=-0" +doAssert fmt"{-0.0=:+g}" == "-0.0=-0" +doAssert fmt"{-0.0=: g}" == "-0.0=-0" +doAssert fmt"{0.0=:g}" == "0.0=0" +doAssert fmt"{0.0=:+g}" == "0.0=+0" +doAssert fmt"{0.0=: g}" == "0.0= 0" - doAssert fmt"data1: {data1=:8} #" == "data1: data1=[ 1, 10000, 10000000] #" - doAssert fmt"data2: {data2=:8} =" == "data2: data2=[10000000, 100, 1] =" +# seq format - block: # custom format Value - type - Vec2[T] = object - x,y: T +let data1 = [1'i64, 10000'i64, 10000000'i64] +let data2 = [10000000'i64, 100'i64, 1'i64] - proc formatValue[T](result: var string; value: Vec2[T]; specifier: string) = - result.add '[' - result.formatValue value.x, specifier +proc formatValue(result: var string; value: (array|seq|openArray); specifier: string) = + result.add "[" + for i, it in value: + if i != 0: result.add ", " - result.formatValue value.y, specifier - result.add "]" + result.formatValue(it, specifier) + result.add "]" - let v1 = Vec2[float32](x:1.0, y: 2.0) - let v2 = Vec2[int32](x:1, y: 1337) - doAssert fmt"v1: {v1:+08} v2: {v2:>4}" == "v1: [+0000001, +0000002] v2: [ 1, 1337]" - doAssert fmt"v1: {v1=:+08} v2: {v2=:>4}" == "v1: v1=[+0000001, +0000002] v2: v2=[ 1, 1337]" +doAssert fmt"data1: {data1:8} #" == "data1: [ 1, 10000, 10000000] #" +doAssert fmt"data2: {data2:8} =" == "data2: [10000000, 100, 1] =" - block: # bug #11012 - type - Animal = object - name, species: string - AnimalRef = ref Animal +doAssert fmt"data1: {data1=:8} #" == "data1: data1=[ 1, 10000, 10000000] #" +doAssert fmt"data2: {data2=:8} =" == "data2: data2=[10000000, 100, 1] =" - proc print_object(animalAddr: AnimalRef): string = - fmt"Received {animalAddr[]}" +# custom format Value - doAssert print_object(AnimalRef(name: "Foo", species: "Bar")) == """Received (name: "Foo", species: "Bar")""" +type + Vec2[T] = object + x,y: T - block: # bug #11723 - let pos: Positive = 64 - doAssert fmt"{pos:3}" == " 64" - doAssert fmt"{pos:3b}" == "1000000" - doAssert fmt"{pos:3d}" == " 64" - doAssert fmt"{pos:3o}" == "100" - doAssert fmt"{pos:3x}" == " 40" - doAssert fmt"{pos:3X}" == " 40" +proc formatValue[T](result: var string; value: Vec2[T]; specifier: string) = + result.add '[' + result.formatValue value.x, specifier + result.add ", " + result.formatValue value.y, specifier + result.add "]" - doAssert fmt"{pos=:3}" == "pos= 64" - doAssert fmt"{pos=:3b}" == "pos=1000000" - doAssert fmt"{pos=:3d}" == "pos= 64" - doAssert fmt"{pos=:3o}" == "pos=100" - doAssert fmt"{pos=:3x}" == "pos= 40" - doAssert fmt"{pos=:3X}" == "pos= 40" +let v1 = Vec2[float32](x:1.0, y: 2.0) +let v2 = Vec2[int32](x:1, y: 1337) +doAssert fmt"v1: {v1:+08} v2: {v2:>4}" == "v1: [+0000001, +0000002] v2: [ 1, 1337]" +doAssert fmt"v1: {v1=:+08} v2: {v2=:>4}" == "v1: v1=[+0000001, +0000002] v2: v2=[ 1, 1337]" - let nat: Natural = 64 - doAssert fmt"{nat:3}" == " 64" - doAssert fmt"{nat:3b}" == "1000000" - doAssert fmt"{nat:3d}" == " 64" - doAssert fmt"{nat:3o}" == "100" - doAssert fmt"{nat:3x}" == " 40" - doAssert fmt"{nat:3X}" == " 40" +# bug #11012 - doAssert fmt"{nat=:3}" == "nat= 64" - doAssert fmt"{nat=:3b}" == "nat=1000000" - doAssert fmt"{nat=:3d}" == "nat= 64" - doAssert fmt"{nat=:3o}" == "nat=100" - doAssert fmt"{nat=:3x}" == "nat= 40" - doAssert fmt"{nat=:3X}" == "nat= 40" +type + Animal = object + name, species: string + AnimalRef = ref Animal - block: # bug #12612 - proc my_proc() = - const value = "value" - const a = &"{value}" - doAssert a == value +proc print_object(animalAddr: AnimalRef) = + echo fmt"Received {animalAddr[]}" - const b = &"{value=}" - doAssert b == "value=" & value +print_object(AnimalRef(name: "Foo", species: "Bar")) - my_proc() +# bug #11723 - block: - template fmt(pattern: string; openCloseChar: char): untyped = - fmt(pattern, openCloseChar, openCloseChar) +let pos: Positive = 64 +doAssert fmt"{pos:3}" == " 64" +doAssert fmt"{pos:3b}" == "1000000" +doAssert fmt"{pos:3d}" == " 64" +doAssert fmt"{pos:3o}" == "100" +doAssert fmt"{pos:3x}" == " 40" +doAssert fmt"{pos:3X}" == " 40" - let - testInt = 123 - testStr = "foobar" - testFlt = 3.141592 - doAssert ">><<".fmt('<', '>') == "><" - doAssert " >> << ".fmt('<', '>') == " > < " - doAssert "<<>>".fmt('<', '>') == "<>" - doAssert " << >> ".fmt('<', '>') == " < > " - doAssert "''".fmt('\'') == "'" - doAssert "''''".fmt('\'') == "''" - doAssert "'' ''".fmt('\'') == "' '" - doAssert "".fmt('<', '>') == "123" - doAssert "".fmt('<', '>') == "123" - doAssert "'testFlt:1.2f'".fmt('\'') == "3.14" - doAssert "".fmt('<', '>') == "123foobar" - doAssert """ ""{"123+123"}"" """.fmt('"') == " \"{246}\" " - doAssert "(((testFlt:1.2f)))((111))".fmt('(', ')') == "(3.14)(111)" - doAssert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)" - doAssert "{}abc`testStr' `testFlt:1.2f' `1+1' ``".fmt('`', '\'') == "{}abcfoobar 3.14 2 `" - doAssert """x = '"foo" & "bar"' - y = '123 + 111' - z = '3 in {2..7}' - """.fmt('\'') == - """x = foobar - y = 234 - z = true - """ +doAssert fmt"{pos=:3}" == "pos= 64" +doAssert fmt"{pos=:3b}" == "pos=1000000" +doAssert fmt"{pos=:3d}" == "pos= 64" +doAssert fmt"{pos=:3o}" == "pos=100" +doAssert fmt"{pos=:3x}" == "pos= 40" +doAssert fmt"{pos=:3X}" == "pos= 40" - block: # tests from the very own strformat documentation! - let msg = "hello" - doAssert fmt"{msg}\n" == "hello\\n" - doAssert &"{msg}\n" == "hello\n" +let nat: Natural = 64 +doAssert fmt"{nat:3}" == " 64" +doAssert fmt"{nat:3b}" == "1000000" +doAssert fmt"{nat:3d}" == " 64" +doAssert fmt"{nat:3o}" == "100" +doAssert fmt"{nat:3x}" == " 40" +doAssert fmt"{nat:3X}" == " 40" - doAssert fmt"{msg}{'\n'}" == "hello\n" - doAssert fmt("{msg}\n") == "hello\n" - doAssert "{msg}\n".fmt == "hello\n" +doAssert fmt"{nat=:3}" == "nat= 64" +doAssert fmt"{nat=:3b}" == "nat=1000000" +doAssert fmt"{nat=:3d}" == "nat= 64" +doAssert fmt"{nat=:3o}" == "nat=100" +doAssert fmt"{nat=:3x}" == "nat= 40" +doAssert fmt"{nat=:3X}" == "nat= 40" - doAssert fmt"{msg=}\n" == "msg=hello\\n" +# bug #12612 +proc my_proc = + const value = "value" + const a = &"{value}" + doAssert a == value - doAssert &"{msg=}\n" == "msg=hello\n" + const b = &"{value=}" + doAssert b == "value=" & value - doAssert fmt"{msg=}{'\n'}" == "msg=hello\n" - doAssert fmt("{msg=}\n") == "msg=hello\n" - doAssert "{msg=}\n".fmt == "msg=hello\n" +my_proc() - doAssert &"""{"abc":>4}""" == " abc" - doAssert &"""{"abc":<4}""" == "abc " +block: + template fmt(pattern: string; openCloseChar: char): untyped = + fmt(pattern, openCloseChar, openCloseChar) - doAssert fmt"{-12345:08}" == "-0012345" - doAssert fmt"{-1:3}" == " -1" - doAssert fmt"{-1:03}" == "-01" - doAssert fmt"{16:#X}" == "0x10" + let + testInt = 123 + testStr = "foobar" + testFlt = 3.141592 + doAssert ">><<".fmt('<', '>') == "><" + doAssert " >> << ".fmt('<', '>') == " > < " + doAssert "<<>>".fmt('<', '>') == "<>" + doAssert " << >> ".fmt('<', '>') == " < > " + doAssert "''".fmt('\'') == "'" + doAssert "''''".fmt('\'') == "''" + doAssert "'' ''".fmt('\'') == "' '" + doAssert "".fmt('<', '>') == "123" + doAssert "".fmt('<', '>') == "123" + doAssert "'testFlt:1.2f'".fmt('\'') == "3.14" + doAssert "".fmt('<', '>') == "123foobar" + doAssert """ ""{"123+123"}"" """.fmt('"') == " \"{246}\" " + doAssert "(((testFlt:1.2f)))((111))".fmt('(', ')') == "(3.14)(111)" + doAssert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)" + doAssert "{}abc`testStr' `testFlt:1.2f' `1+1' ``".fmt('`', '\'') == "{}abcfoobar 3.14 2 `" + doAssert """x = '"foo" & "bar"' + y = '123 + 111' + z = '3 in {2..7}' + """.fmt('\'') == + """x = foobar + y = 234 + z = true + """ - doAssert fmt"{123.456}" == "123.456" - doAssert fmt"{123.456:>9.3f}" == " 123.456" - doAssert fmt"{123.456:9.3f}" == " 123.456" - doAssert fmt"{123.456:9.4f}" == " 123.4560" - doAssert fmt"{123.456:>9.0f}" == " 123." - doAssert fmt"{123.456:<9.4f}" == "123.4560 " - doAssert fmt"{123.456:e}" == "1.234560e+02" - doAssert fmt"{123.456:>13e}" == " 1.234560e+02" - doAssert fmt"{123.456:13e}" == " 1.234560e+02" +# tests from the very own strformat documentation! - doAssert &"""{"abc"=:>4}""" == "\"abc\"= abc" - doAssert &"""{"abc"=:<4}""" == "\"abc\"=abc " +let msg = "hello" +doAssert fmt"{msg}\n" == "hello\\n" - doAssert fmt"{-12345=:08}" == "-12345=-0012345" - doAssert fmt"{-1=:3}" == "-1= -1" - doAssert fmt"{-1=:03}" == "-1=-01" - doAssert fmt"{16=:#X}" == "16=0x10" +doAssert &"{msg}\n" == "hello\n" - doAssert fmt"{123.456=}" == "123.456=123.456" - doAssert fmt"{123.456=:>9.3f}" == "123.456= 123.456" - doAssert fmt"{123.456=:9.3f}" == "123.456= 123.456" - doAssert fmt"{123.456=:9.4f}" == "123.456= 123.4560" - doAssert fmt"{123.456=:>9.0f}" == "123.456= 123." - doAssert fmt"{123.456=:<9.4f}" == "123.456=123.4560 " +doAssert fmt"{msg}{'\n'}" == "hello\n" +doAssert fmt("{msg}\n") == "hello\n" +doAssert "{msg}\n".fmt == "hello\n" - doAssert fmt"{123.456=:e}" == "123.456=1.234560e+02" - doAssert fmt"{123.456=:>13e}" == "123.456= 1.234560e+02" - doAssert fmt"{123.456=:13e}" == "123.456= 1.234560e+02" +doAssert fmt"{msg=}\n" == "msg=hello\\n" - let x = 3.14 - doAssert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847" - doAssert fmt"""{(block: - var res: string - for i in 1..15: - res.add (if i mod 15 == 0: "FizzBuzz" - elif i mod 5 == 0: "Buzz" - elif i mod 3 == 0: "Fizz" - else: $i) & " " - res)}""" == "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz " +doAssert &"{msg=}\n" == "msg=hello\n" - doAssert fmt"""{ "\{(" & msg & ")\}" }""" == "{(hello)}" - doAssert fmt"""{{({ msg })}}""" == "{(hello)}" - doAssert fmt"""{ $(\{msg:1,"world":2\}) }""" == """[("hello", 1), ("world", 2)]""" - block: # tests for debug format string - var name = "hello" - let age = 21 - const hobby = "swim" - doAssert fmt"{age*9 + 16=}" == "age*9 + 16=205" - doAssert &"name: {name =}\nage: { age =: >7}\nhobby: { hobby= : 8}" == - "name: name =hello\nage: age = 21\nhobby: hobby= swim " - doAssert fmt"{age == 12}" == "false" - doAssert fmt"{name.toUpperAscii() = }" == "name.toUpperAscii() = HELLO" - doAssert fmt"{name.toUpperAscii( ) = }" == "name.toUpperAscii( ) = HELLO" - doAssert fmt"{ toUpperAscii( s = name ) = }" == " toUpperAscii( s = name ) = HELLO" - doAssert fmt"{ strutils.toUpperAscii( s = name ) = }" == " strutils.toUpperAscii( s = name ) = HELLO" - doAssert fmt"{age==12}" == "false" - doAssert fmt"{age!= 12}" == "true" - doAssert fmt"{age <= 12}" == "false" - for i in 1 .. 10: - doAssert fmt"{age.float =: .2f}" == "age.float = 21.00" - doAssert fmt"{age.float() =:.3f}" == "age.float() =21.000" - doAssert fmt"{float age= :.3f}" == "float age= 21.000" - doAssert fmt"{12 == int(`!=`(age, 12))}" == "false" - doAssert fmt"{0==1}" == "false" +doAssert fmt"{msg=}{'\n'}" == "msg=hello\n" +doAssert fmt("{msg=}\n") == "msg=hello\n" +doAssert "{msg=}\n".fmt == "msg=hello\n" - block: # It is space sensitive. - let x = "12" - doAssert fmt"{x=:}" == "x=12" - doAssert fmt"{x=}" == "x=12" - doAssert fmt"{x =:}" == "x =12" - doAssert fmt"{x =}" == "x =12" - doAssert fmt"{x= :}" == "x= 12" - doAssert fmt"{x= }" == "x= 12" - doAssert fmt"{x = :}" == "x = 12" - doAssert fmt"{x = }" == "x = 12" - doAssert fmt"{x = :}" == "x = 12" - doAssert fmt"{x = }" == "x = 12" +doAssert &"""{"abc":>4}""" == " abc" +doAssert &"""{"abc":<4}""" == "abc " - block: - let x = "hello" - doAssert fmt"{x=}" == "x=hello" - doAssert fmt"{x =}" == "x =hello" +doAssert fmt"{-12345:08}" == "-0012345" +doAssert fmt"{-1:3}" == " -1" +doAssert fmt"{-1:03}" == "-01" +doAssert fmt"{16:#X}" == "0x10" - let y = 3.1415926 - doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}" - doAssert fmt"{y=}" == fmt"y={y}" - doAssert fmt"{y = : <8}" == fmt"y = 3.14159 " +doAssert fmt"{123.456}" == "123.456" +doAssert fmt"{123.456:>9.3f}" == " 123.456" +doAssert fmt"{123.456:9.3f}" == " 123.456" +doAssert fmt"{123.456:9.4f}" == " 123.4560" +doAssert fmt"{123.456:>9.0f}" == " 123." +doAssert fmt"{123.456:<9.4f}" == "123.4560 " - proc hello(a: string, b: float): int = 12 - template foo(a: string, b: float): int = 18 +doAssert fmt"{123.456:e}" == "1.234560e+02" +doAssert fmt"{123.456:>13e}" == " 1.234560e+02" +doAssert fmt"{123.456:13e}" == " 1.234560e+02" - doAssert fmt"{hello(x, y)=}" == "hello(x, y)=12" - doAssert fmt"{hello(x, y) =}" == "hello(x, y) =12" - doAssert fmt"{hello(x, y)= }" == "hello(x, y)= 12" - doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12" - doAssert fmt"{hello x, y=}" == "hello x, y=12" - doAssert fmt"{hello x, y =}" == "hello x, y =12" - doAssert fmt"{hello x, y= }" == "hello x, y= 12" - doAssert fmt"{hello x, y = }" == "hello x, y = 12" +doAssert &"""{"abc"=:>4}""" == "\"abc\"= abc" +doAssert &"""{"abc"=:<4}""" == "\"abc\"=abc " - doAssert fmt"{x.hello(y)=}" == "x.hello(y)=12" - doAssert fmt"{x.hello(y) =}" == "x.hello(y) =12" - doAssert fmt"{x.hello(y)= }" == "x.hello(y)= 12" - doAssert fmt"{x.hello(y) = }" == "x.hello(y) = 12" +doAssert fmt"{-12345=:08}" == "-12345=-0012345" +doAssert fmt"{-1=:3}" == "-1= -1" +doAssert fmt"{-1=:03}" == "-1=-01" +doAssert fmt"{16=:#X}" == "16=0x10" - doAssert fmt"{foo(x, y)=}" == "foo(x, y)=18" - doAssert fmt"{foo(x, y) =}" == "foo(x, y) =18" - doAssert fmt"{foo(x, y)= }" == "foo(x, y)= 18" - doAssert fmt"{foo(x, y) = }" == "foo(x, y) = 18" +doAssert fmt"{123.456=}" == "123.456=123.456" +doAssert fmt"{123.456=:>9.3f}" == "123.456= 123.456" +doAssert fmt"{123.456=:9.3f}" == "123.456= 123.456" +doAssert fmt"{123.456=:9.4f}" == "123.456= 123.4560" +doAssert fmt"{123.456=:>9.0f}" == "123.456= 123." +doAssert fmt"{123.456=:<9.4f}" == "123.456=123.4560 " - doAssert fmt"{x.foo(y)=}" == "x.foo(y)=18" - doAssert fmt"{x.foo(y) =}" == "x.foo(y) =18" - doAssert fmt"{x.foo(y)= }" == "x.foo(y)= 18" - doAssert fmt"{x.foo(y) = }" == "x.foo(y) = 18" +doAssert fmt"{123.456=:e}" == "123.456=1.234560e+02" +doAssert fmt"{123.456=:>13e}" == "123.456= 1.234560e+02" +doAssert fmt"{123.456=:13e}" == "123.456= 1.234560e+02" - block: - template check(actual, expected: string) = - doAssert actual == expected +## tests for debug format string +block: + var name = "hello" + let age = 21 + const hobby = "swim" + doAssert fmt"{age*9 + 16=}" == "age*9 + 16=205" + doAssert &"name: {name =}\nage: { age =: >7}\nhobby: { hobby= : 8}" == + "name: name =hello\nage: age = 21\nhobby: hobby= swim " + doAssert fmt"{age == 12}" == "false" + doAssert fmt"{name.toUpperAscii() = }" == "name.toUpperAscii() = HELLO" + doAssert fmt"{name.toUpperAscii( ) = }" == "name.toUpperAscii( ) = HELLO" + doAssert fmt"{ toUpperAscii( s = name ) = }" == " toUpperAscii( s = name ) = HELLO" + doAssert fmt"{ strutils.toUpperAscii( s = name ) = }" == " strutils.toUpperAscii( s = name ) = HELLO" + doAssert fmt"{age==12}" == "false" + doAssert fmt"{age!= 12}" == "true" + doAssert fmt"{age <= 12}" == "false" + for i in 1 .. 10: + doAssert fmt"{age.float =: .2f}" == "age.float = 21.00" + doAssert fmt"{age.float() =:.3f}" == "age.float() =21.000" + doAssert fmt"{float age= :.3f}" == "float age= 21.000" + doAssert fmt"{12 == int(`!=`(age, 12))}" == "false" + doAssert fmt"{0==1}" == "false" - # Basic tests - let s = "string" - check &"{0} {s}", "0 string" - check &"{s[0..2].toUpperAscii}", "STR" - check &"{-10:04}", "-010" - check &"{-10:<04}", "-010" - check &"{-10:>04}", "-010" - check &"0x{10:02X}", "0x0A" - check &"{10:#04X}", "0x0A" +# It is space sensitive. +block: + let x = "12" + doAssert fmt"{x=:}" == "x=12" + doAssert fmt"{x=}" == "x=12" + doAssert fmt"{x =:}" == "x =12" + doAssert fmt"{x =}" == "x =12" + doAssert fmt"{x= :}" == "x= 12" + doAssert fmt"{x= }" == "x= 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" - check &"""{"test":#>5}""", "#test" - check &"""{"test":>5}""", " test" +block: + let x = "hello" + doAssert fmt"{x=}" == "x=hello" + doAssert fmt"{x =}" == "x =hello" - check &"""{"test":#^7}""", "#test##" - check &"""{"test": <5}""", "test " - check &"""{"test":<5}""", "test " - check &"{1f:.3f}", "1.000" - check &"Hello, {s}!", "Hello, string!" + let y = 3.1415926 + doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}" + doAssert fmt"{y=}" == fmt"y={y}" + doAssert fmt"{y = : <8}" == fmt"y = 3.14159 " - # Tests for identifiers without parenthesis - check &"{s} works{s}", "string worksstring" - check &"{s:>7}", " string" - doAssert(not compiles(&"{s_works}")) # parsed as identifier `s_works` + proc hello(a: string, b: float): int = 12 + template foo(a: string, b: float): int = 18 - # Misc general tests - check &"{{}}", "{}" - check &"{0}%", "0%" - check &"{0}%asdf", "0%asdf" - check &("\n{\"\\n\"}\n"), "\n\n\n" - check &"""{"abc"}s""", "abcs" + doAssert fmt"{hello(x, y)=}" == "hello(x, y)=12" + doAssert fmt"{hello(x, y) =}" == "hello(x, y) =12" + doAssert fmt"{hello(x, y)= }" == "hello(x, y)= 12" + doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12" - # String tests - check &"""{"abc"}""", "abc" - check &"""{"abc":>4}""", " abc" - check &"""{"abc":<4}""", "abc " - check &"""{"":>4}""", " " - check &"""{"":<4}""", " " + doAssert fmt"{hello x, y=}" == "hello x, y=12" + doAssert fmt"{hello x, y =}" == "hello x, y =12" + doAssert fmt"{hello x, y= }" == "hello x, y= 12" + doAssert fmt"{hello x, y = }" == "hello x, y = 12" - # Int tests - check &"{12345}", "12345" - check &"{ - 12345}", "-12345" - check &"{12345:6}", " 12345" - check &"{12345:>6}", " 12345" - check &"{12345:4}", "12345" - check &"{12345:08}", "00012345" - check &"{-12345:08}", "-0012345" - check &"{0:0}", "0" - check &"{0:02}", "00" - check &"{-1:3}", " -1" - check &"{-1:03}", "-01" - check &"{10}", "10" - check &"{16:#X}", "0x10" - check &"{16:^#7X}", " 0x10 " - check &"{16:^+#7X}", " +0x10 " + doAssert fmt"{x.hello(y)=}" == "x.hello(y)=12" + doAssert fmt"{x.hello(y) =}" == "x.hello(y) =12" + doAssert fmt"{x.hello(y)= }" == "x.hello(y)= 12" + doAssert fmt"{x.hello(y) = }" == "x.hello(y) = 12" - # Hex tests - check &"{0:x}", "0" - check &"{-0:x}", "0" - check &"{255:x}", "ff" - check &"{255:X}", "FF" - check &"{-255:x}", "-ff" - check &"{-255:X}", "-FF" - check &"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe" - check &"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe" - check &"{255:4x}", " ff" - check &"{255:04x}", "00ff" - check &"{-255:4x}", " -ff" - check &"{-255:04x}", "-0ff" + doAssert fmt"{foo(x, y)=}" == "foo(x, y)=18" + doAssert fmt"{foo(x, y) =}" == "foo(x, y) =18" + doAssert fmt"{foo(x, y)= }" == "foo(x, y)= 18" + doAssert fmt"{foo(x, y) = }" == "foo(x, y) = 18" - # Float tests - check &"{123.456}", "123.456" - check &"{-123.456}", "-123.456" - check &"{123.456:.3f}", "123.456" - check &"{123.456:+.3f}", "+123.456" - check &"{-123.456:+.3f}", "-123.456" - check &"{-123.456:.3f}", "-123.456" - check &"{123.456:1g}", "123.456" - check &"{123.456:.1f}", "123.5" - check &"{123.456:.0f}", "123." - check &"{123.456:>9.3f}", " 123.456" - check &"{123.456:9.3f}", " 123.456" - check &"{123.456:>9.4f}", " 123.4560" - check &"{123.456:>9.0f}", " 123." - check &"{123.456:<9.4f}", "123.4560 " + doAssert fmt"{x.foo(y)=}" == "x.foo(y)=18" + doAssert fmt"{x.foo(y) =}" == "x.foo(y) =18" + doAssert fmt"{x.foo(y)= }" == "x.foo(y)= 18" + doAssert fmt"{x.foo(y) = }" == "x.foo(y) = 18" - # Float (scientific) tests - check &"{123.456:e}", "1.234560e+02" - check &"{123.456:>13e}", " 1.234560e+02" - check &"{123.456:<13e}", "1.234560e+02 " - check &"{123.456:.1e}", "1.2e+02" - check &"{123.456:.2e}", "1.23e+02" - check &"{123.456:.3e}", "1.235e+02" +block: + template check(actual, expected: string) = + doAssert actual == expected - # Note: times.format adheres to the format protocol. Test that this - # works: - when nimvm: - discard - else: - var dt = dateTime(2000, mJan, 01, 00, 00, 00) - check &"{dt:yyyy-MM-dd}", "2000-01-01" + # Basic tests + let s = "string" + check &"{0} {s}", "0 string" + check &"{s[0..2].toUpperAscii}", "STR" + check &"{-10:04}", "-010" + check &"{-10:<04}", "-010" + check &"{-10:>04}", "-010" + check &"0x{10:02X}", "0x0A" - var tm = fromUnix(0) - discard &"{tm}" + check &"{10:#04X}", "0x0A" - var noww = now() - check &"{noww}", $noww + check &"""{"test":#>5}""", "#test" + check &"""{"test":>5}""", " test" - # Unicode string tests - check &"""{"αβγ"}""", "αβγ" - check &"""{"αβγ":>5}""", " αβγ" - check &"""{"αβγ":<5}""", "αβγ " - check &"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈" - check &"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 " - # Invalid unicode sequences should be handled as plain strings. - # Invalid examples taken from: https://stackoverflow.com/a/3886015/1804173 - let invalidUtf8 = [ - "\xc3\x28", "\xa0\xa1", - "\xe2\x28\xa1", "\xe2\x82\x28", - "\xf0\x28\x8c\xbc", "\xf0\x90\x28\xbc", "\xf0\x28\x8c\x28" - ] - for s in invalidUtf8: - check &"{s:>5}", repeat(" ", 5-s.len) & s + check &"""{"test":#^7}""", "#test##" - # bug #11089 - let flfoo: float = 1.0 - check &"{flfoo}", "1.0" + check &"""{"test": <5}""", "test " + check &"""{"test":<5}""", "test " + check &"{1f:.3f}", "1.000" + check &"Hello, {s}!", "Hello, string!" - # bug #11092 - check &"{high(int64)}", "9223372036854775807" - check &"{low(int64)}", "-9223372036854775808" + # Tests for identifiers without parenthesis + check &"{s} works{s}", "string worksstring" + check &"{s:>7}", " string" + doAssert(not compiles(&"{s_works}")) # parsed as identifier `s_works` - doAssert fmt"{'a'} {'b'}" == "a b" + # Misc general tests + check &"{{}}", "{}" + check &"{0}%", "0%" + check &"{0}%asdf", "0%asdf" + check &("\n{\"\\n\"}\n"), "\n\n\n" + check &"""{"abc"}s""", "abcs" - block: # test low(int64) - doAssert &"{low(int64):-}" == "-9223372036854775808" - block: #expressions plus formatting - doAssert fmt"{if true\: 123.456 else\: 0=:>9.3f}" == "if true: 123.456 else: 0= 123.456" - doAssert fmt"{(if true: 123.456 else: 0)=}" == "(if true: 123.456 else: 0)=123.456" - doAssert fmt"{if true\: 123.456 else\: 0=:9.3f}" == "if true: 123.456 else: 0= 123.456" - doAssert fmt"{(if true: 123.456 else: 0)=:9.4f}" == "(if true: 123.456 else: 0)= 123.4560" - doAssert fmt"{(if true: 123.456 else: 0)=:>9.0f}" == "(if true: 123.456 else: 0)= 123." - doAssert fmt"{if true\: 123.456 else\: 0=:<9.4f}" == "if true: 123.456 else: 0=123.4560 " + # String tests + check &"""{"abc"}""", "abc" + check &"""{"abc":>4}""", " abc" + check &"""{"abc":<4}""", "abc " + check &"""{"":>4}""", " " + check &"""{"":<4}""", " " - doAssert fmt"""{(case true - of false: 0.0 - of true: 123.456)=:e}""" == """(case true - of false: 0.0 - of true: 123.456)=1.234560e+02""" + # Int tests + check &"{12345}", "12345" + check &"{ - 12345}", "-12345" + check &"{12345:6}", " 12345" + check &"{12345:>6}", " 12345" + check &"{12345:4}", "12345" + check &"{12345:08}", "00012345" + check &"{-12345:08}", "-0012345" + check &"{0:0}", "0" + check &"{0:02}", "00" + check &"{-1:3}", " -1" + check &"{-1:03}", "-01" + check &"{10}", "10" + check &"{16:#X}", "0x10" + check &"{16:^#7X}", " 0x10 " + check &"{16:^+#7X}", " +0x10 " - doAssert fmt"""{block\: - var res = 0.000123456 - for _ in 0..5\: - res *= 10 - res=:>13e}""" == """block: - var res = 0.000123456 - for _ in 0..5: - res *= 10 - res= 1.234560e+02""" - #side effects - var x = 5 - doAssert fmt"{(x=7;123.456)=:13e}" == "(x=7;123.456)= 1.234560e+02" - doAssert x==7 - block: #curly bracket expressions and tuples - proc formatValue(result: var string; value:Table|bool|JsonNode; specifier:string) = result.add $value + # Hex tests + check &"{0:x}", "0" + check &"{-0:x}", "0" + check &"{255:x}", "ff" + check &"{255:X}", "FF" + check &"{-255:x}", "-ff" + check &"{-255:X}", "-FF" + check &"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe" + check &"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe" + check &"{255:4x}", " ff" + check &"{255:04x}", "00ff" + check &"{-255:4x}", " -ff" + check &"{-255:04x}", "-0ff" - doAssert fmt"""{\{"a"\:1,"b"\:2\}.toTable() = }""" == """{"a":1,"b":2}.toTable() = {"a": 1, "b": 2}""" - doAssert fmt"""{(\{3: (1,"hi",0.9),4: (4,"lo",1.1)\}).toTable()}""" == """{3: (1, "hi", 0.9), 4: (4, "lo", 1.1)}""" - doAssert fmt"""{ (%* \{"name": "Isaac", "books": ["Robot Dreams"]\}) }""" == """{"name":"Isaac","books":["Robot Dreams"]}""" - doAssert """%( \%\* {"name": "Isaac"})*""".fmt('%','*') == """{"name":"Isaac"}""" - block: #parens in quotes that fool my syntax highlighter - doAssert fmt"{(if true: ')' else: '(')}" == ")" - doAssert fmt"{(if true: ']' else: ')')}" == "]" - doAssert fmt"""{(if true: "\")\"" else: "\"(")}""" == """")"""" - doAssert &"""{(if true: "\")" else: "")}""" == "\")" - doAssert &"{(if true: \"\\\")\" else: \"\")}" == "\")" - doAssert fmt"""{(if true: "')" else: "")}""" == "')" - doAssert fmt"""{(if true: "'" & "'" & ')' else: "")}""" == "'')" - doAssert &"""{(if true: "'" & "'" & ')' else: "")}""" == "'')" - doAssert &"{(if true: \"\'\" & \"'\" & ')' else: \"\")}" == "'')" - doAssert fmt"""{(if true: "'" & ')' else: "")}""" == "')" + # Float tests + check &"{123.456}", "123.456" + check &"{-123.456}", "-123.456" + check &"{123.456:.3f}", "123.456" + check &"{123.456:+.3f}", "+123.456" + check &"{-123.456:+.3f}", "-123.456" + check &"{-123.456:.3f}", "-123.456" + check &"{123.456:1g}", "123.456" + check &"{123.456:.1f}", "123.5" + check &"{123.456:.0f}", "123." + check &"{123.456:>9.3f}", " 123.456" + check &"{123.456:9.3f}", " 123.456" + check &"{123.456:>9.4f}", " 123.4560" + check &"{123.456:>9.0f}", " 123." + check &"{123.456:<9.4f}", "123.4560 " -static: main() -main() + # Float (scientific) tests + check &"{123.456:e}", "1.234560e+02" + check &"{123.456:>13e}", " 1.234560e+02" + check &"{123.456:<13e}", "1.234560e+02 " + check &"{123.456:.1e}", "1.2e+02" + check &"{123.456:.2e}", "1.23e+02" + check &"{123.456:.3e}", "1.235e+02" + + # Note: times.format adheres to the format protocol. Test that this + # works: + + var dt = initDateTime(01, mJan, 2000, 00, 00, 00) + check &"{dt:yyyy-MM-dd}", "2000-01-01" + + var tm = fromUnix(0) + discard &"{tm}" + + var noww = now() + check &"{noww}", $noww + + # Unicode string tests + check &"""{"αβγ"}""", "αβγ" + check &"""{"αβγ":>5}""", " αβγ" + check &"""{"αβγ":<5}""", "αβγ " + check &"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈" + check &"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 " + # Invalid unicode sequences should be handled as plain strings. + # Invalid examples taken from: https://stackoverflow.com/a/3886015/1804173 + let invalidUtf8 = [ + "\xc3\x28", "\xa0\xa1", + "\xe2\x28\xa1", "\xe2\x82\x28", + "\xf0\x28\x8c\xbc", "\xf0\x90\x28\xbc", "\xf0\x28\x8c\x28" + ] + for s in invalidUtf8: + check &"{s:>5}", repeat(" ", 5-s.len) & s + + # bug #11089 + let flfoo: float = 1.0 + check &"{flfoo}", "1.0" + + # bug #11092 + check &"{high(int64)}", "9223372036854775807" + check &"{low(int64)}", "-9223372036854775808" + + doAssert fmt"{'a'} {'b'}" == "a b" \ No newline at end of file diff --git a/tests/stdlib/tstrimpl.nim b/tests/stdlib/tstrimpl.nim deleted file mode 100644 index d12150f8e..000000000 --- a/tests/stdlib/tstrimpl.nim +++ /dev/null @@ -1,8 +0,0 @@ -import std/private/strimpl - -import std/assertions - -doAssert find(cstring"Hello Nim", cstring"Nim") == 6 -doAssert find(cstring"Hello Nim", cstring"N") == 6 -doAssert find(cstring"Hello Nim", cstring"I") == -1 -doAssert find(cstring"Hello Nim", cstring"O") == -1 diff --git a/tests/stdlib/tstring.nim b/tests/stdlib/tstring.nim index 2e8fe09e1..4d5a15940 100644 --- a/tests/stdlib/tstring.nim +++ b/tests/stdlib/tstring.nim @@ -2,122 +2,105 @@ discard """ targets: "c cpp js" """ -from std/sequtils import toSeq, map -from std/sugar import `=>` -import std/assertions +const characters = "abcdefghijklmnopqrstuvwxyz" +const numbers = "1234567890" + +proc test_string_slice() = + # test "slice of length == len(characters)": + # replace characters completely by numbers + var s: string + s = characters + s[0..^1] = numbers + doAssert s == numbers + + # test "slice of length > len(numbers)": + # replace characters by slice of same length + s = characters + s[1..16] = numbers + doAssert s == "a1234567890rstuvwxyz" + + # test "slice of length == len(numbers)": + # replace characters by slice of same length + s = characters + s[1..10] = numbers + doAssert s == "a1234567890lmnopqrstuvwxyz" + + # test "slice of length < len(numbers)": + # replace slice of length. and insert remaining chars + s = characters + s[1..4] = numbers + doAssert s == "a1234567890fghijklmnopqrstuvwxyz" + + # test "slice of length == 1": + # replace first character. and insert remaining 9 chars + s = characters + s[1..1] = numbers + doAssert s == "a1234567890cdefghijklmnopqrstuvwxyz" + + # test "slice of length == 0": + # insert chars at slice start index + s = characters + s[2..1] = numbers + doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz" + + # test "slice of negative length": + # same as slice of zero length + s = characters + s[2..0] = numbers + doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz" + + when nimvm: + discard + else: + # bug #6223 + doAssertRaises(IndexDefect): + discard s[0..999] + + +proc test_string_cmp() = + let world = "hello\0world" + let earth = "hello\0earth" + let short = "hello\0" + let hello = "hello" + let goodbye = "goodbye" + + doAssert world == world + doAssert world != earth + doAssert world != short + doAssert world != hello + doAssert world != goodbye + + doAssert cmp(world, world) == 0 + doAssert cmp(world, earth) > 0 + doAssert cmp(world, short) > 0 + doAssert cmp(world, hello) > 0 + doAssert cmp(world, goodbye) > 0 + + +#-------------------------- +# bug #7816 +import sugar +import sequtils proc tester[T](x: T) = let test = toSeq(0..4).map(i => newSeq[int]()) doAssert $test == "@[@[], @[], @[], @[], @[]]" + + +# #14497 func reverse*(a: string): string = result = a for i in 0 ..< a.len div 2: swap(result[i], result[^(i + 1)]) + proc main() = - block: # .. - const - characters = "abcdefghijklmnopqrstuvwxyz" - numbers = "1234567890" + test_string_slice() + test_string_cmp() - # test "slice of length == len(characters)": - # replace characters completely by numbers - var s: string - s = characters - s[0..^1] = numbers - doAssert s == numbers - - # test "slice of length > len(numbers)": - # replace characters by slice of same length - s = characters - s[1..16] = numbers - doAssert s == "a1234567890rstuvwxyz" - - # test "slice of length == len(numbers)": - # replace characters by slice of same length - s = characters - s[1..10] = numbers - doAssert s == "a1234567890lmnopqrstuvwxyz" - - # test "slice of length < len(numbers)": - # replace slice of length. and insert remaining chars - s = characters - s[1..4] = numbers - doAssert s == "a1234567890fghijklmnopqrstuvwxyz" - - # test "slice of length == 1": - # replace first character. and insert remaining 9 chars - s = characters - s[1..1] = numbers - doAssert s == "a1234567890cdefghijklmnopqrstuvwxyz" - - # test "slice of length == 0": - # insert chars at slice start index - s = characters - s[2..1] = numbers - doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz" - - # test "slice of negative length": - # same as slice of zero length - s = characters - s[2..0] = numbers - doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz" - - when nimvm: - discard - else: - # bug #6223 - doAssertRaises(IndexDefect): - discard s[0..999] - - block: # ==, cmp - let world = "hello\0world" - let earth = "hello\0earth" - let short = "hello\0" - let hello = "hello" - let goodbye = "goodbye" - - doAssert world == world - doAssert world != earth - doAssert world != short - doAssert world != hello - doAssert world != goodbye - - doAssert cmp(world, world) == 0 - doAssert cmp(world, earth) > 0 - doAssert cmp(world, short) > 0 - doAssert cmp(world, hello) > 0 - doAssert cmp(world, goodbye) > 0 - - block: # bug #7816 - tester(1) - - block: # bug #14497, reverse - doAssert reverse("hello") == "olleh" - - block: # len, high - var a = "ab\0cd" - var b = a.cstring - doAssert a.len == 5 - block: # bug #16405 - when defined(js): - when nimvm: doAssert b.len == 2 - else: doAssert b.len == 5 - else: doAssert b.len == 2 - - doAssert a.high == a.len - 1 - doAssert b.high == b.len - 1 - - doAssert "".len == 0 - doAssert "".high == -1 - doAssert "".cstring.len == 0 - doAssert "".cstring.high == -1 - - block: # bug #16674 - var c: cstring = nil - doAssert c.len == 0 - doAssert c.high == -1 + tester(1) + doAssert reverse("hello") == "olleh" static: main() main() diff --git a/tests/stdlib/tstrmiscs.nim b/tests/stdlib/tstrmiscs.nim index 76b14d27a..2e9131ff8 100644 --- a/tests/stdlib/tstrmiscs.nim +++ b/tests/stdlib/tstrmiscs.nim @@ -1,5 +1,4 @@ -import std/strmisc -import std/assertions +import strmisc doAssert expandTabs("\t", 4) == " " diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim index e30c86279..d443c5dda 100644 --- a/tests/stdlib/tstrscans.nim +++ b/tests/stdlib/tstrscans.nim @@ -2,7 +2,7 @@ discard """ output: "" """ -import std/[strscans, strutils, assertions] +import strscans, strutils block ParsePasswd: proc parsePasswd(content: string): seq[string] = @@ -15,7 +15,7 @@ block ParsePasswd: else: break - const etcPasswd = """root:x:0:0:root:/root:/bin/bash + const etc_passwd = """root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh @@ -23,7 +23,7 @@ nobody:x:65534:65534:nobody:/nonexistent:/bin/sh messagebus:x:103:107::/var/run/dbus:/bin/false """ - const parsedEtcPasswd = @[ + const parsed_etc_passwd = @[ "root:x:0:0:root:/root:/bin/bash", "daemon:x:1:1:daemon:/usr/sbin:/bin/sh", "bin:x:2:2:bin:/bin:/bin/sh", @@ -31,7 +31,7 @@ messagebus:x:103:107::/var/run/dbus:/bin/false "nobody:x:65534:65534:nobody:/nonexistent:/bin/sh", "messagebus:x:103:107::/var/run/dbus:/bin/false", ] - doAssert etcPasswd.parsePasswd == parsedEtcPasswd + doAssert etc_passwd.parsePasswd == parsed_etc_passwd block LastNot: var idx : int @@ -139,27 +139,27 @@ block: break var key, val: string - var intVal: int - var floatVal: float - doAssert scanf("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f", key, val, intVal, floatVal) + var intval: int + var floatval: float + doAssert scanf("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f", key, val, intval, floatVal) doAssert key == "abc" doAssert val == "xyz" - doAssert intVal == 89 + doAssert intval == 89 doAssert floatVal == 33.25 - var binVal: int - var octVal: int - var hexVal: int - doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binVal, octVal, hexVal) - doAssert binVal == 0b0101 - doAssert octVal == 0o1234 - doAssert hexVal == 0xabcd + var binval: int + var octval: int + var hexval: int + doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval) + doAssert binval == 0b0101 + doAssert octval == 0o1234 + doAssert hexval == 0xabcd - let xx = scanf("$abc", "$$$i", intVal) + let xx = scanf("$abc", "$$$i", intval) doAssert xx == false - let xx2 = scanf("$1234", "$$$i", intVal) + let xx2 = scanf("$1234", "$$$i", intval) doAssert xx2 let yy = scanf(";.--Breakpoint00 [output]", @@ -246,32 +246,24 @@ block: result = 0 while start+result < input.len and input[start+result] in seps: inc result - type - ScanRetType = tuple - success: bool - lo: int - hi: int - ch: char - word: string - var res = 0 for line in input.splitLines: - let ret: ScanRetType = scanTuple(line, "$i-$i $c: $w") - if ret.success: + let (success, lo, hi, c ,w) = scanTuple(line, "$i-$i $c: $w") + if success: inc res doAssert res == 4 - let (_, key, val, intVal, floatVal) = scanTuple("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f") + let (_, key, val, intval, floatVal) = scanTuple("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f") doAssert key == "abc" doAssert val == "xyz" - doAssert intVal == 89 + doAssert intval == 89 doAssert floatVal == 33.25 - let (_, binVal, octVal, hexVal) = scanTuple("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binVal, octVal, hexVal) - doAssert binVal == 0b0101 - doAssert octVal == 0o1234 - doAssert hexVal == 0xabcd + let (_, binVal, octVal, hexVal) = scanTuple("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval) + doAssert binval == 0b0101 + doAssert octval == 0o1234 + doAssert hexval == 0xabcd var (xx,_) = scanTuple("$abc", "$$$i") doAssert xx == false @@ -280,9 +272,9 @@ block: let (xx2, _) = block: scanTuple("$1234", "$$$i") doAssert xx2 - var (yy, intVal2, key2) = scanTuple(";.--Breakpoint00 [output]", + var (yy, intval2, key2) = scanTuple(";.--Breakpoint00 [output]", "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.", int) doAssert yy doAssert key2 == "output" - doAssert intVal2 == 13 + doAssert intVal2 == 13 \ No newline at end of file diff --git a/tests/stdlib/tstrset.nim b/tests/stdlib/tstrset.nim index f0cf5cbe4..1b253f862 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 {.inheritable.} = object + TRadixNode = object {.inheritable.} kind: TRadixNodeKind TRadixNodeLinear = object of TRadixNode - len: uint8 + len: int8 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..int(x.len)-1: + for j in 0..ze(x.len)-1: if x.keys[j] == s[i]: if s[i] == '\0': return r r = x.vals[j] @@ -50,7 +50,7 @@ proc search(r: PRadixNode, s: string): PRadixNode = proc contains*(r: PRadixNode, s: string): bool = return search(r, s) != nil -proc testOrIncl*(r: var PRadixNode, s: string): bool = +proc testOrincl*(r: var PRadixNode, s: string): bool = nil proc incl*(r: var PRadixNode, s: string) = discard testOrIncl(r, s) @@ -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..int(x.len)-1: + for i in 0..ze(x.len)-1: if x.keys[i] == '\0': - swap(x.keys[i], x.keys[int(x.len)-1]) + swap(x.keys[i], x.keys[ze(x.len)-1]) dec(x.len) break diff --git a/tests/stdlib/tstrtabs.nim b/tests/stdlib/tstrtabs.nim index 036287bfd..f629c183c 100644 --- a/tests/stdlib/tstrtabs.nim +++ b/tests/stdlib/tstrtabs.nim @@ -88,7 +88,7 @@ value1 = value2 ''' """ -import std/[strtabs, assertions, syncio] +import strtabs var tab = newStringTable({"key1": "val1", "key2": "val2"}, modeStyleInsensitive) diff --git a/tests/stdlib/tstrtabs.nims b/tests/stdlib/tstrtabs.nims index 3563ad0ad..c8ed4ac40 100644 --- a/tests/stdlib/tstrtabs.nims +++ b/tests/stdlib/tstrtabs.nims @@ -1,4 +1,4 @@ -import std/[strtabs, assertions] +import strtabs static: let t = {"name": "John", "city": "Monaco"}.newStringTable diff --git a/tests/stdlib/tstrtabs2.nim b/tests/stdlib/tstrtabs2.nim deleted file mode 100644 index f055b5d33..000000000 --- a/tests/stdlib/tstrtabs2.nim +++ /dev/null @@ -1,16 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -import std/strtabs -import std/assertions - -macro m = - var t = {"name": "John"}.newStringTable - doAssert t["name"] == "John" - -block: - var t = {"name": "John"}.newStringTable - doAssert t["name"] == "John" - -m() diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 4e88197ff..a6248d1e3 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -3,8 +3,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) = @@ -13,6 +12,10 @@ template rejectParse(e) = raise newException(AssertionDefect, "This was supposed to fail: $#!" % astToStr(e)) except ValueError: discard +template disableVm(body) = + when nimvm: discard + else: body + template main() = block: # strip doAssert strip(" ha ") == "ha" @@ -199,30 +202,7 @@ template main() = s.removePrefix("") doAssert s == "\r\n\r\nhello" - block: # delete(slice) - var s = "0123456789ABCDEFGH" - delete(s, 4 .. 5) - doAssert s == "01236789ABCDEFGH" - delete(s, s.len-1 .. s.len-1) - doAssert s == "01236789ABCDEFG" - delete(s, 0..0) - doAssert s == "1236789ABCDEFG" - s = "" - doAssertRaises(IndexDefect): delete(s, 0..0) - doAssert s == "" - s = "abc" - doAssertRaises(IndexDefect): delete(s, -1 .. -2) - doAssertRaises(IndexDefect): delete(s, 2..3) - doAssertRaises(IndexDefect): delete(s, 3..2) - delete(s, 2..2) - doAssert s == "ab" - delete(s, 1..0) - doAssert s == "ab" - delete(s, 0..0) - doAssert s == "b" - - block: # delete(first, last) - {.push warning[deprecated]:off.} + block: # delete var s = "0123456789ABCDEFGH" delete(s, 4, 5) doAssert s == "01236789ABCDEFGH" @@ -230,88 +210,20 @@ template main() = doAssert s == "01236789ABCDEFG" delete(s, 0, 0) doAssert s == "1236789ABCDEFG" - {.pop.} block: # find - 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" - doAssert haystack.len == 14 - - # only last argument - doAssert haystack.find("ABC") == 0 - doAssert haystack.find("ABC", last=13) == 0 # after the second ABC - doAssert haystack.find("ABC", last=5) == 0 # before the second ABC - - # only start argument - doAssert haystack.find("ABC", start=0) == 0 - doAssert haystack.find("ABC", start=1) == 9 - doAssert haystack.find("ABC", start=9) == 9 - doAssert haystack.find("ABC", start=10) == -1 - - # both start and last arguments - doAssert haystack.find("ABC", start=0, last=14) == 0 - doAssert haystack.find("ABC", start=0, last=13) == 0 - doAssert haystack.find("ABC", start=0, last=12) == 0 - doAssert haystack.find("ABC", start=1, last=13) == 9 - doAssert haystack.find("ABC", start=1, last=12) == 9 - doAssert haystack.find("ABC", start=1, last=11) == 9 - doAssert haystack.find("ABC", start=1, last=10) == -1 - - doAssert "".find("/") == -1 - doAssert "/".find("/") == 0 - doAssert "/".find("//") == -1 - doAssert "///".find("//", start=3) == -1 - - # searching for empty string - doAssert "".find("") == 0 - doAssert "abc".find("") == 0 - doAssert "abc".find("", start=1) == 1 - doAssert "abc".find("", start=2) == 2 - doAssert "abc".find("", start=3) == 3 - doAssert "abc".find("", start=4) == -1 - doAssert "abc".find("", start=400) == -1 - doAssert "abc".find("", start=1, last=3) == 1 - doAssert "abc".find("", start=1, last=2) == 1 - doAssert "abc".find("", start=1, last=1) == 1 - doAssert "abc".find("", start=1, last=0) == 1 - doAssert "abc".find("", start=1, last = -1) == 1 - - # when last <= start, searching for non-empty string - block: - 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=2, last=last) == -1 - block: - let last: int = 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=2, last=last) == -1 - block: - let last: int = 1 - 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=2, last=last) == -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") == 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 block: # rfind doAssert "0123456789ABCDEFGAH".rfind('A') == 17 @@ -335,70 +247,6 @@ template main() = doAssert "/1/2/3".rfind('/', last=1) == 0 doAssert "/1/2/3".rfind('0') == -1 - block: - const haystack: string = "ABCABABABABCAB" - doAssert haystack.len == 14 - doAssert haystack.rfind("ABC") == 9 - doAssert haystack.rfind("ABC", last=13) == 9 - doAssert haystack.rfind("ABC", last=12) == 9 - doAssert haystack.rfind("ABC", last=11) == 9 - doAssert haystack.rfind("ABC", last=10) == 0 - - doAssert haystack.rfind("ABC", start=0) == 9 - doAssert haystack.rfind("ABC", start=1) == 9 - doAssert haystack.rfind("ABC", start=9) == 9 - doAssert haystack.rfind("ABC", start=10) == -1 - - doAssert haystack.rfind("ABC", start=0, last=13) == 9 - doAssert haystack.rfind("ABC", start=0, last=12) == 9 - doAssert haystack.rfind("ABC", start=0, last=11) == 9 - doAssert haystack.rfind("ABC", start=0, last=10) == 0 - doAssert haystack.rfind("ABC", start=1, last=10) == -1 - - doAssert "".rfind("/") == -1 - doAssert "/".rfind("/") == 0 - doAssert "/".rfind("//") == -1 - doAssert "///".rfind("//", start=3) == -1 - - # searching for empty string - 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: - let last: int = -1 - doAssert "abcd".rfind("ab", start=0, last=last) == 0 - doAssert "abcd".rfind("ab", start=1, last=last) == -1 - doAssert "abcd".rfind("bc", start=1, last=last) == 1 - doAssert "abcd".rfind("bc", start=2, last=last) == -1 - block: - let last: int = 0 - doAssert "abcd".rfind("ab", start=0, last=last) == -1 - doAssert "abcd".rfind("ab", start=1, last=last) == -1 - doAssert "abcd".rfind("bc", start=1, last=last) == -1 - doAssert "abcd".rfind("bc", start=2, last=last) == -1 - block: - let last: int = 1 - doAssert "abcd".rfind("ab", start=0, last=last) == 0 - doAssert "abcd".rfind("ab", start=1, last=last) == -1 - doAssert "abcd".rfind("bc", start=1, last=last) == -1 - doAssert "abcd".rfind("bc", start=2, last=last) == -1 - block: # trimZeros var x = "1200" x.trimZeros() @@ -433,9 +281,6 @@ template main() = x = "1e0" x.trimZeros() doAssert x == "1e0" - x = "1.23" - x.trimZeros() - doAssert x == "1.23" block: # countLines proc assertCountLines(s: string) = doAssert s.countLines == s.splitLines.len @@ -595,17 +440,6 @@ template main() = let g = parseEnum[Foo]("Bar", A) doAssert g == A - block: # bug #19463 - const CAMPAIGN_TABLE = "wikientries_campaign" - const CHARACTER_TABLE = "wikientries_character" - - type Tables = enum - a = CAMPAIGN_TABLE, - b = CHARACTER_TABLE, - - let myA = CAMPAIGN_TABLE - doAssert $parseEnum[Tables](myA) == "wikientries_campaign" - block: # check enum defined in block type Bar = enum @@ -864,17 +698,5 @@ bar doAssert s.endsWith('a') == false doAssert s.endsWith('\0') == false - block: # nimIdentNormalize - doAssert nimIdentNormalize("") == "" - doAssert nimIdentNormalize("foo") == "foo" - doAssert nimIdentNormalize("foo_bar") == "foobar" - 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 new file mode 100644 index 000000000..881817f90 --- /dev/null +++ b/tests/stdlib/tstrutils2.nim @@ -0,0 +1,32 @@ +import "$lib/.." / compiler/strutils2 + +block: # setLen + var a = "abc" + a.setLen 0 + a.setLen 3, isInit = false + doAssert a[1] == 'b' + a.setLen 0 + a.setLen 3, isInit = true + doAssert a[1] == '\0' + +block: # forceCopy + var a: string + a = "foo" + shallow(a) + var b: string + b = a + doAssert b[0].addr == a[0].addr + var c: string + c.forceCopy a + doAssert c == a + doAssert c[0].addr != a[0].addr + +block: # toLowerAscii + var a = "fooBAr" + a.toLowerAscii + doAssert a == "foobar" + +block: # dataPointer + var a: string + discard a.dataPointer + # doAssert a.dataPointer == nil # not guaranteed diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim index 1b629165a..2f79a9174 100644 --- a/tests/stdlib/tsugar.nim +++ b/tests/stdlib/tsugar.nim @@ -1,305 +1,110 @@ -discard """ - output: ''' -x + y = 30 -''' -""" -import std/[sugar, algorithm, random, sets, tables, strutils, sequtils] -import std/[syncio, assertions] +import sugar -type # for capture test, ref #20679 - FooCapture = ref object - x: int +block dup_with_field: + type + Foo = object + col, pos: int + name: string -template main() = - block: # `=>` - block: - let f1 = () => 42 - doAssert f1() == 42 + proc inc_col(foo: var Foo) = inc(foo.col) + proc inc_pos(foo: var Foo) = inc(foo.pos) + proc name_append(foo: var Foo, s: string) = foo.name &= s - let f2 = (x: int) => x + 1 - doAssert f2(42) == 43 + let a = Foo(col: 1, pos: 2, name: "foo") + block: + let b = a.dup(inc_col, inc_pos): + _.pos = 3 + name_append("bar") + inc_pos - let f3 = (x, y: int) => x + y - doAssert f3(1, 2) == 3 + doAssert(b == Foo(col: 2, pos: 4, name: "foobar")) - var x = 0 - let f4 = () => (x = 12) - f4() - doAssert x == 12 + block: + let b = a.dup(inc_col, pos = 3, name = "bar"): + name_append("bar") + inc_pos - let f5 = () => (discard) # simplest proc that returns void - f5() + doAssert(b == Foo(col: 2, pos: 4, name: "barbar")) - block: - proc call1(f: () -> int): int = f() - doAssert call1(() => 12) == 12 +import algorithm - proc call2(f: int -> int): int = f(42) - doAssert call2(x => x) == 42 - doAssert call2((x) => x) == 42 - doAssert call2((x: int) => x) == 42 +var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] +doAssert dup(a, sort(_)) == sorted(a) +doAssert a.dup(sort) == sorted(a) +#Chaining: +var aCopy = a +aCopy.insert(10) +doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy) - proc call3(f: (int, int) -> int): int = f(1, 2) - doAssert call3((x, y) => x + y) == 3 - doAssert call3((x, y: int) => x + y) == 3 - doAssert call3((x: int, y: int) => x + y) == 3 +import random - var a = 0 - proc call4(f: int -> void) = f(42) - call4((x: int) => (a = x)) - doAssert a == 42 +const b = @[0, 1, 2] +let c = b.dup shuffle() +doAssert b[0] == 0 +doAssert b[1] == 1 - proc call5(f: (int {.noSideEffect.} -> int)): int = f(42) - doAssert call5(x {.noSideEffect.} => x + 1) == 43 +#test collect +import sets, tables - block: # `->` - doAssert $(() -> int) == "proc (): int{.closure.}" - doAssert $(float -> int) == "proc (i0: float): int{.closure.}" - doAssert $((float) -> int) == "proc (i0: float): int{.closure.}" - doAssert $((float, bool) -> int) == "proc (i0: float, i1: bool): int{.closure.}" +let data = @["bird", "word"] # if this gets stuck in your head, its not my fault +doAssert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"] +doAssert collect(initTable(2), for (i, d) in data.pairs: {i: d}) == {0: "bird", + 1: "word"}.toTable +doAssert initHashSet.collect(for d in data.items: {d}) == data.toHashSet - doAssert $(() -> void) == "proc (){.closure.}" - doAssert $(float -> void) == "proc (i0: float){.closure.}" - doAssert $((float) -> void) == "proc (i0: float){.closure.}" - doAssert $((float, bool) -> void) == "proc (i0: float, i1: bool){.closure.}" +let x = collect(newSeqOfCap(4)): + for (i, d) in data.pairs: + if i mod 2 == 0: d +doAssert x == @["bird"] - doAssert $(() {.inline.} -> int) == "proc (): int{.inline.}" - doAssert $(float {.inline.} -> int) == "proc (i0: float): int{.inline.}" - doAssert $((float) {.inline.} -> int) == "proc (i0: float): int{.inline.}" - doAssert $((float, bool) {.inline.} -> int) == "proc (i0: float, i1: bool): int{.inline.}" - - block: # capture - var closure1: () -> int - for i in 0 .. 10: - if i == 5: - capture i: - closure1 = () => i - doAssert closure1() == 5 - - var closure2: () -> (int, int) - for i in 0 .. 10: - for j in 0 .. 10: - if i == 5 and j == 3: - capture i, j: - closure2 = () => (i, j) - doAssert closure2() == (5, 3) - - block: # bug #16967 - var s = newSeq[proc (): int](5) - {.push exportc.} - proc bar() = - for i in 0 ..< s.len: - let foo = i + 1 - capture foo: - s[i] = proc(): int = foo - {.pop.} - - bar() - - for i, p in s.pairs: - let foo = i + 1 - doAssert p() == foo - - block: # issue #20679 - # this should compile. Previously was broken as `var int` is an `nnkHiddenDeref` - # which was not handled correctly +# bug #12874 +let bug1 = collect( + newSeq, + for (i, d) in data.pairs:( block: - var x = 5 - var s1 = newSeq[proc (): int](2) - proc function(data: var int) = - for i in 0 ..< 2: - data = (i+1) * data - capture data: - s1[i] = proc(): int = data - function(x) - doAssert s1[0]() == 5 - doAssert s1[1]() == 10 - - - block: - var y = @[5, 10] - var s2 = newSeq[proc (): seq[int]](2) - proc functionS(data: var seq[int]) = - for i in 0 ..< 2: - data.add (i+1) * 5 - capture data: - s2[i] = proc(): seq[int] = data - functionS(y) - doAssert s2[0]() == @[5, 10, 5] - doAssert s2[1]() == @[5, 10, 5, 10] - - - template typeT(typ, val: untyped): untyped = - var x = val - var s = newSeq[proc (): typ](2) - - proc functionT[T](data: var T) = - for i in 0 ..< 2: - if i == 1: - data = default(T) - capture data: - s[i] = proc (): T = data - - functionT(x) - doAssert s[0]() == val - doAssert s[1]() == x # == default - doAssert s[1]() == default(typ) - - block: - var x = 1.1 - typeT(float, x) - block: - var x = "hello" - typeT(string, x) - block: - var f = FooCapture(x: 5) - typeT(FooCapture, f) - - block: # dup - block dup_with_field: - type - Foo = object - col, pos: int - name: string - - proc inc_col(foo: var Foo) = inc(foo.col) - proc inc_pos(foo: var Foo) = inc(foo.pos) - proc name_append(foo: var Foo, s: string) = foo.name &= s - - let a = Foo(col: 1, pos: 2, name: "foo") - block: - let b = a.dup(inc_col, inc_pos): - _.pos = 3 - name_append("bar") - inc_pos - - doAssert(b == Foo(col: 2, pos: 4, name: "foobar")) - - block: - let b = a.dup(inc_col, pos = 3, name = "bar"): - name_append("bar") - inc_pos - - doAssert(b == Foo(col: 2, pos: 4, name: "barbar")) - - block: - var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - doAssert dup(a, sort(_)) == sorted(a) - doAssert a.dup(sort) == sorted(a) - # Chaining: - var aCopy = a - aCopy.insert(10) - doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy) - - block: - when nimvm: discard - else: - const b = @[0, 1, 2] - discard b.dup shuffle() - doAssert b[0] == 0 - doAssert b[1] == 1 - - block: # collect - let data = @["bird", "word"] # if this gets stuck in your head, its not my fault - - doAssert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"] - doAssert collect(initTable(2), for (i, d) in data.pairs: {i: d}) == - {0: "bird", 1: "word"}.toTable - doAssert collect(initHashSet(), for d in data.items: {d}) == data.toHashSet - - block: - let x = collect(newSeqOfCap(4)): - for (i, d) in data.pairs: - if i mod 2 == 0: d - doAssert x == @["bird"] - - block: # bug #12874 - let bug = collect( - newSeq, - for (i, d) in data.pairs:( - block: - if i mod 2 == 0: - d - else: - d & d - ) + if i mod 2 == 0: + d + else: + d & d ) - doAssert bug == @["bird", "wordword"] +) +doAssert bug1 == @["bird", "wordword"] - block: - let y = collect(newSeq): - for (i, d) in data.pairs: - try: parseInt(d) except: 0 - doAssert y == @[0, 0] +import strutils +let y = collect(newSeq): + for (i, d) in data.pairs: + try: parseInt(d) except: 0 +doAssert y == @[0, 0] - block: - let z = collect(newSeq): - for (i, d) in data.pairs: - case d - of "bird": "word" - else: d - doAssert z == @["word", "word"] +let z = collect(newSeq): + for (i, d) in data.pairs: + case d + of "bird": "word" + else: d +doAssert z == @["word", "word"] - block: - proc tforum(): seq[int] = - collect(newSeq): - for y in 0..10: - if y mod 5 == 2: - for x in 0..y: - x - doAssert tforum() == @[0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7] +proc tforum = + let ans = collect(newSeq): + for y in 0..10: + if y mod 5 == 2: + for x in 0..y: + x +tforum() - block: - let x = collect: - for d in data.items: - when d is int: "word" - else: d - doAssert x == @["bird", "word"] +block: + let x = collect: + for d in data.items: + when d is int: "word" + else: d + doAssert x == @["bird", "word"] +doAssert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")] +doAssert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0] +doAssert collect(for (i, d) in pairs(data): {i: d}) == {1: "word", + 0: "bird"}.toTable +doAssert collect(for d in data.items: {d}) == data.toHashSet - block: - doAssert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")] - doAssert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0] - doAssert collect(for (i, d) in pairs(data): {i: d}) == - {1: "word", 0: "bird"}.toTable - doAssert collect(for d in data.items: {d}) == data.toHashSet - - block: # bug #14332 - template foo = - discard collect(newSeq, for i in 1..3: i) - foo() - -proc mainProc() = - block: # dump - # symbols in templates are gensym'd - let - x = 10 - y = 20 - dump(x + y) # x + y = 30 - - block: # dumpToString - template square(x): untyped = x * x - let x = 10 - doAssert dumpToString(square(x)) == "square(x): x * x = 100" - let s = dumpToString(doAssert 1+1 == 2) - doAssert "failedAssertImpl" in s - let s2 = dumpToString: - doAssertRaises(AssertionDefect): doAssert false - doAssert "except AssertionDefect" in s2 - - block: # bug #20704 - proc test() = - var xs, ys: seq[int] - for i in 0..5: - xs.add(i) - - xs.apply(d => ys.add(d)) - doAssert ys == @[0, 1, 2, 3, 4, 5] - - test() - -static: - main() - mainProc() -main() -mainProc() +# bug #14332 +template foo = + discard collect(newSeq, for i in 1..3: i) +foo() \ No newline at end of file diff --git a/tests/stdlib/tsums.nim b/tests/stdlib/tsums.nim index 071e0b303..4c29d3e10 100644 --- a/tests/stdlib/tsums.nim +++ b/tests/stdlib/tsums.nim @@ -1,6 +1,5 @@ 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 deleted file mode 100644 index e6b65e70f..000000000 --- a/tests/stdlib/tsysrand.nim +++ /dev/null @@ -1,34 +0,0 @@ -discard """ - targets: "c cpp js" - matrix: "--experimental:vmopsDanger" -""" - -import std/sysrand -import std/assertions - -template main() = - block: - var x = array[5, byte].default - doAssert urandom(x) - - block: - var x = newSeq[byte](5) - doAssert urandom(x) - - block: - var x = @[byte(0), 0, 0, 0, 0] - doAssert urandom(x) - - block: - var x = @[byte(1), 2, 3, 4, 5] - doAssert urandom(x) - - block: - doAssert urandom(0).len == 0 - doAssert urandom(10).len == 10 - doAssert urandom(20).len == 20 - doAssert urandom(120).len == 120 - doAssert urandom(113).len == 113 - doAssert urandom(1234) != urandom(1234) # unlikely to fail in practice - -main() diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim deleted file mode 100644 index 810c3af04..000000000 --- a/tests/stdlib/tsystem.nim +++ /dev/null @@ -1,171 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -import stdtest/testutils -import std/assertions - -# TODO: in future work move existing `system` tests here, where they belong - - -template main = - block: # closure - proc outer() = - var a = 0 - proc inner1 = a.inc - proc inner2 = discard - doAssert inner1 is "closure" - doAssert inner2 isnot "closure" - doAssert inner1 is (proc) - doAssert inner2 is (proc) - let inner1b = inner1 - doAssert inner1b is "closure" - doAssert inner1b == inner1 - outer() - - block: # rawProc, rawProc, bug #17911 - proc outer() = - var a = 0 - var b = 0 - proc inner1() = a.inc - proc inner2() = a += 2 - proc inner3() = b.inc - let inner1b = inner1 - doAssert inner2 != inner1 - doAssert inner3 != inner1 - whenVMorJs: discard - do: - doAssert rawProc(inner1b) == rawProc(inner1) - doAssert rawProc(inner2) != rawProc(inner1) - doAssert rawProc(inner3) != rawProc(inner1) - - doAssert rawEnv(inner1b) == rawEnv(inner1) - doAssert rawEnv(inner2) == rawEnv(inner1) # because both use `a` - # doAssert rawEnv(inner3) != rawEnv(inner1) # because `a` vs `b` # this doesn't hold - outer() - - block: # system.delete - block: - var s = @[1] - s.delete(0) - doAssert s == @[] - - block: - var s = @["foo", "bar"] - s.delete(1) - doAssert s == @["foo"] - - when false: - var s: seq[string] - doAssertRaises(IndexDefect): - s.delete(0) - - block: - doAssert not compiles(@["foo"].delete(-1)) - - block: # bug #6710 - var s = @["foo"] - s.delete(0) - doAssert s == @[] - - when false: # bug #16544: deleting out of bounds index should raise - var s = @["foo"] - doAssertRaises(IndexDefect): - s.delete(1) - -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 == {} - -block: # bug #20516 - type Foo = object - x {.bitsize:4.}: uint - y {.bitsize:4.}: uint - - when not defined(js): - let a = create(Foo) diff --git a/tests/stdlib/ttables.nim b/tests/stdlib/ttables.nim index ab6502411..c1ae89b32 100644 --- a/tests/stdlib/ttables.nim +++ b/tests/stdlib/ttables.nim @@ -1,5 +1,4 @@ import tables, hashes -import std/assertions type Person = object diff --git a/tests/stdlib/ttasks.nim b/tests/stdlib/ttasks.nim deleted file mode 100644 index 4889d49d9..000000000 --- a/tests/stdlib/ttasks.nim +++ /dev/null @@ -1,507 +0,0 @@ -discard """ - targets: "c cpp" - matrix: "--gc:orc --threads:off" -""" - -import std/[tasks, strformat] -import std/assertions - -block: - var s = "" - proc `+`(x: int, y: string) = - s.add $x & y - - let literal = "Nim" - let t = toTask(521 + literal) - t.invoke() - - doAssert s == "521Nim" - -block: - var s = "" - proc `!`(x: int) = - s.add $x - - let t = toTask !12 - t.invoke() - - doAssert s == "12" - - -block: - block: - var called = 0 - proc hello(x: static range[1 .. 5]) = - called += x - - let b = toTask hello(3) - b.invoke() - doAssert called == 3 - b.invoke() - doAssert called == 6 - - block: - var called = 0 - proc hello(x: range[1 .. 5]) = - called += x - - let b = toTask hello(3) - b.invoke() - doAssert called == 3 - b.invoke() - doAssert called == 6 - - block: - var called = 0 - proc hello(x: 1 .. 5) = - called += x - - let b = toTask hello(3) - b.invoke() - doAssert called == 3 - b.invoke() - doAssert called == 6 - - block: - var temp = "" - proc hello(a: int or seq[string]) = - when a is seq[string]: - for s in a: - temp.add s - else: - temp.addInt a - - let x = @["1", "2", "3", "4"] - let b = toTask hello(x) - b.invoke() - doAssert temp == "1234" - b.invoke() - doAssert temp == "12341234" - - - block: - var temp = "" - - proc hello(a: int or string) = - when a is string: - temp.add a - - let x = "!2" - - let b = toTask hello(x) - b.invoke() - doAssert temp == x - - block: - var temp = "" - proc hello(a: int or string) = - when a is string: - temp.add a - - let x = "!2" - let b = toTask hello(x) - b.invoke() - doAssert temp == x - - block: - var x = 0 - proc hello(typ: typedesc) = - x += typ(12) - - let b = toTask hello(int) - b.invoke() - doAssert x == 12 - - block: - var temp = "" - proc hello(a: int or seq[string]) = - when a is seq[string]: - for s in a: - temp.add s - - let x = @["1", "2", "3", "4"] - let b = toTask hello(x) - b.invoke() - doAssert temp == "1234" - - block: - var temp = "" - proc hello(a: int | string) = - when a is string: - temp.add a - - let x = "!2" - let b = toTask hello(x) - b.invoke() - doAssert temp == x - - block: - var x = 0 - proc hello(a: int | string) = - when a is int: - x = a - - let b = toTask hello(12) - b.invoke() - doAssert x == 12 - - block: - var a1: seq[int] - var a2 = 0 - proc hello(c: seq[int], a: int) = - a1 = c - a2 = a - - let x = 12 - var y = @[1, 3, 1, 4, 5, x, 1] - let b = toTask hello(y, 12) - b.invoke() - - doAssert a1 == y - doAssert a2 == x - - block: - var a1: seq[int] - var a2 = 0 - proc hello(c: seq[int], a: int) = - a1 = c - a2 = a - var x = 2 - let b = toTask hello(@[1, 3, 1, 4, 5, x, 1], 12) - b.invoke() - - doAssert a1 == @[1, 3, 1, 4, 5, x, 1] - doAssert a2 == 12 - - block: - var a1: array[7, int] - var a2 = 0 - proc hello(c: array[7, int], a: int) = - a1 = c - a2 = a - - let b = toTask hello([1, 3, 1, 4, 5, 2, 1], 12) - b.invoke() - - doAssert a1 == [1, 3, 1, 4, 5, 2, 1] - doAssert a2 == 12 - - block: - var a1: seq[int] - var a2 = 0 - proc hello(c: seq[int], a: int) = - a1 = c - a2 = a - - let b = toTask hello(@[1, 3, 1, 4, 5, 2, 1], 12) - b.invoke() - - doAssert a1 == @[1, 3, 1, 4, 5, 2, 1] - doAssert a2 == 12 - - block: - var a1: seq[int] - var a2 = 0 - proc hello(a: int, c: seq[int]) = - a1 = c - a2 = a - - let b = toTask hello(8, @[1, 3, 1, 4, 5, 2, 1]) - b.invoke() - - doAssert a1 == @[1, 3, 1, 4, 5, 2, 1] - doAssert a2 == 8 - - let c = toTask 8.hello(@[1, 3, 1, 4, 5, 2, 1]) - c.invoke() - - doAssert a1 == @[1, 3, 1, 4, 5, 2, 1] - doAssert a2 == 8 - - block: - var a1: seq[seq[int]] - var a2: int - proc hello(a: int, c: openArray[seq[int]]) = - a1 = @c - a2 = a - - let b = toTask hello(8, @[@[3], @[4], @[5], @[6], @[12], @[7]]) - b.invoke() - - doAssert a1 == @[@[3], @[4], @[5], @[6], @[12], @[7]] - doAssert a2 == 8 - - block: - var a1: seq[int] - var a2: int - proc hello(a: int, c: openArray[int]) = - a1 = @c - a2 = a - - let b = toTask hello(8, @[3, 4, 5, 6, 12, 7]) - b.invoke() - - doAssert a1 == @[3, 4, 5, 6, 12, 7] - doAssert a2 == 8 - - block: - var a1: seq[int] - var a2: int - proc hello(a: int, c: static varargs[int]) = - a1 = @c - a2 = a - - let b = toTask hello(8, @[3, 4, 5, 6, 12, 7]) - b.invoke() - - doAssert a1 == @[3, 4, 5, 6, 12, 7] - doAssert a2 == 8 - - block: - var a1: seq[int] - var a2: int - proc hello(a: int, c: static varargs[int]) = - a1 = @c - a2 = a - - let b = toTask hello(8, [3, 4, 5, 6, 12, 7]) - b.invoke() - - doAssert a1 == @[3, 4, 5, 6, 12, 7] - doAssert a2 == 8 - - block: - var a1: seq[int] - var a2: int - proc hello(a: int, c: varargs[int]) = - a1 = @c - a2 = a - - let x = 12 - let b = toTask hello(8, 3, 4, 5, 6, x, 7) - b.invoke() - - doAssert a1 == @[3, 4, 5, 6, 12, 7] - doAssert a2 == 8 - - block: - var x = 12 - - proc hello(x: ptr int) = - x[] += 12 - - let b = toTask hello(addr x) - b.invoke() - - doAssert x == 24 - - let c = toTask x.addr.hello - invoke(c) - - doAssert x == 36 - block: - type - Test = ref object - id: int - - var x = 0 - proc hello(a: int, c: static Test) = - x += a - x += c.id - - let b = toTask hello(8, Test(id: 12)) - b.invoke() - - doAssert x == 20 - - block: - type - Test = object - id: int - - var x = 0 - proc hello(a: int, c: static Test) = - x += a - x += c.id - - let b = toTask hello(8, Test(id: 12)) - b.invoke() - doAssert x == 20 - - block: - var x = 0 - proc hello(a: int, c: static seq[int]) = - x += a - for i in c: - x += i - - let b = toTask hello(8, @[3, 4, 5, 6, 12, 7]) - b.invoke() - doAssert x == 45 - - block: - var x = 0 - proc hello(a: int, c: static array[5, int]) = - x += a - for i in c: - x += i - - let b = toTask hello(8, [3, 4, 5, 6, 12]) - b.invoke() - doAssert x == 38 - - block: - var aVal = 0 - var cVal = "" - - proc hello(a: int, c: static string) = - aVal += a - cVal.add c - - var x = 1314 - let b = toTask hello(x, "hello") - b.invoke() - - doAssert aVal == x - doAssert cVal == "hello" - - block: - var aVal = "" - - proc hello(a: static string) = - aVal.add a - let b = toTask hello("hello") - b.invoke() - - doAssert aVal == "hello" - - block: - var aVal = 0 - var cVal = "" - - proc hello(a: static int, c: static string) = - aVal += a - cVal.add c - let b = toTask hello(8, "hello") - b.invoke() - - doAssert aVal == 8 - doAssert cVal == "hello" - - block: - var aVal = 0 - var cVal = 0 - - proc hello(a: static int, c: int) = - aVal += a - cVal += c - - let b = toTask hello(c = 0, a = 8) - b.invoke() - - doAssert aVal == 8 - doAssert cVal == 0 - - block: - var aVal = 0 - var cVal = 0 - - proc hello(a: int, c: static int) = - aVal += a - cVal += c - - let b = toTask hello(c = 0, a = 8) - b.invoke() - - doAssert aVal == 8 - doAssert cVal == 0 - - block: - var aVal = 0 - var cVal = 0 - - proc hello(a: static int, c: static int) = - aVal += a - cVal += c - - let b = toTask hello(0, 8) - b.invoke() - - doAssert aVal == 0 - doAssert cVal == 8 - - block: - var temp = "" - proc hello(x: int, y: seq[string], d = 134) = - temp = fmt"{x=} {y=} {d=}" - - - proc main() = - var x = @["23456"] - let t = toTask hello(2233, x) - t.invoke() - - doAssert temp == """x=2233 y=@["23456"] d=134""" - - main() - - - block: - var temp = "" - proc hello(x: int, y: seq[string], d = 134) = - temp.add fmt"{x=} {y=} {d=}" - - proc ok() = - temp = "ok" - - proc main() = - var x = @["23456"] - let t = toTask hello(2233, x) - t.invoke() - t.invoke() - - doAssert temp == """x=2233 y=@["23456"] d=134x=2233 y=@["23456"] d=134""" - - main() - - var x = @["4"] - let m = toTask hello(2233, x, 7) - m.invoke() - - doAssert temp == """x=2233 y=@["23456"] d=134x=2233 y=@["23456"] d=134x=2233 y=@["4"] d=7""" - - let n = toTask ok() - n.invoke() - - doAssert temp == "ok" - - block: - var called = 0 - block: - proc hello() = - inc called - - let a = toTask hello() - invoke(a) - - doAssert called == 1 - - block: - proc hello(a: int) = - inc called, a - - let b = toTask hello(13) - let c = toTask hello(a = 14) - b.invoke() - c.invoke() - - doAssert called == 28 - - block: - proc hello(a: int, c: int) = - inc called, a - - let b = toTask hello(c = 0, a = 8) - b.invoke() - - doAssert called == 36 diff --git a/tests/stdlib/ttempfiles.nim b/tests/stdlib/ttempfiles.nim deleted file mode 100644 index 1159e08ef..000000000 --- a/tests/stdlib/ttempfiles.nim +++ /dev/null @@ -1,50 +0,0 @@ -discard """ - joinable: false # not strictly necessary -""" - -import std/tempfiles -import std/[os, nre] -import std/[assertions, syncio] - -const - prefix = "D20210502T100442" # safety precaution to only affect files/dirs with this prefix - suffix = ".tmp" - -block: - var t1 = createTempFile(prefix, suffix) - var t2 = createTempFile(prefix, suffix) - defer: - close(t1.cfile) - close(t2.cfile) - removeFile(t1.path) - removeFile(t2.path) - - doAssert t1.path != t2.path - - let s = "1234" - write(t1.cfile, s) - doAssert readAll(t2.cfile) == "" - doAssert readAll(t1.cfile) == "" - t1.cfile.setFilePos 0 - doAssert readAll(t1.cfile) == s - -block: # createTempDir - doAssertRaises(OSError): discard createTempDir(prefix, suffix, "nonexistent") - - block: - let dir1 = createTempDir(prefix, suffix) - let dir2 = createTempDir(prefix, suffix) - defer: - removeDir(dir1) - removeDir(dir2) - doAssert dir1 != dir2 - - doAssert dirExists(dir1) - doAssert dir1.lastPathPart.contains(re"^D20210502T100442(\w+).tmp$") - doAssert dir1.parentDir == getTempDir().normalizePathEnd() - - block: - let dir3 = createTempDir(prefix, "_mytmp", ".") - doAssert dir3.lastPathPart.contains(re"^D20210502T100442(\w+)_mytmp$") - doAssert dir3.parentDir == "." # not getCurrentDir(): we honor the absolute/relative state of input `dir` - removeDir(dir3) diff --git a/tests/stdlib/tterminal.nim b/tests/stdlib/tterminal.nim index 16365e71c..364c8d82e 100644 --- a/tests/stdlib/tterminal.nim +++ b/tests/stdlib/tterminal.nim @@ -1,6 +1,7 @@ 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 e9ea3127c..d6034ab57 100644 --- a/tests/stdlib/tterminal_12759.nim +++ b/tests/stdlib/tterminal_12759.nim @@ -3,7 +3,6 @@ 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 0f8bf16cf..1a50d311b 100644 --- a/tests/stdlib/ttestutils.nim +++ b/tests/stdlib/ttestutils.nim @@ -1,40 +1,6 @@ import stdtest/testutils -import std/assertions - -block: # assertAll - assertAll: - 1+1 == 2 - var a = 3 - a == 3 - - doAssertRaises(AssertionDefect): - assertAll: - 1+1 == 2 - var a = 3 - a == 4 block: # greedyOrderedSubsetLines - assertAll: - greedyOrderedSubsetLines("a1\na3", "a0\na1\na2\na3\na4") - not greedyOrderedSubsetLines("a3\na1", "a0\na1\na2\na3\na4") # out of order - not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs - - not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\nprefix:a5") - not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\na5:suffix") - not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\nprefix:a5") - not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\na5:suffix") - -block: # greedyOrderedSubsetLines with allowPrefixMatch = true - template fn(a, b): bool = - greedyOrderedSubsetLines(a, b, allowPrefixMatch = true) - assertAll: - fn("a1\na3", "a0\na1\na2\na3_suffix\na4") - not fn("a1\na3", "a0\na1\na2\nprefix_a3\na4") - # these are same as above, could be refactored - not fn("a3\na1", "a0\na1\na2\na3\na4") # out of order - not fn("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs - - not fn("a1\na5", "a0\na1\na2\na3\na4\nprefix:a5") - fn("a1\na5", "a0\na1\na2\na3\na4\na5:suffix") - not fn("a5", "a0\na1\na2\na3\na4\nprefix:a5") - fn("a5", "a0\na1\na2\na3\na4\na5:suffix") + doAssert greedyOrderedSubsetLines("a1\na3", "a0\na1\na2\na3\na4") + doAssert not greedyOrderedSubsetLines("a3\na1", "a0\na1\na2\na3\na4") # out of order + doAssert not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs diff --git a/tests/stdlib/tthreadpool.nim b/tests/stdlib/tthreadpool.nim deleted file mode 100644 index bc574faeb..000000000 --- a/tests/stdlib/tthreadpool.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - matrix: "--threads:on --gc:arc" - disabled: "freebsd" - output: "42" -""" -import std/assertions -from std/threadpool import spawn, `^`, sync -block: # bug #12005 - proc doworkok(i: int) {.thread.} = echo i - spawn(doworkok(42)) - sync() # this works when returning void! - - proc doworkbad(i: int): int {.thread.} = i - doAssert ^spawn(doworkbad(42)) == 42 # bug was here diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index f6159d942..dc9468def 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -3,7 +3,6 @@ discard """ """ import times, strutils, unittest -import std/assertions when not defined(js): import os @@ -11,17 +10,17 @@ when not defined(js): proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} = let offset = hours * 3600 + minutes * 60 + seconds - proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime = + proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime {.locks: 0.} = result.isDst = false result.utcOffset = offset result.time = adjTime + initDuration(seconds = offset) - proc zonedTimeFromTime(time: Time): ZonedTime = + proc zonedTimeFromTime(time: Time): ZonedTime {.locks: 0.}= result.isDst = false result.utcOffset = offset result.time = time - newTimezone("", zonedTimeFromTime, zonedTimeFromAdjTime) + newTimezone("", zonedTimeFromTime, zonedTImeFromAdjTime) template parseTest(s, f, sExpected: string, ydExpected: int) = let @@ -85,10 +84,6 @@ template runTimezoneTests() = "2001-01-12T08:04:05Z", 11) parseTest("2001-01-12T15:04:05 +07:30:59", "yyyy-MM-dd'T'HH:mm:ss zzzz", "2001-01-12T07:33:06Z", 11) - parseTest("2001-01-12T15:04:05 +0700", "yyyy-MM-dd'T'HH:mm:ss ZZZ", - "2001-01-12T08:04:05Z", 11) - parseTest("2001-01-12T15:04:05 +073059", "yyyy-MM-dd'T'HH:mm:ss ZZZZ", - "2001-01-12T07:33:06Z", 11) # Kitchen = "3:04PM" parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00") @@ -647,98 +642,3 @@ block: # ttimes doAssert initDuration(milliseconds = 500).inMilliseconds == 500 doAssert initDuration(milliseconds = -500).inMilliseconds == -500 doAssert initDuration(nanoseconds = -999999999).inMilliseconds == -999 - - block: # getIsoWeekAndYear - doAssert getIsoWeekAndYear(initDateTime(04, mNov, 2019, 00, 00, 00)) == (isoweek: 45.IsoWeekRange, isoyear: 2019.IsoYear) - doAssert initDateTime(dMon, 45, 2019.IsoYear, 00, 00, 00) == initDateTime(04, mNov, 2019, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(28, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear) - doAssert initDateTime(dSat, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(28, mDec, 2019, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(29, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear) - doAssert initDateTime(dSun, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(29, mDec, 2019, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(30, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dMon, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(30, mDec, 2019, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dTue, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2019, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dWed, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dThu, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(05, mApr, 2020, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dSun, 14, 2020.IsoYear, 00, 00, 00) == initDateTime(05, mApr, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(06, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dMon, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(06, mApr, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(10, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dFri, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(10, mApr, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(12, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dSun, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(12, mApr, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(13, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dMon, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(13, mApr, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(15, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dThu, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(16, mApr, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(17, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dFri, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(17, mJul, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(19, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dSun, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(19, mJul, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(20, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dMon, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(20, mJul, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(23, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dThu, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(23, mJul, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2020, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dThu, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2020, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dFri, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2021, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2021, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(03, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) - doAssert initDateTime(dSun, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(03, mJan, 2021, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(04, mJan, 2021, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2021.IsoYear) - doAssert initDateTime(dMon, 01, 2021.IsoYear, 00, 00, 00) == initDateTime(04, mJan, 2021, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 00, 00, 00)) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear) - doAssert initDateTime(dMon, 05, 2021.IsoYear, 00, 00, 00) == initDateTime(01, mFeb, 2021, 00, 00, 00) - - doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1))) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear) - doAssert initDateTime(dMon, 05, 2021.IsoYear, 01, 02, 03, 400_000_000, staticTz(hours=1)) == initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1)) - - doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0001.IsoYear) - doAssert initDateTime(dSun, 13, 0001.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0001, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0000, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0000.IsoYear) - doAssert initDateTime(dSat, 13, 0000.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0000, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: (-0001).IsoYear) - doAssert initDateTime(dThu, 13, (-0001).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0001, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0002, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0002).IsoYear) - doAssert initDateTime(dWed, 14, (-0002).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0002, 00, 00, 00) - doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0753, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0753).IsoYear) - doAssert initDateTime(dMon, 14, (-0753).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0753, 00, 00, 00) - - block: # getWeeksInIsoYear - doAssert getWeeksInIsoYear((-0014).IsoYear) == 52 - doAssert getWeeksInIsoYear((-0013).IsoYear) == 53 - doAssert getWeeksInIsoYear((-0012).IsoYear) == 52 - - doAssert getWeeksInIsoYear((-0009).IsoYear) == 52 - doAssert getWeeksInIsoYear((-0008).IsoYear) == 53 - doAssert getWeeksInIsoYear((-0007).IsoYear) == 52 - - doAssert getWeeksInIsoYear((-0003).IsoYear) == 52 - doAssert getWeeksInIsoYear((-0002).IsoYear) == 53 - doAssert getWeeksInIsoYear((-0001).IsoYear) == 52 - - doAssert getWeeksInIsoYear(0003.IsoYear) == 52 - doAssert getWeeksInIsoYear(0004.IsoYear) == 53 - doAssert getWeeksInIsoYear(0005.IsoYear) == 52 - - doAssert getWeeksInIsoYear(1653.IsoYear) == 52 - doAssert getWeeksInIsoYear(1654.IsoYear) == 53 - doAssert getWeeksInIsoYear(1655.IsoYear) == 52 - - doAssert getWeeksInIsoYear(1997.IsoYear) == 52 - doAssert getWeeksInIsoYear(1998.IsoYear) == 53 - doAssert getWeeksInIsoYear(1999.IsoYear) == 52 - - doAssert getWeeksInIsoYear(2008.IsoYear) == 52 - doAssert getWeeksInIsoYear(2009.IsoYear) == 53 - doAssert getWeeksInIsoYear(2010.IsoYear) == 52 - - doAssert getWeeksInIsoYear(2014.IsoYear) == 52 - doAssert getWeeksInIsoYear(2015.IsoYear) == 53 - doAssert getWeeksInIsoYear(2016.IsoYear) == 52 diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim index 5e17c151a..61c661a58 100644 --- a/tests/stdlib/ttypeinfo.nim +++ b/tests/stdlib/ttypeinfo.nim @@ -1,5 +1,4 @@ -import std/typeinfo -import std/assertions +import typeinfo type TE = enum @@ -51,7 +50,7 @@ doAssert($x4[].kind() == "akString") block: # gimme a new scope dammit - var myArr: array[0..4, array[0..4, string]] = [ + var myarr: array[0..4, array[0..4, string]] = [ ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]] diff --git a/tests/stdlib/ttypeinfo.nims b/tests/stdlib/ttypeinfo.nims deleted file mode 100644 index d31d0b26f..000000000 --- a/tests/stdlib/ttypeinfo.nims +++ /dev/null @@ -1 +0,0 @@ ---styleCheck:off \ No newline at end of file diff --git a/tests/stdlib/ttypetraits.nim b/tests/stdlib/ttypetraits.nim deleted file mode 100644 index 574204da6..000000000 --- a/tests/stdlib/ttypetraits.nim +++ /dev/null @@ -1,93 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -# xxx merge with tests/metatype/ttypetraits.nim - -import std/typetraits -import std/assertions - -macro testClosure(fn: typed, flag: static bool) = - if flag: - doAssert hasClosure(fn) - else: - doAssert not hasClosure(fn) - -block: - proc h1() = - echo 1 - - testClosure(h1, false) - - proc h2() {.nimcall.} = - echo 2 - - testClosure(h2, false) - - -block: - proc fn(): auto = - proc hello() {.nimcall.} = - echo 3 - hello - - let name = fn() - testClosure(name, false) - -block: - proc fn(): auto = - proc hello() = - echo 3 - hello - - let name = fn() - testClosure(name, false) - -block: - proc fn(): auto = - var x = 0 - proc hello() = - echo 3 - inc x - hello - - let name = fn() - testClosure(name, true) - -block: - proc fn(): auto = - var x = 0 - proc hello() {.closure.} = - echo 3 - inc x - hello - - let name = fn() - testClosure(name, true) - -block: - proc fn(): auto = - var x = 0 - proc hello() {.closure.} = - echo 3 - inc x - hello - - let name = fn() - testClosure(name, true) - - let name2 = name - testClosure(name2, true) - -block: - iterator hello(): int = - yield 1 - - testClosure(hello, false) - -when not defined(js): - block: - iterator hello(): int {.closure.}= - yield 1 - - testClosure(hello, true) diff --git a/tests/stdlib/tunicode.nim b/tests/stdlib/tunicode.nim index 2b1cb2385..a346106f9 100644 --- a/tests/stdlib/tunicode.nim +++ b/tests/stdlib/tunicode.nim @@ -1,5 +1,5 @@ -import std/unicode -import std/assertions +import unicode + proc asRune(s: static[string]): Rune = ## Compile-time conversion proc for converting string literals to a Rune @@ -213,10 +213,3 @@ block differentSizes: doAssert swapCase("ⱥbCd") == "ȺBcD" doAssert swapCase("XyꟆaB") == "xYᶎAb" doAssert swapCase("aᵹcᲈd") == "AꝽCꙊD" - -block: # bug #17768 - let s1 = "abcdef" - let s2 = "abcdéf" - - doAssert s1.runeSubStr(0, -1) == "abcde" - doAssert s2.runeSubStr(0, -1) == "abcdé" diff --git a/tests/stdlib/tunidecode.nim b/tests/stdlib/tunidecode.nim index 653016ea9..be8e0523c 100644 --- a/tests/stdlib/tunidecode.nim +++ b/tests/stdlib/tunidecode.nim @@ -5,7 +5,6 @@ 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 606c9bc70..505fb41a3 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -22,8 +22,7 @@ discard """ targets: "c js" """ -import std/[unittest, sequtils, assertions] -from std/unittest {.all.} import matchFilter +import unittest, sequtils proc doThings(spuds: var int): int = spuds = 24 @@ -34,12 +33,12 @@ test "#964": check spuds == 24 -from std/strutils import toUpperAscii +from strutils import toUpperAscii test "#1384": check(@["hello", "world"].map(toUpperAscii) == @["HELLO", "WORLD"]) -import std/options +import options test "unittest typedescs": check(none(int) == none(int)) check(none(int) != some(1)) @@ -50,8 +49,8 @@ test "unittest multiple requires": require(true) -import std/random -from std/strutils import parseInt +import random +from strutils import parseInt proc defectiveRobot() = case rand(1..4) of 1: raise newException(OSError, "CANNOT COMPUTE!") @@ -178,16 +177,3 @@ suite "test name filtering": check false == matchFilter("suite1", "foo", "*ite2::") check matchFilter("suite1", "q**we::foo", "q**we::foo") check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e") - - -block: - type MyFoo = object - var obj = MyFoo() - let check = 1 - check(obj == obj) - -block: - let check = 123 - var a = 1 - var b = 1 - check(a == b) diff --git a/tests/stdlib/tunittesttemplate.nim b/tests/stdlib/tunittesttemplate.nim index c29e0de01..2ca50a18b 100644 --- a/tests/stdlib/tunittesttemplate.nim +++ b/tests/stdlib/tunittesttemplate.nim @@ -8,9 +8,9 @@ discard """ """ - # bug #6736 -import std/unittest + +import unittest type A = object diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim index 77ba02dd1..6354850fc 100644 --- a/tests/stdlib/turi.nim +++ b/tests/stdlib/turi.nim @@ -1,11 +1,11 @@ discard """ targets: "c js" + joinable: false # because of `include uri` """ -import std/uri -from std/uri {.all.} as uri2 import removeDotSegments +# import std/uri # pending https://github.com/nim-lang/Nim/pull/11865 +include uri # because of `removeDotSegments` from std/sequtils import toSeq -import std/assertions template main() = block: # encodeUrl, decodeUrl @@ -16,24 +16,6 @@ template main() = doAssert decodeUrl(encodeUrl(test1, false), false) == test1 doAssert decodeUrl(encodeUrl(test1)) == test1 - block: # removeDotSegments - doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" - doAssert removeDotSegments("") == "" # empty test - doAssert removeDotSegments(".") == "." # trailing period - doAssert removeDotSegments("a1/a2/../a3/a4/a5/./a6/a7/././") == "a1/a3/a4/a5/a6/a7/" - doAssert removeDotSegments("https://a1/a2/../a3/a4/a5/./a6/a7/././") == "https://a1/a3/a4/a5/a6/a7/" - doAssert removeDotSegments("http://a1/a2") == "http://a1/a2" - doAssert removeDotSegments("http://www.ai.") == "http://www.ai." - when false: # xxx these cases are buggy - # this should work, refs https://webmasters.stackexchange.com/questions/73934/how-can-urls-have-a-dot-at-the-end-e-g-www-bla-de - doAssert removeDotSegments("http://www.ai./") == "http://www.ai./" # fails - echo removeDotSegments("http://www.ai./") # http://www.ai/ - echo removeDotSegments("a/b.../c") # b.c - echo removeDotSegments("a/b../c") # bc - echo removeDotSegments("a/.../c") # .c - echo removeDotSegments("a//../b") # a/b - echo removeDotSegments("a/b/c//") # a/b/c// - block: # parseUri block: let org = "udp://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080" @@ -189,6 +171,10 @@ template main() = let test = parseUri("http://example.com/foo/") / "/bar/asd" doAssert test.path == "/foo/bar/asd" + block: # removeDotSegments + doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" + doAssert removeDotSegments("") == "" # empty test + block: # bug #3207 doAssert parseUri("http://qq/1").combine(parseUri("https://qqq")).`$` == "https://qqq" @@ -275,9 +261,7 @@ 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: @@ -303,20 +287,8 @@ 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("./") - let u2 = parseUri("./path") - let u3 = parseUri("a/path") - doAssert u1.scheme.len == 0 - doAssert u1.path == "./" - doAssert u2.scheme.len == 0 - doAssert u2.path == "./path" - doAssert u3.scheme.len == 0 - doAssert u3.path == "a/path" + doAssertRaises(UriParseError): + discard toSeq(decodeQuery("a=1&b=2c=6")) static: main() main() diff --git a/tests/stdlib/tuserlocks.nim b/tests/stdlib/tuserlocks.nim index ba8ea050e..f6eafa05d 100644 --- a/tests/stdlib/tuserlocks.nim +++ b/tests/stdlib/tuserlocks.nim @@ -1,9 +1,8 @@ discard """ - matrix: "--threads:on" + cmd: "nim $target --threads:on $options $file" """ -import std/rlocks -import std/assertions +import rlocks var r: RLock r.initRLock() @@ -11,11 +10,4 @@ doAssert r.tryAcquire() doAssert r.tryAcquire() r.release() r.release() - -block: - var x = 12 - withRLock r: - inc x - doAssert x == 13 - r.deinitRLock() diff --git a/tests/stdlib/tvarargs.nim b/tests/stdlib/tvarargs.nim deleted file mode 100644 index 3207572b5..000000000 --- a/tests/stdlib/tvarargs.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - targets: "c js" - matrix: "--gc:refc; --gc:arc" -""" -import std/assertions - -template main = - proc hello(x: varargs[string]): seq[string] = - var s: seq[string] - s.add x - s - - doAssert hello() == @[] - doAssert hello("a1") == @["a1"] - doAssert hello("a1", "a2") == @["a1", "a2"] - -static: main() -main() diff --git a/tests/stdlib/tvarints.nim b/tests/stdlib/tvarints.nim index bb0d3d37f..dcdb756ce 100644 --- a/tests/stdlib/tvarints.nim +++ b/tests/stdlib/tvarints.nim @@ -1,7 +1,15 @@ -import std/varints -import std/assertions +discard """ + cmd: "nim c -r --styleCheck:hint --panics:on $options $file" + matrix: "-d:danger; -d:release" + targets: "c cpp" + nimout: "" + action: "run" + exitcode: 0 + timeout: 60.0 +""" + +import std/varints -# xxx doesn't work with js: tvarints.nim(18, 14) `wrLen == rdLen` [AssertionDefect] block: var dest: array[50, byte] @@ -31,39 +39,46 @@ block: block: var hugeIntArray: array[50, byte] var readedInt: uint64 - - template chk(a) = - let b = cast[uint64](a) - doAssert writeVu64(hugeIntArray, b) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == b - - chk 0 - chk uint64.high - chk int64.high - chk int32.high - chk int16.high - chk int16.high - chk int8.high - chk 0.0 - chk -0.0 - chk 0.1 - chk Inf - chk NegInf - chk NaN - chk 3.1415926535897932384626433 + doAssert writeVu64(hugeIntArray, 0.uint64) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == 0.uint64 + doAssert writeVu64(hugeIntArray, uint64.high) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == uint64.high + doAssert writeVu64(hugeIntArray, uint64(int64.high)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == uint64(int64.high) + doAssert writeVu64(hugeIntArray, uint64(int32.high)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == uint64(int32.high) + doAssert writeVu64(hugeIntArray, uint64(int16.high)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == uint64(int16.high) + doAssert writeVu64(hugeIntArray, uint64(int8.high)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == uint64(int8.high) + doAssert writeVu64(hugeIntArray, cast[uint64](0.0)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == cast[uint64](0.0) + doAssert writeVu64(hugeIntArray, cast[uint64](-0.0)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == cast[uint64](-0.0) + doAssert writeVu64(hugeIntArray, cast[uint64](0.1)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == cast[uint64](0.1) + doAssert writeVu64(hugeIntArray, cast[uint64](0.9555555555555555555555501)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == cast[uint64](0.9555555555555555555555501) + doAssert writeVu64(hugeIntArray, cast[uint64](+Inf)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == cast[uint64](+Inf) + doAssert writeVu64(hugeIntArray, cast[uint64](NegInf)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == cast[uint64](NegInf) + doAssert writeVu64(hugeIntArray, cast[uint64](Nan)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == cast[uint64](Nan) + doAssert writeVu64(hugeIntArray, cast[uint64](3.1415926535897932384626433)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == cast[uint64](3.1415926535897932384626433) + doAssert writeVu64(hugeIntArray, cast[uint64](2.71828182845904523536028747)) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == cast[uint64](2.71828182845904523536028747) block: - template chk(a) = - let b = cast[uint64](a) - doAssert encodeZigzag(decodeZigzag(b)) == b - chk 0 - chk uint32.high - chk int32.high - chk int16.high - chk int8.high - chk 0.0 - chk 0.1 - chk 0.9555555555555555555555501 - chk Inf - chk 3.1415926535897932384626433 - chk 2.71828182845904523536028747 + doAssert encodeZigzag(decodeZigzag(0.uint64)) == 0.uint64 + doAssert encodeZigzag(decodeZigzag(uint64(uint32.high))) == uint64(uint32.high) + doAssert encodeZigzag(decodeZigzag(uint64(int32.high))) == uint64(int32.high) + doAssert encodeZigzag(decodeZigzag(uint64(int16.high))) == uint64(int16.high) + doAssert encodeZigzag(decodeZigzag(uint64(int8.high))) == uint64(int8.high) + doAssert encodeZigzag(decodeZigzag(cast[uint64](0.0))) == cast[uint64](0.0) + doAssert encodeZigzag(decodeZigzag(cast[uint64](0.1))) == cast[uint64](0.1) + doAssert encodeZigzag(decodeZigzag(cast[uint64](0.9555555555555555555555501))) == cast[uint64](0.9555555555555555555555501) + doAssert encodeZigzag(decodeZigzag(cast[uint64](+Inf))) == cast[uint64](+Inf) + doAssert encodeZigzag(decodeZigzag(cast[uint64](3.1415926535897932384626433))) == cast[uint64](3.1415926535897932384626433) + doAssert encodeZigzag(decodeZigzag(cast[uint64](2.71828182845904523536028747))) == cast[uint64](2.71828182845904523536028747) diff --git a/tests/stdlib/tvmutils.nim b/tests/stdlib/tvmutils.nim deleted file mode 100644 index f43557ad8..000000000 --- a/tests/stdlib/tvmutils.nim +++ /dev/null @@ -1,31 +0,0 @@ -discard """ - joinable: false - nimout: ''' -0 -1 -2 -tvmutils.nim(28, 13) [opcLdImmInt] if i == 4: -tvmutils.nim(28, 10) [opcEqInt] if i == 4: -tvmutils.nim(28, 10) [opcFJmp] if i == 4: -tvmutils.nim(28, 13) [opcLdImmInt] if i == 4: -tvmutils.nim(28, 10) [opcEqInt] if i == 4: -tvmutils.nim(28, 10) [opcFJmp] if i == 4: -tvmutils.nim(29, 7) [opcLdConst] vmTrace(false) -tvmutils.nim(29, 15) [opcLdImmInt] vmTrace(false) -tvmutils.nim(29, 14) [opcIndCall] vmTrace(false) -5 -6 -''' -""" -# line 20 (only showing a subset of nimout to avoid making the test rigid) -import std/vmutils - -proc main() = - for i in 0..<7: - echo i - if i == 2: - vmTrace(true) - if i == 4: - vmTrace(false) - -static: main() diff --git a/tests/stdlib/twchartoutf8.nim b/tests/stdlib/twchartoutf8.nim index 0b6cf696e..a6602e3e3 100644 --- a/tests/stdlib/twchartoutf8.nim +++ b/tests/stdlib/twchartoutf8.nim @@ -2,15 +2,12 @@ discard """ output: '''OK''' """ -import std/[syncio, assertions] - #assume WideCharToMultiByte always produce correct result #windows only when not defined(windows): echo "OK" else: - import std/widestrs {.push gcsafe.} const CP_UTF8 = 65001'i32 @@ -69,7 +66,8 @@ else: #RFC-2781 "UTF-16, an encoding of ISO 10646" - var wc: WideCString = newWideCString(1024 * 2) + var wc: WideCString + unsafeNew(wc, 1024 * 4 + 2) #U+0000 to U+D7FF #skip the U+0000 diff --git a/tests/stdlib/twith.nim b/tests/stdlib/twith.nim index b2d72bd0c..80382f7c4 100644 --- a/tests/stdlib/twith.nim +++ b/tests/stdlib/twith.nim @@ -1,5 +1,4 @@ import std/with -import std/[assertions, formatfloat] type Foo = object diff --git a/tests/stdlib/twordwrap.nim b/tests/stdlib/twordwrap.nim index a08e64cf9..c90dd9581 100644 --- a/tests/stdlib/twordwrap.nim +++ b/tests/stdlib/twordwrap.nim @@ -1,5 +1,4 @@ import std/wordwrap -import std/assertions when true: let diff --git a/tests/stdlib/twrapnils.nim b/tests/stdlib/twrapnils.nim index 5d5c1ab2d..c56a65d11 100644 --- a/tests/stdlib/twrapnils.nim +++ b/tests/stdlib/twrapnils.nim @@ -1,220 +1,82 @@ import std/wrapnils -from std/options import get, isSome -import std/assertions + +const wrapnilExtendedExports = declared(wrapnil) + # for now, wrapnil, isValid, unwrap are not exported proc checkNotZero(x: float): float = doAssert x != 0 x +var witness = 0 + proc main() = - var witness = 0 - block: - type Bar = object - b1: int - b2: ptr string + type Bar = object + b1: int + b2: ptr string - type Foo = ref object - x1: float - x2: Foo - x3: string - x4: Bar - x5: seq[int] - x6: ptr Bar - x7: array[2, string] - x8: seq[int] - x9: ref Bar + type Foo = ref object + x1: float + x2: Foo + x3: string + x4: Bar + x5: seq[int] + x6: ptr Bar + x7: array[2, string] + x8: seq[int] + x9: ref Bar - type Goo = ref object - foo: Foo + type Gook = ref object + foo: Foo - proc fun(a: Bar): auto = a.b2 + proc fun(a: Bar): auto = a.b2 - var a: Foo + var a: Foo + var x6 = create(Bar) + x6.b1 = 42 + var a2 = Foo(x1: 1.0, x5: @[10, 11], x6: x6) + var a3 = Foo(x1: 1.2, x3: "abc") + a3.x2 = a3 - var x6: ptr Bar - when nimvm: discard # pending https://github.com/timotheecour/Nim/issues/568 - else: - x6 = create(Bar) - x6.b1 = 42 - var a2 = Foo(x1: 1.0, x5: @[10, 11], x6: x6) - var a3 = Foo(x1: 1.2, x3: "abc") - a3.x2 = a3 + var gook = Gook(foo: a) - var goo = Goo(foo: a) + proc initFoo(x1: float): auto = + witness.inc + result = Foo(x1: x1) - proc initFoo(x1: float): auto = - witness.inc - result = Foo(x1: x1) + doAssert ?.a.x2.x2.x1 == 0.0 + doAssert ?.a3.x2.x2.x1 == 1.2 + doAssert ?.a3.x2.x2.x3[1] == 'b' - doAssert ?.a.x2.x2.x1 == 0.0 - doAssert ?.a3.x2.x2.x1 == 1.2 - doAssert ?.a3.x2.x2.x3[1] == 'b' + doAssert ?.a3.x2.x2.x5.len == 0 + doAssert a3.x2.x2.x3.len == 3 - doAssert ?.a3.x2.x2.x5.len == 0 - doAssert a3.x2.x2.x3.len == 3 + when wrapnilExtendedExports: + # example calling wrapnil directly, with and without unwrap + doAssert a3.wrapnil.x2.x2.x3.len == wrapnil(3) + doAssert a3.wrapnil.x2.x2.x3.len.unwrap == 3 + doAssert a2.wrapnil.x4.isValid + doAssert not a.wrapnil.x4.isValid - doAssert ?.a.x2.x2.x3[1] == default(char) - # here we only apply wrapnil around goo.foo, not goo (and assume goo is not nil) - doAssert ?.(goo.foo).x2.x2.x1 == 0.0 + doAssert ?.a.x2.x2.x3[1] == default(char) + # here we only apply wrapnil around gook.foo, not gook (and assume gook is not nil) + doAssert ?.(gook.foo).x2.x2.x1 == 0.0 - when nimvm: discard - else: - doAssert ?.a2.x6[] == Bar(b1: 42) # deref for ptr Bar + doAssert ?.a2.x6[] == Bar(b1: 42) # deref for ptr Bar - doAssert ?.a2.x1.checkNotZero == 1.0 - doAssert a == nil - # shows that checkNotZero won't be called if a nil is found earlier in chain - doAssert ?.a.x1.checkNotZero == 0.0 + doAssert ?.a2.x1.checkNotZero == 1.0 + doAssert a == nil + # shows that checkNotZero won't be called if a nil is found earlier in chain + doAssert ?.a.x1.checkNotZero == 0.0 - when nimvm: discard - else: - # checks that a chain without nil but with an empty seq still raises - doAssertRaises(IndexDefect): discard ?.a2.x8[3] + # checks that a chain without nil but with an empty seq still throws IndexDefect + doAssertRaises(IndexDefect): discard ?.a2.x8[3] - # make sure no double evaluation bug - doAssert witness == 0 - doAssert ?.initFoo(1.3).x1 == 1.3 - doAssert witness == 1 + # make sure no double evaluation bug + doAssert witness == 0 + doAssert ?.initFoo(1.3).x1 == 1.3 + doAssert witness == 1 - # here, it's used twice, to deref `ref Bar` and then `ptr string` - doAssert ?.a.x9[].fun[] == "" - - block: # `??.` - doAssert (??.a3.x2.x2.x3.len).get == 3 - doAssert (??.a2.x4).isSome - doAssert not (??.a.x4).isSome - - block: - type - A = object - b: B - B = object - c: C - C = object - d: D - D = ref object - e: E - e2: array[2, E] - e3: seq[E] - d3: D - i4: int - E = object - f: int - d2: D - proc identity[T](a: T): T = a - proc identity2[T](a: T, ignore: int): T = a - var a: A - doAssert ?.a.b.c.d.e.f == 0 - doAssert ?.a.b.c.d.e.d2.d3[].d3.e.d2.e.f == 0 - doAssert ?.a.b.c.d.d3[].e.f == 0 - doAssert ?.a.b.c.d.e2[0].d2.e3[0].f == 0 - doAssert ?.a == A.default - doAssert ?.a.b.c.d.e == E.default - doAssert ?.a.b.c.d.e.d2 == nil - - doAssert ?.a.identity.b.c.identity2(12).d.d3.e.f == 0 - doAssert ?.a.b.c.d.d3.e2[0].f == 0 - a.b.c.d = D() - a.b.c.d.d3 = a.b.c.d - a.b.c.d.e2[0].f = 5 - doAssert ?.a.b.c.d.d3.e2[0].f == 5 - - var d: D = nil - doAssert ?.d.identity.i4 == 0 - doAssert ?.d.i4.identity == 0 - - block: # case objects - type - Kind = enum k0, k1, k2 - V = object - case kind: Kind - of k0: - x0: int - of k1: - x1: int - of k2: - x2: int - A = object - v0: V - - block: - var a = V(kind: k0, x0: 3) - doAssert ?.a.x0 == 3 - doAssert ?.a.x1 == 0 - a = V(kind: k1, x1: 5) - doAssert ?.a.x0 == 0 - doAssert ?.a.x1 == 5 - - block: - var a = A(v0: V(kind: k0, x0: 10)) - doAssert ?.a.v0.x0 == 10 - doAssert ?.a.v0.x1 == 0 - a.v0 = V(kind: k2, x2: 8) - doAssert ?.a.v0.x0 == 0 - doAssert ?.a.v0.x1 == 0 - doAssert ?.a.v0.x2 == 8 - - block: # `nnkCall` - type - A = object - a0: int - d: D - D = ref object - i4: int - - proc identity[T](a: T): T = a - var d: D = nil - doAssert ?.d.i4.identity == 0 - doAssert ?.identity(?.d.i4) == 0 - doAssert ?.identity(d.i4) == 0 - doAssert ?.identity(d) == nil - doAssert ?.identity(d[]) == default(typeof(d[])) - doAssert ?.identity(d[]).i4 == 0 - var a: A - doAssert ?.identity(a) == default(A) - doAssert ?.identity(a.a0) == 0 - doAssert ?.identity(a.d) == nil - doAssert ?.identity(a.d.i4) == 0 - - block: # lvalue semantic propagation - type - A = ref object - a0: A - a1: seq[A] - a2: int - - B = object - b0: int - case cond: bool - of false: discard - of true: - b1: float - - block: - var a: A - doAssert ?.a.a0.a1[0].a2.addr == nil - a = A(a2: 3) - doAssert ?.a.a0.a1[0].a2.addr == nil - a.a0 = a - a.a1 = @[a] - let p = ?.a.a0.a1[0].a2.addr - doAssert p != nil - p[] = 5 - doAssert a.a2 == 5 - - block: - var b = B(cond: false, b0: 3) - let p = ?.b.b1.addr - doAssert p == nil - b = B(cond: true, b1: 4.5) - let p2 = ?.b.b1.addr - doAssert p2 != nil - p2[] = 4.6 - doAssert b.b1 == 4.6 - # useful pattern, impossible with Options - if (let p3 = ?.b.b1.addr; p3 != nil): - p3[] = 4.7 - doAssert b.b1 == 4.7 + # here, it's used twice, to deref `ref Bar` and then `ptr string` + doAssert ?.a.x9[].fun[] == "" main() -static: main() diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim index 4362f5ec3..d2f713269 100644 --- a/tests/stdlib/txmltree.nim +++ b/tests/stdlib/txmltree.nim @@ -1,4 +1,4 @@ -import std/[xmltree, assertions, xmlparser] +import xmltree block: @@ -83,19 +83,3 @@ block: x.add newElement("sonTag") x.add newEntity("my entity") doAssert $x == "my text&my entity;" - -block: # bug #21290 - let x = newXmlTree("foo",[ - newXmlTree("bar",[ - newText("Hola"), - newXmlTree("qux",[ - newXmlTree("plugh",[]) - ]) - ]) - ]) - - let s = $x - doAssert $parseXml(s) == s - doAssert s == """ - Hola -""" diff --git a/tests/stdlib/tyield.nim b/tests/stdlib/tyield.nim deleted file mode 100644 index 0cf52999c..000000000 --- a/tests/stdlib/tyield.nim +++ /dev/null @@ -1,257 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -import std/[sugar, algorithm] -import std/assertions - -block: - var x = @[(6.0, 6, '6'), - (5.0, 5, '5'), - (4.0, 4, '4'), - (3.0, 3, '3'), - (2.0, 2, '2'), - (1.0, 1, '1')] - - let y = x.reversed - - block: - let res = collect: - for (f, i, c) in x: - (f, i, c) - - doAssert res == x - - iterator popAscending[T](q: var seq[T]): T = - while q.len > 0: yield q.pop - - block: - var res = collect: - for f, i, c in popAscending(x): - (f, i, c) - - doAssert res == y - - let z = reversed(res) - let res2 = collect: - for (f, i, c) in popAscending(res): - (f, i, c) - - doAssert res2 == z - - -block: - var visits = 0 - block: - proc bar(): (int, int) = - inc visits - (visits, visits) - - iterator foo(): (int, int) = - yield bar() - - for a, b in foo(): - doAssert a == b - - doAssert visits == 1 - - block: - proc iterAux(a: seq[int], i: var int): (int, string) = - result = (a[i], $a[i]) - inc i - - iterator pairs(a: seq[int]): (int, string) = - var i = 0 - while i < a.len: - yield iterAux(a, i) - - var x = newSeq[int](10) - for i in 0 ..< x.len: - x[i] = i - - let res = collect: - for k, v in x: - (k, v) - - let expected = collect: - for i in 0 ..< x.len: - (i, $i) - - doAssert res == expected - - block: - proc bar(): (int, int, int) = - inc visits - (visits, visits, visits) - - iterator foo(): (int, int, int) = - yield bar() - - for a, b, c in foo(): - doAssert a == b - - doAssert visits == 2 - - - block: - - proc bar(): int = - inc visits - visits - - proc car(): int = - inc visits - visits - - iterator foo(): (int, int) = - yield (bar(), car()) - yield (bar(), car()) - - for a, b in foo(): - doAssert b == a + 1 - - doAssert visits == 6 - - - block: - proc bar(): (int, int) = - inc visits - (visits, visits) - - proc t2(): int = 99 - - iterator foo(): (int, int) = - yield (12, t2()) - yield bar() - - let res = collect: - for (a, b) in foo(): - (a, b) - - doAssert res == @[(12, 99), (7, 7)] - doAssert visits == 7 - - block: - proc bar(): (int, int) = - inc visits - (visits, visits) - - proc t2(): int = 99 - - iterator foo(): (int, int) = - yield ((12, t2())) - yield (bar()) - - let res = collect: - for (a, b) in foo(): - (a, b) - - doAssert res == @[(12, 99), (8, 8)] - doAssert visits == 8 - - block: - proc bar(): (int, int) = - inc visits - (visits, visits) - - proc t1(): int = 99 - proc t2(): int = 99 - - iterator foo(): (int, int) = - yield (t1(), t2()) - yield bar() - - let res = collect: - for a, b in foo(): - (a, b) - - doAssert res == @[(99, 99), (9, 9)] - doAssert visits == 9 - - - block: - proc bar(): ((int, int), string) = - inc visits - ((visits, visits), $visits) - - proc t2(): int = 99 - - iterator foo(): ((int, int), string) = - yield ((1, 2), $t2()) - yield bar() - - let res = collect: - for a, b in foo(): - (a, b) - - doAssert res == @[((1, 2), "99"), ((10, 10), "10")] - doAssert visits == 10 - - - block: - proc bar(): (int, int) = - inc visits - (visits, visits) - - iterator foo(): (int, int) = - yield (for i in 0 ..< 10: discard bar(); bar()) - yield (bar()) - - let res = collect: - for (a, b) in foo(): - (a, b) - - doAssert res == @[(21, 21), (22, 22)] - - block: - proc bar(): (int, int) = - inc visits - (visits, visits) - - proc t2(): int = 99 - - iterator foo(): (int, int) = - yield if true: bar() else: (t2(), t2()) - yield (bar()) - - let res = collect: - for a, b in foo(): - (a, b) - - doAssert res == @[(23, 23), (24, 24)] - - -block: - iterator foo(): (int, int, int) = - var time = 777 - yield (1, time, 3) - - let res = collect: - for a, b, c in foo(): - (a, b, c) - - doAssert res == @[(1, 777, 3)] - -block: - iterator foo(): (int, int, int) = - var time = 777 - yield (1, time, 3) - - let res = collect: - for t in foo(): - (t[0], t[1], t[2]) - - doAssert res == @[(1, 777, 3)] - - -block: - proc bar(): (int, int, int) = - (1, 2, 3) - iterator foo(): (int, int, int) = - yield bar() - - let res = collect: - for a, b, c in foo(): - (a, b, c) - - doAssert res == @[(1, 2, 3)] diff --git a/tests/stdlib/uselocks.nim b/tests/stdlib/uselocks.nim index f87623b5e..cde9641b2 100644 --- a/tests/stdlib/uselocks.nim +++ b/tests/stdlib/uselocks.nim @@ -1,5 +1,4 @@ import locks -import std/assertions type MyType* [T] = object lock: Lock @@ -10,7 +9,3 @@ proc createMyType*[T]: MyType[T] = proc use* (m: var MyType): int = withLock m.lock: result = 3 - -block: - var l: Lock - doAssert $l == "()" diff --git a/tests/strictnotnil/tnilcheck.nim b/tests/strictnotnil/tnilcheck.nim index b8057de74..5b9292522 100644 --- a/tests/strictnotnil/tnilcheck.nim +++ b/tests/strictnotnil/tnilcheck.nim @@ -1,5 +1,6 @@ discard """ -action: compile +cmd: "nim check $file" +action: "reject" """ import tables @@ -193,7 +194,7 @@ proc testAliasChanging(a: Nilable) = # # proc testPtrAlias(a: Nilable) = # # # pointer to a: hm. # # # alias to a? -# # var ptrA = a.addr # {0, 1} +# # var ptrA = a.unsafeAddr # {0, 1} # # if not a.isNil: # {0, 1} # # ptrA[] = nil # {0, 1} 0: MaybeNil 1: MaybeNil # # echo a.a #[ tt.Warning diff --git a/tests/stylecheck/fileinfo.nim b/tests/stylecheck/fileinfo.nim deleted file mode 100644 index d6faf0c73..000000000 --- a/tests/stylecheck/fileinfo.nim +++ /dev/null @@ -1,2 +0,0 @@ -# fileinfo.nim -type FileInfo* = object \ No newline at end of file diff --git a/tests/stylecheck/t20397.nim b/tests/stylecheck/t20397.nim deleted file mode 100644 index 486a97d73..000000000 --- a/tests/stylecheck/t20397.nim +++ /dev/null @@ -1,4 +0,0 @@ -{.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 deleted file mode 100644 index 76c03dca1..000000000 --- a/tests/stylecheck/t20397_1.nim +++ /dev/null @@ -1,8 +0,0 @@ -discard """ - matrix: "--styleCheck:off --hint:Name:on" -""" - -{.hintAsError[Name]:on.} -var a_b = 1 -discard a_b -{.hintAsError[Name]:off.} diff --git a/tests/stylecheck/t20397_2.nim b/tests/stylecheck/t20397_2.nim deleted file mode 100644 index 3b8e1c4d6..000000000 --- a/tests/stylecheck/t20397_2.nim +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 43a9ab71f..000000000 --- a/tests/stylecheck/taccept.nim +++ /dev/null @@ -1,22 +0,0 @@ -discard """ - matrix: "--styleCheck:error --styleCheck:usages" -""" - -import std/[asyncdispatch, nre] - -type - Name = object - id: int - -template hello = - var iD = "string" - var name: Name - doAssert name.id == 0 - doAssert iD == "string" - -hello() - -# bug #12955 -import os -import fileinfo -var xs: seq[fileinfo.FileInfo] diff --git a/tests/stylecheck/treject.nim b/tests/stylecheck/treject.nim deleted file mode 100644 index 458a2d039..000000000 --- a/tests/stylecheck/treject.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - action: reject - nimout: '''treject.nim(14, 13) Error: 'iD' should be: 'id' [field declared in treject.nim(9, 5)]''' - matrix: "--styleCheck:error --styleCheck:usages --hint:Name:on" -""" - -type - Name = object - id: int - -template hello = - var iD = "string" - var name: Name - echo name.iD - echo iD - -hello() diff --git a/tests/stylecheck/tusages.nim b/tests/stylecheck/tusages.nim deleted file mode 100644 index b689dabb3..000000000 --- a/tests/stylecheck/tusages.nim +++ /dev/null @@ -1,22 +0,0 @@ -discard """ - action: reject - nimout: '''tusages.nim(20, 5) Error: 'BAD_STYLE' should be: 'BADSTYLE' [proc declared in tusages.nim(9, 6)]''' - matrix: "--styleCheck:error --styleCheck:usages --hint:all:off --hint:Name:on" -""" - -import strutils - -proc BADSTYLE(c: char) = discard - -proc toSnakeCase(s: string): string = - result = newStringOfCap(s.len + 3) - for i in 0.. 0 and s[i-1] in {'a'..'z'}: - result.add '_' - result.add toLowerAscii(s[i]) - else: - result.add s[i] - BAD_STYLE(s[i]) - -echo toSnakeCase("fooBarBaz Yes") diff --git a/tests/system/talloc.nim b/tests/system/talloc.nim index a81fef481..9b970fda0 100644 --- a/tests/system/talloc.nim +++ b/tests/system/talloc.nim @@ -1,4 +1,8 @@ -# was: appveyor is "out of memory" +discard """ +disabled: "appveyor" +""" + +# appveyor is "out of memory" var x: ptr int diff --git a/tests/system/talloc2.nim b/tests/system/talloc2.nim index 9d1687f34..c498a3f4b 100644 --- a/tests/system/talloc2.nim +++ b/tests/system/talloc2.nim @@ -5,7 +5,7 @@ joinable: false disabled: 32bit """ # no point to test this on system with smaller address space -# was: appveyor is "out of memory" +# appveyor is "out of memory" const nmax = 2*1024*1024*1024 diff --git a/tests/system/tdeepcopy.nim b/tests/system/tdeepcopy.nim index 92ba48115..383d2e8d1 100644 --- a/tests/system/tdeepcopy.nim +++ b/tests/system/tdeepcopy.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc; --mm:orc --deepcopy:on" output: "ok" """ diff --git a/tests/system/tdollars.nim b/tests/system/tdollars.nim index 93fa5cb9e..1b2602ad0 100644 --- a/tests/system/tdollars.nim +++ b/tests/system/tdollars.nim @@ -71,71 +71,13 @@ block: # `$`(SomeInteger) testType int64 testType BiggestInt -block: # #14350, #16674, #16686 for JS +block: # #14350 for JS var cstr: cstring doAssert cstr == cstring(nil) doAssert cstr == nil doAssert cstr.isNil doAssert cstr != cstring("") - doAssert cstr.len == 0 - when defined(js): - cstr.add(cstring("abc")) - doAssert cstr == cstring("abc") - - var nil1, nil2: cstring = nil - - nil1.add(nil2) - doAssert nil1 == cstring(nil) - doAssert nil2 == cstring(nil) - - nil1.add(cstring("")) - doAssert nil1 == cstring("") - doAssert nil2 == cstring(nil) - - nil1.add(nil2) - doAssert nil1 == cstring("") - doAssert nil2 == cstring(nil) - - nil2.add(nil1) - doAssert nil1 == cstring("") - doAssert nil2 == cstring("") - -block: - when defined(js): # bug #18591 - let a1 = -1'i8 - let a2 = uint8(a1) - # if `uint8(a1)` changes meaning to `cast[uint8](a1)` in future, update this test; - # until then, this is the correct semantics. - let a3 = $a2 - doAssert a2 < 3 - doAssert a3 == "-1" - proc intToStr(a: uint8): cstring {.importjs: "(# + \"\")".} - doAssert $intToStr(a2) == "-1" - else: - block: - let x = -1'i8 - let y = uint32(x) - doAssert $y == "4294967295" - block: - let x = -1'i16 - let y = uint32(x) - doAssert $y == "4294967295" - block: - let x = -1'i32 - let y = uint32(x) - doAssert $y == "4294967295" - block: - proc foo1(arg: int): string = - let x = uint32(arg) - $x - doAssert $foo1(-1) == "4294967295" - - block: - let x = 4294967295'u32 - doAssert $x == "4294967295" - block: - doAssert $(4294967295'u32) == "4294967295" proc main()= block: @@ -158,41 +100,6 @@ proc main()= doAssert $b == "0" doAssert $(0) == "0" - doAssert $uint32.high == "4294967295" - - block: # addInt - var res = newStringOfCap(24) - template test2(a, b) = - res.setLen(0) - res.addInt a - doAssert res == b - - for i in 0 .. 9: - res.addInt int64(i) - doAssert res == "0123456789" - - res.setLen(0) - for i in -9 .. 0: - res.addInt int64(i) - doAssert res == "-9-8-7-6-5-4-3-2-10" - - when not defined(js): - test2 high(int64), "9223372036854775807" - test2 low(int64), "-9223372036854775808" - - test2 high(int32), "2147483647" - test2 low(int32), "-2147483648" - test2 high(int16), "32767" - test2 low(int16), "-32768" - test2 high(int8), "127" - test2 low(int8), "-128" - - block: - const - a: array[3, char] = ['N', 'i', 'm'] - aStr = $(a) - - doAssert aStr == """['N', 'i', 'm']""" static: main() main() diff --git a/tests/system/tgcnone.nim b/tests/system/tgcnone.nim deleted file mode 100644 index 47c6c6014..000000000 --- a/tests/system/tgcnone.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - matrix: "--gc:none -d:useMalloc --threads:off" -""" -# bug #15617 -let x = 4 -doAssert x == 4 diff --git a/tests/system/tnilconcats.nim b/tests/system/tnilconcats.nim index 69fc3913c..c1126405c 100644 --- a/tests/system/tnilconcats.nim +++ b/tests/system/tnilconcats.nim @@ -26,8 +26,7 @@ when true: # casting an empty string as sequence with shallow() should not segfault var s2: string - when defined(gcRefc): - shallow(s2) + shallow(s2) s2 &= "foo" doAssert s2 == "foo" diff --git a/tests/system/tostring.nim b/tests/system/tostring.nim index bb6e453fb..4ff363075 100644 --- a/tests/system/tostring.nim +++ b/tests/system/tostring.nim @@ -1,3 +1,7 @@ +discard """ + output: "DONE: tostring.nim" +""" + doAssert "@[23, 45]" == $(@[23, 45]) doAssert "[32, 45]" == $([32, 45]) doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"]) @@ -47,7 +51,7 @@ import strutils let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0'] doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\\x00']" -doAssert $cast[cstring](addr arr) == "Hello World!" +doAssert $cstring(unsafeAddr arr) == "Hello World!" proc takes(c: cstring) = doAssert c == cstring"" @@ -68,7 +72,7 @@ doAssert yy == "" proc bar(arg: cstring) = doAssert arg[0] == '\0' -proc baz(arg: openArray[char]) = +proc baz(arg: openarray[char]) = doAssert arg.len == 0 proc stringCompare() = @@ -104,7 +108,7 @@ bar(nilstring) static: stringCompare() -# issue #8847 +# bug 8847 var a2: cstring = "fo\"o2" block: @@ -112,14 +116,5 @@ block: s.addQuoted a2 doAssert s == "\"fo\\\"o2\"" -# issue #16650 -template fn() = - doAssert len(cstring"ab\0c") == 5 - doAssert len(cstring("ab\0c")) == 2 - when nimvm: - discard - else: - let c = cstring("ab\0c") - doAssert len(c) == 2 -fn() -static: fn() + +echo "DONE: tostring.nim" diff --git a/tests/system/trefs.nim b/tests/system/trefs.nim deleted file mode 100644 index 8c36aec52..000000000 --- a/tests/system/trefs.nim +++ /dev/null @@ -1,15 +0,0 @@ -discard """ - targets: "c js" -""" - -# bug #21317 -proc parseHook*(v: var ref int) = - var a: ref int - new(a) - a[] = 123 - v = a - -proc fromJson2*(): ref int = - parseHook(result) - -doAssert fromJson2()[] == 123 diff --git a/tests/system/tsigexitcode.nim b/tests/system/tsigexitcode.nim deleted file mode 100644 index 249256b40..000000000 --- a/tests/system/tsigexitcode.nim +++ /dev/null @@ -1,23 +0,0 @@ -discard """ - joinable: false - disabled: windows -""" - -import os, osproc, posix, strutils - -proc main() = - if paramCount() > 0: - let signal = cint parseInt paramStr(1) - discard posix.raise(signal) - else: - # synchronize this list with lib/system/except.nim:registerSignalHandler() - let sigs = [SIGINT, SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGPIPE] - for s in sigs: - let (_, exitCode) = execCmdEx(quoteShellCommand [getAppFilename(), $s]) - if s == SIGPIPE: - # SIGPIPE should be ignored - doAssert exitCode == 0, $(exitCode, s) - else: - doAssert exitCode == 128+s, $(exitCode, s) - -main() diff --git a/tests/system/tslimsystem.nim b/tests/system/tslimsystem.nim deleted file mode 100644 index 690f4ee9b..000000000 --- a/tests/system/tslimsystem.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - output: "123" - matrix: "-d:nimPreviewSlimSystem" -""" - -echo 123 \ No newline at end of file diff --git a/tests/system/tstatic.nim b/tests/system/tstatic.nim deleted file mode 100644 index 6e2893e2b..000000000 --- a/tests/system/tstatic.nim +++ /dev/null @@ -1,58 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -import std/strutils - -# bug #6133 -template main() = - block: - block: - proc foo(q: string, a: int): int = - result = q.len - - proc foo(q: static[string]): int = - result = foo(q, 5) - - doAssert foo("123") == 3 - - block: - type E = enum A - - if false: - var e = A - discard $e - - proc foo(a: string): int = - len(a) # 16640 - - proc foo(a: static[bool]): int {.used.} = - discard - - doAssert foo("") == 0 - - block: - proc foo(a: string): int = - len(a) - - proc foo(a: static[bool]): int {.used.} = - discard - - doAssert foo("abc") == 3 - - block: - proc parseInt(f: static[bool]): int {.used.} = discard - - doAssert "123".parseInt == 123 - block: - type - MyType = object - field: float32 - AType[T: static MyType] = distinct range[0f32 .. T.field] - var a: AType[MyType(field: 5f32)] - proc n(S: static Slice[int]): range[S.a..S.b] = discard - assert typeof(n 1..2) is range[1..2] - - -static: main() -main() diff --git a/tests/system/tstatic_callable.nim b/tests/system/tstatic_callable.nim deleted file mode 100644 index 92d1fca8d..000000000 --- a/tests/system/tstatic_callable.nim +++ /dev/null @@ -1,12 +0,0 @@ -# bug #16987 - -proc getNum(a: int): int = a - -# Below calls "doAssert getNum(123) == 123" at compile time. -static: - doAssert getNum(123) == 123 - -# Below calls evaluate the "getNum(123)" at compile time, but the -# results of those calls get used at run time. -doAssert (static getNum(123)) == 123 -doAssert (static(getNum(123))) == 123 diff --git a/tests/system/tstatic_callable_error.nim b/tests/system/tstatic_callable_error.nim deleted file mode 100644 index c6f1e3d07..000000000 --- a/tests/system/tstatic_callable_error.nim +++ /dev/null @@ -1,14 +0,0 @@ -# bug #16987 - -discard """ -errormsg: "cannot evaluate at compile time: inp" -nimout: ''' -tstatic_callable_error.nim(14, 21) Error: cannot evaluate at compile time: inp''' -""" - - -# line 10 -proc getNum(a: int): int = a - -let inp = 123 -echo (static getNum(inp)) diff --git a/tests/system/tstrmantle.nim b/tests/system/tstrmantle.nim new file mode 100644 index 000000000..1f195adde --- /dev/null +++ b/tests/system/tstrmantle.nim @@ -0,0 +1,46 @@ +var res = newStringOfCap(24) + +for i in 0 .. 9: + res.addInt int64(i) + +doAssert res == "0123456789" + +res.setLen(0) + +for i in -9 .. 0: + res.addInt int64(i) + +doAssert res == "-9-8-7-6-5-4-3-2-10" + +res.setLen(0) +res.addInt high(int64) +doAssert res == "9223372036854775807" + +res.setLen(0) +res.addInt low(int64) +doAssert res == "-9223372036854775808" + +res.setLen(0) +res.addInt high(int32) +doAssert res == "2147483647" + +res.setLen(0) +res.addInt low(int32) +doAssert res == "-2147483648" + +res.setLen(0) +res.addInt high(int16) +doAssert res == "32767" + +res.setLen(0) +res.addInt low(int16) +doAssert res == "-32768" + + +res.setLen(0) +res.addInt high(int8) +doAssert res == "127" + +res.setLen(0) +res.addInt low(int8) +doAssert res == "-128" diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim index 7f5914725..cb879d3b3 100644 --- a/tests/system/tsystem_misc.nim +++ b/tests/system/tsystem_misc.nim @@ -59,6 +59,11 @@ doAssert high(float) > low(float) doAssert high(float32) > low(float32) doAssert high(float64) > low(float64) +# bug #6710 +var s = @[1] +s.delete(0) + + proc foo(a: openArray[int]) = for x in a: echo x @@ -131,12 +136,12 @@ let str = "0123456789" foo(toOpenArrayByte(str, 0, str.high)) -template boundedOpenArray[T](x: seq[T], first, last: int): openArray[T] = +template boundedOpenArray[T](x: seq[T], first, last: int): openarray[T] = toOpenarray(x, max(0, first), min(x.high, last)) # bug #9281 -proc foo[T](x: openArray[T]) = +proc foo[T](x: openarray[T]) = echo x.len let a = @[1, 2, 3] diff --git a/tests/system/tuse_version.nim b/tests/system/tuse_version.nim index 745319f50..36dac46ec 100644 --- a/tests/system/tuse_version.nim +++ b/tests/system/tuse_version.nim @@ -1,5 +1,6 @@ discard """ - matrix: "--useVersion:1.0" + cmd: "nim c --useVersion:1.0 -r $file" + output: "1.0.100" """ {.warning[UnusedImport]: off.} @@ -17,7 +18,7 @@ import std/[ # Strings: editdistance, wordwrap, parseutils, ropes, - pegs, strformat, strmisc, strscans, strtabs, + pegs, punycode, strformat, strmisc, strscans, strtabs, strutils, unicode, unidecode, # Generic operator system services: @@ -46,4 +47,4 @@ import std/[ ] -doAssert NimVersion == "1.0.100" +echo NimVersion diff --git a/tests/system/tuse_version16.nim b/tests/system/tuse_version16.nim deleted file mode 100644 index 88f9728ec..000000000 --- a/tests/system/tuse_version16.nim +++ /dev/null @@ -1,49 +0,0 @@ -discard """ - matrix: "--useVersion:1.6" -""" - -{.warning[UnusedImport]: off.} - -import std/[ - # Core: - bitops, typetraits, lenientops, macros, volatile, - - # Algorithms: - algorithm, sequtils, - - # Collections: - critbits, deques, heapqueue, intsets, lists, options, sets, - sharedlist, tables, - - # Strings: - editdistance, wordwrap, parseutils, ropes, - pegs, strformat, strmisc, strscans, strtabs, - strutils, unicode, unidecode, - - # Generic operator system services: - os, streams, - - # Math libraries: - complex, math, mersenne, random, rationals, stats, sums, - - # Internet protocols: - httpcore, mimetypes, uri, - - # Parsers: - htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml, - - # XML processing: - xmltree, xmlparser, - - # Generators: - htmlgen, - - # Hashing: - base64, hashes, - - # Miscellaneous: - colors, sugar, varints, -] - - -doAssert NimVersion == "1.6.100" diff --git a/tests/system/tvarargslen.nim b/tests/system/tvarargslen.nim index 24b54a1e0..a129aa5c2 100644 --- a/tests/system/tvarargslen.nim +++ b/tests/system/tvarargslen.nim @@ -1,8 +1,8 @@ discard """ output: ''' -tvarargslen.nim:35:2 (1, 2) -tvarargslen.nim:36:2 12 -tvarargslen.nim:37:2 1 +tvarargslen.nim:35:9 (1, 2) +tvarargslen.nim:36:9 12 +tvarargslen.nim:37:9 1 tvarargslen.nim:38:8 done ''' diff --git a/tests/template/m1027a.nim b/tests/template/m1027a.nim deleted file mode 100644 index fad915a2f..000000000 --- a/tests/template/m1027a.nim +++ /dev/null @@ -1 +0,0 @@ -const version_str* = "mod a" diff --git a/tests/template/m1027b.nim b/tests/template/m1027b.nim deleted file mode 100644 index 5ff0b9714..000000000 --- a/tests/template/m1027b.nim +++ /dev/null @@ -1 +0,0 @@ -const version_str* = "mod b" diff --git a/tests/template/otests.nim b/tests/template/otests.nim index 9cb9d7fcb..120146343 100644 --- a/tests/template/otests.nim +++ b/tests/template/otests.nim @@ -163,7 +163,7 @@ when true: #embeddingTest """ # Expression template - proc test_expression(nums: openArray[int] = []): string = + proc test_expression(nums: openarray[int] = []): string = var i = 2 tmpli html""" $(no_substitution()) @@ -171,7 +171,7 @@ when true: #embeddingTest
    Age: $($nums[i] & "!!")
    """ - proc test_statements(nums: openArray[int] = []): string = + proc test_statements(nums: openarray[int] = []): string = tmpli html""" $(test_expression(nums)) $if true { diff --git a/tests/template/sunset.nimf b/tests/template/sunset.nimf index bd57bb8e1..465b12a5e 100644 --- a/tests/template/sunset.nimf +++ b/tests/template/sunset.nimf @@ -1,6 +1,6 @@ #? stdtmpl #proc sunsetTemplate*(current, ticker, content: string, -# tabs: openArray[array[0..1, string]]): string = +# tabs: openarray[array[0..1, string]]): string = # result = "" diff --git a/tests/template/t1027.nim b/tests/template/t1027.nim deleted file mode 100644 index fe03c4ea9..000000000 --- a/tests/template/t1027.nim +++ /dev/null @@ -1,22 +0,0 @@ -discard """ - cmd: "nim check --hints:off $file" - errormsg: "" - nimout: ''' -t1027.nim(20, 19) Error: ambiguous identifier: 'version_str' -- use one of the following: - m1027a.version_str: string - m1027b.version_str: string -''' -""" - - - - - - -import m1027a, m1027b - -# bug #1027 -template wrap_me(stuff): untyped = - echo "Using " & version_str - -wrap_me("hey") diff --git a/tests/template/t11705.nim b/tests/template/t11705.nim deleted file mode 100644 index 65ddc7e6c..000000000 --- a/tests/template/t11705.nim +++ /dev/null @@ -1,17 +0,0 @@ -type RefObj = ref object - -proc `[]`(val: static[int]) = # works with different name/overload or without static arg - discard - -template noRef*(T: typedesc): typedesc = # works without template indirection - typeof(default(T)[]) - -proc `=destroy`(x: var noRef(RefObj)) = - discard - -proc foo = - var x = new RefObj - doAssert $(x[]) == "()" - -# bug #11705 -foo() diff --git a/tests/template/t13515.nim b/tests/template/t13515.nim deleted file mode 100644 index ffebedcbe..000000000 --- a/tests/template/t13515.nim +++ /dev/null @@ -1,16 +0,0 @@ -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/t17433.nim b/tests/template/t17433.nim deleted file mode 100644 index 053d33ff0..000000000 --- a/tests/template/t17433.nim +++ /dev/null @@ -1,16 +0,0 @@ -# Inside template bodies, ensure return types referencing a param are replaced. -# This helps guarantee that return parameter analysis happens after argument -# analysis. - -# bug #17433 - -from std/macros import expandMacros - -proc bar(a: typedesc): a = default(a) -doAssert bar(float) == 0.0 -doAssert bar(string) == "" - -template main = - proc baz(a: typedesc): a = default(a) - doAssert baz(float) == 0.0 -main() diff --git a/tests/template/t18113.nim b/tests/template/t18113.nim deleted file mode 100644 index a84b3fd0e..000000000 --- a/tests/template/t18113.nim +++ /dev/null @@ -1,14 +0,0 @@ -# ensure template pragma handling doesn't eagerly attempt to add an implicit -# 'pushed' pragma to the evaluation of any intermediate AST prior to -# substitution. - -# bug #18113 - -import sequtils - -{.push raises: [Defect].} - -var a = toSeq([1, 2, 3, 5, 10]).filterIt(it > 5) - -doAssert a.len == 1 -doAssert a[0] == 10 diff --git a/tests/template/t19149.nim b/tests/template/t19149.nim deleted file mode 100644 index 631e8fc30..000000000 --- a/tests/template/t19149.nim +++ /dev/null @@ -1,19 +0,0 @@ -type Foo = tuple[active: bool, index: int] - - -var f: Foo - -# template result type during match stage -# f:var Foo -# a:Foo -# tyVar -# tyTuple -# after change to proc -# f:Foo -# a:Foo -# tyTuple -# tyTuple - -template cursor(): var Foo = f -discard cursor() - diff --git a/tests/template/t19700.nim b/tests/template/t19700.nim deleted file mode 100644 index cc2944944..000000000 --- a/tests/template/t19700.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - errormsg: "type mismatch: got " -""" - -type Obj = object - -proc apply[T, R](a, b: T; f: proc(x, y: T): R): R = f(a, b) - -let a, b = Obj() -discard apply(a, b, `!=`) diff --git a/tests/template/t6217.nim b/tests/template/t6217.nim deleted file mode 100644 index b27b61881..000000000 --- a/tests/template/t6217.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - output: ''' -start -side effect! -end -''' -""" - -# bug #6217 - -template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a - -proc f(): int = - echo "side effect!" - result = 55 - -echo "start" -doAssert f() * 2 == 110 -echo "end" diff --git a/tests/template/t9534.nim b/tests/template/t9534.nim deleted file mode 100644 index 8d66f42a0..000000000 --- a/tests/template/t9534.nim +++ /dev/null @@ -1,21 +0,0 @@ -discard """ - targets: "c cpp" -""" - -# bug #9534 -type - Object = object - data: int - -template test() = - proc methodName(o: Object): int = - var p: pointer - doAssert o.data == 521 - let f {.used.} = cast[proc (o: int): int {.nimcall.}](p) - doAssert o.data == 521 - result = 1314 - - var a = Object(data: 521) - doAssert methodName(a) == 1314 - -test() diff --git a/tests/template/tcallsitelineinfo.nim b/tests/template/tcallsitelineinfo.nim deleted file mode 100644 index 5fed93363..000000000 --- a/tests/template/tcallsitelineinfo.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - nimout: ''' -tcallsitelineinfo.nim(17, 4) Warning: abc [User] -''' - exitcode: 1 - outputsub: ''' -tcallsitelineinfo.nim(17) tcallsitelineinfo -Error: unhandled exception: def [ValueError] -''' -""" - -template foo(): untyped {.callsite.} = - {.warning: "abc".} - raise newException(ValueError, "def") - echo "hello" - -foo() diff --git a/tests/template/tcallsitelineinfo2.nim b/tests/template/tcallsitelineinfo2.nim deleted file mode 100644 index d5f257474..000000000 --- a/tests/template/tcallsitelineinfo2.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - nimout: ''' -tcallsitelineinfo2.nim(18, 1) Warning: abc [User] -tcallsitelineinfo2.nim(19, 12) Warning: def [User] -''' - exitcode: 1 - outputsub: ''' -tcallsitelineinfo2.nim(20) tcallsitelineinfo2 -Error: unhandled exception: ghi [ValueError] -''' -""" - -template foo(a: untyped): untyped {.callsite.} = - {.warning: "abc".} - a - echo "hello" - -foo: # with `{.line.}:`, the following do not keep their line information: - {.warning: "def".} - raise newException(ValueError, "ghi") diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim index 1fed694ef..d12b3c3ef 100644 --- a/tests/template/template_issues.nim +++ b/tests/template/template_issues.nim @@ -173,7 +173,7 @@ block t2585: st echo "a ", $fb - proc render(rdat: var RenderData; passes: var openArray[RenderPass]; proj: Mat2; + proc render(rdat: var RenderData; passes: var openarray[RenderPass]; proj: Mat2; indexType = 1) = for i in 0 ..< len(passes): echo "blah ", repr(passes[i]) @@ -256,7 +256,7 @@ discard foo() type IteratorF*[In] = iterator() : In {.closure.} -template foof(In: untyped) : untyped = +template foof(In: untyped) : untyped = proc ggg*(arg: IteratorF[In]) = for i in arg(): echo "foo" @@ -265,7 +265,7 @@ template foof(In: untyped) : untyped = iterator hello() : int {.closure.} = for i in 1 .. 3: yield i - + foof(int) ggg(hello) @@ -274,25 +274,3 @@ ggg(hello) var z = 10'u8 echo z < 9 # Works echo z > 9 # Error: type mismatch - - -# bug #5993 -template foo(p: proc) = - var bla = 5 - p(bla) - -foo() do(t: var int): - discard - t = 5 - -proc bar(t: var int) = - t = 5 - -foo(bar) - -block: # bug #12595 - template test() = - let i = 42 - discard {i: ""} - - test() diff --git a/tests/template/template_pragmas.nim b/tests/template/template_pragmas.nim index da532b010..d6c99080d 100644 --- a/tests/template/template_pragmas.nim +++ b/tests/template/template_pragmas.nim @@ -3,7 +3,7 @@ proc output_x:string {.compileTime.} = "x" template t = const x = output_x() let - bar {.exportc:"bar" & x.} = 100 + bar {.exportC:"bar" & x.} = 100 static: doAssert(compiles (t())) diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim index a3b549e18..75226ea2d 100644 --- a/tests/template/template_various.nim +++ b/tests/template/template_various.nim @@ -94,7 +94,7 @@ block generic_templates: var i3: int = t1[int]("xx") -from strutils import contains + block tgetast_typeliar: proc error(s: string) = quit s @@ -111,9 +111,7 @@ block tgetast_typeliar: var message : NimNode = newLit(condition.repr) # echo message result = getAst assertOrReturn2(condition, message) - # echo result.repr - let s = result.repr - doAssert """error("Assertion failed:""" in s + echo result.repr proc point(size: int16): tuple[x, y: int16] = # returns random point in square area with given `size` @@ -274,10 +272,10 @@ parse9: block gensym1: template x: untyped = -1 template t1() = - template x: untyped {.gensym, redefine.} = 1 + template x: untyped {.gensym.} = 1 echo x() # 1 template t2() = - template x: untyped {.redefine.} = 1 # defaults to {.inject.} + template x: untyped = 1 # defaults to {.inject.} echo x() # -1 injected x not available during template definition t1() t2() @@ -353,18 +351,3 @@ block gensym3: echo a ! b ! c ! d echo a ! b ! c ! d ! e echo x,y,z - - -block identifier_construction_with_overridden_symbol: - # could use add, but wanna make sure it's an override no matter what - func examplefn = discard - func examplefn(x: int) = discard - - # the function our template wants to use - func examplefn1 = discard - - template exampletempl(n) = - # attempt to build a name using the overridden symbol "examplefn" - `examplefn n`() - - exampletempl(1) diff --git a/tests/template/tinnerouterproc.nim b/tests/template/tinnerouterproc.nim deleted file mode 100644 index 1f15fb13e..000000000 --- a/tests/template/tinnerouterproc.nim +++ /dev/null @@ -1,8 +0,0 @@ -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/tmodulealias.nim b/tests/template/tmodulealias.nim index 79b5ec9c6..6681379fb 100644 --- a/tests/template/tmodulealias.nim +++ b/tests/template/tmodulealias.nim @@ -7,7 +7,7 @@ when defined(windows): else: import posix -when defined(windows): +when defined(Windows): template orig: expr = winlean else: diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index b68d7e253..1444667f4 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -72,7 +72,7 @@ proc concreteProc(x: int) = forStatic i, 0..3: echo i -proc genericProc(x: auto) = +proc genericProc(x: any) = forStatic i, 0..3: echo i diff --git a/tests/template/tredefinition_override.nim b/tests/template/tredefinition_override.nim deleted file mode 100644 index 7ae232bba..000000000 --- a/tests/template/tredefinition_override.nim +++ /dev/null @@ -1,33 +0,0 @@ -{.push warningAsError[ImplicitTemplateRedefinition]: 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/tunderscore1.nim b/tests/template/tunderscore1.nim deleted file mode 100644 index 71af39501..000000000 --- a/tests/template/tunderscore1.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - errormsg: "undeclared identifier: '_'" -""" - -# issue #12094, #13804 - -template foo = - let _ = 1 - echo _ - -foo() diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim index 13f53d119..5b8663cf9 100644 --- a/tests/template/twrongmapit.nim +++ b/tests/template/twrongmapit.nim @@ -1,5 +1,4 @@ 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 d70746578..7674ba7c0 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 {.redefine.} = + template fail(msg: string): typed = body assertionFailed: diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index 725e75ebc..ea640cac6 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -3,59 +3,50 @@ {.warning[UnusedImport]: off.} -from stdtest/specialpaths import buildDir - -when defined(nimPreviewSlimSystem): - import std/[ - syncio, assertions, formatfloat, objectdollar, widestrs - ] - import std/[ # Core: bitops, typetraits, lenientops, macros, volatile, - # fails due to FFI: typeinfo - # fails due to cstring cast/copyMem: endians - # works but uses FFI: cpuinfo, rlocks, locks + # fails: typeinfo, endians + # works but shouldn't: cpuinfo, rlocks, locks # Algorithms: - algorithm, enumutils, sequtils, setutils, + algorithm, sequtils, # Collections: critbits, deques, heapqueue, intsets, lists, options, sets, - tables, packedsets, + sharedlist, tables, + # fails: sharedtables # Strings: editdistance, wordwrap, parseutils, ropes, - pegs, strformat, strmisc, strscans, strtabs, - strutils, unicode, unidecode, cstrutils, - # works but uses FFI: encodings + pegs, punycode, strformat, strmisc, strscans, strtabs, + strutils, unicode, unidecode, + # works but shouldn't: cstrutils, encodings # Time handling: - # fails due to FFI: monotimes, times + # fails: monotimes, times # but times.getTime() implemented for VM # Generic operator system services: - os, streams, distros, - # fails due to FFI: memfiles, osproc, terminal - # works but uses FFI: dynlib - # intentionally fails: marshal + os, streams, + # fails: distros, dynlib, marshal, memfiles, osproc, terminal # Math libraries: - complex, math, random, rationals, stats, sums, - # works but uses FFI: fenv, sysrand + complex, math, mersenne, random, rationals, stats, sums, + # works but shouldn't: fenv # Internet protocols: httpcore, mimetypes, uri, - # fails due to FFI: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, + # fails: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, # asyncnet, cgi, cookies, httpclient, nativesockets, net, selectors, smtp - # works but no need to test: asyncstreams, asyncfutures + # works but shouldn't test: asyncstreams, asyncfutures # Threading: - # fails due to FFI: threadpool + # fails: threadpool # Parsers: htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml, - parseopt, jsonutils, + # fails: parseopt # XML processing: xmltree, xmlparser, @@ -64,31 +55,20 @@ import std/[ htmlgen, # Hashing: - base64, hashes, md5, - # fails due to cstring cast/times import/endians import: oids - # fails due to copyMem/endians import: sha1 + base64, hashes, + # fails: md5, oids, sha1 # Miscellaneous: - colors, sugar, varints, enumerate, with, - # fails due to FFI: browsers, coro, segfaults - # fails due to times import/methods: logging - # fails due to methods: unittest + colors, sugar, varints, + # fails: browsers, coro, logging (times), segfaults, unittest (uses methods) # Modules for JS backend: - # fails intentionally: asyncjs, dom, jsconsole, jscore, jsffi, jsbigints, - # jsfetch, jsformdata, jsheaders + # fails: asyncjs, dom, jsconsole, jscore, jsffi, # Unlisted in lib.html: - decls, compilesettings, wrapnils, effecttraits, genasts, - importutils, isolation + decls, compilesettings, with, wrapnils ] -# non-std imports -import stdtest/testutils -# tests (increase coverage via code reuse) -import stdlib/trandom -import stdlib/tosenv - echo "Nimscript imports are successful." block: @@ -107,32 +87,3 @@ block: try: doAssert false except Exception as e: discard - -block: # cpDir, cpFile, dirExists, fileExists, mkDir, mvDir, mvFile, rmDir, rmFile - const dname = buildDir/"D20210121T175016" - const subDir = dname/"sub" - const subDir2 = dname/"sub2" - const fpath = subDir/"f" - const fpath2 = subDir/"f2" - const fpath3 = subDir2/"f" - mkDir(subDir) - writeFile(fpath, "some text") - cpFile(fpath, fpath2) - doAssert fileExists(fpath2) - rmFile(fpath2) - cpDir(subDir, subDir2) - doAssert fileExists(fpath3) - rmDir(subDir2) - mvFile(fpath, fpath2) - doAssert not fileExists(fpath) - doAssert fileExists(fpath2) - mvFile(fpath2, fpath) - mvDir(subDir, subDir2) - doAssert not dirExists(subDir) - doAssert dirExists(subDir2) - mvDir(subDir2, subDir) - rmDir(dname) - -block: - # check parseopt can get command line: - discard initOptParser() diff --git a/tests/testament/tinlinemsg.nim b/tests/testament/tinlinemsg.nim deleted file mode 100644 index 199c263e9..000000000 --- a/tests/testament/tinlinemsg.nim +++ /dev/null @@ -1,8 +0,0 @@ -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/tjoinable.nim b/tests/testament/tjoinable.nim index c651780de..7a0ad7985 100644 --- a/tests/testament/tjoinable.nim +++ b/tests/testament/tjoinable.nim @@ -3,7 +3,6 @@ discard """ """ # checks that this is joinable -# checks that megatest allows duplicate names, see also `tests/misc/tjoinable.nim` doAssert defined(testing) doAssert defined(nimMegatest) echo "ok" # intentional to make sure this doesn't prevent `isJoinableSpec` diff --git a/tests/testament/tshould_not_work.nim b/tests/testament/tshould_not_work.nim index 8c99510a0..a0b4d6a36 100644 --- a/tests/testament/tshould_not_work.nim +++ b/tests/testament/tshould_not_work.nim @@ -1,53 +1,37 @@ discard """ - joinable: false -""" - -const expected = """ -FAIL: tests/shouldfail/tccodecheck.nim +cmd: "testament/testament --directory:testament --colors:off --backendLogging:off --nim:$nim category shouldfail" +action: compile +nimout: ''' +FAIL: tests/shouldfail/tccodecheck.nim c Failure: reCodegenFailure Expected: baz -FAIL: tests/shouldfail/tcolumn.nim +FAIL: tests/shouldfail/tcolumn.nim c Failure: reLinesDiffer -FAIL: tests/shouldfail/terrormsg.nim +FAIL: tests/shouldfail/terrormsg.nim c Failure: reMsgsDiffer -FAIL: tests/shouldfail/texitcode1.nim +FAIL: tests/shouldfail/texitcode1.nim c Failure: reExitcodesDiffer -FAIL: tests/shouldfail/tfile.nim +FAIL: tests/shouldfail/tfile.nim c Failure: reFilesDiffer -FAIL: tests/shouldfail/tline.nim +FAIL: tests/shouldfail/tline.nim c Failure: reLinesDiffer -FAIL: tests/shouldfail/tmaxcodesize.nim +FAIL: tests/shouldfail/tmaxcodesize.nim c Failure: reCodegenFailure max allowed size: 1 -FAIL: tests/shouldfail/tnimout.nim +FAIL: tests/shouldfail/tnimout.nim c Failure: reMsgsDiffer -FAIL: tests/shouldfail/tnimoutfull.nim -Failure: reMsgsDiffer -FAIL: tests/shouldfail/toutput.nim +FAIL: tests/shouldfail/toutput.nim c Failure: reOutputsDiffer -FAIL: tests/shouldfail/toutputsub.nim +FAIL: tests/shouldfail/toutputsub.nim c Failure: reOutputsDiffer -FAIL: tests/shouldfail/treject.nim +FAIL: tests/shouldfail/treject.nim c Failure: reFilesDiffer -FAIL: tests/shouldfail/tsortoutput.nim +FAIL: tests/shouldfail/tsortoutput.nim c Failure: reOutputsDiffer -FAIL: tests/shouldfail/ttimeout.nim +FAIL: tests/shouldfail/ttimeout.nim c Failure: reTimeout -FAIL: tests/shouldfail/tvalgrind.nim +FAIL: tests/shouldfail/tvalgrind.nim c Failure: reExitcodesDiffer +''' """ - -import std/[os,strformat,osproc] -import stdtest/testutils - -proc main = - const nim = getCurrentCompilerExe() - 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 - - let ok = greedyOrderedSubsetLines(expected, outp, allowPrefixMatch = true) - doAssert ok, &"\nexpected:\n{expected}\noutp:\n{outp}" -main() diff --git a/tests/testament/tspecialpaths.nim b/tests/testament/tspecialpaths.nim deleted file mode 100644 index 3c97dc88a..000000000 --- a/tests/testament/tspecialpaths.nim +++ /dev/null @@ -1,9 +0,0 @@ -import stdtest/specialpaths -import std/os -block: # splitTestFile - doAssert splitTestFile("tests/fakedir/tfakename.nim") == ("fakedir", "tests/fakedir/tfakename.nim".unixToNativePath) - doAssert splitTestFile("/pathto/tests/fakedir/tfakename.nim") == ("fakedir", "/pathto/tests/fakedir/tfakename.nim".unixToNativePath) - doAssert splitTestFile(getCurrentDir() / "tests/fakedir/tfakename.nim") == ("fakedir", "tests/fakedir/tfakename.nim".unixToNativePath) - doAssert splitTestFile(getCurrentDir() / "sub/tests/fakedir/tfakename.nim") == ("fakedir", "sub/tests/fakedir/tfakename.nim".unixToNativePath) - doAssertRaises(AssertionDefect): discard splitTestFile("testsbad/fakedir/tfakename.nim") - doAssertRaises(AssertionDefect): discard splitTestFile("tests/tfakename.nim") diff --git a/tests/testdata/mycert.pem b/tests/testdata/mycert.pem index 61dcb685c..69039fe04 100644 --- a/tests/testdata/mycert.pem +++ b/tests/testdata/mycert.pem @@ -1,81 +1,32 @@ -----BEGIN PRIVATE KEY----- -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= +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= -----END PRIVATE KEY----- -----BEGIN CERTIFICATE----- -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 +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= -----END CERTIFICATE----- diff --git a/tests/threads/t7172.nim b/tests/threads/t7172.nim deleted file mode 100644 index 87e89417b..000000000 --- a/tests/threads/t7172.nim +++ /dev/null @@ -1,34 +0,0 @@ -discard """ - disabled: i386 - output: ''' -In doStuff() -In initProcess() -TEST -initProcess() done -Crashes before getting here! -''' - joinable: false -""" - -import std/os -import std/typedthreads - -proc whatever() {.thread, nimcall.} = - echo("TEST") - -proc initProcess(): void = - echo("In initProcess()") - var thread: Thread[void] - createThread(thread, whatever) - joinThread(thread) - echo("initProcess() done") - -proc doStuff(): void = - echo("In doStuff()") - # ... - initProcess() - sleep(500) - # ... - echo("Crashes before getting here!") - -doStuff() diff --git a/tests/threads/t8535.nim b/tests/threads/t8535.nim index a4296df11..1af11f9ad 100644 --- a/tests/threads/t8535.nim +++ b/tests/threads/t8535.nim @@ -1,5 +1,4 @@ discard """ - disabled: i386 output: '''0 hello''' """ diff --git a/tests/threads/threadex.nim b/tests/threads/threadex.nim index 90119aee7..50a1a4d34 100644 --- a/tests/threads/threadex.nim +++ b/tests/threads/threadex.nim @@ -1,5 +1,4 @@ discard """ - disabled: i386 outputsub: "Just a simple text for test" """ diff --git a/tests/threads/tjsthreads.nim b/tests/threads/tjsthreads.nim deleted file mode 100644 index 5122c9cd6..000000000 --- a/tests/threads/tjsthreads.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - targets: "js" - matrix: "--threads:on" -""" - -echo 123 diff --git a/tests/threads/tmanyjoin.nim b/tests/threads/tmanyjoin.nim index af5bc460e..aca284965 100644 --- a/tests/threads/tmanyjoin.nim +++ b/tests/threads/tmanyjoin.nim @@ -1,5 +1,4 @@ discard """ - disabled: i386 outputsub: "129" """ diff --git a/tests/threads/tonthreadcreation.nim b/tests/threads/tonthreadcreation.nim index 61529477d..f588a21c9 100644 --- a/tests/threads/tonthreadcreation.nim +++ b/tests/threads/tonthreadcreation.nim @@ -1,6 +1,4 @@ discard """ - disabled: i386 - matrix: "--mm:refc; --mm:orc --deepcopy:on" output: '''some string here dying some string here''' """ diff --git a/tests/threads/tracy_allocator.nim b/tests/threads/tracy_allocator.nim index f3b39f4dc..e8f0ec927 100644 --- a/tests/threads/tracy_allocator.nim +++ b/tests/threads/tracy_allocator.nim @@ -1,5 +1,4 @@ discard """ - disabled: i386 output: '''true''' """ diff --git a/tests/threads/treusetvar.nim b/tests/threads/treusetvar.nim index f0337801a..2b0017595 100644 --- a/tests/threads/treusetvar.nim +++ b/tests/threads/treusetvar.nim @@ -1,5 +1,4 @@ discard """ - disabled: i386 outputsub: "65" """ diff --git a/tests/threads/tthreadvars.nim b/tests/threads/tthreadvars.nim index 745e3562c..81aa2e5ec 100644 --- a/tests/threads/tthreadvars.nim +++ b/tests/threads/tthreadvars.nim @@ -1,5 +1,4 @@ discard """ -disabled: i386 output: ''' 10 1111 diff --git a/tests/threads/ttryrecv.nim b/tests/threads/ttryrecv.nim index fcff94e78..cadf6c550 100644 --- a/tests/threads/ttryrecv.nim +++ b/tests/threads/ttryrecv.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" outputsub: "channel is empty" """ diff --git a/tests/tools/compile/config.nims b/tests/tools/compile/config.nims deleted file mode 100644 index a19545668..000000000 --- a/tests/tools/compile/config.nims +++ /dev/null @@ -1 +0,0 @@ -switch("path", "$lib/../") \ No newline at end of file diff --git a/tests/tools/compile/readme.md b/tests/tools/compile/readme.md deleted file mode 100644 index cb5058e02..000000000 --- a/tests/tools/compile/readme.md +++ /dev/null @@ -1 +0,0 @@ -Test whether the tools compile. \ No newline at end of file diff --git a/tests/tools/compile/tdeps.nim b/tests/tools/compile/tdeps.nim deleted file mode 100644 index 971ca1b8e..000000000 --- a/tests/tools/compile/tdeps.nim +++ /dev/null @@ -1,5 +0,0 @@ -discard """ - action: compile -""" - -include tools/deps diff --git a/tests/tools/compile/tdetect.nim b/tests/tools/compile/tdetect.nim deleted file mode 100644 index 253fc0d39..000000000 --- a/tests/tools/compile/tdetect.nim +++ /dev/null @@ -1,5 +0,0 @@ -discard """ - action: compile -""" - -include tools/detect/detect diff --git a/tests/tools/compile/tkoch.nim b/tests/tools/compile/tkoch.nim deleted file mode 100644 index 008721dc0..000000000 --- a/tests/tools/compile/tkoch.nim +++ /dev/null @@ -1,5 +0,0 @@ -discard """ - action: compile -""" - -include koch \ No newline at end of file diff --git a/tests/tools/config.nims b/tests/tools/config.nims deleted file mode 100644 index b4bb92b30..000000000 --- a/tests/tools/config.nims +++ /dev/null @@ -1,2 +0,0 @@ ---d:nimPreviewSlimSystem ---d:nimPreviewCstringConversion \ No newline at end of file diff --git a/tests/tools/tlinter.nim b/tests/tools/tlinter.nim index 16f67905e..a0ed9bfc2 100644 --- a/tests/tools/tlinter.nim +++ b/tests/tools/tlinter.nim @@ -1,13 +1,13 @@ discard """ cmd: '''nim c --styleCheck:hint $file''' nimout: ''' -tlinter.nim(25, 1) Hint: 'tyPE' should be: 'type' [Name] tlinter.nim(21, 14) Hint: 'nosideeffect' should be: 'noSideEffect' [Name] -tlinter.nim(21, 28) Hint: 'myown' should be: 'myOwn' [template declared in tlinter.nim(19, 9)] [Name] +tlinter.nim(21, 28) Hint: 'myown' should be: 'myOwn' [Name] tlinter.nim(21, 35) Hint: 'inLine' should be: 'inline' [Name] -tlinter.nim(23, 1) Hint: 'foO' should be: 'foo' [proc declared in tlinter.nim(21, 6)] [Name] -tlinter.nim(27, 14) Hint: 'Foo_bar' should be: 'FooBar' [type declared in tlinter.nim(25, 6)] [Name] -tlinter.nim(29, 6) Hint: 'someVAR' should be: 'someVar' [var declared in tlinter.nim(27, 5)] [Name] +tlinter.nim(25, 1) Hint: 'tyPE' should be: 'type' [Name] +tlinter.nim(23, 1) Hint: 'foO' should be: 'foo' [Name] +tlinter.nim(27, 14) Hint: 'Foo_bar' should be: 'FooBar' [Name] +tlinter.nim(29, 6) Hint: 'someVAR' should be: 'someVar' [Name] tlinter.nim(32, 7) Hint: 'i_fool' should be: 'iFool' [Name] tlinter.nim(39, 5) Hint: 'meh_field' should be: 'mehField' [Name] ''' diff --git a/tests/tools/tnimgrep.nim b/tests/tools/tnimgrep.nim deleted file mode 100644 index 890a36e79..000000000 --- a/tests/tools/tnimgrep.nim +++ /dev/null @@ -1,404 +0,0 @@ -discard """ - output: ''' - -[Suite] nimgrep filesystem - -[Suite] nimgrep contents filtering -''' -""" -## Authors: quantimnot, a-mr - -import std/[osproc, os, streams, unittest, strutils] - -import std/syncio - -#======= -# 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.. can't use a template +block t4479: + type + MyTuple = tuple + num: int + strings: seq[string] + ints: seq[int] -static: - main() - mainProc() + var foo = MyTuple(( + num: 7, + strings: @[], + ints: @[], + )) -main() -mainProc() + var bar = ( + num: 7, + strings: @[], + ints: @[], + ).MyTuple + + var fooUnnamed = MyTuple((7, @[], @[])) + var n = 7 + var fooSym = MyTuple((num: n, strings: @[], ints: @[])) + + +block t1910: + var p = newOrderedTable[tuple[a:int], int]() + var q = newOrderedTable[tuple[x:int], int]() + for key in p.keys: + echo key.a + for key in q.keys: + echo key.x + + +block t2121: + type + Item[K,V] = tuple + key: K + value: V + + var q = newseq[Item[int,int]](1) + let (x,y) = q[0] + + +block t2369: + type HashedElem[T] = tuple[num: int, storedVal: ref T] + + proc append[T](tab: var seq[HashedElem[T]], n: int, val: ref T) = + #tab.add((num: n, storedVal: val)) + var he: HashedElem[T] = (num: n, storedVal: val) + #tab.add(he) + + var g: seq[HashedElem[int]] = @[] + + proc foo() = + var x: ref int + new(x) + x[] = 77 + g.append(44, x) + + +block t1986: + proc test(): int64 = + return 0xdeadbeef.int64 + + const items = [ + (var1: test(), var2: 100'u32), + (var1: test(), var2: 192'u32) + ] + +# bug #14911 +echo (a: 1) # works +echo (`a`: 1) # works +echo (`a`: 1, `b`: 2) # Error: named expression expected diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim index 9cad6eccd..7136c8cab 100644 --- a/tests/tuples/tuple_with_nil.nim +++ b/tests/tuples/tuple_with_nil.nim @@ -477,7 +477,7 @@ proc writeformat(o: var Writer; b: bool; fmt: Format) = else: raise newException(FormatError, "Boolean values must of one of the following types: s,b,o,x,X,d,n") -proc writeformat(o: var Writer; ary: openArray[system.any]; fmt: Format) = +proc writeformat(o: var Writer; ary: openarray[system.any]; fmt: Format) = ## Write array `ary` according to format `fmt` using output object ## `o` and output function `add`. if ary.len == 0: return @@ -657,7 +657,7 @@ proc literal[T](x: T): NimNode {.compiletime, nosideeffect.} = result = newLit(x) proc generatefmt(fmtstr: string; - args: var openArray[tuple[arg:NimNode, cnt:int]]; + args: var openarray[tuple[arg:NimNode, cnt:int]]; arg: var int;): seq[tuple[val, fmt:NimNode]] {.compiletime.} = ## fmtstr ## the format string diff --git a/tests/typerel/t4799_1.nim b/tests/typerel/t4799_1.nim index 03d2a0cfa..74012190b 100644 --- a/tests/typerel/t4799_1.nim +++ b/tests/typerel/t4799_1.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" targets: "c cpp" outputsub: '''ObjectAssignmentDefect''' exitcode: "1" diff --git a/tests/typerel/t4799_2.nim b/tests/typerel/t4799_2.nim index ef8571f25..f97b89622 100644 --- a/tests/typerel/t4799_2.nim +++ b/tests/typerel/t4799_2.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" targets: "c cpp" outputsub: '''ObjectAssignmentDefect''' exitcode: "1" diff --git a/tests/typerel/t4799_3.nim b/tests/typerel/t4799_3.nim index 26258b983..6102b69cc 100644 --- a/tests/typerel/t4799_3.nim +++ b/tests/typerel/t4799_3.nim @@ -1,5 +1,4 @@ discard """ - matrix: "--mm:refc" targets: "c cpp" outputsub: '''ObjectAssignmentDefect''' exitcode: "1" diff --git a/tests/typerel/t7600_1.nim b/tests/typerel/t7600_1.nim index 83f93ae7f..e9d01bd0d 100644 --- a/tests/typerel/t7600_1.nim +++ b/tests/typerel/t7600_1.nim @@ -1,6 +1,6 @@ discard """ errormsg: "type mismatch: got " -nimout: '''t7600_1.nim(21, 1) Error: type mismatch: got +nimout: '''t7600_1.nim(21, 6) Error: type mismatch: got but expected one of: proc test[T](x: Paper[T]) first type mismatch at position: 1 diff --git a/tests/typerel/t7600_2.nim b/tests/typerel/t7600_2.nim index 9488a44bc..371707f4c 100644 --- a/tests/typerel/t7600_2.nim +++ b/tests/typerel/t7600_2.nim @@ -1,6 +1,6 @@ discard """ errormsg: "type mismatch: got " -nimout: '''t7600_2.nim(20, 1) Error: type mismatch: got +nimout: '''t7600_2.nim(20, 6) Error: type mismatch: got but expected one of: proc test(x: Paper) first type mismatch at position: 1 diff --git a/tests/typerel/tcommontype.nim b/tests/typerel/tcommontype.nim index f32228177..8215ebd5e 100644 --- a/tests/typerel/tcommontype.nim +++ b/tests/typerel/tcommontype.nim @@ -1,5 +1,5 @@ type - TAnimal{.inheritable.}=object + TAnimal=object {.inheritable.} PAnimal=ref TAnimal TDog=object of TAnimal @@ -12,7 +12,7 @@ type proc newDog():PDog = new(result) proc newCat():PCat = new(result) -proc test(a:openArray[PAnimal])= +proc test(a:openarray[PAnimal])= echo("dummy") #test(newDog(),newCat()) #does not work diff --git a/tests/typerel/texplicitcmp.nim b/tests/typerel/texplicitcmp.nim index 57f1e81b6..b11aa2f4e 100644 --- a/tests/typerel/texplicitcmp.nim +++ b/tests/typerel/texplicitcmp.nim @@ -8,7 +8,7 @@ discard """ import json, tables, algorithm -proc outp(a: openArray[int]) = +proc outp(a: openarray[int]) = stdout.write "[" for i in a: stdout.write($i & " ") stdout.write "]\n" diff --git a/tests/typerel/tint.nim b/tests/typerel/tint.nim deleted file mode 100644 index de0e78a81..000000000 --- a/tests/typerel/tint.nim +++ /dev/null @@ -1,4 +0,0 @@ - -template a(T: type int32) = discard -template a(T: type int64) = discard -a(int) diff --git a/tests/typerel/tnoargopenarray.nim b/tests/typerel/tnoargopenarray.nim new file mode 100644 index 000000000..9e2b2fb86 --- /dev/null +++ b/tests/typerel/tnoargopenarray.nim @@ -0,0 +1,8 @@ +discard """ +action: compile +""" + +import db_sqlite + +var db: DbConn +exec(db, sql"create table blabla()") diff --git a/tests/typerel/tsigmatch.nim b/tests/typerel/tsigmatch.nim deleted file mode 100644 index 7541f2028..000000000 --- a/tests/typerel/tsigmatch.nim +++ /dev/null @@ -1,6 +0,0 @@ -block: # bug #13618 - proc test(x: Natural or BackwardsIndex): int = - int(x) - - doAssert test(^1) == 1 - doAssert test(1) == 1 diff --git a/tests/typerel/tstr_as_openarray.nim b/tests/typerel/tstr_as_openarray.nim index 14cd21479..fc28d6c93 100644 --- a/tests/typerel/tstr_as_openarray.nim +++ b/tests/typerel/tstr_as_openarray.nim @@ -3,18 +3,18 @@ discard """ """ var s = "HI" -proc x (zz: openArray[char]) = +proc x (zz: openarray[char]) = discard x s -proc z [T] (zz: openArray[T]) = +proc z [T] (zz: openarray[T]) = discard z s z([s,s,s]) -proc y [T] (arg: var openArray[T]) = +proc y [T] (arg: var openarray[T]) = arg[0] = 'X' y s doAssert s == "XI" diff --git a/tests/typerel/ttypedesc_as_genericparam1.nim b/tests/typerel/ttypedesc_as_genericparam1.nim index 8fdcf217e..b7c3e727d 100644 --- a/tests/typerel/ttypedesc_as_genericparam1.nim +++ b/tests/typerel/ttypedesc_as_genericparam1.nim @@ -1,7 +1,6 @@ discard """ - matrix: "--mm:refc" - errormsg: "type mismatch: got " - line: 7 + errormsg: "type mismatch: got " + line: 6 """ # bug #3079, #1146 -echo repr(int) \ No newline at end of file +echo repr(int) diff --git a/tests/typerel/ttypedesc_as_genericparam1_orc.nim b/tests/typerel/ttypedesc_as_genericparam1_orc.nim deleted file mode 100644 index d528a7421..000000000 --- a/tests/typerel/ttypedesc_as_genericparam1_orc.nim +++ /dev/null @@ -1,5 +0,0 @@ -discard """ - matrix: "--mm:orc" -""" - -doAssert repr(int) == "int" diff --git a/tests/typerel/ttypedesc_as_genericparam2.nim b/tests/typerel/ttypedesc_as_genericparam2.nim index 882f66584..ea06606f9 100644 --- a/tests/typerel/ttypedesc_as_genericparam2.nim +++ b/tests/typerel/ttypedesc_as_genericparam2.nim @@ -1,7 +1,6 @@ discard """ - matrix: "--mm:refc" errormsg: "'repr' doesn't support 'void' type" - line: 10 + line: 9 """ # bug #2879 diff --git a/tests/typerel/ttypelessemptyset.nim b/tests/typerel/ttypelessemptyset.nim index a4b6f003a..5f49c33fd 100644 --- a/tests/typerel/ttypelessemptyset.nim +++ b/tests/typerel/ttypelessemptyset.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "statement returns no value that can be discarded" + errormsg: "internal error: invalid kind for lastOrd(tyEmpty)" """ var q = false discard (if q: {} else: {}) diff --git a/tests/typerel/ttypenoval.nim b/tests/typerel/ttypenoval.nim index ca6c920ec..c7829f9dd 100644 --- a/tests/typerel/ttypenoval.nim +++ b/tests/typerel/ttypenoval.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch: got but expected 'int'" + errormsg: "type mismatch: got but expected 'int'" file: "ttypenoval.nim" line: 38 """ diff --git a/tests/typerel/ttypenovalue.nim b/tests/typerel/ttypenovalue.nim index 4664253ea..9af978466 100644 --- a/tests/typerel/ttypenovalue.nim +++ b/tests/typerel/ttypenovalue.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "invalid type: 'typedesc[seq[tuple[title: string, body: string]]]' for var" + errormsg: "invalid type: 'type seq[tuple[title: string, body: string]]' for var" line: 7 """ diff --git a/tests/typerel/tvoid.nim b/tests/typerel/tvoid.nim index 8bb5691b8..1e46d016e 100644 --- a/tests/typerel/tvoid.nim +++ b/tests/typerel/tvoid.nim @@ -35,64 +35,3 @@ ReturnT[void]() echo ReturnT[string]("abc") nothing() -block: # typeof(stmt) - proc fn1(): auto = - discard - proc fn2(): auto = - 1 - doAssert type(fn1()) is void - doAssert typeof(fn1()) is void - doAssert typeof(fn1()) isnot int - - doAssert type(fn2()) isnot void - doAssert typeof(fn2()) isnot void - when typeof(fn1()) is void: discard - else: doAssert false - - doAssert typeof(1+1) is int - doAssert typeof((discard)) is void - - type A1 = typeof(fn1()) - doAssert A1 is void - type A2 = type(fn1()) - doAssert A2 is void - doAssert A2 is A1 - - when false: - # xxx: MCS/UFCS doesn't work here: Error: expression 'fn1()' has no type (or is ambiguous) - type A3 = fn1().type - proc bar[T](a: T): string = $T - doAssert bar(1) == "int" - doAssert bar(fn1()) == "void" - - proc bar2[T](a: T): bool = T is void - doAssert not bar2(1) - doAssert bar2(fn1()) - - block: - proc bar3[T](a: T): T = a - let a1 = bar3(1) - doAssert compiles(block: - let a1 = bar3(fn2())) - doAssert not compiles(block: - let a2 = bar3(fn1())) - doAssert compiles(block: bar3(fn1())) - doAssert compiles(bar3(fn1())) - doAssert typeof(bar3(fn1())) is void - doAssert not compiles(sizeof(bar3(fn1()))) - - block: - var a = 1 - doAssert typeof((a = 2)) is void - doAssert typeof((a = 2; a = 3)) is void - doAssert typeof(block: - a = 2; a = 3) is void - - block: - var a = 1 - template bad1 = echo (a; a = 2) - doAssert not compiles(bad1()) - - block: - template bad2 = echo (nonexistent; discard) - doAssert not compiles(bad2()) diff --git a/tests/typerel/typedescs2.nim b/tests/typerel/typedescs2.nim index a0308719d..0b0b12986 100644 --- a/tests/typerel/typedescs2.nim +++ b/tests/typerel/typedescs2.nim @@ -1,10 +1,10 @@ discard """ - errormsg: "invalid type: 'typedesc[Table]' for const" + errormsg: "invalid type: 'type Table' for const" file: "typedescs2.nim" line: 16 """ -# bug #9961 +# issue #9961 import typetraits import tables diff --git a/tests/types/t15836.nim b/tests/types/t15836.nim deleted file mode 100644 index 9c0c26dec..000000000 --- a/tests/types/t15836.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - errormsg: "type mismatch: got but expected 'int'" - line: 11 -""" - -proc takesProc[T](x: T, f: proc(x: T): int) = - echo f(x) + 2 - -takesProc(1, proc (a: int): int = 2) # ok, prints 4 -takesProc(1, proc (a: auto): auto = 2) # ok, prints 4 -takesProc(1, proc (a: auto): auto = "uh uh") # prints garbage diff --git a/tests/types/t15836_2.nim b/tests/types/t15836_2.nim deleted file mode 100644 index 9afef416a..000000000 --- a/tests/types/t15836_2.nim +++ /dev/null @@ -1,26 +0,0 @@ - -discard """ - action: "compile" - disabled: true -""" -import std/sugar - -type Tensor[T] = object - x: T - -proc numerical_gradient*[T](input: T, f: (proc(x: T): T), h = T(1e-5)): T {.inline.} = - result = default(T) - -proc numerical_gradient*[T](input: Tensor[T], f: (proc(x: Tensor[T]): T), h = T(1e-5)): Tensor[T] {.noinit.} = - result = default(Tensor[T]) - -proc conv2d*[T](input: Tensor[T]): Tensor[T] {.inline.} = - result = default(Tensor[T]) - -proc sum*[T](arg: Tensor[T]): T = default(T) - -proc sum*[T](arg: Tensor[T], axis: int): Tensor[T] {.noinit.} = default(Tensor[T]) - -let dinput = Tensor[int](x: 1) -let target_grad_input = dinput.numerical_gradient( - x => conv2d(x).sum()) diff --git a/tests/types/t21027.nim b/tests/types/t21027.nim deleted file mode 100644 index 3a992177a..000000000 --- a/tests/types/t21027.nim +++ /dev/null @@ -1,5 +0,0 @@ -discard """ - errormsg: "Invalid usage of cast, cast requires a type to convert to, e.g., cast[int](0d)." -""" -# bug #21027 -let x: uint64 = cast(5) diff --git a/tests/types/t21260.nim b/tests/types/t21260.nim deleted file mode 100644 index 90d6613c1..000000000 --- a/tests/types/t21260.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - errormsg: "illegal recursion in type 'Foo'" - line: 8 -""" - -type - Kind = enum kA, kB - Foo = object - case k: Kind: - of kA: - foo: Foo - of kB: - discard diff --git a/tests/types/tfinalobj.nim b/tests/types/tfinalobj.nim index ad3085132..94f2e4f0e 100644 --- a/tests/types/tfinalobj.nim +++ b/tests/types/tfinalobj.nim @@ -3,7 +3,7 @@ discard """ """ type - TA {.pure, final.} = object + TA = object {.pure, final.} x: string var diff --git a/tests/types/tillegaltyperecursion.nim b/tests/types/tillegaltyperecursion.nim index 372615c4d..4c53a8b0e 100644 --- a/tests/types/tillegaltyperecursion.nim +++ b/tests/types/tillegaltyperecursion.nim @@ -1,17 +1,66 @@ discard """ cmd: "nim $target --threads:on $options $file" errormsg: "illegal recursion in type 'TIRC'" - line: 12 + line: 16 """ +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 b79926356..01f2307c0 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* {.inheritable.} = ref object + TTreeIteratorA* = ref object {.inheritable.} TKeysIteratorA* = ref object of TTreeIteratorA #compiles - TTreeIterator* [T,D] {.inheritable.} = ref object + TTreeIterator* [T,D] = ref object {.inheritable.} TKeysIterator* [T,D] = ref object of TTreeIterator[T,D] #this not diff --git a/tests/types/titerable.nim b/tests/types/titerable.nim deleted file mode 100644 index fe27de4fd..000000000 --- a/tests/types/titerable.nim +++ /dev/null @@ -1,143 +0,0 @@ -discard """ - targets: "c js" -""" - -from stdtest/testutils import accept, reject, whenVMorJs - -# toSeq-like templates - -template toSeq2(a: iterable): auto = - var ret: seq[typeof(a)] - for ai in a: ret.add ai - ret - -template toSeq3(a: iterable[string]): auto = - var ret: seq[typeof(a)] - for ai in a: ret.add ai - ret - -template toSeq4[T](a: iterable[T]): auto = - var ret: seq[typeof(a)] - for ai in a: ret.add ai - ret - -template toSeq5[T: SomeInteger](a: iterable[T]): auto = - var ret: seq[typeof(a)] - for ai in a: ret.add ai - ret - -template toSeq6(a: iterable[int | float]): auto = - var ret: seq[typeof(a)] - for ai in a: ret.add ai - ret - -template toSeq7(a: iterable[seq]): auto = - var ret: seq[typeof(a)] - for ai in a: ret.add ai - ret - -template fn7b(a: untyped) = discard -template fn7c(a: typed) = discard -template fn7d(a: auto) = discard -template fn7e[T](a: T) = discard - -template fn8a(a: iterable) = discard -template fn8b[T](a: iterable[T]) = discard -template fn8c(a: iterable[int]) = discard - -template bad1 = - template fn4(a: int, b: iterable[float, int]) = - discard - -template bad2 = - proc fn4(a: iterable) = discard - -template bad3 = - proc fn4(a: iterable[int]) = discard - -template good4 = - template fn1(a: iterable) = discard - template fn2(a: iterable[int]) = discard - -# iterators -iterator iota(n: int): auto = - for i in 0.. /dev/null # 2>&1 redirects stderr to stdout (all output in stdout) -gdb -x gdb_pretty_printer_test.py --batch-silent --args gdbNew 2>&1 - - -# Do it all again, but with old runtime -nim c --debugger:native --mm:refc --out:gdbOld gdb_pretty_printer_test_program.nim &> /dev/null -echo "Running old runtime tests" -gdb -x gdb_pretty_printer_test.py --batch-silent --args gdbOld 2>&1 +# <(...) is a bash feature that makes the output of a command into a +# file handle. +# diff compares the two files, the expected output, and the file +# handle that is created by the execution of gdb. +diff ./gdb_pretty_printer_test_output.txt <(gdb -x gdb_pretty_printer_test.py --batch-silent --args gdb_pretty_printer_test_program 2>&1) +# The exit code of diff is forwarded as the exit code of this +# script. So when the comparison fails, the exit code of this script +# won't be 0. So this script should be embeddable in a test suite. diff --git a/tests/untestable/thttpclient_ssl.nim b/tests/untestable/thttpclient_ssl.nim new file mode 100644 index 000000000..038eba99d --- /dev/null +++ b/tests/untestable/thttpclient_ssl.nim @@ -0,0 +1,207 @@ +# +# +# Nim - SSL integration tests +# (c) Copyright 2017 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# +## Warning: this test performs external networking. +## Test with: +## ./bin/nim c -d:ssl -p:. --threads:on -r tests/untestable/thttpclient_ssl.nim +## +## See https://github.com/FedericoCeratto/ssl-comparison/blob/master/README.md +## for a comparison with other clients. + +import + httpclient, + net, + strutils, + threadpool, + unittest + + +type + # bad and dubious tests should not pass SSL validation + # "_broken" mark the test as skipped. Some tests have different + # behavior depending on OS and SSL version! + # TODO: chase and fix the broken tests + Category = enum + good, bad, dubious, good_broken, bad_broken, dubious_broken + CertTest = tuple[url:string, category:Category, desc: string] + +const certificate_tests: array[0..55, CertTest] = [ + ("https://wrong.host.badssl.com/", bad, "wrong.host"), + ("https://captive-portal.badssl.com/", bad, "captive-portal"), + ("https://expired.badssl.com/", bad, "expired"), + ("https://google.com/", good, "good"), + ("https://self-signed.badssl.com/", bad, "self-signed"), + ("https://untrusted-root.badssl.com/", bad, "untrusted-root"), + ("https://revoked.badssl.com/", bad_broken, "revoked"), + ("https://pinning-test.badssl.com/", bad_broken, "pinning-test"), + ("https://no-common-name.badssl.com/", dubious_broken, "no-common-name"), + ("https://no-subject.badssl.com/", dubious_broken, "no-subject"), + ("https://incomplete-chain.badssl.com/", dubious_broken, "incomplete-chain"), + ("https://sha1-intermediate.badssl.com/", bad, "sha1-intermediate"), + ("https://sha256.badssl.com/", good, "sha256"), + ("https://sha384.badssl.com/", good, "sha384"), + ("https://sha512.badssl.com/", good, "sha512"), + ("https://1000-sans.badssl.com/", good, "1000-sans"), + ("https://10000-sans.badssl.com/", good_broken, "10000-sans"), + ("https://ecc256.badssl.com/", good, "ecc256"), + ("https://ecc384.badssl.com/", good, "ecc384"), + ("https://rsa2048.badssl.com/", good, "rsa2048"), + ("https://rsa8192.badssl.com/", dubious_broken, "rsa8192"), + ("http://http.badssl.com/", good, "regular http"), + ("https://http.badssl.com/", bad_broken, "http on https URL"), # FIXME + ("https://cbc.badssl.com/", dubious, "cbc"), + ("https://rc4-md5.badssl.com/", bad, "rc4-md5"), + ("https://rc4.badssl.com/", bad, "rc4"), + ("https://3des.badssl.com/", bad, "3des"), + ("https://null.badssl.com/", bad, "null"), + ("https://mozilla-old.badssl.com/", bad_broken, "mozilla-old"), + ("https://mozilla-intermediate.badssl.com/", dubious_broken, "mozilla-intermediate"), + ("https://mozilla-modern.badssl.com/", good, "mozilla-modern"), + ("https://dh480.badssl.com/", bad, "dh480"), + ("https://dh512.badssl.com/", bad, "dh512"), + ("https://dh1024.badssl.com/", dubious_broken, "dh1024"), + ("https://dh2048.badssl.com/", good, "dh2048"), + ("https://dh-small-subgroup.badssl.com/", bad_broken, "dh-small-subgroup"), + ("https://dh-composite.badssl.com/", bad_broken, "dh-composite"), + ("https://static-rsa.badssl.com/", dubious, "static-rsa"), + ("https://tls-v1-0.badssl.com:1010/", dubious, "tls-v1-0"), + ("https://tls-v1-1.badssl.com:1011/", dubious, "tls-v1-1"), + ("https://invalid-expected-sct.badssl.com/", bad, "invalid-expected-sct"), + ("https://hsts.badssl.com/", good, "hsts"), + ("https://upgrade.badssl.com/", good, "upgrade"), + ("https://preloaded-hsts.badssl.com/", good, "preloaded-hsts"), + ("https://subdomain.preloaded-hsts.badssl.com/", bad, "subdomain.preloaded-hsts"), + ("https://https-everywhere.badssl.com/", good, "https-everywhere"), + ("https://long-extended-subdomain-name-containing-many-letters-and-dashes.badssl.com/", good, + "long-extended-subdomain-name-containing-many-letters-and-dashes"), + ("https://longextendedsubdomainnamewithoutdashesinordertotestwordwrapping.badssl.com/", good, + "longextendedsubdomainnamewithoutdashesinordertotestwordwrapping"), + ("https://superfish.badssl.com/", bad, "(Lenovo) Superfish"), + ("https://edellroot.badssl.com/", bad, "(Dell) eDellRoot"), + ("https://dsdtestprovider.badssl.com/", bad, "(Dell) DSD Test Provider"), + ("https://preact-cli.badssl.com/", bad, "preact-cli"), + ("https://webpack-dev-server.badssl.com/", bad, "webpack-dev-server"), + ("https://mitm-software.badssl.com/", bad, "mitm-software"), + ("https://sha1-2016.badssl.com/", dubious, "sha1-2016"), + ("https://sha1-2017.badssl.com/", bad, "sha1-2017"), +] + + +template evaluate(exception_msg: string, category: Category, desc: string) = + # Evaluate test outcome. Testes flagged as _broken are evaluated and skipped + let raised = (exception_msg.len > 0) + let should_not_raise = category in {good, dubious_broken, bad_broken} + if should_not_raise xor raised: + # we are seeing a known behavior + if category in {good_broken, dubious_broken, bad_broken}: + skip() + if raised: + # check exception_msg == "No SSL certificate found." or + doAssert exception_msg == "No SSL certificate found." or + exception_msg == "SSL Certificate check failed." or + exception_msg.contains("certificate verify failed") or + exception_msg.contains("key too small") or + exception_msg.contains("alert handshake failure") or + exception_msg.contains("bad dh p length") or + # TODO: This one should only triggers for 10000-sans + exception_msg.contains("excessive message size"), exception_msg + + else: + # this is unexpected + if raised: + echo " $# ($#) raised: $#" % [desc, $category, exception_msg] + else: + echo " $# ($#) did not raise" % [desc, $category] + if category in {good, dubious, bad}: + fail() + + +suite "SSL certificate check - httpclient": + + for i, ct in certificate_tests: + + test ct.desc: + var ctx = newContext(verifyMode=CVerifyPeer) + var client = newHttpClient(sslContext=ctx) + let exception_msg = + try: + let a = $client.getContent(ct.url) + "" + except: + getCurrentExceptionMsg() + + evaluate(exception_msg, ct.category, ct.desc) + + + +# threaded tests + + +type + TTOutcome = ref object + desc, exception_msg: string + category: Category + +proc run_t_test(ct: CertTest): TTOutcome {.thread.} = + ## Run test in a {.thread.} - return by ref + result = TTOutcome(desc:ct.desc, exception_msg:"", category: ct.category) + try: + var ctx = newContext(verifyMode=CVerifyPeer) + var client = newHttpClient(sslContext=ctx) + let a = $client.getContent(ct.url) + except: + result.exception_msg = getCurrentExceptionMsg() + + +suite "SSL certificate check - httpclient - threaded": + + # Spawn threads before the "test" blocks + var outcomes = newSeq[FlowVar[TTOutcome]](certificate_tests.len) + for i, ct in certificate_tests: + let t = spawn run_t_test(ct) + outcomes[i] = t + + # create "test" blocks and handle thread outputs + for t in outcomes: + let outcome = ^t # wait for a thread to terminate + + test outcome.desc: + + evaluate(outcome.exception_msg, outcome.category, outcome.desc) + + +# net tests + + +type NetSocketTest = tuple[hostname: string, port: Port, category:Category, desc: string] +const net_tests:array[0..3, NetSocketTest] = [ + ("imap.gmail.com", 993.Port, good, "IMAP"), + ("wrong.host.badssl.com", 443.Port, bad, "wrong.host"), + ("captive-portal.badssl.com", 443.Port, bad, "captive-portal"), + ("expired.badssl.com", 443.Port, bad, "expired"), +] +# TODO: ("null.badssl.com", 443.Port, bad_broken, "null"), + + +suite "SSL certificate check - sockets": + + for ct in net_tests: + + test ct.desc: + + var sock = newSocket() + var ctx = newContext() + ctx.wrapSocket(sock) + let exception_msg = + try: + sock.connect(ct.hostname, ct.port) + "" + except: + getCurrentExceptionMsg() + + evaluate(exception_msg, ct.category, ct.desc) diff --git a/tests/untestable/thttpclient_ssl_disabled.nim b/tests/untestable/thttpclient_ssl_disabled.nim index b95dad2c6..0f7e95831 100644 --- a/tests/untestable/thttpclient_ssl_disabled.nim +++ b/tests/untestable/thttpclient_ssl_disabled.nim @@ -5,32 +5,36 @@ # See the file "copying.txt", included in this # distribution, for details about the copyright. # +## Warning: this test performs external networking. ## Compile and run with: -## nim r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:nimDisableCertificateValidation -d:ssl -p:. tests/untestable/thttpclient_ssl_disabled.nim +## ./bin/nim c -d:nimDisableCertificateValidation -d:ssl -r -p:. tests/untestable/thttpclient_ssl_disabled.nim -from stdtest/testutils import enableRemoteNetworking -when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(openbsd)): - import httpclient, net, unittest +import httpclient, + net, + unittest, + os - const expired = "https://expired.badssl.com/" +from strutils import contains - doAssert defined(nimDisableCertificateValidation) +const expired = "https://expired.badssl.com/" - suite "SSL certificate check - disabled": +doAssert defined(nimDisableCertificateValidation) - test "httpclient in insecure mode": - var ctx = newContext(verifyMode = CVerifyPeer) - var client = newHttpClient(sslContext = ctx) - let a = $client.getContent(expired) +suite "SSL certificate check - disabled": - test "httpclient in insecure mode": - var ctx = newContext(verifyMode = CVerifyPeerUseEnvVars) - var client = newHttpClient(sslContext = ctx) - let a = $client.getContent(expired) + test "httpclient in insecure mode": + var ctx = newContext(verifyMode = CVerifyPeer) + var client = newHttpClient(sslContext = ctx) + let a = $client.getContent(expired) - test "net socket in insecure mode": - var sock = newSocket() - var ctx = newContext(verifyMode = CVerifyPeerUseEnvVars) - ctx.wrapSocket(sock) - sock.connect("expired.badssl.com", 443.Port) - sock.close + test "httpclient in insecure mode": + var ctx = newContext(verifyMode = CVerifyPeerUseEnvVars) + var client = newHttpClient(sslContext = ctx) + let a = $client.getContent(expired) + + test "net socket in insecure mode": + var sock = newSocket() + var ctx = newContext(verifyMode = CVerifyPeerUseEnvVars) + ctx.wrapSocket(sock) + sock.connect("expired.badssl.com", 443.Port) + sock.close diff --git a/tests/untestable/thttpclient_ssl_remotenetwork.nim b/tests/untestable/thttpclient_ssl_remotenetwork.nim deleted file mode 100644 index d2366d9a9..000000000 --- a/tests/untestable/thttpclient_ssl_remotenetwork.nim +++ /dev/null @@ -1,218 +0,0 @@ -# -# -# Nim - SSL integration tests -# (c) Copyright 2017 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# -## Test with: -## nim r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:ssl -p:. --threads:on tests/untestable/thttpclient_ssl_remotenetwork.nim -## -## See https://github.com/FedericoCeratto/ssl-comparison/blob/master/README.md -## for a comparison with other clients. - -from stdtest/testutils import enableRemoteNetworking -when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(windows) and not defined(openbsd)): - # Not supported on Windows due to old openssl version - import - httpclient, - net, - strutils, - threadpool, - unittest - - - type - # bad and dubious tests should not pass SSL validation - # "_broken" mark the test as skipped. Some tests have different - # behavior depending on OS and SSL version! - # TODO: chase and fix the broken tests - Category = enum - good, bad, dubious, good_broken, bad_broken, dubious_broken - CertTest = tuple[url:string, category:Category, desc: string] - - const certificate_tests: array[0..54, CertTest] = [ - ("https://wrong.host.badssl.com/", bad, "wrong.host"), - ("https://captive-portal.badssl.com/", bad, "captive-portal"), - ("https://expired.badssl.com/", bad, "expired"), - ("https://google.com/", good, "good"), - ("https://self-signed.badssl.com/", bad, "self-signed"), - ("https://untrusted-root.badssl.com/", bad, "untrusted-root"), - ("https://revoked.badssl.com/", bad_broken, "revoked"), - ("https://pinning-test.badssl.com/", bad_broken, "pinning-test"), - ("https://no-common-name.badssl.com/", bad, "no-common-name"), - ("https://no-subject.badssl.com/", bad, "no-subject"), - ("https://sha1-intermediate.badssl.com/", bad, "sha1-intermediate"), - ("https://sha256.badssl.com/", good, "sha256"), - ("https://sha384.badssl.com/", bad, "sha384"), - ("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_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"), - ("https://http.badssl.com/", bad_broken, "http on https URL"), # FIXME - ("https://cbc.badssl.com/", dubious, "cbc"), - ("https://rc4-md5.badssl.com/", bad, "rc4-md5"), - ("https://rc4.badssl.com/", bad, "rc4"), - ("https://3des.badssl.com/", bad, "3des"), - ("https://null.badssl.com/", bad, "null"), - ("https://mozilla-old.badssl.com/", bad_broken, "mozilla-old"), - ("https://mozilla-intermediate.badssl.com/", dubious_broken, "mozilla-intermediate"), - ("https://mozilla-modern.badssl.com/", good, "mozilla-modern"), - ("https://dh480.badssl.com/", bad, "dh480"), - ("https://dh512.badssl.com/", bad, "dh512"), - ("https://dh1024.badssl.com/", dubious_broken, "dh1024"), - ("https://dh2048.badssl.com/", good, "dh2048"), - ("https://dh-small-subgroup.badssl.com/", bad_broken, "dh-small-subgroup"), - ("https://dh-composite.badssl.com/", bad_broken, "dh-composite"), - ("https://static-rsa.badssl.com/", dubious, "static-rsa"), - ("https://tls-v1-0.badssl.com:1010/", dubious, "tls-v1-0"), - ("https://tls-v1-1.badssl.com:1011/", dubious, "tls-v1-1"), - ("https://invalid-expected-sct.badssl.com/", bad, "invalid-expected-sct"), - ("https://hsts.badssl.com/", good, "hsts"), - ("https://upgrade.badssl.com/", good, "upgrade"), - ("https://preloaded-hsts.badssl.com/", good, "preloaded-hsts"), - ("https://subdomain.preloaded-hsts.badssl.com/", bad, "subdomain.preloaded-hsts"), - ("https://https-everywhere.badssl.com/", good, "https-everywhere"), - ("https://long-extended-subdomain-name-containing-many-letters-and-dashes.badssl.com/", good, - "long-extended-subdomain-name-containing-many-letters-and-dashes"), - ("https://longextendedsubdomainnamewithoutdashesinordertotestwordwrapping.badssl.com/", good, - "longextendedsubdomainnamewithoutdashesinordertotestwordwrapping"), - ("https://superfish.badssl.com/", bad, "(Lenovo) Superfish"), - ("https://edellroot.badssl.com/", bad, "(Dell) eDellRoot"), - ("https://dsdtestprovider.badssl.com/", bad, "(Dell) DSD Test Provider"), - ("https://preact-cli.badssl.com/", bad, "preact-cli"), - ("https://webpack-dev-server.badssl.com/", bad, "webpack-dev-server"), - ("https://mitm-software.badssl.com/", bad, "mitm-software"), - ("https://sha1-2016.badssl.com/", dubious, "sha1-2016"), - ("https://sha1-2017.badssl.com/", bad, "sha1-2017"), - ] - - - template evaluate(exception_msg: string, category: Category, desc: string) = - # Evaluate test outcome. Tests flagged as `_broken` are evaluated and skipped - let raised = (exception_msg.len > 0) - let should_not_raise = category in {good, dubious_broken, bad_broken} - if should_not_raise xor raised: - # we are seeing a known behavior - if category in {good_broken, dubious_broken, bad_broken}: - skip() - if raised: - # check exception_msg == "No SSL certificate found." or - doAssert exception_msg == "No SSL certificate found." or - exception_msg == "SSL Certificate check failed." or - exception_msg.contains("certificate verify failed") or - exception_msg.contains("key too small") or - exception_msg.contains("alert handshake failure") or - exception_msg.contains("bad dh p length") or - # TODO: This one should only triggers for 10000-sans - exception_msg.contains("excessive message size"), exception_msg - - else: - # this is unexpected - var fatal = true - var msg = "" - if raised: - msg = " $# ($#) raised: $#" % [desc, $category, exception_msg] - if "500 Internal Server Error" in exception_msg: - # refs https://github.com/nim-lang/Nim/issues/16338#issuecomment-804300278 - # we got: `good (good) raised: 500 Internal Server Error` - fatal = false - msg.add " (http 500 => assuming this is not our problem)" - else: - msg = " $# ($#) did not raise" % [desc, $category] - - if category in {good, dubious, bad} and fatal: - echo "D20210322T121353: error: " & msg - fail() - else: - echo "D20210322T121353: warning: " & msg - - - suite "SSL certificate check - httpclient": - - for i, ct in certificate_tests: - - test ct.desc: - var ctx = newContext(verifyMode=CVerifyPeer) - var client = newHttpClient(sslContext=ctx) - let exception_msg = - try: - let a = $client.getContent(ct.url) - "" - except: - getCurrentExceptionMsg() - - evaluate(exception_msg, ct.category, ct.desc) - - - - # threaded tests - - - type - TTOutcome = ref object - desc, exception_msg: string - category: Category - - proc run_t_test(ct: CertTest): TTOutcome {.thread.} = - ## Run test in a {.thread.} - return by ref - result = TTOutcome(desc:ct.desc, exception_msg:"", category: ct.category) - try: - var ctx = newContext(verifyMode=CVerifyPeer) - var client = newHttpClient(sslContext=ctx) - let a = $client.getContent(ct.url) - except: - result.exception_msg = getCurrentExceptionMsg() - - - suite "SSL certificate check - httpclient - threaded": - when defined(nimTestsEnableFlaky) or not defined(linux): # xxx pending bug #16338 - # Spawn threads before the "test" blocks - var outcomes = newSeq[FlowVar[TTOutcome]](certificate_tests.len) - for i, ct in certificate_tests: - let t = spawn run_t_test(ct) - outcomes[i] = t - - # create "test" blocks and handle thread outputs - for t in outcomes: - let outcome = ^t # wait for a thread to terminate - test outcome.desc: - evaluate(outcome.exception_msg, outcome.category, outcome.desc) - else: - echo "skipped test" - - # net tests - - - type NetSocketTest = tuple[hostname: string, port: Port, category:Category, desc: string] - const net_tests:array[0..3, NetSocketTest] = [ - ("imap.gmail.com", 993.Port, good, "IMAP"), - ("wrong.host.badssl.com", 443.Port, bad, "wrong.host"), - ("captive-portal.badssl.com", 443.Port, bad, "captive-portal"), - ("expired.badssl.com", 443.Port, bad, "expired"), - ] - # TODO: ("null.badssl.com", 443.Port, bad_broken, "null"), - - - suite "SSL certificate check - sockets": - - for ct in net_tests: - - test ct.desc: - - var sock = newSocket() - var ctx = newContext() - ctx.wrapSocket(sock) - let exception_msg = - try: - sock.connect(ct.hostname, ct.port) - "" - except: - getCurrentExceptionMsg() - - evaluate(exception_msg, ct.category, ct.desc) diff --git a/tests/untestable/tpostgres.nim b/tests/untestable/tpostgres.nim index 8b1378917..d3397e53a 100644 --- a/tests/untestable/tpostgres.nim +++ b/tests/untestable/tpostgres.nim @@ -1 +1,328 @@ +import db_postgres, strutils + + +let db = open("localhost", "dom", "", "test") +db.exec(sql"DROP TABLE IF EXISTS myTable") +db.exec(sql("""CREATE TABLE myTable ( + id integer PRIMARY KEY, + name varchar(50) not null)""")) +let name = "Dom" +db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)", + name) +doAssert db.getValue(sql"SELECT name FROM myTable") == name +# Check issue #3513 +doAssert db.getValue(sql"SELECT name FROM myTable") == name + + +# issue #3560 +proc addToDb(conn: DbConn, fileId: int, fileName: string): int64 = + result = conn.insertId(sql("INSERT into files (id, filename) VALUES (?, ?)"), fileId, fileName) + +db.exec(sql"DROP TABLE IF EXISTS files") +db.exec(sql"DROP TABLE IF EXISTS fileobjects") +db.exec(sql("""CREATE TABLE FILEOBJECTS( + ID SERIAL PRIMARY KEY, + FILE_SIZE INT, + MD5 CHAR(32) NOT NULL UNIQUE + );""")) + +db.exec(sql("""CREATE TABLE FILES( + ID SERIAL PRIMARY KEY, + OBJECT_ID INT, + FILENAME TEXT NOT NULL, + URI TEXT, + SCHEME CHAR(10), + PUBLIC BOOLEAN DEFAULT FALSE, + CONSTRAINT fk1_fileobjs FOREIGN KEY (object_id) + REFERENCES fileobjects (id) MATCH SIMPLE + ON DELETE CASCADE + );""")) + +let f1 = db.addToDb(1, "hello.tmp") +doAssert f1 == 1 +let f2 = db.addToDb(2, "hello2.tmp") +doAssert f2 == 2 + +# PreparedStmt vs. normal query +try: + echo db.getValue(sql("select * from files where id = $1"), 1) + doAssert false, "Exception expected" +except DbError: + let msg = getCurrentExceptionMsg().normalize + doAssert "expects" in msg + doAssert "?" in msg + doAssert "parameter substitution" in msg + +doAssert db.getValue(sql("select filename from files where id = ?"), 1) == "hello.tmp" + +var first = prepare(db, "one", sql"select filename from files where id = $1", 1) +doAssert db.getValue(first, 1) == "hello.tmp" + +try: + var second = prepare(db, "two", sql"select filename from files where id = ?", 1) + doAssert false, "Exception expected" +except: + let msg = getCurrentExceptionMsg().normalize + doAssert "expects" in msg + doAssert "$1" in msg + doAssert "parameter substitution" in msg + +# issue #3569 +db.exec(SqlQuery("DROP TABLE IF EXISTS tags")) +db.exec(SqlQuery("CREATE TABLE tags(id serial UNIQUE, name varchar(255))")) + +for i in 1..10: + var name = "t" & $i + echo(name) + discard db.getRow( + SqlQuery("INSERT INTO tags(name) VALUES(\'$1\') RETURNING id" % [name])) + +# get column details +db.exec(SqlQuery("DROP TABLE IF EXISTS dbtypes;")) +db.exec(SqlQuery("DROP TYPE IF EXISTS custom_enum;")) +db.exec(SqlQuery("CREATE TYPE custom_enum AS ENUM ('1', '2', '3');")) +db.exec(SqlQuery("DROP TYPE IF EXISTS custom_composite;")) +db.exec(SqlQuery("CREATE TYPE custom_composite AS (r double precision, i double precision);")) +db.exec(SqlQuery("""CREATE TABLE dbtypes( + id serial UNIQUE, + bytea_col bytea, + smallint_col smallint, + integer_col integer, + bigint_col bigint, + decimal_col decimal, + numeric_col numeric, + real_col real, + double_precision_col double precision, + smallserial_col smallserial, + serial_col serial, + bigserial_col bigserial, + money_col money, + varchar_col varchar(10), + character_col character(1), + text_col text, + timestamp_col timestamp, + date_col date, + time_col time, + interval_col interval, + bool_col boolean, + custom_enum_col custom_enum, + point_col point, + line_col line, + lseg_col lseg, + box_col box, + path_col path, + polygon_col polygon, + circle_col circle, + cidr_col cidr, + inet_col inet, + macaddr_col macaddr, + bit_col bit, + varbit_col bit(3), + tsvector_col tsvector, + tsquery_col tsquery, + uuid_col uuid, + xml_col xml, + json_col json, + array_col integer[], + custom_composite_col custom_composite, + range_col int4range + );""")) +db.exec(SqlQuery("INSERT INTO dbtypes (id) VALUES(0);")) + +var dbCols : DbColumns = @[] +for row in db.instantRows(dbCols, sql"SELECT * FROM dbtypes"): + doAssert len(dbCols) == 42 + +doAssert dbCols[0].name == "id" +doAssert dbCols[0].typ.kind == DbTypeKind.dbInt +doAssert dbCols[0].typ.name == "int4" +doAssert dbCols[0].typ.size == 4 + +doAssert dbCols[1].name == "bytea_col" +doAssert dbCols[1].typ.kind == DbTypeKind.dbBlob +doAssert dbCols[1].typ.name == "bytea" + +doAssert dbCols[2].name == "smallint_col" +doAssert dbCols[2].typ.kind == DbTypeKind.dbInt +doAssert dbCols[2].typ.name == "int2" +doAssert dbCols[2].typ.size == 2 + +doAssert dbCols[3].name == "integer_col" +doAssert dbCols[3].typ.kind == DbTypeKind.dbInt +doAssert dbCols[3].typ.name == "int4" +doAssert dbCols[3].typ.size == 4 + +doAssert dbCols[4].name == "bigint_col" +doAssert dbCols[4].typ.kind == DbTypeKind.dbInt +doAssert dbCols[4].typ.name == "int8" +doAssert dbCols[4].typ.size == 8 + +doAssert dbCols[5].name == "decimal_col" +doAssert dbCols[5].typ.kind == DbTypeKind.dbDecimal +doAssert dbCols[5].typ.name == "numeric" + +doAssert dbCols[6].name == "numeric_col" +doAssert dbCols[6].typ.kind == DbTypeKind.dbDecimal +doAssert dbCols[6].typ.name == "numeric" + +doAssert dbCols[7].name == "real_col" +doAssert dbCols[7].typ.kind == DbTypeKind.dbFloat +doAssert dbCols[7].typ.name == "float4" + +doAssert dbCols[8].name == "double_precision_col" +doAssert dbCols[8].typ.kind == DbTypeKind.dbFloat +doAssert dbCols[8].typ.name == "float8" + +doAssert dbCols[9].name == "smallserial_col" +doAssert dbCols[9].typ.kind == DbTypeKind.dbInt +doAssert dbCols[9].typ.name == "int2" + +doAssert dbCols[10].name == "serial_col" +doAssert dbCols[10].typ.kind == DbTypeKind.dbInt +doAssert dbCols[10].typ.name == "int4" + +doAssert dbCols[11].name == "bigserial_col" +doAssert dbCols[11].typ.kind == DbTypeKind.dbInt +doAssert dbCols[11].typ.name == "int8" + +doAssert dbCols[12].name == "money_col" +doAssert dbCols[12].typ.kind == DbTypeKind.dbDecimal +doAssert dbCols[12].typ.name == "money" + +doAssert dbCols[13].name == "varchar_col" +doAssert dbCols[13].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[13].typ.name == "varchar" + +doAssert dbCols[14].name == "character_col" +doAssert dbCols[14].typ.kind == DbTypeKind.dbFixedChar +doAssert dbCols[14].typ.name == "bpchar" + +doAssert dbCols[15].name == "text_col" +doAssert dbCols[15].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[15].typ.name == "text" + +doAssert dbCols[16].name == "timestamp_col" +doAssert dbCols[16].typ.kind == DbTypeKind.dbTimestamp +doAssert dbCols[16].typ.name == "timestamp" + +doAssert dbCols[17].name == "date_col" +doAssert dbCols[17].typ.kind == DbTypeKind.dbDate +doAssert dbCols[17].typ.name == "date" + +doAssert dbCols[18].name == "time_col" +doAssert dbCols[18].typ.kind == DbTypeKind.dbTime +doAssert dbCols[18].typ.name == "time" + +doAssert dbCols[19].name == "interval_col" +doAssert dbCols[19].typ.kind == DbTypeKind.dbTimeInterval +doAssert dbCols[19].typ.name == "interval" + +doAssert dbCols[20].name == "bool_col" +doAssert dbCols[20].typ.kind == DbTypeKind.dbBool +doAssert dbCols[20].typ.name == "bool" + +doAssert dbCols[21].name == "custom_enum_col" +doAssert dbCols[21].typ.kind == DbTypeKind.dbUnknown +doAssert parseInt(dbCols[21].typ.name) > 0 + +doAssert dbCols[22].name == "point_col" +doAssert dbCols[22].typ.kind == DbTypeKind.dbPoint +doAssert dbCols[22].typ.name == "point" + +doAssert dbCols[23].name == "line_col" +doAssert dbCols[23].typ.kind == DbTypeKind.dbLine +doAssert dbCols[23].typ.name == "line" + +doAssert dbCols[24].name == "lseg_col" +doAssert dbCols[24].typ.kind == DbTypeKind.dbLseg +doAssert dbCols[24].typ.name == "lseg" + +doAssert dbCols[25].name == "box_col" +doAssert dbCols[25].typ.kind == DbTypeKind.dbBox +doAssert dbCols[25].typ.name == "box" + +doAssert dbCols[26].name == "path_col" +doAssert dbCols[26].typ.kind == DbTypeKind.dbPath +doAssert dbCols[26].typ.name == "path" + +doAssert dbCols[27].name == "polygon_col" +doAssert dbCols[27].typ.kind == DbTypeKind.dbPolygon +doAssert dbCols[27].typ.name == "polygon" + +doAssert dbCols[28].name == "circle_col" +doAssert dbCols[28].typ.kind == DbTypeKind.dbCircle +doAssert dbCols[28].typ.name == "circle" + +doAssert dbCols[29].name == "cidr_col" +doAssert dbCols[29].typ.kind == DbTypeKind.dbInet +doAssert dbCols[29].typ.name == "cidr" + +doAssert dbCols[30].name == "inet_col" +doAssert dbCols[30].typ.kind == DbTypeKind.dbInet +doAssert dbCols[30].typ.name == "inet" + +doAssert dbCols[31].name == "macaddr_col" +doAssert dbCols[31].typ.kind == DbTypeKind.dbMacAddress +doAssert dbCols[31].typ.name == "macaddr" + +doAssert dbCols[32].name == "bit_col" +doAssert dbCols[32].typ.kind == DbTypeKind.dbBit +doAssert dbCols[32].typ.name == "bit" + +doAssert dbCols[33].name == "varbit_col" +doAssert dbCols[33].typ.kind == DbTypeKind.dbBit +doAssert dbCols[33].typ.name == "bit" + +doAssert dbCols[34].name == "tsvector_col" +doAssert dbCols[34].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[34].typ.name == "tsvector" + +doAssert dbCols[35].name == "tsquery_col" +doAssert dbCols[35].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[35].typ.name == "tsquery" + +doAssert dbCols[36].name == "uuid_col" +doAssert dbCols[36].typ.kind == DbTypeKind.dbVarchar +doAssert dbCols[36].typ.name == "uuid" + +doAssert dbCols[37].name == "xml_col" +doAssert dbCols[37].typ.kind == DbTypeKind.dbXml +doAssert dbCols[37].typ.name == "xml" + +doAssert dbCols[38].name == "json_col" +doAssert dbCols[38].typ.kind == DbTypeKind.dbJson +doAssert dbCols[38].typ.name == "json" + +doAssert dbCols[39].name == "array_col" +doAssert dbCols[39].typ.kind == DbTypeKind.dbArray +doAssert dbCols[39].typ.name == "int4[]" + +doAssert dbCols[40].name == "custom_composite_col" +doAssert dbCols[40].typ.kind == DbTypeKind.dbUnknown +doAssert parseInt(dbCols[40].typ.name) > 0 + +doAssert dbCols[41].name == "range_col" +doAssert dbCols[41].typ.kind == DbTypeKind.dbComposite +doAssert dbCols[41].typ.name == "int4range" + +# issue 6571 +db.exec(sql"DROP TABLE IF EXISTS DICTIONARY") +db.exec(sql("""CREATE TABLE DICTIONARY( + id SERIAL PRIMARY KEY, + entry VARCHAR(1000) NOT NULL, + definition VARCHAR(4000) NOT NULL + );""")) +var entry = "あっそ" +var definition = "(int) (See ああそうそう) oh, really (uninterested)/oh yeah?/hmmmmm" +discard db.getRow( + SqlQuery("INSERT INTO DICTIONARY(entry, definition) VALUES(\'$1\', \'$2\') RETURNING id" % [entry, definition])) +doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition +entry = "Format string entry" +definition = "Format string definition" +db.exec(sql"INSERT INTO DICTIONARY(entry, definition) VALUES (?, ?)", entry, definition) +doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition + +echo("All tests succeeded!") + +db.close() diff --git a/tests/usingstmt/tthis.nim b/tests/usingstmt/tthis.nim new file mode 100644 index 000000000..83d75d08c --- /dev/null +++ b/tests/usingstmt/tthis.nim @@ -0,0 +1,15 @@ + +# 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/misc/tcsharpusingstatement.nim b/tests/usingstmt/tusingstatement.nim similarity index 100% rename from tests/misc/tcsharpusingstatement.nim rename to tests/usingstmt/tusingstatement.nim diff --git a/tests/usingstmt/tusingstmt.nim b/tests/usingstmt/tusingstmt.nim deleted file mode 100644 index 11803878e..000000000 --- a/tests/usingstmt/tusingstmt.nim +++ /dev/null @@ -1,16 +0,0 @@ -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/valgrind/tleak_arc.nim b/tests/valgrind/tleak_arc.nim index c47ee137a..8dea7c62a 100644 --- a/tests/valgrind/tleak_arc.nim +++ b/tests/valgrind/tleak_arc.nim @@ -4,7 +4,7 @@ cmd: "nim $target --gc:arc -d:useMalloc $options $file" exitcode: 1 outputsub: " definitely lost: 7 bytes in 2 blocks" disabled: "freebsd" -disabled: "osx" +disabled: "macosx" disabled: "openbsd" disabled: "windows" disabled: "32bit" diff --git a/tests/varres/tprevent_forloopvar_mutations.nim b/tests/varres/tprevent_forloopvar_mutations.nim index c9aeb94d8..15f31d8a3 100644 --- a/tests/varres/tprevent_forloopvar_mutations.nim +++ b/tests/varres/tprevent_forloopvar_mutations.nim @@ -1,8 +1,9 @@ discard """ errormsg: "type mismatch: got " - nimout: '''tprevent_forloopvar_mutations.nim(16, 3) Error: type mismatch: got + line: 17 + nimout: '''type mismatch: got but expected one of: -proc inc[T, V: Ordinal](x: var T; y: V = 1) +proc inc[T: Ordinal](x: var T; y = 1) first type mismatch at position: 1 required type for x: var T: Ordinal but expression 'i' is immutable, not 'var' diff --git a/tests/views/t19986.nim b/tests/views/t19986.nim deleted file mode 100644 index 85a7cf97d..000000000 --- a/tests/views/t19986.nim +++ /dev/null @@ -1,42 +0,0 @@ -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/tcannot_borrow.nim b/tests/views/tcannot_borrow.nim index 0b8793159..d1c194b25 100644 --- a/tests/views/tcannot_borrow.nim +++ b/tests/views/tcannot_borrow.nim @@ -1,7 +1,8 @@ discard """ errormsg: "cannot borrow" - nimout: '''tcannot_borrow.nim(20, 7) Error: cannot borrow meh; what it borrows from is potentially mutated -tcannot_borrow.nim(21, 3) the mutation is here''' + nimout: '''tcannot_borrow.nim(21, 7) Error: cannot borrow meh; what it borrows from is potentially mutated +tcannot_borrow.nim(22, 3) the mutation is here''' + line: 21 """ diff --git a/tests/views/tconst_views.nim b/tests/views/tconst_views.nim index a85b03864..d7f1fc481 100644 --- a/tests/views/tconst_views.nim +++ b/tests/views/tconst_views.nim @@ -24,14 +24,3 @@ proc `$`(x: openArray[int]): string = echo c echo c2.data - -type MyObj = object - data: openarray[char] - -const - val1 = Foo(data: toOpenArray([1, 2, 3], 1, 1)) - val2 = Foo(data: toOpenArray([1, 2, 3], 0, 2)) - val3 = MyObj(data: "Hello".toOpenArray(0, 2)) -assert val1.data == [2] -assert val2.data == [1, 2, 3] -assert val3.data == "Hel" diff --git a/tests/views/tdont_mutate.nim b/tests/views/tdont_mutate.nim index eb5a82cbf..d243c7c7a 100644 --- a/tests/views/tdont_mutate.nim +++ b/tests/views/tdont_mutate.nim @@ -9,9 +9,8 @@ import tables 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 - ]# = +proc split*(s: string, seps: set[char] = Whitespace, + maxsplit: int = -1): Table[int, openArray[char]] = var last = 0 var splits = maxsplit result = initTable[int, openArray[char]]() @@ -23,7 +22,9 @@ proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): Table[ if splits == 0: last = len(s) result[first] = toOpenArray(s, first, last-1) - result[first][0] = 'c' + result[first][0] = 'c' #[tt.Error + attempt to mutate a borrowed location from an immutable view + ]# if splits == 0: break dec(splits) @@ -35,7 +36,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 b81b17f30..51f17b9d6 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -5,9 +5,7 @@ discard """ 3 2 3 -3 -15 -(oa: [1, 3, 4])''' +3''' targets: "c cpp" """ @@ -28,52 +26,3 @@ proc main(s: seq[int]) = take x main(@[11, 22, 33]) - -var x: int - -proc foo(x: var int): var int = - once: x = 42 - return x - -var y: var int = foo(x) -y = 15 -echo foo(x) -# bug #16132 - -# bug #18690 - -type - F = object - oa: openArray[int] - -let s1 = @[1,3,4,5,6] -var test = F(oa: toOpenArray(s1, 0, 2)) -echo test - -type - Foo = object - x: string - y: seq[int] - data: array[10000, byte] - - View[T] = object - x: lent T - -proc mainB = - let f = Foo(y: @[1, 2, 3]) - let foo = View[Foo](x: f) - assert foo.x.x == "" - 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/t17039.nim b/tests/vm/t17039.nim deleted file mode 100644 index f92c93f30..000000000 --- a/tests/vm/t17039.nim +++ /dev/null @@ -1,10 +0,0 @@ -type - Obj1 = object - case kind: bool - of false: - field: seq[int] - else: discard - -static: - var obj1 = Obj1() - obj1.field.add(@[]) diff --git a/tests/vm/t17121.nim b/tests/vm/t17121.nim deleted file mode 100644 index bf2d6423f..000000000 --- a/tests/vm/t17121.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - errormsg: "cannot 'importc' variable at compile time; c_printf" -""" - -proc c_printf*(frmt: cstring): cint {.importc: "printf", header: "", varargs, discardable.} = - ## foo bar - runnableExamples: discard -static: - let a = c_printf("abc\n") diff --git a/tests/vm/t19075.nim b/tests/vm/t19075.nim deleted file mode 100644 index 89ca9cb19..000000000 --- a/tests/vm/t19075.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - timeout: 10 - joinable: false -""" - -# bug #19075 -const size = 50_000 - -const stuff = block: - var a: array[size, int] - a - -const zeugs = block: - var zeugs: array[size, int] - for i in 0..= 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")] - -# bug #17864 -macro transform*(fn: typed) = - quote do: - `fn` - -var map: Table[string, HashSet[string]] -proc publish*(): void {.transform.} = - map["k"] = init_hash_set[string]() - map["k"].incl "d" - -publish() diff --git a/tests/vm/tnewseqofcap.nim b/tests/vm/tnewseqofcap.nim index a7dc07aa6..c61c3c485 100644 --- a/tests/vm/tnewseqofcap.nim +++ b/tests/vm/tnewseqofcap.nim @@ -1,3 +1,8 @@ +discard """ + output: '''@["aaa", "bbb", "ccc"]''' +""" + + const foo = @["aaa", "bbb", "ccc"] @@ -11,4 +16,4 @@ proc myTuple: tuple[n: int, bar: seq[string]] = const (n, bar) = myTuple() -doAssert bar == @["aaa", "bbb", "ccc"] \ No newline at end of file +echo bar \ No newline at end of file diff --git a/tests/vm/topenarrays.nim b/tests/vm/topenarrays.nim deleted file mode 100644 index 0a822f583..000000000 --- a/tests/vm/topenarrays.nim +++ /dev/null @@ -1,69 +0,0 @@ -proc mutate(a: var openarray[int]) = - var i = 0 - for x in a.mitems: - x = i - inc i - -proc mutate(a: var openarray[char]) = - var i = 1 - for ch in a.mitems: - ch = 'a' - - -static: - var a = [10, 20, 30] - assert a.toOpenArray(1, 2).len == 2 - - mutate(a) - assert a.toOpenArray(0, 2) == [0, 1, 2] - assert a.toOpenArray(0, 0) == [0] - assert a.toOpenArray(1, 2) == [1, 2] - assert "Hello".toOpenArray(1, 4) == "ello" - var str = "Hello" - str.toOpenArray(2, 4).mutate() - assert str.toOpenArray(0, 4).len == 5 - assert str.toOpenArray(0, 0).len == 1 - assert str.toOpenArray(0, 0).high == 0 - assert str == "Heaaa" - assert str.toOpenArray(0, 4) == "Heaaa" - - var arr: array[3..4, int] = [1, 2] - assert arr.toOpenArray(3, 4) == [1, 2] - assert arr.toOpenArray(3, 4).len == 2 - assert arr.toOpenArray(3, 3).high == 0 - - assert arr.toOpenArray(3, 4).toOpenArray(0, 0) == [1] - - -proc doThing(s: static openArray[int]) = discard - -doThing([10, 20, 30].toOpenArray(0, 0)) - -# bug #19969 -proc f(): array[1, byte] = - var a: array[1, byte] - result[0..0] = a.toOpenArray(0, 0) - -doAssert static(f()) == [byte(0)] - - -# bug #15952 -proc main1[T](a: openArray[T]) = discard -proc main2[T](a: var openArray[T]) = discard - -proc main = - var a = [1,2,3,4,5] - main1(a.toOpenArray(1,3)) - main2(a.toOpenArray(1,3)) -static: main() -main() - -# bug #16306 -{.experimental: "views".} -proc test(x: openArray[int]): tuple[id: int] = - let y: openArray[int] = toOpenArray(x, 0, 2) - result = (y[0],) -template fn= - doAssert test([0,1,2,3,4,5]).id == 0 -fn() # ok -static: fn() diff --git a/tests/vm/tquadplus.nim b/tests/vm/tquadplus.nim index acf20cd96..552e8fef7 100644 --- a/tests/vm/tquadplus.nim +++ b/tests/vm/tquadplus.nim @@ -1,5 +1,8 @@ # bug #1023 +discard """ + output: "1 == 1" +""" type Quadruple = tuple[a, b, c, d: int] @@ -11,4 +14,4 @@ const B = (a: 0, b: -2, c: 1, d: 0) C = A + B -doAssert $C.d & " == " & $(A+B).d == "1 == 1" +echo C.d, " == ", (A+B).d diff --git a/tests/vm/treset.nim b/tests/vm/treset.nim index 32fbc7f04..ff8bc5b83 100644 --- a/tests/vm/treset.nim +++ b/tests/vm/treset.nim @@ -1,3 +1,6 @@ +discard """ + output: '''0''' +""" static: type Obj = object field: int @@ -45,6 +48,6 @@ proc main = var x = 4 x = default(int) - doAssert x == 0 + echo x main() diff --git a/tests/vm/triangle_array.nim b/tests/vm/triangle_array.nim index 0704239c6..054c66f22 100644 --- a/tests/vm/triangle_array.nim +++ b/tests/vm/triangle_array.nim @@ -1,3 +1,7 @@ +discard """ + output: "56" +""" + # bug #1781 proc initCombinations: array[11, array[11, int]] = @@ -10,4 +14,4 @@ proc initCombinations: array[11, array[11, int]] = result[6][6 .. 10] = [52,53,54,55,56] const combinations = initCombinations() -doAssert combinations[6][10] == 56 +echo combinations[6][10] diff --git a/tests/vm/tstring_openarray.nim b/tests/vm/tstring_openarray.nim index 9318344f8..1b8a1304c 100644 --- a/tests/vm/tstring_openarray.nim +++ b/tests/vm/tstring_openarray.nim @@ -12,16 +12,16 @@ proc set_all[T](s: var openArray[T]; val: T) = for i in 0.. 0 - import tables block: # bug #8007 @@ -361,7 +317,7 @@ block: # bug #14340 envelopeSin[a]() block: - type Mutator = proc() {.noSideEffect, gcsafe.} + type Mutator = proc() {.noSideEffect, gcsafe, locks: 0.} proc mutator0() = discard const mTable = [Mutator(mutator0)] var i=0 @@ -376,7 +332,7 @@ block: # VM wrong register free causes errors in unrelated code in let prc = if not isClosure: bb.sym else: bb[0].sym ]# proc bar2(head: string): string = "asdf" - proc zook(u1: int) = discard + proc gook(u1: int) = discard type PathEntry = object kind: int @@ -385,20 +341,20 @@ block: # VM wrong register free causes errors in unrelated code iterator globOpt(): int = var u1: int - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) - zook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) var entry = PathEntry() entry.path = bar2("") @@ -496,163 +452,3 @@ block: # VM wrong register free causes errors in unrelated code proc main() = processAux(bar(globOpt(initGlobOpt(dir)))) static: main() - -block: # bug #8015 - block: - type Foo = object - case b: bool - of false: v1: int - of true: v2: int - const t = [Foo(b: false, v1: 1), Foo(b: true, v2: 2)] - doAssert $t == "[(b: false, v1: 1), (b: true, v2: 2)]" - doAssert $t[0] == "(b: false, v1: 1)" # was failing - - block: - type - CostKind = enum - Fixed, - Dynamic - - Cost = object - case kind*: CostKind - of Fixed: - cost*: int - of Dynamic: - handler*: proc(): int {.nimcall.} - - proc foo1(): int {.nimcall.} = - 100 - - proc foo2(): int {.nimcall.} = - 200 - - # Change to `let` and it doesn't crash - const costTable = [ - 0: Cost(kind: Fixed, cost: 999), - 1: Cost(kind: Dynamic, handler: foo1), - 2: Cost(kind: Dynamic, handler: foo2) - ] - - doAssert $costTable[0] == "(kind: Fixed, cost: 999)" - doAssert costTable[1].handler() == 100 - doAssert costTable[2].handler() == 200 - - # Now trying to carry the table as an object field - type - Wrapper = object - table: array[3, Cost] - - proc procNewWrapper(): Wrapper = - result.table = costTable - - # Alternatively, change to `const` and it doesn't crash - let viaProc = procNewWrapper() - - doAssert viaProc.table[1].handler != nil - doAssert viaProc.table[2].handler != nil - doAssert $viaProc.table[0] == "(kind: Fixed, cost: 999)" - doAssert viaProc.table[1].handler() == 100 - doAssert viaProc.table[2].handler() == 200 - - -# bug #19198 - -block: - type - Foo[n: static int] = int - -block: - static: - let x = int 1 - doAssert $(x.type) == "Foo" # Foo - -block: - static: - let x = int 1 - let y = x + 1 - # Error: unhandled exception: value out of range: -8 notin 0 .. 65535 [RangeDefect] - doAssert y == 2 - - -type Atom* = object - bar: int - -proc main() = # bug #12994 - var s: seq[Atom] - var atom: Atom - var checked = 0 - for i in 0..<2: - atom.bar = 5 - s.add atom - atom.reset - if i == 0: - checked += 1 - doAssert $s == "@[(bar: 5)]" - else: - checked += 1 - doAssert $s == "@[(bar: 5), (bar: 5)]" - doAssert checked == 2 - -static: main() -main() - -# bug #19201 -proc foo(s: sink string) = doAssert s.len == 3 - -static: - foo("abc") - - -static: - for i in '1' .. '2': # bug #10938 - var s: set[char] - doAssert s == {} - incl(s, i) - - for _ in 0 ..< 3: # bug #13312 - var s: string - s.add("foo") - doAssert s == "foo" - - for i in 1 .. 5: # bug #13918 - var arr: array[3, int] - var val: int - doAssert arr == [0, 0, 0] and val == 0 - for j in 0 ..< len(arr): - arr[j] = i - val = i - -# bug #20985 -let a = block: - var groups: seq[seq[int]] - for i in 0 ..< 3: - var group: seq[int] - for j in 0 ..< 3: - group.add j - groups.add group - groups - -const b = block: - var groups: seq[seq[int]] - for i in 0 ..< 3: - var group: seq[int] - for j in 0 ..< 3: - group.add j - groups.add group - groups - -doAssert a == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]] -doAssert b == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]] - -macro m1(s: string): int = - var ProcID {.global, compileTime.}: int - inc(ProcID) - result = newLit(ProcID) - -proc macroGlobal = - doAssert m1("Macro argument") == 1 - doAssert m1("Macro argument") == 2 - doAssert m1("Macro argument") == 3 - -static: macroGlobal() -macroGlobal() diff --git a/tests/vm/tvmops.nim b/tests/vm/tvmops.nim index 3d8d5a9ac..c8febd982 100644 --- a/tests/vm/tvmops.nim +++ b/tests/vm/tvmops.nim @@ -9,6 +9,16 @@ import os import math import strutils +template forceConst(a: untyped): untyped = + ## Force evaluation at CT, useful for example here: + ## `callFoo(forceConst(getBar1()), getBar2())` + ## instead of: + ## block: + ## const a = getBar1() + ## `callFoo(a, getBar2())` + const ret = a + ret + static: # TODO: add more tests block: #getAppFilename, gorgeEx, gorge @@ -16,7 +26,7 @@ static: let ret = gorgeEx(nim & " --version") doAssert ret.exitCode == 0 doAssert ret.output.contains "Nim Compiler" - let ret2 = gorgeEx(nim & " --nonxistent") + let ret2 = gorgeEx(nim & " --unexistant") doAssert ret2.exitCode != 0 let output3 = gorge(nim & " --version") doAssert output3.contains "Nim Compiler" @@ -41,6 +51,6 @@ static: block: # Check against bugs like #9176 - doAssert getCurrentCompilerExe() == getCurrentCompilerExe().static + doAssert getCurrentCompilerExe() == forceConst(getCurrentCompilerExe()) if false: #pending #9176 - doAssert gorgeEx("nonxistent") == gorgeEx("nonxistent").static + doAssert gorgeEx("unexistant") == forceConst(gorgeEx("unexistant")) diff --git a/tests/vm/tvmopsDanger.nim b/tests/vm/tvmopsDanger.nim deleted file mode 100644 index 5e7bac29a..000000000 --- a/tests/vm/tvmopsDanger.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - cmd: "nim c --experimental:vmopsDanger -r $file" -""" -when defined(nimPreviewSlimSystem): - import std/assertions -import std/times - -const foo = getTime() -let bar = foo -doAssert bar > low(Time) \ No newline at end of file diff --git a/tests/vm/twrong_concat.nim b/tests/vm/twrong_concat.nim index 59a10bdb9..b9cca8341 100644 --- a/tests/vm/twrong_concat.nim +++ b/tests/vm/twrong_concat.nim @@ -1,3 +1,7 @@ +discard """ + output: '''success''' +""" + # bug #3804 #import sequtils @@ -20,3 +24,5 @@ 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 8cb402bff..a3ad9b707 100644 --- a/tests/vm/tyaytypedesc.nim +++ b/tests/vm/tyaytypedesc.nim @@ -1,3 +1,7 @@ +discard """ + output: "ntWhitespace" +""" + # bug #3357 type NodeType* = enum @@ -6,7 +10,7 @@ type NodeType* = enum type TokenType* = enum ttWhitespace -proc enumTable*[A, B, C](a: openArray[tuple[key: A, val: B]], ret: typedesc[C]): C = +proc enumTable*[A, B, C](a: openarray[tuple[key: A, val: B]], ret: typedesc[C]): C = for item in a: result[item.key] = item.val @@ -14,4 +18,4 @@ const tokenTypeToNodeType = { ttWhitespace: ntWhitespace, }.enumTable(array[ttWhitespace..ttWhitespace, NodeType]) -doAssert tokenTypeToNodeType[ttWhitespace] == ntWhitespace +echo tokenTypeToNodeType[ttWhitespace] diff --git a/tests/vm/tzero_extend.nim b/tests/vm/tzero_extend.nim index 418dbc486..1fed5d419 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 int(cast[uint8](i8)) == 0xFD - doAssert int64(cast[uint8](i8)) == 0xFD - doAssert int(cast[uint16](i16)) == 0xFFFD - doAssert int64(cast[uint16](i16)) == 0xFFFD + doAssert i8.ze == 0xFD + doAssert i8.ze64 == 0xFD + doAssert i16.ze == 0xFFFD + doAssert i16.ze64 == 0xFFFD result[0] = @[]; result[1] = @[]; result[2] = @[] diff --git a/tests/whenstmt/t12517.nim b/tests/whenstmt/t12517.nim deleted file mode 100644 index 8be0171e1..000000000 --- a/tests/whenstmt/t12517.nim +++ /dev/null @@ -1,21 +0,0 @@ -# Test based on issue #12517 - -discard """ - nimout: ''' -nimvm -both -''' - output: ''' -both -''' -""" - -proc test() = - when nimvm: - echo "nimvm" - echo "both" - -static: - test() -test() - diff --git a/tests/whenstmt/t19426.nim b/tests/whenstmt/t19426.nim deleted file mode 100644 index 95fb54a9e..000000000 --- a/tests/whenstmt/t19426.nim +++ /dev/null @@ -1,16 +0,0 @@ -type - MyInt = object - bitWidth: int - -template toRealType*(t: MyInt): typedesc = - when t.bitWidth == 32: int32 - elif t.bitWidth == 64: int64 - else: {.error.} - -proc doFail(T: typedesc): T = default(T) - -proc test = - const myInt = MyInt(bitWidth:32) - discard doFail(toRealType(myInt)) - -test() \ No newline at end of file diff --git a/tests/whenstmt/twhen.nim b/tests/whenstmt/twhen.nim deleted file mode 100644 index b155ddcfb..000000000 --- a/tests/whenstmt/twhen.nim +++ /dev/null @@ -1,47 +0,0 @@ -discard """ - nimout: ''' -nimvm - when -nimvm - whenElif -nimvm - whenElse -''' - output: ''' -when -whenElif -whenElse -''' -""" - -# test both when and when nimvm to ensure proper evaluation - -proc compileOrRuntimeProc(s: string) = - when nimvm: - echo "nimvm - " & s - else: - echo s - -template output(s: string) = - static: - compileOrRuntimeProc(s) - compileOrRuntimeProc(s) - -when compiles(1): - output("when") -elif compiles(2): - output("fail - whenElif") -else: - output("fail - whenElse") - -when compiles(nonexistent): - output("fail - when") -elif compiles(1): - output("whenElif") -else: - output("fail - whenElse") - -when compiles(nonexistent): - output("fail - when") -elif compiles(nonexistent): - output("fail - whenElif") -else: - output("whenElse") - diff --git a/tests/whenstmt/twhen_macro.nim b/tests/whenstmt/twhen_macro.nim deleted file mode 100644 index deb1dddc9..000000000 --- a/tests/whenstmt/twhen_macro.nim +++ /dev/null @@ -1,18 +0,0 @@ -import macros - -# test that when stmt works from within a macro - -macro output(s: string, xs: varargs[untyped]): auto = - result = quote do: - when compiles(`s`): - "when - " & `s` - elif compiles(`s`): - "elif - " & `s` - # should never get here so this should not break - broken.xs - else: - "else - " & `s` - # should never get here so this should not break - more.broken.xs - -doAssert output("test") == "when - test" \ No newline at end of file diff --git a/tests/xml/ttree_add.nim b/tests/xml/ttree_add.nim deleted file mode 100644 index 4c6ef6cf9..000000000 --- a/tests/xml/ttree_add.nim +++ /dev/null @@ -1,51 +0,0 @@ -discard """ - output: ''' - -
    Some text in body
    -
    Some more text in body
    - - - -
    Some text
    -
    Some more text
    - - -
    Some text in body
    -
    Some more text in body
    - -
    -''' -""" - -# Test xmltree add/insert/delete/replace operations -import xmlparser -import xmltree -var baseDocHead = """ - - -
    Some text
    -
    Some more text
    - -
    -""" -var baseDocHeadTree = parseXml(baseDocHead) -var baseDocBody = """ - -
    Some text in body
    -
    Some more text in body
    - -""" -var baseDocBodyTree = parseXml(baseDocBody) - -proc test_add() = - var testDoc = baseDocHeadTree - var newBody = newElement("body") - for item in baseDocBodyTree.items(): - newBody.add(item) - - echo $newBody - - testDoc.add(newBody) - echo $testDoc - -test_add() diff --git a/tests/xml/ttree_add1.nim b/tests/xml/ttree_add1.nim deleted file mode 100644 index 30ec83c02..000000000 --- a/tests/xml/ttree_add1.nim +++ /dev/null @@ -1,53 +0,0 @@ -discard """ - output: ''' - -
    Some text in body
    -
    Some more text in body
    - - - -
    Some text
    -
    Some more text
    - - -
    Some text in body
    -
    Some more text in body
    - -
    -''' -""" - -# Test xmltree add/insert/delete/replace operations -import xmlparser -import xmltree -var baseDocHead = """ - - -
    Some text
    -
    Some more text
    - -
    -""" -var baseDocHeadTree = parseXml(baseDocHead) -var baseDocBody = """ - -
    Some text in body
    -
    Some more text in body
    - -""" -var baseDocBodyTree = parseXml(baseDocBody) - -proc test_add() = - var testDoc = baseDocHeadTree - var newBody = newElement("body") - var bodyItems: seq[XmlNode] = @[] - for item in baseDocBodyTree.items(): - bodyItems.add(item) - newBody.add(bodyItems) - - echo $newBody - - testDoc.add(newBody) - echo $testDoc - -test_add() diff --git a/tests/xml/ttree_delete.nim b/tests/xml/ttree_delete.nim deleted file mode 100644 index 32b477839..000000000 --- a/tests/xml/ttree_delete.nim +++ /dev/null @@ -1,47 +0,0 @@ -discard """ - output: ''' - - -
    Some text
    -
    Some more text
    - - -
    Some text in body
    -
    Some more text in body
    - -
    -''' -""" - -# Test xmltree add/insert/delete/replace operations -import xmlparser -import xmltree -let initialDocBase = """ - - -
    Some text
    -
    Some more text
    - - -
    MORE TEXT
    -
    MORE TEXT Some more text
    -
    - -
    MORE TEXT
    -
    MORE TEXT Some more text
    -
    - -
    Some text in body
    -
    Some more text in body
    - -
    -""" -var initialDocBaseTree = parseXml(initialDocBase) - -proc test_delete() = - var testDoc = initialDocBaseTree - - testDoc.delete(1..2) - echo $testDoc - -test_delete() diff --git a/tests/xml/ttree_delete1.nim b/tests/xml/ttree_delete1.nim deleted file mode 100644 index a8442a093..000000000 --- a/tests/xml/ttree_delete1.nim +++ /dev/null @@ -1,48 +0,0 @@ -discard """ - output: ''' - - -
    Some text
    -
    Some more text
    - - -
    Some text in body
    -
    Some more text in body
    - -
    -''' -""" - -# Test xmltree add/insert/delete/replace operations -import xmlparser -import xmltree -let initialDocBase = """ - - -
    Some text
    -
    Some more text
    - - -
    MORE TEXT
    -
    MORE TEXT Some more text
    -
    - -
    MORE TEXT
    -
    MORE TEXT Some more text
    -
    - -
    Some text in body
    -
    Some more text in body
    - -
    -""" -var initialDocBaseTree = parseXml(initialDocBase) - -proc test_delete() = - var testDoc = initialDocBaseTree - - testDoc.delete(1) - testDoc.delete(1) - echo $testDoc - -test_delete() diff --git a/tests/xml/ttree_insert.nim b/tests/xml/ttree_insert.nim deleted file mode 100644 index b2941395b..000000000 --- a/tests/xml/ttree_insert.nim +++ /dev/null @@ -1,53 +0,0 @@ -discard """ - output: ''' - -
    Some text in body
    -
    Some more text in body
    - - - -
    Some text
    -
    Some more text
    - - -
    Some text in body
    -
    Some more text in body
    - -
    -''' -""" - -# Test xmltree add/insert/delete/replace operations -import xmlparser -import xmltree -var baseDocHead = """ - - -
    Some text
    -
    Some more text
    - -
    -""" -var baseDocHeadTree = parseXml(baseDocHead) -var baseDocBody = """ - -
    Some text in body
    -
    Some more text in body
    - -""" -var baseDocBodyTree = parseXml(baseDocBody) - -proc test_insert() = - var testDoc = baseDocHeadTree - var newBody = newElement("body") - var bodyItems: seq[XmlNode] = @[] - for item in baseDocBodyTree.items(): - bodyItems.insert(item, len(bodyItems)) - newBody.insert(bodyItems, 1) - - echo $newBody - - testDoc.insert(newBody, 1) - echo $testDoc - -test_insert() diff --git a/tests/xml/ttree_insert1.nim b/tests/xml/ttree_insert1.nim deleted file mode 100644 index 9aa3faf69..000000000 --- a/tests/xml/ttree_insert1.nim +++ /dev/null @@ -1,51 +0,0 @@ -discard """ - output: ''' - -
    Some text in body
    -
    Some more text in body
    - - - -
    Some text
    -
    Some more text
    - - -
    Some text in body
    -
    Some more text in body
    - -
    -''' -""" - -# Test xmltree add/insert/delete/replace operations -import xmlparser -import xmltree -var baseDocHead = """ - - -
    Some text
    -
    Some more text
    - -
    -""" -var baseDocHeadTree = parseXml(baseDocHead) -var baseDocBody = """ - -
    Some text in body
    -
    Some more text in body
    - -""" -var baseDocBodyTree = parseXml(baseDocBody) - -proc test_insert() = - var testDoc = baseDocHeadTree - var newBody = newElement("body") - for item in baseDocBodyTree.items(): - newBody.insert(item, len(newBody)) - - echo $newBody - - testDoc.insert(newBody, 1) - echo $testDoc - -test_insert() diff --git a/tests/xml/ttree_replace.nim b/tests/xml/ttree_replace.nim deleted file mode 100644 index 97d2db638..000000000 --- a/tests/xml/ttree_replace.nim +++ /dev/null @@ -1,46 +0,0 @@ -discard """ - output: ''' - - -
    Some text
    -
    Some more text
    - - -
    Some text in body
    -
    Some more text in body
    - -
    -''' -""" - -# Test xmltree add/insert/delete/replace operations -import xmlparser -import xmltree -var baseDocBody = """ - -
    Some text in body
    -
    Some more text in body
    - -""" -var baseDocBodyTree = parseXml(baseDocBody) -let initialDocBase = """ - - -
    Some text
    -
    Some more text
    - - -
    Some text in body before replace
    -
    Some more text in body before replace
    - -
    -""" -var initialDocBaseTree = parseXml(initialDocBase) - -proc test_replace() = - var testDoc = initialDocBaseTree - - testDoc.replace(1, @[baseDocBodyTree]) - echo $testDoc - -test_replace() diff --git a/tests/xml/ttree_replace1.nim b/tests/xml/ttree_replace1.nim deleted file mode 100644 index 059ce2085..000000000 --- a/tests/xml/ttree_replace1.nim +++ /dev/null @@ -1,53 +0,0 @@ -discard """ - output: ''' - - -
    Some text
    -
    Some more text
    - - -
    Some text in body
    -
    Some more text in body
    - -
    -''' -""" - -# Test xmltree add/insert/delete/replace operations -import xmlparser -import xmltree -var baseDocHead = """ - -
    Some text
    -
    Some more text
    - -""" -var baseDocHeadTree = parseXml(baseDocHead) -var baseDocBody = """ - -
    Some text in body
    -
    Some more text in body
    - -""" -var baseDocBodyTree = parseXml(baseDocBody) -let initialDocBase = """ - - -
    Some text before replace
    -
    Some more text before replace
    - - -
    Some text in body before replace
    -
    Some more text in body before replace
    - -
    -""" -var initialDocBaseTree = parseXml(initialDocBase) - -proc test_replace() = - var testDoc = initialDocBaseTree - - testDoc.replace(0..1, @[baseDocHeadTree, baseDocBodyTree]) - echo $testDoc - -test_replace() diff --git a/tools/atlas/atlas.md b/tools/atlas/atlas.md deleted file mode 100644 index ee770cd62..000000000 --- a/tools/atlas/atlas.md +++ /dev/null @@ -1,81 +0,0 @@ -# Atlas Package Cloner - -Atlas is a simple package cloner tool that automates some of the -workflows and needs for Nim's stdlib evolution. - -Atlas is compatible with Nimble in the sense that it supports the Nimble -file format. - - -## How it works - -Atlas uses git commits internally; version requirements are translated -to git commits via `git show-ref --tags`. - -Atlas uses URLs internally; Nimble package names are translated to URLs -via Nimble's `packages.json` file. - -Atlas does not call the Nim compiler for a build, instead it creates/patches -a `nim.cfg` file for the compiler. For example: - -``` -############# begin Atlas config section ########## ---noNimblePath ---path:"../nimx" ---path:"../sdl2/src" ---path:"../opengl/src" -############# end Atlas config section ########## -``` - -The version selection is deterministic, it picks up the *minimum* required -version. Thanks to this design, lock files are much less important. - - -## Dependencies - -Dependencies are neither installed globally, nor locally into the current -project. Instead a "workspace" is used. The workspace is the nearest parent -directory of the current directory that does not contain a `.git` subdirectory. -Dependencies are managed as **siblings**, not as children. Dependencies are -kept as git repositories. - -Thanks to this setup, it's easy to develop multiple projects at the same time. - -A project plus its dependencies are stored in a workspace: - - $workspace / main project - $workspace / dependency A - $workspace / dependency B - - -No attempts are being made at keeping directory hygiene inside the -workspace, you're supposed to create appropriate `$workspace` directories -at your own leisure. - - -## Commands - -Atlas supports the following commands: - - -### Clone - -Clones a URL and all of its dependencies (recursively) into the workspace. -Creates or patches a `nim.cfg` file with the required `--path` entries. - - -### Clone - -The `` is translated into an URL via `packages.json` and -then `clone ` is performed. - - -### Search - -Search the package index `packages.json` for a package that the given terms -in its description (or name or list of tags). - - -### Install - -Use the .nimble file to setup the project's dependencies. diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim deleted file mode 100644 index 274e94517..000000000 --- a/tools/atlas/atlas.nim +++ /dev/null @@ -1,559 +0,0 @@ -# -# Atlas Package Cloner -# (c) Copyright 2021 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Simple tool to automate frequent workflows: Can "clone" -## a Nimble dependency and its dependencies recursively. - -import std/[parseopt, strutils, os, osproc, unicode, tables, sets, json, jsonutils] -import parse_requires, osutils, packagesjson - -const - Version = "0.2" - Usage = "atlas - Nim Package Cloner Version " & Version & """ - - (c) 2021 Andreas Rumpf -Usage: - atlas [options] [command] [arguments] -Command: - clone url|pkgname clone a package and all of its dependencies - install proj.nimble use the .nimble file to setup the project's dependencies - search keyw keywB... search for package that contains the given keywords - extract file.nimble extract the requirements and custom commands from - the given Nimble file - -Options: - --keepCommits do not perform any `git checkouts` - --cfgHere also create/maintain a nim.cfg in the current - working directory - --workspace=DIR use DIR as workspace - --version show the version - --help show this help -""" - -proc writeHelp() = - stdout.write(Usage) - stdout.flushFile() - quit(0) - -proc writeVersion() = - stdout.write(Version & "\n") - stdout.flushFile() - quit(0) - -const - MockupRun = defined(atlasTests) - TestsDir = "tools/atlas/tests" - -type - PackageName = distinct string - DepRelation = enum - normal, strictlyLess, strictlyGreater - - Dependency = object - name: PackageName - url, commit: string - rel: DepRelation # "requires x < 1.0" is silly, but Nimble allows it so we have too. - AtlasContext = object - projectDir, workspace: string - hasPackageList: bool - keepCommits: bool - cfgHere: bool - p: Table[string, string] # name -> url mapping - processed: HashSet[string] # the key is (url / commit) - errors: int - when MockupRun: - currentDir: string - step: int - mockupSuccess: bool - -const - InvalidCommit = "" - ProduceTest = false - -type - Command = enum - GitDiff = "git diff", - GitTags = "git show-ref --tags", - GitRevParse = "git rev-parse", - GitCheckout = "git checkout", - GitPull = "git pull", - GitCurrentCommit = "git log -n 1 --format=%H" - GitMergeBase = "git merge-base" - -include testdata - -proc exec(c: var AtlasContext; cmd: Command; args: openArray[string]): (string, int) = - when MockupRun: - assert TestLog[c.step].cmd == cmd, $(TestLog[c.step].cmd, cmd) - case cmd - of GitDiff, GitTags, GitRevParse, GitPull, GitCurrentCommit: - result = (TestLog[c.step].output, TestLog[c.step].exitCode) - of GitCheckout: - assert args[0] == TestLog[c.step].output - of GitMergeBase: - let tmp = TestLog[c.step].output.splitLines() - assert tmp.len == 4, $tmp.len - assert tmp[0] == args[0] - assert tmp[1] == args[1] - assert tmp[3] == "" - result[0] = tmp[2] - result[1] = TestLog[c.step].exitCode - inc c.step - else: - var cmdLine = $cmd - for i in 0.. ", result - -proc cloneUrl(c: var AtlasContext; url, dest: string; cloneUsingHttps: bool): string = - when MockupRun: - result = "" - else: - result = osutils.cloneUrl(url, dest, cloneUsingHttps) - when ProduceTest: - echo "cloned ", url, " into ", dest - -template withDir*(c: var AtlasContext; dir: string; body: untyped) = - when MockupRun: - c.currentDir = dir - body - else: - let oldDir = getCurrentDir() - try: - when ProduceTest: - echo "Current directory is now ", dir - setCurrentDir(dir) - body - finally: - setCurrentDir(oldDir) - -proc extractRequiresInfo(c: var AtlasContext; nimbleFile: string): NimbleFileInfo = - result = extractRequiresInfo(nimbleFile) - when ProduceTest: - echo "nimble ", nimbleFile, " info ", result - -proc toDepRelation(s: string): DepRelation = - case s - of "<": strictlyLess - of ">": strictlyGreater - else: normal - -proc isCleanGit(c: var AtlasContext; dir: string): string = - result = "" - let (outp, status) = exec(c, GitDiff, []) - if outp.len != 0: - result = "'git diff' not empty" - elif status != 0: - result = "'git diff' returned non-zero" - -proc message(c: var AtlasContext; category: string; p: PackageName; args: varargs[string]) = - var msg = category & "(" & p.string & ")" - for a in args: - msg.add ' ' - msg.add a - stdout.writeLine msg - inc c.errors - -proc warn(c: var AtlasContext; p: PackageName; args: varargs[string]) = - message(c, "[Warning] ", p, args) - -proc error(c: var AtlasContext; p: PackageName; args: varargs[string]) = - message(c, "[Error] ", p, args) - -proc sameVersionAs(tag, ver: string): bool = - const VersionChars = {'0'..'9', '.'} - - proc safeCharAt(s: string; i: int): char {.inline.} = - if i >= 0 and i < s.len: s[i] else: '\0' - - let idx = find(tag, ver) - if idx >= 0: - # we found the version as a substring inside the `tag`. But we - # need to watch out the the boundaries are not part of a - # larger/different version number: - result = safeCharAt(tag, idx-1) notin VersionChars and - safeCharAt(tag, idx+ver.len) notin VersionChars - -proc versionToCommit(c: var AtlasContext; d: Dependency): string = - let (outp, status) = exec(c, GitTags, []) - if status == 0: - var useNextOne = false - for line in splitLines(outp): - let commitsAndTags = strutils.splitWhitespace(line) - if commitsAndTags.len == 2: - case d.rel - of normal: - if commitsAndTags[1].sameVersionAs(d.commit): - return commitsAndTags[0] - of strictlyLess: - if d.commit == InvalidCommit or not commitsAndTags[1].sameVersionAs(d.commit): - return commitsAndTags[0] - of strictlyGreater: - if commitsAndTags[1].sameVersionAs(d.commit): - useNextOne = true - elif useNextOne: - return commitsAndTags[0] - - return "" - -proc shortToCommit(c: var AtlasContext; short: string): string = - let (cc, status) = exec(c, GitRevParse, [short]) - result = if status == 0: strutils.strip(cc) else: "" - -proc checkoutGitCommit(c: var AtlasContext; p: PackageName; commit: string) = - let (_, status) = exec(c, GitCheckout, [commit]) - if status != 0: - error(c, p, "could not checkout commit", commit) - -proc gitPull(c: var AtlasContext; p: PackageName) = - let (_, status) = exec(c, GitPull, []) - if status != 0: - error(c, p, "could not 'git pull'") - -proc updatePackages(c: var AtlasContext) = - if dirExists(c.workspace / PackagesDir): - withDir(c, c.workspace / PackagesDir): - gitPull(c, PackageName PackagesDir) - else: - withDir c, c.workspace: - let err = cloneUrl(c, "https://github.com/nim-lang/packages", PackagesDir, false) - if err != "": - error c, PackageName(PackagesDir), err - -proc fillPackageLookupTable(c: var AtlasContext) = - if not c.hasPackageList: - c.hasPackageList = true - when not MockupRun: - updatePackages(c) - let plist = getPackages(when MockupRun: TestsDir else: c.workspace) - for entry in plist: - c.p[unicode.toLower entry.name] = entry.url - -proc toUrl(c: var AtlasContext; p: string): string = - if p.isUrl: - result = p - else: - fillPackageLookupTable(c) - result = c.p.getOrDefault(unicode.toLower p) - if result.len == 0: - inc c.errors - -proc toName(p: string): PackageName = - if p.isUrl: - result = PackageName splitFile(p).name - else: - result = PackageName p - -proc needsCommitLookup(commit: string): bool {.inline.} = - '.' in commit or commit == InvalidCommit - -proc isShortCommitHash(commit: string): bool {.inline.} = - commit.len >= 4 and commit.len < 40 - -proc checkoutCommit(c: var AtlasContext; w: Dependency) = - let dir = c.workspace / w.name.string - withDir c, dir: - if w.commit.len == 0 or cmpIgnoreCase(w.commit, "head") == 0: - gitPull(c, w.name) - else: - let err = isCleanGit(c, dir) - if err != "": - warn c, w.name, err - else: - let requiredCommit = - if needsCommitLookup(w.commit): versionToCommit(c, w) - elif isShortCommitHash(w.commit): shortToCommit(c, w.commit) - else: w.commit - let (cc, status) = exec(c, GitCurrentCommit, []) - let currentCommit = strutils.strip(cc) - if requiredCommit == "" or status != 0: - if requiredCommit == "" and w.commit == InvalidCommit: - warn c, w.name, "package has no tagged releases" - else: - warn c, w.name, "cannot find specified version/commit", w.commit - else: - if currentCommit != requiredCommit: - # checkout the later commit: - # git merge-base --is-ancestor - let (cc, status) = exec(c, GitMergeBase, [currentCommit, requiredCommit]) - let mergeBase = strutils.strip(cc) - if status == 0 and (mergeBase == currentCommit or mergeBase == requiredCommit): - # conflict resolution: pick the later commit: - if mergeBase == currentCommit: - checkoutGitCommit(c, w.name, requiredCommit) - else: - checkoutGitCommit(c, w.name, requiredCommit) - when false: - warn c, w.name, "do not know which commit is more recent:", - currentCommit, "(current) or", w.commit, " =", requiredCommit, "(required)" - -proc findNimbleFile(c: AtlasContext; dep: Dependency): string = - when MockupRun: - result = TestsDir / dep.name.string & ".nimble" - doAssert fileExists(result), "file does not exist " & result - else: - result = c.workspace / dep.name.string / (dep.name.string & ".nimble") - if not fileExists(result): - result = "" - for x in walkFiles(c.workspace / dep.name.string / "*.nimble"): - if result.len == 0: - result = x - else: - # ambiguous .nimble file - return "" - -proc addUniqueDep(c: var AtlasContext; work: var seq[Dependency]; - tokens: seq[string]) = - let oldErrors = c.errors - let url = toUrl(c, tokens[0]) - if oldErrors != c.errors: - warn c, toName(tokens[0]), "cannot resolve package name" - elif not c.processed.containsOrIncl(url / tokens[2]): - work.add Dependency(name: toName(tokens[0]), url: url, commit: tokens[2], - rel: toDepRelation(tokens[1])) - -template toDestDir(p: PackageName): string = p.string - -proc collectDeps(c: var AtlasContext; work: var seq[Dependency]; - dep: Dependency; nimbleFile: string): string = - # If there is a .nimble file, return the dependency path & srcDir - # else return "". - assert nimbleFile != "" - let nimbleInfo = extractRequiresInfo(c, nimbleFile) - for r in nimbleInfo.requires: - var tokens: seq[string] = @[] - for token in tokenizeRequires(r): - tokens.add token - if tokens.len == 1: - # nimx uses dependencies like 'requires "sdl2"'. - # Via this hack we map them to the first tagged release. - # (See the `isStrictlySmallerThan` logic.) - tokens.add "<" - tokens.add InvalidCommit - elif tokens.len == 2 and tokens[1].startsWith("#"): - # Dependencies can also look like 'requires "sdl2#head" - var commit = tokens[1][1 .. ^1] - tokens[1] = "==" - tokens.add commit - - if tokens.len >= 3 and cmpIgnoreCase(tokens[0], "nim") != 0: - c.addUniqueDep work, tokens - result = toDestDir(dep.name) / nimbleInfo.srcDir - -proc collectNewDeps(c: var AtlasContext; work: var seq[Dependency]; - dep: Dependency; result: var seq[string]; - isMainProject: bool) = - let nimbleFile = findNimbleFile(c, dep) - if nimbleFile != "": - let x = collectDeps(c, work, dep, nimbleFile) - result.add x - else: - result.add toDestDir(dep.name) - -proc cloneLoop(c: var AtlasContext; work: var seq[Dependency]): seq[string] = - result = @[] - var i = 0 - while i < work.len: - let w = work[i] - let destDir = toDestDir(w.name) - let oldErrors = c.errors - - if not dirExists(c.workspace / destDir): - withDir c, c.workspace: - let err = cloneUrl(c, w.url, destDir, false) - if err != "": - error c, w.name, err - if oldErrors == c.errors: - if not c.keepCommits: checkoutCommit(c, w) - # even if the checkout fails, we can make use of the somewhat - # outdated .nimble file to clone more of the most likely still relevant - # dependencies: - collectNewDeps(c, work, w, result, i == 0) - inc i - -proc clone(c: var AtlasContext; start: string): seq[string] = - # non-recursive clone. - let url = toUrl(c, start) - var work = @[Dependency(name: toName(start), url: url, commit: "")] - - if url == "": - error c, toName(start), "cannot resolve package name" - return - - c.projectDir = c.workspace / toDestDir(work[0].name) - result = cloneLoop(c, work) - -const - configPatternBegin = "############# begin Atlas config section ##########\n" - configPatternEnd = "############# end Atlas config section ##########\n" - -proc patchNimCfg(c: var AtlasContext; deps: seq[string]; cfgPath: string) = - var paths = "--noNimblePath\n" - for d in deps: - let pkgname = toDestDir d.PackageName - let x = relativePath(c.workspace / pkgname, cfgPath, '/') - paths.add "--path:\"" & x & "\"\n" - var cfgContent = configPatternBegin & paths & configPatternEnd - - when MockupRun: - assert readFile(TestsDir / "nim.cfg") == cfgContent - c.mockupSuccess = true - else: - let cfg = cfgPath / "nim.cfg" - if cfgPath.len > 0 and not dirExists(cfgPath): - error(c, c.projectDir.PackageName, "could not write the nim.cfg") - elif not fileExists(cfg): - writeFile(cfg, cfgContent) - else: - let content = readFile(cfg) - let start = content.find(configPatternBegin) - if start >= 0: - cfgContent = content.substr(0, start-1) & cfgContent - let theEnd = content.find(configPatternEnd, start) - if theEnd >= 0: - cfgContent.add content.substr(theEnd+len(configPatternEnd)) - else: - cfgContent = content & "\n" & cfgContent - if cfgContent != content: - # do not touch the file if nothing changed - # (preserves the file date information): - writeFile(cfg, cfgContent) - -proc error*(msg: string) = - when defined(debug): - writeStackTrace() - quit "[Error] " & msg - -proc findSrcDir(c: var AtlasContext): string = - for nimbleFile in walkPattern("*.nimble"): - let nimbleInfo = extractRequiresInfo(c, nimbleFile) - return nimbleInfo.srcDir - return "" - -proc installDependencies(c: var AtlasContext; nimbleFile: string) = - # 1. find .nimble file in CWD - # 2. install deps from .nimble - var work: seq[Dependency] = @[] - let (path, pkgname, _) = splitFile(nimbleFile) - let dep = Dependency(name: toName(pkgname), url: "", commit: "") - discard collectDeps(c, work, dep, nimbleFile) - let paths = cloneLoop(c, work) - patchNimCfg(c, paths, if c.cfgHere: getCurrentDir() else: findSrcDir(c)) - -proc main = - var action = "" - var args: seq[string] = @[] - template singleArg() = - if args.len != 1: - error action & " command takes a single package name" - - template noArgs() = - if args.len != 0: - error action & " command takes no arguments" - - var c = AtlasContext( - projectDir: getCurrentDir(), - workspace: "") - - for kind, key, val in getopt(): - case kind - of cmdArgument: - if action.len == 0: - action = key.normalize - else: - args.add key - of cmdLongOption, cmdShortOption: - case normalize(key) - of "help", "h": writeHelp() - of "version", "v": writeVersion() - of "keepcommits": c.keepCommits = true - of "workspace": - if val.len > 0: - c.workspace = val - createDir(val) - else: - writeHelp() - of "cfghere": c.cfgHere = true - else: writeHelp() - of cmdEnd: assert false, "cannot happen" - - if c.workspace.len > 0: - if not dirExists(c.workspace): error "Workspace directory '" & c.workspace & "' not found." - else: - c.workspace = getCurrentDir() - while c.workspace.len > 0 and dirExists(c.workspace / ".git"): - c.workspace = c.workspace.parentDir() - echo "Using workspace ", c.workspace - - case action - of "": - error "No action." - of "clone": - singleArg() - let deps = clone(c, args[0]) - patchNimCfg c, deps, if c.cfgHere: getCurrentDir() else: findSrcDir(c) - when MockupRun: - if not c.mockupSuccess: - error "There were problems." - else: - if c.errors > 0: - error "There were problems." - of "install": - if args.len > 1: - error "install command takes a single argument" - var nimbleFile = "" - if args.len == 1: - nimbleFile = args[0] - else: - for x in walkPattern("*.nimble"): - nimbleFile = x - break - if nimbleFile.len == 0: - error "could not find a .nimble file" - installDependencies(c, nimbleFile) - of "refresh": - noArgs() - updatePackages(c) - of "search", "list": - updatePackages(c) - search getPackages(c.workspace), args - of "extract": - singleArg() - if fileExists(args[0]): - echo toJson(extractRequiresInfo(args[0])) - else: - error "File does not exist: " & args[0] - else: - error "Invalid action: " & action - -when isMainModule: - main() - -when false: - # some testing code for the `patchNimCfg` logic: - var c = AtlasContext( - projectDir: getCurrentDir(), - workspace: getCurrentDir().parentDir) - - patchNimCfg(c, @[PackageName"abc", PackageName"xyz"]) - -when false: - assert sameVersionAs("v0.2.0", "0.2.0") - assert sameVersionAs("v1", "1") - - assert sameVersionAs("1.90", "1.90") - - assert sameVersionAs("v1.2.3-zuzu", "1.2.3") - assert sameVersionAs("foo-1.2.3.4", "1.2.3.4") - - assert not sameVersionAs("foo-1.2.3.4", "1.2.3") - assert not sameVersionAs("foo", "1.2.3") - assert not sameVersionAs("", "1.2.3") diff --git a/tools/atlas/osutils.nim b/tools/atlas/osutils.nim deleted file mode 100644 index 6134830b5..000000000 --- a/tools/atlas/osutils.nim +++ /dev/null @@ -1,51 +0,0 @@ -## OS utilities like 'withDir'. -## (c) 2021 Andreas Rumpf - -import os, strutils, osproc - -proc isUrl*(x: string): bool = - x.startsWith("git://") or x.startsWith("https://") or x.startsWith("http://") - -proc cloneUrl*(url, dest: string; cloneUsingHttps: bool): string = - ## Returns an error message on error or else "". - result = "" - var modUrl = - if url.startsWith("git://") and cloneUsingHttps: - "https://" & url[6 .. ^1] - else: url - - # github + https + trailing url slash causes a - # checkout/ls-remote to fail with Repository not found - var isGithub = false - if modUrl.contains("github.com") and modUrl.endsWith("/"): - modUrl = modUrl[0 .. ^2] - isGithub = true - - let (_, exitCode) = execCmdEx("git ls-remote --quiet --tags " & modUrl) - var xcode = exitCode - if isGithub and exitCode != QuitSuccess: - # retry multiple times to avoid annoying github timeouts: - for i in 0..4: - os.sleep(4000) - xcode = execCmdEx("git ls-remote --quiet --tags " & modUrl)[1] - if xcode == QuitSuccess: break - - if xcode == QuitSuccess: - # retry multiple times to avoid annoying github timeouts: - let cmd = "git clone " & modUrl & " " & dest - for i in 0..4: - if execShellCmd(cmd) == 0: return "" - os.sleep(4000) - result = "exernal program failed: " & cmd - elif not isGithub: - let (_, exitCode) = execCmdEx("hg identify " & modUrl) - if exitCode == QuitSuccess: - let cmd = "hg clone " & modUrl & " " & dest - for i in 0..4: - if execShellCmd(cmd) == 0: return "" - os.sleep(4000) - result = "exernal program failed: " & cmd - else: - result = "Unable to identify url: " & modUrl - else: - result = "Unable to identify url: " & modUrl diff --git a/tools/atlas/packagesjson.nim b/tools/atlas/packagesjson.nim deleted file mode 100644 index 0b8599769..000000000 --- a/tools/atlas/packagesjson.nim +++ /dev/null @@ -1,114 +0,0 @@ - -import std / [json, os, sets, strutils] - -type - Package* = ref object - # Required fields in a package. - name*: string - url*: string # Download location. - license*: string - downloadMethod*: string - description*: string - tags*: seq[string] # \ - # From here on, optional fields set to the empty string if not available. - version*: string - dvcsTag*: string - web*: string # Info url for humans. - -proc optionalField(obj: JsonNode, name: string, default = ""): string = - if hasKey(obj, name) and obj[name].kind == JString: - result = obj[name].str - else: - result = default - -proc requiredField(obj: JsonNode, name: string): string = - result = optionalField(obj, name, "") - -proc fromJson*(obj: JSonNode): Package = - result = Package() - result.name = obj.requiredField("name") - if result.name.len == 0: return nil - result.version = obj.optionalField("version") - result.url = obj.requiredField("url") - if result.url.len == 0: return nil - result.downloadMethod = obj.requiredField("method") - if result.downloadMethod.len == 0: return nil - result.dvcsTag = obj.optionalField("dvcs-tag") - result.license = obj.optionalField("license") - result.tags = @[] - for t in obj["tags"]: - result.tags.add(t.str) - result.description = obj.requiredField("description") - result.web = obj.optionalField("web") - -const PackagesDir* = "packages" - -proc getPackages*(workspaceDir: string): seq[Package] = - result = @[] - var uniqueNames = initHashSet[string]() - var jsonFiles = 0 - for kind, path in walkDir(workspaceDir / PackagesDir): - if kind == pcFile and path.endsWith(".json"): - inc jsonFiles - let packages = json.parseFile(path) - for p in packages: - let pkg = p.fromJson() - if pkg != nil and not uniqueNames.containsOrIncl(pkg.name): - result.add(pkg) - -proc `$`*(pkg: Package): string = - result = pkg.name & ":\n" - result &= " url: " & pkg.url & " (" & pkg.downloadMethod & ")\n" - result &= " tags: " & pkg.tags.join(", ") & "\n" - result &= " description: " & pkg.description & "\n" - result &= " license: " & pkg.license & "\n" - if pkg.web.len > 0: - result &= " website: " & pkg.web & "\n" - -proc search*(pkgList: seq[Package]; terms: seq[string]) = - var found = false - template onFound = - echo pkg - found = true - break forPackage - - for pkg in pkgList: - if terms.len > 0: - block forPackage: - for term in terms: - let word = term.toLower - # Search by name. - if word in pkg.name.toLower: - onFound() - # Search by tag. - for tag in pkg.tags: - if word in tag.toLower: - onFound() - else: - echo(pkg) - - if not found and terms.len > 0: - echo("No package found.") - -type PkgCandidates* = array[3, seq[Package]] - -proc determineCandidates*(pkgList: seq[Package]; - terms: seq[string]): PkgCandidates = - result[0] = @[] - result[1] = @[] - result[2] = @[] - for pkg in pkgList: - block termLoop: - for term in terms: - let word = term.toLower - if word == pkg.name.toLower: - result[0].add pkg - break termLoop - elif word in pkg.name.toLower: - result[1].add pkg - break termLoop - else: - for tag in pkg.tags: - if word in tag.toLower: - result[2].add pkg - break termLoop diff --git a/tools/atlas/parse_requires.nim b/tools/atlas/parse_requires.nim deleted file mode 100644 index 7e26a1656..000000000 --- a/tools/atlas/parse_requires.nim +++ /dev/null @@ -1,101 +0,0 @@ -## Utility API for Nim package managers. -## (c) 2021 Andreas Rumpf - -import std / strutils -import ".." / compiler / [ast, idents, msgs, syntaxes, options, pathutils] - -type - NimbleFileInfo* = object - requires*: seq[string] - srcDir*: string - tasks*: seq[(string, string)] - -proc extract(n: PNode; conf: ConfigRef; result: var NimbleFileInfo) = - case n.kind - of nkStmtList, nkStmtListExpr: - for child in n: - extract(child, conf, result) - of nkCallKinds: - if n[0].kind == nkIdent: - case n[0].ident.s - of "requires": - for i in 1.. 0: ch = ch.lastSon - if ch.kind in {nkStrLit..nkTripleStrLit}: - result.requires.add ch.strVal - else: - localError(conf, ch.info, "'requires' takes string literals") - of "task": - if n.len >= 3 and n[1].kind == nkIdent and n[2].kind in {nkStrLit..nkTripleStrLit}: - result.tasks.add((n[1].ident.s, n[2].strVal)) - else: discard - of nkAsgn, nkFastAsgn: - if n[0].kind == nkIdent and cmpIgnoreCase(n[0].ident.s, "srcDir") == 0: - if n[1].kind in {nkStrLit..nkTripleStrLit}: - result.srcDir = n[1].strVal - else: - localError(conf, n[1].info, "assignments to 'srcDir' must be string literals") - else: - discard - -proc extractRequiresInfo*(nimbleFile: string): NimbleFileInfo = - ## Extract the `requires` information from a Nimble file. This does **not** - ## evaluate the Nimble file. Errors are produced on stderr/stdout and are - ## formatted as the Nim compiler does it. The parser uses the Nim compiler - ## as an API. The result can be empty, this is not an error, only parsing - ## errors are reported. - var conf = newConfigRef() - conf.foreignPackageNotes = {} - conf.notes = {} - conf.mainPackageNotes = {} - - let fileIdx = fileInfoIdx(conf, AbsoluteFile nimbleFile) - var parser: Parser - if setupParser(parser, fileIdx, newIdentCache(), conf): - extract(parseAll(parser), conf, result) - closeParser(parser) - -const Operators* = {'<', '>', '=', '&', '@', '!', '^'} - -proc token(s: string; idx: int; lit: var string): int = - var i = idx - if i >= s.len: return i - while s[i] in Whitespace: inc(i) - case s[i] - of Letters, '#': - lit.add s[i] - inc i - while i < s.len and s[i] notin (Whitespace + {'@', '#'}): - lit.add s[i] - inc i - of '0'..'9': - while i < s.len and s[i] in {'0'..'9', '.'}: - lit.add s[i] - inc i - of '"': - inc i - while i < s.len and s[i] != '"': - lit.add s[i] - inc i - inc i - of Operators: - while i < s.len and s[i] in Operators: - lit.add s[i] - inc i - else: - lit.add s[i] - inc i - result = i - -iterator tokenizeRequires*(s: string): string = - var start = 0 - var tok = "" - while start < s.len: - tok.setLen 0 - start = token(s, start, tok) - yield tok - -when isMainModule: - for x in tokenizeRequires("jester@#head >= 1.5 & <= 1.8"): - echo x diff --git a/tools/atlas/testdata.nim b/tools/atlas/testdata.nim deleted file mode 100644 index aefaeacd2..000000000 --- a/tools/atlas/testdata.nim +++ /dev/null @@ -1,63 +0,0 @@ - -type - PerDirData = object - dirname: string - cmd: Command - exitCode: int - output: string - -template toData(a, b, c, d): untyped = - PerDirData(dirname: a, cmd: b, exitCode: c, output: d) - -const - TestLog = [ - toData("balls", GitPull, 0, "Already up to date.\n"), - toData("grok", GitDiff, 0, ""), - toData("grok", GitTags, 0, "2ca193c31fa2377c1e991a080d60ca3215ff6cf0 refs/tags/0.0.1\n48007554b21ba2f65c726ae2fdda88d621865b4a refs/tags/0.0.2\n7092a0286421c7818cd335cca9ebc72d03d866c2 refs/tags/0.0.3\n62707b8ac684efac35d301dbde57dc750880268e refs/tags/0.0.4\n876f2504e0c2f785ffd2cf65a78e2aea474fa8aa refs/tags/0.0.5\nb7eb1f2501aa2382cb3a38353664a13af62a9888 refs/tags/0.0.6\nf5d818bfd6038884b3d8b531c58484ded20a58a4 refs/tags/0.1.0\n961eaddea49c3144d130d105195583d3f11fb6c6 refs/tags/0.2.0\n15ab8ed8d4f896232a976a9008548bd53af72a66 refs/tags/0.2.1\n426a7d7d4603f77ced658e73ad7f3f582413f6cd refs/tags/0.3.0\n83cf7a39b2fe897786fb0fe01a7a5933c3add286 refs/tags/0.3.1\n8d2e3c900edbc95fa0c036fd76f8e4f814aef2c1 refs/tags/0.3.2\n48b43372f49a3bb4dc0969d82a0fca183fb94662 refs/tags/0.3.3\n9ca947a3009ea6ba17814b20eb953272064eb2e6 refs/tags/0.4.0\n1b5643d04fba6d996a16d1ffc13d034a40003f8f refs/tags/0.5.0\n486b0eb580b1c465453d264ac758cc490c19c33e refs/tags/0.5.1\naedb0d9497390e20b9d2541cef2bb05a5cda7a71 refs/tags/0.5.2\n"), - toData("grok", GitCurrentCommit, 0, "349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"), - toData("grok", GitMergeBase, 0, "349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n1b5643d04fba6d996a16d1ffc13d034a40003f8f\n349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"), - toData("grok", GitCheckout, 0, "1b5643d04fba6d996a16d1ffc13d034a40003f8f"), # watch out! - - toData("ups", GitDiff, 0, ""), - toData("ups", GitTags, 0, "4008f9339cd22b30e180bc87a6cca7270fd28ac1 refs/tags/0.0.2\n19bc490c22b4f5b0628c31cdedead1375b279356 refs/tags/0.0.3\nff34602aaea824cb46d6588cd5fe1178132e9702 refs/tags/0.0.4\n09de599138f20b745133b6e4fe563e204415a7e8 refs/tags/0.0.5\n85fee3b74798311108a105635df31f892150f5d0 refs/tags/0.0.6\nfd303913b22b121dc42f332109e9c44950b9acd4 refs/tags/0.0.7\n"), - toData("ups", GitCurrentCommit, 0, "74c31af8030112dac758440aa51ef175992f71f3\n"), - toData("ups", GitMergeBase, 0, "74c31af8030112dac758440aa51ef175992f71f3\n4008f9339cd22b30e180bc87a6cca7270fd28ac1\n74c31af8030112dac758440aa51ef175992f71f3\n"), - toData("ups", GitCheckout, 0, "4008f9339cd22b30e180bc87a6cca7270fd28ac1"), - - toData("sync", GitDiff, 0, ""), - toData("sync", GitRevParse, 0, "810bd2d75e9f6e182534ae2488670b51a9f13fc3\n"), - toData("sync", GitCurrentCommit, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n"), - toData("sync", GitMergeBase, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n"), - - toData("npeg", GitDiff, 0, ""), - toData("npeg", GitTags, 0, "8df2f0c9391995fd086b8aab00e8ab7aded1e8f0 refs/tags/0.1.0\n4c959a72db5283b55eeef491076eefb5e02316f1 refs/tags/0.10.0\n802f47c0f7f4318a4f0858ba5a6a6ed2333bde71 refs/tags/0.11.0\n82c8d92837108dce225358ace2c416bf9a3f30ce refs/tags/0.12.0\n87d2f2c4f6ef7da350d45beb5a336611bde7f518 refs/tags/0.13.0\n39964f0d220bfaade47a568bf03c1cf28aa2bc37 refs/tags/0.14.0\nbe9f03f92304cbeab70572944a8563db9b23b2fb refs/tags/0.14.1\na933fb9832566fc95273e417597bfb4faf564ca6 refs/tags/0.15.0\n6aad2e438c52ff0636c7bfb64338e444ac3e83ba refs/tags/0.16.0\nf4ddffb5848c42c6151743dd9c7eddcaaabc56cc refs/tags/0.17.0\n30b446b39442cdbc53a97018ab8a54149aa7c3b7 refs/tags/0.17.1\n1a9d36aa3b34a6169d4530463f1c17a3fe1e075e refs/tags/0.18.0\ndd34f903a9a63b876cb2db19b7a4ce0bcc252134 refs/tags/0.19.0\nd93d49c81fc8722d7929ac463b435c0f2e10c53b refs/tags/0.2.0\neeae7746c9b1118bcf27744ab2aee26969051256 refs/tags/0.20.0\n8c3471a548129f3bf62df15cd0fd8cca1787d852 refs/tags/0.21.0\nc0e873a17bc713c80e74fec3c30cb62dcd5d194a refs/tags/0.21.1\nbae84c47a1bb259b209b6f6be1582327b784539d refs/tags/0.21.2\nbfcb4bcae76a917c3c88736ca773e4cb67dbb2d8 refs/tags/0.21.3\n0eabb7c462d30932049f0b7e6a030c1562cf9fee refs/tags/0.22.0\n2e75367095f54d4351005078bad98041a55b14c1 refs/tags/0.22.1\n814ea235dd398108d7b18f966694c3d951575701 refs/tags/0.22.2\na812064587d983c129737f8500bf74990e6b8dab refs/tags/0.23.0\nbd969ad3745db0d66022564cac76cf9424651104 refs/tags/0.23.1\na037c646a47623b92718efadc2bb74d03664b360 refs/tags/0.23.2\n078475ccceeaca0fac947492acdd24514da8d863 refs/tags/0.24.0\ne7bd87dc992512fd5825a557a56907647e03c979 refs/tags/0.24.1\n45ea601e1c7f64fb857bc99df984b86673621d2c refs/tags/0.3.0\n1ea9868a3fee3aa487ab7ec9129208a4dd483d0d refs/tags/0.4.0\n39afdb5733d3245386d29d08c5ff61c89268f499 refs/tags/0.5.0\n458c7b5910fcb157af3fc51bc3b3e663fdb3ed4a refs/tags/0.6.0\n06c38bd8563d822455bc237c2a98c153d938ed1b refs/tags/0.7.0\nf446b6056eef6d8dc9d8b47a79aca93d17dc8230 refs/tags/0.8.0\nbb25a195133f9f7af06386d0809793923cc5e8ab refs/tags/0.9.0\n"), - toData("npeg", GitCurrentCommit, 0, "5d80f93aa720898936668b3bc47d0fff101ec414\n"), - toData("npeg", GitMergeBase, 0, "5d80f93aa720898936668b3bc47d0fff101ec414\na037c646a47623b92718efadc2bb74d03664b360\na037c646a47623b92718efadc2bb74d03664b360\n"), - - toData("testes", GitDiff, 0, ""), - toData("testes", GitTags, 0, "3ce9b2968b5f644755a0ced1baa3eece88c2f12e refs/tags/0.1.0\nf73af8318b54737678fab8b54bdcd8a451015e0d refs/tags/0.1.1\nd21d84d37b161a123a43318bae353108755916de refs/tags/0.1.2\n5c36b6095353ed03b08ac939d00aff2d73f79a35 refs/tags/0.1.3\na1220d11237ee8f135f772ff9731c11b2d91ba31 refs/tags/0.1.4\n574f741b90d04a7ce8c9b990e6077708d7ad076e refs/tags/0.1.5\nced0a9e58234b680def6931578e09165a32e6291 refs/tags/0.1.6\nbb248952e8742a6011eb1a45a9d2059aeb0341d7 refs/tags/0.1.7\nabb7d7c552da0a8e0ddc586c15ccf7e74b0d068b refs/tags/0.10.0\n6e42a768a90d6442196b344bcdcb6f834b76e7b7 refs/tags/0.2.0\n9d136c3a0851ca2c021f5fb4f7b63f0a0ef77232 refs/tags/0.2.1\ndcb282b2da863fd2939e1969cec7a99788feb456 refs/tags/0.2.2\nf708a632afaa40a322a1a61c1c13722edac8e8c5 refs/tags/0.3.0\n3213f59e3f9ba052452c59f01d1418360d856af6 refs/tags/0.3.1\nf7bb1743dffd327958dfcebae4cfb6f61cc1cb8c refs/tags/0.3.2\n6b64569ebecad6bc60cc8697713701e7659204f4 refs/tags/0.3.3\nb51c25a4367bd17f419f78cb5a27f319e9d820f5 refs/tags/0.3.4\nb265612710cbd5ddb1b173c94ece8ec5c7ceccac refs/tags/0.3.5\ne404bcfe42e92d7509717a2dfa115cacb4964c5d refs/tags/0.3.6\n5e4d0d5b7e7f314dde701c546c4365c59782d3dc refs/tags/0.3.7\ne13f91c9c913d2b81c59adeaad687efa2b35293a refs/tags/0.3.8\n17599625f09af0ae4b525e63ab726a3002540702 refs/tags/0.3.9\n13e907f70571dd146d8dc29ddec4599b40ba4e85 refs/tags/0.4.0\n155a74cf676495df1e0674dd07b5e4a0291a9a4a refs/tags/0.4.1\nf37abccdc148cb02ca637a6f0bc8821491cce358 refs/tags/0.4.2\n0250d29ebdd02f28f9020445adb5a4e51fd1902c refs/tags/0.5.0\n2fb87db6d9f34109a70205876030c53f815739b7 refs/tags/0.5.1\n629d17ba8d6a1a4eca8145eb089ed5bca4473dfc refs/tags/0.6.0\ne926130f5f1b7903f68be49cc1563225bd9d948d refs/tags/0.7.0\n7365303897e6185796c274425c079916047e3f14 refs/tags/0.7.1\na735c4adabeba637409f41c4325dd8fc5fb91e2d refs/tags/0.7.10\nfe023fd27404889c5122f902456cbba14b767405 refs/tags/0.7.11\n4430e72972c77a5e9c1555d59bba11d840682691 refs/tags/0.7.12\nf0e53eb490a9558c7f594d2e095b70665e36ca88 refs/tags/0.7.13\nf6520e25e7c329c2957cda447f149fc6a930db0d refs/tags/0.7.2\nd509762f7191757c240d3c79c9ecda53f8c0cfe3 refs/tags/0.7.3\nc02e7a783d1c42fd1f91bca7142f7c3733950c05 refs/tags/0.7.4\n8c8a9e496e9b86ba7602709438980ca31e6989d9 refs/tags/0.7.5\n29839c18b4ac83c0111a178322b57ebb8a8d402c refs/tags/0.7.6\n3b62973cf74fafd8ea906644d89ac34d29a8a6cf refs/tags/0.7.7\ne67ff99dc43c391e89a37f97a9d298c3428bbde2 refs/tags/0.7.8\n4b72ecda0d40ed8e5ab8ad4095a0691d30ec6cd0 refs/tags/0.7.9\n2512b8cc3d7f001d277e89978da2049a5feee5c4 refs/tags/0.8.0\n86c47029690bd2731d204245f3f54462227bba0d refs/tags/0.9.0\n9a7f94f78588e9b5ba7ca077e1f7eae0607c6cf6 refs/tags/0.9.1\n08c915dc016d16c1dfa9a77d0b045ec29c9f2074 refs/tags/0.9.2\n3fb658b1ce1e1efa37d6f9f14322bdac8def02a5 refs/tags/0.9.3\n738fda0add962379ffe6aa6ca5f01a6943a98a2e refs/tags/0.9.4\n48d821add361f7ad768ecb35a0b19c38f90c919e refs/tags/0.9.5\nff9ae890f597dac301b2ac6e6805eb9ac5afd49a refs/tags/0.9.6\n483c78f06e60b0ec5e79fc3476df075ee7286890 refs/tags/0.9.7\n416eec87a5ae39a1a6035552e9e9a47d76b13026 refs/tags/1.0.0\na935cfe9445cc5218fbdd7e0afb35aa1587fff61 refs/tags/1.0.1\n4b83863a9181f054bb695b11b5d663406dfd85d2 refs/tags/1.0.2\n295145fddaa4fe29c1e71a5044d968a84f9dbf69 refs/tags/1.1.0\n8f74ea4e5718436c47305b4488842e6458a13dac refs/tags/1.1.1\n4135bb291e53d615a976e997c44fb2bd9e1ad343 refs/tags/1.1.10\n8c09dbcd16612f5989065db02ea2e7a752dd2656 refs/tags/1.1.11\naedfebdb6c016431d84b0c07cf181b957a900640 refs/tags/1.1.12\n2c2e958366ef6998115740bdf110588d730e5738 refs/tags/1.1.2\nbecc77258321e6ec40d89efdddf37bafd0d07fc3 refs/tags/1.1.3\ne070d7c9853bf94c35b81cf0c0a8980c2449bb22 refs/tags/1.1.4\n12c986cbbf65e8571a486e9230808bf887e5f04f refs/tags/1.1.5\n63df8986f5b56913b02d26954fa033eeaf43714c refs/tags/1.1.6\n38e02c9c6bd728b043036fe0d1894d774cab3108 refs/tags/1.1.7\n3c3879fff16450d28ade79a6b08982bf5cefc061 refs/tags/1.1.8\ne32b811b3b2e70a1d189d7a663bc2583e9c18f96 refs/tags/1.1.9\n0c1b4277c08197ce7e7e0aa2bad91d909fcd96ac refs/tags/2.0.0\n"), - toData("testes", GitCurrentCommit, 0, "d9db2ad09aa38fc26625341e1b666602959e144f\n"), - toData("testes", GitMergeBase, 0, "d9db2ad09aa38fc26625341e1b666602959e144f\n416eec87a5ae39a1a6035552e9e9a47d76b13026\nd9db2ad09aa38fc26625341e1b666602959e144f\n"), - toData("testes", GitCheckout, 0, "416eec87a5ae39a1a6035552e9e9a47d76b13026"), - - toData("grok", GitDiff, 0, ""), - toData("grok", GitTags, 0, "2ca193c31fa2377c1e991a080d60ca3215ff6cf0 refs/tags/0.0.1\n48007554b21ba2f65c726ae2fdda88d621865b4a refs/tags/0.0.2\n7092a0286421c7818cd335cca9ebc72d03d866c2 refs/tags/0.0.3\n62707b8ac684efac35d301dbde57dc750880268e refs/tags/0.0.4\n876f2504e0c2f785ffd2cf65a78e2aea474fa8aa refs/tags/0.0.5\nb7eb1f2501aa2382cb3a38353664a13af62a9888 refs/tags/0.0.6\nf5d818bfd6038884b3d8b531c58484ded20a58a4 refs/tags/0.1.0\n961eaddea49c3144d130d105195583d3f11fb6c6 refs/tags/0.2.0\n15ab8ed8d4f896232a976a9008548bd53af72a66 refs/tags/0.2.1\n426a7d7d4603f77ced658e73ad7f3f582413f6cd refs/tags/0.3.0\n83cf7a39b2fe897786fb0fe01a7a5933c3add286 refs/tags/0.3.1\n8d2e3c900edbc95fa0c036fd76f8e4f814aef2c1 refs/tags/0.3.2\n48b43372f49a3bb4dc0969d82a0fca183fb94662 refs/tags/0.3.3\n9ca947a3009ea6ba17814b20eb953272064eb2e6 refs/tags/0.4.0\n1b5643d04fba6d996a16d1ffc13d034a40003f8f refs/tags/0.5.0\n486b0eb580b1c465453d264ac758cc490c19c33e refs/tags/0.5.1\naedb0d9497390e20b9d2541cef2bb05a5cda7a71 refs/tags/0.5.2\n"), - toData("grok", GitCurrentCommit, 0, "4e6526a91a23eaec778184e16ce9a34d25d48bdc\n"), - toData("grok", GitMergeBase, 0, "4e6526a91a23eaec778184e16ce9a34d25d48bdc\n62707b8ac684efac35d301dbde57dc750880268e\n349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"), - toData("grok", GitCheckout, 0, "62707b8ac684efac35d301dbde57dc750880268e"), - - toData("nim-bytes2human", GitDiff, 0, ""), - toData("nim-bytes2human", GitTags, 0, ""), - toData("nim-bytes2human", GitCurrentcommit, 0, "ec2c1a758cabdd4751a06c8ebf2b923f19e32731\n") - ] - -#[ -Current directory is now E:\atlastest\nim-bytes2human -cmd git diff args [] --> ("", 0) -cmd git show-ref --tags args [] --> ("", 1) -cmd git log -n 1 --format=%H args [] --> (, 0) -[Warning] (nim-bytes2human) package has no tagged releases -nimble E:\atlastest\nim-bytes2human\bytes2human.nimble info (requires: @["nim >= 1.0.0"], srcDir: "src", tasks: @[]) -[Error] There were problems. -Error: execution of an external program failed: 'E:\nim\tools\atlas\atlas.exe clone https://github.com/disruptek/balls' -]# diff --git a/tools/atlas/tests/balls.nimble b/tools/atlas/tests/balls.nimble deleted file mode 100644 index 143e757e9..000000000 --- a/tools/atlas/tests/balls.nimble +++ /dev/null @@ -1,32 +0,0 @@ -version = "3.4.1" -author = "disruptek" -description = "a unittest framework with balls 🔴🟡🟢" -license = "MIT" - -# requires newTreeFrom -requires "https://github.com/disruptek/grok >= 0.5.0 & < 1.0.0" -requires "https://github.com/disruptek/ups < 1.0.0" -requires "https://github.com/planetis-m/sync#810bd2d" -#requires "https://github.com/c-blake/cligen < 2.0.0" - -bin = @["balls"] # build the binary for basic test running -installExt = @["nim"] # we need to install balls.nim also -skipDirs = @["tests"] # so stupid... who doesn't want tests? -#installFiles = @["balls.nim"] # https://github.com/nim-lang/Nim/issues/16661 - -task test, "run tests for ci": - when defined(windows): - exec "balls.cmd" - else: - exec "balls" - -task demo, "produce a demo": - exec "nim c --define:release balls.nim" - when (NimMajor, NimMinor) != (1, 0): - echo "due to nim bug #16307, use nim-1.0" - quit 1 - exec """demo docs/demo.svg "nim c --out=\$1 examples/fails.nim"""" - exec """demo docs/clean.svg "nim c --define:danger -f --out=\$1 tests/test.nim"""" - exec "nim c --define:release --define:ballsDry balls.nim" - exec """demo docs/runner.svg "balls"""" - diff --git a/tools/atlas/tests/grok.nimble b/tools/atlas/tests/grok.nimble deleted file mode 100644 index 1b6d77c08..000000000 --- a/tools/atlas/tests/grok.nimble +++ /dev/null @@ -1,5 +0,0 @@ -version = "0.0.4" -author = "disruptek" -description = "don't read too much into it" -license = "MIT" -requires "nim >= 1.0.0" diff --git a/tools/atlas/tests/nim-bytes2human.nimble b/tools/atlas/tests/nim-bytes2human.nimble deleted file mode 100644 index 9f3ae2479..000000000 --- a/tools/atlas/tests/nim-bytes2human.nimble +++ /dev/null @@ -1,7 +0,0 @@ -version = "0.2.2" -author = "Juan Carlos" -description = "Convert bytes to kilobytes, megabytes, gigabytes, etc." -license = "MIT" -srcDir = "src" - -requires "nim >= 1.0.0" # https://github.com/juancarlospaco/nim-bytes2human/issues/2#issue-714338524 diff --git a/tools/atlas/tests/nim.cfg b/tools/atlas/tests/nim.cfg deleted file mode 100644 index 5f568569b..000000000 --- a/tools/atlas/tests/nim.cfg +++ /dev/null @@ -1,11 +0,0 @@ -############# begin Atlas config section ########## ---noNimblePath ---path:"../balls" ---path:"../grok" ---path:"../ups" ---path:"../sync" ---path:"../npeg/src" ---path:"../testes" ---path:"../grok" ---path:"../nim-bytes2human/src" -############# end Atlas config section ########## diff --git a/tools/atlas/tests/npeg.nimble b/tools/atlas/tests/npeg.nimble deleted file mode 100644 index e71fc5aa5..000000000 --- a/tools/atlas/tests/npeg.nimble +++ /dev/null @@ -1,48 +0,0 @@ -# Package - -version = "0.24.1" -author = "Ico Doornekamp" -description = "a PEG library" -license = "MIT" -srcDir = "src" -installExt = @["nim"] - -# Dependencies - -requires "nim >= 0.19.0" - -# Test - -task test, "Runs the test suite": - exec "nimble testc && nimble testcpp && nimble testarc && nimble testjs" - -task testc, "C tests": - exec "nim c -r tests/tests.nim" - -task testcpp, "CPP tests": - exec "nim cpp -r tests/tests.nim" - -task testjs, "JS tests": - exec "nim js -r tests/tests.nim" - -task testdanger, "Runs the test suite in danger mode": - exec "nim c -d:danger -r tests/tests.nim" - -task testwin, "Mingw tests": - exec "nim c -d:mingw tests/tests.nim && wine tests/tests.exe" - -task test32, "32 bit tests": - exec "nim c --cpu:i386 --passC:-m32 --passL:-m32 tests/tests.nim && tests/tests" - -task testall, "Test all": - exec "nimble test && nimble testcpp && nimble testdanger && nimble testjs && nimble testwin" - -when (NimMajor, NimMinor) >= (1, 1): - task testarc, "--gc:arc tests": - exec "nim c --gc:arc -r tests/tests.nim" -else: - task testarc, "--gc:arc tests": - exec "true" - -task perf, "Test performance": - exec "nim cpp -r -d:danger tests/performance.nim" diff --git a/tools/atlas/tests/packages/packages.json b/tools/atlas/tests/packages/packages.json deleted file mode 100644 index d054a201b..000000000 --- a/tools/atlas/tests/packages/packages.json +++ /dev/null @@ -1,36 +0,0 @@ -[ - { - "name": "bytes2human", - "url": "https://github.com/juancarlospaco/nim-bytes2human", - "method": "git", - "tags": [ - "bytes", - "human", - "minimalism", - "size" - ], - "description": "Convert bytes to kilobytes, megabytes, gigabytes, etc.", - "license": "LGPLv3", - "web": "https://github.com/juancarlospaco/nim-bytes2human" - }, - { - "name": "npeg", - "url": "https://github.com/zevv/npeg", - "method": "git", - "tags": [ - "PEG", - "parser", - "parsing", - "regexp", - "regular", - "grammar", - "lexer", - "lexing", - "pattern", - "matching" - ], - "description": "PEG (Parsing Expression Grammars) string matching library for Nim", - "license": "MIT", - "web": "https://github.com/zevv/npeg" - } -] diff --git a/tools/atlas/tests/sync.nimble b/tools/atlas/tests/sync.nimble deleted file mode 100644 index a07ae8925..000000000 --- a/tools/atlas/tests/sync.nimble +++ /dev/null @@ -1,10 +0,0 @@ -# Package - -version = "1.4.0" -author = "Antonis Geralis" -description = "Useful synchronization primitives." -license = "MIT" - -# Deps - -requires "nim >= 1.0.0" diff --git a/tools/atlas/tests/testes.nimble b/tools/atlas/tests/testes.nimble deleted file mode 100644 index 60fe1d508..000000000 --- a/tools/atlas/tests/testes.nimble +++ /dev/null @@ -1,23 +0,0 @@ -version = "1.0.0" -author = "disruptek" -description = "a cure for salty testes" -license = "MIT" - -#requires "cligen >= 0.9.41 & <= 0.9.45" -#requires "bump >= 1.8.18 & < 2.0.0" -requires "https://github.com/disruptek/grok >= 0.0.4 & < 1.0.0" -requires "https://github.com/juancarlospaco/nim-bytes2human" - -bin = @["testes"] # build the binary for basic test running -installExt = @["nim"] # we need to install testes.nim also -skipDirs = @["tests"] # so stupid... who doesn't want tests? - -task test, "run tests for ci": - exec "nim c --run testes.nim" - -task demo, "produce a demo": - when (NimMajor, NimMinor) != (1, 0): - echo "due to nim bug #16307, use nim-1.0" - quit 1 - exec """demo docs/demo.svg "nim c --out=\$1 examples/balls.nim"""" - exec """demo docs/clean.svg "nim c --define:danger --out=\$1 tests/testicles.nim"""" diff --git a/tools/atlas/tests/ups.nimble b/tools/atlas/tests/ups.nimble deleted file mode 100644 index d91abbe60..000000000 --- a/tools/atlas/tests/ups.nimble +++ /dev/null @@ -1,13 +0,0 @@ -version = "0.0.2" -author = "disruptek" -description = "a package handler" -license = "MIT" - -requires "npeg >= 0.23.2 & < 1.0.0" -requires "https://github.com/disruptek/testes >= 1.0.0 & < 2.0.0" - -task test, "run tests": - when defined(windows): - exec "testes.cmd" - else: - exec findExe"testes" diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index 46bc39470..dba10d74a 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -1,6 +1,6 @@ ##[ avoid code duplication in CI pipelines. -For now, this is only used for openbsd + freebsd, but there is a lot of other code +For now, this is only used for openbsd, but there is a lot of other code duplication that could be removed. ## usage @@ -10,114 +10,12 @@ nim r tools/ci_generate.nim ``` ]## -import std/[strformat, os] +import std/strformat -const doNotEdit = "DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`" -proc genCiBsd(header: string, batch: int, num: int): string = +proc genCIopenbsd(batch: int, num: int): string = result = fmt""" -## {doNotEdit} +## do not edit directly; auto-generated by `nim r tools/ci_generate.nim` -{header} - -sources: -- https://github.com/nim-lang/Nim -environment: - NIM_TESTAMENT_BATCH: "{batch}_{num}" - 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 -""" - -proc genBuildExtras(echoRun, koch, nim: string): string = - result = fmt""" -{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 -""" - -proc genWindowsScript(buildAll: bool): string = - result = fmt""" -@echo off -rem {doNotEdit} -rem Build development version of the compiler; can be rerun safely -rem bare bones version of ci/funs.sh adapted for windows. - -rem Read in some common shared variables (shared with other tools), -rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file -for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H -SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe -echo "building from csources: %nim_csources%" - -if not exist %nim_csourcesDir% ( - git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir% -) - -if not exist %nim_csources% ( - cd %nim_csourcesDir% - git checkout %nim_csourcesHash% - echo "%PROCESSOR_ARCHITECTURE%" - if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( - SET ARCH=64 - ) - CALL build.bat - cd .. - copy /y bin\nim.exe %nim_csources% -) -""" - - if buildAll: - result.add genBuildExtras("", "koch", r"bin\nim.exe") - -proc genPosixScript(): string = - result = fmt""" -#!/bin/sh -# {doNotEdit} - -# build development version of the compiler; can be rerun safely. -# arguments can be passed, e.g.: -# CC=gcc ucpu=amd64 uos=darwin - -set -u # error on undefined variables -set -e # exit on first error - -. ci/funs.sh -nimBuildCsourcesIfNeeded "$@" - -{genBuildExtras("echo_run", "./koch", "bin/nim")} -""" - -proc main()= - let dir = ".builds" - # not too large to be resource friendly, refs bug #17107 - let num = 2 - # if you reduce this, make sure to remove files that shouldn't be generated, - # or better, do the cleanup logic here e.g.: `rm .builds/openbsd_*` - let headerFreebsd = """ -# 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 -""" - - let headerOpenbsd = """ image: openbsd/latest packages: - gmake @@ -128,14 +26,38 @@ packages: - sfml - sdl2 - libffi +sources: +- https://github.com/nim-lang/Nim +environment: + NIM_TESTAMENT_BATCH: "{batch}_{num}" + CC: /usr/bin/clang +tasks: +- setup: | + cd Nim + git clone --depth 1 -q https://github.com/nim-lang/csources.git + gmake -C csources -j $(sysctl -n hw.ncpuonline) + bin/nim c koch + echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv +- test: | + cd Nim + if ! ./koch runCI; then + nim c -r tools/ci_testresults.nim + exit 1 + fi +triggers: +- action: email + condition: failure + to: Andreas Rumpf """ +proc main()= + let num = 3 + # if you reduce this, make sure to remove files that shouldn't be generated, + # or better, do the cleanup logic here e.g.: `rm .builds/openbsd_*` for i in 0.. lldb.SBValue: - if isinstance(name, str): - return self.GetChildMemberWithName(name) - else: - return self.GetChildAtIndex(name) - - -# Make this easier to work with -lldb.SBValue.__getitem__ = sbvaluegetitem - - -def colored(in_str, *args, **kwargs): - # TODO: Output in color if user is in terminal - return in_str - - -def reprEnum(val, typ): - """ - this is a port of the nim runtime function `reprEnum` to python - NOTE: DOES NOT WORK WITH ORC - """ - val = int(val) - n = typ["node"] - sons_type = n["sons"].type.GetPointeeType().GetPointeeType() - sons = n["sons"].deref.Cast(sons_type.GetPointerType().GetArrayType(3)) - flags = int(typ["flags"].unsigned) - # 1 << 6 is {ntfEnumHole} - if ((1 << 6) & flags) == 0: - offset = val - sons[0]["offset"].unsigned - if offset >= 0 and 0 < n["len"].unsigned: - return NCSTRING(sons[offset]["name"])[1:-1] - else: - # ugh we need a slow linear search: - for i in range(n["len"].unsigned): - if sons[i]["offset"].unsigned == val: - return NCSTRING(sons[i]["name"])[1:-1] - - return str(val) + " (invalid data!)" - - -def get_nti(value, nim_name=None): - """DOES NOT WORK WITH ORC""" - name_split = value.type.name.split("_") - type_nim_name = nim_name or name_split[1] - id_string = name_split[-1].split(" ")[0] - - type_info_name = "NTI" + type_nim_name.lower() + "__" + id_string + "_" - print("TYPEINFONAME: ", type_info_name) - nti = value.target.FindFirstGlobalVariable(type_info_name) - if nti is None: - type_info_name = "NTI" + "__" + id_string + "_" - nti = value.target.FindFirstGlobalVariable(type_info_name) - if nti is None: - print( - f"NimEnumPrinter: lookup global symbol: '{type_info_name}' failed for {value.type.name}.\n" - ) - return type_nim_name, nti - - -def enum_to_string(value): - type_nim_name, nti = get_nti(value) - if nti is None: - return type_nim_name + "(" + str(value.unsigned) + ")" - return reprEnum(value.signed, nti), nti - - -def to_string(value): - # For getting NimStringDesc * value - data = value["data"] - try: - size = int(value["Sup"]["len"].unsigned) - if size > 2**14: - return None - except TypeError: - return None - - cast = data.Cast(value.target.FindFirstType("char").GetArrayType(size)) - return bytearray(cast.data.uint8s).decode("utf-8") - - -def to_stringV2(value: lldb.SBValue): - # For getting NimStringDesc * value - data = value["p"]["data"] - try: - size = int(value["len"].signed) - if size > 2**14: - return "... (too long)" - except TypeError: - return "" - - base_data_type = data.type.GetArrayElementType().GetTypedefedType() - cast = data.Cast(base_data_type.GetArrayType(size)) - return bytearray(cast.data.uint8s).decode("utf-8") - - -def NimStringDesc(value, internal_dict): - res = to_string(value) - if res: - return colored('"' + res + '"', "red") - else: - return str(value) - - -def NimStringV2(value: lldb.SBValue, internal_dict): - res = to_stringV2(value.GetNonSyntheticValue()) - if res is not None: - return colored('"' + res + '"', "red") - else: - return str(value) - - -def NCSTRING(value: lldb.SBValue, internal_dict=None): - ty = value.Dereference().type - val = value.target.CreateValueFromAddress( - value.name or "temp", lldb.SBAddress(value.unsigned, value.target), ty - ).AddressOf() - return val.summary - - -def ObjectV1(value, internal_dict): - if not value.num_children and not value.value: - return "" - - ignore_fields = set() - if "colonObjectType" in value.type.name: - value = value.Dereference() - ignore_fields.add("Sup") - - if not value.type.name: - return "" - - summary = value.summary - if summary is not None: - return summary - - if "_" in value.type.name: - obj_name = value.type.name.split("_")[1].replace("colonObjectType", "") - else: - obj_name = value.type.name - - obj_name = colored(obj_name, "green") - - num_children = value.num_children - - fields = ", ".join( - [ - value[i].name - + ": " - + (value[i].summary or value[i].value or value[i].type.name or "not found") - for i in range(num_children) - if value[i].name not in ignore_fields - ] - ) - - res = f"{obj_name}({fields})" - return res - - -def ObjectV2(value: lldb.SBValue, internal_dict): - custom_summary = get_summary(value) - if not custom_summary is None: - return custom_summary - - orig_value = value.GetNonSyntheticValue() - while orig_value.type.is_pointer: - orig_value = orig_value.Dereference() - - if "_" in orig_value.type.name: - obj_name = orig_value.type.name.split("_")[1].replace("colonObjectType", "") - else: - obj_name = orig_value.type.name - - num_children = value.num_children - fields = [] - - for i in range(num_children): - fields.append(f"{value[i].name}: {value[i].summary}") - - res = f"{obj_name}(" + ", ".join(fields) + ")" - return res - - -def Number(value: lldb.SBValue, internal_dict): - while value.type.is_pointer: - value = value.Dereference() - return colored(str(value.signed), "yellow") - - -def Float(value: lldb.SBValue, internal_dict): - while value.type.is_pointer: - value = value.Dereference() - return colored(str(value.value), "yellow") - - -def UnsignedNumber(value: lldb.SBValue, internal_dict): - while value.type.is_pointer: - value = value.Dereference() - return colored(str(value.unsigned), "yellow") - - -def Bool(value: lldb.SBValue, internal_dict): - while value.type.is_pointer: - value = value.Dereference() - return colored(str(value.GetValue()), "red") - - -def CharArray(value: lldb.SBValue, internal_dict): - return str([colored(f"'{char}'", "red") for char in value.uint8s]) - - -def Array(value: lldb.SBValue, internal_dict): - value = value.GetNonSyntheticValue() - return "[" + ", ".join([value[i].summary for i in range(value.num_children)]) + "]" - - -def Tuple(value: lldb.SBValue, internal_dict): - while value.type.is_pointer: - value = value.Dereference() - - num_children = value.num_children - - fields = [] - - for i in range(num_children): - key = value[i].name - val = value[i].summary - if key.startswith("Field"): - fields.append(f"{val}") - else: - fields.append(f"{key}: {val}") - - return "(" + ", ".join(fields) + f")" - - -def Enum(value, internal_dict): - tname = value.type.name.split("_")[1] - return colored(f"{tname}." + str(value.signed), "blue") - - -def EnumSet(value, internal_dict): - type_nim_name = value.type.name.split("_")[2] - # type_nim_name, nti = get_nti(value, type_nim_name) - - val = int(value.signed) - # if nti: - # enum_strings = [] - # i = 0 - # while val > 0: - # if (val & 1) == 1: - # enum_strings.append(reprEnum(i, nti)) - # val = val >> 1 - # i += 1 - - # return '{' + ', '.join(enum_strings) + '}' - return colored(f"{type_nim_name}." + str(val), "blue") - - -def Set(value, internal_dict): - vals = [] - max_vals = 7 - for child in value.children: - vals.append(child.value) - if len(vals) > max_vals: - vals.append("...") - break - - return "{" + ", ".join(vals) + "}" - - -def Table(value: lldb.SBValue, internal_dict): - fields = [] - - for i in range(value.num_children): - key = value[i].name - val = value[i].summary - fields.append(f"{key}: {val}") - - table_suffix = "Table" - return "{" + ", ".join(fields) + f"}}.{table_suffix}" - - -def HashSet(value: lldb.SBValue, internal_dict): - fields = [] - - for i in range(value.num_children): - fields.append(f"{value[i].summary}") - - table_suffix = "HashSet" - - return "{" + ", ".join(fields) + f"}}.{table_suffix}" - - -def StringTable(value: lldb.SBValue, internal_dict): - table = value.GetNonSyntheticValue() - mode = table["mode"].unsigned - - table_suffix = "StringTable" - - table_mode = "" - if mode == 0: - table_mode = "Case Sensitive" - elif mode == 1: - table_mode = "Case Insensitive" - elif mode == 2: - table_mode = "Style Insensitive" - - fields = [] - - for i in range(value.num_children): - key = value[i].name - val = value[i].summary - fields.append(f"{key}: {val}") - - return "{" + ", ".join(fields) + f"}}.{table_suffix}({table_mode})" - - -def Sequence(value: lldb.SBValue, internal_dict): - value = value.GetNonSyntheticValue() - - data_len = int(value["len"].unsigned) - data = value["p"]["data"] - base_data_type = data.type.GetArrayElementType() - - cast = data.Cast(base_data_type.GetArrayType(data_len)) - - return ( - "@[" - + ", ".join([cast[i].summary or cast[i].type.name for i in range(data_len)]) - + "]" - ) - - -class StringChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.data_type: lldb.SBType - self.first_element: lldb.SBValue - self.update() - self.count = 0 - - def num_children(self): - return self.count - - def get_child_index(self, name): - return int(name.lstrip("[").rstrip("]")) - - def get_child_at_index(self, index): - offset = index * self.data_size - return self.first_element.CreateChildAtOffset( - "[" + str(index) + "]", offset, self.data_type - ) - - def update(self): - data = self.value["p"]["data"] - size = int(self.value["len"].unsigned) - - self.count = size - self.first_element = data - - self.data_type = data.type.GetArrayElementType().GetTypedefedType() - self.data_size = self.data_type.GetByteSize() - - def has_children(self): - return bool(self.num_children()) - - -class ArrayChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.data_type: lldb.SBType - self.first_element: lldb.SBValue - self.update() - - def num_children(self): - return self.value.num_children - - def get_child_index(self, name: str): - return int(name.lstrip("[").rstrip("]")) - - def get_child_at_index(self, index): - offset = index * self.value[index].GetByteSize() - return self.first_element.CreateChildAtOffset( - "[" + str(index) + "]", offset, self.data_type - ) - - def update(self): - if not self.has_children(): - return - - self.first_element = self.value[0] - self.data_type = self.value.type.GetArrayElementType() - - def has_children(self): - return bool(self.num_children()) - - -class SeqChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.data_type: lldb.SBType - self.first_element: lldb.SBValue - self.data: lldb.SBValue - self.update() - - def num_children(self): - return int(self.value["len"].unsigned) - - def get_child_index(self, name: str): - return int(name.lstrip("[").rstrip("]")) - - def get_child_at_index(self, index): - offset = index * self.data[index].GetByteSize() - return self.first_element.CreateChildAtOffset( - "[" + str(index) + "]", offset, self.data_type - ) - - def update(self): - if not self.has_children(): - return - - data = self.value["p"]["data"] - self.data_type = data.type.GetArrayElementType() - - self.data = data.Cast(self.data_type.GetArrayType(self.num_children())) - self.first_element = self.data - - def has_children(self): - return bool(self.num_children()) - - -class ObjectChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.data_type: lldb.SBType - self.first_element: lldb.SBValue - self.data: lldb.SBValue - self.children: OrderedDict[str, int] = OrderedDict() - self.child_list: list[lldb.SBValue] = [] - self.update() - - def num_children(self): - return len(self.children) - - def get_child_index(self, name: str): - return self.children[name] - - def get_child_at_index(self, index): - return self.child_list[index] - - def populate_children(self): - self.children.clear() - self.child_list = [] - stack = [self.value] - - index = 0 - - while stack: - cur_val = stack.pop() - while cur_val.type.is_pointer: - cur_val = cur_val.Dereference() - - if cur_val.num_children > 0 and cur_val[0].name == "m_type": - if "_" in cur_val.type.name: - tname = cur_val.type.name.split("_")[1].replace( - "colonObjectType", "" - ) - else: - tname = cur_val.type.name - if tname == "TNimTypeV2": - # We've reached the end - break - - if ( - cur_val.num_children > 0 - and cur_val[0].name == "Sup" - and cur_val[0].type.name.startswith("tyObject") - ): - stack.append(cur_val[0]) - - for i in range(cur_val.num_children): - child = cur_val[i] - if child.name == "Sup": - continue - self.children[child.name] = index - self.child_list.append(child) - index += 1 - - def update(self): - self.populate_children() - - def has_children(self): - return bool(self.num_children()) - - -class HashSetChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.child_list: list[lldb.SBValue] = [] - self.update() - - def num_children(self): - return len(self.child_list) - - def get_child_index(self, name: str): - return int(name.lstrip("[").rstrip("]")) - - def get_child_at_index(self, index): - return self.child_list[index] - - def update(self): - self.child_list = [] - data = self.value["data"] - - tuple_len = int(data["len"].unsigned) - tuple = data["p"]["data"] - - base_data_type = tuple.type.GetArrayElementType() - - cast = tuple.Cast(base_data_type.GetArrayType(tuple_len)) - - index = 0 - for i in range(tuple_len): - el = cast[i] - field0 = int(el["Field0"].unsigned) - if field0 == 0: - continue - key = el["Field1"] - child = key.CreateValueFromAddress( - f"[{str(index)}]", key.GetLoadAddress(), key.GetType() - ) - index += 1 - - self.child_list.append(child) - - def has_children(self): - return bool(self.num_children()) - - -class SetCharChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.ty = self.value.target.FindFirstType("char") - self.child_list: list[lldb.SBValue] = [] - self.update() - - def num_children(self): - return len(self.child_list) - - def get_child_index(self, name: str): - return int(name.lstrip("[").rstrip("]")) - - def get_child_at_index(self, index): - return self.child_list[index] - - def update(self): - self.child_list = [] - cur_pos = 0 - for child in self.value.children: - child_val = child.signed - if child_val != 0: - temp = child_val - num_bits = 8 - while temp != 0: - is_set = temp & 1 - if is_set == 1: - data = lldb.SBData.CreateDataFromInt(cur_pos) - child = self.value.synthetic_child_from_data( - f"[{len(self.child_list)}]", data, self.ty - ) - self.child_list.append(child) - temp = temp >> 1 - cur_pos += 1 - num_bits -= 1 - cur_pos += num_bits - else: - cur_pos += 8 - - def has_children(self): - return bool(self.num_children()) - - -class SetIntChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.ty = self.value.target.FindFirstType(f"NI64") - self.child_list: list[lldb.SBValue] = [] - self.update() - - def num_children(self): - return len(self.child_list) - - def get_child_index(self, name: str): - return int(name.lstrip("[").rstrip("]")) - - def get_child_at_index(self, index): - return self.child_list[index] - - def update(self): - self.child_list = [] - bits = self.value.GetByteSize() * 8 - - cur_pos = -(bits // 2) - - if self.value.num_children > 0: - children = self.value.children - else: - children = [self.value] - - for child in children: - child_val = child.signed - if child_val != 0: - temp = child_val - num_bits = 8 - while temp != 0: - is_set = temp & 1 - if is_set == 1: - data = lldb.SBData.CreateDataFromInt(cur_pos) - child = self.value.synthetic_child_from_data( - f"[{len(self.child_list)}]", data, self.ty - ) - self.child_list.append(child) - temp = temp >> 1 - cur_pos += 1 - num_bits -= 1 - cur_pos += num_bits - else: - cur_pos += 8 - - def has_children(self): - return bool(self.num_children()) - - -class SetUIntChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.ty = self.value.target.FindFirstType(f"NU64") - self.child_list: list[lldb.SBValue] = [] - self.update() - - def num_children(self): - return len(self.child_list) - - def get_child_index(self, name: str): - return int(name.lstrip("[").rstrip("]")) - - def get_child_at_index(self, index): - return self.child_list[index] - - def update(self): - self.child_list = [] - - cur_pos = 0 - if self.value.num_children > 0: - children = self.value.children - else: - children = [self.value] - - for child in children: - child_val = child.signed - if child_val != 0: - temp = child_val - num_bits = 8 - while temp != 0: - is_set = temp & 1 - if is_set == 1: - data = lldb.SBData.CreateDataFromInt(cur_pos) - child = self.value.synthetic_child_from_data( - f"[{len(self.child_list)}]", data, self.ty - ) - self.child_list.append(child) - temp = temp >> 1 - cur_pos += 1 - num_bits -= 1 - cur_pos += num_bits - else: - cur_pos += 8 - - def has_children(self): - return bool(self.num_children()) - - -class SetEnumChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.ty = self.value.target.FindFirstType(f"NU64") - self.child_list: list[lldb.SBValue] = [] - self.update() - - def num_children(self): - return len(self.child_list) - - def get_child_index(self, name: str): - return int(name.lstrip("[").rstrip("]")) - - def get_child_at_index(self, index): - return self.child_list[index] - - def update(self): - self.child_list = [] - - cur_pos = 0 - if self.value.num_children > 0: - children = self.value.children - else: - children = [self.value] - - for child in children: - child_val = child.unsigned - if child_val != 0: - temp = child_val - num_bits = 8 - while temp != 0: - is_set = temp & 1 - if is_set == 1: - data = lldb.SBData.CreateDataFromInt(cur_pos) - child = self.value.synthetic_child_from_data( - f"[{len(self.child_list)}]", data, self.ty - ) - self.child_list.append(child) - temp = temp >> 1 - cur_pos += 1 - num_bits -= 1 - cur_pos += num_bits - else: - cur_pos += 8 - - def has_children(self): - return bool(self.num_children()) - - -class TableChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.children: OrderedDict[str, int] = OrderedDict() - self.child_list: list[lldb.SBValue] = [] - self.update() - - def num_children(self): - return len(self.child_list) - - def get_child_index(self, name: str): - return self.children[name] - - def get_child_at_index(self, index): - return self.child_list[index] - - def update(self): - self.child_list = [] - data = self.value["data"] - - tuple_len = int(data["len"].unsigned) - tuple = data["p"]["data"] - - base_data_type = tuple.type.GetArrayElementType() - - cast = tuple.Cast(base_data_type.GetArrayType(tuple_len)) - - index = 0 - for i in range(tuple_len): - el = cast[i] - field0 = int(el["Field0"].unsigned) - if field0 == 0: - continue - key = el["Field1"] - val = el["Field2"] - child = val.CreateValueFromAddress( - key.summary, val.GetLoadAddress(), val.GetType() - ) - self.child_list.append(child) - self.children[key.summary] = index - index += 1 - - def has_children(self): - return bool(self.num_children()) - - -class StringTableChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - self.value = value - self.children: OrderedDict[str, int] = OrderedDict() - self.child_list: list[lldb.SBValue] = [] - self.update() - - def num_children(self): - return len(self.child_list) - - def get_child_index(self, name: str): - return self.children[name] - - def get_child_at_index(self, index): - return self.child_list[index] - - def update(self): - self.children.clear() - self.child_list = [] - data = self.value["data"] - - tuple_len = int(data["len"].unsigned) - tuple = data["p"]["data"] - - base_data_type = tuple.type.GetArrayElementType() - - cast = tuple.Cast(base_data_type.GetArrayType(tuple_len)) - - index = 0 - for i in range(tuple_len): - el = cast[i] - field0 = int(el["Field2"].unsigned) - if field0 == 0: - continue - key = el["Field0"] - val = el["Field1"] - child = val.CreateValueFromAddress( - key.summary, val.GetLoadAddress(), val.GetType() - ) - self.child_list.append(child) - self.children[key.summary] = index - index += 1 - - def has_children(self): - return bool(self.num_children()) - - -class CustomObjectChildrenProvider: - def __init__(self, value: lldb.SBValue, internalDict): - print("CUSTOMOBJ: ", value.name) - self.value: lldb.SBValue = get_synthetic(value) or value - for child in self.value.children: - print(child) - - def num_children(self): - return self.value.num_children - - def get_child_index(self, name: str): - return self.value.GetIndexOfChildWithName(name) - - def get_child_at_index(self, index): - return self.value.GetChildAtIndex(index) - - def update(self): - pass - - def has_children(self): - return self.num_children() > 0 - - -def echo(debugger: lldb.SBDebugger, command: str, result, internal_dict): - debugger.HandleCommand("po " + command) - - -SUMMARY_FUNCTIONS: dict[str, lldb.SBFunction] = {} -SYNTHETIC_FUNCTIONS: dict[str, lldb.SBFunction] = {} - - -def get_summary(value: lldb.SBValue) -> Union[str, None]: - base_type = get_base_type(value.type) - - fn = SUMMARY_FUNCTIONS.get(base_type.name) - if fn is None: - return None - - res = executeCommand( - f"{fn.name}(*({base_type.GetPointerType().name})" + str(value.GetLoadAddress()) + ");" - ) - - if res.error.fail: - return None - - return res.summary.strip('"') - - -def get_synthetic(value: lldb.SBValue) -> Union[lldb.SBValue, None]: - base_type = get_base_type(value.type) - - fn = SYNTHETIC_FUNCTIONS.get(base_type.name) - if fn is None: - return None - - res = executeCommand( - f"{fn.name}(*({base_type.GetPointerType().name})" + str(value.GetLoadAddress()) + ");" - ) - - if res.error.fail: - return None - - return res - - -def get_base_type(ty: lldb.SBType) -> lldb.SBType: - temp = ty - while temp.IsPointerType(): - temp = temp.GetPointeeType() - return temp - - -def breakpoint_function_wrapper(frame: lldb.SBFrame, bp_loc, internal_dict): - """This allows function calls to Nim for custom object summaries and synthetic children""" - debugger = lldb.debugger - - global SUMMARY_FUNCTIONS - global SYNTHETIC_FUNCTIONS - for tname, fn in SYNTHETIC_FUNCTIONS.items(): - print("DELETING SYNTH: ", tname) - debugger.HandleCommand(f"type synthetic delete -w nim {tname}") - - SUMMARY_FUNCTIONS = {} - SYNTHETIC_FUNCTIONS = {} - - target: lldb.SBTarget = debugger.GetSelectedTarget() - print("BREAKPOINT") - module = frame.GetSymbolContext(lldb.eSymbolContextModule).module - - for sym in module: - if not sym.name.startswith("lldbDebugSummary") and not sym.name.startswith( - "lldbDebugSynthetic" - ): - continue - - print("SYM: ", sym.name) - - fn_syms: lldb.SBSymbolContextList = target.FindFunctions(sym.name) - if not fn_syms.GetSize() > 0: - continue - fn_sym: lldb.SBSymbolContext = fn_syms.GetContextAtIndex(0) - - print("fn found!") - - fn: lldb.SBFunction = fn_sym.function - fn_type: lldb.SBType = fn.type - arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes() - - if not arg_types.GetSize() > 0: - continue - arg_type: lldb.SBType = get_base_type(arg_types.GetTypeAtIndex(0)) - - print("FIRST ARG TYPE: ", arg_type.name) - - if sym.name.startswith("lldbDebugSummary"): - SUMMARY_FUNCTIONS[arg_type.name] = fn - elif sym.name.startswith("lldbDebugSynthetic"): - SYNTHETIC_FUNCTIONS[arg_type.name] = fn - debugger.HandleCommand( - f"type synthetic add -w nim -l {__name__}.CustomObjectChildrenProvider -x {arg_type.name}$" - ) - - -def executeCommand(command, *args): - debugger = lldb.debugger - process = debugger.GetSelectedTarget().GetProcess() - frame: lldb.SBFrame = process.GetSelectedThread().GetSelectedFrame() - # module = frame.GetSymbolContext(lldb.eSymbolContextModule).module - # for sym in module: - # print("SYM: ", sym.name) - # target = debugger.GetSelectedTarget() - - expr_options = lldb.SBExpressionOptions() - expr_options.SetIgnoreBreakpoints(False) - expr_options.SetFetchDynamicValue(lldb.eDynamicCanRunTarget) - expr_options.SetTimeoutInMicroSeconds(30 * 1000 * 1000) # 30 second timeout - expr_options.SetTryAllThreads(True) - expr_options.SetUnwindOnError(False) - expr_options.SetGenerateDebugInfo(True) - expr_options.SetLanguage(lldb.eLanguageTypeC) - expr_options.SetCoerceResultToId(True) - res = frame.EvaluateExpression(command, expr_options) - # if res.error.fail: - # print("ERROR: ", res.error.GetError()) - # return str(res.error) - return res - - -def __lldb_init_module(debugger, internal_dict): - # debugger.HandleCommand(f"type summary add -w nim -n any -F {__name__}.CatchAll -x .*") - debugger.HandleCommand(f"type summary add -w nim -n sequence -F {__name__}.Sequence -x tySequence_+[[:alnum:]]+$") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SeqChildrenProvider -x tySequence_+[[:alnum:]]+$") - - debugger.HandleCommand(f"type summary add -w nim -n chararray -F {__name__}.CharArray -x char\s+[\d+]") - debugger.HandleCommand(f"type summary add -w nim -n array -F {__name__}.Array -x tyArray_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ArrayChildrenProvider -x tyArray_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n string -F {__name__}.NimStringDesc NimStringDesc") - - debugger.HandleCommand(f"type summary add -w nim -n stringv2 -F {__name__}.NimStringV2 -x NimStringV2$") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringChildrenProvider -x NimStringV2$") - - debugger.HandleCommand(f"type summary add -w nim -n cstring -F {__name__}.NCSTRING NCSTRING") - - debugger.HandleCommand(f"type summary add -w nim -n object -F {__name__}.ObjectV2 -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ObjectChildrenProvider -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+$") - - debugger.HandleCommand(f"type summary add -w nim -n tframe -F {__name__}.ObjectV2 -x TFrame$") - - debugger.HandleCommand(f"type summary add -w nim -n rootobj -F {__name__}.ObjectV2 -x RootObj$") - - debugger.HandleCommand(f"type summary add -w nim -n enum -F {__name__}.Enum -x tyEnum_+[[:alnum:]]+_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n hashset -F {__name__}.HashSet -x tyObject_+HashSet_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.HashSetChildrenProvider -x tyObject_+HashSet_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n setuint -F {__name__}.Set -x tySet_+tyInt_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetIntChildrenProvider -x tySet_+tyInt[0-9]+_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n setint -F {__name__}.Set -x tySet_+tyInt[0-9]+_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n setuint2 -F {__name__}.Set -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyInt_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n setenum -F {__name__}.EnumSet -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n setchar -F {__name__}.Set -x tySet_+tyChar_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetCharChildrenProvider -x tySet_+tyChar_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n table -F {__name__}.Table -x tyObject_+Table_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.TableChildrenProvider -x tyObject_+Table_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n stringtable -F {__name__}.StringTable -x tyObject_+StringTableObj_+[[:alnum:]]+") - debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringTableChildrenProvider -x tyObject_+StringTableObj_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n tuple2 -F {__name__}.Tuple -x tyObject_+Tuple_+[[:alnum:]]+") - debugger.HandleCommand(f"type summary add -w nim -n tuple -F {__name__}.Tuple -x tyTuple_+[[:alnum:]]+") - # debugger.HandleCommand(f"type summary add -w nim -n TNimType -F {__name__}.Object TNimType") - debugger.HandleCommand(f"type summary add -w nim -n TNimTypeV2 -F {__name__}.ObjectV2 TNimTypeV2") - # debugger.HandleCommand(f"type summary add -w nim -n TNimNode -F {__name__}.Object TNimNode") - debugger.HandleCommand(f"type summary add -w nim -n float -F {__name__}.Float NF") - debugger.HandleCommand(f"type summary add -w nim -n float32 -F {__name__}.Float NF32") - debugger.HandleCommand(f"type summary add -w nim -n float64 -F {__name__}.Float NF64") - debugger.HandleCommand(f"type summary add -w nim -n integer -F {__name__}.Number -x NI") - debugger.HandleCommand(f"type summary add -w nim -n integer8 -F {__name__}.Number -x NI8") - debugger.HandleCommand(f"type summary add -w nim -n integer16 -F {__name__}.Number -x NI16") - debugger.HandleCommand(f"type summary add -w nim -n integer32 -F {__name__}.Number -x NI32") - debugger.HandleCommand(f"type summary add -w nim -n integer64 -F {__name__}.Number -x NI64") - debugger.HandleCommand(f"type summary add -w nim -n bool -F {__name__}.Bool -x bool") - debugger.HandleCommand(f"type summary add -w nim -n bool2 -F {__name__}.Bool -x NIM_BOOL") - debugger.HandleCommand(f"type summary add -w nim -n uinteger -F {__name__}.UnsignedNumber -x NU") - debugger.HandleCommand(f"type summary add -w nim -n uinteger8 -F {__name__}.UnsignedNumber -x NU8") - debugger.HandleCommand(f"type summary add -w nim -n uinteger16 -F {__name__}.UnsignedNumber -x NU16") - debugger.HandleCommand(f"type summary add -w nim -n uinteger32 -F {__name__}.UnsignedNumber -x NU32") - debugger.HandleCommand(f"type summary add -w nim -n uinteger64 -F {__name__}.UnsignedNumber -x NU64") - debugger.HandleCommand("type category enable nim") - debugger.HandleCommand(f"command script add -f {__name__}.echo echo") - debugger.HandleCommand(f"command script add -f {__name__}.handle_command ddp") - debugger.HandleCommand(f"breakpoint command add -F {__name__}.breakpoint_function_wrapper --script-type python 1") \ No newline at end of file diff --git a/tools/deps.nim b/tools/deps.nim index e43f7a2b4..b238f12ed 100644 --- a/tools/deps.nim +++ b/tools/deps.nim @@ -1,22 +1,26 @@ -import std/[os, uri, strformat, strutils] -import std/private/gitutils - -when defined(nimPreviewSlimSystem): - import std/assertions +import os, uri, strformat, osproc, strutils proc exec(cmd: string) = echo "deps.cmd: " & cmd let status = execShellCmd(cmd) doAssert status == 0, cmd -proc execRetry(cmd: string) = - let ok = retryCall(call = block: - let status = execShellCmd(cmd) - let result = status == 0 - if not result: - echo fmt"failed command: '{cmd}', status: {status}" - result) - doAssert ok, cmd +proc execEx(cmd: string): tuple[output: TaintedString, exitCode: int] = + echo "deps.cmd: " & cmd + execCmdEx(cmd, {poStdErrToStdOut, poUsePath, poEvalCommand}) + +proc isGitRepo(dir: string): bool = + # This command is used to get the relative path to the root of the repository. + # Using this, we can verify whether a folder is a git repository by checking + # whether the command success and if the output is empty. + let (output, status) = execEx fmt"git -C {quoteShell(dir)} rev-parse --show-cdup" + # On Windows there will be a trailing newline on success, remove it. + # The value of a successful call typically won't have a whitespace (it's + # usually a series of ../), so we know that it's safe to unconditionally + # remove trailing whitespaces from the result. + result = status == 0 and output.strip() == "" + +const commitHead* = "HEAD" proc cloneDependency*(destDirBase: string, url: string, commit = commitHead, appendRepoName = true, allowBundled = false) = @@ -25,19 +29,14 @@ proc cloneDependency*(destDirBase: string, url: string, commit = commitHead, let name = p.splitFile.name var destDir = destDirBase if appendRepoName: destDir = destDir / name - let quotedDestDir = destDir.quoteShell + let destDir2 = destDir.quoteShell if not dirExists(destDir): # note: old code used `destDir / .git` but that wouldn't prevent git clone # from failing - execRetry fmt"git clone -q {url} {quotedDestDir}" + exec fmt"git clone -q {url} {destDir2}" if isGitRepo(destDir): - let oldDir = getCurrentDir() - setCurrentDir(destDir) - try: - execRetry "git fetch -q" - exec fmt"git checkout -q {commit}" - finally: - setCurrentDir(oldDir) + exec fmt"git -C {destDir2} fetch -q" + exec fmt"git -C {destDir2} checkout -q {commit}" elif allowBundled: discard "this dependency was bundled with Nim, don't do anything" else: diff --git a/tools/detect/detect.nim b/tools/detect/detect.nim index ed1caf78c..841b3a675 100644 --- a/tools/detect/detect.nim +++ b/tools/detect/detect.nim @@ -12,10 +12,7 @@ # The second one is more portable, and less semantically correct. It only works # when there's a backing C compiler available as well, preventing standalone # compilation. -import std/[os, strutils] - -when defined(nimPreviewSlimSystem): - import std/syncio +import os, strutils when defined(openbsd) or defined(freebsd) or defined(netbsd): const diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index 0753248bc..4a491cf88 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -1,52 +1,49 @@ import dom import fuzzysearch -import std/[jsfetch, asyncjs] +proc textContent(e: Element): cstring {. + importcpp: "#.textContent", nodecl.} -proc setTheme(theme: cstring) {.exportc.} = - document.documentElement.setAttribute("data-theme", theme) - window.localStorage.setItem("theme", theme) +proc textContent(e: Node): cstring {. + importcpp: "#.textContent", nodecl.} -# 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") - - for pragmaDots in document.getElementsByClassName("pragmadots"): - pragmaDots.onclick = proc (event: Event) = - # Hide tease - event.target.parentNode.style.display = "none" - # Show actual - event.target.parentNode.nextSibling.style.display = "inline" - - -proc tree(tag: cstring; kids: varargs[Element]): Element = +proc tree(tag: string; kids: varargs[Element]): Element = result = document.createElement tag for k in kids: result.appendChild k proc add(parent, kid: Element) = - if parent.nodeName == "TR" and (kid.nodeName == "TD" or kid.nodeName == "TH"): + if parent.nodeName == cstring"TR" and ( + kid.nodeName == cstring"TD" or kid.nodeName == cstring"TH"): let k = document.createElement("TD") appendChild(k, kid) appendChild(parent, k) else: appendChild(parent, kid) -proc setClass(e: Element; value: cstring) = +proc setClass(e: Element; value: string) = 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 = document.getElementById(id) + let x = 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" + contents = @[] + for ahref in db: + contents.add ahref.getAttribute("data-doc-search-tag") let ul = tree("UL") result = tree("DIV") result.setClass"search_results" var matches: seq[(Node, int)] = @[] for i in 0.. { - - const div = document.createElement("div") - div.classList.add("copyToClipBoard") - - const preTag = document.createElement("pre") - preTag.innerHTML = e.innerHTML - - const button = document.createElement("button") - button.value = e.textContent.replace('...', '') - button.classList.add("copyToClipBoardBtn") - button.style.cursor = "pointer" - - div.appendChild(preTag) - div.appendChild(button) - - e.outerHTML = div.outerHTML - - }) - } - - - function copyTextToClipboard(e) { - const clipBoardContent = e.target.value - navigator.clipboard.writeText(clipBoardContent).then(function() { - e.target.style.setProperty("--clipboard-image", "var(--clipboard-image-selected)") - }, function(err) { - console.error("Could not copy text: ", err); - }); - } - - window.addEventListener("click", (e) => { - if (e.target.classList.contains("copyToClipBoardBtn")) { - copyTextToClipboard(e) - } - }) - - window.addEventListener("mouseover", (e) => { - if (e.target.nodeName === "PRE") { - e.target.nextElementSibling.style.setProperty("--clipboard-image", "var(--clipboard-image-normal)") - } - }) - - window.addEventListener("DOMContentLoaded", updatePreTags) - - """ - .} - -copyToClipboard() -window.addEventListener("DOMContentLoaded", onDOMLoaded) diff --git a/tools/finish.nim b/tools/finish.nim index 69838829c..4b5131045 100644 --- a/tools/finish.nim +++ b/tools/finish.nim @@ -41,7 +41,7 @@ proc downloadMingw(): DownloadResult = let curl = findExe"curl" var cmd: string if curl.len > 0: - cmd = quoteShell(curl) & " --output " & "dist" / mingw & " " & url + cmd = quoteShell(curl) & " --out " & "dist" / mingw & " " & url elif fileExists"bin/nimgrab.exe": cmd = r"bin\nimgrab.exe " & url & " dist" / mingw if cmd.len > 0: diff --git a/tools/grammar_nanny.nim b/tools/grammar_nanny.nim index bcb3a044f..397041559 100644 --- a/tools/grammar_nanny.nim +++ b/tools/grammar_nanny.nim @@ -3,11 +3,8 @@ import std / [strutils, sets] -when defined(nimPreviewSlimSystem): - import std/syncio - import ".." / compiler / [ - llstream, lexer, options, msgs, idents, + llstream, ast, lexer, options, msgs, idents, lineinfos, pathutils] proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) = @@ -16,7 +13,6 @@ proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) = var stream = llStreamOpen(data) var declaredSyms = initHashSet[string]() var usedSyms = initHashSet[string]() - usedSyms.incl "module" # 'module' is the start rule. if stream != nil: declaredSyms.incl "section" # special case for 'section(RULE)' in the grammar var @@ -39,10 +35,6 @@ proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) = usedSyms.incl word else: rawGetTok(L, tok) - for u in declaredSyms: - if u notin usedSyms: - echo "Unused non-terminal: ", u - for u in usedSyms: if u notin declaredSyms: echo "Undeclared non-terminal: ", u diff --git a/tools/heapdump2dot.nim b/tools/heapdump2dot.nim index 6704cdfb0..4cee6d674 100644 --- a/tools/heapdump2dot.nim +++ b/tools/heapdump2dot.nim @@ -1,5 +1,5 @@ -include std/prelude +include prelude proc main(input, output: string) = type NodeKind = enum diff --git a/tools/heapdumprepl.nim b/tools/heapdumprepl.nim index 4f06cf111..dbaef2f2c 100644 --- a/tools/heapdumprepl.nim +++ b/tools/heapdumprepl.nim @@ -1,4 +1,5 @@ -include std/prelude + +include prelude import intsets type @@ -16,6 +17,7 @@ type roots: Table[int, NodeKind] proc add(father: Node; son: int) = + if father.kids.isNil: father.kids = @[] father.kids.add(son) proc renderNode(g: Graph; id: int) = @@ -139,8 +141,8 @@ proc importData(input: string): Graph = close(i) else: quit "error: cannot open " & input - result.nodes = move nodes - result.roots = move roots + shallowCopy(result.nodes, nodes) + shallowCopy(result.roots, roots) if paramCount() == 1: repl(importData(paramStr(1))) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index f63aea030..81117dd52 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -1,36 +1,21 @@ ## Part of 'koch' responsible for the documentation generation. -import std/[os, strutils, osproc, sets, pathnorm, sequtils] - -import officialpackages -export exec - -when defined(nimPreviewSlimSystem): - import std/assertions - -# XXX: Remove this feature check once the csources supports it. -when defined(nimHasCastPragmaBlocks): - import std/pegs +import os, strutils, osproc, sets, pathnorm, pegs from std/private/globs import nativeToUnixPath, walkDirRecFilter, PathEntry import "../compiler/nimpaths" const gaCode* = " --doc.googleAnalytics:UA-48159761-1" - paCode* = " --doc.plausibleAnalytics:nim-lang.org" # errormax: subsequent errors are probably consequences of 1st one; a simple # bug could cause unlimited number of errors otherwise, hard to debug in CI. - docDefines = "-d:nimExperimentalLinenoiseExtra" - nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off --hint:XDeclaredButNotUsed:off --warning:UnusedImport:off -d:boot --putenv:nimversion=$# $#" % [system.NimVersion, docDefines] + nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off --hint:XDeclaredButNotUsed:off --warning:UnusedImport:off -d:boot --putenv:nimversion=$#" % system.NimVersion gitUrl = "https://github.com/nim-lang/Nim" docHtmlOutput = "doc/html" webUploadOutput = "web/upload" var nimExe*: string -const allowList = ["jsbigints.nim", "jsheaders.nim", "jsformdata.nim", "jsfetch.nim", "jsutils.nim"] -template isJsOnly(file: string): bool = - file.isRelativeTo("lib/js") or - file.extractFilename in allowList +template isJsOnly(file: string): bool = file.isRelativeTo("lib/js") proc exe*(f: string): string = result = addFileExt(f, ExeExt) @@ -51,20 +36,38 @@ proc findNimImpl*(): tuple[path: string, ok: bool] = proc findNim*(): string = findNimImpl().path +proc exec*(cmd: string, errorcode: int = QuitFailure, additionalPath = "") = + let prevPath = getEnv("PATH") + if additionalPath.len > 0: + var absolute = additionalPath + if not absolute.isAbsolute: + absolute = getCurrentDir() / absolute + echo("Adding to $PATH: ", absolute) + putEnv("PATH", (if prevPath.len > 0: prevPath & PathSep else: "") & absolute) + echo(cmd) + if execShellCmd(cmd) != 0: quit("FAILURE", errorcode) + putEnv("PATH", prevPath) + template inFold*(desc, body) = - if existsEnv("GITHUB_ACTIONS"): + if existsEnv("TRAVIS"): + echo "travis_fold:start:" & desc.replace(" ", "_") + elif existsEnv("GITHUB_ACTIONS"): echo "::group::" & desc elif existsEnv("TF_BUILD"): echo "##[group]" & desc + body - if existsEnv("GITHUB_ACTIONS"): + + if existsEnv("TRAVIS"): + echo "travis_fold:end:" & desc.replace(" ", "_") + elif existsEnv("GITHUB_ACTIONS"): echo "::endgroup::" elif existsEnv("TF_BUILD"): echo "##[endgroup]" proc execFold*(desc, cmd: string, errorcode: int = QuitFailure, additionalPath = "") = ## Execute shell command. Add log folding for various CI services. - let desc = if desc.len == 0: cmd else: desc + # https://github.com/travis-ci/travis-ci/issues/2285#issuecomment-42724719 inFold(desc): exec(cmd, errorcode, additionalPath) @@ -90,39 +93,44 @@ proc nimCompile*(input: string, outputDir = "bin", mode = "c", options = "") = let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input exec cmd -proc nimCompileFold*(desc, input: string, outputDir = "bin", mode = "c", options = "", outputName = "") = - let outputName2 = if outputName.len == 0: input.splitFile.name.exe else: outputName.exe - let output = outputDir / outputName2 +proc nimCompileFold*(desc, input: string, outputDir = "bin", mode = "c", options = "") = + let output = outputDir / input.splitFile.name.exe let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input execFold(desc, cmd) -proc getMd2html(): seq[string] = +proc getRst2html(): seq[string] = for a in walkDirRecFilter("doc"): let path = a.path - if a.kind == pcFile and path.splitFile.ext == ".md" and path.lastPathPart notin - ["docs.md", "nimfix.md", - "docstyle.md" # docstyle.md shouldn't be converted to html separately; - # it's included in contributing.md. - ]: + if a.kind == pcFile and path.splitFile.ext == ".rst" and path.lastPathPart notin + ["docs.rst", "nimfix.rst"]: # maybe we should still show nimfix, could help reviving it # `docs` is redundant with `overview`, might as well remove that file? result.add path - doAssert "doc/manual/var_t_return.md".unixToNativePath in result # sanity check + doAssert "doc/manual/var_t_return.rst".unixToNativePath in result # sanity check const - mdPdfList = """ -manual.md -lib.md -tut1.md -tut2.md -tut3.md -nimc.md -niminst.md -mm.md -""".splitWhitespace().mapIt("doc" / it) + pdf = """ +doc/manual.rst +doc/lib.rst +doc/tut1.rst +doc/tut2.rst +doc/tut3.rst +doc/nimc.rst +doc/niminst.rst +doc/gc.rst +""".splitWhitespace() + + doc0 = """ +lib/system/threads.nim +lib/system/channels.nim +""".splitWhitespace() # ran by `nim doc0` instead of `nim doc` withoutIndex = """ +lib/wrappers/mysql.nim +lib/wrappers/sqlite3.nim +lib/wrappers/postgres.nim lib/wrappers/tinyc.nim +lib/wrappers/odbcsql.nim lib/wrappers/pcre.nim lib/wrappers/openssl.nim lib/posix/posix.nim @@ -132,6 +140,7 @@ lib/posix/termios.nim # some of these are include files so shouldn't be docgen'd ignoredModules = """ +lib/prelude.nim lib/pure/future.nim lib/pure/collections/hashcommon.nim lib/pure/collections/tableimpl.nim @@ -152,35 +161,6 @@ lib/posix/posix_openbsd_amd64.nim lib/posix/posix_haiku.nim """.splitWhitespace() - officialPackagesList = """ -pkgs/asyncftpclient/src/asyncftpclient.nim -pkgs/smtp/src/smtp.nim -pkgs/punycode/src/punycode.nim -pkgs/db_connector/src/db_connector/db_common.nim -pkgs/db_connector/src/db_connector/db_mysql.nim -pkgs/db_connector/src/db_connector/db_odbc.nim -pkgs/db_connector/src/db_connector/db_postgres.nim -pkgs/db_connector/src/db_connector/db_sqlite.nim -""".splitWhitespace() - - officialPackagesListWithoutIndex = """ -pkgs/db_connector/src/db_connector/mysql.nim -pkgs/db_connector/src/db_connector/sqlite3.nim -pkgs/db_connector/src/db_connector/postgres.nim -pkgs/db_connector/src/db_connector/odbcsql.nim -pkgs/db_connector/src/db_connector/private/dbutils.nim -""".splitWhitespace() - -proc findName(name: string): string = - doAssert name[0..4] == "pkgs/" - var i = 5 - while i < name.len: - if name[i] != '/': - inc i - result.add name[i] - else: - break - when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo): proc isRelativeTo(path, base: string): bool = let path = path.normalizedPath @@ -190,23 +170,22 @@ when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo): proc getDocList(): seq[string] = var docIgnore: HashSet[string] + for a in doc0: docIgnore.incl a for a in withoutIndex: docIgnore.incl a for a in ignoredModules: docIgnore.incl a # don't ignore these even though in lib/system (not include files) const goodSystem = """ +lib/system/io.nim lib/system/nimscript.nim lib/system/assertions.nim lib/system/iterators.nim -lib/system/exceptions.nim lib/system/dollars.nim -lib/system/ctypes.nim +lib/system/widestrs.nim """.splitWhitespace() proc follow(a: PathEntry): bool = - result = a.path.lastPathPart notin ["nimcache", htmldocsDirname, - "includes", "deprecated", "genode"] and - not a.path.isRelativeTo("lib/fusion") # fusion was un-bundled but we need to keep this in case user has it installed + a.path.lastPathPart notin ["nimcache", "htmldocs", "includes", "deprecated", "genode"] for entry in walkDirRecFilter("lib", follow = follow): let a = entry.path if entry.kind != pcFile or a.splitFile.ext != ".nim" or @@ -240,48 +219,49 @@ proc buildDocSamples(nimArgs, destPath: string) = exec(findNim().quoteShell() & " doc $# -o:$# $#" % [nimArgs, destPath / "docgen_sample.html", "doc" / "docgen_sample.nim"]) -proc buildDocPackages(nimArgs, destPath: string, indexOnly: bool) = +proc buildDocPackages(nimArgs, destPath: string) = # compiler docs; later, other packages (perhaps tools, testament etc) let nim = findNim().quoteShell() # to avoid broken links to manual from compiler dir, but a multi-package # structure could be supported later proc docProject(outdir, options, mainproj: string) = - exec("$nim doc --project --outdir:$outdir $nimArgs --git.url:$gitUrl $index $options $mainproj" % [ + exec("$nim doc --project --outdir:$outdir $nimArgs --git.url:$gitUrl $options $mainproj" % [ "nim", nim, "outdir", outdir, "nimArgs", nimArgs, "gitUrl", gitUrl, "options", options, "mainproj", mainproj, - "index", if indexOnly: "--index:only" else: "" ]) let extra = "-u:boot" # xxx keep in sync with what's in $nim_prs_D/config/nimdoc.cfg, or, rather, # start using nims instead of nimdoc.cfg docProject(destPath/"compiler", extra, "compiler/index.nim") -proc buildDoc(nimArgs, destPath: string, indexOnly: bool) = +proc buildDoc(nimArgs, destPath: string) = # call nim for the documentation: - let rst2html = getMd2html() + let rst2html = getRst2html() var - commands = newSeq[string](rst2html.len + len(doc) + withoutIndex.len + - officialPackagesList.len + officialPackagesListWithoutIndex.len) + commands = newSeq[string](rst2html.len + len(doc0) + len(doc) + withoutIndex.len) i = 0 let nim = findNim().quoteShell() - - let index = if indexOnly: "--index:only" else: "" for d in items(rst2html): - commands[i] = nim & " md2html $# --git.url:$# -o:$# $# $#" % + commands[i] = nim & " rst2html $# --git.url:$# -o:$# --index:on $#" % [nimArgs, gitUrl, - destPath / changeFileExt(splitFile(d).name, "html"), index, d] + destPath / changeFileExt(splitFile(d).name, "html"), d] + i.inc + for d in items(doc0): + commands[i] = nim & " doc0 $# --git.url:$# -o:$# --index:on $#" % + [nimArgs, gitUrl, + destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc for d in items(doc): let extra = if isJsOnly(d): "--backend:js" else: "" var nimArgs2 = nimArgs if d.isRelativeTo("compiler"): doAssert false - commands[i] = nim & " doc $# $# --git.url:$# --outdir:$# $# $#" % - [extra, nimArgs2, gitUrl, destPath, index, d] + commands[i] = nim & " doc $# $# --git.url:$# --outdir:$# --index:on $#" % + [extra, nimArgs2, gitUrl, destPath, d] i.inc for d in items(withoutIndex): commands[i] = nim & " doc $# --git.url:$# -o:$# $#" % @@ -289,82 +269,55 @@ proc buildDoc(nimArgs, destPath: string, indexOnly: bool) = destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc - - for d in items(officialPackagesList): - var nimArgs2 = nimArgs - if d.isRelativeTo("compiler"): doAssert false - commands[i] = nim & " doc $# --outdir:$# --index:on $#" % - [nimArgs2, destPath, d] - i.inc - for d in items(officialPackagesListWithoutIndex): - commands[i] = nim & " doc $# -o:$# $#" % - [nimArgs, - destPath / changeFileExt(splitFile(d).name, "html"), d] - i.inc - mexec(commands) - -proc nim2pdf(src: string, dst: string, nimArgs: string) = - # xxx expose as a `nim` command or in some other reusable way. - let outDir = "build" / "xelatextmp" # xxx factor pending https://github.com/timotheecour/Nim/issues/616 - # note: this will generate temporary files in gitignored `outDir`: aux toc log out tex - exec("$# md2tex $# --outdir:$# $#" % [findNim().quoteShell(), nimArgs, outDir.quoteShell, src.quoteShell]) - let texFile = outDir / src.lastPathPart.changeFileExt("tex") - for i in 0..<3: # call LaTeX three times to get cross references right: - let xelatexLog = outDir / "xelatex.log" - # `>` should work on windows, if not, we can use `execCmdEx` - let cmd = "xelatex -interaction=nonstopmode -output-directory=$# $# > $#" % [outDir.quoteShell, texFile.quoteShell, xelatexLog.quoteShell] - exec(cmd) # on error, user can inspect `xelatexLog` - if i == 1: # build .ind file - var texFileBase = texFile - texFileBase.removeSuffix(".tex") - let cmd = "makeindex $# > $#" % [ - texFileBase.quoteShell, xelatexLog.quoteShell] - exec(cmd) - moveFile(texFile.changeFileExt("pdf"), dst) + exec(nim & " buildIndex -o:$1/theindex.html $1" % [destPath]) + # caveat: this works so long it's called before `buildDocPackages` which + # populates `compiler/` with unrelated idx files that shouldn't be in index, + # so should work in CI but you may need to remove your generated html files + # locally after calling `./koch docs`. The clean fix would be for `idx` files + # to be transient with `--project` (eg all in memory). proc buildPdfDoc*(nimArgs, destPath: string) = - var pdfList: seq[string] createDir(destPath) - if os.execShellCmd("xelatex -version") != 0: - doAssert false, "xelatex not found" # or, raise an exception + if os.execShellCmd("pdflatex -version") != 0: + echo "pdflatex not found; no PDF documentation generated" else: - for src in items(mdPdfList): - let dst = destPath / src.lastPathPart.changeFileExt("pdf") - pdfList.add dst - nim2pdf(src, dst, nimArgs) - echo "\nOutput PDF files: \n ", pdfList.join(" ") # because `nim2pdf` is a bit verbose + const pdflatexcmd = "pdflatex -interaction=nonstopmode " + for d in items(pdf): + exec(findNim().quoteShell() & " rst2tex $# $#" % [nimArgs, d]) + let tex = splitFile(d).name & ".tex" + removeFile("doc" / tex) + moveFile(tex, "doc" / tex) + # 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 buildJS(): string = let nim = findNim() - exec("$# js -d:release --out:$# tools/nimblepkglist.nim" % - [nim.quoteShell(), webUploadOutput / "nimblepkglist.js"]) + exec(nim.quoteShell() & " js -d:release --out:$1 tools/nimblepkglist.nim" % + [webUploadOutput / "nimblepkglist.js"]) # xxx deadcode? and why is it only for webUploadOutput, not for local docs? result = getDocHacksJs(nimr = getCurrentDir(), nim) proc buildDocsDir*(args: string, dir: string) = let args = nimArgs & " " & args let docHackJsSource = buildJS() - gitClonePackages(@["asyncftpclient", "punycode", "smtp", "db_connector"]) createDir(dir) buildDocSamples(args, dir) - - # generate `.idx` files and top-level `theindex.html`: - buildDoc(args, dir, indexOnly=true) # bottleneck - let nim = findNim().quoteShell() - exec(nim & " buildIndex -o:$1/theindex.html $1" % [dir]) - # caveat: this works so long it's called before `buildDocPackages` which - # populates `compiler/` with unrelated idx files that shouldn't be in index, - # so should work in CI but you may need to remove your generated html files - # locally after calling `./koch docs`. The clean fix would be for `idx` files - # to be transient with `--project` (eg all in memory). - buildDocPackages(args, dir, indexOnly=true) - - # generate HTML and package-level `theindex.html`: - buildDoc(args, dir, indexOnly=false) # bottleneck - buildDocPackages(args, dir, indexOnly=false) - + buildDoc(args, dir) # bottleneck copyFile(dir / "overview.html", dir / "index.html") + buildDocPackages(args, dir) copyFile(docHackJsSource, dir / docHackJsSource.lastPathPart) proc buildDocs*(args: string, localOnly = false, localOutDir = "") = @@ -379,9 +332,7 @@ proc buildDocs*(args: string, localOnly = false, localOutDir = "") = if not localOnly: buildDocsDir(args, webUploadOutput / NimVersion) - # XXX: Remove this feature check once the csources supports it. - when defined(nimHasCastPragmaBlocks): - let gaFilter = peg"@( y'--doc.googleAnalytics:' @(\s / $) )" - args = args.replace(gaFilter) + let gaFilter = peg"@( y'--doc.googleAnalytics:' @(\s / $) )" + args = args.replace(gaFilter) buildDocsDir(args, localOutDir) diff --git a/tools/debug/nim-gdb.py b/tools/nim-gdb.py similarity index 73% rename from tools/debug/nim-gdb.py rename to tools/nim-gdb.py index 1050197c9..8143b94d5 100644 --- a/tools/debug/nim-gdb.py +++ b/tools/nim-gdb.py @@ -1,7 +1,6 @@ import gdb import re import sys -import traceback # some feedback that the nim runtime support is loading, isn't a bad # thing at all. @@ -14,23 +13,14 @@ def printErrorOnce(id, message): global errorSet if id not in errorSet: errorSet.add(id) - gdb.write("printErrorOnce: " + message, gdb.STDERR) + gdb.write(message, gdb.STDERR) -def debugPrint(x): - gdb.write(str(x) + "\n", gdb.STDERR) - -NIM_STRING_TYPES = ["NimStringDesc", "NimStringV2"] ################################################################################ ##### Type pretty printers ################################################################################ -type_hash_regex = re.compile("^([A-Za-z0-9]*)_([A-Za-z0-9]*)_+([A-Za-z0-9]*)$") - -def getNimName(typ): - if m := type_hash_regex.match(typ): - return m.group(2) - return f"unknown <{typ}>" +type_hash_regex = re.compile("^\w*_([A-Za-z0-9]*)$") def getNimRti(type_name): """ Return a ``gdb.Value`` object for the Nim Runtime Information of ``type_name``. """ @@ -39,19 +29,12 @@ def getNimRti(type_name): # every non trivial Nim type. m = type_hash_regex.match(type_name) if m: - lookups = [ - "NTI" + m.group(2).lower() + "__" + m.group(3) + "_", - "NTI" + "__" + m.group(3) + "_", - "NTI" + m.group(2).replace("colon", "58").lower() + "__" + m.group(3) + "_" - ] - for l in lookups: - try: - return gdb.parse_and_eval(l) - except: - pass - None + try: + return gdb.parse_and_eval("NTI__" + m.group(1) + "_") + except: + return None -def getNameFromNimRti(rti): +def getNameFromNimRti(rti_val): """ Return name (or None) given a Nim RTI ``gdb.Value`` """ try: # sometimes there isn't a name field -- example enums @@ -77,7 +60,7 @@ class NimTypeRecognizer: 'NIM_BOOL': 'bool', - 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string', 'NimStringV2': 'string' + 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string' } # object_type_pattern = re.compile("^(\w*):ObjectType$") @@ -112,12 +95,6 @@ class NimTypeRecognizer: result = self.type_map_static.get(tname, None) if result: return result - elif tname.startswith("tyEnum_"): - return getNimName(tname) - elif tname.startswith("tyTuple__"): - # We make the name be the field types (Just like in Nim) - fields = ", ".join([self.recognize(field.type) for field in type_obj.fields()]) - return f"({fields})" rti = getNimRti(tname) if rti: @@ -151,7 +128,7 @@ class DollarPrintFunction (gdb.Function): "Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)" dollar_functions = re.findall( - '(?:NimStringDesc \*|NimStringV2)\s?(dollar__[A-z0-9_]+?)\(([^,)]*)\);', + 'NimStringDesc \*(dollar__[A-z0-9_]+?)\(([^,)]*)\);', gdb.execute("info functions dollar__", True, True) ) @@ -160,10 +137,13 @@ class DollarPrintFunction (gdb.Function): @staticmethod - def invoke_static(arg, ignore_errors = False): - if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name in NIM_STRING_TYPES: + def invoke_static(arg): + + if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name == "NimStringDesc": return arg + argTypeName = str(arg.type) + for func, arg_typ in DollarPrintFunction.dollar_functions: # this way of overload resolution cannot deal with type aliases, # therefore it won't find all overloads. @@ -175,8 +155,7 @@ class DollarPrintFunction (gdb.Function): func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value() return func_value(arg.address) - if not ignore_errors: - debugPrint(f"No suitable Nim $ operator found for type: {getNimName(argTypeName)}\n") + printErrorOnce(argTypeName, "No suitable Nim $ operator found for type: " + argTypeName + "\n") return None def invoke(self, arg): @@ -197,11 +176,11 @@ class NimStringEqFunction (gdb.Function): @staticmethod def invoke_static(arg1,arg2): - if arg1.type.code == gdb.TYPE_CODE_PTR and arg1.type.target().name in NIM_STRING_TYPES: + if arg1.type.code == gdb.TYPE_CODE_PTR and arg1.type.target().name == "NimStringDesc": str1 = NimStringPrinter(arg1).to_string() else: str1 = arg1.string() - if arg2.type.code == gdb.TYPE_CODE_PTR and arg2.type.target().name in NIM_STRING_TYPES: + if arg2.type.code == gdb.TYPE_CODE_PTR and arg2.type.target().name == "NimStringDesc": str2 = NimStringPrinter(arg1).to_string() else: str2 = arg2.string() @@ -213,7 +192,6 @@ class NimStringEqFunction (gdb.Function): NimStringEqFunction() - ################################################################################ ##### GDB Command, equivalent of Nim's $ operator ################################################################################ @@ -229,7 +207,7 @@ class DollarPrintCmd (gdb.Command): strValue = DollarPrintFunction.invoke_static(param) if strValue: gdb.write( - str(NimStringPrinter(strValue)) + "\n", + NimStringPrinter(strValue).to_string() + "\n", gdb.STDOUT ) @@ -267,6 +245,7 @@ class KochCmd (gdb.Command): os.path.dirname(os.path.dirname(__file__)), "koch") def invoke(self, argument, from_tty): + import os subprocess.run([self.binary] + gdb.string_to_argv(argument)) KochCmd() @@ -320,14 +299,8 @@ class NimBoolPrinter: ################################################################################ -def strFromLazy(strVal): - if isinstance(strVal, str): - return strVal - else: - return strVal.value().string("utf-8") - class NimStringPrinter: - pattern = re.compile(r'^(NimStringDesc \*|NimStringV2)$') + pattern = re.compile(r'^NimStringDesc \*$') def __init__(self, val): self.val = val @@ -337,18 +310,26 @@ class NimStringPrinter: def to_string(self): if self.val: - if self.val.type.name == "NimStringV2": - l = int(self.val["len"]) - data = self.val["p"]["data"] - else: - l = int(self.val['Sup']['len']) - data = self.val["data"] - return data.lazy_string(encoding="utf-8", length=l) + l = int(self.val['Sup']['len']) + return self.val['data'].lazy_string(encoding="utf-8", length=l) else: return "" - def __str__(self): - return strFromLazy(self.to_string()) +# class NimStringPrinter: +# pattern = re.compile(r'^NimStringDesc$') + +# def __init__(self, val): +# self.val = val + +# def display_hint(self): +# return 'string' + +# def to_string(self): +# if self.val: +# l = int(self.val['Sup']['len']) +# return self.val['data'].lazy_string(encoding="utf-8", length=l) +# else: +# return "" class NimRopePrinter: pattern = re.compile(r'^tyObject_RopeObj__([A-Za-z0-9]*) \*$') @@ -371,43 +352,59 @@ class NimRopePrinter: ################################################################################ -def reprEnum(e, typ): - # Casts the value to the enum type and then calls the enum printer - e = int(e) - val = gdb.Value(e).cast(typ) - return strFromLazy(NimEnumPrinter(val).to_string()) +# proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = +# ## Return string representation for enumeration values +# var n = typ.node +# if ntfEnumHole notin typ.flags: +# let o = e - n.sons[0].offset +# if o >= 0 and o <% typ.node.len: +# return $n.sons[o].name +# else: +# # ugh we need a slow linear search: +# var s = n.sons +# for i in 0 .. n.len-1: +# if s[i].offset == e: +# return $s[i].name +# result = $e & " (invalid data!)" -def enumNti(typeNimName, idString): - typeInfoName = "NTI" + typeNimName.lower() + "__" + idString + "_" - nti = gdb.lookup_global_symbol(typeInfoName) - if nti is None: - typeInfoName = "NTI" + "__" + idString + "_" - nti = gdb.lookup_global_symbol(typeInfoName) - return (typeInfoName, nti) +def reprEnum(e, typ): + """ this is a port of the nim runtime function `reprEnum` to python """ + e = int(e) + n = typ["node"] + flags = int(typ["flags"]) + # 1 << 2 is {ntfEnumHole} + if ((1 << 2) & flags) == 0: + o = e - int(n["sons"][0]["offset"]) + if o >= 0 and 0 < int(n["len"]): + return n["sons"][o]["name"].string("utf-8", "ignore") + else: + # ugh we need a slow linear search: + s = n["sons"] + for i in range(0, int(n["len"])): + if int(s[i]["offset"]) == e: + return s[i]["name"].string("utf-8", "ignore") + + return str(e) + " (invalid data!)" class NimEnumPrinter: - pattern = re.compile(r'^tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$') - enumReprProc = gdb.lookup_global_symbol("reprEnum", gdb.SYMBOL_FUNCTIONS_DOMAIN) + pattern = re.compile(r'^tyEnum_(\w*)__([A-Za-z0-9]*)$') def __init__(self, val): self.val = val typeName = self.val.type.name match = self.pattern.match(typeName) self.typeNimName = match.group(1) - typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2)) + typeInfoName = "NTI__" + match.group(2) + "_" + self.nti = gdb.lookup_global_symbol(typeInfoName) + + if self.nti is None: + printErrorOnce(typeInfoName, f"NimEnumPrinter: lookup global symbol '{typeInfoName}' failed for {typeName}.\n") def to_string(self): - if NimEnumPrinter.enumReprProc and self.nti: - # Use the old runtimes enumRepr function. - # We call the Nim proc itself so that the implementation is correct - f = gdb.newest_frame() - # We need to strip the quotes so it looks like an enum instead of a string - reprProc = NimEnumPrinter.enumReprProc.value() - return str(reprProc(self.val, self.nti.value(f).address)).strip('"') - elif dollarResult := DollarPrintFunction.invoke_static(self.val): - # New runtime doesn't use enumRepr so we instead try and call the - # dollar function for it - return str(NimStringPrinter(dollarResult)) + if self.nti: + arg0 = self.val + arg1 = self.nti.value(gdb.newest_frame()) + return reprEnum(arg0, arg1) else: return self.typeNimName + "(" + str(int(self.val)) + ")" @@ -417,27 +414,34 @@ class NimSetPrinter: ## the set printer is limited to sets that fit in an integer. Other ## sets are compiled to `NU8 *` (ptr uint8) and are invisible to ## gdb (currently). - pattern = re.compile(r'^tySet_tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$') + pattern = re.compile(r'^tySet_tyEnum_(\w*)_([A-Za-z0-9]*)$') def __init__(self, val): self.val = val - typeName = self.val.type.name - match = self.pattern.match(typeName) - self.typeNimName = match.group(1) + match = self.pattern.match(self.val.type.name) + self.typeNimName = match.group(1) + + typeInfoName = "NTI__" + match.group(2) + "_" + self.nti = gdb.lookup_global_symbol(typeInfoName) + + if self.nti is None: + printErrorOnce(typeInfoName, "NimSetPrinter: lookup global symbol '"+ typeInfoName +" failed for " + self.val.type.name + ".\n") def to_string(self): - # Remove the tySet from the type name - typ = gdb.lookup_type(self.val.type.name[6:]) - enumStrings = [] - val = int(self.val) - i = 0 - while val > 0: - if (val & 1) == 1: - enumStrings.append(reprEnum(i, typ)) - val = val >> 1 - i += 1 + if self.nti: + nti = self.nti.value(gdb.newest_frame()) + enumStrings = [] + val = int(self.val) + i = 0 + while val > 0: + if (val & 1) == 1: + enumStrings.append(reprEnum(i, nti)) + val = val >> 1 + i += 1 - return '{' + ', '.join(enumStrings) + '}' + return '{' + ', '.join(enumStrings) + '}' + else: + return str(int(self.val)) ################################################################################ @@ -469,81 +473,41 @@ class NimHashSetPrinter: ################################################################################ -class NimSeq: - # Wrapper around sequences. - # This handles the differences between old and new runtime +class NimSeqPrinter: + # the pointer is explicity part of the type. So it is part of + # ``pattern``. + pattern = re.compile(r'^tySequence_\w* \*$') def __init__(self, val): self.val = val - # new runtime has sequences on stack, old has them on heap - self.new = val.type.code != gdb.TYPE_CODE_PTR - if self.new: - # Some seqs are just the content and to save repeating ourselves we do - # handle them here. Only thing that needs to check this is the len/data getters - self.isContent = val.type.name.endswith("Content") - - def __bool__(self): - if self.new: - return self.val is not None - else: - return bool(self.val) - - def __len__(self): - if not self: - return 0 - if self.new: - if self.isContent: - return int(self.val["cap"]) - else: - return int(self.val["len"]) - else: - return self.val["Sup"]["len"] - - @property - def data(self): - if self.new: - if self.isContent: - return self.val["data"] - elif self.val["p"]: - return self.val["p"]["data"] - else: - return self.val["data"] - - @property - def cap(self): - if not self: - return 0 - if self.new: - if self.isContent: - return int(self.val["cap"]) - elif self.val["p"]: - return int(self.val["p"]["cap"]) - else: - return 0 - return int(self.val['Sup']['reserved']) - -class NimSeqPrinter: - pattern = re.compile(r'^tySequence_\w*\s?\*?$') - - def __init__(self, val): - self.val = NimSeq(val) - def display_hint(self): return 'array' def to_string(self): - return f'seq({len(self.val)}, {self.val.cap})' + len = 0 + cap = 0 + if self.val: + len = int(self.val['Sup']['len']) + cap = int(self.val['Sup']['reserved']) + + return 'seq({0}, {1})'.format(len, cap) def children(self): if self.val: val = self.val - length = len(val) + valType = val.type + length = int(val['Sup']['len']) if length <= 0: return - data = val.data + dataType = valType['data'].type + data = val['data'] + + if self.val.type.name is None: + dataType = valType['data'].type.target().pointer() + data = val['data'].cast(dataType) inaccessible = False for i in range(length): @@ -599,7 +563,7 @@ class NimStringTablePrinter: def children(self): if self.val: - data = NimSeqPrinter(self.val['data'].referenced_value()) + data = NimSeqPrinter(self.val['data'].dereference()) for idxStr, entry in data.children(): if int(entry['Field0']) != 0: yield (idxStr + ".Field0", entry['Field0']) @@ -622,7 +586,7 @@ class NimTablePrinter: if self.val: counter = int(self.val['counter']) if self.val['data']: - capacity = NimSeq(self.val["data"]).cap + capacity = int(self.val['data']['Sup']['len']) return 'Table({0}, {1})'.format(counter, capacity) @@ -634,18 +598,61 @@ class NimTablePrinter: yield (idxStr + '.Field1', entry['Field1']) yield (idxStr + '.Field2', entry['Field2']) -################################################################################ +################################################################ -class NimTuplePrinter: - pattern = re.compile(r"^tyTuple__([A-Za-z0-9]*)") +# this is untested, therefore disabled - def __init__(self, val): - self.val = val +# class NimObjectPrinter: +# pattern = re.compile(r'^tyObject_.*$') + +# def __init__(self, val): +# self.val = val + +# def display_hint(self): +# return 'object' + +# def to_string(self): +# return str(self.val.type) + +# def children(self): +# if not self.val: +# yield "object", "" +# raise StopIteration + +# for (i, field) in enumerate(self.val.type.fields()): +# if field.type.code == gdb.TYPE_CODE_UNION: +# yield _union_field +# else: +# yield (field.name, self.val[field]) + +# def _union_field(self, i, field): +# rti = getNimRti(self.val.type.name) +# if rti is None: +# return (field.name, "UNION field can't be displayed without RTI") + +# node_sons = rti['node'].dereference()['sons'] +# prev_field = self.val.type.fields()[i - 1] + +# descriminant_node = None +# for i in range(int(node['len'])): +# son = node_sons[i].dereference() +# if son['name'].string("utf-8", "ignore") == str(prev_field.name): +# descriminant_node = son +# break +# if descriminant_node is None: +# raise ValueError("Can't find union descriminant field in object RTI") + +# if descriminant_node is None: raise ValueError("Can't find union field in object RTI") +# union_node = descriminant_node['sons'][int(self.val[prev_field])].dereference() +# union_val = self.val[field] + +# for f1 in union_val.type.fields(): +# for f2 in union_val[f1].type.fields(): +# if str(f2.name) == union_node['name'].string("utf-8", "ignore"): +# return (str(f2.name), union_val[f1][f2]) + +# raise ValueError("RTI is absent or incomplete, can't find union definition in RTI") - def to_string(self): - # We don't have field names so just print out the tuple as if it was anonymous - tupleValues = [str(self.val[field.name]) for field in self.val.type.fields()] - return f"({', '.join(tupleValues)})" ################################################################################ diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion index 1c3670fd9..f7aeb72c6 100644 --- a/tools/nim.zsh-completion +++ b/tools/nim.zsh-completion @@ -1,120 +1,148 @@ #compdef nim -# Installation note: -# Please name this file as _nim (with underscore!) and copy it to a -# completions directory, either: -# - system wide one, like /usr/share/zsh/functions/Completion/Unix/ on Linux -# - or to a user directory like ~/scripts. Then you also need to add -# that directory in your ~/.zshrc to `fpath` array like so: -# fpath=( ~/scripts "${fpath[@]}" ) - _nim() { - local -a nimCommands=( - {compile,c}:'compile project with default code generator C' - {compileToC,cc}:'compile project with C code generator' - {compileToCpp,cpp}:'compile project to C++ code' - {compileToOC,objc}:'compile project to Objective C code' - 'js:compile project to Javascript' - 'e:run a Nimscript file' - 'doc:generate the HTML documentation for inputfile' - 'rst2html:convert a reStructuredText file to HTML' - 'doc2tex:generate the documentation for inputfile to LaTeX' - 'rst2tex:convert a reStructuredText file to TeX' - 'jsondoc:extract the documentation to a json file' - 'buildIndex:build an index for the whole documentation' - 'genDepend:generate a DOT file containing the module dependency graph' - 'dump:dump all defined conditionals and search paths' - 'check:checks the project for syntax and semantic' - {--help,-h}:'basic help' - '--fullhelp:show all switches' - {-v,--version}:'show version' - ) - - _arguments '*:: :->anyState' && return 0 - - if (( CURRENT == 1 )); then - _describe -t commands "Nim commands" nimCommands -V1 - return - fi - - local -a conditionalSymbols=( - release danger mingw androidNDK useNimRtl useMalloc noSignalHandler ssl - debug leanCompiler gcDestructors) - local -a sharedOpts=( - {--define\\:-,-d\\:-}'[define a conditional symbol]:x:($conditionalSymbols)' - {--undef\\:-,-u\\:-}'[undefine a conditional symbol]:x:($conditionalSymbols)' - {--path\\:-,-p\\:-}'[add path to search paths]:x:_files' - '--verbosity\:-[set verbosity level (default\: 1)]:x:(0 1 2 3)' - '--hints\:-[print compilation hints? (or `list`)]:x:(on off list)' - ) - local runOpts=( - {--run,-r}'[run the application]' - ) - local docOpts=( - '--index\:-[enable index .idx files?]:x:(on off)' - '--project\:-[output any dependency for doc?]:x:(on off)' - '--docInternal\:-[generate module-private documentation?]:x:(on off)' - ) - local -a codeOpts=( - {--forceBuild,-f}'[force rebuilding of all modules]' - '--stackTrace\:-[enable stack tracing?]:x:(on off)' - '--lineTrace\:-[enable line tracing?]:x:(on off)' - '--threads\:-[enable support for multi-threading?]:x:(on off)' - {--checks\\:-,-x\\:-}'[enable/disable all runtime checks?]:x:(on off)' - '--objChecks\:-[enable obj conversion checks]:x:(on off)' - '--fieldChecks\:-[enable case variant field checks?]:x:(on off)' - '--rangeChecks\:-[enable range checks?]:x:(on off)' - '--boundChecks\:-[enable bound checks?]:x:(on off)' - '--overflowChecks\:-[enable integer over-/underflow checks?]:x:(on off)' - {--assertions\\:-,-a\\:-}'[enable assertions?]:x:(on off)' - '--floatChecks\:-[enable floating point (NaN/Inf) checks?]:x:(on off)' - '--nanChecks\:-[enable NaN checks?]:x:(on off)' - '--infChecks\:-[enable Inf checks?]:x:(on off)' - '--nilChecks\:-[enable nil checks?]:x:(on off)' - '--expandArc\:-[show how given proc looks before final backend pass]' - '--expandMacro\:-[dump every generated AST from given macro]' - ) - local -a nativeOpts=( - '--opt\:-[optimization mode]:x:(none speed size)' - '--debugger\:native[use native debugger (gdb)]' - '--app\:-[generate this type of app (lib=dynamic)]:x:(console gui lib staticlib)' - '--cpu\:-[target architecture]:x:(alpha amd64 arm arm64 avr e2k esp hppa i386 ia64 js loongarch64 m68k mips mipsel mips64 mips64el msp430 nimvm powerpc powerpc64 powerpc64el riscv32 riscv64 sparc sparc64 vm wasm32)' - '--gc\:-[memory management algorithm to use (default\: refc)]:x:(refc arc orc markAndSweep boehm go regions none)' - '--os\:-[operating system to compile for]:x:(AIX Amiga Android Any Atari DOS DragonFly FreeBSD FreeRTOS Genode Haiku iOS Irix JS Linux MacOS MacOSX MorphOS NetBSD Netware NimVM NintendoSwitch OS2 OpenBSD PalmOS Standalone QNX SkyOS Solaris VxWorks Windows)' - '--panics\:-[turn panics into process termination (default\: off)]:x:(off on)' - ) - - case "$words[1]" in - compile|c|compileToC|cpp|compileToCpp|compileToOC|objc) - _arguments $codeOpts $runOpts $sharedOpts $nativeOpts \ - '*:filename:_files -g"*.nim"' - ;; - js) - _arguments $codeOpts $runOpts $sharedOpts \ - '*:filename:_files -g"*.nim"' - ;; - e) - _arguments $codeOpts $runOpts $sharedOpts \ - '*:filename:_files -g"*.nims"' - ;; - doc|doc2tex|jsondoc) - _arguments $runOpts $sharedOpts '*:filename:_files -g"*.nim"' - ;; - rst2html|rst2tex) - _arguments $runOpts $sharedOpts $docOpts '*:filename:_files -g"*.rst"' - ;; - buildIndex|genDepend|check) - _arguments $sharedOpts '*:filename:_files -g"*.nim"' - ;; - dump) - _arguments $sharedOpts - ;; - *) - _arguments '*:filename:_files -g"*"' - ;; - esac - - return 1 + _arguments -C \ + ':command:(( + {compile,c}\:compile\ project\ with\ default\ code\ generator\ C + doc\:generate\ the\ documentation\ for\ inputfile + {compileToC,cc}\:compile\ project\ with\ C\ code\ generator + {compileToCpp,cpp}\:compile\ project\ to\ C++\ code + {compileToOC,objc}\:compile\ project\ to\ Objective\ C\ code + js\:compile\ project\ to\ Javascript + e\:run\ a\ Nimscript\ file + rst2html\:convert\ a\ reStructuredText\ file\ to\ HTML + rst2tex\:convert\ a\ reStructuredText\ file\ to\ TeX + jsondoc\:extract\ the\ documentation\ to\ a\ json\ file + buildIndex\:build\ an\ index\ for\ the\ whole\ documentation + genDepend\:generate\ a\ DOT\ file\ containing\ the\ module\ dependency\ graph + dump\:dump\ all\ defined\ conditionals\ and\ search\ paths + check\:checks\ the\ project\ for\ syntax\ and\ semantic + ))' \ + '*-r[run the application]' \ + '*--run[run the application]' \ + '*-p=[add path to search paths]' \ + '*--path=[add path to search paths]' \ + '*-d=[define a conditional symbol]' \ + '*--define=[define a conditional symbol]' \ + '*-u=[undefine a conditional symbol]' \ + '*--undef=[undefine a conditional symbol]' \ + '*-f[force rebuilding of all modules]' \ + '*--forceBuild[force rebuilding of all modules]' \ + '*--stackTrace=on[turn stack tracing on]' \ + '*--stackTrace=off[turn stack tracing off]' \ + '*--lineTrace=on[turn line tracing on]' \ + '*--lineTrace=off[turn line tracing off]' \ + '*--threads=on[turn support for multi-threading on]' \ + '*--threads=off[turn support for multi-threading off]' \ + '*-x=on[turn all runtime checks on]' \ + '*-x=off[turn all runtime checks off]' \ + '*--checks=on[turn all runtime checks on]' \ + '*--checks=off[turn all runtime checks off]' \ + '*--objChecks=on[turn obj conversion checks on]' \ + '*--objChecks=off[turn obj conversion checks off]' \ + '*--fieldChecks=on[turn case variant field checks on]' \ + '*--fieldChecks=off[turn case variant field checks off]' \ + '*--rangeChecks=on[turn range checks on]' \ + '*--rangeChecks=off[turn range checks off]' \ + '*--boundChecks=on[turn bound checks on]' \ + '*--boundChecks=off[turn bound checks off]' \ + '*--overflowChecks=on[turn int over-/underflow checks on]' \ + '*--overflowChecks=off[turn int over-/underflow checks off]' \ + '*-a[turn assertions on]' \ + '*-a[turn assertions off]' \ + '*--assertions=on[turn assertions on]' \ + '*--assertions=off[turn assertions off]' \ + '*--floatChecks=on[turn all floating point (NaN/Inf) checks on]' \ + '*--floatChecks=off[turn all floating point (NaN/Inf) checks off]' \ + '*--nanChecks=on[turn NaN checks on]' \ + '*--nanChecks=off[turn NaN checks off]' \ + '*--infChecks=on[turn Inf checks on]' \ + '*--infChecks=off[turn Inf checks off]' \ + '*--nilChecks=on[turn nil checks on]' \ + '*--nilChecks=off[turn nil checks off]' \ + '*--opt=none[do not optimize]' \ + '*--opt=speed[optimize for speed|size - use -d:release for a release build]' \ + '*--opt=size[optimize for size]' \ + '*--debugger:native[use native debugger (gdb)]' \ + '*--app=console[generate a console app]' \ + '*--app=gui[generate a GUI app]' \ + '*--app=lib[generate a dynamic library]' \ + '*--app=staticlib[generate a static library]' \ + '*--cpu=alpha[compile for Alpha architecture]' \ + '*--cpu=amd64[compile for x86_64 architecture]' \ + '*--cpu=arm[compile for ARM architecture]' \ + '*--cpu=arm64[compile for ARM64 architecture]' \ + '*--cpu=avr[compile for AVR architecture]' \ + '*--cpu=esp[compile for ESP architecture]' \ + '*--cpu=hppa[compile for HPPA architecture]' \ + '*--cpu=i386[compile for i386 architecture]' \ + '*--cpu=ia64[compile for ia64 architecture]' \ + '*--cpu=js[compile to JavaScript]' \ + '*--cpu=m68k[compile for m68k architecture]' \ + '*--cpu=mips[compile for MIPS architecture]' \ + '*--cpu=mipsel[compile for MIPS EL architecture]' \ + '*--cpu=mips64[compile for MIPS64 architecture]' \ + '*--cpu=mips64el[compile for MIPS64 EL architecture]' \ + '*--cpu=msp430[compile for msp430 architecture]' \ + '*--cpu=nimvm[compile for Nim VM]' \ + '*--cpu=powerpc[compile for PowerPC architecture]' \ + '*--cpu=powerpc64[compile for PowerPC64 architecture]' \ + '*--cpu=powerpc64el[compile for PowerPC64 EL architecture]' \ + '*--cpu=riscv32[compile for RISC-V 32 architecture]' \ + '*--cpu=riscv64[compile for RISC-V 64 architecture]' \ + '*--cpu=sparc[compile for SPARC architecture]' \ + '*--cpu=sparc64[compile for SPARC64 architecture]' \ + '*--cpu=vm[compile for Nim VM]' \ + '*--cpu=wasm32[compile to WASM 32]' \ + '*--gc=refc[use reference counting garbage collection]' \ + '*--gc=arc[use ARC garbage collection]' \ + '*--gc=orc[use ORC garbage collection]' \ + '*--gc=markAndSweep[use mark-and-sweep garbage collection]' \ + '*--gc=boehm[use Boehm garbage collection]' \ + '*--gc=go[use Go garbage collection]' \ + '*--gc=regions[use region-based memory management]' \ + '*--gc=none[disable garbage collection]' \ + '*--os=Standalone[generate a stand-alone executable]' \ + '*--os=AIX[compile for AIX]' \ + '*--os=Amiga[compile for Amiga OS]' \ + '*--os=Android[compile for Android]' \ + '*--os=Any[compile for any OS]' \ + '*--os=Atari[compile for Atari]' \ + '*--os=DOS[compile for DOS]' \ + '*--os=DragonFly[compile for DragonFly]' \ + '*--os=FreeBSD[compile for FreeBSD]' \ + '*--os=FreeRTOS[compile for FreeRTOS]' \ + '*--os=Genode[compile for Genode]' \ + '*--os=Haiku[compile for Haiku]' \ + '*--os=iOS[compile for iOS]' \ + '*--os=Irix[compile for Irix]' \ + '*--os=Linux[compile for Linux]' \ + '*--os=MacOS[compile for MacOS]' \ + '*--os=MacOSX[compile for MacOSX]' \ + '*--os=MorphOS[compile for MorphOS]' \ + '*--os=NetBSD[compile for NetBSD]' \ + '*--os=Netware[compile for Netware]' \ + '*--os=NimVM[compile for NimVM]' \ + '*--os=NintendoSwitch[compile for NintendoSwitch]' \ + '*--os=OS2[compile for OS2]' \ + '*--os=OpenBSD[compile for OpenBSD]' \ + '*--os=PalmOS[compile for PalmOS]' \ + '*--os=QNX[compile for QNX]' \ + '*--os=SkyOS[compile for SkyOS]' \ + '*--os=Solaris[compile for Solaris]' \ + '*--os=VxWorks[compile for VxWorks]' \ + '*--os=Windows[compile for Windows]' \ + '*--os=JS[generate javascript]' \ + '*--panics=off[turn panics into process terminations: off by default]' \ + '*--panics=on[turn panics into process terminations]' \ + '*--verbosity=0[set verbosity to 0]' \ + '*--verbosity=1[set verbosity to 1 (default)]' \ + '*--verbosity=2[set verbosity to 2]' \ + '*--verbosity=3[set verbosity to 3]' \ + '*--hints=on[print compilation hints]' \ + '*--hints=off[disable compilation hints]' \ + '*--hints=list[print compilation hints list]' \ + ':filename:_files -g"*.nim"' } _nim "$@" diff --git a/tools/nimblepkglist.nim b/tools/nimblepkglist.nim index 92e1cad20..c4bec4485 100644 --- a/tools/nimblepkglist.nim +++ b/tools/nimblepkglist.nim @@ -56,16 +56,16 @@ proc processContent(content: string) = var officialPkgListDiv = document.getElementById("officialPkgList") officialPkgListDiv.innerHTML = - (p("There are currently " & $officialCount & + p("There are currently " & $officialCount & " official packages in the Nimble package repository.") & - ul(officialList)).cstring + ul(officialList) var unofficialPkgListDiv = document.getElementById("unofficialPkgList") unofficialPkgListDiv.innerHTML = - (p("There are currently " & $unofficialCount & + p("There are currently " & $unofficialCount & " unofficial packages in the Nimble package repository.") & - ul(unofficialList)).cstring + ul(unofficialList) proc gotPackageList(apiReply: TData) {.exportc.} = let decoded = decodeContent($apiReply.content) @@ -76,5 +76,5 @@ proc gotPackageList(apiReply: TData) {.exportc.} = var unofficialPkgListDiv = document.getElementById("unofficialPkgList") let msg = p("Unable to retrieve package list: ", code(getCurrentExceptionMsg())) - officialPkgListDiv.innerHTML = msg.cstring - unofficialPkgListDiv.innerHTML = msg.cstring + officialPkgListDiv.innerHTML = msg + unofficialPkgListDiv.innerHTML = msg diff --git a/tools/nimfind.nim b/tools/nimfind.nim new file mode 100644 index 000000000..9c8247606 --- /dev/null +++ b/tools/nimfind.nim @@ -0,0 +1,234 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Nimfind is a tool that helps to give editors IDE like capabilities. + +when not defined(nimcore): + {.error: "nimcore MUST be defined for Nim's core tooling".} +when not defined(nimfind): + {.error: "nimfind MUST be defined for Nim's nimfind tool".} + +const Usage = """ +Nimfind - Tool to find declarations or usages for Nim symbols +Usage: + nimfind [options] file.nim:line:col + +Options: + --help, -h show this help + --rebuild rebuild the index + --project:file.nim use file.nim as the entry point + +In addition, all command line options of Nim that do not affect code generation +are supported. +""" + +import strutils, os, parseopt + +import "../compiler" / [options, commands, modules, sem, + passes, passaux, msgs, ast, + idents, modulegraphs, lineinfos, cmdlinehelper, + pathutils] + +import db_sqlite + +proc createDb(db: DbConn) = + db.exec(sql""" + create table if not exists filenames( + id integer primary key, + fullpath varchar(8000) not null + ); + """) + db.exec sql"create index if not exists FilenameIx on filenames(fullpath);" + + # every sym can have potentially 2 different definitions due to forward + # declarations. + db.exec(sql""" + create table if not exists syms( + id integer primary key, + nimid integer not null, + name varchar(256) not null, + defline integer not null, + defcol integer not null, + deffile integer not null, + deflineB integer not null default 0, + defcolB integer not null default 0, + deffileB integer not null default 0, + foreign key (deffile) references filenames(id), + foreign key (deffileB) references filenames(id) + ); + """) + + db.exec(sql""" + create table if not exists usages( + id integer primary key, + nimid integer not null, + line integer not null, + col integer not null, + colB integer not null, + file integer not null, + foreign key (file) references filenames(id), + foreign key (nimid) references syms(nimid) + ); + """) + db.exec sql"create index if not exists UsagesIx on usages(file, line);" + +proc toDbFileId*(db: DbConn; conf: ConfigRef; fileIdx: FileIndex): int = + if fileIdx == FileIndex(-1): return -1 + let fullpath = toFullPath(conf, fileIdx) + let row = db.getRow(sql"select id from filenames where fullpath = ?", fullpath) + let id = row[0] + if id.len == 0: + result = int db.insertID(sql"insert into filenames(fullpath) values (?)", + fullpath) + else: + result = parseInt(id) + +type + FinderRef = ref object of RootObj + db: DbConn + +proc writeDef(graph: ModuleGraph; s: PSym; info: TLineInfo) = + let f = FinderRef(graph.backend) + f.db.exec(sql"""insert into syms(nimid, name, defline, defcol, deffile) values (?, ?, ?, ?, ?)""", + s.id, s.name.s, info.line, info.col, + toDbFileId(f.db, graph.config, info.fileIndex)) + +proc writeDefResolveForward(graph: ModuleGraph; s: PSym; info: TLineInfo) = + let f = FinderRef(graph.backend) + f.db.exec(sql"""update syms set deflineB = ?, defcolB = ?, deffileB = ? + where nimid = ?""", info.line, info.col, + toDbFileId(f.db, graph.config, info.fileIndex), s.id) + +proc writeUsage(graph: ModuleGraph; s: PSym; info: TLineInfo) = + let f = FinderRef(graph.backend) + f.db.exec(sql"""insert into usages(nimid, line, col, colB, file) values (?, ?, ?, ?, ?)""", + s.id, info.line, info.col, info.col + s.name.s.len - 1, + toDbFileId(f.db, graph.config, info.fileIndex)) + +proc performSearch(conf: ConfigRef; dbfile: AbsoluteFile) = + var db = open(connection=string dbfile, user="nim", password="", + database="nim") + let pos = conf.m.trackPos + let fid = toDbFileId(db, conf, pos.fileIndex) + let known = toFullPath(conf, pos.fileIndex) + let nimids = db.getRow(sql"""select distinct nimid from usages where line = ? and file = ? and ? between col and colB""", + pos.line, fid, pos.col) + if nimids.len > 0: + var idSet = "" + for id in nimids: + if idSet.len > 0: idSet.add ", " + idSet.add id + var outputLater = "" + for r in db.rows(sql"""select line, col, filenames.fullpath from usages + inner join filenames on filenames.id = file + where nimid in (?)""", idSet): + let line = parseInt(r[0]) + let col = parseInt(r[1]) + let file = r[2] + if file == known and line == pos.line.int: + # output the line we already know last: + outputLater.add file & ":" & $line & ":" & $(col+1) & "\n" + else: + echo file, ":", line, ":", col+1 + if outputLater.len > 0: stdout.write outputLater + close(db) + +proc setupDb(g: ModuleGraph; dbfile: AbsoluteFile) = + var f = FinderRef() + removeFile(dbfile) + f.db = open(connection=string dbfile, user="nim", password="", + database="nim") + createDb(f.db) + f.db.exec(sql"pragma journal_mode=off") + # This MUST be turned off, otherwise it's way too slow even for testing purposes: + f.db.exec(sql"pragma SYNCHRONOUS=off") + f.db.exec(sql"pragma LOCKING_MODE=exclusive") + g.backend = f + +proc mainCommand(graph: ModuleGraph) = + let conf = graph.config + let dbfile = getNimcacheDir(conf) / RelativeFile"nimfind.db" + if not fileExists(dbfile) or optForceFullMake in conf.globalOptions: + clearPasses(graph) + registerPass graph, verbosePass + registerPass graph, semPass + conf.setCmd cmdIdeTools + wantMainModule(conf) + setupDb(graph, dbfile) + + graph.onDefinition = writeUsage # writeDef + graph.onDefinitionResolveForward = writeUsage # writeDefResolveForward + graph.onUsage = writeUsage + + if not fileExists(conf.projectFull): + quit "cannot find file: " & conf.projectFull.string + add(conf.searchPaths, conf.libpath) + conf.setErrorMaxHighMaybe + try: + compileProject(graph) + finally: + close(FinderRef(graph.backend).db) + performSearch(conf, dbfile) + +proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = + var p = parseopt.initOptParser(cmd) + while true: + parseopt.next(p) + case p.kind + of cmdEnd: break + of cmdLongOption, cmdShortOption: + case p.key.normalize + of "help", "h": + stdout.writeLine(Usage) + quit() + of "project": + conf.projectName = p.val + of "rebuild": + incl conf.globalOptions, optForceFullMake + else: processSwitch(pass, p, conf) + of cmdArgument: + let info = p.key.split(':') + if info.len == 3: + conf.projectName = findProjectNimFile(conf, info[0].splitFile.dir) + if conf.projectName.len == 0: conf.projectName = info[0] + try: + conf.m.trackPos = newLineInfo(conf, AbsoluteFile info[0], + parseInt(info[1]), parseInt(info[2])-1) + except ValueError: + quit "invalid command line" + else: + quit "invalid command line" + +proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = + let self = NimProg( + suggestMode: true, + processCmdLine: processCmdLine + ) + self.initDefinesProg(conf, "nimfind") + + if paramCount() == 0: + stdout.writeLine(Usage) + return + + self.processCmdLineAndProjectPath(conf) + + # Find Nim's prefix dir. + let binaryPath = findExe("nim") + if binaryPath == "": + raise newException(IOError, + "Cannot find Nim standard library: Nim compiler not in PATH") + conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir() + if not dirExists(conf.prefixDir / RelativeDir"lib"): + conf.prefixDir = AbsoluteDir"" + + var graph = newModuleGraph(cache, conf) + if self.loadConfigsAndProcessCmdLine(cache, conf, graph): + mainCommand(graph) + +handleCmdLine(newIdentCache(), newConfigRef()) diff --git a/tools/nimfind.nims b/tools/nimfind.nims new file mode 100644 index 000000000..3013c360d --- /dev/null +++ b/tools/nimfind.nims @@ -0,0 +1,2 @@ +--define:nimfind +--define:nimcore diff --git a/tools/nimgrab.nim b/tools/nimgrab.nim index 7e4161faf..ee5eced1e 100644 --- a/tools/nimgrab.nim +++ b/tools/nimgrab.nim @@ -1,14 +1,33 @@ -import std/[os, httpclient] -proc syncDownload(url, file: string) = - var client = newHttpClient() - proc onProgressChanged(total, progress, speed: BiggestInt) = - echo "Downloading " & url & " " & $(speed div 1000) & "kb/s" - echo clamp(int(progress*100 div total), 0, 100), "%" - client.onProgressChanged = onProgressChanged - client.downloadFile(url, file) - echo "100%" +when defined(windows): + import os, urldownloader + + proc syncDownload(url, file: string) = + proc progress(status: DownloadStatus, progress: uint, total: uint, + message: string) {.gcsafe.} = + echo "Downloading " & url + let t = total.BiggestInt + if t != 0: + echo clamp(int(progress.BiggestInt*100 div t), 0, 100), "%" + else: + echo "0%" + + downloadToFile(url, file, {optUseCache}, progress) + echo "100%" + +else: + import os, asyncdispatch, httpclient + + proc syncDownload(url, file: string) = + var client = newHttpClient() + proc onProgressChanged(total, progress, speed: BiggestInt) = + echo "Downloading " & url & " " & $(speed div 1000) & "kb/s" + echo clamp(int(progress*100 div total), 0, 100), "%" + + client.onProgressChanged = onProgressChanged + client.downloadFile(url, file) + echo "100%" if os.paramCount() != 2: quit "Usage: nimgrab " diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index 599c616ba..cb46f30b8 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -11,12 +11,111 @@ import os, strutils, parseopt, pegs, re, terminal, osproc, tables, algorithm, times const - Version = "2.0.0" + Version = "1.6.0" Usage = "nimgrep - Nim Grep Searching and Replacement Utility Version " & Version & """ (c) 2012-2020 Andreas Rumpf -""" & slurp "../doc/nimgrep_cmdline.txt" + +Usage: +* To search: + nimgrep [options] PATTERN [(FILE/DIRECTORY)*/-] +* To replace: + nimgrep [options] PATTERN --replace REPLACEMENT (FILE/DIRECTORY)*/- +* To list file names: + nimgrep [options] --filenames [PATTERN] [(FILE/DIRECTORY)*] + +Positional arguments, from left to right: +* PATERN is either Regex (default) or Peg if --peg is specified. + PATTERN and REPLACEMENT should be skipped when --stdin is specified. +* REPLACEMENT supports $1, $# notations for captured groups in PATTERN. + Note: --replace mode DOES NOT ask confirmation unless --confirm is specified! +* Final arguments are a list of paths (FILE/DIRECTORY) or a standalone + minus '-' (pipe) or not specified (empty). Note for the empty case: when + no FILE/DIRECTORY/- is specified nimgrep DOES NOT read the pipe, but + searches files in the current dir instead! + - read buffer once from stdin: pipe or terminal input; + in --replace mode the result is directed to stdout; + it's not compatible with --stdin, --filenames, --confirm + (empty) current directory '.' is assumed (not with --replace) + For any given DIRECTORY nimgrep searches only its immediate files without + traversing sub-directories unless --recursive is specified. + In replacement mode all 3 positional arguments are required to avoid damaging. + +Options: +* Mode of operation: + --find, -f find the PATTERN (default) + --replace, -! replace the PATTERN to REPLACEMENT, rewriting the files + --confirm confirm each occurrence/replacement; there is a chance + to abort any time without touching the file + --filenames just list filenames. Provide a PATTERN to find it in + the filenames (not in the contents of a file) or run + with empty pattern to just list all files: + nimgrep --filenames # In current directory + nimgrep --filenames "" DIRECTORY # Note empty pattern "" + +* 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 + --ignoreStyle, -y be style insensitive in PATTERN and PAT + NOTE: PATERN and patterns PAT (see below in other options) are all either + Regex or Peg simultaneously and options --rex, --word, --ignoreCase, + --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 whole directory path containing PAT + --excludeDir:PAT skip directories whose name (not path) contain pattern PAT + --if,--ef,--id,--ed abbreviations of 4 options above + --sortTime order files by the last modification time (default: off): + -s[:asc|desc] 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 + --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 + +* Represent results: + --nocolor output will be given without any colors + --color[:on] force color even if output is redirected (default: auto) + --colorTheme:THEME select color THEME from 'simple' (default), + 'bnw' (black and white) ,'ack', or 'gnu' (GNU grep) + --count only print counts of matches for files that matched + --context:N, -c:N print N lines of leading context before every match and + N lines of trailing context after it (default N: 0) + --afterContext:N, + -a:N print N lines of trailing context after every match + --beforeContext:N, + -b:N print N lines of leading context before every match + --group, -g group matches by file + --newLine, -l display every matching line starting from a new line + --cols[:N] limit max displayed columns/width of output lines from + files by N characters, cropping overflows (default: off) + --cols:auto, -% calculate columns from terminal width for every line + --onlyAscii, -@ display only printable ASCII Latin characters 0x20-0x7E + substitutions: 0 -> ^@, 1 -> ^A, ... 0x1F -> ^_, + 0x7F -> '7F, ..., 0xFF -> 'FF +* Miscellaneous: + --threads:N, -j:N speed up search by N additional workers (default: 0, off) + --stdin read PATTERN from stdin (to avoid the shell's confusing + quoting rules) and, if --replace given, REPLACEMENT + --verbose be verbose: list every processed file + --help, -h shows this help + --version, -v shows the version +""" # Limitations / ideas / TODO: # * No unicode support with --cols @@ -95,34 +194,26 @@ type filename: string, fileResult: FileResult] WalkOpt = tuple # used for walking directories/producing paths extensions: seq[string] - notExtensions: seq[string] - filename: seq[string] - notFilename: seq[string] - dirPath: seq[string] - notDirPath: seq[string] - dirname : seq[string] - notDirname : seq[string] + skipExtensions: seq[string] + excludeFile: seq[string] + includeFile: seq[string] + includeDir : seq[string] + excludeDir : seq[string] WalkOptComp[Pat] = tuple # a compiled version of the previous - filename: seq[Pat] - notFilename: seq[Pat] - dirname : seq[Pat] - notDirname : seq[Pat] - dirPath: seq[Pat] - notDirPath: seq[Pat] + excludeFile: seq[Pat] + includeFile: seq[Pat] + includeDir : seq[Pat] + excludeDir : seq[Pat] SearchOpt = tuple # used for searching inside a file - 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 + patternSet: bool # to distinguish uninitialized 'pattern' and empty one + pattern: string # main PATTERN + checkMatch: string # --match + checkNoMatch: string # --nomatch + checkBin: Bin # --bin SearchOptComp[Pat] = tuple # a compiled version of the previous pattern: Pat - inFile: seq[Pat] - notInFile: seq[Pat] - inContext: seq[Pat] - notInContext: seq[Pat] + checkMatch: Pat + checkNoMatch: Pat SinglePattern[PAT] = tuple # compile single pattern for replacef pattern: PAT Column = tuple # current column info for the cropping (--limit) feature @@ -657,7 +748,7 @@ template updateCounters(output: Output) = proc printInfo(filename:string, output: Output) = case output.kind of openError: - printError("cannot open path '" & filename & "': " & output.msg) + printError("can not open path " & filename & " " & output.msg) of rejected: if optVerbose in options: echo "(rejected: ", output.reason, ")" @@ -727,9 +818,6 @@ iterator searchFile(pattern: Pattern; buffer: string): Output = pre: pre, match: move(curMi)) i = t.last+1 - when typeof(pattern) is Regex: - if buffer.len > MaxReBufSize: - yield Output(kind: openError, msg: "PCRE size limit is " & $MaxReBufSize) func detectBin(buffer: string): bool = for i in 0 ..< min(1024, buffer.len): @@ -815,33 +903,6 @@ 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 @@ -871,13 +932,13 @@ iterator processFile(searchOptC: SearchOptComp[Pattern], filename: string, reason = "text file" if not reject: - ensureIncluded searchOptC.inFile, buffer: - reject = true + if searchOpt.checkMatch != "": + reject = not contains(buffer, searchOptC.checkMatch, 0) reason = "doesn't contain a requested match" if not reject: - ensureExcluded searchOptC.notInFile, buffer: - reject = true + if searchOpt.checkNoMatch != "": + reject = contains(buffer, searchOptC.checkNoMatch, 0) reason = "contains a forbidden match" if reject: @@ -887,50 +948,20 @@ iterator processFile(searchOptC: SearchOptComp[Pattern], filename: string, else: var found = false var cnt = 0 - 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`. + for output in searchFile(searchOptC.pattern, buffer): + found = true + if optCount notin options: + yield output + else: + if output.kind in {blockFirstMatch, blockNextMatch}: + inc(cnt) 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 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 + +proc hasRightFileName(path: string, walkOptC: WalkOptComp[Pattern]): bool = let filename = path.lastPathPart let ex = filename.splitFile.ext.substr(1) # skip leading '.' if walkOpt.extensions.len != 0: @@ -940,44 +971,31 @@ proc hasRightPath(path: string, walkOptC: WalkOptComp[Pattern]): bool = matched = true break if not matched: return false - for x in walkOpt.notExtensions: + for x in walkOpt.skipExtensions: if os.cmpPaths(x, ex) == 0: return false - 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: + if walkOptC.includeFile.len != 0: + var matched = false + for pat in walkOptC.includeFile: + if filename.contains(pat): + matched = true break - (nextParent, dirname) = splitPath(nextParent) - if badDirname: # badDirname was set to true for all the dirs - return false + 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 + break + if not matched: return false result = true -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 +proc hasRightDirectory(path: string, walkOptC: WalkOptComp[Pattern]): bool = + let dirname = path.lastPathPart + for pat in walkOptC.excludeDir: + if dirname.contains(pat): return false result = true iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string @@ -986,24 +1004,22 @@ 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, skipSpecial = true): + for kind, path in walkDir(d): case kind of pcFile: - if path.hasRightPath(walkOptC) and rightDirForFiles: + if path.hasRightFileName(walkOptC): files.add(path) of pcLinkToFile: - if optFollow in options and path.hasRightPath(walkOptC) and - rightDirForFiles: + if optFollow in options and path.hasRightFileName(walkOptC): files.add(path) of pcDir: - if optRecursive in options and path.descendToDirectory(walkOptC): + if optRecursive in options and path.hasRightDirectory(walkOptC): dirs.add path of pcLinkToDir: if optFollow in options and optRecursive in options and - path.descendToDirectory(walkOptC): + path.hasRightDirectory(walkOptC): dirs.add path if sortTime: # sort by time - collect files before yielding for file in files: @@ -1028,12 +1044,10 @@ iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string iterator walkRec(paths: seq[string]): tuple[error: string, filename: string] {.closure.} = declareCompiledPatterns(walkOptC, WalkOptComp): - 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() + walkOptC.excludeFile.add walkOpt.excludeFile.compileArray() + walkOptC.includeFile.add walkOpt.includeFile.compileArray() + walkOptC.includeDir.add walkOpt.includeDir.compileArray() + walkOptC.excludeDir.add walkOpt.excludeDir.compileArray() for path in paths: if dirExists(path): for p in walkDirBasic(path, walkOptC): @@ -1112,10 +1126,8 @@ template processFileResult(pattern: Pattern; filename: string, proc run1Thread() = declareCompiledPatterns(searchOptC, SearchOptComp): compile1Pattern(searchOpt.pattern, searchOptC.pattern) - searchOptC.inFile.add searchOpt.inFile.compileArray() - searchOptC.notInFile.add searchOpt.notInFile.compileArray() - searchOptC.inContext.add searchOpt.inContext.compileArray() - searchOptC.notInContext.add searchOpt.notInContext.compileArray() + compile1Pattern(searchOpt.checkMatch, searchOptC.checkMatch) + compile1Pattern(searchOpt.checkNoMatch, searchOptC.checkNoMatch) if optPipe in options: processFileResult(searchOptC.pattern, "-", processFile(searchOptC, "-", @@ -1157,10 +1169,8 @@ proc worker(initSearchOpt: SearchOpt) {.thread.} = searchOpt = initSearchOpt # init thread-local var declareCompiledPatterns(searchOptC, SearchOptComp): compile1Pattern(searchOpt.pattern, searchOptC.pattern) - searchOptC.inFile.add searchOpt.inFile.compileArray() - searchOptC.notInFile.add searchOpt.notInFile.compileArray() - searchOptC.inContext.add searchOpt.inContext.compileArray() - searchOptC.notInContext.add searchOpt.notInContext.compileArray() + compile1Pattern(searchOpt.checkMatch, searchOptC.checkMatch) + compile1Pattern(searchOpt.checkNoMatch, searchOptC.checkNoMatch) while true: let (fileNo, filename) = searchRequestsChan.recv() var fileResult: FileResult @@ -1258,11 +1268,6 @@ for kind, key, val in getopt(): else: paths.add(key) of cmdLongOption, cmdShortOption: - proc addNotEmpty(s: var seq[string], name: string) = - if name == "": - reportError("empty string given for option --" & key & - " (did you forget `:`?)") - s.add name case normalize(key) of "find", "f": incl(options, optFind) of "replace", "!": incl(options, optReplace) @@ -1288,36 +1293,15 @@ for kind, key, val in getopt(): nWorkers = countProcessors() else: nWorkers = parseNonNegative(val, key) - 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.addNotEmpty val - of "ndirname", "notdirname", "ndi", "notdi", - "excludedir", "exclude-dir", "ed": # 3 deprecated options - walkOpt.notDirname.addNotEmpty val - of "dirpath", "dirp", - "includedir", "include-dir", "id": # 3 deprecated options - walkOpt.dirPath.addNotEmpty val - of "ndirpath", "notdirpath", "ndirp", "notdirp": - walkOpt.notDirPath.addNotEmpty val - of "filename", "fi", - "includefile", "include-file", "if": # 3 deprecated options - walkOpt.filename.addNotEmpty val - of "nfilename", "nfi", "notfilename", "notfi", - "excludefile", "exclude-file", "ef": # 3 deprecated options - walkOpt.notFilename.addNotEmpty val - of "infile", "inf", - "matchfile", "match", "mf": # 3 deprecated options - searchOpt.inFile.addNotEmpty val - of "ninfile", "notinfile", "ninf", "notinf", - "nomatchfile", "nomatch", "nf": # 3 options are deprecated - searchOpt.notInFile.addNotEmpty val - of "incontext", "inc": - searchOpt.inContext.addNotEmpty val - of "nincontext", "notincontext", "ninc", "notinc": - searchOpt.notInContext.addNotEmpty val + 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 "bin": case val of "on": searchOpt.checkBin = biOn diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 6b99c49ee..e13221781 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -1,6 +1,6 @@ #? stdtmpl(subsChar='?') | standard #proc generateBuildShellScript(c: ConfigData): string = -# result = "#!/bin/sh\n# Generated from niminst\n" & +# result = "#! /bin/sh\n# Generated from niminst\n" & # "# Template is in tools/niminst/buildsh.nimf\n" & # "# To regenerate run ``niminst csource`` or ``koch csource``\n" @@ -21,23 +21,10 @@ do optosname=$2 shift 2 ;; - --parallel) - parallel=$2 - shift 2 - ;; --extraBuildArgs) extraBuildArgs=" $2" shift 2 ;; - -h | --help) - echo "Options:" - echo " --os " - echo " --cpu " - echo " --osname Additional OS specification (used for Android)" - echo " --extraBuildArgs Additional arguments passed to the compiler" - echo " --parallel Multiprocess build. Requires GNU parallel" - exit 0 - ;; --) # End of all options shift break; @@ -52,15 +39,7 @@ do esac done -parallel="${parallel:-0}" CC="${CC:-gcc}" -if [ "$parallel" -gt 1 ]; then - if ! command -v sem > /dev/null; then - echo "Error: GNU parallel is required to use --parallel" - exit 1 - fi - CC="sem -j $parallel --id $$ ${CC}" -fi COMP_FLAGS="${CPPFLAGS:-} ${CFLAGS:-} ?{c.ccompiler.flags}$extraBuildArgs" LINK_FLAGS="${LDFLAGS:-} ?{c.linker.flags}" PS4="" @@ -109,11 +88,6 @@ case $uos in CC="clang" LINK_FLAGS="$LINK_FLAGS -lm" ;; - *crossos* ) - myos="crossos" - CC="clang" - LINK_FLAGS="$LINK_FLAGS -lm" - ;; *openbsd* ) myos="openbsd" CC="clang" @@ -139,11 +113,6 @@ case $uos in myos="solaris" LINK_FLAGS="$LINK_FLAGS -ldl -lm -lsocket -lnsl" ;; - *SunOS* ) - myos="solaris" - LINK_FLAGS="$LINK_FLAGS -ldl -lm -lsocket -lnsl" - isOpenIndiana="yes" - ;; *haiku* ) myos="haiku" LINK_FLAGS="$LINK_FLAGS -lroot -lnetwork" @@ -164,12 +133,7 @@ esac case $ucpu in *i386* | *i486* | *i586* | *i686* | *bepc* | *i86pc* ) - if [ "$isOpenIndiana" = "yes" ] || [ `uname -o` == "illumos" ] ; then - mycpu="amd64" - else - mycpu="i386" - fi - ;; + mycpu="i386" ;; *amd*64* | *x86-64* | *x86_64* ) mycpu="amd64" ;; *sparc*|*sun* ) @@ -192,10 +156,8 @@ case $ucpu in mycpu="powerpc64" ;; *power*|*ppc* ) if [ "$myos" = "freebsd" ] ; then - if [ "$ucpu" != "powerpc" ] ; then - COMP_FLAGS="$COMP_FLAGS -m64" - LINK_FLAGS="$LINK_FLAGS -m64" - fi + COMP_FLAGS="$COMP_FLAGS -m64" + LINK_FLAGS="$LINK_FLAGS -m64" mycpu=`uname -p` case $mycpu in powerpc64le) @@ -225,14 +187,10 @@ case $ucpu in mycpu="alpha" ;; *aarch64*|*arm64* ) mycpu="arm64" ;; - *arm*|*armv6l*|*armv7l*|*armv8l* ) + *arm*|*armv6l*|*armv71* ) mycpu="arm" ;; *riscv64|riscv* ) mycpu="riscv64" ;; - *e2k* ) - mycpu="e2k" ;; - *loongarch64* ) - mycpu="loongarch64" ;; *) echo 2>&1 "Error: unknown processor: $ucpu" exit 1 @@ -264,9 +222,6 @@ case $myos in $CC $COMP_FLAGS -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} # add(linkCmd, " \\\n" & changeFileExt(f, "o")) # end for - if [ "$parallel" -gt 0 ]; then - sem --wait --id $$ - fi $CC -o ?{"$binDir/" & toLowerAscii(c.name)} ?linkCmd $LINK_FLAGS ;; # end for diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim index 219cb44ce..d0f46fa52 100644 --- a/tools/niminst/debcreation.nim +++ b/tools/niminst/debcreation.nim @@ -9,10 +9,6 @@ import osproc, times, os, strutils - -when defined(nimPreviewSlimSystem): - import std/[assertions, syncio] - # http://www.debian.org/doc/manuals/maint-guide/ # Required files for debhelper. diff --git a/tools/niminst/deinstall.nimf b/tools/niminst/deinstall.nimf index 0dcfda75e..8b4477369 100644 --- a/tools/niminst/deinstall.nimf +++ b/tools/niminst/deinstall.nimf @@ -1,6 +1,6 @@ #? stdtmpl(subsChar='?') | standard #proc generateDeinstallScript(c: ConfigData): string = -# result = "#!/bin/sh\n# Generated by niminst\n" +# result = "#! /bin/sh\n# Generated by niminst\n" # var proj = c.name.toLowerAscii if [ $# -eq 1 ] ; then diff --git a/tools/niminst/install.nimf b/tools/niminst/install.nimf index 75ff9ce11..a78914ecd 100644 --- a/tools/niminst/install.nimf +++ b/tools/niminst/install.nimf @@ -1,24 +1,24 @@ #? stdtmpl(subsChar = '?') | standard #proc generateInstallScript(c: ConfigData): string = -# result = "#!/bin/sh\n# Generated by niminst\n" +# result = "#! /bin/sh\n# Generated by niminst\n" # var proj = c.name.toLowerAscii set -e if [ $# -eq 1 ] ; then -#if c.cat[fcUnixBin].len > 0: - if [ -f "?{c.cat[fcUnixBin][0].toUnix}" ] +# if c.cat[fcUnixBin].len > 0: + if test -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] [env DESTDIR=...] sh install.sh DIR" + echo "Usage: [sudo] 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=$1 - configdir="/etc/?proj" - libdir="/usr/lib/?proj" - docdir="/usr/share/?proj/doc" - datadir="/usr/share/?proj/data" + bindir=/usr/bin + 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=$1 - configdir="/etc/?proj" - libdir="/usr/local/lib/?proj" - docdir="/usr/local/share/?proj/doc" - datadir="/usr/local/share/?proj/data" + bindir=/usr/local/bin + 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,6 +51,9 @@ 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" @@ -59,22 +62,16 @@ 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 - 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" + mkdir -p $libdir + mkdir -p $docdir + mkdir -p $configdir + mkdir -p $nimbleDir/ echo "copying files..." #var createdDirs = newStringTable() #for cat in {fcConfig..fcLib, fcNimble}: @@ -87,46 +84,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] [env DESTDIR=...] sh install.sh DIR" + echo "Usage: [sudo] sh install.sh DIR" echo "Where DIR may be:" echo " /usr/bin" echo " /usr/local/bin" diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index b392ab472..aff0c5da0 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -16,7 +16,6 @@ endif target := ?{"$(binDir)/" & toLowerAscii(c.name)} - ucpu := $(shell sh -c 'uname -m | tr "[:upper:]" "[:lower:]"') ifeq ($(OS),Windows_NT) uos := windows @@ -26,8 +25,7 @@ endif ifeq ($(uos),linux) myos = linux - ## add -lrt to avoid "undefined reference to `clock_gettime'" with glibc<2.17 - LDFLAGS += -ldl -lm -lrt + LDFLAGS += -ldl -lm endif ifeq ($(uos),dragonfly) myos = freebsd @@ -104,18 +102,9 @@ endif ifeq ($(ucpu),x86_64) mycpu = amd64 endif -ifeq ($(ucpu),parisc64) - mycpu = hppa -endif -ifeq ($(ucpu),s390x) - mycpu = s390x -endif ifeq ($(ucpu),sparc) mycpu = sparc endif -ifeq ($(ucpu),sparc64) - mycpu = sparc64 -endif ifeq ($(ucpu),sun) mycpu = sparc endif @@ -141,7 +130,7 @@ ifeq ($(ucpu),powerpc) endif endif ifeq ($(ucpu),ppc) - mycpu = powerpc + mycpu = ppc endif ifneq (,$(filter $(ucpu), mips mips64)) mycpu = $(shell /bin/sh -c '"$(CC)" -dumpmachine | sed "s/-.*//"') @@ -167,26 +156,14 @@ endif ifeq ($(ucpu),armv7hl) mycpu = arm endif -ifeq ($(ucpu),armv8l) - mycpu = arm -endif ifeq ($(ucpu),aarch64) mycpu = arm64 endif -ifeq ($(ucpu),arm64) - mycpu = arm64 -endif ifeq ($(ucpu),riscv64) mycpu = riscv64 endif -ifeq ($(ucpu),e2k) - mycpu = e2k -endif -ifeq ($(ucpu),loongarch64) - mycpu = loongarch64 -endif ifndef mycpu - $(error unknown CPU architecture: $(ucpu) See makefile.nimf) + $(error unknown processor: $(ucpu)) endif # for osA in 1..c.oses.len: diff --git a/tools/niminst/nim-file.ico b/tools/niminst/nim-file.ico deleted file mode 100644 index 0685fedcb..000000000 Binary files a/tools/niminst/nim-file.ico and /dev/null differ diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 81b46653d..47137f8f3 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -11,13 +11,6 @@ import os, strutils, parseopt, parsecfg, strtabs, streams, debcreation, std / sha1 - -when defined(nimPreviewSlimSystem): - import std/syncio - -when not defined(nimHasEffectsOf): - {.pragma: effectsOf.} - const maxOS = 20 # max number of OSes maxCPU = 20 # max number of CPUs @@ -168,11 +161,11 @@ proc parseCmdLine(c: var ConfigData) = next(p) var kind = p.kind var key = p.key - var val = p.val + var val = p.val.string case kind of cmdArgument: if c.actions == {}: - for a in split(normalize(key), {';', ','}): + for a in split(normalize(key.string), {';', ','}): case a of "csource": incl(c.actions, actionCSource) of "scripts": incl(c.actions, actionScripts) @@ -183,11 +176,11 @@ proc parseCmdLine(c: var ConfigData) = of "deb": incl(c.actions, actionDeb) else: quit(Usage) else: - c.infile = addFileExt(key, "ini") - c.nimArgs = cmdLineRest(p) + c.infile = addFileExt(key.string, "ini") + c.nimArgs = cmdLineRest(p).string break of cmdLongOption, cmdShortOption: - case normalize(key) + case normalize(key.string) of "help", "h": stdout.write(Usage) quit(0) @@ -205,7 +198,7 @@ proc parseCmdLine(c: var ConfigData) = if c.infile.len == 0: quit(Usage) if c.mainfile.len == 0: c.mainfile = changeFileExt(c.infile, "nim") -proc eqT(a, b: string; t: proc (a: char): char {.nimcall.}): bool {.effectsOf: t.} = +proc eqT(a, b: string; t: proc (a: char): char{.nimcall.}): bool = ## equality under a transformation ``t``. candidate for the stdlib? var i = 0 var j = 0 @@ -484,6 +477,8 @@ proc deduplicateFiles(c: var ConfigData) = let build = getOutputDir(c) for osA in countup(1, c.oses.len): for cpuA in countup(1, c.cpus.len): + when not defined(nimNoNilSeqs): + if c.cfiles[osA][cpuA].isNil: c.cfiles[osA][cpuA] = @[] if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue for dup in mitems(c.cfiles[osA][cpuA]): let key = $secureHashFile(build / dup) @@ -515,17 +510,6 @@ template gatherFiles(fun, libpath, outDir) = # commenting out for now, see discussion in https://github.com/nim-lang/Nim/pull/13413 # copySrc(libpath / "lib/wrappers/linenoise/linenoise.h") -proc exe(f: string): string = - result = addFileExt(f, ExeExt) - when defined(windows): - result = result.replace('/','\\') - -proc findNim(): string = - let nim = "nim".exe - result = quoteShell("bin" / nim) - if not fileExists(result): - result = "nim" - proc srcdist(c: var ConfigData) = let cCodeDir = getOutputDir(c) / "c_code" if not dirExists(cCodeDir): createDir(cCodeDir) @@ -544,10 +528,10 @@ proc srcdist(c: var ConfigData) = var dir = getOutputDir(c) / buildDir(osA, cpuA) if dirExists(dir): removeDir(dir) createDir(dir) - var cmd = ("$# compile -f --incremental:off --compileonly " & + var cmd = ("nim compile -f --symbolfiles:off --compileonly " & "--gen_mapping --cc:gcc --skipUserCfg" & " --os:$# --cpu:$# $# $#") % - [findNim(), osname, cpuname, c.nimArgs, c.mainfile] + [osname, cpuname, c.nimArgs, c.mainfile] echo(cmd) if execShellCmd(cmd) != 0: quit("Error: call to nim compiler failed") diff --git a/tools/niminst/setup.ico b/tools/niminst/setup.ico deleted file mode 100644 index 867163046..000000000 Binary files a/tools/niminst/setup.ico and /dev/null differ diff --git a/tools/niminst/uninstall.ico b/tools/niminst/uninstall.ico deleted file mode 100644 index aff054644..000000000 Binary files a/tools/niminst/uninstall.ico and /dev/null differ diff --git a/tools/nimweb.nim b/tools/nimweb.nim new file mode 100644 index 000000000..f71b5f3be --- /dev/null +++ b/tools/nimweb.nim @@ -0,0 +1,556 @@ +# +# +# 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/officialpackages.nim b/tools/officialpackages.nim deleted file mode 100644 index 633944a14..000000000 --- a/tools/officialpackages.nim +++ /dev/null @@ -1,21 +0,0 @@ -import std/[strformat, paths, dirs, envvars] -from std/os import execShellCmd - -proc exec*(cmd: string, errorcode: int = QuitFailure, additionalPath = "") = - let prevPath = getEnv("PATH") - if additionalPath.len > 0: - var absolute = Path(additionalPath) - if not absolute.isAbsolute: - absolute = getCurrentDir() / absolute - echo("Adding to $PATH: ", string(absolute)) - putEnv("PATH", (if prevPath.len > 0: prevPath & PathSep else: "") & string(absolute)) - echo(cmd) - if execShellCmd(cmd) != 0: quit("FAILURE", errorcode) - putEnv("PATH", prevPath) - -proc gitClonePackages*(names: seq[string]) = - if not dirExists(Path"pkgs"): - createDir(Path"pkgs") - for name in names: - if not dirExists(Path"pkgs" / Path(name)): - exec fmt"git clone https://github.com/nim-lang/{name} pkgs/{name}" diff --git a/tools/trimcc.nim b/tools/trimcc.nim index 41795e1aa..959eaf310 100644 --- a/tools/trimcc.nim +++ b/tools/trimcc.nim @@ -31,7 +31,7 @@ proc processIncludes(dir: string, whitelist: StringTableRef) = if ('.' notin name and "include" in path) or ("c++" in path): let n = whitelist.getOrDefault(name) if n != "processed": whitelist[name] = "found" - if name.endsWith(".h"): + if name.endswith(".h"): let n = whitelist.getOrDefault(name) if n == "found": includes(path, name, whitelist) of pcDir: processIncludes(path, whitelist) diff --git a/tools/urldownloader.nim b/tools/urldownloader.nim new file mode 100644 index 000000000..73e4034c9 --- /dev/null +++ b/tools/urldownloader.nim @@ -0,0 +1,431 @@ + +# +# +# 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/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim index d548666b9..1b5b2fb82 100644 --- a/tools/vccexe/vccexe.nim +++ b/tools/vccexe/vccexe.nim @@ -41,7 +41,6 @@ const platformPrefix = "--platform" sdktypePrefix = "--sdktype" sdkversionPrefix = "--sdkversion" - vctoolsetPrefix = "--vctoolset" verbosePrefix = "--verbose" vccversionSepIdx = vccversionPrefix.len @@ -50,7 +49,6 @@ const platformSepIdx = platformPrefix.len sdktypeSepIdx = sdktypePrefix.len sdkversionSepIdx = sdkversionPrefix.len - vctoolsetSepIdx = vctoolsetPrefix.len vcvarsallDefaultPath = "vcvarsall.bat" @@ -95,12 +93,10 @@ Options: --sdktype: Specify the SDK flavor to use. Defaults to the Desktop SDK. : {empty} | store | uwp | onecore --sdkversion: Use a specific Windows SDK version: - is either the full Windows 10 SDK version number or + is either the full Windows 10 SDK version number or "8.1" to use the windows 8.1 SDK --verbose Echoes the command line for loading the Developer Command Prompt and the command line passed on to the secondary command. - --vctoolset Optionally specifies the Visual Studio compiler toolset to use. - By default, the environment is set to use the current Visual Studio compiler toolset. Other command line arguments are passed on to the secondary command specified by --command or to the @@ -108,12 +104,12 @@ Microsoft (R) C/C++ Optimizing Compiler if no secondary command was specified """ -proc parseVccexeCmdLine(argseq: seq[string], +proc parseVccexeCmdLine(argseq: seq[TaintedString], vccversionArg: var seq[string], printPathArg: var bool, vcvarsallArg: var string, commandArg: var string, noCommandArg: var bool, platformArg: var VccArch, sdkTypeArg: var VccPlatformType, - sdkVersionArg: var string, vctoolsetArg: var string, verboseArg: var bool, - clArgs: var seq[string]) = + sdkVersionArg: var string, verboseArg: var bool, + clArgs: var seq[TaintedString]) = ## Cannot use usual command-line argument parser here ## Since vccexe command-line arguments are intermingled ## with the secondary command-line arguments which have @@ -129,7 +125,7 @@ proc parseVccexeCmdLine(argseq: seq[string], responseargs = parseCmdLine(responsecontent) parseVccexeCmdLine(responseargs, vccversionArg, printPathArg, vcvarsallArg, commandArg, noCommandArg, platformArg, sdkTypeArg, - sdkVersionArg, vctoolsetArg, verboseArg, clArgs) + sdkVersionArg, verboseArg, clArgs) elif wargv.startsWith(vccversionPrefix): # Check for vccversion vccversionArg.add(wargv.substr(vccversionSepIdx + 1)) elif wargv.cmpIgnoreCase(printPathPrefix) == 0: # Check for printPath @@ -146,8 +142,6 @@ proc parseVccexeCmdLine(argseq: seq[string], sdkTypeArg = parseEnum[VccPlatformType](wargv.substr(sdktypeSepIdx + 1)) elif wargv.startsWith(sdkversionPrefix): # Check for sdkversion sdkVersionArg = wargv.substr(sdkversionSepIdx + 1) - elif wargv.startsWith(vctoolsetPrefix): # Check for vctoolset - vctoolsetArg = wargv.substr(vctoolsetSepIdx + 1) elif wargv.startsWith(verbosePrefix): verboseArg = true else: # Regular cl.exe argument -> store for final cl.exe invocation @@ -164,14 +158,13 @@ when isMainModule: var platformArg: VccArch var sdkTypeArg: VccPlatformType var sdkVersionArg: string - var vctoolsetArg: string var verboseArg: bool = false - var clArgs: seq[string] = @[] + var clArgs: seq[TaintedString] = @[] let wrapperArgs = commandLineParams() parseVccexeCmdLine(wrapperArgs, vccversionArg, printPathArg, vcvarsallArg, - commandArg, noCommandArg, platformArg, sdkTypeArg, sdkVersionArg, vctoolsetArg, + commandArg, noCommandArg, platformArg, sdkTypeArg, sdkVersionArg, verboseArg, clArgs) @@ -202,7 +195,7 @@ when isMainModule: echo "$1: $2" % [head, vcvarsallArg] # Call vcvarsall to get the appropriate VCC process environment - var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, vctoolsetArg, verboseArg) + var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, verboseArg) if vcvars != nil: for vccEnvKey, vccEnvVal in vcvars: putEnv(vccEnvKey, vccEnvVal) diff --git a/tools/vccexe/vcvarsall.nim b/tools/vccexe/vcvarsall.nim index 73b103e3c..29d13cc7e 100644 --- a/tools/vccexe/vcvarsall.nim +++ b/tools/vccexe/vcvarsall.nim @@ -33,7 +33,7 @@ type vccplatUWP = "uwp", ## Universal Windows Platform (UWP) Application vccplatOneCore = "onecore" # Undocumented platform type in the Windows SDK, probably XBox One SDK platform type. -proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version, vctoolset: string = "", verbose: bool = false): StringTableRef = +proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version: string = "", verbose: bool = false): StringTableRef = ## Returns a string table containing the proper process environment to successfully execute VCC compile commands for the specified SDK version, CPU architecture and platform type. ## ## path @@ -44,8 +44,6 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type ## The compile target Platform Type. Defaults to the Windows Desktop platform, i.e. a regular Windows executable binary. ## sdk_version ## The Windows SDK version to use. - ## vctoolset - ## Visual Studio compiler toolset to use. ## verbose ## Echo the command-line passed on to the system to load the VCC environment. Defaults to `false`. @@ -65,9 +63,6 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type if sdk_version.len > 0: args.add(sdk_version) - - if vctoolset.len > 0: - args.add("-vcvars_ver="&vctoolset) let argStr = args.join " " diff --git a/tools/website.nimf b/tools/website.nimf new file mode 100644 index 000000000..bde84b32f --- /dev/null +++ b/tools/website.nimf @@ -0,0 +1,266 @@ +#? 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
    text1